summaryrefslogtreecommitdiff
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/Makefile1
-rw-r--r--sound/aoa/core/gpio-feature.c4
-rw-r--r--sound/aoa/fabrics/layout.c15
-rw-r--r--sound/aoa/soundbus/i2sbus/core.c2
-rw-r--r--sound/arm/Kconfig15
-rw-r--r--sound/core/compress_offload.c81
-rw-r--r--sound/core/control.c34
-rw-r--r--sound/core/pcm.c14
-rw-r--r--sound/core/pcm_native.c4
-rw-r--r--sound/core/rawmidi.c4
-rw-r--r--sound/core/seq/oss/seq_oss_synth.c10
-rw-r--r--sound/core/seq/seq_clientmgr.c722
-rw-r--r--sound/core/seq/seq_compat.c7
-rw-r--r--sound/core/seq/seq_timer.c23
-rw-r--r--sound/core/seq/seq_timer.h2
-rw-r--r--sound/core/timer.c36
-rw-r--r--sound/drivers/dummy.c1
-rw-r--r--sound/firewire/bebob/Makefile5
-rw-r--r--sound/firewire/bebob/bebob.c8
-rw-r--r--sound/firewire/bebob/bebob.h3
-rw-r--r--sound/firewire/bebob/bebob_terratec.c31
-rw-r--r--sound/firewire/bebob/bebob_yamaha_terratec.c (renamed from sound/firewire/bebob/bebob_yamaha.c)9
-rw-r--r--sound/firewire/dice/dice-pcm.c4
-rw-r--r--sound/firewire/digi00x/digi00x-pcm.c4
-rw-r--r--sound/firewire/fireworks/fireworks.h1
-rw-r--r--sound/firewire/fireworks/fireworks_hwdep.c71
-rw-r--r--sound/firewire/fireworks/fireworks_proc.c4
-rw-r--r--sound/firewire/fireworks/fireworks_transaction.c5
-rw-r--r--sound/firewire/oxfw/oxfw-pcm.c4
-rw-r--r--sound/firewire/tascam/tascam-hwdep.c33
-rw-r--r--sound/firewire/tascam/tascam-pcm.c4
-rw-r--r--sound/hda/array.c4
-rw-r--r--sound/hda/ext/hdac_ext_controller.c91
-rw-r--r--sound/hda/ext/hdac_ext_stream.c56
-rw-r--r--sound/hda/hdac_controller.c75
-rw-r--r--sound/hda/hdac_regmap.c4
-rw-r--r--sound/hda/hdmi_chmap.c28
-rw-r--r--sound/i2c/other/ak4114.c2
-rw-r--r--sound/i2c/other/ak4117.c2
-rw-r--r--sound/isa/ad1848/ad1848.c13
-rw-r--r--sound/isa/adlib.c13
-rw-r--r--sound/isa/cmi8328.c13
-rw-r--r--sound/isa/cs423x/cs4231.c13
-rw-r--r--sound/isa/galaxy/galaxy.c13
-rw-r--r--sound/isa/gus/gusclassic.c13
-rw-r--r--sound/isa/gus/gusextreme.c13
-rw-r--r--sound/isa/gus/gusmax.c13
-rw-r--r--sound/isa/sb/jazz16.c13
-rw-r--r--sound/isa/sb/sb8.c13
-rw-r--r--sound/isa/sc6000.c13
-rw-r--r--sound/oss/ad1848.c2
-rw-r--r--sound/oss/aedsp16.c14
-rw-r--r--sound/oss/sound_firmware.h29
-rw-r--r--sound/oss/sound_timer.c2
-rw-r--r--sound/oss/sys_timer.c2
-rw-r--r--sound/pci/ad1889.c4
-rw-r--r--sound/pci/ali5451/ali5451.c2
-rw-r--r--sound/pci/als300.c4
-rw-r--r--sound/pci/als4000.c4
-rw-r--r--sound/pci/asihpi/asihpi.c4
-rw-r--r--sound/pci/asihpi/hpifunc.c7
-rw-r--r--sound/pci/atiixp.c6
-rw-r--r--sound/pci/atiixp_modem.c4
-rw-r--r--sound/pci/au88x0/au88x0_core.c7
-rw-r--r--sound/pci/aw2/aw2-alsa.c4
-rw-r--r--sound/pci/azt3328.c6
-rw-r--r--sound/pci/bt87x.c2
-rw-r--r--sound/pci/ca0106/ca0106_main.c16
-rw-r--r--sound/pci/cmipci.c10
-rw-r--r--sound/pci/cs4281.c4
-rw-r--r--sound/pci/cs46xx/cs46xx_lib.c4
-rw-r--r--sound/pci/cs46xx/dsp_spos.c2
-rw-r--r--sound/pci/cs5535audio/cs5535audio_pcm.c4
-rw-r--r--sound/pci/ctxfi/ctatc.c2
-rw-r--r--sound/pci/ctxfi/cthw20k2.c34
-rw-r--r--sound/pci/ctxfi/ctpcm.c4
-rw-r--r--sound/pci/ctxfi/ctvmem.c6
-rw-r--r--sound/pci/echoaudio/echoaudio.c10
-rw-r--r--sound/pci/emu10k1/emu10k1x.c4
-rw-r--r--sound/pci/emu10k1/emupcm.c12
-rw-r--r--sound/pci/emu10k1/p16v.c20
-rw-r--r--sound/pci/ens1370.c6
-rw-r--r--sound/pci/es1938.c4
-rw-r--r--sound/pci/es1968.c4
-rw-r--r--sound/pci/fm801.c4
-rw-r--r--sound/pci/hda/hda_codec.c8
-rw-r--r--sound/pci/hda/hda_controller.c205
-rw-r--r--sound/pci/hda/hda_controller.h3
-rw-r--r--sound/pci/hda/hda_generic.c6
-rw-r--r--sound/pci/hda/hda_intel.c72
-rw-r--r--sound/pci/hda/hda_tegra.c20
-rw-r--r--sound/pci/hda/patch_ca0132.c10
-rw-r--r--sound/pci/hda/patch_conexant.c10
-rw-r--r--sound/pci/hda/patch_hdmi.c5
-rw-r--r--sound/pci/hda/patch_realtek.c211
-rw-r--r--sound/pci/ice1712/ice1712.c10
-rw-r--r--sound/pci/ice1712/ice1724.c14
-rw-r--r--sound/pci/korg1212/korg1212.c4
-rw-r--r--sound/pci/lola/lola_pcm.c2
-rw-r--r--sound/pci/lx6464es/lx6464es.c4
-rw-r--r--sound/pci/maestro3.c4
-rw-r--r--sound/pci/mixart/mixart.c4
-rw-r--r--sound/pci/mixart/mixart_mixer.c2
-rw-r--r--sound/pci/nm256/nm256.c4
-rw-r--r--sound/pci/oxygen/oxygen_pcm.c12
-rw-r--r--sound/pci/pcxhr/pcxhr.c2
-rw-r--r--sound/pci/riptide/riptide.c8
-rw-r--r--sound/pci/rme32.c16
-rw-r--r--sound/pci/rme96.c8
-rw-r--r--sound/pci/rme9652/hdsp.c4
-rw-r--r--sound/pci/rme9652/hdspm.c4
-rw-r--r--sound/pci/rme9652/rme9652.c4
-rw-r--r--sound/pci/sonicvibes.c4
-rw-r--r--sound/pci/trident/trident_main.c12
-rw-r--r--sound/pci/via82xx.c10
-rw-r--r--sound/pci/via82xx_modem.c4
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c10
-rw-r--r--sound/ppc/awacs.c1
-rw-r--r--sound/ppc/snd_ps3.c4
-rw-r--r--sound/ppc/tumbler.c8
-rw-r--r--sound/sh/aica.c16
-rw-r--r--sound/soc/amd/acp-pcm-dma.c2
-rw-r--r--sound/soc/atmel/Kconfig1
-rw-r--r--sound/soc/atmel/atmel-classd.c11
-rw-r--r--sound/soc/atmel/atmel-pcm-pdc.c2
-rw-r--r--sound/soc/atmel/atmel-pdmic.c25
-rw-r--r--sound/soc/atmel/atmel_ssc_dai.c7
-rw-r--r--sound/soc/bcm/Kconfig9
-rw-r--r--sound/soc/bcm/Makefile5
-rw-r--r--sound/soc/bcm/cygnus-pcm.c861
-rw-r--r--sound/soc/bcm/cygnus-ssp.c1529
-rw-r--r--sound/soc/bcm/cygnus-ssp.h139
-rw-r--r--sound/soc/codecs/88pm860x-codec.c14
-rw-r--r--sound/soc/codecs/Kconfig63
-rw-r--r--sound/soc/codecs/Makefile20
-rw-r--r--sound/soc/codecs/ab8500-codec.c57
-rw-r--r--sound/soc/codecs/ac97.c10
-rw-r--r--sound/soc/codecs/ad1836.c14
-rw-r--r--sound/soc/codecs/ad193x.c14
-rw-r--r--sound/soc/codecs/ad1980.c14
-rw-r--r--sound/soc/codecs/ad73311.c10
-rw-r--r--sound/soc/codecs/adau-utils.c61
-rw-r--r--sound/soc/codecs/adau-utils.h7
-rw-r--r--sound/soc/codecs/adau1373.c52
-rw-r--r--sound/soc/codecs/adau1701.c15
-rw-r--r--sound/soc/codecs/adau1761-i2c.c2
-rw-r--r--sound/soc/codecs/adau1761-spi.c2
-rw-r--r--sound/soc/codecs/adau1761.c14
-rw-r--r--sound/soc/codecs/adau1781-i2c.c2
-rw-r--r--sound/soc/codecs/adau1781-spi.c2
-rw-r--r--sound/soc/codecs/adau1781.c14
-rw-r--r--sound/soc/codecs/adau17x1.c251
-rw-r--r--sound/soc/codecs/adau17x1.h6
-rw-r--r--sound/soc/codecs/adau1977.c14
-rw-r--r--sound/soc/codecs/adau7002.c82
-rw-r--r--sound/soc/codecs/adav80x.c14
-rw-r--r--sound/soc/codecs/ads117x.c10
-rw-r--r--sound/soc/codecs/ak4104.c15
-rw-r--r--sound/soc/codecs/ak4535.c14
-rw-r--r--sound/soc/codecs/ak4554.c10
-rw-r--r--sound/soc/codecs/ak4613.c28
-rw-r--r--sound/soc/codecs/ak4641.c14
-rw-r--r--sound/soc/codecs/ak4642.c27
-rw-r--r--sound/soc/codecs/ak4671.c14
-rw-r--r--sound/soc/codecs/ak5386.c10
-rw-r--r--sound/soc/codecs/alc5632.c14
-rw-r--r--sound/soc/codecs/arizona.c169
-rw-r--r--sound/soc/codecs/arizona.h39
-rw-r--r--sound/soc/codecs/bt-sco.c62
-rw-r--r--sound/soc/codecs/cq93vc.c6
-rw-r--r--sound/soc/codecs/cs35l32.c15
-rw-r--r--sound/soc/codecs/cs35l33.c1304
-rw-r--r--sound/soc/codecs/cs35l33.h221
-rw-r--r--sound/soc/codecs/cs4265.c15
-rw-r--r--sound/soc/codecs/cs4270.c14
-rw-r--r--sound/soc/codecs/cs4271.c14
-rw-r--r--sound/soc/codecs/cs42l51.c14
-rw-r--r--sound/soc/codecs/cs42l52.c15
-rw-r--r--sound/soc/codecs/cs42l56.c15
-rw-r--r--sound/soc/codecs/cs42l73.c19
-rw-r--r--sound/soc/codecs/cs42xx8.c14
-rw-r--r--sound/soc/codecs/cs4349.c15
-rw-r--r--sound/soc/codecs/cs47l24.c45
-rw-r--r--sound/soc/codecs/cs53l30.c1144
-rw-r--r--sound/soc/codecs/cs53l30.h459
-rw-r--r--sound/soc/codecs/cx20442.c11
-rw-r--r--sound/soc/codecs/da7210.c15
-rw-r--r--sound/soc/codecs/da7213.c149
-rw-r--r--sound/soc/codecs/da7213.h12
-rw-r--r--sound/soc/codecs/da7218.c49
-rw-r--r--sound/soc/codecs/da7218.h2
-rw-r--r--sound/soc/codecs/da7219-aad.c211
-rw-r--r--sound/soc/codecs/da7219-aad.h11
-rw-r--r--sound/soc/codecs/da7219.c129
-rw-r--r--sound/soc/codecs/da7219.h8
-rw-r--r--sound/soc/codecs/da732x.c14
-rw-r--r--sound/soc/codecs/da9055.c15
-rw-r--r--sound/soc/codecs/dmic.c10
-rw-r--r--sound/soc/codecs/es8328.c14
-rw-r--r--sound/soc/codecs/gtm601.c10
-rw-r--r--sound/soc/codecs/hdac_hdmi.c29
-rw-r--r--sound/soc/codecs/hdmi-codec.c94
-rw-r--r--sound/soc/codecs/inno_rk3036.c14
-rw-r--r--sound/soc/codecs/isabelle.c14
-rw-r--r--sound/soc/codecs/jz4740.c14
-rw-r--r--sound/soc/codecs/l3.c71
-rw-r--r--sound/soc/codecs/lm49453.c14
-rw-r--r--sound/soc/codecs/max98088.c14
-rw-r--r--sound/soc/codecs/max98095.c14
-rw-r--r--sound/soc/codecs/max98357a.c10
-rw-r--r--sound/soc/codecs/max98371.c2
-rw-r--r--sound/soc/codecs/max9850.c14
-rw-r--r--sound/soc/codecs/max98504.c383
-rw-r--r--sound/soc/codecs/max98504.h59
-rw-r--r--sound/soc/codecs/max9860.c755
-rw-r--r--sound/soc/codecs/max9860.h162
-rw-r--r--[-rwxr-xr-x]sound/soc/codecs/max9867.c19
-rw-r--r--[-rwxr-xr-x]sound/soc/codecs/max9867.h0
-rw-r--r--sound/soc/codecs/max9877.h2
-rw-r--r--sound/soc/codecs/max98925.c14
-rw-r--r--sound/soc/codecs/max98926.c20
-rw-r--r--sound/soc/codecs/mc13783.c14
-rw-r--r--sound/soc/codecs/ml26124.c14
-rw-r--r--sound/soc/codecs/nau8810.c884
-rw-r--r--sound/soc/codecs/nau8810.h281
-rw-r--r--sound/soc/codecs/nau8825.c1260
-rw-r--r--sound/soc/codecs/nau8825.h127
-rw-r--r--sound/soc/codecs/pcm1681.c16
-rw-r--r--sound/soc/codecs/pcm179x.c16
-rw-r--r--sound/soc/codecs/pcm3008.c10
-rw-r--r--sound/soc/codecs/pcm3168a.c14
-rw-r--r--sound/soc/codecs/pcm5102a.c1
-rw-r--r--sound/soc/codecs/pcm512x.c14
-rw-r--r--sound/soc/codecs/rt286.c21
-rw-r--r--sound/soc/codecs/rt298.c14
-rw-r--r--sound/soc/codecs/rt5514-spi.c447
-rw-r--r--sound/soc/codecs/rt5514-spi.h38
-rw-r--r--sound/soc/codecs/rt5514.c188
-rw-r--r--sound/soc/codecs/rt5514.h11
-rw-r--r--sound/soc/codecs/rt5616.c23
-rw-r--r--sound/soc/codecs/rt5631.c14
-rw-r--r--sound/soc/codecs/rt5640.c17
-rw-r--r--sound/soc/codecs/rt5640.h1
-rw-r--r--sound/soc/codecs/rt5645.c40
-rw-r--r--sound/soc/codecs/rt5645.h3
-rw-r--r--sound/soc/codecs/rt5651.c14
-rw-r--r--sound/soc/codecs/rt5659.c41
-rw-r--r--sound/soc/codecs/rt5659.h7
-rw-r--r--sound/soc/codecs/rt5660.c1353
-rw-r--r--sound/soc/codecs/rt5660.h847
-rw-r--r--sound/soc/codecs/rt5663.c3218
-rw-r--r--sound/soc/codecs/rt5663.h1121
-rw-r--r--sound/soc/codecs/rt5670.c19
-rw-r--r--sound/soc/codecs/rt5677.c69
-rw-r--r--sound/soc/codecs/sgtl5000.c448
-rw-r--r--sound/soc/codecs/sgtl5000.h2
-rw-r--r--sound/soc/codecs/si476x.c10
-rw-r--r--sound/soc/codecs/sn95031.c14
-rw-r--r--sound/soc/codecs/spdif_receiver.c10
-rw-r--r--sound/soc/codecs/spdif_transmitter.c10
-rw-r--r--sound/soc/codecs/ssm2518.c14
-rw-r--r--sound/soc/codecs/ssm2602.c14
-rw-r--r--sound/soc/codecs/ssm4567.c14
-rw-r--r--sound/soc/codecs/sta32x.c14
-rw-r--r--sound/soc/codecs/sta350.c14
-rw-r--r--sound/soc/codecs/sta529.c6
-rw-r--r--sound/soc/codecs/stac9766.c14
-rw-r--r--sound/soc/codecs/sti-sas.c8
-rw-r--r--sound/soc/codecs/tas2552.c14
-rw-r--r--sound/soc/codecs/tas5086.c16
-rw-r--r--sound/soc/codecs/tas571x.c202
-rw-r--r--sound/soc/codecs/tas571x.h40
-rw-r--r--sound/soc/codecs/tas5720.c14
-rw-r--r--sound/soc/codecs/tfa9879.c15
-rw-r--r--sound/soc/codecs/tlv320aic23.c14
-rw-r--r--sound/soc/codecs/tlv320aic26.c14
-rw-r--r--sound/soc/codecs/tlv320aic31xx.c218
-rw-r--r--sound/soc/codecs/tlv320aic31xx.h136
-rw-r--r--sound/soc/codecs/tlv320aic32x4.c14
-rw-r--r--sound/soc/codecs/tlv320aic3x.c14
-rw-r--r--sound/soc/codecs/tlv320dac33.c31
-rw-r--r--sound/soc/codecs/tpa6130a2.c397
-rw-r--r--sound/soc/codecs/tpa6130a2.h14
-rw-r--r--sound/soc/codecs/twl4030.c14
-rw-r--r--sound/soc/codecs/twl6040.c14
-rw-r--r--sound/soc/codecs/uda134x.c17
-rw-r--r--sound/soc/codecs/uda1380.c14
-rw-r--r--sound/soc/codecs/wl1273.c14
-rw-r--r--sound/soc/codecs/wm0010.c12
-rw-r--r--sound/soc/codecs/wm1250-ev1.c13
-rw-r--r--sound/soc/codecs/wm2000.c18
-rw-r--r--sound/soc/codecs/wm2200.c16
-rw-r--r--sound/soc/codecs/wm5100.c18
-rw-r--r--sound/soc/codecs/wm5102.c30
-rw-r--r--sound/soc/codecs/wm5110.c48
-rw-r--r--sound/soc/codecs/wm8350.c16
-rw-r--r--sound/soc/codecs/wm8400.c16
-rw-r--r--sound/soc/codecs/wm8510.c16
-rw-r--r--sound/soc/codecs/wm8523.c16
-rw-r--r--sound/soc/codecs/wm8580.c16
-rw-r--r--sound/soc/codecs/wm8711.c16
-rw-r--r--sound/soc/codecs/wm8727.c12
-rw-r--r--sound/soc/codecs/wm8728.c16
-rw-r--r--sound/soc/codecs/wm8731.c21
-rw-r--r--sound/soc/codecs/wm8737.c16
-rw-r--r--sound/soc/codecs/wm8741.c12
-rw-r--r--sound/soc/codecs/wm8750.c16
-rw-r--r--sound/soc/codecs/wm8753.c19
-rw-r--r--sound/soc/codecs/wm8770.c16
-rw-r--r--sound/soc/codecs/wm8776.c16
-rw-r--r--sound/soc/codecs/wm8782.c12
-rw-r--r--sound/soc/codecs/wm8804.c10
-rw-r--r--sound/soc/codecs/wm8900.c16
-rw-r--r--sound/soc/codecs/wm8903.c18
-rw-r--r--sound/soc/codecs/wm8904.c2
-rw-r--r--sound/soc/codecs/wm8940.c17
-rw-r--r--sound/soc/codecs/wm8955.c16
-rw-r--r--sound/soc/codecs/wm8960.c7
-rw-r--r--sound/soc/codecs/wm8961.c16
-rw-r--r--sound/soc/codecs/wm8962.c6
-rw-r--r--sound/soc/codecs/wm8971.c16
-rw-r--r--sound/soc/codecs/wm8974.c16
-rw-r--r--sound/soc/codecs/wm8978.c16
-rw-r--r--sound/soc/codecs/wm8983.c16
-rw-r--r--sound/soc/codecs/wm8985.c147
-rw-r--r--sound/soc/codecs/wm8985.h38
-rw-r--r--sound/soc/codecs/wm8988.c14
-rw-r--r--sound/soc/codecs/wm8990.c16
-rw-r--r--sound/soc/codecs/wm8991.c44
-rw-r--r--sound/soc/codecs/wm8993.c2
-rw-r--r--sound/soc/codecs/wm8994.c2
-rw-r--r--sound/soc/codecs/wm8995.c14
-rw-r--r--sound/soc/codecs/wm8996.c18
-rw-r--r--sound/soc/codecs/wm8997.c16
-rw-r--r--sound/soc/codecs/wm8998.c27
-rw-r--r--sound/soc/codecs/wm9081.c16
-rw-r--r--sound/soc/codecs/wm9090.c2
-rw-r--r--sound/soc/codecs/wm9705.c16
-rw-r--r--sound/soc/codecs/wm9712.c16
-rw-r--r--sound/soc/codecs/wm9713.c16
-rw-r--r--sound/soc/codecs/wm_adsp.c120
-rw-r--r--sound/soc/codecs/wm_adsp.h11
-rw-r--r--sound/soc/davinci/davinci-mcasp.c67
-rw-r--r--sound/soc/davinci/davinci-mcasp.h4
-rw-r--r--sound/soc/dwc/Kconfig9
-rw-r--r--sound/soc/dwc/Makefile4
-rw-r--r--sound/soc/dwc/designware_i2s.c233
-rw-r--r--sound/soc/dwc/designware_pcm.c225
-rw-r--r--sound/soc/dwc/local.h128
-rw-r--r--sound/soc/fsl/Kconfig1
-rw-r--r--sound/soc/fsl/fsl_asrc.c12
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c2
-rw-r--r--sound/soc/fsl/fsl_esai.c8
-rw-r--r--sound/soc/fsl/fsl_sai.c6
-rw-r--r--sound/soc/fsl/fsl_spdif.c4
-rw-r--r--sound/soc/fsl/fsl_ssi.c14
-rw-r--r--sound/soc/generic/Kconfig12
-rw-r--r--sound/soc/generic/Makefile6
-rw-r--r--sound/soc/generic/simple-card-utils.c244
-rw-r--r--sound/soc/generic/simple-card.c529
-rw-r--r--sound/soc/generic/simple-scu-card.c345
-rw-r--r--sound/soc/img/pistachio-internal-dac.c14
-rw-r--r--sound/soc/intel/Kconfig85
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.c32
-rw-r--r--sound/soc/intel/atom/sst-atom-controls.h6
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-compress.c9
-rw-r--r--sound/soc/intel/atom/sst-mfld-platform-pcm.c2
-rw-r--r--sound/soc/intel/atom/sst/sst.c5
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c141
-rw-r--r--sound/soc/intel/atom/sst/sst_ipc.c3
-rw-r--r--sound/soc/intel/atom/sst/sst_pvt.c14
-rw-r--r--sound/soc/intel/baytrail/sst-baytrail-ipc.c12
-rw-r--r--sound/soc/intel/boards/Makefile4
-rw-r--r--sound/soc/intel/boards/bdw-rt5677.c347
-rw-r--r--sound/soc/intel/boards/bxt_da7219_max98357a.c523
-rw-r--r--sound/soc/intel/boards/bxt_rt298.c130
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c507
-rw-r--r--sound/soc/intel/boards/cht_bsw_rt5645.c20
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_max98357a.c51
-rw-r--r--sound/soc/intel/boards/skl_nau88l25_ssm4567.c51
-rw-r--r--sound/soc/intel/boards/skl_rt286.c9
-rw-r--r--sound/soc/intel/common/Makefile4
-rw-r--r--sound/soc/intel/common/sst-acpi.c1
-rw-r--r--sound/soc/intel/common/sst-acpi.h4
-rw-r--r--sound/soc/intel/common/sst-dsp-priv.h4
-rw-r--r--sound/soc/intel/common/sst-dsp.c69
-rw-r--r--sound/soc/intel/common/sst-dsp.h2
-rw-r--r--sound/soc/intel/common/sst-firmware.c68
-rw-r--r--sound/soc/intel/haswell/sst-haswell-pcm.c3
-rw-r--r--sound/soc/intel/skylake/Makefile2
-rw-r--r--sound/soc/intel/skylake/bxt-sst.c305
-rw-r--r--sound/soc/intel/skylake/skl-messages.c120
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c40
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.h22
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c151
-rw-r--r--sound/soc/intel/skylake/skl-sst-cldma.c4
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.c260
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.h114
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.c45
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.h30
-rw-r--r--sound/soc/intel/skylake/skl-sst-utils.c407
-rw-r--r--sound/soc/intel/skylake/skl-sst.c155
-rw-r--r--sound/soc/intel/skylake/skl-topology.c1142
-rw-r--r--sound/soc/intel/skylake/skl-topology.h22
-rw-r--r--sound/soc/intel/skylake/skl-tplg-interface.h95
-rw-r--r--sound/soc/intel/skylake/skl.c49
-rw-r--r--sound/soc/intel/skylake/skl.h12
-rw-r--r--sound/soc/kirkwood/kirkwood-dma.c2
-rw-r--r--sound/soc/mediatek/Kconfig38
-rw-r--r--sound/soc/mediatek/Makefile10
-rw-r--r--sound/soc/mediatek/common/Makefile16
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.c380
-rw-r--r--sound/soc/mediatek/common/mtk-afe-fe-dai.h45
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.c90
-rw-r--r--sound/soc/mediatek/common/mtk-afe-platform-driver.h23
-rw-r--r--sound/soc/mediatek/common/mtk-base-afe.h104
-rw-r--r--sound/soc/mediatek/mt2701/Makefile19
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c464
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h38
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-common.h172
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-pcm.c1656
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-cs42448.c412
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-reg.h186
-rw-r--r--sound/soc/mediatek/mt8173/Makefile7
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-common.h73
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-afe-pcm.c (renamed from sound/soc/mediatek/mtk-afe-pcm.c)925
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-max98090.c (renamed from sound/soc/mediatek/mt8173-max98090.c)2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c (renamed from sound/soc/mediatek/mt8173-rt5650-rt5514.c)2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c (renamed from sound/soc/mediatek/mt8173-rt5650-rt5676.c)4
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650.c (renamed from sound/soc/mediatek/mt8173-rt5650.c)73
-rw-r--r--sound/soc/mediatek/mtk-afe-common.h101
-rw-r--r--sound/soc/omap/Kconfig3
-rw-r--r--sound/soc/omap/mcbsp.c24
-rw-r--r--sound/soc/omap/mcbsp.h3
-rw-r--r--sound/soc/omap/omap-abe-twl6040.c75
-rw-r--r--sound/soc/omap/omap-hdmi-audio.c1
-rw-r--r--sound/soc/omap/omap-mcbsp.c5
-rw-r--r--sound/soc/omap/omap-mcpdm.c62
-rw-r--r--sound/soc/omap/omap-pcm.c2
-rw-r--r--sound/soc/omap/rx51.c46
-rw-r--r--sound/soc/qcom/apq8016_sbc.c20
-rw-r--r--sound/soc/qcom/lpass-platform.c16
-rw-r--r--sound/soc/qcom/lpass.h2
-rw-r--r--sound/soc/rockchip/Kconfig12
-rw-r--r--sound/soc/rockchip/Makefile2
-rw-r--r--sound/soc/rockchip/rk3399_gru_sound.c397
-rw-r--r--sound/soc/rockchip/rockchip_i2s.c81
-rw-r--r--sound/soc/rockchip/rockchip_i2s.h11
-rw-r--r--sound/soc/rockchip/rockchip_max98090.c65
-rw-r--r--sound/soc/rockchip/rockchip_spdif.c29
-rw-r--r--sound/soc/samsung/Kconfig65
-rw-r--r--sound/soc/samsung/Makefile2
-rw-r--r--sound/soc/samsung/ac97.c37
-rw-r--r--sound/soc/samsung/dma.h29
-rw-r--r--sound/soc/samsung/dmaengine.c54
-rw-r--r--sound/soc/samsung/i2s.c109
-rw-r--r--sound/soc/samsung/idma.c1
-rw-r--r--sound/soc/samsung/odroidx2_max98090.c185
-rw-r--r--sound/soc/samsung/pcm.c29
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.c5
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.h4
-rw-r--r--sound/soc/samsung/s3c2412-i2s.c27
-rw-r--r--sound/soc/samsung/s3c24xx-i2s.c27
-rw-r--r--sound/soc/samsung/s3c24xx_uda134x.c141
-rw-r--r--sound/soc/samsung/smdk_wm8580pcm.c1
-rw-r--r--sound/soc/samsung/smdk_wm8994pcm.c1
-rw-r--r--sound/soc/samsung/spdif.c15
-rw-r--r--sound/soc/sh/Kconfig5
-rw-r--r--sound/soc/sh/rcar/Makefile3
-rw-r--r--sound/soc/sh/rcar/adg.c20
-rw-r--r--sound/soc/sh/rcar/core.c4
-rw-r--r--sound/soc/sh/rcar/gen.c12
-rw-r--r--sound/soc/sh/rcar/rsrc-card.c529
-rw-r--r--sound/soc/sh/rcar/src.c6
-rw-r--r--sound/soc/sh/rcar/ssi.c2
-rw-r--r--sound/soc/soc-ac97.c2
-rw-r--r--sound/soc/soc-compress.c9
-rw-r--r--sound/soc/soc-core.c20
-rw-r--r--sound/soc/soc-dapm.c94
-rw-r--r--sound/soc/soc-ops.c2
-rw-r--r--sound/soc/soc-pcm.c56
-rw-r--r--sound/soc/soc-topology.c115
-rw-r--r--sound/soc/soc-utils.c2
-rw-r--r--sound/soc/sti/sti_uniperif.c130
-rw-r--r--sound/soc/sti/uniperif.h28
-rw-r--r--sound/soc/sti/uniperif_player.c87
-rw-r--r--sound/soc/sti/uniperif_reader.c46
-rw-r--r--sound/soc/sunxi/Kconfig10
-rw-r--r--sound/soc/sunxi/Makefile2
-rw-r--r--sound/soc/sunxi/sun4i-codec.c60
-rw-r--r--sound/soc/sunxi/sun4i-i2s.c701
-rw-r--r--sound/soc/sunxi/sun4i-spdif.c17
-rw-r--r--sound/soc/tegra/Kconfig11
-rw-r--r--sound/soc/tegra/Makefile2
-rw-r--r--sound/soc/tegra/tegra_rt5640.c2
-rw-r--r--sound/soc/tegra/tegra_sgtl5000.c212
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c6
-rw-r--r--sound/sound_firmware.c77
-rw-r--r--sound/usb/caiaq/audio.c1
-rw-r--r--sound/usb/card.c2
-rw-r--r--sound/usb/card.h2
-rw-r--r--sound/usb/endpoint.c13
-rw-r--r--sound/usb/format.c4
-rw-r--r--sound/usb/line6/Kconfig5
-rw-r--r--sound/usb/line6/capture.c50
-rw-r--r--sound/usb/line6/driver.c269
-rw-r--r--sound/usb/line6/driver.h59
-rw-r--r--sound/usb/line6/midi.c2
-rw-r--r--sound/usb/line6/pcm.c86
-rw-r--r--sound/usb/line6/pcm.h20
-rw-r--r--sound/usb/line6/playback.c37
-rw-r--r--sound/usb/line6/pod.c24
-rw-r--r--sound/usb/line6/podhd.c300
-rw-r--r--sound/usb/line6/toneport.c6
-rw-r--r--sound/usb/line6/variax.c6
-rw-r--r--sound/usb/mixer_maps.c6
-rw-r--r--sound/usb/mixer_quirks.c22
-rw-r--r--sound/usb/quirks.c9
518 files changed, 34630 insertions, 7054 deletions
diff --git a/sound/Makefile b/sound/Makefile
index 77320709fd26..c41bdf5fdf24 100644
--- a/sound/Makefile
+++ b/sound/Makefile
@@ -2,7 +2,6 @@
#
obj-$(CONFIG_SOUND) += soundcore.o
-obj-$(CONFIG_SOUND_PRIME) += sound_firmware.o
obj-$(CONFIG_SOUND_PRIME) += oss/
obj-$(CONFIG_DMASOUND) += oss/
obj-$(CONFIG_SND) += core/ i2c/ drivers/ isa/ pci/ ppc/ arm/ sh/ synth/ usb/ \
diff --git a/sound/aoa/core/gpio-feature.c b/sound/aoa/core/gpio-feature.c
index f34153962d07..71960089e207 100644
--- a/sound/aoa/core/gpio-feature.c
+++ b/sound/aoa/core/gpio-feature.c
@@ -118,7 +118,7 @@ static void get_irq(struct device_node * np, int *irqptr)
if (np)
*irqptr = irq_of_parse_and_map(np, 0);
else
- *irqptr = NO_IRQ;
+ *irqptr = 0;
}
/* 0x4 is outenable, 0x1 is out, thus 4 or 5 */
@@ -336,7 +336,7 @@ static int ftr_set_notify(struct gpio_runtime *rt,
return -EINVAL;
}
- if (irq == NO_IRQ)
+ if (!irq)
return -ENODEV;
mutex_lock(&notif->mutex);
diff --git a/sound/aoa/fabrics/layout.c b/sound/aoa/fabrics/layout.c
index 8f71f7e4d966..a0c4a5de809c 100644
--- a/sound/aoa/fabrics/layout.c
+++ b/sound/aoa/fabrics/layout.c
@@ -112,6 +112,7 @@ MODULE_ALIAS("sound-layout-100");
MODULE_ALIAS("aoa-device-id-14");
MODULE_ALIAS("aoa-device-id-22");
+MODULE_ALIAS("aoa-device-id-31");
MODULE_ALIAS("aoa-device-id-35");
MODULE_ALIAS("aoa-device-id-44");
@@ -362,6 +363,13 @@ static struct layout layouts[] = {
.connections = tas_connections_nolineout,
},
},
+ /* PowerBook6,1 */
+ { .device_id = 31,
+ .codecs[0] = {
+ .name = "tas",
+ .connections = tas_connections_nolineout,
+ },
+ },
/* PowerBook6,5 */
{ .device_id = 44,
.codecs[0] = {
@@ -1161,12 +1169,7 @@ static struct soundbus_driver aoa_soundbus_driver = {
static int __init aoa_fabric_layout_init(void)
{
- int err;
-
- err = soundbus_register_driver(&aoa_soundbus_driver);
- if (err)
- return err;
- return 0;
+ return soundbus_register_driver(&aoa_soundbus_driver);
}
static void __exit aoa_fabric_layout_exit(void)
diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c
index 1cbf210080a1..000b58522106 100644
--- a/sound/aoa/soundbus/i2sbus/core.c
+++ b/sound/aoa/soundbus/i2sbus/core.c
@@ -197,7 +197,7 @@ static int i2sbus_add_dev(struct macio_dev *macio,
* so restrict to those we do handle for now.
*/
if (id && (*id == 22 || *id == 14 || *id == 35 ||
- *id == 44)) {
+ *id == 31 || *id == 44)) {
snprintf(dev->sound.modalias, 32,
"aoa-device-id-%d", *id);
ok = 1;
diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig
index e0406211716b..65171f6657a2 100644
--- a/sound/arm/Kconfig
+++ b/sound/arm/Kconfig
@@ -9,14 +9,6 @@ menuconfig SND_ARM
Drivers that are implemented on ASoC can be found in
"ALSA for SoC audio support" section.
-config SND_PXA2XX_LIB
- tristate
- select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97
- select SND_DMAENGINE_PCM
-
-config SND_PXA2XX_LIB_AC97
- bool
-
if SND_ARM
config SND_ARMAACI
@@ -42,3 +34,10 @@ config SND_PXA2XX_AC97
endif # SND_ARM
+config SND_PXA2XX_LIB
+ tristate
+ select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97
+ select SND_DMAENGINE_PCM
+
+config SND_PXA2XX_LIB_AC97
+ bool
diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c
index 9b3334be9df2..fec1dfdb14ad 100644
--- a/sound/core/compress_offload.c
+++ b/sound/core/compress_offload.c
@@ -67,6 +67,8 @@ struct snd_compr_file {
struct snd_compr_stream stream;
};
+static void error_delayed_work(struct work_struct *work);
+
/*
* a note on stream states used:
* we use following states in the compressed core
@@ -123,6 +125,9 @@ static int snd_compr_open(struct inode *inode, struct file *f)
snd_card_unref(compr->card);
return -ENOMEM;
}
+
+ INIT_DELAYED_WORK(&data->stream.error_work, error_delayed_work);
+
data->stream.ops = compr->ops;
data->stream.direction = dirn;
data->stream.private_data = compr->private_data;
@@ -153,6 +158,8 @@ static int snd_compr_free(struct inode *inode, struct file *f)
struct snd_compr_file *data = f->private_data;
struct snd_compr_runtime *runtime = data->stream.runtime;
+ cancel_delayed_work_sync(&data->stream.error_work);
+
switch (runtime->state) {
case SNDRV_PCM_STATE_RUNNING:
case SNDRV_PCM_STATE_DRAINING:
@@ -237,6 +244,15 @@ snd_compr_ioctl_avail(struct snd_compr_stream *stream, unsigned long arg)
avail = snd_compr_calc_avail(stream, &ioctl_avail);
ioctl_avail.avail = avail;
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ return -EBADFD;
+ case SNDRV_PCM_STATE_XRUN:
+ return -EPIPE;
+ default:
+ break;
+ }
+
if (copy_to_user((__u64 __user *)arg,
&ioctl_avail, sizeof(ioctl_avail)))
return -EFAULT;
@@ -346,11 +362,13 @@ static ssize_t snd_compr_read(struct file *f, char __user *buf,
switch (stream->runtime->state) {
case SNDRV_PCM_STATE_OPEN:
case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_XRUN:
case SNDRV_PCM_STATE_SUSPENDED:
case SNDRV_PCM_STATE_DISCONNECTED:
retval = -EBADFD;
goto out;
+ case SNDRV_PCM_STATE_XRUN:
+ retval = -EPIPE;
+ goto out;
}
avail = snd_compr_get_avail(stream);
@@ -399,10 +417,16 @@ static unsigned int snd_compr_poll(struct file *f, poll_table *wait)
stream = &data->stream;
mutex_lock(&stream->device->lock);
- if (stream->runtime->state == SNDRV_PCM_STATE_OPEN) {
+
+ switch (stream->runtime->state) {
+ case SNDRV_PCM_STATE_OPEN:
+ case SNDRV_PCM_STATE_XRUN:
retval = snd_compr_get_poll(stream) | POLLERR;
goto out;
+ default:
+ break;
}
+
poll_wait(f, &stream->runtime->sleep, wait);
avail = snd_compr_get_avail(stream);
@@ -529,13 +553,9 @@ snd_compr_set_params(struct snd_compr_stream *stream, unsigned long arg)
* we should allow parameter change only when stream has been
* opened not in other cases
*/
- params = kmalloc(sizeof(*params), GFP_KERNEL);
- if (!params)
- return -ENOMEM;
- if (copy_from_user(params, (void __user *)arg, sizeof(*params))) {
- retval = -EFAULT;
- goto out;
- }
+ params = memdup_user((void __user *)arg, sizeof(*params));
+ if (IS_ERR(params))
+ return PTR_ERR(params);
retval = snd_compress_check_input(params);
if (retval)
@@ -697,6 +717,45 @@ static int snd_compr_stop(struct snd_compr_stream *stream)
return retval;
}
+static void error_delayed_work(struct work_struct *work)
+{
+ struct snd_compr_stream *stream;
+
+ stream = container_of(work, struct snd_compr_stream, error_work.work);
+
+ mutex_lock(&stream->device->lock);
+
+ stream->ops->trigger(stream, SNDRV_PCM_TRIGGER_STOP);
+ wake_up(&stream->runtime->sleep);
+
+ mutex_unlock(&stream->device->lock);
+}
+
+/*
+ * snd_compr_stop_error: Report a fatal error on a stream
+ * @stream: pointer to stream
+ * @state: state to transition the stream to
+ *
+ * Stop the stream and set its state.
+ *
+ * Should be called with compressed device lock held.
+ */
+int snd_compr_stop_error(struct snd_compr_stream *stream,
+ snd_pcm_state_t state)
+{
+ if (stream->runtime->state == state)
+ return 0;
+
+ stream->runtime->state = state;
+
+ pr_debug("Changing state to: %d\n", state);
+
+ queue_delayed_work(system_power_efficient_wq, &stream->error_work, 0);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_compr_stop_error);
+
static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
{
int ret;
@@ -721,7 +780,7 @@ static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
ret = wait_event_interruptible(stream->runtime->sleep,
(stream->runtime->state != SNDRV_PCM_STATE_DRAINING));
if (ret == -ERESTARTSYS)
- pr_debug("wait aborted by a signal");
+ pr_debug("wait aborted by a signal\n");
else if (ret)
pr_debug("wait for drain failed with %d\n", ret);
@@ -903,7 +962,7 @@ static int snd_compress_dev_register(struct snd_device *device)
compr->card, compr->device,
&snd_compr_file_ops, compr, &compr->dev);
if (ret < 0) {
- pr_err("snd_register_device failed\n %d", ret);
+ pr_err("snd_register_device failed %d\n", ret);
return ret;
}
return ret;
diff --git a/sound/core/control.c b/sound/core/control.c
index a85d45595d02..fb096cb20a80 100644
--- a/sound/core/control.c
+++ b/sound/core/control.c
@@ -160,6 +160,8 @@ void snd_ctl_notify(struct snd_card *card, unsigned int mask,
if (snd_BUG_ON(!card || !id))
return;
+ if (card->shutdown)
+ return;
read_lock(&card->ctl_files_rwlock);
#if IS_ENABLED(CONFIG_SND_MIXER_OSS)
card->mixer_oss_change_count++;
@@ -805,6 +807,36 @@ static int snd_ctl_elem_list(struct snd_card *card,
return 0;
}
+static bool validate_element_member_dimension(struct snd_ctl_elem_info *info)
+{
+ unsigned int members;
+ unsigned int i;
+
+ if (info->dimen.d[0] == 0)
+ return true;
+
+ members = 1;
+ for (i = 0; i < ARRAY_SIZE(info->dimen.d); ++i) {
+ if (info->dimen.d[i] == 0)
+ break;
+ members *= info->dimen.d[i];
+
+ /*
+ * info->count should be validated in advance, to guarantee
+ * calculation soundness.
+ */
+ if (members > info->count)
+ return false;
+ }
+
+ for (++i; i < ARRAY_SIZE(info->dimen.d); ++i) {
+ if (info->dimen.d[i] > 0)
+ return false;
+ }
+
+ return members == info->count;
+}
+
static int snd_ctl_elem_info(struct snd_ctl_file *ctl,
struct snd_ctl_elem_info *info)
{
@@ -1272,6 +1304,8 @@ static int snd_ctl_elem_add(struct snd_ctl_file *file,
if (info->count < 1 ||
info->count > max_value_counts[info->type])
return -EINVAL;
+ if (!validate_element_member_dimension(info))
+ return -EINVAL;
private_size = value_sizes[info->type] * info->count;
/*
diff --git a/sound/core/pcm.c b/sound/core/pcm.c
index 308c9ecf73db..8e980aa678d0 100644
--- a/sound/core/pcm.c
+++ b/sound/core/pcm.c
@@ -849,6 +849,14 @@ int snd_pcm_new_internal(struct snd_card *card, const char *id, int device,
}
EXPORT_SYMBOL(snd_pcm_new_internal);
+static void free_chmap(struct snd_pcm_str *pstr)
+{
+ if (pstr->chmap_kctl) {
+ snd_ctl_remove(pstr->pcm->card, pstr->chmap_kctl);
+ pstr->chmap_kctl = NULL;
+ }
+}
+
static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
{
struct snd_pcm_substream *substream, *substream_next;
@@ -871,6 +879,7 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
kfree(setup);
}
#endif
+ free_chmap(pstr);
if (pstr->substream_count)
put_device(&pstr->dev);
}
@@ -1135,10 +1144,7 @@ static int snd_pcm_dev_disconnect(struct snd_device *device)
for (cidx = 0; cidx < 2; cidx++) {
if (!pcm->internal)
snd_unregister_device(&pcm->streams[cidx].dev);
- if (pcm->streams[cidx].chmap_kctl) {
- snd_ctl_remove(pcm->card, pcm->streams[cidx].chmap_kctl);
- pcm->streams[cidx].chmap_kctl = NULL;
- }
+ free_chmap(&pcm->streams[cidx]);
}
mutex_unlock(&pcm->open_mutex);
mutex_unlock(&register_mutex);
diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c
index c61fd50f771f..9d33c1e85c79 100644
--- a/sound/core/pcm_native.c
+++ b/sound/core/pcm_native.c
@@ -2637,9 +2637,11 @@ static int snd_pcm_hwsync(struct snd_pcm_substream *substream)
break;
/* Fall through */
case SNDRV_PCM_STATE_PREPARED:
- case SNDRV_PCM_STATE_SUSPENDED:
err = 0;
break;
+ case SNDRV_PCM_STATE_SUSPENDED:
+ err = -ESTRPIPE;
+ break;
case SNDRV_PCM_STATE_XRUN:
err = -EPIPE;
break;
diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c
index 795437b10082..b450a27588c8 100644
--- a/sound/core/rawmidi.c
+++ b/sound/core/rawmidi.c
@@ -1633,11 +1633,13 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
return -EBUSY;
}
list_add_tail(&rmidi->list, &snd_rawmidi_devices);
+ mutex_unlock(&register_mutex);
err = snd_register_device(SNDRV_DEVICE_TYPE_RAWMIDI,
rmidi->card, rmidi->device,
&snd_rawmidi_f_ops, rmidi, &rmidi->dev);
if (err < 0) {
rmidi_err(rmidi, "unable to register\n");
+ mutex_lock(&register_mutex);
list_del(&rmidi->list);
mutex_unlock(&register_mutex);
return err;
@@ -1645,6 +1647,7 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
if (rmidi->ops && rmidi->ops->dev_register &&
(err = rmidi->ops->dev_register(rmidi)) < 0) {
snd_unregister_device(&rmidi->dev);
+ mutex_lock(&register_mutex);
list_del(&rmidi->list);
mutex_unlock(&register_mutex);
return err;
@@ -1677,7 +1680,6 @@ static int snd_rawmidi_dev_register(struct snd_device *device)
}
}
#endif /* CONFIG_SND_OSSEMUL */
- mutex_unlock(&register_mutex);
sprintf(name, "midi%d", rmidi->device);
entry = snd_info_create_card_entry(rmidi->card, name, rmidi->card->proc_root);
if (entry) {
diff --git a/sound/core/seq/oss/seq_oss_synth.c b/sound/core/seq/oss/seq_oss_synth.c
index b16dbef04174..cd0e0ebbfdb1 100644
--- a/sound/core/seq/oss/seq_oss_synth.c
+++ b/sound/core/seq/oss/seq_oss_synth.c
@@ -70,11 +70,11 @@ struct seq_oss_synth {
static int max_synth_devs;
static struct seq_oss_synth *synth_devs[SNDRV_SEQ_OSS_MAX_SYNTH_DEVS];
static struct seq_oss_synth midi_synth_dev = {
- -1, /* seq_device */
- SYNTH_TYPE_MIDI, /* synth_type */
- 0, /* synth_subtype */
- 16, /* nr_voices */
- "MIDI", /* name */
+ .seq_device = -1,
+ .synth_type = SYNTH_TYPE_MIDI,
+ .synth_subtype = 0,
+ .nr_voices = 16,
+ .name = "MIDI",
};
static DEFINE_SPINLOCK(register_lock);
diff --git a/sound/core/seq/seq_clientmgr.c b/sound/core/seq/seq_clientmgr.c
index d6d9419d8bac..4c935202ce23 100644
--- a/sound/core/seq/seq_clientmgr.c
+++ b/sound/core/seq/seq_clientmgr.c
@@ -87,21 +87,6 @@ static int snd_seq_deliver_single_event(struct snd_seq_client *client,
/*
*/
-
-static inline mm_segment_t snd_enter_user(void)
-{
- mm_segment_t fs = get_fs();
- set_fs(get_ds());
- return fs;
-}
-
-static inline void snd_leave_user(mm_segment_t fs)
-{
- set_fs(fs);
-}
-
-/*
- */
static inline unsigned short snd_seq_file_flags(struct file *file)
{
switch (file->f_mode & (FMODE_READ | FMODE_WRITE)) {
@@ -1128,59 +1113,69 @@ static unsigned int snd_seq_poll(struct file *file, poll_table * wait)
/*-----------------------------------------------------*/
+static int snd_seq_ioctl_pversion(struct snd_seq_client *client, void *arg)
+{
+ int *pversion = arg;
+
+ *pversion = SNDRV_SEQ_VERSION;
+ return 0;
+}
+
+static int snd_seq_ioctl_client_id(struct snd_seq_client *client, void *arg)
+{
+ int *client_id = arg;
+
+ *client_id = client->number;
+ return 0;
+}
/* SYSTEM_INFO ioctl() */
-static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void __user *arg)
+static int snd_seq_ioctl_system_info(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_system_info info;
+ struct snd_seq_system_info *info = arg;
- memset(&info, 0, sizeof(info));
+ memset(info, 0, sizeof(*info));
/* fill the info fields */
- info.queues = SNDRV_SEQ_MAX_QUEUES;
- info.clients = SNDRV_SEQ_MAX_CLIENTS;
- info.ports = SNDRV_SEQ_MAX_PORTS;
- info.channels = 256; /* fixed limit */
- info.cur_clients = client_usage.cur;
- info.cur_queues = snd_seq_queue_get_cur_queues();
-
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
+ info->queues = SNDRV_SEQ_MAX_QUEUES;
+ info->clients = SNDRV_SEQ_MAX_CLIENTS;
+ info->ports = SNDRV_SEQ_MAX_PORTS;
+ info->channels = 256; /* fixed limit */
+ info->cur_clients = client_usage.cur;
+ info->cur_queues = snd_seq_queue_get_cur_queues();
+
return 0;
}
/* RUNNING_MODE ioctl() */
-static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void __user *arg)
+static int snd_seq_ioctl_running_mode(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_running_info info;
+ struct snd_seq_running_info *info = arg;
struct snd_seq_client *cptr;
int err = 0;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
/* requested client number */
- cptr = snd_seq_client_use_ptr(info.client);
+ cptr = snd_seq_client_use_ptr(info->client);
if (cptr == NULL)
return -ENOENT; /* don't change !!! */
#ifdef SNDRV_BIG_ENDIAN
- if (! info.big_endian) {
+ if (!info->big_endian) {
err = -EINVAL;
goto __err;
}
#else
- if (info.big_endian) {
+ if (info->big_endian) {
err = -EINVAL;
goto __err;
}
#endif
- if (info.cpu_mode > sizeof(long)) {
+ if (info->cpu_mode > sizeof(long)) {
err = -EINVAL;
goto __err;
}
- cptr->convert32 = (info.cpu_mode < sizeof(long));
+ cptr->convert32 = (info->cpu_mode < sizeof(long));
__err:
snd_seq_client_unlock(cptr);
return err;
@@ -1214,51 +1209,43 @@ static void get_client_info(struct snd_seq_client *cptr,
}
static int snd_seq_ioctl_get_client_info(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_client_info *client_info = arg;
struct snd_seq_client *cptr;
- struct snd_seq_client_info client_info;
-
- if (copy_from_user(&client_info, arg, sizeof(client_info)))
- return -EFAULT;
/* requested client number */
- cptr = snd_seq_client_use_ptr(client_info.client);
+ cptr = snd_seq_client_use_ptr(client_info->client);
if (cptr == NULL)
return -ENOENT; /* don't change !!! */
- get_client_info(cptr, &client_info);
+ get_client_info(cptr, client_info);
snd_seq_client_unlock(cptr);
- if (copy_to_user(arg, &client_info, sizeof(client_info)))
- return -EFAULT;
return 0;
}
/* CLIENT_INFO ioctl() */
static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_client_info client_info;
-
- if (copy_from_user(&client_info, arg, sizeof(client_info)))
- return -EFAULT;
+ struct snd_seq_client_info *client_info = arg;
/* it is not allowed to set the info fields for an another client */
- if (client->number != client_info.client)
+ if (client->number != client_info->client)
return -EPERM;
/* also client type must be set now */
- if (client->type != client_info.type)
+ if (client->type != client_info->type)
return -EINVAL;
/* fill the info fields */
- if (client_info.name[0])
- strlcpy(client->name, client_info.name, sizeof(client->name));
+ if (client_info->name[0])
+ strlcpy(client->name, client_info->name, sizeof(client->name));
- client->filter = client_info.filter;
- client->event_lost = client_info.event_lost;
- memcpy(client->event_filter, client_info.event_filter, 32);
+ client->filter = client_info->filter;
+ client->event_lost = client_info->event_lost;
+ memcpy(client->event_filter, client_info->event_filter, 32);
return 0;
}
@@ -1267,30 +1254,26 @@ static int snd_seq_ioctl_set_client_info(struct snd_seq_client *client,
/*
* CREATE PORT ioctl()
*/
-static int snd_seq_ioctl_create_port(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_create_port(struct snd_seq_client *client, void *arg)
{
+ struct snd_seq_port_info *info = arg;
struct snd_seq_client_port *port;
- struct snd_seq_port_info info;
struct snd_seq_port_callback *callback;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
/* it is not allowed to create the port for an another client */
- if (info.addr.client != client->number)
+ if (info->addr.client != client->number)
return -EPERM;
- port = snd_seq_create_port(client, (info.flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info.addr.port : -1);
+ port = snd_seq_create_port(client, (info->flags & SNDRV_SEQ_PORT_FLG_GIVEN_PORT) ? info->addr.port : -1);
if (port == NULL)
return -ENOMEM;
- if (client->type == USER_CLIENT && info.kernel) {
+ if (client->type == USER_CLIENT && info->kernel) {
snd_seq_delete_port(client, port->addr.port);
return -EINVAL;
}
if (client->type == KERNEL_CLIENT) {
- if ((callback = info.kernel) != NULL) {
+ if ((callback = info->kernel) != NULL) {
if (callback->owner)
port->owner = callback->owner;
port->private_data = callback->private_data;
@@ -1303,37 +1286,29 @@ static int snd_seq_ioctl_create_port(struct snd_seq_client *client,
}
}
- info.addr = port->addr;
+ info->addr = port->addr;
- snd_seq_set_port_info(port, &info);
+ snd_seq_set_port_info(port, info);
snd_seq_system_client_ev_port_start(port->addr.client, port->addr.port);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
-
return 0;
}
/*
* DELETE PORT ioctl()
*/
-static int snd_seq_ioctl_delete_port(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_delete_port(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_port_info info;
+ struct snd_seq_port_info *info = arg;
int err;
- /* set passed parameters */
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
/* it is not allowed to remove the port for an another client */
- if (info.addr.client != client->number)
+ if (info->addr.client != client->number)
return -EPERM;
- err = snd_seq_delete_port(client, info.addr.port);
+ err = snd_seq_delete_port(client, info->addr.port);
if (err >= 0)
- snd_seq_system_client_ev_port_exit(client->number, info.addr.port);
+ snd_seq_system_client_ev_port_exit(client->number, info->addr.port);
return err;
}
@@ -1341,32 +1316,27 @@ static int snd_seq_ioctl_delete_port(struct snd_seq_client *client,
/*
* GET_PORT_INFO ioctl() (on any client)
*/
-static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client, void *arg)
{
+ struct snd_seq_port_info *info = arg;
struct snd_seq_client *cptr;
struct snd_seq_client_port *port;
- struct snd_seq_port_info info;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
- cptr = snd_seq_client_use_ptr(info.addr.client);
+ cptr = snd_seq_client_use_ptr(info->addr.client);
if (cptr == NULL)
return -ENXIO;
- port = snd_seq_port_use_ptr(cptr, info.addr.port);
+ port = snd_seq_port_use_ptr(cptr, info->addr.port);
if (port == NULL) {
snd_seq_client_unlock(cptr);
return -ENOENT; /* don't change */
}
/* get port info */
- snd_seq_get_port_info(port, &info);
+ snd_seq_get_port_info(port, info);
snd_seq_port_unlock(port);
snd_seq_client_unlock(cptr);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
return 0;
}
@@ -1374,20 +1344,16 @@ static int snd_seq_ioctl_get_port_info(struct snd_seq_client *client,
/*
* SET_PORT_INFO ioctl() (only ports on this/own client)
*/
-static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_set_port_info(struct snd_seq_client *client, void *arg)
{
+ struct snd_seq_port_info *info = arg;
struct snd_seq_client_port *port;
- struct snd_seq_port_info info;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
- if (info.addr.client != client->number) /* only set our own ports ! */
+ if (info->addr.client != client->number) /* only set our own ports ! */
return -EPERM;
- port = snd_seq_port_use_ptr(client, info.addr.port);
+ port = snd_seq_port_use_ptr(client, info->addr.port);
if (port) {
- snd_seq_set_port_info(port, &info);
+ snd_seq_set_port_info(port, info);
snd_seq_port_unlock(port);
}
return 0;
@@ -1453,34 +1419,31 @@ int snd_seq_client_notify_subscription(int client, int port,
* add to port's subscription list IOCTL interface
*/
static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_port_subscribe *subs = arg;
int result = -EINVAL;
struct snd_seq_client *receiver = NULL, *sender = NULL;
struct snd_seq_client_port *sport = NULL, *dport = NULL;
- struct snd_seq_port_subscribe subs;
-
- if (copy_from_user(&subs, arg, sizeof(subs)))
- return -EFAULT;
- if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
+ if ((receiver = snd_seq_client_use_ptr(subs->dest.client)) == NULL)
goto __end;
- if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+ if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL)
goto __end;
- if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+ if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL)
goto __end;
- if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
+ if ((dport = snd_seq_port_use_ptr(receiver, subs->dest.port)) == NULL)
goto __end;
- result = check_subscription_permission(client, sport, dport, &subs);
+ result = check_subscription_permission(client, sport, dport, subs);
if (result < 0)
goto __end;
/* connect them */
- result = snd_seq_port_connect(client, sender, sport, receiver, dport, &subs);
+ result = snd_seq_port_connect(client, sender, sport, receiver, dport, subs);
if (! result) /* broadcast announce */
snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
- &subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
+ subs, SNDRV_SEQ_EVENT_PORT_SUBSCRIBED);
__end:
if (sport)
snd_seq_port_unlock(sport);
@@ -1498,33 +1461,30 @@ static int snd_seq_ioctl_subscribe_port(struct snd_seq_client *client,
* remove from port's subscription list
*/
static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_port_subscribe *subs = arg;
int result = -ENXIO;
struct snd_seq_client *receiver = NULL, *sender = NULL;
struct snd_seq_client_port *sport = NULL, *dport = NULL;
- struct snd_seq_port_subscribe subs;
-
- if (copy_from_user(&subs, arg, sizeof(subs)))
- return -EFAULT;
- if ((receiver = snd_seq_client_use_ptr(subs.dest.client)) == NULL)
+ if ((receiver = snd_seq_client_use_ptr(subs->dest.client)) == NULL)
goto __end;
- if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+ if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL)
goto __end;
- if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+ if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL)
goto __end;
- if ((dport = snd_seq_port_use_ptr(receiver, subs.dest.port)) == NULL)
+ if ((dport = snd_seq_port_use_ptr(receiver, subs->dest.port)) == NULL)
goto __end;
- result = check_subscription_permission(client, sport, dport, &subs);
+ result = check_subscription_permission(client, sport, dport, subs);
if (result < 0)
goto __end;
- result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, &subs);
+ result = snd_seq_port_disconnect(client, sender, sport, receiver, dport, subs);
if (! result) /* broadcast announce */
snd_seq_client_notify_subscription(SNDRV_SEQ_ADDRESS_SUBSCRIBERS, 0,
- &subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
+ subs, SNDRV_SEQ_EVENT_PORT_UNSUBSCRIBED);
__end:
if (sport)
snd_seq_port_unlock(sport);
@@ -1539,17 +1499,13 @@ static int snd_seq_ioctl_unsubscribe_port(struct snd_seq_client *client,
/* CREATE_QUEUE ioctl() */
-static int snd_seq_ioctl_create_queue(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_create_queue(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_queue_info info;
+ struct snd_seq_queue_info *info = arg;
int result;
struct snd_seq_queue *q;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- result = snd_seq_queue_alloc(client->number, info.locked, info.flags);
+ result = snd_seq_queue_alloc(client->number, info->locked, info->flags);
if (result < 0)
return result;
@@ -1557,181 +1513,150 @@ static int snd_seq_ioctl_create_queue(struct snd_seq_client *client,
if (q == NULL)
return -EINVAL;
- info.queue = q->queue;
- info.locked = q->locked;
- info.owner = q->owner;
+ info->queue = q->queue;
+ info->locked = q->locked;
+ info->owner = q->owner;
/* set queue name */
- if (! info.name[0])
- snprintf(info.name, sizeof(info.name), "Queue-%d", q->queue);
- strlcpy(q->name, info.name, sizeof(q->name));
+ if (!info->name[0])
+ snprintf(info->name, sizeof(info->name), "Queue-%d", q->queue);
+ strlcpy(q->name, info->name, sizeof(q->name));
queuefree(q);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
-
return 0;
}
/* DELETE_QUEUE ioctl() */
-static int snd_seq_ioctl_delete_queue(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_delete_queue(struct snd_seq_client *client, void *arg)
{
- struct snd_seq_queue_info info;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
+ struct snd_seq_queue_info *info = arg;
- return snd_seq_queue_delete(client->number, info.queue);
+ return snd_seq_queue_delete(client->number, info->queue);
}
/* GET_QUEUE_INFO ioctl() */
static int snd_seq_ioctl_get_queue_info(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_info info;
+ struct snd_seq_queue_info *info = arg;
struct snd_seq_queue *q;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- q = queueptr(info.queue);
+ q = queueptr(info->queue);
if (q == NULL)
return -EINVAL;
- memset(&info, 0, sizeof(info));
- info.queue = q->queue;
- info.owner = q->owner;
- info.locked = q->locked;
- strlcpy(info.name, q->name, sizeof(info.name));
+ memset(info, 0, sizeof(*info));
+ info->queue = q->queue;
+ info->owner = q->owner;
+ info->locked = q->locked;
+ strlcpy(info->name, q->name, sizeof(info->name));
queuefree(q);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
-
return 0;
}
/* SET_QUEUE_INFO ioctl() */
static int snd_seq_ioctl_set_queue_info(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_info info;
+ struct snd_seq_queue_info *info = arg;
struct snd_seq_queue *q;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- if (info.owner != client->number)
+ if (info->owner != client->number)
return -EINVAL;
/* change owner/locked permission */
- if (snd_seq_queue_check_access(info.queue, client->number)) {
- if (snd_seq_queue_set_owner(info.queue, client->number, info.locked) < 0)
+ if (snd_seq_queue_check_access(info->queue, client->number)) {
+ if (snd_seq_queue_set_owner(info->queue, client->number, info->locked) < 0)
return -EPERM;
- if (info.locked)
- snd_seq_queue_use(info.queue, client->number, 1);
+ if (info->locked)
+ snd_seq_queue_use(info->queue, client->number, 1);
} else {
return -EPERM;
}
- q = queueptr(info.queue);
+ q = queueptr(info->queue);
if (! q)
return -EINVAL;
if (q->owner != client->number) {
queuefree(q);
return -EPERM;
}
- strlcpy(q->name, info.name, sizeof(q->name));
+ strlcpy(q->name, info->name, sizeof(q->name));
queuefree(q);
return 0;
}
/* GET_NAMED_QUEUE ioctl() */
-static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client, void __user *arg)
+static int snd_seq_ioctl_get_named_queue(struct snd_seq_client *client,
+ void *arg)
{
- struct snd_seq_queue_info info;
+ struct snd_seq_queue_info *info = arg;
struct snd_seq_queue *q;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- q = snd_seq_queue_find_name(info.name);
+ q = snd_seq_queue_find_name(info->name);
if (q == NULL)
return -EINVAL;
- info.queue = q->queue;
- info.owner = q->owner;
- info.locked = q->locked;
+ info->queue = q->queue;
+ info->owner = q->owner;
+ info->locked = q->locked;
queuefree(q);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
-
return 0;
}
/* GET_QUEUE_STATUS ioctl() */
static int snd_seq_ioctl_get_queue_status(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_status status;
+ struct snd_seq_queue_status *status = arg;
struct snd_seq_queue *queue;
struct snd_seq_timer *tmr;
- if (copy_from_user(&status, arg, sizeof(status)))
- return -EFAULT;
-
- queue = queueptr(status.queue);
+ queue = queueptr(status->queue);
if (queue == NULL)
return -EINVAL;
- memset(&status, 0, sizeof(status));
- status.queue = queue->queue;
+ memset(status, 0, sizeof(*status));
+ status->queue = queue->queue;
tmr = queue->timer;
- status.events = queue->tickq->cells + queue->timeq->cells;
+ status->events = queue->tickq->cells + queue->timeq->cells;
- status.time = snd_seq_timer_get_cur_time(tmr);
- status.tick = snd_seq_timer_get_cur_tick(tmr);
+ status->time = snd_seq_timer_get_cur_time(tmr);
+ status->tick = snd_seq_timer_get_cur_tick(tmr);
- status.running = tmr->running;
+ status->running = tmr->running;
- status.flags = queue->flags;
+ status->flags = queue->flags;
queuefree(queue);
- if (copy_to_user(arg, &status, sizeof(status)))
- return -EFAULT;
return 0;
}
/* GET_QUEUE_TEMPO ioctl() */
static int snd_seq_ioctl_get_queue_tempo(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_tempo tempo;
+ struct snd_seq_queue_tempo *tempo = arg;
struct snd_seq_queue *queue;
struct snd_seq_timer *tmr;
- if (copy_from_user(&tempo, arg, sizeof(tempo)))
- return -EFAULT;
-
- queue = queueptr(tempo.queue);
+ queue = queueptr(tempo->queue);
if (queue == NULL)
return -EINVAL;
- memset(&tempo, 0, sizeof(tempo));
- tempo.queue = queue->queue;
+ memset(tempo, 0, sizeof(*tempo));
+ tempo->queue = queue->queue;
tmr = queue->timer;
- tempo.tempo = tmr->tempo;
- tempo.ppq = tmr->ppq;
- tempo.skew_value = tmr->skew;
- tempo.skew_base = tmr->skew_base;
+ tempo->tempo = tmr->tempo;
+ tempo->ppq = tmr->ppq;
+ tempo->skew_value = tmr->skew;
+ tempo->skew_base = tmr->skew_base;
queuefree(queue);
- if (copy_to_user(arg, &tempo, sizeof(tempo)))
- return -EFAULT;
return 0;
}
@@ -1747,31 +1672,25 @@ int snd_seq_set_queue_tempo(int client, struct snd_seq_queue_tempo *tempo)
EXPORT_SYMBOL(snd_seq_set_queue_tempo);
static int snd_seq_ioctl_set_queue_tempo(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_queue_tempo *tempo = arg;
int result;
- struct snd_seq_queue_tempo tempo;
-
- if (copy_from_user(&tempo, arg, sizeof(tempo)))
- return -EFAULT;
- result = snd_seq_set_queue_tempo(client->number, &tempo);
+ result = snd_seq_set_queue_tempo(client->number, tempo);
return result < 0 ? result : 0;
}
/* GET_QUEUE_TIMER ioctl() */
static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_timer timer;
+ struct snd_seq_queue_timer *timer = arg;
struct snd_seq_queue *queue;
struct snd_seq_timer *tmr;
- if (copy_from_user(&timer, arg, sizeof(timer)))
- return -EFAULT;
-
- queue = queueptr(timer.queue);
+ queue = queueptr(timer->queue);
if (queue == NULL)
return -EINVAL;
@@ -1780,41 +1699,36 @@ static int snd_seq_ioctl_get_queue_timer(struct snd_seq_client *client,
return -ERESTARTSYS;
}
tmr = queue->timer;
- memset(&timer, 0, sizeof(timer));
- timer.queue = queue->queue;
+ memset(timer, 0, sizeof(*timer));
+ timer->queue = queue->queue;
- timer.type = tmr->type;
+ timer->type = tmr->type;
if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
- timer.u.alsa.id = tmr->alsa_id;
- timer.u.alsa.resolution = tmr->preferred_resolution;
+ timer->u.alsa.id = tmr->alsa_id;
+ timer->u.alsa.resolution = tmr->preferred_resolution;
}
mutex_unlock(&queue->timer_mutex);
queuefree(queue);
- if (copy_to_user(arg, &timer, sizeof(timer)))
- return -EFAULT;
return 0;
}
/* SET_QUEUE_TIMER ioctl() */
static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_queue_timer *timer = arg;
int result = 0;
- struct snd_seq_queue_timer timer;
-
- if (copy_from_user(&timer, arg, sizeof(timer)))
- return -EFAULT;
- if (timer.type != SNDRV_SEQ_TIMER_ALSA)
+ if (timer->type != SNDRV_SEQ_TIMER_ALSA)
return -EINVAL;
- if (snd_seq_queue_check_access(timer.queue, client->number)) {
+ if (snd_seq_queue_check_access(timer->queue, client->number)) {
struct snd_seq_queue *q;
struct snd_seq_timer *tmr;
- q = queueptr(timer.queue);
+ q = queueptr(timer->queue);
if (q == NULL)
return -ENXIO;
if (mutex_lock_interruptible(&q->timer_mutex)) {
@@ -1822,13 +1736,13 @@ static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client,
return -ERESTARTSYS;
}
tmr = q->timer;
- snd_seq_queue_timer_close(timer.queue);
- tmr->type = timer.type;
+ snd_seq_queue_timer_close(timer->queue);
+ tmr->type = timer->type;
if (tmr->type == SNDRV_SEQ_TIMER_ALSA) {
- tmr->alsa_id = timer.u.alsa.id;
- tmr->preferred_resolution = timer.u.alsa.resolution;
+ tmr->alsa_id = timer->u.alsa.id;
+ tmr->preferred_resolution = timer->u.alsa.resolution;
}
- result = snd_seq_queue_timer_open(timer.queue);
+ result = snd_seq_queue_timer_open(timer->queue);
mutex_unlock(&q->timer_mutex);
queuefree(q);
} else {
@@ -1841,38 +1755,30 @@ static int snd_seq_ioctl_set_queue_timer(struct snd_seq_client *client,
/* GET_QUEUE_CLIENT ioctl() */
static int snd_seq_ioctl_get_queue_client(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_queue_client info;
+ struct snd_seq_queue_client *info = arg;
int used;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- used = snd_seq_queue_is_used(info.queue, client->number);
+ used = snd_seq_queue_is_used(info->queue, client->number);
if (used < 0)
return -EINVAL;
- info.used = used;
- info.client = client->number;
+ info->used = used;
+ info->client = client->number;
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
return 0;
}
/* SET_QUEUE_CLIENT ioctl() */
static int snd_seq_ioctl_set_queue_client(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_queue_client *info = arg;
int err;
- struct snd_seq_queue_client info;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
- if (info.used >= 0) {
- err = snd_seq_queue_use(info.queue, client->number, info.used);
+ if (info->used >= 0) {
+ err = snd_seq_queue_use(info->queue, client->number, info->used);
if (err < 0)
return err;
}
@@ -1883,78 +1789,70 @@ static int snd_seq_ioctl_set_queue_client(struct snd_seq_client *client,
/* GET_CLIENT_POOL ioctl() */
static int snd_seq_ioctl_get_client_pool(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_client_pool info;
+ struct snd_seq_client_pool *info = arg;
struct snd_seq_client *cptr;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- cptr = snd_seq_client_use_ptr(info.client);
+ cptr = snd_seq_client_use_ptr(info->client);
if (cptr == NULL)
return -ENOENT;
- memset(&info, 0, sizeof(info));
- info.client = cptr->number;
- info.output_pool = cptr->pool->size;
- info.output_room = cptr->pool->room;
- info.output_free = info.output_pool;
- info.output_free = snd_seq_unused_cells(cptr->pool);
+ memset(info, 0, sizeof(*info));
+ info->client = cptr->number;
+ info->output_pool = cptr->pool->size;
+ info->output_room = cptr->pool->room;
+ info->output_free = info->output_pool;
+ info->output_free = snd_seq_unused_cells(cptr->pool);
if (cptr->type == USER_CLIENT) {
- info.input_pool = cptr->data.user.fifo_pool_size;
- info.input_free = info.input_pool;
+ info->input_pool = cptr->data.user.fifo_pool_size;
+ info->input_free = info->input_pool;
if (cptr->data.user.fifo)
- info.input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool);
+ info->input_free = snd_seq_unused_cells(cptr->data.user.fifo->pool);
} else {
- info.input_pool = 0;
- info.input_free = 0;
+ info->input_pool = 0;
+ info->input_free = 0;
}
snd_seq_client_unlock(cptr);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
return 0;
}
/* SET_CLIENT_POOL ioctl() */
static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_client_pool info;
+ struct snd_seq_client_pool *info = arg;
int rc;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
-
- if (client->number != info.client)
+ if (client->number != info->client)
return -EINVAL; /* can't change other clients */
- if (info.output_pool >= 1 && info.output_pool <= SNDRV_SEQ_MAX_EVENTS &&
+ if (info->output_pool >= 1 && info->output_pool <= SNDRV_SEQ_MAX_EVENTS &&
(! snd_seq_write_pool_allocated(client) ||
- info.output_pool != client->pool->size)) {
+ info->output_pool != client->pool->size)) {
if (snd_seq_write_pool_allocated(client)) {
/* remove all existing cells */
snd_seq_queue_client_leave_cells(client->number);
snd_seq_pool_done(client->pool);
}
- client->pool->size = info.output_pool;
+ client->pool->size = info->output_pool;
rc = snd_seq_pool_init(client->pool);
if (rc < 0)
return rc;
}
if (client->type == USER_CLIENT && client->data.user.fifo != NULL &&
- info.input_pool >= 1 &&
- info.input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS &&
- info.input_pool != client->data.user.fifo_pool_size) {
+ info->input_pool >= 1 &&
+ info->input_pool <= SNDRV_SEQ_MAX_CLIENT_EVENTS &&
+ info->input_pool != client->data.user.fifo_pool_size) {
/* change pool size */
- rc = snd_seq_fifo_resize(client->data.user.fifo, info.input_pool);
+ rc = snd_seq_fifo_resize(client->data.user.fifo, info->input_pool);
if (rc < 0)
return rc;
- client->data.user.fifo_pool_size = info.input_pool;
+ client->data.user.fifo_pool_size = info->input_pool;
}
- if (info.output_room >= 1 &&
- info.output_room <= client->pool->size) {
- client->pool->room = info.output_room;
+ if (info->output_room >= 1 &&
+ info->output_room <= client->pool->size) {
+ client->pool->room = info->output_room;
}
return snd_seq_ioctl_get_client_pool(client, arg);
@@ -1963,17 +1861,14 @@ static int snd_seq_ioctl_set_client_pool(struct snd_seq_client *client,
/* REMOVE_EVENTS ioctl() */
static int snd_seq_ioctl_remove_events(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
- struct snd_seq_remove_events info;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
+ struct snd_seq_remove_events *info = arg;
/*
* Input mostly not implemented XXX.
*/
- if (info.remove_mode & SNDRV_SEQ_REMOVE_INPUT) {
+ if (info->remove_mode & SNDRV_SEQ_REMOVE_INPUT) {
/*
* No restrictions so for a user client we can clear
* the whole fifo
@@ -1982,8 +1877,8 @@ static int snd_seq_ioctl_remove_events(struct snd_seq_client *client,
snd_seq_fifo_clear(client->data.user.fifo);
}
- if (info.remove_mode & SNDRV_SEQ_REMOVE_OUTPUT)
- snd_seq_queue_remove_cells(client->number, &info);
+ if (info->remove_mode & SNDRV_SEQ_REMOVE_OUTPUT)
+ snd_seq_queue_remove_cells(client->number, info);
return 0;
}
@@ -1993,26 +1888,23 @@ static int snd_seq_ioctl_remove_events(struct snd_seq_client *client,
* get subscription info
*/
static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_port_subscribe *subs = arg;
int result;
struct snd_seq_client *sender = NULL;
struct snd_seq_client_port *sport = NULL;
- struct snd_seq_port_subscribe subs;
struct snd_seq_subscribers *p;
- if (copy_from_user(&subs, arg, sizeof(subs)))
- return -EFAULT;
-
result = -EINVAL;
- if ((sender = snd_seq_client_use_ptr(subs.sender.client)) == NULL)
+ if ((sender = snd_seq_client_use_ptr(subs->sender.client)) == NULL)
goto __end;
- if ((sport = snd_seq_port_use_ptr(sender, subs.sender.port)) == NULL)
+ if ((sport = snd_seq_port_use_ptr(sender, subs->sender.port)) == NULL)
goto __end;
- p = snd_seq_port_get_subscription(&sport->c_src, &subs.dest);
+ p = snd_seq_port_get_subscription(&sport->c_src, &subs->dest);
if (p) {
result = 0;
- subs = p->info;
+ *subs = p->info;
} else
result = -ENOENT;
@@ -2021,10 +1913,7 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
snd_seq_port_unlock(sport);
if (sender)
snd_seq_client_unlock(sender);
- if (result >= 0) {
- if (copy_to_user(arg, &subs, sizeof(subs)))
- return -EFAULT;
- }
+
return result;
}
@@ -2032,26 +1921,22 @@ static int snd_seq_ioctl_get_subscription(struct snd_seq_client *client,
/*
* get subscription info - check only its presence
*/
-static int snd_seq_ioctl_query_subs(struct snd_seq_client *client,
- void __user *arg)
+static int snd_seq_ioctl_query_subs(struct snd_seq_client *client, void *arg)
{
+ struct snd_seq_query_subs *subs = arg;
int result = -ENXIO;
struct snd_seq_client *cptr = NULL;
struct snd_seq_client_port *port = NULL;
- struct snd_seq_query_subs subs;
struct snd_seq_port_subs_info *group;
struct list_head *p;
int i;
- if (copy_from_user(&subs, arg, sizeof(subs)))
- return -EFAULT;
-
- if ((cptr = snd_seq_client_use_ptr(subs.root.client)) == NULL)
+ if ((cptr = snd_seq_client_use_ptr(subs->root.client)) == NULL)
goto __end;
- if ((port = snd_seq_port_use_ptr(cptr, subs.root.port)) == NULL)
+ if ((port = snd_seq_port_use_ptr(cptr, subs->root.port)) == NULL)
goto __end;
- switch (subs.type) {
+ switch (subs->type) {
case SNDRV_SEQ_QUERY_SUBS_READ:
group = &port->c_src;
break;
@@ -2064,22 +1949,22 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client,
down_read(&group->list_mutex);
/* search for the subscriber */
- subs.num_subs = group->count;
+ subs->num_subs = group->count;
i = 0;
result = -ENOENT;
list_for_each(p, &group->list_head) {
- if (i++ == subs.index) {
+ if (i++ == subs->index) {
/* found! */
struct snd_seq_subscribers *s;
- if (subs.type == SNDRV_SEQ_QUERY_SUBS_READ) {
+ if (subs->type == SNDRV_SEQ_QUERY_SUBS_READ) {
s = list_entry(p, struct snd_seq_subscribers, src_list);
- subs.addr = s->info.dest;
+ subs->addr = s->info.dest;
} else {
s = list_entry(p, struct snd_seq_subscribers, dest_list);
- subs.addr = s->info.sender;
+ subs->addr = s->info.sender;
}
- subs.flags = s->info.flags;
- subs.queue = s->info.queue;
+ subs->flags = s->info.flags;
+ subs->queue = s->info.queue;
result = 0;
break;
}
@@ -2091,10 +1976,7 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client,
snd_seq_port_unlock(port);
if (cptr)
snd_seq_client_unlock(cptr);
- if (result >= 0) {
- if (copy_to_user(arg, &subs, sizeof(subs)))
- return -EFAULT;
- }
+
return result;
}
@@ -2103,31 +1985,26 @@ static int snd_seq_ioctl_query_subs(struct snd_seq_client *client,
* query next client
*/
static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_client_info *info = arg;
struct snd_seq_client *cptr = NULL;
- struct snd_seq_client_info info;
-
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
/* search for next client */
- info.client++;
- if (info.client < 0)
- info.client = 0;
- for (; info.client < SNDRV_SEQ_MAX_CLIENTS; info.client++) {
- cptr = snd_seq_client_use_ptr(info.client);
+ info->client++;
+ if (info->client < 0)
+ info->client = 0;
+ for (; info->client < SNDRV_SEQ_MAX_CLIENTS; info->client++) {
+ cptr = snd_seq_client_use_ptr(info->client);
if (cptr)
break; /* found */
}
if (cptr == NULL)
return -ENOENT;
- get_client_info(cptr, &info);
+ get_client_info(cptr, info);
snd_seq_client_unlock(cptr);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
return 0;
}
@@ -2135,43 +2012,41 @@ static int snd_seq_ioctl_query_next_client(struct snd_seq_client *client,
* query next port
*/
static int snd_seq_ioctl_query_next_port(struct snd_seq_client *client,
- void __user *arg)
+ void *arg)
{
+ struct snd_seq_port_info *info = arg;
struct snd_seq_client *cptr;
struct snd_seq_client_port *port = NULL;
- struct snd_seq_port_info info;
- if (copy_from_user(&info, arg, sizeof(info)))
- return -EFAULT;
- cptr = snd_seq_client_use_ptr(info.addr.client);
+ cptr = snd_seq_client_use_ptr(info->addr.client);
if (cptr == NULL)
return -ENXIO;
/* search for next port */
- info.addr.port++;
- port = snd_seq_port_query_nearest(cptr, &info);
+ info->addr.port++;
+ port = snd_seq_port_query_nearest(cptr, info);
if (port == NULL) {
snd_seq_client_unlock(cptr);
return -ENOENT;
}
/* get port info */
- info.addr = port->addr;
- snd_seq_get_port_info(port, &info);
+ info->addr = port->addr;
+ snd_seq_get_port_info(port, info);
snd_seq_port_unlock(port);
snd_seq_client_unlock(cptr);
- if (copy_to_user(arg, &info, sizeof(info)))
- return -EFAULT;
return 0;
}
/* -------------------------------------------------------- */
-static struct seq_ioctl_table {
+static const struct ioctl_handler {
unsigned int cmd;
- int (*func)(struct snd_seq_client *client, void __user * arg);
-} ioctl_tables[] = {
+ int (*func)(struct snd_seq_client *client, void *arg);
+} ioctl_handlers[] = {
+ { SNDRV_SEQ_IOCTL_PVERSION, snd_seq_ioctl_pversion },
+ { SNDRV_SEQ_IOCTL_CLIENT_ID, snd_seq_ioctl_client_id },
{ SNDRV_SEQ_IOCTL_SYSTEM_INFO, snd_seq_ioctl_system_info },
{ SNDRV_SEQ_IOCTL_RUNNING_MODE, snd_seq_ioctl_running_mode },
{ SNDRV_SEQ_IOCTL_GET_CLIENT_INFO, snd_seq_ioctl_get_client_info },
@@ -2204,40 +2079,65 @@ static struct seq_ioctl_table {
{ 0, NULL },
};
-static int snd_seq_do_ioctl(struct snd_seq_client *client, unsigned int cmd,
- void __user *arg)
+static long snd_seq_ioctl(struct file *file, unsigned int cmd,
+ unsigned long arg)
{
- struct seq_ioctl_table *p;
+ struct snd_seq_client *client = file->private_data;
+ /* To use kernel stack for ioctl data. */
+ union {
+ int pversion;
+ int client_id;
+ struct snd_seq_system_info system_info;
+ struct snd_seq_running_info running_info;
+ struct snd_seq_client_info client_info;
+ struct snd_seq_port_info port_info;
+ struct snd_seq_port_subscribe port_subscribe;
+ struct snd_seq_queue_info queue_info;
+ struct snd_seq_queue_status queue_status;
+ struct snd_seq_queue_tempo tempo;
+ struct snd_seq_queue_timer queue_timer;
+ struct snd_seq_queue_client queue_client;
+ struct snd_seq_client_pool client_pool;
+ struct snd_seq_remove_events remove_events;
+ struct snd_seq_query_subs query_subs;
+ } buf;
+ const struct ioctl_handler *handler;
+ unsigned long size;
+ int err;
- switch (cmd) {
- case SNDRV_SEQ_IOCTL_PVERSION:
- /* return sequencer version number */
- return put_user(SNDRV_SEQ_VERSION, (int __user *)arg) ? -EFAULT : 0;
- case SNDRV_SEQ_IOCTL_CLIENT_ID:
- /* return the id of this client */
- return put_user(client->number, (int __user *)arg) ? -EFAULT : 0;
- }
+ if (snd_BUG_ON(!client))
+ return -ENXIO;
- if (! arg)
- return -EFAULT;
- for (p = ioctl_tables; p->cmd; p++) {
- if (p->cmd == cmd)
- return p->func(client, arg);
+ for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
+ if (handler->cmd == cmd)
+ break;
}
- pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n",
- cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
- return -ENOTTY;
-}
+ if (handler->cmd == 0)
+ return -ENOTTY;
+ memset(&buf, 0, sizeof(buf));
-static long snd_seq_ioctl(struct file *file, unsigned int cmd, unsigned long arg)
-{
- struct snd_seq_client *client = file->private_data;
+ /*
+ * All of ioctl commands for ALSA sequencer get an argument of size
+ * within 13 bits. We can safely pick up the size from the command.
+ */
+ size = _IOC_SIZE(handler->cmd);
+ if (handler->cmd & IOC_IN) {
+ if (copy_from_user(&buf, (const void __user *)arg, size))
+ return -EFAULT;
+ }
- if (snd_BUG_ON(!client))
- return -ENXIO;
-
- return snd_seq_do_ioctl(client, cmd, (void __user *) arg);
+ err = handler->func(client, &buf);
+ if (err >= 0) {
+ /* Some commands includes a bug in 'dir' field. */
+ if (handler->cmd == SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT ||
+ handler->cmd == SNDRV_SEQ_IOCTL_SET_CLIENT_POOL ||
+ (handler->cmd & IOC_OUT))
+ if (copy_to_user((void __user *)arg, &buf, size))
+ return -EFAULT;
+ }
+
+ return err;
}
#ifdef CONFIG_COMPAT
@@ -2423,23 +2323,35 @@ int snd_seq_kernel_client_dispatch(int client, struct snd_seq_event * ev,
EXPORT_SYMBOL(snd_seq_kernel_client_dispatch);
-/*
- * exported, called by kernel clients to perform same functions as with
- * userland ioctl()
+/**
+ * snd_seq_kernel_client_ctl - operate a command for a client with data in
+ * kernel space.
+ * @clientid: A numerical ID for a client.
+ * @cmd: An ioctl(2) command for ALSA sequencer operation.
+ * @arg: A pointer to data in kernel space.
+ *
+ * Against its name, both kernel/application client can be handled by this
+ * kernel API. A pointer of 'arg' argument should be in kernel space.
+ *
+ * Return: 0 at success. Negative error code at failure.
*/
int snd_seq_kernel_client_ctl(int clientid, unsigned int cmd, void *arg)
{
+ const struct ioctl_handler *handler;
struct snd_seq_client *client;
- mm_segment_t fs;
- int result;
client = clientptr(clientid);
if (client == NULL)
return -ENXIO;
- fs = snd_enter_user();
- result = snd_seq_do_ioctl(client, cmd, (void __force __user *)arg);
- snd_leave_user(fs);
- return result;
+
+ for (handler = ioctl_handlers; handler->cmd > 0; ++handler) {
+ if (handler->cmd == cmd)
+ return handler->func(client, arg);
+ }
+
+ pr_debug("ALSA: seq unknown ioctl() 0x%x (type='%c', number=0x%02x)\n",
+ cmd, _IOC_TYPE(cmd), _IOC_NR(cmd));
+ return -ENOTTY;
}
EXPORT_SYMBOL(snd_seq_kernel_client_ctl);
diff --git a/sound/core/seq/seq_compat.c b/sound/core/seq/seq_compat.c
index 65175902a68a..fce5697e4261 100644
--- a/sound/core/seq/seq_compat.c
+++ b/sound/core/seq/seq_compat.c
@@ -47,7 +47,6 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned
{
int err = -EFAULT;
struct snd_seq_port_info *data;
- mm_segment_t fs;
data = kmalloc(sizeof(*data), GFP_KERNEL);
if (!data)
@@ -59,9 +58,7 @@ static int snd_seq_call_port_info_ioctl(struct snd_seq_client *client, unsigned
goto error;
data->kernel = NULL;
- fs = snd_enter_user();
- err = snd_seq_do_ioctl(client, cmd, data);
- snd_leave_user(fs);
+ err = snd_seq_kernel_client_ctl(client->number, cmd, &data);
if (err < 0)
goto error;
@@ -123,7 +120,7 @@ static long snd_seq_ioctl_compat(struct file *file, unsigned int cmd, unsigned l
case SNDRV_SEQ_IOCTL_GET_SUBSCRIPTION:
case SNDRV_SEQ_IOCTL_QUERY_NEXT_CLIENT:
case SNDRV_SEQ_IOCTL_RUNNING_MODE:
- return snd_seq_do_ioctl(client, cmd, argp);
+ return snd_seq_ioctl(file, cmd, arg);
case SNDRV_SEQ_IOCTL_CREATE_PORT32:
return snd_seq_call_port_info_ioctl(client, SNDRV_SEQ_IOCTL_CREATE_PORT, argp);
case SNDRV_SEQ_IOCTL_DELETE_PORT32:
diff --git a/sound/core/seq/seq_timer.c b/sound/core/seq/seq_timer.c
index 293104926098..dcc102813aef 100644
--- a/sound/core/seq/seq_timer.c
+++ b/sound/core/seq/seq_timer.c
@@ -165,7 +165,7 @@ static void snd_seq_timer_interrupt(struct snd_timer_instance *timeri,
snd_seq_timer_update_tick(&tmr->tick, resolution);
/* register actual time of this timer update */
- do_gettimeofday(&tmr->last_update);
+ ktime_get_ts64(&tmr->last_update);
spin_unlock_irqrestore(&tmr->lock, flags);
@@ -392,7 +392,7 @@ static int seq_timer_start(struct snd_seq_timer *tmr)
return -EINVAL;
snd_timer_start(tmr->timeri, tmr->ticks);
tmr->running = 1;
- do_gettimeofday(&tmr->last_update);
+ ktime_get_ts64(&tmr->last_update);
return 0;
}
@@ -420,7 +420,7 @@ static int seq_timer_continue(struct snd_seq_timer *tmr)
}
snd_timer_start(tmr->timeri, tmr->ticks);
tmr->running = 1;
- do_gettimeofday(&tmr->last_update);
+ ktime_get_ts64(&tmr->last_update);
return 0;
}
@@ -444,17 +444,12 @@ snd_seq_real_time_t snd_seq_timer_get_cur_time(struct snd_seq_timer *tmr)
spin_lock_irqsave(&tmr->lock, flags);
cur_time = tmr->cur_time;
if (tmr->running) {
- struct timeval tm;
- int usec;
- do_gettimeofday(&tm);
- usec = (int)(tm.tv_usec - tmr->last_update.tv_usec);
- if (usec < 0) {
- cur_time.tv_nsec += (1000000 + usec) * 1000;
- cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec - 1;
- } else {
- cur_time.tv_nsec += usec * 1000;
- cur_time.tv_sec += tm.tv_sec - tmr->last_update.tv_sec;
- }
+ struct timespec64 tm;
+
+ ktime_get_ts64(&tm);
+ tm = timespec64_sub(tm, tmr->last_update);
+ cur_time.tv_nsec = tm.tv_nsec;
+ cur_time.tv_sec = tm.tv_sec;
snd_seq_sanity_real_time(&cur_time);
}
spin_unlock_irqrestore(&tmr->lock, flags);
diff --git a/sound/core/seq/seq_timer.h b/sound/core/seq/seq_timer.h
index 88dfb71805ae..9506b661fe5b 100644
--- a/sound/core/seq/seq_timer.h
+++ b/sound/core/seq/seq_timer.h
@@ -52,7 +52,7 @@ struct snd_seq_timer {
unsigned int skew;
unsigned int skew_base;
- struct timeval last_update; /* time of last clock update, used for interpolation */
+ struct timespec64 last_update; /* time of last clock update, used for interpolation */
spinlock_t lock;
};
diff --git a/sound/core/timer.c b/sound/core/timer.c
index e722022d325d..fc144f43faa6 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -35,6 +35,9 @@
#include <sound/initval.h>
#include <linux/kmod.h>
+/* internal flags */
+#define SNDRV_TIMER_IFLG_PAUSED 0x00010000
+
#if IS_ENABLED(CONFIG_SND_HRTIMER)
#define DEFAULT_TIMER_LIMIT 4
#else
@@ -294,8 +297,21 @@ int snd_timer_open(struct snd_timer_instance **ti,
get_device(&timer->card->card_dev);
timeri->slave_class = tid->dev_sclass;
timeri->slave_id = slave_id;
- if (list_empty(&timer->open_list_head) && timer->hw.open)
- timer->hw.open(timer);
+
+ if (list_empty(&timer->open_list_head) && timer->hw.open) {
+ int err = timer->hw.open(timer);
+ if (err) {
+ kfree(timeri->owner);
+ kfree(timeri);
+
+ if (timer->card)
+ put_device(&timer->card->card_dev);
+ module_put(timer->module);
+ mutex_unlock(&register_mutex);
+ return err;
+ }
+ }
+
list_add_tail(&timeri->open_list, &timer->open_list_head);
snd_timer_check_master(timeri);
mutex_unlock(&register_mutex);
@@ -526,6 +542,10 @@ static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
}
}
timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
+ if (stop)
+ timeri->flags &= ~SNDRV_TIMER_IFLG_PAUSED;
+ else
+ timeri->flags |= SNDRV_TIMER_IFLG_PAUSED;
snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
SNDRV_TIMER_EVENT_CONTINUE);
unlock:
@@ -587,6 +607,10 @@ int snd_timer_stop(struct snd_timer_instance *timeri)
*/
int snd_timer_continue(struct snd_timer_instance *timeri)
{
+ /* timer can continue only after pause */
+ if (!(timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
+ return -EINVAL;
+
if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
return snd_timer_start_slave(timeri, false);
else
@@ -813,6 +837,7 @@ int snd_timer_new(struct snd_card *card, char *id, struct snd_timer_id *tid,
timer->tmr_subdevice = tid->subdevice;
if (id)
strlcpy(timer->id, id, sizeof(timer->id));
+ timer->sticks = 1;
INIT_LIST_HEAD(&timer->device_list);
INIT_LIST_HEAD(&timer->open_list_head);
INIT_LIST_HEAD(&timer->active_list_head);
@@ -1817,6 +1842,9 @@ static int snd_timer_user_continue(struct file *file)
tu = file->private_data;
if (!tu->timeri)
return -EBADFD;
+ /* start timer instead of continue if it's not used before */
+ if (!(tu->timeri->flags & SNDRV_TIMER_IFLG_PAUSED))
+ return snd_timer_user_start(file);
tu->timeri->lost = 0;
return (err = snd_timer_continue(tu->timeri)) < 0 ? err : 0;
}
@@ -1955,8 +1983,10 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
qhead = tu->qhead++;
tu->qhead %= tu->queue_size;
+ tu->qused--;
spin_unlock_irq(&tu->qlock);
+ mutex_lock(&tu->ioctl_lock);
if (tu->tread) {
if (copy_to_user(buffer, &tu->tqueue[qhead],
sizeof(struct snd_timer_tread)))
@@ -1966,9 +1996,9 @@ static ssize_t snd_timer_user_read(struct file *file, char __user *buffer,
sizeof(struct snd_timer_read)))
err = -EFAULT;
}
+ mutex_unlock(&tu->ioctl_lock);
spin_lock_irq(&tu->qlock);
- tu->qused--;
if (err < 0)
goto _error;
result += unit;
diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c
index c0f8f613f1f1..172dacd925f5 100644
--- a/sound/drivers/dummy.c
+++ b/sound/drivers/dummy.c
@@ -420,6 +420,7 @@ static int dummy_hrtimer_stop(struct snd_pcm_substream *substream)
static inline void dummy_hrtimer_sync(struct dummy_hrtimer_pcm *dpcm)
{
+ hrtimer_cancel(&dpcm->timer);
tasklet_kill(&dpcm->tasklet);
}
diff --git a/sound/firewire/bebob/Makefile b/sound/firewire/bebob/Makefile
index af7ed6643266..dd454867240d 100644
--- a/sound/firewire/bebob/Makefile
+++ b/sound/firewire/bebob/Makefile
@@ -1,4 +1,5 @@
snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
- bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \
- bebob_focusrite.o bebob_maudio.o bebob.o
+ bebob_pcm.o bebob_hwdep.o bebob_terratec.o \
+ bebob_yamaha_terratec.o bebob_focusrite.o bebob_maudio.o \
+ bebob.o
obj-$(CONFIG_SND_BEBOB) += snd-bebob.o
diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c
index f7e2cbd2a313..3469ac14c89c 100644
--- a/sound/firewire/bebob/bebob.c
+++ b/sound/firewire/bebob/bebob.c
@@ -458,17 +458,17 @@ static const struct ieee1394_device_id bebob_id_table[] = {
/* TerraTec Electronic GmbH, PHASE 88 Rack FW */
SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000003, &phase88_rack_spec),
/* TerraTec Electronic GmbH, PHASE 24 FW */
- SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000004, &phase24_series_spec),
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000004, &yamaha_terratec_spec),
/* TerraTec Electronic GmbH, Phase X24 FW */
- SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000007, &phase24_series_spec),
+ SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000007, &yamaha_terratec_spec),
/* TerraTec Electronic GmbH, EWS MIC2/MIC8 */
SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000005, &spec_normal),
/* Terratec Electronic GmbH, Aureon 7.1 Firewire */
SND_BEBOB_DEV_ENTRY(VEN_TERRATEC, 0x00000002, &spec_normal),
/* Yamaha, GO44 */
- SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000b, &yamaha_go_spec),
+ SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000b, &yamaha_terratec_spec),
/* YAMAHA, GO46 */
- SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000c, &yamaha_go_spec),
+ SND_BEBOB_DEV_ENTRY(VEN_YAMAHA, 0x0010000c, &yamaha_terratec_spec),
/* Focusrite, SaffirePro 26 I/O */
SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000003, &saffirepro_26_spec),
/* Focusrite, SaffirePro 10 I/O */
diff --git a/sound/firewire/bebob/bebob.h b/sound/firewire/bebob/bebob.h
index e7f1bb925b12..175da875162d 100644
--- a/sound/firewire/bebob/bebob.h
+++ b/sound/firewire/bebob/bebob.h
@@ -235,8 +235,7 @@ int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
/* model specific operations */
extern const struct snd_bebob_spec phase88_rack_spec;
-extern const struct snd_bebob_spec phase24_series_spec;
-extern const struct snd_bebob_spec yamaha_go_spec;
+extern const struct snd_bebob_spec yamaha_terratec_spec;
extern const struct snd_bebob_spec saffirepro_26_spec;
extern const struct snd_bebob_spec saffirepro_10_spec;
extern const struct snd_bebob_spec saffire_le_spec;
diff --git a/sound/firewire/bebob/bebob_terratec.c b/sound/firewire/bebob/bebob_terratec.c
index c38358b82ada..2fdaf93e7a8d 100644
--- a/sound/firewire/bebob/bebob_terratec.c
+++ b/sound/firewire/bebob/bebob_terratec.c
@@ -36,25 +36,6 @@ end:
return err;
}
-static enum snd_bebob_clock_type phase24_series_clk_src_types[] = {
- SND_BEBOB_CLOCK_TYPE_INTERNAL,
- SND_BEBOB_CLOCK_TYPE_EXTERNAL, /* S/PDIF */
-};
-static int
-phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
-{
- int err;
-
- err = avc_audio_get_selector(bebob->unit, 0, 4, id);
- if (err < 0)
- return err;
-
- if (*id >= ARRAY_SIZE(phase24_series_clk_src_types))
- return -EIO;
-
- return 0;
-}
-
static const struct snd_bebob_rate_spec phase_series_rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
@@ -71,15 +52,3 @@ const struct snd_bebob_spec phase88_rack_spec = {
.rate = &phase_series_rate_spec,
.meter = NULL
};
-
-/* 'PHASE 24 FW' and 'PHASE X24 FW' */
-static const struct snd_bebob_clock_spec phase24_series_clk = {
- .num = ARRAY_SIZE(phase24_series_clk_src_types),
- .types = phase24_series_clk_src_types,
- .get = &phase24_series_clk_src_get,
-};
-const struct snd_bebob_spec phase24_series_spec = {
- .clock = &phase24_series_clk,
- .rate = &phase_series_rate_spec,
- .meter = NULL
-};
diff --git a/sound/firewire/bebob/bebob_yamaha.c b/sound/firewire/bebob/bebob_yamaha_terratec.c
index 90d4404f77ce..a6be3e7138e0 100644
--- a/sound/firewire/bebob/bebob_yamaha.c
+++ b/sound/firewire/bebob/bebob_yamaha_terratec.c
@@ -14,7 +14,7 @@
* must be accompanied. If changing the state, a LED on the device starts to
* blink and its sync status is false. In this state, the device sounds nothing
* even if streaming. To start streaming at the current sampling rate is only
- * way to revocer this state. GO46 is better for stand-alone mixer.
+ * way to recover this state. GO46 is better for stand-alone mixer.
*
* Both of them have a capability to change its sampling rate up to 192.0kHz.
* At 192.0kHz, the device reports 4 PCM-in, 1 MIDI-in, 6 PCM-out, 1 MIDI-out.
@@ -25,7 +25,10 @@
* streaming with many asynchronous transactions brings sounds with noises.
* Unfortunately current 'ffado-mixer' generated many asynchronous transaction
* to observe device's state, mainly check cmp connection and signal format. I
- * reccomend users to close ffado-mixer at 192.0kHz if mixer is needless.
+ * recommend users to close ffado-mixer at 192.0kHz if mixer is needless.
+ *
+ * Terratec PHASE 24 FW and PHASE X24 FW are internally the same as
+ * Yamaha GO 44 and GO 46. Yamaha and Terratec had cooperated for these models.
*/
static enum snd_bebob_clock_type clk_src_types[] = {
@@ -55,7 +58,7 @@ static const struct snd_bebob_rate_spec rate_spec = {
.get = &snd_bebob_stream_get_rate,
.set = &snd_bebob_stream_set_rate,
};
-const struct snd_bebob_spec yamaha_go_spec = {
+const struct snd_bebob_spec yamaha_terratec_spec = {
.clock = &clock_spec,
.rate = &rate_spec,
.meter = NULL
diff --git a/sound/firewire/dice/dice-pcm.c b/sound/firewire/dice/dice-pcm.c
index 4aa0249826fd..6074fe1f00f7 100644
--- a/sound/firewire/dice/dice-pcm.c
+++ b/sound/firewire/dice/dice-pcm.c
@@ -302,7 +302,7 @@ static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
int snd_dice_create_pcm(struct snd_dice *dice)
{
- static struct snd_pcm_ops capture_ops = {
+ static const struct snd_pcm_ops capture_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -314,7 +314,7 @@ int snd_dice_create_pcm(struct snd_dice *dice)
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
- static struct snd_pcm_ops playback_ops = {
+ static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c
index cac28f70aef7..613f05872770 100644
--- a/sound/firewire/digi00x/digi00x-pcm.c
+++ b/sound/firewire/digi00x/digi00x-pcm.c
@@ -329,7 +329,7 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
return amdtp_stream_pcm_pointer(&dg00x->rx_stream);
}
-static struct snd_pcm_ops pcm_capture_ops = {
+static const struct snd_pcm_ops pcm_capture_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -341,7 +341,7 @@ static struct snd_pcm_ops pcm_capture_ops = {
.page = snd_pcm_lib_get_vmalloc_page,
};
-static struct snd_pcm_ops pcm_playback_ops = {
+static const struct snd_pcm_ops pcm_playback_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/firewire/fireworks/fireworks.h b/sound/firewire/fireworks/fireworks.h
index 03ed35237e2b..d73c12b8753d 100644
--- a/sound/firewire/fireworks/fireworks.h
+++ b/sound/firewire/fireworks/fireworks.h
@@ -108,7 +108,6 @@ struct snd_efw {
u8 *resp_buf;
u8 *pull_ptr;
u8 *push_ptr;
- unsigned int resp_queues;
};
int snd_efw_transaction_cmd(struct fw_unit *unit,
diff --git a/sound/firewire/fireworks/fireworks_hwdep.c b/sound/firewire/fireworks/fireworks_hwdep.c
index 33df8655fe81..2e1d9a23920c 100644
--- a/sound/firewire/fireworks/fireworks_hwdep.c
+++ b/sound/firewire/fireworks/fireworks_hwdep.c
@@ -25,6 +25,7 @@ hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
{
unsigned int length, till_end, type;
struct snd_efw_transaction *t;
+ u8 *pull_ptr;
long count = 0;
if (remained < sizeof(type) + sizeof(struct snd_efw_transaction))
@@ -38,8 +39,17 @@ hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
buf += sizeof(type);
/* write into buffer as many responses as possible */
- while (efw->resp_queues > 0) {
- t = (struct snd_efw_transaction *)(efw->pull_ptr);
+ spin_lock_irq(&efw->lock);
+
+ /*
+ * When another task reaches here during this task's access to user
+ * space, it picks up current position in buffer and can read the same
+ * series of responses.
+ */
+ pull_ptr = efw->pull_ptr;
+
+ while (efw->push_ptr != pull_ptr) {
+ t = (struct snd_efw_transaction *)(pull_ptr);
length = be32_to_cpu(t->length) * sizeof(__be32);
/* confirm enough space for this response */
@@ -49,26 +59,39 @@ hwdep_read_resp_buf(struct snd_efw *efw, char __user *buf, long remained,
/* copy from ring buffer to user buffer */
while (length > 0) {
till_end = snd_efw_resp_buf_size -
- (unsigned int)(efw->pull_ptr - efw->resp_buf);
+ (unsigned int)(pull_ptr - efw->resp_buf);
till_end = min_t(unsigned int, length, till_end);
- if (copy_to_user(buf, efw->pull_ptr, till_end))
+ spin_unlock_irq(&efw->lock);
+
+ if (copy_to_user(buf, pull_ptr, till_end))
return -EFAULT;
- efw->pull_ptr += till_end;
- if (efw->pull_ptr >= efw->resp_buf +
- snd_efw_resp_buf_size)
- efw->pull_ptr -= snd_efw_resp_buf_size;
+ spin_lock_irq(&efw->lock);
+
+ pull_ptr += till_end;
+ if (pull_ptr >= efw->resp_buf + snd_efw_resp_buf_size)
+ pull_ptr -= snd_efw_resp_buf_size;
length -= till_end;
buf += till_end;
count += till_end;
remained -= till_end;
}
-
- efw->resp_queues--;
}
+ /*
+ * All of tasks can read from the buffer nearly simultaneously, but the
+ * last position for each task is different depending on the length of
+ * given buffer. Here, for simplicity, a position of buffer is set by
+ * the latest task. It's better for a listening application to allow one
+ * thread to read from the buffer. Unless, each task can read different
+ * sequence of responses depending on variation of buffer length.
+ */
+ efw->pull_ptr = pull_ptr;
+
+ spin_unlock_irq(&efw->lock);
+
return count;
}
@@ -76,14 +99,17 @@ static long
hwdep_read_locked(struct snd_efw *efw, char __user *buf, long count,
loff_t *offset)
{
- union snd_firewire_event event;
+ union snd_firewire_event event = {
+ .lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
+ };
- memset(&event, 0, sizeof(event));
+ spin_lock_irq(&efw->lock);
- event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
event.lock_status.status = (efw->dev_lock_count > 0);
efw->dev_lock_changed = false;
+ spin_unlock_irq(&efw->lock);
+
count = min_t(long, count, sizeof(event.lock_status));
if (copy_to_user(buf, &event, count))
@@ -98,10 +124,15 @@ hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
{
struct snd_efw *efw = hwdep->private_data;
DEFINE_WAIT(wait);
+ bool dev_lock_changed;
+ bool queued;
spin_lock_irq(&efw->lock);
- while ((!efw->dev_lock_changed) && (efw->resp_queues == 0)) {
+ dev_lock_changed = efw->dev_lock_changed;
+ queued = efw->push_ptr != efw->pull_ptr;
+
+ while (!dev_lock_changed && !queued) {
prepare_to_wait(&efw->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
spin_unlock_irq(&efw->lock);
schedule();
@@ -109,15 +140,17 @@ hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
if (signal_pending(current))
return -ERESTARTSYS;
spin_lock_irq(&efw->lock);
+ dev_lock_changed = efw->dev_lock_changed;
+ queued = efw->push_ptr != efw->pull_ptr;
}
- if (efw->dev_lock_changed)
+ spin_unlock_irq(&efw->lock);
+
+ if (dev_lock_changed)
count = hwdep_read_locked(efw, buf, count, offset);
- else if (efw->resp_queues > 0)
+ else if (queued)
count = hwdep_read_resp_buf(efw, buf, count, offset);
- spin_unlock_irq(&efw->lock);
-
return count;
}
@@ -160,7 +193,7 @@ hwdep_poll(struct snd_hwdep *hwdep, struct file *file, poll_table *wait)
poll_wait(file, &efw->hwdep_wait, wait);
spin_lock_irq(&efw->lock);
- if (efw->dev_lock_changed || (efw->resp_queues > 0))
+ if (efw->dev_lock_changed || efw->pull_ptr != efw->push_ptr)
events = POLLIN | POLLRDNORM;
else
events = 0;
diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c
index 0639dcb13f7d..beb0a0ffee57 100644
--- a/sound/firewire/fireworks/fireworks_proc.c
+++ b/sound/firewire/fireworks/fireworks_proc.c
@@ -188,8 +188,8 @@ proc_read_queues_state(struct snd_info_entry *entry,
else
consumed = (unsigned int)(efw->push_ptr - efw->pull_ptr);
- snd_iprintf(buffer, "%d %d/%d\n",
- efw->resp_queues, consumed, snd_efw_resp_buf_size);
+ snd_iprintf(buffer, "%d/%d\n",
+ consumed, snd_efw_resp_buf_size);
}
static void
diff --git a/sound/firewire/fireworks/fireworks_transaction.c b/sound/firewire/fireworks/fireworks_transaction.c
index f550808d1784..36a08ba51ec7 100644
--- a/sound/firewire/fireworks/fireworks_transaction.c
+++ b/sound/firewire/fireworks/fireworks_transaction.c
@@ -121,11 +121,11 @@ copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
size_t capacity, till_end;
struct snd_efw_transaction *t;
- spin_lock_irq(&efw->lock);
-
t = (struct snd_efw_transaction *)data;
length = min_t(size_t, be32_to_cpu(t->length) * sizeof(u32), length);
+ spin_lock_irq(&efw->lock);
+
if (efw->push_ptr < efw->pull_ptr)
capacity = (unsigned int)(efw->pull_ptr - efw->push_ptr);
else
@@ -155,7 +155,6 @@ copy_resp_to_buf(struct snd_efw *efw, void *data, size_t length, int *rcode)
}
/* for hwdep */
- efw->resp_queues++;
wake_up(&efw->hwdep_wait);
*rcode = RCODE_COMPLETE;
diff --git a/sound/firewire/oxfw/oxfw-pcm.c b/sound/firewire/oxfw/oxfw-pcm.c
index 8d233417695d..f3530f89a025 100644
--- a/sound/firewire/oxfw/oxfw-pcm.c
+++ b/sound/firewire/oxfw/oxfw-pcm.c
@@ -388,7 +388,7 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstm)
int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
{
- static struct snd_pcm_ops capture_ops = {
+ static const struct snd_pcm_ops capture_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -400,7 +400,7 @@ int snd_oxfw_create_pcm(struct snd_oxfw *oxfw)
.page = snd_pcm_lib_get_vmalloc_page,
.mmap = snd_pcm_lib_mmap_vmalloc,
};
- static struct snd_pcm_ops playback_ops = {
+ static const struct snd_pcm_ops playback_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c
index 131267c3a042..106406cbfaa3 100644
--- a/sound/firewire/tascam/tascam-hwdep.c
+++ b/sound/firewire/tascam/tascam-hwdep.c
@@ -16,31 +16,14 @@
#include "tascam.h"
-static long hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
- long count)
-{
- union snd_firewire_event event;
-
- memset(&event, 0, sizeof(event));
-
- event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
- event.lock_status.status = (tscm->dev_lock_count > 0);
- tscm->dev_lock_changed = false;
-
- count = min_t(long, count, sizeof(event.lock_status));
-
- if (copy_to_user(buf, &event, count))
- return -EFAULT;
-
- return count;
-}
-
static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
loff_t *offset)
{
struct snd_tscm *tscm = hwdep->private_data;
DEFINE_WAIT(wait);
- union snd_firewire_event event;
+ union snd_firewire_event event = {
+ .lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS,
+ };
spin_lock_irq(&tscm->lock);
@@ -54,10 +37,16 @@ static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
spin_lock_irq(&tscm->lock);
}
- memset(&event, 0, sizeof(event));
- count = hwdep_read_locked(tscm, buf, count);
+ event.lock_status.status = (tscm->dev_lock_count > 0);
+ tscm->dev_lock_changed = false;
+
spin_unlock_irq(&tscm->lock);
+ count = min_t(long, count, sizeof(event.lock_status));
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
return count;
}
diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c
index 380d3db969a5..79db1b651f5c 100644
--- a/sound/firewire/tascam/tascam-pcm.c
+++ b/sound/firewire/tascam/tascam-pcm.c
@@ -268,7 +268,7 @@ static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
return amdtp_stream_pcm_pointer(&tscm->rx_stream);
}
-static struct snd_pcm_ops pcm_capture_ops = {
+static const struct snd_pcm_ops pcm_capture_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -280,7 +280,7 @@ static struct snd_pcm_ops pcm_capture_ops = {
.page = snd_pcm_lib_get_vmalloc_page,
};
-static struct snd_pcm_ops pcm_playback_ops = {
+static const struct snd_pcm_ops pcm_playback_ops = {
.open = pcm_open,
.close = pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/hda/array.c b/sound/hda/array.c
index 516795baa7db..5dfa610e4471 100644
--- a/sound/hda/array.c
+++ b/sound/hda/array.c
@@ -21,13 +21,15 @@ void *snd_array_new(struct snd_array *array)
return NULL;
if (array->used >= array->alloced) {
int num = array->alloced + array->alloc_align;
+ int oldsize = array->alloced * array->elem_size;
int size = (num + 1) * array->elem_size;
void *nlist;
if (snd_BUG_ON(num >= 4096))
return NULL;
- nlist = krealloc(array->list, size, GFP_KERNEL | __GFP_ZERO);
+ nlist = krealloc(array->list, size, GFP_KERNEL);
if (!nlist)
return NULL;
+ memset(nlist + oldsize, 0, size - oldsize);
array->list = nlist;
array->alloced = num;
}
diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c
index 860f8cad6602..261469188566 100644
--- a/sound/hda/ext/hdac_ext_controller.c
+++ b/sound/hda/ext/hdac_ext_controller.c
@@ -29,81 +29,6 @@
*/
#define HDAC_MAX_CAPS 10
-/**
- * snd_hdac_ext_bus_parse_capabilities - parse capablity structure
- * @ebus: the pointer to extended bus object
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *ebus)
-{
- unsigned int cur_cap;
- unsigned int offset;
- struct hdac_bus *bus = &ebus->bus;
- unsigned int counter = 0;
-
- offset = snd_hdac_chip_readl(bus, LLCH);
-
- /* Lets walk the linked capabilities list */
- do {
- cur_cap = _snd_hdac_chip_read(l, bus, offset);
-
- dev_dbg(bus->dev, "Capability version: 0x%x\n",
- ((cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF));
-
- dev_dbg(bus->dev, "HDA capability ID: 0x%x\n",
- (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF);
-
- switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
- case AZX_ML_CAP_ID:
- dev_dbg(bus->dev, "Found ML capability\n");
- ebus->mlcap = bus->remap_addr + offset;
- break;
-
- case AZX_GTS_CAP_ID:
- dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset);
- ebus->gtscap = bus->remap_addr + offset;
- break;
-
- case AZX_PP_CAP_ID:
- /* PP capability found, the Audio DSP is present */
- dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset);
- ebus->ppcap = bus->remap_addr + offset;
- break;
-
- case AZX_SPB_CAP_ID:
- /* SPIB capability found, handler function */
- dev_dbg(bus->dev, "Found SPB capability\n");
- ebus->spbcap = bus->remap_addr + offset;
- break;
-
- case AZX_DRSM_CAP_ID:
- /* DMA resume capability found, handler function */
- dev_dbg(bus->dev, "Found DRSM capability\n");
- ebus->drsmcap = bus->remap_addr + offset;
- break;
-
- default:
- dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap);
- break;
- }
-
- counter++;
-
- if (counter > HDAC_MAX_CAPS) {
- dev_err(bus->dev, "We exceeded HDAC Ext capablities!!!\n");
- break;
- }
-
- /* read the offset of next capabiity */
- offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK;
-
- } while (offset);
-
- return 0;
-}
-EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_parse_capabilities);
-
/*
* processing pipe helpers - these helpers are useful for dealing with HDA
* new capability of processing pipelines
@@ -118,15 +43,15 @@ void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *ebus, bool enable)
{
struct hdac_bus *bus = &ebus->bus;
- if (!ebus->ppcap) {
+ if (!bus->ppcap) {
dev_err(bus->dev, "Address of PP capability is NULL");
return;
}
if (enable)
- snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN);
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN);
else
- snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0);
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable);
@@ -139,15 +64,15 @@ void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *ebus, bool enable)
{
struct hdac_bus *bus = &ebus->bus;
- if (!ebus->ppcap) {
+ if (!bus->ppcap) {
dev_err(bus->dev, "Address of PP capability is NULL\n");
return;
}
if (enable)
- snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE);
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE);
else
- snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0);
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable);
@@ -171,7 +96,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
struct hdac_ext_link *hlink;
struct hdac_bus *bus = &ebus->bus;
- link_count = readl(ebus->mlcap + AZX_REG_ML_MLCD) + 1;
+ link_count = readl(bus->mlcap + AZX_REG_ML_MLCD) + 1;
dev_dbg(bus->dev, "In %s Link count: %d\n", __func__, link_count);
@@ -181,7 +106,7 @@ int snd_hdac_ext_bus_get_ml_capabilities(struct hdac_ext_bus *ebus)
return -ENOMEM;
hlink->index = idx;
hlink->bus = bus;
- hlink->ml_addr = ebus->mlcap + AZX_ML_BASE +
+ hlink->ml_addr = bus->mlcap + AZX_ML_BASE +
(AZX_ML_INTERVAL * idx);
hlink->lcaps = readl(hlink->ml_addr + AZX_REG_ML_LCAP);
hlink->lsdiid = readw(hlink->ml_addr + AZX_REG_ML_LSDIID);
diff --git a/sound/hda/ext/hdac_ext_stream.c b/sound/hda/ext/hdac_ext_stream.c
index 626f3bb24c55..3be051ab5533 100644
--- a/sound/hda/ext/hdac_ext_stream.c
+++ b/sound/hda/ext/hdac_ext_stream.c
@@ -40,27 +40,27 @@ void snd_hdac_ext_stream_init(struct hdac_ext_bus *ebus,
{
struct hdac_bus *bus = &ebus->bus;
- if (ebus->ppcap) {
- stream->pphc_addr = ebus->ppcap + AZX_PPHC_BASE +
+ if (bus->ppcap) {
+ stream->pphc_addr = bus->ppcap + AZX_PPHC_BASE +
AZX_PPHC_INTERVAL * idx;
- stream->pplc_addr = ebus->ppcap + AZX_PPLC_BASE +
+ stream->pplc_addr = bus->ppcap + AZX_PPLC_BASE +
AZX_PPLC_MULTI * ebus->num_streams +
AZX_PPLC_INTERVAL * idx;
}
- if (ebus->spbcap) {
- stream->spib_addr = ebus->spbcap + AZX_SPB_BASE +
+ if (bus->spbcap) {
+ stream->spib_addr = bus->spbcap + AZX_SPB_BASE +
AZX_SPB_INTERVAL * idx +
AZX_SPB_SPIB;
- stream->fifo_addr = ebus->spbcap + AZX_SPB_BASE +
+ stream->fifo_addr = bus->spbcap + AZX_SPB_BASE +
AZX_SPB_INTERVAL * idx +
AZX_SPB_MAXFIFO;
}
- if (ebus->drsmcap)
- stream->dpibr_addr = ebus->drsmcap + AZX_DRSM_BASE +
+ if (bus->drsmcap)
+ stream->dpibr_addr = bus->drsmcap + AZX_DRSM_BASE +
AZX_DRSM_INTERVAL * idx;
stream->decoupled = false;
@@ -131,10 +131,10 @@ void snd_hdac_ext_stream_decouple(struct hdac_ext_bus *ebus,
spin_lock_irq(&bus->reg_lock);
if (decouple)
- snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL, 0,
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0,
AZX_PPCTL_PROCEN(hstream->index));
else
- snd_hdac_updatel(ebus->ppcap, AZX_REG_PP_PPCTL,
+ snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL,
AZX_PPCTL_PROCEN(hstream->index), 0);
stream->decoupled = decouple;
spin_unlock_irq(&bus->reg_lock);
@@ -255,7 +255,7 @@ hdac_ext_link_stream_assign(struct hdac_ext_bus *ebus,
struct hdac_stream *stream = NULL;
struct hdac_bus *hbus = &ebus->bus;
- if (!ebus->ppcap) {
+ if (!hbus->ppcap) {
dev_err(hbus->dev, "stream type not supported\n");
return NULL;
}
@@ -296,7 +296,7 @@ hdac_ext_host_stream_assign(struct hdac_ext_bus *ebus,
struct hdac_stream *stream = NULL;
struct hdac_bus *hbus = &ebus->bus;
- if (!ebus->ppcap) {
+ if (!hbus->ppcap) {
dev_err(hbus->dev, "stream type not supported\n");
return NULL;
}
@@ -423,21 +423,21 @@ void snd_hdac_ext_stream_spbcap_enable(struct hdac_ext_bus *ebus,
u32 register_mask = 0;
struct hdac_bus *bus = &ebus->bus;
- if (!ebus->spbcap) {
- dev_err(bus->dev, "Address of SPB capability is NULL");
+ if (!bus->spbcap) {
+ dev_err(bus->dev, "Address of SPB capability is NULL\n");
return;
}
mask |= (1 << index);
- register_mask = readl(ebus->spbcap + AZX_REG_SPB_SPBFCCTL);
+ register_mask = readl(bus->spbcap + AZX_REG_SPB_SPBFCCTL);
mask |= register_mask;
if (enable)
- snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask);
+ snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, 0, mask);
else
- snd_hdac_updatel(ebus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
+ snd_hdac_updatel(bus->spbcap, AZX_REG_SPB_SPBFCCTL, mask, 0);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_spbcap_enable);
@@ -452,8 +452,8 @@ int snd_hdac_ext_stream_set_spib(struct hdac_ext_bus *ebus,
{
struct hdac_bus *bus = &ebus->bus;
- if (!ebus->spbcap) {
- dev_err(bus->dev, "Address of SPB capability is NULL");
+ if (!bus->spbcap) {
+ dev_err(bus->dev, "Address of SPB capability is NULL\n");
return -EINVAL;
}
@@ -475,8 +475,8 @@ int snd_hdac_ext_stream_get_spbmaxfifo(struct hdac_ext_bus *ebus,
{
struct hdac_bus *bus = &ebus->bus;
- if (!ebus->spbcap) {
- dev_err(bus->dev, "Address of SPB capability is NULL");
+ if (!bus->spbcap) {
+ dev_err(bus->dev, "Address of SPB capability is NULL\n");
return -EINVAL;
}
@@ -515,21 +515,21 @@ void snd_hdac_ext_stream_drsm_enable(struct hdac_ext_bus *ebus,
u32 register_mask = 0;
struct hdac_bus *bus = &ebus->bus;
- if (!ebus->drsmcap) {
- dev_err(bus->dev, "Address of DRSM capability is NULL");
+ if (!bus->drsmcap) {
+ dev_err(bus->dev, "Address of DRSM capability is NULL\n");
return;
}
mask |= (1 << index);
- register_mask = readl(ebus->drsmcap + AZX_REG_SPB_SPBFCCTL);
+ register_mask = readl(bus->drsmcap + AZX_REG_SPB_SPBFCCTL);
mask |= register_mask;
if (enable)
- snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, 0, mask);
+ snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, 0, mask);
else
- snd_hdac_updatel(ebus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
+ snd_hdac_updatel(bus->drsmcap, AZX_REG_DRSM_CTL, mask, 0);
}
EXPORT_SYMBOL_GPL(snd_hdac_ext_stream_drsm_enable);
@@ -544,8 +544,8 @@ int snd_hdac_ext_stream_set_dpibr(struct hdac_ext_bus *ebus,
{
struct hdac_bus *bus = &ebus->bus;
- if (!ebus->drsmcap) {
- dev_err(bus->dev, "Address of DRSM capability is NULL");
+ if (!bus->drsmcap) {
+ dev_err(bus->dev, "Address of DRSM capability is NULL\n");
return -EINVAL;
}
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index 9fee464e5d49..043065867656 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -255,6 +255,81 @@ int snd_hdac_bus_get_response(struct hdac_bus *bus, unsigned int addr,
}
EXPORT_SYMBOL_GPL(snd_hdac_bus_get_response);
+#define HDAC_MAX_CAPS 10
+/**
+ * snd_hdac_bus_parse_capabilities - parse capability structure
+ * @bus: the pointer to bus object
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus)
+{
+ unsigned int cur_cap;
+ unsigned int offset;
+ unsigned int counter = 0;
+
+ offset = snd_hdac_chip_readl(bus, LLCH);
+
+ /* Lets walk the linked capabilities list */
+ do {
+ cur_cap = _snd_hdac_chip_read(l, bus, offset);
+
+ dev_dbg(bus->dev, "Capability version: 0x%x\n",
+ (cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF);
+
+ dev_dbg(bus->dev, "HDA capability ID: 0x%x\n",
+ (cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF);
+
+ switch ((cur_cap & AZX_CAP_HDR_ID_MASK) >> AZX_CAP_HDR_ID_OFF) {
+ case AZX_ML_CAP_ID:
+ dev_dbg(bus->dev, "Found ML capability\n");
+ bus->mlcap = bus->remap_addr + offset;
+ break;
+
+ case AZX_GTS_CAP_ID:
+ dev_dbg(bus->dev, "Found GTS capability offset=%x\n", offset);
+ bus->gtscap = bus->remap_addr + offset;
+ break;
+
+ case AZX_PP_CAP_ID:
+ /* PP capability found, the Audio DSP is present */
+ dev_dbg(bus->dev, "Found PP capability offset=%x\n", offset);
+ bus->ppcap = bus->remap_addr + offset;
+ break;
+
+ case AZX_SPB_CAP_ID:
+ /* SPIB capability found, handler function */
+ dev_dbg(bus->dev, "Found SPB capability\n");
+ bus->spbcap = bus->remap_addr + offset;
+ break;
+
+ case AZX_DRSM_CAP_ID:
+ /* DMA resume capability found, handler function */
+ dev_dbg(bus->dev, "Found DRSM capability\n");
+ bus->drsmcap = bus->remap_addr + offset;
+ break;
+
+ default:
+ dev_dbg(bus->dev, "Unknown capability %d\n", cur_cap);
+ break;
+ }
+
+ counter++;
+
+ if (counter > HDAC_MAX_CAPS) {
+ dev_err(bus->dev, "We exceeded HDAC capabilities!!!\n");
+ break;
+ }
+
+ /* read the offset of next capability */
+ offset = cur_cap & AZX_CAP_HDR_NXT_PTR_MASK;
+
+ } while (offset);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_bus_parse_capabilities);
+
/*
* Lowlevel interface
*/
diff --git a/sound/hda/hdac_regmap.c b/sound/hda/hdac_regmap.c
index 87041ddd29cb..47a358fab132 100644
--- a/sound/hda/hdac_regmap.c
+++ b/sound/hda/hdac_regmap.c
@@ -444,7 +444,7 @@ int snd_hdac_regmap_write_raw(struct hdac_device *codec, unsigned int reg,
err = reg_raw_write(codec, reg, val);
if (err == -EAGAIN) {
err = snd_hdac_power_up_pm(codec);
- if (!err)
+ if (err >= 0)
err = reg_raw_write(codec, reg, val);
snd_hdac_power_down_pm(codec);
}
@@ -470,7 +470,7 @@ static int __snd_hdac_regmap_read_raw(struct hdac_device *codec,
err = reg_raw_read(codec, reg, val, uncached);
if (err == -EAGAIN) {
err = snd_hdac_power_up_pm(codec);
- if (!err)
+ if (err >= 0)
err = reg_raw_read(codec, reg, val, uncached);
snd_hdac_power_down_pm(codec);
}
diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c
index c6c75e7e0981..81acc20c2535 100644
--- a/sound/hda/hdmi_chmap.c
+++ b/sound/hda/hdmi_chmap.c
@@ -353,7 +353,8 @@ static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap,
int hdmi_slot = 0;
/* fill actual channel mappings in ALSA channel (i) order */
for (i = 0; i < ch_alloc->channels; i++) {
- while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8))
+ while (!WARN_ON(hdmi_slot >= 8) &&
+ !ch_alloc->speakers[7 - hdmi_slot])
hdmi_slot++; /* skip zero slots */
hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
@@ -430,6 +431,12 @@ static int to_cea_slot(int ordered_ca, unsigned char pos)
int mask = snd_hdac_chmap_to_spk_mask(pos);
int i;
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (ordered_ca >= ARRAY_SIZE(channel_allocations))
+ return -1;
+
if (mask) {
for (i = 0; i < 8; i++) {
if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
@@ -456,7 +463,15 @@ EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap);
/* from CEA slot to ALSA API channel position */
static int from_cea_slot(int ordered_ca, unsigned char slot)
{
- int mask = channel_allocations[ordered_ca].speakers[7 - slot];
+ int mask;
+
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (slot >= 8)
+ return 0;
+
+ mask = channel_allocations[ordered_ca].speakers[7 - slot];
return snd_hdac_spk_to_chmap(mask);
}
@@ -523,7 +538,8 @@ static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
int ordered_ca = get_channel_allocation_order(ca);
for (i = 0; i < 8; i++) {
- if (i < channel_allocations[ordered_ca].channels)
+ if (ordered_ca < ARRAY_SIZE(channel_allocations) &&
+ i < channel_allocations[ordered_ca].channels)
map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
else
map[i] = 0;
@@ -551,6 +567,12 @@ int snd_hdac_get_active_channels(int ca)
{
int ordered_ca = get_channel_allocation_order(ca);
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (ordered_ca >= ARRAY_SIZE(channel_allocations))
+ ordered_ca = 0;
+
return channel_allocations[ordered_ca].channels;
}
EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels);
diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c
index 5a4cf3fab4ae..d53c9bb36281 100644
--- a/sound/i2c/other/ak4114.c
+++ b/sound/i2c/other/ak4114.c
@@ -121,7 +121,7 @@ int snd_ak4114_create(struct snd_card *card,
__fail:
snd_ak4114_free(chip);
- return err < 0 ? err : -EIO;
+ return err;
}
EXPORT_SYMBOL(snd_ak4114_create);
diff --git a/sound/i2c/other/ak4117.c b/sound/i2c/other/ak4117.c
index 48848909a5a9..0702f0552d19 100644
--- a/sound/i2c/other/ak4117.c
+++ b/sound/i2c/other/ak4117.c
@@ -110,7 +110,7 @@ int snd_ak4117_create(struct snd_card *card, ak4117_read_t *read, ak4117_write_t
__fail:
snd_ak4117_free(chip);
- return err < 0 ? err : -EIO;
+ return err;
}
void snd_ak4117_reg_write(struct ak4117 *chip, unsigned char reg, unsigned char mask, unsigned char val)
diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c
index f159da4ec890..a302d1f8d14f 100644
--- a/sound/isa/ad1848/ad1848.c
+++ b/sound/isa/ad1848/ad1848.c
@@ -170,15 +170,4 @@ static struct isa_driver snd_ad1848_driver = {
}
};
-static int __init alsa_card_ad1848_init(void)
-{
- return isa_register_driver(&snd_ad1848_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_ad1848_exit(void)
-{
- isa_unregister_driver(&snd_ad1848_driver);
-}
-
-module_init(alsa_card_ad1848_init);
-module_exit(alsa_card_ad1848_exit);
+module_isa_driver(snd_ad1848_driver, SNDRV_CARDS);
diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c
index 120c524bb2a0..8d3060fd7ad7 100644
--- a/sound/isa/adlib.c
+++ b/sound/isa/adlib.c
@@ -112,15 +112,4 @@ static struct isa_driver snd_adlib_driver = {
}
};
-static int __init alsa_card_adlib_init(void)
-{
- return isa_register_driver(&snd_adlib_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_adlib_exit(void)
-{
- isa_unregister_driver(&snd_adlib_driver);
-}
-
-module_init(alsa_card_adlib_init);
-module_exit(alsa_card_adlib_exit);
+module_isa_driver(snd_adlib_driver, SNDRV_CARDS);
diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c
index 2c89d95da674..787475084f46 100644
--- a/sound/isa/cmi8328.c
+++ b/sound/isa/cmi8328.c
@@ -469,15 +469,4 @@ static struct isa_driver snd_cmi8328_driver = {
},
};
-static int __init alsa_card_cmi8328_init(void)
-{
- return isa_register_driver(&snd_cmi8328_driver, CMI8328_MAX);
-}
-
-static void __exit alsa_card_cmi8328_exit(void)
-{
- isa_unregister_driver(&snd_cmi8328_driver);
-}
-
-module_init(alsa_card_cmi8328_init)
-module_exit(alsa_card_cmi8328_exit)
+module_isa_driver(snd_cmi8328_driver, CMI8328_MAX);
diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c
index 282cd75d2235..ef7448e9f813 100644
--- a/sound/isa/cs423x/cs4231.c
+++ b/sound/isa/cs423x/cs4231.c
@@ -186,15 +186,4 @@ static struct isa_driver snd_cs4231_driver = {
}
};
-static int __init alsa_card_cs4231_init(void)
-{
- return isa_register_driver(&snd_cs4231_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_cs4231_exit(void)
-{
- isa_unregister_driver(&snd_cs4231_driver);
-}
-
-module_init(alsa_card_cs4231_init);
-module_exit(alsa_card_cs4231_exit);
+module_isa_driver(snd_cs4231_driver, SNDRV_CARDS);
diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c
index 32278847884f..379abe2cbeb2 100644
--- a/sound/isa/galaxy/galaxy.c
+++ b/sound/isa/galaxy/galaxy.c
@@ -634,15 +634,4 @@ static struct isa_driver snd_galaxy_driver = {
}
};
-static int __init alsa_card_galaxy_init(void)
-{
- return isa_register_driver(&snd_galaxy_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_galaxy_exit(void)
-{
- isa_unregister_driver(&snd_galaxy_driver);
-}
-
-module_init(alsa_card_galaxy_init);
-module_exit(alsa_card_galaxy_exit);
+module_isa_driver(snd_galaxy_driver, SNDRV_CARDS);
diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c
index f0019715d82e..c169be49ed71 100644
--- a/sound/isa/gus/gusclassic.c
+++ b/sound/isa/gus/gusclassic.c
@@ -229,15 +229,4 @@ static struct isa_driver snd_gusclassic_driver = {
}
};
-static int __init alsa_card_gusclassic_init(void)
-{
- return isa_register_driver(&snd_gusclassic_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_gusclassic_exit(void)
-{
- isa_unregister_driver(&snd_gusclassic_driver);
-}
-
-module_init(alsa_card_gusclassic_init);
-module_exit(alsa_card_gusclassic_exit);
+module_isa_driver(snd_gusclassic_driver, SNDRV_CARDS);
diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c
index 693d95f46804..77ac2fd723b4 100644
--- a/sound/isa/gus/gusextreme.c
+++ b/sound/isa/gus/gusextreme.c
@@ -358,15 +358,4 @@ static struct isa_driver snd_gusextreme_driver = {
}
};
-static int __init alsa_card_gusextreme_init(void)
-{
- return isa_register_driver(&snd_gusextreme_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_gusextreme_exit(void)
-{
- isa_unregister_driver(&snd_gusextreme_driver);
-}
-
-module_init(alsa_card_gusextreme_init);
-module_exit(alsa_card_gusextreme_exit);
+module_isa_driver(snd_gusextreme_driver, SNDRV_CARDS);
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index 8216e8d8f017..dd88c9d33492 100644
--- a/sound/isa/gus/gusmax.c
+++ b/sound/isa/gus/gusmax.c
@@ -370,15 +370,4 @@ static struct isa_driver snd_gusmax_driver = {
},
};
-static int __init alsa_card_gusmax_init(void)
-{
- return isa_register_driver(&snd_gusmax_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_gusmax_exit(void)
-{
- isa_unregister_driver(&snd_gusmax_driver);
-}
-
-module_init(alsa_card_gusmax_init)
-module_exit(alsa_card_gusmax_exit)
+module_isa_driver(snd_gusmax_driver, SNDRV_CARDS);
diff --git a/sound/isa/sb/jazz16.c b/sound/isa/sb/jazz16.c
index 6b4884d052a5..4d909971eedb 100644
--- a/sound/isa/sb/jazz16.c
+++ b/sound/isa/sb/jazz16.c
@@ -387,15 +387,4 @@ static struct isa_driver snd_jazz16_driver = {
},
};
-static int __init alsa_card_jazz16_init(void)
-{
- return isa_register_driver(&snd_jazz16_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_jazz16_exit(void)
-{
- isa_unregister_driver(&snd_jazz16_driver);
-}
-
-module_init(alsa_card_jazz16_init)
-module_exit(alsa_card_jazz16_exit)
+module_isa_driver(snd_jazz16_driver, SNDRV_CARDS);
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
index b8e2391c33ff..ad42d2364199 100644
--- a/sound/isa/sb/sb8.c
+++ b/sound/isa/sb/sb8.c
@@ -251,15 +251,4 @@ static struct isa_driver snd_sb8_driver = {
},
};
-static int __init alsa_card_sb8_init(void)
-{
- return isa_register_driver(&snd_sb8_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_sb8_exit(void)
-{
- isa_unregister_driver(&snd_sb8_driver);
-}
-
-module_init(alsa_card_sb8_init)
-module_exit(alsa_card_sb8_exit)
+module_isa_driver(snd_sb8_driver, SNDRV_CARDS);
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
index 51cfa7615f72..b61a6633d8f2 100644
--- a/sound/isa/sc6000.c
+++ b/sound/isa/sc6000.c
@@ -711,15 +711,4 @@ static struct isa_driver snd_sc6000_driver = {
};
-static int __init alsa_card_sc6000_init(void)
-{
- return isa_register_driver(&snd_sc6000_driver, SNDRV_CARDS);
-}
-
-static void __exit alsa_card_sc6000_exit(void)
-{
- isa_unregister_driver(&snd_sc6000_driver);
-}
-
-module_init(alsa_card_sc6000_init)
-module_exit(alsa_card_sc6000_exit)
+module_isa_driver(snd_sc6000_driver, SNDRV_CARDS);
diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c
index 10c8de1f8d29..6368e5c7d0ba 100644
--- a/sound/oss/ad1848.c
+++ b/sound/oss/ad1848.c
@@ -254,7 +254,7 @@ static void ad_write(ad1848_info * devc, int reg, int data)
static void wait_for_calibration(ad1848_info * devc)
{
- int timeout = 0;
+ int timeout;
/*
* Wait until the auto calibration process has finished.
diff --git a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c
index 35b5912cf3f8..bb477d5c8528 100644
--- a/sound/oss/aedsp16.c
+++ b/sound/oss/aedsp16.c
@@ -482,13 +482,13 @@ static struct orVals orDMA[] __initdata = {
};
static struct aedsp16_info ae_config = {
- DEF_AEDSP16_IOB,
- DEF_AEDSP16_IRQ,
- DEF_AEDSP16_MRQ,
- DEF_AEDSP16_DMA,
- -1,
- -1,
- INIT_NONE
+ .base_io = DEF_AEDSP16_IOB,
+ .irq = DEF_AEDSP16_IRQ,
+ .mpu_irq = DEF_AEDSP16_MRQ,
+ .dma = DEF_AEDSP16_DMA,
+ .mss_base = -1,
+ .mpu_base = -1,
+ .init = INIT_NONE
};
/*
diff --git a/sound/oss/sound_firmware.h b/sound/oss/sound_firmware.h
index 0a0cbfdfb855..da4c67e005ed 100644
--- a/sound/oss/sound_firmware.h
+++ b/sound/oss/sound_firmware.h
@@ -1,2 +1,29 @@
-extern int mod_firmware_load(const char *fn, char **fp);
+#include <linux/fs.h>
+/**
+ * mod_firmware_load - load sound driver firmware
+ * @fn: filename
+ * @fp: return for the buffer.
+ *
+ * Load the firmware for a sound module (up to 128K) into a buffer.
+ * The buffer is returned in *fp. It is allocated with vmalloc so is
+ * virtually linear and not DMAable. The caller should free it with
+ * vfree when finished.
+ *
+ * The length of the buffer is returned on a successful load, the
+ * value zero on a failure.
+ *
+ * Caution: This API is not recommended. Firmware should be loaded via
+ * request_firmware.
+ */
+static inline int mod_firmware_load(const char *fn, char **fp)
+{
+ loff_t size;
+ int err;
+
+ err = kernel_read_file_from_path((char *)fn, (void **)fp, &size,
+ 131072, READING_FIRMWARE);
+ if (err < 0)
+ return 0;
+ return size;
+}
diff --git a/sound/oss/sound_timer.c b/sound/oss/sound_timer.c
index 8021c85f076d..3a444a6f10eb 100644
--- a/sound/oss/sound_timer.c
+++ b/sound/oss/sound_timer.c
@@ -17,7 +17,7 @@
#include "sound_config.h"
static volatile int initialized, opened, tmr_running;
-static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned int tmr_offs, tmr_ctr;
static volatile unsigned long ticks_offs;
static volatile int curr_tempo, curr_timebase;
static volatile unsigned long curr_ticks;
diff --git a/sound/oss/sys_timer.c b/sound/oss/sys_timer.c
index 2226dda0eff0..d17019d25b99 100644
--- a/sound/oss/sys_timer.c
+++ b/sound/oss/sys_timer.c
@@ -19,7 +19,7 @@
#include "sound_config.h"
static volatile int opened, tmr_running;
-static volatile time_t tmr_offs, tmr_ctr;
+static volatile unsigned int tmr_offs, tmr_ctr;
static volatile unsigned long ticks_offs;
static volatile int curr_tempo, curr_timebase;
static volatile unsigned long curr_ticks;
diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c
index 1fc6d8bc09e5..8c36990e26f6 100644
--- a/sound/pci/ad1889.c
+++ b/sound/pci/ad1889.c
@@ -571,7 +571,7 @@ snd_ad1889_capture_pointer(struct snd_pcm_substream *ss)
return bytes_to_frames(ss->runtime, ptr);
}
-static struct snd_pcm_ops snd_ad1889_playback_ops = {
+static const struct snd_pcm_ops snd_ad1889_playback_ops = {
.open = snd_ad1889_playback_open,
.close = snd_ad1889_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -582,7 +582,7 @@ static struct snd_pcm_ops snd_ad1889_playback_ops = {
.pointer = snd_ad1889_playback_pointer,
};
-static struct snd_pcm_ops snd_ad1889_capture_ops = {
+static const struct snd_pcm_ops snd_ad1889_capture_ops = {
.open = snd_ad1889_capture_open,
.close = snd_ad1889_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 36470af7eda7..92b819e4f729 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -1408,6 +1408,7 @@ snd_ali_playback_pointer(struct snd_pcm_substream *substream)
spin_unlock(&codec->reg_lock);
dev_dbg(codec->card->dev, "playback pointer returned cso=%xh.\n", cso);
+ cso %= runtime->buffer_size;
return cso;
}
@@ -1428,6 +1429,7 @@ static snd_pcm_uframes_t snd_ali_pointer(struct snd_pcm_substream *substream)
cso = inw(ALI_REG(codec, ALI_CSO_ALPHA_FMS + 2));
spin_unlock(&codec->reg_lock);
+ cso %= runtime->buffer_size;
return cso;
}
diff --git a/sound/pci/als300.c b/sound/pci/als300.c
index add3176398d3..ab75601d7c2c 100644
--- a/sound/pci/als300.c
+++ b/sound/pci/als300.c
@@ -563,7 +563,7 @@ static snd_pcm_uframes_t snd_als300_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(substream->runtime, current_ptr);
}
-static struct snd_pcm_ops snd_als300_playback_ops = {
+static const struct snd_pcm_ops snd_als300_playback_ops = {
.open = snd_als300_playback_open,
.close = snd_als300_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -574,7 +574,7 @@ static struct snd_pcm_ops snd_als300_playback_ops = {
.pointer = snd_als300_pointer,
};
-static struct snd_pcm_ops snd_als300_capture_ops = {
+static const struct snd_pcm_ops snd_als300_capture_ops = {
.open = snd_als300_capture_open,
.close = snd_als300_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index ff39a0c7277b..edabe1371660 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -672,7 +672,7 @@ static int snd_als4000_capture_close(struct snd_pcm_substream *substream)
/******************************************************************/
-static struct snd_pcm_ops snd_als4000_playback_ops = {
+static const struct snd_pcm_ops snd_als4000_playback_ops = {
.open = snd_als4000_playback_open,
.close = snd_als4000_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -683,7 +683,7 @@ static struct snd_pcm_ops snd_als4000_playback_ops = {
.pointer = snd_als4000_playback_pointer
};
-static struct snd_pcm_ops snd_als4000_capture_ops = {
+static const struct snd_pcm_ops snd_als4000_capture_ops = {
.open = snd_als4000_capture_open,
.close = snd_als4000_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c
index 1039eccbb895..976a3d23557e 100644
--- a/sound/pci/asihpi/asihpi.c
+++ b/sound/pci/asihpi/asihpi.c
@@ -1138,7 +1138,7 @@ static int snd_card_asihpi_playback_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
+static const struct snd_pcm_ops snd_card_asihpi_playback_mmap_ops = {
.open = snd_card_asihpi_playback_open,
.close = snd_card_asihpi_playback_close,
.ioctl = snd_card_asihpi_playback_ioctl,
@@ -1305,7 +1305,7 @@ static int snd_card_asihpi_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
+static const struct snd_pcm_ops snd_card_asihpi_capture_mmap_ops = {
.open = snd_card_asihpi_capture_open,
.close = snd_card_asihpi_capture_close,
.ioctl = snd_card_asihpi_capture_ioctl,
diff --git a/sound/pci/asihpi/hpifunc.c b/sound/pci/asihpi/hpifunc.c
index 510e56cffd31..f9b57647b319 100644
--- a/sound/pci/asihpi/hpifunc.c
+++ b/sound/pci/asihpi/hpifunc.c
@@ -2323,11 +2323,8 @@ u16 hpi_sample_clock_get_source_index(u32 h_control, u16 *pw_source_index)
u16 hpi_sample_clock_query_local_rate(const u32 h_clock, const u32 index,
u32 *prate)
{
- u16 err;
- err = hpi_control_query(h_clock, HPI_SAMPLECLOCK_LOCAL_SAMPLERATE,
- index, 0, prate);
-
- return err;
+ return hpi_control_query(h_clock, HPI_SAMPLECLOCK_LOCAL_SAMPLERATE,
+ index, 0, prate);
}
u16 hpi_sample_clock_set_local_rate(u32 h_control, u32 sample_rate)
diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c
index 2ce0022dbc46..a40c918c8dff 100644
--- a/sound/pci/atiixp.c
+++ b/sound/pci/atiixp.c
@@ -1148,7 +1148,7 @@ static int snd_atiixp_spdif_close(struct snd_pcm_substream *substream)
}
/* AC97 playback */
-static struct snd_pcm_ops snd_atiixp_playback_ops = {
+static const struct snd_pcm_ops snd_atiixp_playback_ops = {
.open = snd_atiixp_playback_open,
.close = snd_atiixp_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1160,7 +1160,7 @@ static struct snd_pcm_ops snd_atiixp_playback_ops = {
};
/* AC97 capture */
-static struct snd_pcm_ops snd_atiixp_capture_ops = {
+static const struct snd_pcm_ops snd_atiixp_capture_ops = {
.open = snd_atiixp_capture_open,
.close = snd_atiixp_capture_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1172,7 +1172,7 @@ static struct snd_pcm_ops snd_atiixp_capture_ops = {
};
/* SPDIF playback */
-static struct snd_pcm_ops snd_atiixp_spdif_ops = {
+static const struct snd_pcm_ops snd_atiixp_spdif_ops = {
.open = snd_atiixp_spdif_open,
.close = snd_atiixp_spdif_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c
index c534552963e7..40152feef1e7 100644
--- a/sound/pci/atiixp_modem.c
+++ b/sound/pci/atiixp_modem.c
@@ -947,7 +947,7 @@ static int snd_atiixp_capture_close(struct snd_pcm_substream *substream)
/* AC97 playback */
-static struct snd_pcm_ops snd_atiixp_playback_ops = {
+static const struct snd_pcm_ops snd_atiixp_playback_ops = {
.open = snd_atiixp_playback_open,
.close = snd_atiixp_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -959,7 +959,7 @@ static struct snd_pcm_ops snd_atiixp_playback_ops = {
};
/* AC97 capture */
-static struct snd_pcm_ops snd_atiixp_capture_ops = {
+static const struct snd_pcm_ops snd_atiixp_capture_ops = {
.open = snd_atiixp_capture_open,
.close = snd_atiixp_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index 4a054d720112..e1af24f87566 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -1043,7 +1043,7 @@ static void vortex_fifo_init(vortex_t * vortex)
for (x = NR_ADB - 1; x >= 0; x--) {
hwwrite(vortex->mmio, addr, (FIFO_U0 | FIFO_U1));
if (hwread(vortex->mmio, addr) != (FIFO_U0 | FIFO_U1))
- dev_err(vortex->card->dev, "bad adb fifo reset!");
+ dev_err(vortex->card->dev, "bad adb fifo reset!\n");
vortex_fifo_clearadbdata(vortex, x, FIFO_SIZE);
addr -= 4;
}
@@ -1444,9 +1444,8 @@ static int vortex_wtdma_bufshift(vortex_t * vortex, int wtdma)
int page, p, pp, delta, i;
page =
- (hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2)) &
- WT_SUBBUF_MASK)
- >> WT_SUBBUF_SHIFT;
+ (hwread(vortex->mmio, VORTEX_WTDMA_STAT + (wtdma << 2))
+ >> WT_SUBBUF_SHIFT) & WT_SUBBUF_MASK;
if (dma->nr_periods >= 4)
delta = (page - dma->period_real) & 3;
else {
diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c
index 167714303070..57bbb87d0c62 100644
--- a/sound/pci/aw2/aw2-alsa.c
+++ b/sound/pci/aw2/aw2-alsa.c
@@ -179,7 +179,7 @@ static struct pci_driver aw2_driver = {
module_pci_driver(aw2_driver);
/* operators for playback PCM alsa interface */
-static struct snd_pcm_ops snd_aw2_playback_ops = {
+static const struct snd_pcm_ops snd_aw2_playback_ops = {
.open = snd_aw2_pcm_playback_open,
.close = snd_aw2_pcm_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -191,7 +191,7 @@ static struct snd_pcm_ops snd_aw2_playback_ops = {
};
/* operators for capture PCM alsa interface */
-static struct snd_pcm_ops snd_aw2_capture_ops = {
+static const struct snd_pcm_ops snd_aw2_capture_ops = {
.open = snd_aw2_pcm_capture_open,
.close = snd_aw2_pcm_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c
index 5e2ef0bb7057..80c4a4456197 100644
--- a/sound/pci/azt3328.c
+++ b/sound/pci/azt3328.c
@@ -2090,7 +2090,7 @@ snd_azf3328_pcm_close(struct snd_pcm_substream *substream
/******************************************************************/
-static struct snd_pcm_ops snd_azf3328_playback_ops = {
+static const struct snd_pcm_ops snd_azf3328_playback_ops = {
.open = snd_azf3328_pcm_playback_open,
.close = snd_azf3328_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -2101,7 +2101,7 @@ static struct snd_pcm_ops snd_azf3328_playback_ops = {
.pointer = snd_azf3328_pcm_pointer
};
-static struct snd_pcm_ops snd_azf3328_capture_ops = {
+static const struct snd_pcm_ops snd_azf3328_capture_ops = {
.open = snd_azf3328_pcm_capture_open,
.close = snd_azf3328_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -2112,7 +2112,7 @@ static struct snd_pcm_ops snd_azf3328_capture_ops = {
.pointer = snd_azf3328_pcm_pointer
};
-static struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
+static const struct snd_pcm_ops snd_azf3328_i2s_out_ops = {
.open = snd_azf3328_pcm_i2s_out_open,
.close = snd_azf3328_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index 5925b7170e25..f2c0709d7441 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -550,7 +550,7 @@ static snd_pcm_uframes_t snd_bt87x_pointer(struct snd_pcm_substream *substream)
return (snd_pcm_uframes_t)bytes_to_frames(runtime, chip->current_line * chip->line_bytes);
}
-static struct snd_pcm_ops snd_bt87x_pcm_ops = {
+static const struct snd_pcm_ops snd_bt87x_pcm_ops = {
.open = snd_bt87x_pcm_open,
.close = snd_bt87x_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c
index d3cd95633ee2..6165a57a94ae 100644
--- a/sound/pci/ca0106/ca0106_main.c
+++ b/sound/pci/ca0106/ca0106_main.c
@@ -1109,7 +1109,7 @@ snd_ca0106_pcm_pointer_capture(struct snd_pcm_substream *substream)
}
/* operators */
-static struct snd_pcm_ops snd_ca0106_playback_front_ops = {
+static const struct snd_pcm_ops snd_ca0106_playback_front_ops = {
.open = snd_ca0106_pcm_open_playback_front,
.close = snd_ca0106_pcm_close_playback,
.ioctl = snd_pcm_lib_ioctl,
@@ -1120,7 +1120,7 @@ static struct snd_pcm_ops snd_ca0106_playback_front_ops = {
.pointer = snd_ca0106_pcm_pointer_playback,
};
-static struct snd_pcm_ops snd_ca0106_capture_0_ops = {
+static const struct snd_pcm_ops snd_ca0106_capture_0_ops = {
.open = snd_ca0106_pcm_open_0_capture,
.close = snd_ca0106_pcm_close_capture,
.ioctl = snd_pcm_lib_ioctl,
@@ -1131,7 +1131,7 @@ static struct snd_pcm_ops snd_ca0106_capture_0_ops = {
.pointer = snd_ca0106_pcm_pointer_capture,
};
-static struct snd_pcm_ops snd_ca0106_capture_1_ops = {
+static const struct snd_pcm_ops snd_ca0106_capture_1_ops = {
.open = snd_ca0106_pcm_open_1_capture,
.close = snd_ca0106_pcm_close_capture,
.ioctl = snd_pcm_lib_ioctl,
@@ -1142,7 +1142,7 @@ static struct snd_pcm_ops snd_ca0106_capture_1_ops = {
.pointer = snd_ca0106_pcm_pointer_capture,
};
-static struct snd_pcm_ops snd_ca0106_capture_2_ops = {
+static const struct snd_pcm_ops snd_ca0106_capture_2_ops = {
.open = snd_ca0106_pcm_open_2_capture,
.close = snd_ca0106_pcm_close_capture,
.ioctl = snd_pcm_lib_ioctl,
@@ -1153,7 +1153,7 @@ static struct snd_pcm_ops snd_ca0106_capture_2_ops = {
.pointer = snd_ca0106_pcm_pointer_capture,
};
-static struct snd_pcm_ops snd_ca0106_capture_3_ops = {
+static const struct snd_pcm_ops snd_ca0106_capture_3_ops = {
.open = snd_ca0106_pcm_open_3_capture,
.close = snd_ca0106_pcm_close_capture,
.ioctl = snd_pcm_lib_ioctl,
@@ -1164,7 +1164,7 @@ static struct snd_pcm_ops snd_ca0106_capture_3_ops = {
.pointer = snd_ca0106_pcm_pointer_capture,
};
-static struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = {
+static const struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = {
.open = snd_ca0106_pcm_open_playback_center_lfe,
.close = snd_ca0106_pcm_close_playback,
.ioctl = snd_pcm_lib_ioctl,
@@ -1175,7 +1175,7 @@ static struct snd_pcm_ops snd_ca0106_playback_center_lfe_ops = {
.pointer = snd_ca0106_pcm_pointer_playback,
};
-static struct snd_pcm_ops snd_ca0106_playback_unknown_ops = {
+static const struct snd_pcm_ops snd_ca0106_playback_unknown_ops = {
.open = snd_ca0106_pcm_open_playback_unknown,
.close = snd_ca0106_pcm_close_playback,
.ioctl = snd_pcm_lib_ioctl,
@@ -1186,7 +1186,7 @@ static struct snd_pcm_ops snd_ca0106_playback_unknown_ops = {
.pointer = snd_ca0106_pcm_pointer_playback,
};
-static struct snd_pcm_ops snd_ca0106_playback_rear_ops = {
+static const struct snd_pcm_ops snd_ca0106_playback_rear_ops = {
.open = snd_ca0106_pcm_open_playback_rear,
.close = snd_ca0106_pcm_close_playback,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index 24cdcba06d27..73f593526b2d 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -1838,7 +1838,7 @@ static int snd_cmipci_capture_spdif_close(struct snd_pcm_substream *substream)
/*
*/
-static struct snd_pcm_ops snd_cmipci_playback_ops = {
+static const struct snd_pcm_ops snd_cmipci_playback_ops = {
.open = snd_cmipci_playback_open,
.close = snd_cmipci_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1849,7 +1849,7 @@ static struct snd_pcm_ops snd_cmipci_playback_ops = {
.pointer = snd_cmipci_playback_pointer,
};
-static struct snd_pcm_ops snd_cmipci_capture_ops = {
+static const struct snd_pcm_ops snd_cmipci_capture_ops = {
.open = snd_cmipci_capture_open,
.close = snd_cmipci_capture_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1860,7 +1860,7 @@ static struct snd_pcm_ops snd_cmipci_capture_ops = {
.pointer = snd_cmipci_capture_pointer,
};
-static struct snd_pcm_ops snd_cmipci_playback2_ops = {
+static const struct snd_pcm_ops snd_cmipci_playback2_ops = {
.open = snd_cmipci_playback2_open,
.close = snd_cmipci_playback2_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1871,7 +1871,7 @@ static struct snd_pcm_ops snd_cmipci_playback2_ops = {
.pointer = snd_cmipci_capture_pointer, /* channel B */
};
-static struct snd_pcm_ops snd_cmipci_playback_spdif_ops = {
+static const struct snd_pcm_ops snd_cmipci_playback_spdif_ops = {
.open = snd_cmipci_playback_spdif_open,
.close = snd_cmipci_playback_spdif_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1882,7 +1882,7 @@ static struct snd_pcm_ops snd_cmipci_playback_spdif_ops = {
.pointer = snd_cmipci_playback_pointer,
};
-static struct snd_pcm_ops snd_cmipci_capture_spdif_ops = {
+static const struct snd_pcm_ops snd_cmipci_capture_spdif_ops = {
.open = snd_cmipci_capture_spdif_open,
.close = snd_cmipci_capture_spdif_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index c296fd0dbc9c..615d8a99d8c8 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -951,7 +951,7 @@ static int snd_cs4281_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_cs4281_playback_ops = {
+static const struct snd_pcm_ops snd_cs4281_playback_ops = {
.open = snd_cs4281_playback_open,
.close = snd_cs4281_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -962,7 +962,7 @@ static struct snd_pcm_ops snd_cs4281_playback_ops = {
.pointer = snd_cs4281_pointer,
};
-static struct snd_pcm_ops snd_cs4281_capture_ops = {
+static const struct snd_pcm_ops snd_cs4281_capture_ops = {
.open = snd_cs4281_capture_open,
.close = snd_cs4281_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c
index 2706f271a83b..528102cc2d5d 100644
--- a/sound/pci/cs46xx/cs46xx_lib.c
+++ b/sound/pci/cs46xx/cs46xx_lib.c
@@ -2460,7 +2460,7 @@ static int cs46xx_detect_codec(struct snd_cs46xx *chip, int codec)
udelay(10);
if (snd_cs46xx_codec_read(chip, AC97_RESET, codec) & 0x8000) {
dev_dbg(chip->card->dev,
- "seconadry codec not present\n");
+ "secondary codec not present\n");
return -ENXIO;
}
}
@@ -2503,7 +2503,7 @@ int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
chip->nr_ac97_codecs = 1;
#ifdef CONFIG_SND_CS46XX_NEW_DSP
- dev_dbg(chip->card->dev, "detecting seconadry codec\n");
+ dev_dbg(chip->card->dev, "detecting secondary codec\n");
/* try detect a secondary codec */
if (! cs46xx_detect_codec(chip, CS46XX_SECONDARY_CODEC_INDEX))
chip->nr_ac97_codecs = 2;
diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c
index d2951ed4bf71..4a0cbd2241d8 100644
--- a/sound/pci/cs46xx/dsp_spos.c
+++ b/sound/pci/cs46xx/dsp_spos.c
@@ -1441,7 +1441,7 @@ int cs46xx_dsp_scb_and_task_init (struct snd_cs46xx *chip)
if (chip->nr_ac97_codecs == 2) {
/* create CODEC tasklet for rear Center/LFE output
- slot 6 and 9 on seconadry CODEC */
+ slot 6 and 9 on secondary CODEC */
clfe_codec_out_scb = cs46xx_dsp_create_codec_out_scb(chip,"CodecOutSCB_CLFE",0x0030,0x0030,
CLFE_MIXER_SCB_ADDR,
CLFE_CODEC_SCB_ADDR,
diff --git a/sound/pci/cs5535audio/cs5535audio_pcm.c b/sound/pci/cs5535audio/cs5535audio_pcm.c
index 27fa57da8dc4..c208c1d8dbb2 100644
--- a/sound/pci/cs5535audio/cs5535audio_pcm.c
+++ b/sound/pci/cs5535audio/cs5535audio_pcm.c
@@ -380,7 +380,7 @@ static int snd_cs5535audio_capture_prepare(struct snd_pcm_substream *substream)
substream->runtime->rate);
}
-static struct snd_pcm_ops snd_cs5535audio_playback_ops = {
+static const struct snd_pcm_ops snd_cs5535audio_playback_ops = {
.open = snd_cs5535audio_playback_open,
.close = snd_cs5535audio_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -391,7 +391,7 @@ static struct snd_pcm_ops snd_cs5535audio_playback_ops = {
.pointer = snd_cs5535audio_pcm_pointer,
};
-static struct snd_pcm_ops snd_cs5535audio_capture_ops = {
+static const struct snd_pcm_ops snd_cs5535audio_capture_ops = {
.open = snd_cs5535audio_capture_open,
.close = snd_cs5535audio_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c
index 977a59855fa6..908658a00377 100644
--- a/sound/pci/ctxfi/ctatc.c
+++ b/sound/pci/ctxfi/ctatc.c
@@ -1623,7 +1623,7 @@ static int atc_resume(struct ct_atc *atc)
}
#endif
-static struct ct_atc atc_preset = {
+static const struct ct_atc atc_preset = {
.map_audio_buffer = ct_map_audio_buffer,
.unmap_audio_buffer = ct_unmap_audio_buffer,
.pcm_playback_prepare = atc_pcm_playback_prepare,
diff --git a/sound/pci/ctxfi/cthw20k2.c b/sound/pci/ctxfi/cthw20k2.c
index 9dc2950e1ab7..6414ecf93efa 100644
--- a/sound/pci/ctxfi/cthw20k2.c
+++ b/sound/pci/ctxfi/cthw20k2.c
@@ -1615,23 +1615,23 @@ static int hw_dac_init(struct hw *hw, const struct dac_conf *info)
int i;
struct regs_cs4382 cs_read = {0};
struct regs_cs4382 cs_def = {
- 0x00000001, /* Mode Control 1 */
- 0x00000000, /* Mode Control 2 */
- 0x00000084, /* Mode Control 3 */
- 0x00000000, /* Filter Control */
- 0x00000000, /* Invert Control */
- 0x00000024, /* Mixing Control Pair 1 */
- 0x00000000, /* Vol Control A1 */
- 0x00000000, /* Vol Control B1 */
- 0x00000024, /* Mixing Control Pair 2 */
- 0x00000000, /* Vol Control A2 */
- 0x00000000, /* Vol Control B2 */
- 0x00000024, /* Mixing Control Pair 3 */
- 0x00000000, /* Vol Control A3 */
- 0x00000000, /* Vol Control B3 */
- 0x00000024, /* Mixing Control Pair 4 */
- 0x00000000, /* Vol Control A4 */
- 0x00000000 /* Vol Control B4 */
+ .mode_control_1 = 0x00000001, /* Mode Control 1 */
+ .mode_control_2 = 0x00000000, /* Mode Control 2 */
+ .mode_control_3 = 0x00000084, /* Mode Control 3 */
+ .filter_control = 0x00000000, /* Filter Control */
+ .invert_control = 0x00000000, /* Invert Control */
+ .mix_control_P1 = 0x00000024, /* Mixing Control Pair 1 */
+ .vol_control_A1 = 0x00000000, /* Vol Control A1 */
+ .vol_control_B1 = 0x00000000, /* Vol Control B1 */
+ .mix_control_P2 = 0x00000024, /* Mixing Control Pair 2 */
+ .vol_control_A2 = 0x00000000, /* Vol Control A2 */
+ .vol_control_B2 = 0x00000000, /* Vol Control B2 */
+ .mix_control_P3 = 0x00000024, /* Mixing Control Pair 3 */
+ .vol_control_A3 = 0x00000000, /* Vol Control A3 */
+ .vol_control_B3 = 0x00000000, /* Vol Control B3 */
+ .mix_control_P4 = 0x00000024, /* Mixing Control Pair 4 */
+ .vol_control_A4 = 0x00000000, /* Vol Control A4 */
+ .vol_control_B4 = 0x00000000 /* Vol Control B4 */
};
if (hw->model == CTSB1270) {
diff --git a/sound/pci/ctxfi/ctpcm.c b/sound/pci/ctxfi/ctpcm.c
index d86c474ca5b6..974978041558 100644
--- a/sound/pci/ctxfi/ctpcm.c
+++ b/sound/pci/ctxfi/ctpcm.c
@@ -372,7 +372,7 @@ ct_pcm_capture_pointer(struct snd_pcm_substream *substream)
}
/* PCM operators for playback */
-static struct snd_pcm_ops ct_pcm_playback_ops = {
+static const struct snd_pcm_ops ct_pcm_playback_ops = {
.open = ct_pcm_playback_open,
.close = ct_pcm_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -385,7 +385,7 @@ static struct snd_pcm_ops ct_pcm_playback_ops = {
};
/* PCM operators for capture */
-static struct snd_pcm_ops ct_pcm_capture_ops = {
+static const struct snd_pcm_ops ct_pcm_capture_ops = {
.open = ct_pcm_capture_open,
.close = ct_pcm_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/ctxfi/ctvmem.c b/sound/pci/ctxfi/ctvmem.c
index 419306ef825f..520e19bc649f 100644
--- a/sound/pci/ctxfi/ctvmem.c
+++ b/sound/pci/ctxfi/ctvmem.c
@@ -166,11 +166,7 @@ static void ct_vm_unmap(struct ct_vm *vm, struct ct_vm_block *block)
static dma_addr_t
ct_get_ptp_phys(struct ct_vm *vm, int index)
{
- dma_addr_t addr;
-
- addr = (index >= CT_PTP_NUM) ? ~0UL : vm->ptp[index].addr;
-
- return addr;
+ return (index >= CT_PTP_NUM) ? ~0UL : vm->ptp[index].addr;
}
int ct_vm_create(struct ct_vm **rvm, struct pci_dev *pci)
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 1cb85aeb0cea..937071760bc4 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -1272,11 +1272,11 @@ static int snd_echo_mixer_info(struct snd_kcontrol *kcontrol,
chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
uinfo->value.integer.min = ECHOGAIN_MINOUT;
uinfo->value.integer.max = ECHOGAIN_MAXOUT;
uinfo->dimen.d[0] = num_busses_out(chip);
uinfo->dimen.d[1] = num_busses_in(chip);
+ uinfo->count = uinfo->dimen.d[0] * uinfo->dimen.d[1];
return 0;
}
@@ -1344,11 +1344,11 @@ static int snd_echo_vmixer_info(struct snd_kcontrol *kcontrol,
chip = snd_kcontrol_chip(kcontrol);
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 1;
uinfo->value.integer.min = ECHOGAIN_MINOUT;
uinfo->value.integer.max = ECHOGAIN_MAXOUT;
uinfo->dimen.d[0] = num_busses_out(chip);
uinfo->dimen.d[1] = num_pipes_out(chip);
+ uinfo->count = uinfo->dimen.d[0] * uinfo->dimen.d[1];
return 0;
}
@@ -1728,7 +1728,6 @@ static int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_info *uinfo)
{
uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
- uinfo->count = 96;
uinfo->value.integer.min = ECHOGAIN_MINOUT;
uinfo->value.integer.max = 0;
#ifdef ECHOCARD_HAS_VMIXER
@@ -1738,6 +1737,7 @@ static int snd_echo_vumeters_info(struct snd_kcontrol *kcontrol,
#endif
uinfo->dimen.d[1] = 16; /* 16 channels */
uinfo->dimen.d[2] = 2; /* 0=level, 1=peak */
+ uinfo->count = uinfo->dimen.d[0] * uinfo->dimen.d[1] * uinfo->dimen.d[2];
return 0;
}
@@ -2200,11 +2200,11 @@ static int snd_echo_resume(struct device *dev)
u32 pipe_alloc_mask;
int err;
- commpage_bak = kmalloc(sizeof(struct echoaudio), GFP_KERNEL);
+ commpage_bak = kmalloc(sizeof(*commpage), GFP_KERNEL);
if (commpage_bak == NULL)
return -ENOMEM;
commpage = chip->comm_page;
- memcpy(commpage_bak, commpage, sizeof(struct comm_page));
+ memcpy(commpage_bak, commpage, sizeof(*commpage));
err = init_hw(chip, chip->pci->device, chip->pci->subsystem_device);
if (err < 0) {
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 6d1b98d14327..921037ed8468 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -548,7 +548,7 @@ snd_emu10k1x_pcm_pointer(struct snd_pcm_substream *substream)
}
/* operators */
-static struct snd_pcm_ops snd_emu10k1x_playback_ops = {
+static const struct snd_pcm_ops snd_emu10k1x_playback_ops = {
.open = snd_emu10k1x_playback_open,
.close = snd_emu10k1x_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -694,7 +694,7 @@ snd_emu10k1x_pcm_pointer_capture(struct snd_pcm_substream *substream)
return ptr;
}
-static struct snd_pcm_ops snd_emu10k1x_capture_ops = {
+static const struct snd_pcm_ops snd_emu10k1x_capture_ops = {
.open = snd_emu10k1x_pcm_open_capture,
.close = snd_emu10k1x_pcm_close_capture,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index 14a305bd8a98..37be1e14d756 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -1364,7 +1364,7 @@ static int snd_emu10k1_capture_efx_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_emu10k1_playback_ops = {
+static const struct snd_pcm_ops snd_emu10k1_playback_ops = {
.open = snd_emu10k1_playback_open,
.close = snd_emu10k1_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1376,7 +1376,7 @@ static struct snd_pcm_ops snd_emu10k1_playback_ops = {
.page = snd_pcm_sgbuf_ops_page,
};
-static struct snd_pcm_ops snd_emu10k1_capture_ops = {
+static const struct snd_pcm_ops snd_emu10k1_capture_ops = {
.open = snd_emu10k1_capture_open,
.close = snd_emu10k1_capture_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1388,7 +1388,7 @@ static struct snd_pcm_ops snd_emu10k1_capture_ops = {
};
/* EFX playback */
-static struct snd_pcm_ops snd_emu10k1_efx_playback_ops = {
+static const struct snd_pcm_ops snd_emu10k1_efx_playback_ops = {
.open = snd_emu10k1_efx_playback_open,
.close = snd_emu10k1_efx_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1455,7 +1455,7 @@ int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device)
}
-static struct snd_pcm_ops snd_emu10k1_capture_mic_ops = {
+static const struct snd_pcm_ops snd_emu10k1_capture_mic_ops = {
.open = snd_emu10k1_capture_mic_open,
.close = snd_emu10k1_capture_mic_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1550,7 +1550,7 @@ static struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
.put = snd_emu10k1_pcm_efx_voices_mask_put
};
-static struct snd_pcm_ops snd_emu10k1_capture_efx_ops = {
+static const struct snd_pcm_ops snd_emu10k1_capture_efx_ops = {
.open = snd_emu10k1_capture_efx_open,
.close = snd_emu10k1_capture_efx_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1791,7 +1791,7 @@ static int snd_emu10k1_fx8010_playback_close(struct snd_pcm_substream *substream
return 0;
}
-static struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = {
+static const struct snd_pcm_ops snd_emu10k1_fx8010_playback_ops = {
.open = snd_emu10k1_fx8010_playback_open,
.close = snd_emu10k1_fx8010_playback_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c
index 3c60b433de9f..c11f1a29f35d 100644
--- a/sound/pci/emu10k1/p16v.c
+++ b/sound/pci/emu10k1/p16v.c
@@ -300,37 +300,29 @@ static int snd_p16v_pcm_open_capture(struct snd_pcm_substream *substream)
static int snd_p16v_pcm_hw_params_playback(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
- int result;
- result = snd_pcm_lib_malloc_pages(substream,
+ return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
- return result;
}
/* hw_params callback */
static int snd_p16v_pcm_hw_params_capture(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *hw_params)
{
- int result;
- result = snd_pcm_lib_malloc_pages(substream,
+ return snd_pcm_lib_malloc_pages(substream,
params_buffer_bytes(hw_params));
- return result;
}
/* hw_free callback */
static int snd_p16v_pcm_hw_free_playback(struct snd_pcm_substream *substream)
{
- int result;
- result = snd_pcm_lib_free_pages(substream);
- return result;
+ return snd_pcm_lib_free_pages(substream);
}
/* hw_free callback */
static int snd_p16v_pcm_hw_free_capture(struct snd_pcm_substream *substream)
{
- int result;
- result = snd_pcm_lib_free_pages(substream);
- return result;
+ return snd_pcm_lib_free_pages(substream);
}
@@ -601,7 +593,7 @@ snd_p16v_pcm_pointer_capture(struct snd_pcm_substream *substream)
}
/* operators */
-static struct snd_pcm_ops snd_p16v_playback_front_ops = {
+static const struct snd_pcm_ops snd_p16v_playback_front_ops = {
.open = snd_p16v_pcm_open_playback_front,
.close = snd_p16v_pcm_close_playback,
.ioctl = snd_pcm_lib_ioctl,
@@ -612,7 +604,7 @@ static struct snd_pcm_ops snd_p16v_playback_front_ops = {
.pointer = snd_p16v_pcm_pointer_playback,
};
-static struct snd_pcm_ops snd_p16v_capture_ops = {
+static const struct snd_pcm_ops snd_p16v_capture_ops = {
.open = snd_p16v_pcm_open_capture,
.close = snd_p16v_pcm_close_capture,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index 626cd2167d29..7e760fed0728 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -1227,7 +1227,7 @@ static int snd_ensoniq_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_ensoniq_playback1_ops = {
+static const struct snd_pcm_ops snd_ensoniq_playback1_ops = {
.open = snd_ensoniq_playback1_open,
.close = snd_ensoniq_playback1_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1238,7 +1238,7 @@ static struct snd_pcm_ops snd_ensoniq_playback1_ops = {
.pointer = snd_ensoniq_playback1_pointer,
};
-static struct snd_pcm_ops snd_ensoniq_playback2_ops = {
+static const struct snd_pcm_ops snd_ensoniq_playback2_ops = {
.open = snd_ensoniq_playback2_open,
.close = snd_ensoniq_playback2_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1249,7 +1249,7 @@ static struct snd_pcm_ops snd_ensoniq_playback2_ops = {
.pointer = snd_ensoniq_playback2_pointer,
};
-static struct snd_pcm_ops snd_ensoniq_capture_ops = {
+static const struct snd_pcm_ops snd_ensoniq_capture_ops = {
.open = snd_ensoniq_capture_open,
.close = snd_ensoniq_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c
index 8963d7688fb0..681355829484 100644
--- a/sound/pci/es1938.c
+++ b/sound/pci/es1938.c
@@ -992,7 +992,7 @@ static int snd_es1938_playback_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_es1938_playback_ops = {
+static const struct snd_pcm_ops snd_es1938_playback_ops = {
.open = snd_es1938_playback_open,
.close = snd_es1938_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1003,7 +1003,7 @@ static struct snd_pcm_ops snd_es1938_playback_ops = {
.pointer = snd_es1938_playback_pointer,
};
-static struct snd_pcm_ops snd_es1938_capture_ops = {
+static const struct snd_pcm_ops snd_es1938_capture_ops = {
.open = snd_es1938_capture_open,
.close = snd_es1938_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c
index 514f2604086e..8146fb76a4ad 100644
--- a/sound/pci/es1968.c
+++ b/sound/pci/es1968.c
@@ -1677,7 +1677,7 @@ static int snd_es1968_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_es1968_playback_ops = {
+static const struct snd_pcm_ops snd_es1968_playback_ops = {
.open = snd_es1968_playback_open,
.close = snd_es1968_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1688,7 +1688,7 @@ static struct snd_pcm_ops snd_es1968_playback_ops = {
.pointer = snd_es1968_pcm_pointer,
};
-static struct snd_pcm_ops snd_es1968_capture_ops = {
+static const struct snd_pcm_ops snd_es1968_capture_ops = {
.open = snd_es1968_capture_open,
.close = snd_es1968_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c
index bade9b907b92..c47287d79306 100644
--- a/sound/pci/fm801.c
+++ b/sound/pci/fm801.c
@@ -691,7 +691,7 @@ static int snd_fm801_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_fm801_playback_ops = {
+static const struct snd_pcm_ops snd_fm801_playback_ops = {
.open = snd_fm801_playback_open,
.close = snd_fm801_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -702,7 +702,7 @@ static struct snd_pcm_ops snd_fm801_playback_ops = {
.pointer = snd_fm801_playback_pointer,
};
-static struct snd_pcm_ops snd_fm801_capture_ops = {
+static const struct snd_pcm_ops snd_fm801_capture_ops = {
.open = snd_fm801_capture_open,
.close = snd_fm801_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 83741887faa1..9913be8532ab 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -3584,6 +3584,12 @@ static void setup_dig_out_stream(struct hda_codec *codec, hda_nid_t nid,
bool reset;
spdif = snd_hda_spdif_out_of_nid(codec, nid);
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (WARN_ON(spdif == NULL))
+ return;
+
curr_fmt = snd_hda_codec_read(codec, nid, 0,
AC_VERB_GET_STREAM_FORMAT, 0);
reset = codec->spdif_status_reset &&
@@ -3768,7 +3774,7 @@ int snd_hda_multi_out_analog_prepare(struct hda_codec *codec,
spdif = snd_hda_spdif_out_of_nid(codec, mout->dig_out_nid);
if (mout->dig_out_nid && mout->share_spdif &&
mout->dig_out_used != HDA_DIG_EXCLUSIVE) {
- if (chs == 2 &&
+ if (chs == 2 && spdif != NULL &&
snd_hda_is_supported_format(codec, mout->dig_out_nid,
format) &&
!(spdif->status & IEC958_AES0_NONAUDIO)) {
diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c
index 27de8015717d..500878556578 100644
--- a/sound/pci/hda/hda_controller.c
+++ b/sound/pci/hda/hda_controller.c
@@ -27,6 +27,12 @@
#include <linux/module.h>
#include <linux/pm_runtime.h>
#include <linux/slab.h>
+
+#ifdef CONFIG_X86
+/* for art-tsc conversion */
+#include <asm/tsc.h>
+#endif
+
#include <sound/core.h>
#include <sound/initval.h>
#include "hda_controller.h"
@@ -337,12 +343,173 @@ static snd_pcm_uframes_t azx_pcm_pointer(struct snd_pcm_substream *substream)
azx_get_position(chip, azx_dev));
}
+/*
+ * azx_scale64: Scale base by mult/div while not overflowing sanely
+ *
+ * Derived from scale64_check_overflow in kernel/time/timekeeping.c
+ *
+ * The tmestamps for a 48Khz stream can overflow after (2^64/10^9)/48K which
+ * is about 384307 ie ~4.5 days.
+ *
+ * This scales the calculation so that overflow will happen but after 2^64 /
+ * 48000 secs, which is pretty large!
+ *
+ * In caln below:
+ * base may overflow, but since there isn’t any additional division
+ * performed on base it’s OK
+ * rem can’t overflow because both are 32-bit values
+ */
+
+#ifdef CONFIG_X86
+static u64 azx_scale64(u64 base, u32 num, u32 den)
+{
+ u64 rem;
+
+ rem = do_div(base, den);
+
+ base *= num;
+ rem *= num;
+
+ do_div(rem, den);
+
+ return base + rem;
+}
+
+static int azx_get_sync_time(ktime_t *device,
+ struct system_counterval_t *system, void *ctx)
+{
+ struct snd_pcm_substream *substream = ctx;
+ struct azx_dev *azx_dev = get_azx_dev(substream);
+ struct azx_pcm *apcm = snd_pcm_substream_chip(substream);
+ struct azx *chip = apcm->chip;
+ struct snd_pcm_runtime *runtime;
+ u64 ll_counter, ll_counter_l, ll_counter_h;
+ u64 tsc_counter, tsc_counter_l, tsc_counter_h;
+ u32 wallclk_ctr, wallclk_cycles;
+ bool direction;
+ u32 dma_select;
+ u32 timeout = 200;
+ u32 retry_count = 0;
+
+ runtime = substream->runtime;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ direction = 1;
+ else
+ direction = 0;
+
+ /* 0th stream tag is not used, so DMA ch 0 is for 1st stream tag */
+ do {
+ timeout = 100;
+ dma_select = (direction << GTSCC_CDMAS_DMA_DIR_SHIFT) |
+ (azx_dev->core.stream_tag - 1);
+ snd_hdac_chip_writel(azx_bus(chip), GTSCC, dma_select);
+
+ /* Enable the capture */
+ snd_hdac_chip_updatel(azx_bus(chip), GTSCC, 0, GTSCC_TSCCI_MASK);
+
+ while (timeout) {
+ if (snd_hdac_chip_readl(azx_bus(chip), GTSCC) &
+ GTSCC_TSCCD_MASK)
+ break;
+
+ timeout--;
+ }
+
+ if (!timeout) {
+ dev_err(chip->card->dev, "GTSCC capture Timedout!\n");
+ return -EIO;
+ }
+
+ /* Read wall clock counter */
+ wallclk_ctr = snd_hdac_chip_readl(azx_bus(chip), WALFCC);
+
+ /* Read TSC counter */
+ tsc_counter_l = snd_hdac_chip_readl(azx_bus(chip), TSCCL);
+ tsc_counter_h = snd_hdac_chip_readl(azx_bus(chip), TSCCU);
+
+ /* Read Link counter */
+ ll_counter_l = snd_hdac_chip_readl(azx_bus(chip), LLPCL);
+ ll_counter_h = snd_hdac_chip_readl(azx_bus(chip), LLPCU);
+
+ /* Ack: registers read done */
+ snd_hdac_chip_writel(azx_bus(chip), GTSCC, GTSCC_TSCCD_SHIFT);
+
+ tsc_counter = (tsc_counter_h << TSCCU_CCU_SHIFT) |
+ tsc_counter_l;
+
+ ll_counter = (ll_counter_h << LLPC_CCU_SHIFT) | ll_counter_l;
+ wallclk_cycles = wallclk_ctr & WALFCC_CIF_MASK;
+
+ /*
+ * An error occurs near frame "rollover". The clocks in
+ * frame value indicates whether this error may have
+ * occurred. Here we use the value of 10 i.e.,
+ * HDA_MAX_CYCLE_OFFSET
+ */
+ if (wallclk_cycles < HDA_MAX_CYCLE_VALUE - HDA_MAX_CYCLE_OFFSET
+ && wallclk_cycles > HDA_MAX_CYCLE_OFFSET)
+ break;
+
+ /*
+ * Sleep before we read again, else we may again get
+ * value near to MAX_CYCLE. Try to sleep for different
+ * amount of time so we dont hit the same number again
+ */
+ udelay(retry_count++);
+
+ } while (retry_count != HDA_MAX_CYCLE_READ_RETRY);
+
+ if (retry_count == HDA_MAX_CYCLE_READ_RETRY) {
+ dev_err_ratelimited(chip->card->dev,
+ "Error in WALFCC cycle count\n");
+ return -EIO;
+ }
+
+ *device = ns_to_ktime(azx_scale64(ll_counter,
+ NSEC_PER_SEC, runtime->rate));
+ *device = ktime_add_ns(*device, (wallclk_cycles * NSEC_PER_SEC) /
+ ((HDA_MAX_CYCLE_VALUE + 1) * runtime->rate));
+
+ *system = convert_art_to_tsc(tsc_counter);
+
+ return 0;
+}
+
+#else
+static int azx_get_sync_time(ktime_t *device,
+ struct system_counterval_t *system, void *ctx)
+{
+ return -ENXIO;
+}
+#endif
+
+static int azx_get_crosststamp(struct snd_pcm_substream *substream,
+ struct system_device_crosststamp *xtstamp)
+{
+ return get_device_system_crosststamp(azx_get_sync_time,
+ substream, NULL, xtstamp);
+}
+
+static inline bool is_link_time_supported(struct snd_pcm_runtime *runtime,
+ struct snd_pcm_audio_tstamp_config *ts)
+{
+ if (runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME)
+ if (ts->type_requested == SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED)
+ return true;
+
+ return false;
+}
+
static int azx_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 azx_dev *azx_dev = get_azx_dev(substream);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct system_device_crosststamp xtstamp;
+ int ret;
u64 nsec;
if ((substream->runtime->hw.info & SNDRV_PCM_INFO_HAS_LINK_ATIME) &&
@@ -361,8 +528,37 @@ static int azx_get_time_info(struct snd_pcm_substream *substream,
audio_tstamp_report->accuracy_report = 1; /* rest of structure is valid */
audio_tstamp_report->accuracy = 42; /* 24 MHz WallClock == 42ns resolution */
- } else
+ } else if (is_link_time_supported(runtime, audio_tstamp_config)) {
+
+ ret = azx_get_crosststamp(substream, &xtstamp);
+ if (ret)
+ return ret;
+
+ switch (runtime->tstamp_type) {
+ case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC:
+ return -EINVAL;
+
+ case SNDRV_PCM_TSTAMP_TYPE_MONOTONIC_RAW:
+ *system_ts = ktime_to_timespec(xtstamp.sys_monoraw);
+ break;
+
+ default:
+ *system_ts = ktime_to_timespec(xtstamp.sys_realtime);
+ break;
+
+ }
+
+ *audio_ts = ktime_to_timespec(xtstamp.device);
+
+ audio_tstamp_report->actual_type =
+ SNDRV_PCM_AUDIO_TSTAMP_TYPE_LINK_SYNCHRONIZED;
+ audio_tstamp_report->accuracy_report = 1;
+ /* 24 MHz WallClock == 42ns resolution */
+ audio_tstamp_report->accuracy = 42;
+
+ } else {
audio_tstamp_report->actual_type = SNDRV_PCM_AUDIO_TSTAMP_TYPE_DEFAULT;
+ }
return 0;
}
@@ -412,6 +608,11 @@ static int azx_pcm_open(struct snd_pcm_substream *substream)
goto unlock;
}
runtime->private_data = azx_dev;
+
+ if (chip->gts_present)
+ azx_pcm_hw.info = azx_pcm_hw.info |
+ SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME;
+
runtime->hw = azx_pcm_hw;
runtime->hw.channels_min = hinfo->channels_min;
runtime->hw.channels_max = hinfo->channels_max;
@@ -495,7 +696,7 @@ static int azx_pcm_mmap(struct snd_pcm_substream *substream,
return snd_pcm_lib_default_mmap(substream, area);
}
-static struct snd_pcm_ops azx_pcm_ops = {
+static const struct snd_pcm_ops azx_pcm_ops = {
.open = azx_pcm_open,
.close = azx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h
index ec63bbf1ec6d..a50e0532622a 100644
--- a/sound/pci/hda/hda_controller.h
+++ b/sound/pci/hda/hda_controller.h
@@ -159,6 +159,9 @@ struct azx {
unsigned int region_requested:1;
unsigned int disabled:1; /* disabled by vga_switcheroo */
+ /* GTS present */
+ unsigned int gts_present:1;
+
#ifdef CONFIG_SND_HDA_DSP_LOADER
struct azx_dev saved_azx_dev;
#endif
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index 320445f3bf73..e7c8f4f076d5 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -2492,10 +2492,6 @@ static int create_loopback_mixing_ctl(struct hda_codec *codec)
if (!snd_hda_gen_add_kctl(spec, NULL, &loopback_mixing_enum))
return -ENOMEM;
spec->have_aamix_ctl = 1;
- /* if no explicit aamix path is present (e.g. for Realtek codecs),
- * enable aamix as default -- just for compatibility
- */
- spec->aamix_mode = !has_aamix_out_paths(spec);
return 0;
}
@@ -3977,6 +3973,8 @@ static hda_nid_t set_path_power(struct hda_codec *codec, hda_nid_t nid,
for (n = 0; n < spec->paths.used; n++) {
path = snd_array_elem(&spec->paths, n);
+ if (!path->depth)
+ continue;
if (path->path[0] == nid ||
path->path[path->depth - 1] == nid) {
bool pin_old = path->pin_enabled;
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index 9a0d1445ca5c..c3469f756ec2 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -54,6 +54,7 @@
/* for snoop control */
#include <asm/pgtable.h>
#include <asm/cacheflush.h>
+#include <asm/cpufeature.h>
#endif
#include <sound/core.h>
#include <sound/initval.h>
@@ -365,8 +366,12 @@ enum {
#define IS_SKL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa170)
#define IS_SKL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d70)
+#define IS_KBL(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa171)
+#define IS_KBL_LP(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x9d71)
+#define IS_KBL_H(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0xa2f0)
#define IS_BXT(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x5a98)
-#define IS_SKL_PLUS(pci) (IS_SKL(pci) || IS_SKL_LP(pci) || IS_BXT(pci))
+#define IS_SKL_PLUS(pci) (IS_SKL(pci) || IS_SKL_LP(pci) || IS_BXT(pci)) || \
+ IS_KBL(pci) || IS_KBL_LP(pci) || IS_KBL_H(pci)
static char *driver_short_names[] = {
[AZX_DRIVER_ICH] = "HDA Intel",
@@ -902,20 +907,23 @@ static int azx_resume(struct device *dev)
struct snd_card *card = dev_get_drvdata(dev);
struct azx *chip;
struct hda_intel *hda;
+ struct hdac_bus *bus;
if (!card)
return 0;
chip = card->private_data;
hda = container_of(chip, struct hda_intel, chip);
+ bus = azx_bus(chip);
if (chip->disabled || hda->init_failed || !chip->running)
return 0;
- if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
- && hda->need_i915_power) {
- snd_hdac_display_power(azx_bus(chip), true);
- snd_hdac_i915_set_bclk(azx_bus(chip));
+ if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+ snd_hdac_display_power(bus, true);
+ if (hda->need_i915_power)
+ snd_hdac_i915_set_bclk(bus);
}
+
if (chip->msi)
if (pci_enable_msi(pci) < 0)
chip->msi = 0;
@@ -925,6 +933,11 @@ static int azx_resume(struct device *dev)
hda_intel_init_chip(chip, true);
+ /* power down again for link-controlled chips */
+ if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) &&
+ !hda->need_i915_power)
+ snd_hdac_display_power(bus, false);
+
snd_power_change_state(card, SNDRV_CTL_POWER_D0);
trace_azx_resume(chip);
@@ -1004,6 +1017,7 @@ static int azx_runtime_resume(struct device *dev)
chip = card->private_data;
hda = container_of(chip, struct hda_intel, chip);
+ bus = azx_bus(chip);
if (chip->disabled || hda->init_failed)
return 0;
@@ -1011,15 +1025,9 @@ static int azx_runtime_resume(struct device *dev)
return 0;
if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
- bus = azx_bus(chip);
- if (hda->need_i915_power) {
- snd_hdac_display_power(bus, true);
+ snd_hdac_display_power(bus, true);
+ if (hda->need_i915_power)
snd_hdac_i915_set_bclk(bus);
- } else {
- /* toggle codec wakeup bit for STATESTS read */
- snd_hdac_set_codec_wakeup(bus, true);
- snd_hdac_set_codec_wakeup(bus, false);
- }
}
/* Read STATESTS before controller reset */
@@ -1039,6 +1047,11 @@ static int azx_runtime_resume(struct device *dev)
azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
~STATESTS_INT_MASK);
+ /* power down again for link-controlled chips */
+ if ((chip->driver_caps & AZX_DCAPS_I915_POWERWELL) &&
+ !hda->need_i915_power)
+ snd_hdac_display_power(bus, false);
+
trace_azx_runtime_resume(chip);
return 0;
}
@@ -1214,8 +1227,10 @@ static int azx_free(struct azx *chip)
if (use_vga_switcheroo(hda)) {
if (chip->disabled && hda->probe_continued)
snd_hda_unlock_devices(&chip->bus);
- if (hda->vga_switcheroo_registered)
+ if (hda->vga_switcheroo_registered) {
vga_switcheroo_unregister_client(chip->pci);
+ vga_switcheroo_fini_domain_pm_ops(chip->card->dev);
+ }
}
if (bus->chip_init) {
@@ -1649,6 +1664,22 @@ static int azx_first_init(struct azx *chip)
return -ENXIO;
}
+ if (IS_SKL_PLUS(pci))
+ snd_hdac_bus_parse_capabilities(bus);
+
+ /*
+ * Some Intel CPUs has always running timer (ART) feature and
+ * controller may have Global time sync reporting capability, so
+ * check both of these before declaring synchronized time reporting
+ * capability SNDRV_PCM_INFO_HAS_LINK_SYNCHRONIZED_ATIME
+ */
+ chip->gts_present = false;
+
+#ifdef CONFIG_X86
+ if (bus->ppcap && boot_cpu_has(X86_FEATURE_ART))
+ chip->gts_present = true;
+#endif
+
if (chip->msi) {
if (chip->driver_caps & AZX_DCAPS_NO_MSI64) {
dev_dbg(card->dev, "Disabling 64bit MSI\n");
@@ -2181,6 +2212,15 @@ static const struct pci_device_id azx_ids[] = {
/* Sunrise Point-LP */
{ PCI_DEVICE(0x8086, 0x9d70),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
+ /* Kabylake */
+ { PCI_DEVICE(0x8086, 0xa171),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
+ /* Kabylake-LP */
+ { PCI_DEVICE(0x8086, 0x9d71),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
+ /* Kabylake-H */
+ { PCI_DEVICE(0x8086, 0xa2f0),
+ .driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_SKYLAKE },
/* Broxton-P(Apollolake) */
{ PCI_DEVICE(0x8086, 0x5a98),
.driver_data = AZX_DRIVER_PCH | AZX_DCAPS_INTEL_BROXTON },
@@ -2250,10 +2290,14 @@ static const struct pci_device_id azx_ids[] = {
{ PCI_DEVICE(0x1022, 0x780d),
.driver_data = AZX_DRIVER_GENERIC | AZX_DCAPS_PRESET_ATI_SB },
/* ATI HDMI */
+ { PCI_DEVICE(0x1002, 0x0002),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
{ PCI_DEVICE(0x1002, 0x1308),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
{ PCI_DEVICE(0x1002, 0x157a),
.driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
+ { PCI_DEVICE(0x1002, 0x15b3),
+ .driver_data = AZX_DRIVER_ATIHDMI_NS | AZX_DCAPS_PRESET_ATI_HDMI_NS },
{ PCI_DEVICE(0x1002, 0x793b),
.driver_data = AZX_DRIVER_ATIHDMI | AZX_DCAPS_PRESET_ATI_HDMI },
{ PCI_DEVICE(0x1002, 0x7919),
diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c
index 17fd81736d3d..0621920f7617 100644
--- a/sound/pci/hda/hda_tegra.c
+++ b/sound/pci/hda/hda_tegra.c
@@ -115,20 +115,20 @@ static int substream_free_pages(struct azx *chip,
/*
* Register access ops. Tegra HDA register access is DWORD only.
*/
-static void hda_tegra_writel(u32 value, u32 *addr)
+static void hda_tegra_writel(u32 value, u32 __iomem *addr)
{
writel(value, addr);
}
-static u32 hda_tegra_readl(u32 *addr)
+static u32 hda_tegra_readl(u32 __iomem *addr)
{
return readl(addr);
}
-static void hda_tegra_writew(u16 value, u16 *addr)
+static void hda_tegra_writew(u16 value, u16 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
- void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
@@ -137,20 +137,20 @@ static void hda_tegra_writew(u16 value, u16 *addr)
writel(v, dword_addr);
}
-static u16 hda_tegra_readw(u16 *addr)
+static u16 hda_tegra_readw(u16 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
- void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
return (v >> shift) & 0xffff;
}
-static void hda_tegra_writeb(u8 value, u8 *addr)
+static void hda_tegra_writeb(u8 value, u8 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
- void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
@@ -159,10 +159,10 @@ static void hda_tegra_writeb(u8 value, u8 *addr)
writel(v, dword_addr);
}
-static u8 hda_tegra_readb(u8 *addr)
+static u8 hda_tegra_readb(u8 __iomem *addr)
{
unsigned int shift = ((unsigned long)(addr) & 0x3) << 3;
- void *dword_addr = (void *)((unsigned long)(addr) & ~0x3);
+ void __iomem *dword_addr = (void __iomem *)((unsigned long)(addr) & ~0x3);
u32 v;
v = readl(dword_addr);
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 9ceb2bc36e68..ad06866d7c69 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -4018,7 +4018,7 @@ static int ca0132_build_controls(struct hda_codec *codec)
/*
* PCM
*/
-static struct hda_pcm_stream ca0132_pcm_analog_playback = {
+static const struct hda_pcm_stream ca0132_pcm_analog_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 6,
@@ -4029,7 +4029,7 @@ static struct hda_pcm_stream ca0132_pcm_analog_playback = {
},
};
-static struct hda_pcm_stream ca0132_pcm_analog_capture = {
+static const struct hda_pcm_stream ca0132_pcm_analog_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@@ -4040,7 +4040,7 @@ static struct hda_pcm_stream ca0132_pcm_analog_capture = {
},
};
-static struct hda_pcm_stream ca0132_pcm_digital_playback = {
+static const struct hda_pcm_stream ca0132_pcm_digital_playback = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@@ -4052,7 +4052,7 @@ static struct hda_pcm_stream ca0132_pcm_digital_playback = {
},
};
-static struct hda_pcm_stream ca0132_pcm_digital_capture = {
+static const struct hda_pcm_stream ca0132_pcm_digital_capture = {
.substreams = 1,
.channels_min = 2,
.channels_max = 2,
@@ -4614,7 +4614,7 @@ static void ca0132_free(struct hda_codec *codec)
kfree(codec->spec);
}
-static struct hda_codec_ops ca0132_patch_ops = {
+static const struct hda_codec_ops ca0132_patch_ops = {
.build_controls = ca0132_build_controls,
.build_pcms = ca0132_build_pcms,
.init = ca0132_init,
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 56fefbd85782..ed62748a6d55 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -261,6 +261,7 @@ enum {
CXT_FIXUP_HP_530,
CXT_FIXUP_CAP_MIX_AMP_5047,
CXT_FIXUP_MUTE_LED_EAPD,
+ CXT_FIXUP_HP_SPECTRE,
};
/* for hda_fixup_thinkpad_acpi() */
@@ -765,6 +766,14 @@ static const struct hda_fixup cxt_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_mute_led_eapd,
},
+ [CXT_FIXUP_HP_SPECTRE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ /* enable NID 0x1d for the speaker on top */
+ { 0x1d, 0x91170111 },
+ { }
+ }
+ },
};
static const struct snd_pci_quirk cxt5045_fixups[] = {
@@ -814,6 +823,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1025, 0x054c, "Acer Aspire 3830TG", CXT_FIXUP_ASPIRE_DMIC),
SND_PCI_QUIRK(0x1025, 0x054f, "Acer Aspire 4830T", CXT_FIXUP_ASPIRE_DMIC),
+ SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
SND_PCI_QUIRK(0x152d, 0x0833, "OLPC XO-1.5", CXT_FIXUP_OLPC_XO),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index d0d5ad8beac5..56e5204ac9c1 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -1680,6 +1680,11 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
mutex_lock(&codec->spdif_mutex);
spdif = snd_hda_spdif_out_of_nid(codec, cvt_nid);
+ /* Add sanity check to pass klockwork check.
+ * This should never happen.
+ */
+ if (WARN_ON(spdif == NULL))
+ return true;
non_pcm = !!(spdif->status & IEC958_AES0_NONAUDIO);
mutex_unlock(&codec->spdif_mutex);
return non_pcm;
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index d53c25e7a1c1..b58e8c76346a 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -346,6 +346,9 @@ static void alc_fill_eapd_coef(struct hda_codec *codec)
case 0x10ec0234:
case 0x10ec0274:
case 0x10ec0294:
+ case 0x10ec0700:
+ case 0x10ec0701:
+ case 0x10ec0703:
alc_update_coef_idx(codec, 0x10, 1<<15, 0);
break;
case 0x10ec0662:
@@ -2655,6 +2658,7 @@ enum {
ALC269_TYPE_ALC256,
ALC269_TYPE_ALC225,
ALC269_TYPE_ALC294,
+ ALC269_TYPE_ALC700,
};
/*
@@ -2686,6 +2690,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
case ALC269_TYPE_ALC256:
case ALC269_TYPE_ALC225:
case ALC269_TYPE_ALC294:
+ case ALC269_TYPE_ALC700:
ssids = alc269_ssids;
break;
default:
@@ -3618,13 +3623,20 @@ static void alc269_fixup_hp_line1_mic1_led(struct hda_codec *codec,
static void alc_headset_mode_unplugged(struct hda_codec *codec)
{
static struct coef_fw coef0255[] = {
- WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */
WRITE_COEF(0x45, 0xd089), /* UAJ function set to menual mode */
UPDATE_COEFEX(0x57, 0x05, 1<<14, 0), /* Direct Drive HP Amp control(Set to verb control)*/
WRITE_COEF(0x06, 0x6104), /* Set MIC2 Vref gate with HP */
WRITE_COEFEX(0x57, 0x03, 0x8aa6), /* Direct Drive HP Amp control */
{}
};
+ static struct coef_fw coef0255_1[] = {
+ WRITE_COEF(0x1b, 0x0c0b), /* LDO and MISC control */
+ {}
+ };
+ static struct coef_fw coef0256[] = {
+ WRITE_COEF(0x1b, 0x0c4b), /* LDO and MISC control */
+ {}
+ };
static struct coef_fw coef0233[] = {
WRITE_COEF(0x1b, 0x0c0b),
WRITE_COEF(0x45, 0xc429),
@@ -3677,7 +3689,11 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
switch (codec->core.vendor_id) {
case 0x10ec0255:
+ alc_process_coef_fw(codec, coef0255_1);
+ alc_process_coef_fw(codec, coef0255);
+ break;
case 0x10ec0256:
+ alc_process_coef_fw(codec, coef0256);
alc_process_coef_fw(codec, coef0255);
break;
case 0x10ec0233:
@@ -3702,6 +3718,9 @@ static void alc_headset_mode_unplugged(struct hda_codec *codec)
case 0x10ec0295:
alc_process_coef_fw(codec, coef0225);
break;
+ case 0x10ec0867:
+ alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+ break;
}
codec_dbg(codec, "Headset jack set to unplugged mode.\n");
}
@@ -3789,6 +3808,9 @@ static void alc_headset_mode_mic_in(struct hda_codec *codec, hda_nid_t hp_pin,
alc_process_coef_fw(codec, coef0293);
snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
break;
+ case 0x10ec0867:
+ alc_update_coefex_idx(codec, 0x57, 0x5, 0, 1<<14);
+ /* fallthru */
case 0x10ec0662:
snd_hda_set_pin_ctl_cache(codec, hp_pin, 0);
snd_hda_set_pin_ctl_cache(codec, mic_pin, PIN_VREF50);
@@ -3883,6 +3905,9 @@ static void alc_headset_mode_default(struct hda_codec *codec)
case 0x10ec0668:
alc_process_coef_fw(codec, coef0688);
break;
+ case 0x10ec0867:
+ alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+ break;
}
codec_dbg(codec, "Headset jack set to headphone (default) mode.\n");
}
@@ -3896,6 +3921,12 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
WRITE_COEFEX(0x57, 0x03, 0x8ea6),
{}
};
+ static struct coef_fw coef0256[] = {
+ WRITE_COEF(0x45, 0xd489), /* Set to CTIA type */
+ WRITE_COEF(0x1b, 0x0c6b),
+ WRITE_COEFEX(0x57, 0x03, 0x8ea6),
+ {}
+ };
static struct coef_fw coef0233[] = {
WRITE_COEF(0x45, 0xd429),
WRITE_COEF(0x1b, 0x0c2b),
@@ -3936,9 +3967,11 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
switch (codec->core.vendor_id) {
case 0x10ec0255:
- case 0x10ec0256:
alc_process_coef_fw(codec, coef0255);
break;
+ case 0x10ec0256:
+ alc_process_coef_fw(codec, coef0256);
+ break;
case 0x10ec0233:
case 0x10ec0283:
alc_process_coef_fw(codec, coef0233);
@@ -3965,6 +3998,9 @@ static void alc_headset_mode_ctia(struct hda_codec *codec)
case 0x10ec0295:
alc_process_coef_fw(codec, coef0225);
break;
+ case 0x10ec0867:
+ alc_update_coefex_idx(codec, 0x57, 0x5, 1<<14, 0);
+ break;
}
codec_dbg(codec, "Headset jack set to iPhone-style headset mode.\n");
}
@@ -3978,6 +4014,12 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
WRITE_COEFEX(0x57, 0x03, 0x8ea6),
{}
};
+ static struct coef_fw coef0256[] = {
+ WRITE_COEF(0x45, 0xe489), /* Set to OMTP Type */
+ WRITE_COEF(0x1b, 0x0c6b),
+ WRITE_COEFEX(0x57, 0x03, 0x8ea6),
+ {}
+ };
static struct coef_fw coef0233[] = {
WRITE_COEF(0x45, 0xe429),
WRITE_COEF(0x1b, 0x0c2b),
@@ -4018,9 +4060,11 @@ static void alc_headset_mode_omtp(struct hda_codec *codec)
switch (codec->core.vendor_id) {
case 0x10ec0255:
- case 0x10ec0256:
alc_process_coef_fw(codec, coef0255);
break;
+ case 0x10ec0256:
+ alc_process_coef_fw(codec, coef0256);
+ break;
case 0x10ec0233:
case 0x10ec0283:
alc_process_coef_fw(codec, coef0233);
@@ -4134,6 +4178,9 @@ static void alc_determine_headset_type(struct hda_codec *codec)
val = alc_read_coef_idx(codec, 0x46);
is_ctia = (val & 0x00f0) == 0x00f0;
break;
+ case 0x10ec0867:
+ is_ctia = true;
+ break;
}
codec_dbg(codec, "Headset jack detected iPhone-style headset: %s\n",
@@ -4266,7 +4313,7 @@ static void alc_fixup_headset_mode_no_hp_mic(struct hda_codec *codec,
static void alc255_set_default_jack_type(struct hda_codec *codec)
{
/* Set to iphone type */
- static struct coef_fw fw[] = {
+ static struct coef_fw alc255fw[] = {
WRITE_COEF(0x1b, 0x880b),
WRITE_COEF(0x45, 0xd089),
WRITE_COEF(0x1b, 0x080b),
@@ -4274,7 +4321,22 @@ static void alc255_set_default_jack_type(struct hda_codec *codec)
WRITE_COEF(0x1b, 0x0c0b),
{}
};
- alc_process_coef_fw(codec, fw);
+ static struct coef_fw alc256fw[] = {
+ WRITE_COEF(0x1b, 0x884b),
+ WRITE_COEF(0x45, 0xd089),
+ WRITE_COEF(0x1b, 0x084b),
+ WRITE_COEF(0x46, 0x0004),
+ WRITE_COEF(0x1b, 0x0c4b),
+ {}
+ };
+ switch (codec->core.vendor_id) {
+ case 0x10ec0255:
+ alc_process_coef_fw(codec, alc255fw);
+ break;
+ case 0x10ec0256:
+ alc_process_coef_fw(codec, alc256fw);
+ break;
+ }
msleep(30);
}
@@ -4627,6 +4689,22 @@ static void alc290_fixup_mono_speakers(struct hda_codec *codec,
}
}
+static void alc298_fixup_speaker_volume(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ /* The speaker is routed to the Node 0x06 by a mistake, as a result
+ we can't adjust the speaker's volume since this node does not has
+ Amp-out capability. we change the speaker's route to:
+ Node 0x02 (Audio Output) -> Node 0x0c (Audio Mixer) -> Node 0x17 (
+ Pin Complex), since Node 0x02 has Amp-out caps, we can adjust
+ speaker's volume now. */
+
+ hda_nid_t conn1[1] = { 0x0c };
+ snd_hda_override_conn_list(codec, 0x17, 1, conn1);
+ }
+}
+
/* Hook to update amp GPIO4 for automute */
static void alc280_hp_gpio4_automute_hook(struct hda_codec *codec,
struct hda_jack_callback *jack)
@@ -4750,7 +4828,7 @@ enum {
ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC292_FIXUP_TPT440_DOCK,
ALC292_FIXUP_TPT440,
- ALC283_FIXUP_BXBT2807_MIC,
+ ALC283_FIXUP_HEADSET_MIC,
ALC255_FIXUP_DELL_WMI_MIC_MUTE_LED,
ALC282_FIXUP_ASPIRE_V5_PINS,
ALC280_FIXUP_HP_GPIO4,
@@ -4776,6 +4854,8 @@ enum {
ALC280_FIXUP_HP_HEADSET_MIC,
ALC221_FIXUP_HP_FRONT_MIC,
ALC292_FIXUP_TPT460,
+ ALC298_FIXUP_SPK_VOLUME,
+ ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -5184,6 +5264,8 @@ static const struct hda_fixup alc269_fixups[] = {
[ALC269_FIXUP_THINKPAD_ACPI] = {
.type = HDA_FIXUP_FUNC,
.v.func = hda_fixup_thinkpad_acpi,
+ .chained = true,
+ .chain_id = ALC269_FIXUP_SKU_IGNORE,
},
[ALC269_FIXUP_DMIC_THINKPAD_ACPI] = {
.type = HDA_FIXUP_FUNC,
@@ -5242,7 +5324,7 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC292_FIXUP_TPT440_DOCK,
},
- [ALC283_FIXUP_BXBT2807_MIC] = {
+ [ALC283_FIXUP_HEADSET_MIC] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
{ 0x19, 0x04a110f0 },
@@ -5431,6 +5513,21 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC293_FIXUP_LENOVO_SPK_NOISE,
},
+ [ALC298_FIXUP_SPK_VOLUME] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc298_fixup_speaker_volume,
+ .chained = true,
+ .chain_id = ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
+ },
+ [ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x1b, 0x90170151 },
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_DELL1_MIC_NO_PRESENCE
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -5475,8 +5572,10 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x1028, 0x06df, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
SND_PCI_QUIRK(0x1028, 0x06e0, "Dell", ALC293_FIXUP_DISABLE_AAMIX_MULTIJACK),
SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13 9350", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
+ SND_PCI_QUIRK(0x1028, 0x0706, "Dell Inspiron 7559", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER),
SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE),
SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE),
+ SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME),
SND_PCI_QUIRK(0x1028, 0x164a, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x1028, 0x164b, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x1586, "HP", ALC269_FIXUP_HP_MUTE_LED_MIC2),
@@ -5565,7 +5664,8 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x10cf, 0x1757, "Lifebook E752", ALC269_FIXUP_LIFEBOOK_HP_PIN),
SND_PCI_QUIRK(0x10cf, 0x1845, "Lifebook U904", ALC269_FIXUP_LIFEBOOK_EXTMIC),
SND_PCI_QUIRK(0x144d, 0xc109, "Samsung Ativ book 9 (NP900X3G)", ALC269_FIXUP_INV_DMIC),
- SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_BXBT2807_MIC),
+ SND_PCI_QUIRK(0x1458, 0xfa53, "Gigabyte BXBT-2807", ALC283_FIXUP_HEADSET_MIC),
+ SND_PCI_QUIRK(0x1462, 0xb120, "MSI Cubi MS-B120", ALC283_FIXUP_HEADSET_MIC),
SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
SND_PCI_QUIRK(0x17aa, 0x21b8, "Thinkpad Edge 14", ALC269_FIXUP_SKU_IGNORE),
@@ -5587,6 +5687,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x2218, "Thinkpad X1 Carbon 2nd", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2223, "ThinkPad T550", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK),
+ SND_PCI_QUIRK(0x17aa, 0x2231, "Thinkpad T560", ALC292_FIXUP_TPT460),
SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC292_FIXUP_TPT460),
SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
@@ -5602,6 +5703,9 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x503c, "Thinkpad L450", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x504a, "ThinkPad X260", ALC292_FIXUP_TPT440_DOCK),
SND_PCI_QUIRK(0x17aa, 0x504b, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
+ SND_PCI_QUIRK(0x17aa, 0x5050, "Thinkpad T560p", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x5051, "Thinkpad L460", ALC292_FIXUP_TPT460),
+ SND_PCI_QUIRK(0x17aa, 0x5053, "Thinkpad T460", ALC292_FIXUP_TPT460),
SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
@@ -5687,7 +5791,6 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{}
};
#define ALC225_STANDARD_PINS \
- {0x12, 0xb7a60130}, \
{0x21, 0x04211020}
#define ALC256_STANDARD_PINS \
@@ -5705,6 +5808,13 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{0x14, 0x90170110}, \
{0x15, 0x0221401f}
+#define ALC295_STANDARD_PINS \
+ {0x12, 0xb7a60130}, \
+ {0x14, 0x90170110}, \
+ {0x17, 0x21014020}, \
+ {0x18, 0x21a19030}, \
+ {0x21, 0x04211020}
+
#define ALC298_STANDARD_PINS \
{0x12, 0x90a60130}, \
{0x21, 0x03211020}
@@ -5712,10 +5822,24 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
+ {0x14, 0x901701a0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
+ {0x14, 0x901701b0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60150},
{0x14, 0x901701a0}),
SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60150},
{0x14, 0x901701b0}),
+ SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC225_STANDARD_PINS,
+ {0x12, 0xb7a60130},
+ {0x1b, 0x90170110}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
{0x14, 0x90170110},
{0x21, 0x02211020}),
@@ -5731,10 +5855,18 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x14, 0x90170120},
{0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x02011020},
+ {0x21, 0x0221101f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x14, 0x90170130},
{0x1b, 0x01014020},
{0x21, 0x0221103f}),
SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x14, 0x90170130},
+ {0x1b, 0x02011020},
+ {0x21, 0x0221103f}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x14, 0x90170150},
{0x1b, 0x02011020},
{0x21, 0x0221105f}),
@@ -5775,11 +5907,27 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x12, 0x90a60180},
{0x14, 0x90170130},
{0x21, 0x02211040}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1028, "Dell Inspiron 5565", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60180},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
{0x12, 0x90a60160},
{0x14, 0x90170120},
{0x21, 0x02211030}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60170},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell Inspiron 5468", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0x90a60180},
+ {0x14, 0x90170120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
+ {0x12, 0xb7a60130},
+ {0x14, 0x90170110},
+ {0x21, 0x02211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC256_STANDARD_PINS),
SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
{0x12, 0x90a60130},
@@ -5890,6 +6038,8 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
SND_HDA_PIN_QUIRK(0x10ec0293, 0x1028, "Dell", ALC293_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC292_STANDARD_PINS,
{0x13, 0x90a60140}),
+ SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE,
+ ALC295_STANDARD_PINS),
SND_HDA_PIN_QUIRK(0x10ec0298, 0x1028, "Dell", ALC298_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC298_STANDARD_PINS,
{0x17, 0x90170110}),
@@ -6053,6 +6203,14 @@ static int patch_alc269(struct hda_codec *codec)
case 0x10ec0294:
spec->codec_variant = ALC269_TYPE_ALC294;
break;
+ case 0x10ec0700:
+ case 0x10ec0701:
+ case 0x10ec0703:
+ spec->codec_variant = ALC269_TYPE_ALC700;
+ spec->gen.mixer_nid = 0; /* ALC700 does not have any loopback mixer path */
+ alc_update_coef_idx(codec, 0x4a, 0, 1 << 15); /* Combo jack auto trigger control */
+ break;
+
}
if (snd_hda_codec_read(codec, 0x51, 0, AC_VERB_PARAMETERS, 0) == 0x10ec5505) {
@@ -6452,6 +6610,9 @@ enum {
ALC668_FIXUP_DELL_XPS13,
ALC662_FIXUP_ASUS_Nx50,
ALC668_FIXUP_ASUS_Nx51,
+ ALC891_FIXUP_HEADSET_MODE,
+ ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
+ ALC662_FIXUP_ACER_VERITON,
};
static const struct hda_fixup alc662_fixups[] = {
@@ -6707,6 +6868,27 @@ static const struct hda_fixup alc662_fixups[] = {
.chained = true,
.chain_id = ALC662_FIXUP_BASS_CHMAP,
},
+ [ALC891_FIXUP_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode,
+ },
+ [ALC891_FIXUP_DELL_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC891_FIXUP_HEADSET_MODE
+ },
+ [ALC662_FIXUP_ACER_VERITON] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x15, 0x50170120 }, /* no internal speaker */
+ { }
+ }
+ },
};
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@@ -6745,6 +6927,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo Ideapad Y550P", ALC662_FIXUP_IDEAPAD),
SND_PCI_QUIRK(0x17aa, 0x3a0d, "Lenovo Ideapad Y550", ALC662_FIXUP_IDEAPAD),
SND_PCI_QUIRK(0x19da, 0xa130, "Zotac Z68", ALC662_FIXUP_ZOTAC_Z68),
+ SND_PCI_QUIRK(0x1b0a, 0x01b8, "ACER Veriton", ALC662_FIXUP_ACER_VERITON),
SND_PCI_QUIRK(0x1b35, 0x2206, "CZC P10T", ALC662_FIXUP_CZC_P10T),
#if 0
@@ -6823,6 +7006,11 @@ static const struct hda_model_fixup alc662_fixup_models[] = {
};
static const struct snd_hda_pin_quirk alc662_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec0867, 0x1028, "Dell", ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
+ {0x17, 0x02211010},
+ {0x18, 0x01a19030},
+ {0x1a, 0x01813040},
+ {0x21, 0x01014020}),
SND_HDA_PIN_QUIRK(0x10ec0662, 0x1028, "Dell", ALC662_FIXUP_DELL_MIC_NO_PRESENCE,
{0x14, 0x01014010},
{0x18, 0x01a19020},
@@ -7008,7 +7196,10 @@ static const struct hda_device_id snd_hda_id_realtek[] = {
HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662),
HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662),
HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680),
- HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc882),
+ HDA_CODEC_ENTRY(0x10ec0700, "ALC700", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0701, "ALC701", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0703, "ALC703", patch_alc269),
+ HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc662),
HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880),
HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882),
HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882),
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index 8ae3bb7975d1..b4aa4c1370a8 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -847,7 +847,7 @@ static int snd_ice1712_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_ice1712_playback_ops = {
+static const struct snd_pcm_ops snd_ice1712_playback_ops = {
.open = snd_ice1712_playback_open,
.close = snd_ice1712_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -858,7 +858,7 @@ static struct snd_pcm_ops snd_ice1712_playback_ops = {
.pointer = snd_ice1712_playback_pointer,
};
-static struct snd_pcm_ops snd_ice1712_playback_ds_ops = {
+static const struct snd_pcm_ops snd_ice1712_playback_ds_ops = {
.open = snd_ice1712_playback_ds_open,
.close = snd_ice1712_playback_ds_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -869,7 +869,7 @@ static struct snd_pcm_ops snd_ice1712_playback_ds_ops = {
.pointer = snd_ice1712_playback_ds_pointer,
};
-static struct snd_pcm_ops snd_ice1712_capture_ops = {
+static const struct snd_pcm_ops snd_ice1712_capture_ops = {
.open = snd_ice1712_capture_open,
.close = snd_ice1712_capture_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1228,7 +1228,7 @@ static int snd_ice1712_capture_pro_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_ice1712_playback_pro_ops = {
+static const struct snd_pcm_ops snd_ice1712_playback_pro_ops = {
.open = snd_ice1712_playback_pro_open,
.close = snd_ice1712_playback_pro_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1239,7 +1239,7 @@ static struct snd_pcm_ops snd_ice1712_playback_pro_ops = {
.pointer = snd_ice1712_playback_pro_pointer,
};
-static struct snd_pcm_ops snd_ice1712_capture_pro_ops = {
+static const struct snd_pcm_ops snd_ice1712_capture_pro_ops = {
.open = snd_ice1712_capture_pro_open,
.close = snd_ice1712_capture_pro_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 0b22c00642bb..e5c52ed9b674 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -620,9 +620,7 @@ static const unsigned int stdclock_rate_list[16] = {
static unsigned int stdclock_get_rate(struct snd_ice1712 *ice)
{
- unsigned int rate;
- rate = stdclock_rate_list[inb(ICEMT1724(ice, RATE)) & 15];
- return rate;
+ return stdclock_rate_list[inb(ICEMT1724(ice, RATE)) & 15];
}
static void stdclock_set_rate(struct snd_ice1712 *ice, unsigned int rate)
@@ -1113,7 +1111,7 @@ static int snd_vt1724_capture_pro_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_vt1724_playback_pro_ops = {
+static const struct snd_pcm_ops snd_vt1724_playback_pro_ops = {
.open = snd_vt1724_playback_pro_open,
.close = snd_vt1724_playback_pro_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1124,7 +1122,7 @@ static struct snd_pcm_ops snd_vt1724_playback_pro_ops = {
.pointer = snd_vt1724_playback_pro_pointer,
};
-static struct snd_pcm_ops snd_vt1724_capture_pro_ops = {
+static const struct snd_pcm_ops snd_vt1724_capture_pro_ops = {
.open = snd_vt1724_capture_pro_open,
.close = snd_vt1724_capture_pro_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1292,7 +1290,7 @@ static int snd_vt1724_capture_spdif_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_vt1724_playback_spdif_ops = {
+static const struct snd_pcm_ops snd_vt1724_playback_spdif_ops = {
.open = snd_vt1724_playback_spdif_open,
.close = snd_vt1724_playback_spdif_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1303,7 +1301,7 @@ static struct snd_pcm_ops snd_vt1724_playback_spdif_ops = {
.pointer = snd_vt1724_pcm_pointer,
};
-static struct snd_pcm_ops snd_vt1724_capture_spdif_ops = {
+static const struct snd_pcm_ops snd_vt1724_capture_spdif_ops = {
.open = snd_vt1724_capture_spdif_open,
.close = snd_vt1724_capture_spdif_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1437,7 +1435,7 @@ static int snd_vt1724_playback_indep_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_vt1724_playback_indep_ops = {
+static const struct snd_pcm_ops snd_vt1724_playback_indep_ops = {
.open = snd_vt1724_playback_indep_open,
.close = snd_vt1724_playback_indep_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c
index 9e1ad119a3ce..565f7f55c3ca 100644
--- a/sound/pci/korg1212/korg1212.c
+++ b/sound/pci/korg1212/korg1212.c
@@ -1681,7 +1681,7 @@ static int snd_korg1212_capture_copy(struct snd_pcm_substream *substream,
return snd_korg1212_copy_to(korg1212, dst, pos, count, 0, korg1212->channels * 2);
}
-static struct snd_pcm_ops snd_korg1212_playback_ops = {
+static const struct snd_pcm_ops snd_korg1212_playback_ops = {
.open = snd_korg1212_playback_open,
.close = snd_korg1212_playback_close,
.ioctl = snd_korg1212_ioctl,
@@ -1693,7 +1693,7 @@ static struct snd_pcm_ops snd_korg1212_playback_ops = {
.silence = snd_korg1212_playback_silence,
};
-static struct snd_pcm_ops snd_korg1212_capture_ops = {
+static const struct snd_pcm_ops snd_korg1212_capture_ops = {
.open = snd_korg1212_capture_open,
.close = snd_korg1212_capture_close,
.ioctl = snd_korg1212_ioctl,
diff --git a/sound/pci/lola/lola_pcm.c b/sound/pci/lola/lola_pcm.c
index 3bd6985430e8..1268ba329016 100644
--- a/sound/pci/lola/lola_pcm.c
+++ b/sound/pci/lola/lola_pcm.c
@@ -586,7 +586,7 @@ void lola_pcm_update(struct lola *chip, struct lola_pcm *pcm, unsigned int bits)
}
}
-static struct snd_pcm_ops lola_pcm_ops = {
+static const struct snd_pcm_ops lola_pcm_ops = {
.open = lola_pcm_open,
.close = lola_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index 8b8e2e54fba3..c0f0c349c3ec 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -804,7 +804,7 @@ mac_ready:
return err;
}
-static struct snd_pcm_ops lx_ops_playback = {
+static const struct snd_pcm_ops lx_ops_playback = {
.open = lx_pcm_open,
.close = lx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -815,7 +815,7 @@ static struct snd_pcm_ops lx_ops_playback = {
.pointer = lx_pcm_stream_pointer,
};
-static struct snd_pcm_ops lx_ops_capture = {
+static const struct snd_pcm_ops lx_ops_capture = {
.open = lx_pcm_open,
.close = lx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c
index 17ae92613de4..cafea6dc5c01 100644
--- a/sound/pci/maestro3.c
+++ b/sound/pci/maestro3.c
@@ -1834,7 +1834,7 @@ snd_m3_capture_close(struct snd_pcm_substream *subs)
* create pcm instance
*/
-static struct snd_pcm_ops snd_m3_playback_ops = {
+static const struct snd_pcm_ops snd_m3_playback_ops = {
.open = snd_m3_playback_open,
.close = snd_m3_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1845,7 +1845,7 @@ static struct snd_pcm_ops snd_m3_playback_ops = {
.pointer = snd_m3_pcm_pointer,
};
-static struct snd_pcm_ops snd_m3_capture_ops = {
+static const struct snd_pcm_ops snd_m3_capture_ops = {
.open = snd_m3_capture_open,
.close = snd_m3_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c
index 25c0ddd3a53b..80d439944cb5 100644
--- a/sound/pci/mixart/mixart.c
+++ b/sound/pci/mixart/mixart.c
@@ -922,7 +922,7 @@ static snd_pcm_uframes_t snd_mixart_stream_pointer(struct snd_pcm_substream *sub
-static struct snd_pcm_ops snd_mixart_playback_ops = {
+static const struct snd_pcm_ops snd_mixart_playback_ops = {
.open = snd_mixart_playback_open,
.close = snd_mixart_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -933,7 +933,7 @@ static struct snd_pcm_ops snd_mixart_playback_ops = {
.pointer = snd_mixart_stream_pointer,
};
-static struct snd_pcm_ops snd_mixart_capture_ops = {
+static const struct snd_pcm_ops snd_mixart_capture_ops = {
.open = snd_mixart_capture_open,
.close = snd_mixart_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
index 58fd79ebac20..51e53497f0ad 100644
--- a/sound/pci/mixart/mixart_mixer.c
+++ b/sound/pci/mixart/mixart_mixer.c
@@ -965,7 +965,7 @@ static int mixart_update_monitoring(struct snd_mixart* chip, int channel)
int err;
struct mixart_msg request;
struct mixart_set_out_audio_level audio_level;
- u32 resp;
+ u32 resp = 0;
if(chip->pipe_out_ana.status == PIPE_UNDEFINED)
return -EINVAL; /* no pipe defined */
diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c
index 4735e27cc773..103fe311e5a9 100644
--- a/sound/pci/nm256/nm256.c
+++ b/sound/pci/nm256/nm256.c
@@ -902,7 +902,7 @@ snd_nm256_capture_close(struct snd_pcm_substream *substream)
/*
* create a pcm instance
*/
-static struct snd_pcm_ops snd_nm256_playback_ops = {
+static const struct snd_pcm_ops snd_nm256_playback_ops = {
.open = snd_nm256_playback_open,
.close = snd_nm256_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -917,7 +917,7 @@ static struct snd_pcm_ops snd_nm256_playback_ops = {
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_nm256_capture_ops = {
+static const struct snd_pcm_ops snd_nm256_capture_ops = {
.open = snd_nm256_capture_open,
.close = snd_nm256_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/oxygen/oxygen_pcm.c b/sound/pci/oxygen/oxygen_pcm.c
index aa2ebd1d6d12..042a2439fea5 100644
--- a/sound/pci/oxygen/oxygen_pcm.c
+++ b/sound/pci/oxygen/oxygen_pcm.c
@@ -631,7 +631,7 @@ static snd_pcm_uframes_t oxygen_pointer(struct snd_pcm_substream *substream)
return bytes_to_frames(runtime, curr_addr - (u32)runtime->dma_addr);
}
-static struct snd_pcm_ops oxygen_rec_a_ops = {
+static const struct snd_pcm_ops oxygen_rec_a_ops = {
.open = oxygen_rec_a_open,
.close = oxygen_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -642,7 +642,7 @@ static struct snd_pcm_ops oxygen_rec_a_ops = {
.pointer = oxygen_pointer,
};
-static struct snd_pcm_ops oxygen_rec_b_ops = {
+static const struct snd_pcm_ops oxygen_rec_b_ops = {
.open = oxygen_rec_b_open,
.close = oxygen_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -653,7 +653,7 @@ static struct snd_pcm_ops oxygen_rec_b_ops = {
.pointer = oxygen_pointer,
};
-static struct snd_pcm_ops oxygen_rec_c_ops = {
+static const struct snd_pcm_ops oxygen_rec_c_ops = {
.open = oxygen_rec_c_open,
.close = oxygen_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -664,7 +664,7 @@ static struct snd_pcm_ops oxygen_rec_c_ops = {
.pointer = oxygen_pointer,
};
-static struct snd_pcm_ops oxygen_spdif_ops = {
+static const struct snd_pcm_ops oxygen_spdif_ops = {
.open = oxygen_spdif_open,
.close = oxygen_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -675,7 +675,7 @@ static struct snd_pcm_ops oxygen_spdif_ops = {
.pointer = oxygen_pointer,
};
-static struct snd_pcm_ops oxygen_multich_ops = {
+static const struct snd_pcm_ops oxygen_multich_ops = {
.open = oxygen_multich_open,
.close = oxygen_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -686,7 +686,7 @@ static struct snd_pcm_ops oxygen_multich_ops = {
.pointer = oxygen_pointer,
};
-static struct snd_pcm_ops oxygen_ac97_ops = {
+static const struct snd_pcm_ops oxygen_ac97_ops = {
.open = oxygen_ac97_open,
.close = oxygen_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c
index 9293235281dc..bb7eee9d0c2b 100644
--- a/sound/pci/pcxhr/pcxhr.c
+++ b/sound/pci/pcxhr/pcxhr.c
@@ -1146,7 +1146,7 @@ static snd_pcm_uframes_t pcxhr_stream_pointer(struct snd_pcm_substream *subs)
}
-static struct snd_pcm_ops pcxhr_ops = {
+static const struct snd_pcm_ops pcxhr_ops = {
.open = pcxhr_open,
.close = pcxhr_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 94639d6b5fb5..ada5f01d479c 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -644,7 +644,7 @@ static struct lbuspath lbus_play_paths[] = {
.mono = lbus_play_mono3,
},
};
-static struct lbuspath lbus_rec_path = {
+static const struct lbuspath lbus_rec_path = {
.noconv = lbus_rec_noconv1,
.stereo = lbus_rec_stereo1,
.mono = lbus_rec_mono1,
@@ -1496,7 +1496,7 @@ static int snd_riptide_prepare(struct snd_pcm_substream *substream)
f = PAGE_SIZE;
while ((size + (f >> 1) - 1) <= (f << 7) && (f << 1) > period)
f = f >> 1;
- pages = (size + f - 1) / f;
+ pages = DIV_ROUND_UP(size, f);
data->size = size;
data->pages = pages;
snd_printdd
@@ -1669,7 +1669,7 @@ static int snd_riptide_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_riptide_playback_ops = {
+static const struct snd_pcm_ops snd_riptide_playback_ops = {
.open = snd_riptide_playback_open,
.close = snd_riptide_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1680,7 +1680,7 @@ static struct snd_pcm_ops snd_riptide_playback_ops = {
.trigger = snd_riptide_trigger,
.pointer = snd_riptide_pointer,
};
-static struct snd_pcm_ops snd_riptide_capture_ops = {
+static const struct snd_pcm_ops snd_riptide_capture_ops = {
.open = snd_riptide_capture_open,
.close = snd_riptide_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c
index cd94ac548ba3..96d15db65dfd 100644
--- a/sound/pci/rme32.c
+++ b/sound/pci/rme32.c
@@ -1196,7 +1196,7 @@ snd_rme32_capture_fd_pointer(struct snd_pcm_substream *substream)
}
/* for halfduplex mode */
-static struct snd_pcm_ops snd_rme32_playback_spdif_ops = {
+static const struct snd_pcm_ops snd_rme32_playback_spdif_ops = {
.open = snd_rme32_playback_spdif_open,
.close = snd_rme32_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1210,7 +1210,7 @@ static struct snd_pcm_ops snd_rme32_playback_spdif_ops = {
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_rme32_capture_spdif_ops = {
+static const struct snd_pcm_ops snd_rme32_capture_spdif_ops = {
.open = snd_rme32_capture_spdif_open,
.close = snd_rme32_capture_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1223,7 +1223,7 @@ static struct snd_pcm_ops snd_rme32_capture_spdif_ops = {
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_rme32_playback_adat_ops = {
+static const struct snd_pcm_ops snd_rme32_playback_adat_ops = {
.open = snd_rme32_playback_adat_open,
.close = snd_rme32_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1236,7 +1236,7 @@ static struct snd_pcm_ops snd_rme32_playback_adat_ops = {
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_rme32_capture_adat_ops = {
+static const struct snd_pcm_ops snd_rme32_capture_adat_ops = {
.open = snd_rme32_capture_adat_open,
.close = snd_rme32_capture_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1249,7 +1249,7 @@ static struct snd_pcm_ops snd_rme32_capture_adat_ops = {
};
/* for fullduplex mode */
-static struct snd_pcm_ops snd_rme32_playback_spdif_fd_ops = {
+static const struct snd_pcm_ops snd_rme32_playback_spdif_fd_ops = {
.open = snd_rme32_playback_spdif_open,
.close = snd_rme32_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1261,7 +1261,7 @@ static struct snd_pcm_ops snd_rme32_playback_spdif_fd_ops = {
.ack = snd_rme32_playback_fd_ack,
};
-static struct snd_pcm_ops snd_rme32_capture_spdif_fd_ops = {
+static const struct snd_pcm_ops snd_rme32_capture_spdif_fd_ops = {
.open = snd_rme32_capture_spdif_open,
.close = snd_rme32_capture_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1273,7 +1273,7 @@ static struct snd_pcm_ops snd_rme32_capture_spdif_fd_ops = {
.ack = snd_rme32_capture_fd_ack,
};
-static struct snd_pcm_ops snd_rme32_playback_adat_fd_ops = {
+static const struct snd_pcm_ops snd_rme32_playback_adat_fd_ops = {
.open = snd_rme32_playback_adat_open,
.close = snd_rme32_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1284,7 +1284,7 @@ static struct snd_pcm_ops snd_rme32_playback_adat_fd_ops = {
.ack = snd_rme32_playback_fd_ack,
};
-static struct snd_pcm_ops snd_rme32_capture_adat_fd_ops = {
+static const struct snd_pcm_ops snd_rme32_capture_adat_fd_ops = {
.open = snd_rme32_capture_adat_open,
.close = snd_rme32_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c
index 41c31db65039..05b9da30990d 100644
--- a/sound/pci/rme96.c
+++ b/sound/pci/rme96.c
@@ -1505,7 +1505,7 @@ snd_rme96_capture_pointer(struct snd_pcm_substream *substream)
return snd_rme96_capture_ptr(rme96);
}
-static struct snd_pcm_ops snd_rme96_playback_spdif_ops = {
+static const struct snd_pcm_ops snd_rme96_playback_spdif_ops = {
.open = snd_rme96_playback_spdif_open,
.close = snd_rme96_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1518,7 +1518,7 @@ static struct snd_pcm_ops snd_rme96_playback_spdif_ops = {
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_rme96_capture_spdif_ops = {
+static const struct snd_pcm_ops snd_rme96_capture_spdif_ops = {
.open = snd_rme96_capture_spdif_open,
.close = snd_rme96_capture_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1530,7 +1530,7 @@ static struct snd_pcm_ops snd_rme96_capture_spdif_ops = {
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_rme96_playback_adat_ops = {
+static const struct snd_pcm_ops snd_rme96_playback_adat_ops = {
.open = snd_rme96_playback_adat_open,
.close = snd_rme96_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1543,7 +1543,7 @@ static struct snd_pcm_ops snd_rme96_playback_adat_ops = {
.mmap = snd_pcm_lib_mmap_iomem,
};
-static struct snd_pcm_ops snd_rme96_capture_adat_ops = {
+static const struct snd_pcm_ops snd_rme96_capture_adat_ops = {
.open = snd_rme96_capture_adat_open,
.close = snd_rme96_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c
index 7c8941b8b2de..b94fc6357139 100644
--- a/sound/pci/rme9652/hdsp.c
+++ b/sound/pci/rme9652/hdsp.c
@@ -4861,7 +4861,7 @@ static int snd_hdsp_hwdep_ioctl(struct snd_hwdep *hw, struct file *file, unsigne
return 0;
}
-static struct snd_pcm_ops snd_hdsp_playback_ops = {
+static const struct snd_pcm_ops snd_hdsp_playback_ops = {
.open = snd_hdsp_playback_open,
.close = snd_hdsp_playback_release,
.ioctl = snd_hdsp_ioctl,
@@ -4873,7 +4873,7 @@ static struct snd_pcm_ops snd_hdsp_playback_ops = {
.silence = snd_hdsp_hw_silence,
};
-static struct snd_pcm_ops snd_hdsp_capture_ops = {
+static const struct snd_pcm_ops snd_hdsp_capture_ops = {
.open = snd_hdsp_capture_open,
.close = snd_hdsp_capture_release,
.ioctl = snd_hdsp_ioctl,
diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c
index a4a999a0317e..14bbf55c1ef9 100644
--- a/sound/pci/rme9652/hdspm.c
+++ b/sound/pci/rme9652/hdspm.c
@@ -1666,7 +1666,7 @@ static int hdspm_set_rate(struct hdspm * hdspm, int rate, int called_internally)
HDSPM_AUTOSYNC_FROM_NONE) {
dev_warn(hdspm->card->dev,
- "Detected no Externel Sync\n");
+ "Detected no External Sync\n");
not_set = 1;
} else if (rate != external_freq) {
@@ -6361,7 +6361,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
return 0;
}
-static struct snd_pcm_ops snd_hdspm_ops = {
+static const struct snd_pcm_ops snd_hdspm_ops = {
.open = snd_hdspm_open,
.close = snd_hdspm_release,
.ioctl = snd_hdspm_ioctl,
diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c
index fdbc0aa2776a..55172c689991 100644
--- a/sound/pci/rme9652/rme9652.c
+++ b/sound/pci/rme9652/rme9652.c
@@ -2368,7 +2368,7 @@ static int snd_rme9652_capture_release(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_rme9652_playback_ops = {
+static const struct snd_pcm_ops snd_rme9652_playback_ops = {
.open = snd_rme9652_playback_open,
.close = snd_rme9652_playback_release,
.ioctl = snd_rme9652_ioctl,
@@ -2380,7 +2380,7 @@ static struct snd_pcm_ops snd_rme9652_playback_ops = {
.silence = snd_rme9652_hw_silence,
};
-static struct snd_pcm_ops snd_rme9652_capture_ops = {
+static const struct snd_pcm_ops snd_rme9652_capture_ops = {
.open = snd_rme9652_capture_open,
.close = snd_rme9652_capture_release,
.ioctl = snd_rme9652_ioctl,
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index 1b6fad7d4d56..e1a13870bb80 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -857,7 +857,7 @@ static int snd_sonicvibes_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_sonicvibes_playback_ops = {
+static const struct snd_pcm_ops snd_sonicvibes_playback_ops = {
.open = snd_sonicvibes_playback_open,
.close = snd_sonicvibes_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -868,7 +868,7 @@ static struct snd_pcm_ops snd_sonicvibes_playback_ops = {
.pointer = snd_sonicvibes_playback_pointer,
};
-static struct snd_pcm_ops snd_sonicvibes_capture_ops = {
+static const struct snd_pcm_ops snd_sonicvibes_capture_ops = {
.open = snd_sonicvibes_capture_open,
.close = snd_sonicvibes_capture_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index 599d2b7eb5b8..27f0ed840979 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -2070,7 +2070,7 @@ static int snd_trident_foldback_close(struct snd_pcm_substream *substream)
PCM operations
---------------------------------------------------------------------------*/
-static struct snd_pcm_ops snd_trident_playback_ops = {
+static const struct snd_pcm_ops snd_trident_playback_ops = {
.open = snd_trident_playback_open,
.close = snd_trident_playback_close,
.ioctl = snd_trident_ioctl,
@@ -2081,7 +2081,7 @@ static struct snd_pcm_ops snd_trident_playback_ops = {
.pointer = snd_trident_playback_pointer,
};
-static struct snd_pcm_ops snd_trident_nx_playback_ops = {
+static const struct snd_pcm_ops snd_trident_nx_playback_ops = {
.open = snd_trident_playback_open,
.close = snd_trident_playback_close,
.ioctl = snd_trident_ioctl,
@@ -2115,7 +2115,7 @@ static struct snd_pcm_ops snd_trident_si7018_capture_ops = {
.pointer = snd_trident_playback_pointer,
};
-static struct snd_pcm_ops snd_trident_foldback_ops = {
+static const struct snd_pcm_ops snd_trident_foldback_ops = {
.open = snd_trident_foldback_open,
.close = snd_trident_foldback_close,
.ioctl = snd_trident_ioctl,
@@ -2126,7 +2126,7 @@ static struct snd_pcm_ops snd_trident_foldback_ops = {
.pointer = snd_trident_playback_pointer,
};
-static struct snd_pcm_ops snd_trident_nx_foldback_ops = {
+static const struct snd_pcm_ops snd_trident_nx_foldback_ops = {
.open = snd_trident_foldback_open,
.close = snd_trident_foldback_close,
.ioctl = snd_trident_ioctl,
@@ -2138,7 +2138,7 @@ static struct snd_pcm_ops snd_trident_nx_foldback_ops = {
.page = snd_pcm_sgbuf_ops_page,
};
-static struct snd_pcm_ops snd_trident_spdif_ops = {
+static const struct snd_pcm_ops snd_trident_spdif_ops = {
.open = snd_trident_spdif_open,
.close = snd_trident_spdif_close,
.ioctl = snd_trident_ioctl,
@@ -2149,7 +2149,7 @@ static struct snd_pcm_ops snd_trident_spdif_ops = {
.pointer = snd_trident_spdif_pointer,
};
-static struct snd_pcm_ops snd_trident_spdif_7018_ops = {
+static const struct snd_pcm_ops snd_trident_spdif_7018_ops = {
.open = snd_trident_spdif_open,
.close = snd_trident_spdif_close,
.ioctl = snd_trident_ioctl,
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 3dd038bdb204..38a17b4342a6 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -1366,7 +1366,7 @@ static int snd_via8233_playback_close(struct snd_pcm_substream *substream)
/* via686 playback callbacks */
-static struct snd_pcm_ops snd_via686_playback_ops = {
+static const struct snd_pcm_ops snd_via686_playback_ops = {
.open = snd_via686_playback_open,
.close = snd_via82xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1379,7 +1379,7 @@ static struct snd_pcm_ops snd_via686_playback_ops = {
};
/* via686 capture callbacks */
-static struct snd_pcm_ops snd_via686_capture_ops = {
+static const struct snd_pcm_ops snd_via686_capture_ops = {
.open = snd_via82xx_capture_open,
.close = snd_via82xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1392,7 +1392,7 @@ static struct snd_pcm_ops snd_via686_capture_ops = {
};
/* via823x DSX playback callbacks */
-static struct snd_pcm_ops snd_via8233_playback_ops = {
+static const struct snd_pcm_ops snd_via8233_playback_ops = {
.open = snd_via8233_playback_open,
.close = snd_via8233_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1405,7 +1405,7 @@ static struct snd_pcm_ops snd_via8233_playback_ops = {
};
/* via823x multi-channel playback callbacks */
-static struct snd_pcm_ops snd_via8233_multi_ops = {
+static const struct snd_pcm_ops snd_via8233_multi_ops = {
.open = snd_via8233_multi_open,
.close = snd_via82xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1418,7 +1418,7 @@ static struct snd_pcm_ops snd_via8233_multi_ops = {
};
/* via823x capture callbacks */
-static struct snd_pcm_ops snd_via8233_capture_ops = {
+static const struct snd_pcm_ops snd_via8233_capture_ops = {
.open = snd_via82xx_capture_open,
.close = snd_via82xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c
index 99b9137bc677..2f6d40f10618 100644
--- a/sound/pci/via82xx_modem.c
+++ b/sound/pci/via82xx_modem.c
@@ -804,7 +804,7 @@ static int snd_via82xx_pcm_close(struct snd_pcm_substream *substream)
/* via686 playback callbacks */
-static struct snd_pcm_ops snd_via686_playback_ops = {
+static const struct snd_pcm_ops snd_via686_playback_ops = {
.open = snd_via82xx_playback_open,
.close = snd_via82xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -817,7 +817,7 @@ static struct snd_pcm_ops snd_via686_playback_ops = {
};
/* via686 capture callbacks */
-static struct snd_pcm_ops snd_via686_capture_ops = {
+static const struct snd_pcm_ops snd_via686_capture_ops = {
.open = snd_via82xx_capture_open,
.close = snd_via82xx_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index 4c26076dbf78..ffee284898b3 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -1123,7 +1123,7 @@ static int snd_ymfpci_capture_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops snd_ymfpci_playback_ops = {
+static const struct snd_pcm_ops snd_ymfpci_playback_ops = {
.open = snd_ymfpci_playback_open,
.close = snd_ymfpci_playback_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1134,7 +1134,7 @@ static struct snd_pcm_ops snd_ymfpci_playback_ops = {
.pointer = snd_ymfpci_playback_pointer,
};
-static struct snd_pcm_ops snd_ymfpci_capture_rec_ops = {
+static const struct snd_pcm_ops snd_ymfpci_capture_rec_ops = {
.open = snd_ymfpci_capture_rec_open,
.close = snd_ymfpci_capture_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1169,7 +1169,7 @@ int snd_ymfpci_pcm(struct snd_ymfpci *chip, int device)
snd_pcm_std_chmaps, 2, 0, NULL);
}
-static struct snd_pcm_ops snd_ymfpci_capture_ac97_ops = {
+static const struct snd_pcm_ops snd_ymfpci_capture_ac97_ops = {
.open = snd_ymfpci_capture_ac97_open,
.close = snd_ymfpci_capture_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1203,7 +1203,7 @@ int snd_ymfpci_pcm2(struct snd_ymfpci *chip, int device)
return 0;
}
-static struct snd_pcm_ops snd_ymfpci_playback_spdif_ops = {
+static const struct snd_pcm_ops snd_ymfpci_playback_spdif_ops = {
.open = snd_ymfpci_playback_spdif_open,
.close = snd_ymfpci_playback_spdif_close,
.ioctl = snd_pcm_lib_ioctl,
@@ -1236,7 +1236,7 @@ int snd_ymfpci_pcm_spdif(struct snd_ymfpci *chip, int device)
return 0;
}
-static struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = {
+static const struct snd_pcm_ops snd_ymfpci_playback_4ch_ops = {
.open = snd_ymfpci_playback_4ch_open,
.close = snd_ymfpci_playback_4ch_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/ppc/awacs.c b/sound/ppc/awacs.c
index 09da7b52bc2e..1468e4b7bf93 100644
--- a/sound/ppc/awacs.c
+++ b/sound/ppc/awacs.c
@@ -991,6 +991,7 @@ snd_pmac_awacs_init(struct snd_pmac *chip)
if (err < 0)
return err;
}
+ master_vol = NULL;
if (pm7500)
err = build_mixers(chip,
ARRAY_SIZE(snd_pmac_awacs_mixers_pmac7500),
diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c
index 368242519279..b84d7d34f188 100644
--- a/sound/ppc/snd_ps3.c
+++ b/sound/ppc/snd_ps3.c
@@ -564,9 +564,7 @@ static int snd_ps3_pcm_hw_params(struct snd_pcm_substream *substream,
static int snd_ps3_pcm_hw_free(struct snd_pcm_substream *substream)
{
- int ret;
- ret = snd_pcm_lib_free_pages(substream);
- return ret;
+ return snd_pcm_lib_free_pages(substream);
};
static int snd_ps3_delay_to_bytes(struct snd_pcm_substream *substream,
diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c
index c8fafba218a5..58ee8089bbf9 100644
--- a/sound/ppc/tumbler.c
+++ b/sound/ppc/tumbler.c
@@ -1303,19 +1303,19 @@ static int tumbler_init(struct snd_pmac *chip)
&mix->line_mute, 1);
irq = tumbler_find_device("headphone-detect",
NULL, &mix->hp_detect, 0);
- if (irq <= NO_IRQ)
+ if (irq <= 0)
irq = tumbler_find_device("headphone-detect",
NULL, &mix->hp_detect, 1);
- if (irq <= NO_IRQ)
+ if (irq <= 0)
irq = tumbler_find_device("keywest-gpio15",
NULL, &mix->hp_detect, 1);
mix->headphone_irq = irq;
irq = tumbler_find_device("line-output-detect",
NULL, &mix->line_detect, 0);
- if (irq <= NO_IRQ)
+ if (irq <= 0)
irq = tumbler_find_device("line-output-detect",
NULL, &mix->line_detect, 1);
- if (IS_G4DA && irq <= NO_IRQ)
+ if (IS_G4DA && irq <= 0)
irq = tumbler_find_device("keywest-gpio16",
NULL, &mix->line_detect, 1);
mix->lineout_irq = irq;
diff --git a/sound/sh/aica.c b/sound/sh/aica.c
index ad3d9ae38034..fbbc25279559 100644
--- a/sound/sh/aica.c
+++ b/sound/sh/aica.c
@@ -63,9 +63,6 @@ MODULE_PARM_DESC(id, "ID string for " CARD_NAME " soundcard.");
module_param(enable, bool, 0644);
MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard.");
-/* Use workqueue */
-static struct workqueue_struct *aica_queue;
-
/* Simple platform device */
static struct platform_device *pd;
static struct resource aica_memory_space[2] = {
@@ -327,7 +324,7 @@ static void aica_period_elapsed(unsigned long timer_var)
dreamcastcard->current_period = play_period;
if (unlikely(dreamcastcard->dma_check == 0))
dreamcastcard->dma_check = 1;
- queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
+ schedule_work(&(dreamcastcard->spu_dma_work));
}
static void spu_begin_dma(struct snd_pcm_substream *substream)
@@ -337,7 +334,7 @@ static void spu_begin_dma(struct snd_pcm_substream *substream)
runtime = substream->runtime;
dreamcastcard = substream->pcm->private_data;
/*get the queue to do the work */
- queue_work(aica_queue, &(dreamcastcard->spu_dma_work));
+ schedule_work(&(dreamcastcard->spu_dma_work));
/* Timer may already be running */
if (unlikely(dreamcastcard->timer.data)) {
mod_timer(&dreamcastcard->timer, jiffies + 4);
@@ -381,7 +378,7 @@ static int snd_aicapcm_pcm_close(struct snd_pcm_substream
*substream)
{
struct snd_card_aica *dreamcastcard = substream->pcm->private_data;
- flush_workqueue(aica_queue);
+ flush_work(&(dreamcastcard->spu_dma_work));
if (dreamcastcard->timer.data)
del_timer(&dreamcastcard->timer);
kfree(dreamcastcard->channel);
@@ -633,9 +630,6 @@ static int snd_aica_probe(struct platform_device *devptr)
if (unlikely(err < 0))
goto freedreamcast;
platform_set_drvdata(devptr, dreamcastcard);
- aica_queue = create_workqueue(CARD_NAME);
- if (unlikely(!aica_queue))
- goto freedreamcast;
snd_printk
("ALSA Driver for Yamaha AICA Super Intelligent Sound Processor\n");
return 0;
@@ -671,10 +665,6 @@ static int __init aica_init(void)
static void __exit aica_exit(void)
{
- /* Destroy the aica kernel thread *
- * being extra cautious to check if it exists*/
- if (likely(aica_queue))
- destroy_workqueue(aica_queue);
platform_device_unregister(pd);
platform_driver_unregister(&snd_aica_driver);
/* Kill any sound still playing and reset ARM7 to safe state */
diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c
index d1fb035f44db..504c7cd7f58a 100644
--- a/sound/soc/amd/acp-pcm-dma.c
+++ b/sound/soc/amd/acp-pcm-dma.c
@@ -897,7 +897,7 @@ static int acp_dma_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops acp_dma_ops = {
+static const struct snd_pcm_ops acp_dma_ops = {
.open = acp_dma_open,
.close = acp_dma_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig
index 06e099e802df..22aec9a1e9a4 100644
--- a/sound/soc/atmel/Kconfig
+++ b/sound/soc/atmel/Kconfig
@@ -10,6 +10,7 @@ if SND_ATMEL_SOC
config SND_ATMEL_SOC_PDC
tristate
+ depends on HAS_DMA
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)
diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
index 6107de9c538b..89ac5f5a93eb 100644
--- a/sound/soc/atmel/atmel-classd.c
+++ b/sound/soc/atmel/atmel-classd.c
@@ -308,9 +308,11 @@ static struct regmap *atmel_classd_codec_get_remap(struct device *dev)
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,
+ .component_driver = {
+ .controls = atmel_classd_snd_controls,
+ .num_controls = ARRAY_SIZE(atmel_classd_snd_controls),
+ },
};
/* codec dai component */
@@ -593,11 +595,6 @@ static int atmel_classd_probe(struct platform_device *pdev)
}
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);
diff --git a/sound/soc/atmel/atmel-pcm-pdc.c b/sound/soc/atmel/atmel-pcm-pdc.c
index da861b44413f..91b7069c3499 100644
--- a/sound/soc/atmel/atmel-pcm-pdc.c
+++ b/sound/soc/atmel/atmel-pcm-pdc.c
@@ -381,7 +381,7 @@ static int atmel_pcm_close(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops atmel_pcm_ops = {
+static const struct snd_pcm_ops atmel_pcm_ops = {
.open = atmel_pcm_open,
.close = atmel_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/soc/atmel/atmel-pdmic.c b/sound/soc/atmel/atmel-pdmic.c
index aee4787a0b89..c917df7715d1 100644
--- a/sound/soc/atmel/atmel-pdmic.c
+++ b/sound/soc/atmel/atmel-pdmic.c
@@ -80,7 +80,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);
}
@@ -115,8 +115,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);
@@ -283,7 +285,7 @@ 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);
@@ -357,8 +359,10 @@ static int atmel_pdmic_codec_probe(struct snd_soc_codec *codec)
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),
+ .component_driver = {
+ .controls = atmel_pdmic_snd_controls,
+ .num_controls = ARRAY_SIZE(atmel_pdmic_snd_controls),
+ },
};
/* codec dai component */
@@ -596,7 +600,7 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
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);
+ dev_err(dev, "failed to get irq: %d\n", ret);
return ret;
}
@@ -614,7 +618,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);
@@ -624,11 +628,6 @@ static int atmel_pdmic_probe(struct platform_device *pdev)
}
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);
@@ -654,7 +653,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 */
diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c
index 1267e1af0fae..16e459aedffe 100644
--- a/sound/soc/atmel/atmel_ssc_dai.c
+++ b/sound/soc/atmel/atmel_ssc_dai.c
@@ -299,8 +299,9 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
clk_enable(ssc_p->ssc->clk);
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;
@@ -321,7 +322,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;
diff --git a/sound/soc/bcm/Kconfig b/sound/soc/bcm/Kconfig
index 6a834e109f1d..d528aaceaad9 100644
--- a/sound/soc/bcm/Kconfig
+++ b/sound/soc/bcm/Kconfig
@@ -7,3 +7,12 @@ 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. \ No newline at end of file
diff --git a/sound/soc/bcm/Makefile b/sound/soc/bcm/Makefile
index bc816b71e5a4..fc739d007884 100644
--- a/sound/soc/bcm/Makefile
+++ b/sound/soc/bcm/Makefile
@@ -3,3 +3,8 @@ snd-soc-bcm2835-i2s-objs := bcm2835-i2s.o
obj-$(CONFIG_SND_BCM2835_SOC_I2S) += snd-soc-bcm2835-i2s.o
+# CYGNUS Platform Support
+snd-soc-cygnus-objs := cygnus-pcm.o cygnus-ssp.o
+
+obj-$(CONFIG_SND_SOC_CYGNUS) += snd-soc-cygnus.o
+
diff --git a/sound/soc/bcm/cygnus-pcm.c b/sound/soc/bcm/cygnus-pcm.c
new file mode 100644
index 000000000000..d616e096462e
--- /dev/null
+++ b/sound/soc/bcm/cygnus-pcm.c
@@ -0,0 +1,861 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * This program is free software; 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/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 = substream->private_data;
+
+ return snd_soc_dai_get_dma_data(soc_runtime->cpu_dai, 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 = substream->private_data;
+ struct cygnus_aio_port *aio;
+ u32 set_mask;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ dev_dbg(rtd->cpu_dai->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_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_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ 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(rtd->cpu_dai->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_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct cygnus_aio_port *aio;
+
+ aio = cygnus_dai_get_dma_data(substream);
+
+ dev_dbg(rtd->cpu_dai->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(rtd->cpu_dai->dev, "freed port %d\n", aio->portnum);
+
+ return 0;
+}
+
+static int cygnus_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 cygnus_aio_port *aio;
+ int ret = 0;
+
+ aio = cygnus_dai_get_dma_data(substream);
+ dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
+
+ snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
+ runtime->dma_bytes = params_buffer_bytes(params);
+
+ return ret;
+}
+
+static int cygnus_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct cygnus_aio_port *aio;
+
+ aio = cygnus_dai_get_dma_data(substream);
+ dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
+
+ snd_pcm_set_runtime_buffer(substream, NULL);
+ return 0;
+}
+
+static int cygnus_pcm_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct cygnus_aio_port *aio;
+ unsigned long bufsize, periodsize;
+ int ret = 0;
+ bool is_play;
+ u32 start;
+ struct ringbuf_regs *p_rbuf = NULL;
+
+ aio = cygnus_dai_get_dma_data(substream);
+ dev_dbg(rtd->cpu_dai->dev, "%s port %d\n", __func__, aio->portnum);
+
+ bufsize = snd_pcm_lib_buffer_bytes(substream);
+ periodsize = snd_pcm_lib_period_bytes(substream);
+
+ dev_dbg(rtd->cpu_dai->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 ret;
+}
+
+static snd_pcm_uframes_t cygnus_pcm_pointer(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_pcm_preallocate_dma_buffer(struct snd_pcm *pcm, int stream)
+{
+ struct snd_pcm_substream *substream = pcm->streams[stream].substream;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_dma_buffer *buf = &substream->dma_buffer;
+ size_t size;
+
+ size = cygnus_pcm_hw.buffer_bytes_max;
+
+ 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);
+
+ dev_dbg(rtd->cpu_dai->dev, "%s: size 0x%zx @ %pK\n",
+ __func__, size, buf->area);
+
+ if (!buf->area) {
+ dev_err(rtd->cpu_dai->dev, "%s: dma_alloc failed\n", __func__);
+ return -ENOMEM;
+ }
+ buf->bytes = size;
+
+ return 0;
+}
+
+
+static const struct snd_pcm_ops cygnus_pcm_ops = {
+ .open = cygnus_pcm_open,
+ .close = cygnus_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = cygnus_pcm_hw_params,
+ .hw_free = cygnus_pcm_hw_free,
+ .prepare = cygnus_pcm_prepare,
+ .trigger = cygnus_pcm_trigger,
+ .pointer = cygnus_pcm_pointer,
+};
+
+static void cygnus_dma_free_dma_buffers(struct snd_pcm *pcm)
+{
+ struct snd_pcm_substream *substream;
+ struct snd_dma_buffer *buf;
+
+ substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+ if (substream) {
+ buf = &substream->dma_buffer;
+ if (buf->area) {
+ dma_free_coherent(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+ }
+
+ substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+ if (substream) {
+ buf = &substream->dma_buffer;
+ if (buf->area) {
+ dma_free_coherent(pcm->card->dev, buf->bytes,
+ buf->area, buf->addr);
+ buf->area = NULL;
+ }
+ }
+}
+
+static int cygnus_dma_new(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_card *card = rtd->card->snd_card;
+ struct snd_pcm *pcm = rtd->pcm;
+ int ret;
+
+ 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);
+
+ if (pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream) {
+ ret = cygnus_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_PLAYBACK);
+ if (ret)
+ return ret;
+ }
+
+ if (pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream) {
+ ret = cygnus_pcm_preallocate_dma_buffer(pcm,
+ SNDRV_PCM_STREAM_CAPTURE);
+ if (ret) {
+ cygnus_dma_free_dma_buffers(pcm);
+ return ret;
+ }
+ }
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver cygnus_soc_platform = {
+ .ops = &cygnus_pcm_ops,
+ .pcm_new = cygnus_dma_new,
+ .pcm_free = cygnus_dma_free_dma_buffers,
+};
+
+int cygnus_soc_platform_register(struct device *dev,
+ struct cygnus_audio *cygaud)
+{
+ int rc = 0;
+
+ 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 = snd_soc_register_platform(dev, &cygnus_soc_platform);
+ if (rc) {
+ dev_err(dev, "%s failed\n", __func__);
+ return rc;
+ }
+
+ return 0;
+}
+
+int cygnus_soc_platform_unregister(struct device *dev)
+{
+ snd_soc_unregister_platform(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..e710bb0c5637
--- /dev/null
+++ b/sound/soc/bcm/cygnus-ssp.c
@@ -0,0 +1,1529 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * This program is free software; 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/delay.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/of_device.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 CYGNUS_TDM_RATE \
+ (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
+ SNDRV_PCM_RATE_11025 | SNDRV_PCM_RATE_22050 | \
+ SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | \
+ SNDRV_PCM_RATE_48000)
+
+#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},
+};
+
+/* List of valid frame sizes for tdm mode */
+static const int ssp_valid_tdm_framesize[] = {32, 64, 128, 256, 512};
+
+/*
+ * Use this relationship to derive the sampling rate (lrclk)
+ * lrclk = (mclk) / ((2*mclk_to_sclk_ratio) * (32 * SCLK))).
+ *
+ * Use mclk and pll_ch from the table above
+ *
+ * Valid SCLK = 0/1/2/4/8/12
+ *
+ * mclk_to_sclk_ratio = number of MCLK per SCLK. Division is twice the
+ * value programmed in this field.
+ * Valid mclk_to_sclk_ratio = 1 through to 15
+ *
+ * eg: To set lrclk = 48khz, set mclk = 12288000, mclk_to_sclk_ratio = 2,
+ * SCLK = 64
+ */
+struct _ssp_clk_coeff {
+ u32 mclk;
+ u32 sclk_rate;
+ u32 rate;
+ u32 mclk_rate;
+};
+
+static const struct _ssp_clk_coeff ssp_clk_coeff[] = {
+ { 4096000, 32, 16000, 4},
+ { 4096000, 32, 32000, 2},
+ { 4096000, 64, 8000, 4},
+ { 4096000, 64, 16000, 2},
+ { 4096000, 64, 32000, 1},
+ { 4096000, 128, 8000, 2},
+ { 4096000, 128, 16000, 1},
+ { 4096000, 256, 8000, 1},
+
+ { 6144000, 32, 16000, 6},
+ { 6144000, 32, 32000, 3},
+ { 6144000, 32, 48000, 2},
+ { 6144000, 32, 96000, 1},
+ { 6144000, 64, 8000, 6},
+ { 6144000, 64, 16000, 3},
+ { 6144000, 64, 48000, 1},
+ { 6144000, 128, 8000, 3},
+
+ { 8192000, 32, 32000, 4},
+ { 8192000, 64, 16000, 4},
+ { 8192000, 64, 32000, 2},
+ { 8192000, 128, 8000, 4},
+ { 8192000, 128, 16000, 2},
+ { 8192000, 128, 32000, 1},
+ { 8192000, 256, 8000, 2},
+ { 8192000, 256, 16000, 1},
+ { 8192000, 512, 8000, 1},
+
+ {12288000, 32, 32000, 6},
+ {12288000, 32, 48000, 4},
+ {12288000, 32, 96000, 2},
+ {12288000, 32, 192000, 1},
+ {12288000, 64, 16000, 6},
+ {12288000, 64, 32000, 3},
+ {12288000, 64, 48000, 2},
+ {12288000, 64, 96000, 1},
+ {12288000, 128, 8000, 6},
+ {12288000, 128, 16000, 3},
+ {12288000, 128, 48000, 1},
+ {12288000, 256, 8000, 3},
+
+ {16384000, 64, 32000, 4},
+ {16384000, 128, 16000, 4},
+ {16384000, 128, 32000, 2},
+ {16384000, 256, 8000, 4},
+ {16384000, 256, 16000, 2},
+ {16384000, 256, 32000, 1},
+ {16384000, 512, 8000, 2},
+ {16384000, 512, 16000, 1},
+
+ {24576000, 32, 96000, 4},
+ {24576000, 32, 192000, 2},
+ {24576000, 64, 32000, 6},
+ {24576000, 64, 48000, 4},
+ {24576000, 64, 96000, 2},
+ {24576000, 64, 192000, 1},
+ {24576000, 128, 16000, 6},
+ {24576000, 128, 32000, 3},
+ {24576000, 128, 48000, 2},
+ {24576000, 256, 8000, 6},
+ {24576000, 256, 16000, 3},
+ {24576000, 256, 48000, 1},
+ {24576000, 512, 8000, 3},
+
+ {49152000, 32, 192000, 4},
+ {49152000, 64, 96000, 4},
+ {49152000, 64, 192000, 2},
+ {49152000, 128, 32000, 6},
+ {49152000, 128, 48000, 4},
+ {49152000, 128, 96000, 2},
+ {49152000, 128, 192000, 1},
+ {49152000, 256, 16000, 6},
+ {49152000, 256, 32000, 3},
+ {49152000, 256, 48000, 2},
+ {49152000, 256, 96000, 1},
+ {49152000, 512, 8000, 6},
+ {49152000, 512, 16000, 3},
+ {49152000, 512, 48000, 1},
+
+ { 5644800, 32, 22050, 4},
+ { 5644800, 32, 44100, 2},
+ { 5644800, 32, 88200, 1},
+ { 5644800, 64, 11025, 4},
+ { 5644800, 64, 22050, 2},
+ { 5644800, 64, 44100, 1},
+
+ {11289600, 32, 44100, 4},
+ {11289600, 32, 88200, 2},
+ {11289600, 32, 176400, 1},
+ {11289600, 64, 22050, 4},
+ {11289600, 64, 44100, 2},
+ {11289600, 64, 88200, 1},
+ {11289600, 128, 11025, 4},
+ {11289600, 128, 22050, 2},
+ {11289600, 128, 44100, 1},
+
+ {22579200, 32, 88200, 4},
+ {22579200, 32, 176400, 2},
+ {22579200, 64, 44100, 4},
+ {22579200, 64, 88200, 2},
+ {22579200, 64, 176400, 1},
+ {22579200, 128, 22050, 4},
+ {22579200, 128, 44100, 2},
+ {22579200, 128, 88200, 1},
+ {22579200, 256, 11025, 4},
+ {22579200, 256, 22050, 2},
+ {22579200, 256, 44100, 1},
+
+ {45158400, 32, 176400, 4},
+ {45158400, 64, 88200, 4},
+ {45158400, 64, 176400, 2},
+ {45158400, 128, 44100, 4},
+ {45158400, 128, 88200, 2},
+ {45158400, 128, 176400, 1},
+ {45158400, 256, 22050, 4},
+ {45158400, 256, 44100, 2},
+ {45158400, 256, 88200, 1},
+ {45158400, 512, 11025, 4},
+ {45158400, 512, 22050, 2},
+ {45158400, 512, 44100, 1},
+};
+
+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,
+ struct cygnus_audio *cygaud)
+{
+ u32 value, i = 0;
+ u32 mask = 0xf;
+ u32 sclk;
+ bool found = false;
+ const struct _ssp_clk_coeff *p_entry = NULL;
+
+ for (i = 0; i < ARRAY_SIZE(ssp_clk_coeff); i++) {
+ p_entry = &ssp_clk_coeff[i];
+ if ((p_entry->rate == aio->lrclk) &&
+ (p_entry->sclk_rate == aio->bit_per_frame) &&
+ (p_entry->mclk == aio->mclk)) {
+ found = true;
+ break;
+ }
+ }
+ if (!found) {
+ dev_err(aio->cygaud->dev,
+ "No valid match found in ssp_clk_coeff array\n");
+ dev_err(aio->cygaud->dev, "lrclk = %u, bits/frame = %u, mclk = %u\n",
+ aio->lrclk, aio->bit_per_frame, aio->mclk);
+ return -EINVAL;
+ }
+
+ sclk = aio->bit_per_frame;
+ if (sclk == 512)
+ sclk = 0;
+ /* sclks_per_1fs_div = sclk cycles/32 */
+ sclk /= 32;
+ /* Set sclk rate */
+ switch (aio->port_type) {
+ case PORT_TDM:
+ /* 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 |= (p_entry->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);
+ struct cygnus_audio *cygaud = snd_soc_dai_get_drvdata(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);
+ /* Configure channels as mono or stereo/TDM */
+ if (params_channels(params) == 1)
+ value |= BIT(BF_SRC_CFGX_SAMPLE_CH_MODE);
+ else
+ 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_S8:
+ if (aio->port_type == PORT_SPDIF) {
+ dev_err(aio->cygaud->dev,
+ "SPDIF does not support 8bit format\n");
+ return -EINVAL;
+ }
+ bitres = 8;
+ break;
+
+ 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, cygaud);
+
+ 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;
+
+ 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;
+ }
+}
+
+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_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ssp_newcfg |= BIT(I2S_OUT_CFGX_SLAVE_MODE);
+ aio->is_slave = 1;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ 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 < 0) || (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 (!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_resume(struct snd_soc_dai *cpu_dai)
+{
+ struct cygnus_aio_port *aio = cygnus_dai_get_portinfo(cpu_dai);
+ int error;
+
+ 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;
+}
+#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,
+};
+
+
+#define INIT_CPU_DAI(num) { \
+ .name = "cygnus-ssp" #num, \
+ .playback = { \
+ .channels_min = 1, \
+ .channels_max = 16, \
+ .rates = CYGNUS_TDM_RATE | 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_S32_LE, \
+ }, \
+ .capture = { \
+ .channels_min = 2, \
+ .channels_max = 16, \
+ .rates = CYGNUS_TDM_RATE | 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_S32_LE, \
+ }, \
+ .ops = &cygnus_ssp_dai_ops, \
+ .suspend = cygnus_ssp_suspend, \
+ .resume = cygnus_ssp_resume, \
+}
+
+static const struct snd_soc_dai_driver cygnus_ssp_dai_info[] = {
+ INIT_CPU_DAI(0),
+ INIT_CPU_DAI(1),
+ INIT_CPU_DAI(2),
+};
+
+static struct snd_soc_dai_driver cygnus_spdif_dai_info = {
+ .name = "cygnus-spdif",
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = CYGNUS_TDM_RATE | 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_S32_LE,
+ },
+ .ops = &cygnus_ssp_dai_ops,
+ .suspend = cygnus_ssp_suspend,
+ .resume = cygnus_ssp_resume,
+};
+
+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",
+};
+
+/*
+ * 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 resource *res = pdev->resource;
+ struct cygnus_audio *cygaud;
+ int err = -EINVAL;
+ 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);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "aud");
+ cygaud->audio = devm_ioremap_resource(dev, res);
+ if (IS_ERR(cygaud->audio))
+ return PTR_ERR(cygaud->audio);
+
+ res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "i2s_in");
+ cygaud->i2s_in = devm_ioremap_resource(dev, res);
+ 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)
+ 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 = 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) {
+ dev_err(dev, "platform_get_irq failed\n");
+ err = cygaud->irq_num;
+ goto err_irq;
+ }
+
+ err = audio_clk_init(pdev, cygaud);
+ if (err) {
+ dev_err(dev, "audio clock initialization failed\n");
+ goto err_irq;
+ }
+
+ err = cygnus_soc_platform_register(dev, cygaud);
+ if (err) {
+ dev_err(dev, "platform reg error %d\n", err);
+ goto err_irq;
+ }
+
+ return 0;
+
+err_irq:
+ snd_soc_unregister_component(dev);
+ return err;
+}
+
+static int cygnus_ssp_remove(struct platform_device *pdev)
+{
+ cygnus_soc_platform_unregister(&pdev->dev);
+ snd_soc_unregister_component(&pdev->dev);
+
+ return 0;
+}
+
+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..33dd34305928
--- /dev/null
+++ b/sound/soc/bcm/cygnus-ssp.h
@@ -0,0 +1,139 @@
+/*
+ * Copyright (C) 2014-2015 Broadcom Corporation
+ *
+ * This program is free software; 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.
+ */
+#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_get_mode(struct snd_soc_dai *cpu_dai);
+extern int cygnus_ssp_add_pll_tweak_controls(struct snd_soc_pcm_runtime *rtd);
+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/codecs/88pm860x-codec.c b/sound/soc/codecs/88pm860x-codec.c
index e8bed6b0c9db..b013a4c62b63 100644
--- a/sound/soc/codecs/88pm860x-codec.c
+++ b/sound/soc/codecs/88pm860x-codec.c
@@ -1361,12 +1361,14 @@ static struct snd_soc_codec_driver soc_codec_dev_pm860x = {
.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),
+ .component_driver = {
+ .controls = pm860x_snd_controls,
+ .num_controls = ARRAY_SIZE(pm860x_snd_controls),
+ .dapm_widgets = pm860x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pm860x_dapm_widgets),
+ .dapm_routes = pm860x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pm860x_dapm_routes),
+ },
};
static int pm860x_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index 4d82a58ff6b0..c67667bb970f 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -32,6 +32,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_ADAU1977_SPI if SPI_MASTER
select SND_SOC_ADAU1977_I2C if I2C
select SND_SOC_ADAU1701 if I2C
+ select SND_SOC_ADAU7002
select SND_SOC_ADS117X
select SND_SOC_AK4104 if SPI_MASTER
select SND_SOC_AK4535 if I2C
@@ -46,6 +47,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_BT_SCO
select SND_SOC_CQ0093VC if MFD_DAVINCI_VOICECODEC
select SND_SOC_CS35L32 if I2C
+ select SND_SOC_CS35L33 if I2C
select SND_SOC_CS42L51_I2C if I2C
select SND_SOC_CS42L52 if I2C && INPUT
select SND_SOC_CS42L56 if I2C && INPUT
@@ -57,6 +59,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS42XX8_I2C if I2C
select SND_SOC_CS4349 if I2C
select SND_SOC_CS47L24 if MFD_CS47L24
+ select SND_SOC_CS53L30 if I2C
select SND_SOC_CX20442 if TTY
select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
select SND_SOC_DA7213 if I2C
@@ -84,10 +87,12 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX98925 if I2C
select SND_SOC_MAX98926 if I2C
select SND_SOC_MAX9850 if I2C
+ select SND_SOC_MAX9860 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_NAU8810 if I2C
select SND_SOC_NAU8825 if I2C
select SND_SOC_HDMI_CODEC
select SND_SOC_PCM1681 if I2C
@@ -108,6 +113,8 @@ config SND_SOC_ALL_CODECS
select SND_SOC_RT5645 if I2C
select SND_SOC_RT5651 if I2C
select SND_SOC_RT5659 if I2C
+ select SND_SOC_RT5660 if I2C
+ select SND_SOC_RT5663 if I2C
select SND_SOC_RT5670 if I2C
select SND_SOC_RT5677 if I2C && SPI_MASTER
select SND_SOC_SGTL5000 if I2C
@@ -269,8 +276,12 @@ config SND_SOC_AD1980
config SND_SOC_AD73311
tristate
+config SND_SOC_ADAU_UTILS
+ tristate
+
config SND_SOC_ADAU1373
tristate
+ select SND_SOC_ADAU_UTILS
config SND_SOC_ADAU1701
tristate "Analog Devices ADAU1701 CODEC"
@@ -280,6 +291,7 @@ config SND_SOC_ADAU1701
config SND_SOC_ADAU17X1
tristate
select SND_SOC_SIGMADSP_REGMAP
+ select SND_SOC_ADAU_UTILS
config SND_SOC_ADAU1761
tristate
@@ -322,6 +334,9 @@ config SND_SOC_ADAU1977_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_ADAV80X
tristate
@@ -371,7 +386,7 @@ config SND_SOC_ALC5632
tristate
config SND_SOC_BT_SCO
- tristate
+ tristate "Dummy BT SCO codec driver"
config SND_SOC_CQ0093VC
tristate
@@ -380,6 +395,10 @@ 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_CS42L51
tristate
@@ -450,6 +469,11 @@ config SND_SOC_CS4349
config SND_SOC_CS47L24
tristate
+# Cirrus Logic Quad-Channel ADC
+config SND_SOC_CS53L30
+ tristate "Cirrus Logic CS53L30 CODEC"
+ depends on I2C
+
config SND_SOC_CX20442
tristate
depends on TTY
@@ -483,9 +507,10 @@ config SND_SOC_DMIC
tristate
config SND_SOC_HDMI_CODEC
- tristate
- select SND_PCM_ELD
- select SND_PCM_IEC958
+ tristate
+ select SND_PCM_ELD
+ select SND_PCM_IEC958
+ select HDMI
config SND_SOC_ES8328
tristate "Everest Semi ES8328 CODEC"
@@ -535,6 +560,10 @@ config SND_SOC_MAX98357A
config SND_SOC_MAX98371
tristate
+config SND_SOC_MAX98504
+ tristate "Maxim MAX98504 speaker amplifier"
+ depends on I2C
+
config SND_SOC_MAX9867
tristate
@@ -547,6 +576,11 @@ config SND_SOC_MAX98926
config SND_SOC_MAX9850
tristate
+config SND_SOC_MAX9860
+ tristate "Maxim MAX9860 Mono Audio Voice Codec"
+ depends on I2C
+ select REGMAP_I2C
+
config SND_SOC_PCM1681
tristate "Texas Instruments PCM1681 CODEC"
depends on I2C
@@ -614,6 +648,8 @@ config SND_SOC_RL6231
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_RT5670=y
default y if SND_SOC_RT5677=y
default m if SND_SOC_RT5514=m
@@ -622,6 +658,8 @@ config SND_SOC_RL6231
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_RT5670=m
default m if SND_SOC_RT5677=m
@@ -634,6 +672,7 @@ config SND_SOC_RL6347A
config SND_SOC_RT286
tristate
+ select SND_SOC_RT5663
depends on I2C
config SND_SOC_RT298
@@ -643,6 +682,9 @@ config SND_SOC_RT298
config SND_SOC_RT5514
tristate
+config SND_SOC_RT5514_SPI
+ tristate
+
config SND_SOC_RT5616
tristate "Realtek RT5616 CODEC"
depends on I2C
@@ -663,6 +705,12 @@ config SND_SOC_RT5651
config SND_SOC_RT5659
tristate
+config SND_SOC_RT5660
+ tristate
+
+config SND_SOC_RT5663
+ tristate
+
config SND_SOC_RT5670
tristate
@@ -968,7 +1016,8 @@ config SND_SOC_WM8983
tristate
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
@@ -1029,6 +1078,10 @@ config SND_SOC_MC13783
config SND_SOC_ML26124
tristate
+config SND_SOC_NAU8810
+ tristate "Nuvoton Technology Corporation NAU88C10 CODEC"
+ depends on I2C
+
config SND_SOC_NAU8825
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 0f548fd34ca3..958cd4912fbc 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -7,6 +7,7 @@ snd-soc-ad193x-spi-objs := ad193x-spi.o
snd-soc-ad193x-i2c-objs := ad193x-i2c.o
snd-soc-ad1980-objs := ad1980.o
snd-soc-ad73311-objs := ad73311.o
+snd-soc-adau-utils-objs := adau-utils.o
snd-soc-adau1373-objs := adau1373.o
snd-soc-adau1701-objs := adau1701.o
snd-soc-adau17x1-objs := adau17x1.o
@@ -19,6 +20,7 @@ snd-soc-adau1781-spi-objs := adau1781-spi.o
snd-soc-adau1977-objs := adau1977.o
snd-soc-adau1977-spi-objs := adau1977-spi.o
snd-soc-adau1977-i2c-objs := adau1977-i2c.o
+snd-soc-adau7002-objs := adau7002.o
snd-soc-adav80x-objs := adav80x.o
snd-soc-adav801-objs := adav801.o
snd-soc-adav803-objs := adav803.o
@@ -35,6 +37,7 @@ snd-soc-arizona-objs := arizona.o
snd-soc-bt-sco-objs := bt-sco.o
snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cs35l32-objs := cs35l32.o
+snd-soc-cs35l33-objs := cs35l33.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
snd-soc-cs42l52-objs := cs42l52.o
@@ -49,6 +52,7 @@ 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-cs53l30-objs := cs53l30.o
snd-soc-cx20442-objs := cx20442.o
snd-soc-da7210-objs := da7210.o
snd-soc-da7213-objs := da7213.o
@@ -79,8 +83,10 @@ snd-soc-max9867-objs := max9867.o
snd-soc-max98925-objs := max98925.o
snd-soc-max98926-objs := max98926.o
snd-soc-max9850-objs := max9850.o
+snd-soc-max9860-objs := max9860.o
snd-soc-mc13783-objs := mc13783.o
snd-soc-ml26124-objs := ml26124.o
+snd-soc-nau8810-objs := nau8810.o
snd-soc-nau8825-objs := nau8825.o
snd-soc-hdmi-codec-objs := hdmi-codec.o
snd-soc-pcm1681-objs := pcm1681.o
@@ -100,12 +106,15 @@ snd-soc-rl6347a-objs := rl6347a.o
snd-soc-rt286-objs := rt286.o
snd-soc-rt298-objs := rt298.o
snd-soc-rt5514-objs := rt5514.o
+snd-soc-rt5514-spi-objs := rt5514-spi.o
snd-soc-rt5616-objs := rt5616.o
snd-soc-rt5631-objs := rt5631.o
snd-soc-rt5640-objs := rt5640.o
snd-soc-rt5645-objs := rt5645.o
snd-soc-rt5651-objs := rt5651.o
snd-soc-rt5659-objs := rt5659.o
+snd-soc-rt5660-objs := rt5660.o
+snd-soc-rt5663-objs := rt5663.o
snd-soc-rt5670-objs := rt5670.o
snd-soc-rt5677-objs := rt5677.o
snd-soc-rt5677-spi-objs := rt5677-spi.o
@@ -208,6 +217,7 @@ snd-soc-wm-hubs-objs := wm_hubs.o
# Amp
snd-soc-max9877-objs := max9877.o
+snd-soc-max98504-objs := max98504.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
snd-soc-tas2552-objs := tas2552.o
@@ -220,6 +230,7 @@ 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_ADAU1373) += snd-soc-adau1373.o
obj-$(CONFIG_SND_SOC_ADAU1701) += snd-soc-adau1701.o
obj-$(CONFIG_SND_SOC_ADAU17X1) += snd-soc-adau17x1.o
@@ -232,6 +243,7 @@ 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_ADAV80X) += snd-soc-adav80x.o
obj-$(CONFIG_SND_SOC_ADAV801) += snd-soc-adav801.o
obj-$(CONFIG_SND_SOC_ADAV803) += snd-soc-adav803.o
@@ -250,6 +262,7 @@ obj-$(CONFIG_SND_SOC_ARIZONA) += snd-soc-arizona.o
obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o
obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o
obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o
+obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.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
@@ -264,6 +277,7 @@ obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o
obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o
obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o
+obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o
obj-$(CONFIG_SND_SOC_CX20442) += snd-soc-cx20442.o
obj-$(CONFIG_SND_SOC_DA7210) += snd-soc-da7210.o
obj-$(CONFIG_SND_SOC_DA7213) += snd-soc-da7213.o
@@ -293,8 +307,10 @@ 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_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_NAU8810) += snd-soc-nau8810.o
obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
@@ -314,12 +330,15 @@ obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.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_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_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
@@ -419,4 +438,5 @@ obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
# Amp
obj-$(CONFIG_SND_SOC_MAX9877) += snd-soc-max9877.o
+obj-$(CONFIG_SND_SOC_MAX98504) += snd-soc-max98504.o
obj-$(CONFIG_SND_SOC_TPA6130A2) += snd-soc-tpa6130a2.o
diff --git a/sound/soc/codecs/ab8500-codec.c b/sound/soc/codecs/ab8500-codec.c
index 8b1d0c1a7839..935ff7cb71c5 100644
--- a/sound/soc/codecs/ab8500-codec.c
+++ b/sound/soc/codecs/ab8500-codec.c
@@ -2408,28 +2408,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;
@@ -2464,45 +2464,20 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
struct device *dev = codec->dev;
struct device_node *np = dev->of_node;
struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(dev);
- struct ab8500_platform_data *pdata;
+ struct 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(codec, &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(codec, codec_pdata.ear_cmv);
if (status < 0) {
pr_err("%s: Failed to set earpiece CM-voltage (%d)!\n",
__func__, status);
@@ -2550,12 +2525,14 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
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),
+ .component_driver = {
+ .controls = ab8500_ctrls,
+ .num_controls = ARRAY_SIZE(ab8500_ctrls),
+ .dapm_widgets = ab8500_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ab8500_dapm_widgets),
+ .dapm_routes = ab8500_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ab8500_dapm_routes),
+ },
};
static int ab8500_codec_driver_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/ac97.c b/sound/soc/codecs/ac97.c
index 5b3224c63943..f7f04c6be01e 100644
--- a/sound/soc/codecs/ac97.c
+++ b/sound/soc/codecs/ac97.c
@@ -117,10 +117,12 @@ static struct snd_soc_codec_driver soc_codec_dev_ac97 = {
.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),
+ .component_driver = {
+ .dapm_widgets = ac97_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ac97_widgets),
+ .dapm_routes = ac97_routes,
+ .num_dapm_routes = ARRAY_SIZE(ac97_routes),
+ },
};
static int ac97_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/ad1836.c b/sound/soc/codecs/ad1836.c
index e2ce6c4d7ece..a478239aadac 100644
--- a/sound/soc/codecs/ad1836.c
+++ b/sound/soc/codecs/ad1836.c
@@ -327,12 +327,14 @@ static struct snd_soc_codec_driver soc_codec_dev_ad1836 = {
.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),
+ .component_driver = {
+ .controls = ad183x_controls,
+ .num_controls = ARRAY_SIZE(ad183x_controls),
+ .dapm_widgets = ad183x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad183x_dapm_widgets),
+ .dapm_routes = ad183x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad183x_dapm_routes),
+ },
};
static const struct reg_default ad1836_reg_defaults[] = {
diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c
index 3a3f3f2343d7..d643557d89a7 100644
--- a/sound/soc/codecs/ad193x.c
+++ b/sound/soc/codecs/ad193x.c
@@ -410,12 +410,14 @@ static int ad193x_codec_probe(struct snd_soc_codec *codec)
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),
+ .component_driver = {
+ .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),
+ },
};
const struct regmap_config ad193x_regmap_config = {
diff --git a/sound/soc/codecs/ad1980.c b/sound/soc/codecs/ad1980.c
index 9ef20dbccbe3..b7c1b9f4bf5f 100644
--- a/sound/soc/codecs/ad1980.c
+++ b/sound/soc/codecs/ad1980.c
@@ -299,12 +299,14 @@ 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),
+ .component_driver = {
+ .controls = ad1980_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls),
+ .dapm_widgets = ad1980_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
+ .dapm_routes = ad1980_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad1980_dapm_routes),
+ },
};
static int ad1980_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/ad73311.c b/sound/soc/codecs/ad73311.c
index a9400aef60b5..3e358a49442d 100644
--- a/sound/soc/codecs/ad73311.c
+++ b/sound/soc/codecs/ad73311.c
@@ -55,10 +55,12 @@ static struct snd_soc_dai_driver ad73311_dai = {
};
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),
+ .component_driver = {
+ .dapm_widgets = ad73311_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ad73311_dapm_widgets),
+ .dapm_routes = ad73311_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ad73311_dapm_routes),
+ },
};
static int ad73311_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/adau-utils.c b/sound/soc/codecs/adau-utils.c
new file mode 100644
index 000000000000..19d6a6f41b12
--- /dev/null
+++ b/sound/soc/codecs/adau-utils.c
@@ -0,0 +1,61 @@
+/*
+ * Shared helper functions for devices from the ADAU family
+ *
+ * Copyright 2011-2016 Analog Devices Inc.
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2 or later.
+ */
+
+#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..939b5f37762f
--- /dev/null
+++ b/sound/soc/codecs/adau-utils.h
@@ -0,0 +1,7 @@
+#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/adau1373.c b/sound/soc/codecs/adau1373.c
index fe1353a797b9..8fa9045571ff 100644
--- a/sound/soc/codecs/adau1373.c
+++ b/sound/soc/codecs/adau1373.c
@@ -23,6 +23,7 @@
#include <sound/adau1373.h>
#include "adau1373.h"
+#include "adau-utils.h"
struct adau1373_dai {
unsigned int clk_src;
@@ -1254,7 +1255,8 @@ static int adau1373_set_pll(struct snd_soc_codec *codec, int pll_id,
{
struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
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 +1297,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 +1313,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);
@@ -1484,12 +1466,14 @@ static struct snd_soc_codec_driver adau1373_codec_driver = {
.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),
+ .component_driver = {
+ .controls = adau1373_controls,
+ .num_controls = ARRAY_SIZE(adau1373_controls),
+ .dapm_widgets = adau1373_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1373_dapm_widgets),
+ .dapm_routes = adau1373_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1373_dapm_routes),
+ },
};
static int adau1373_i2c_probe(struct i2c_client *client,
diff --git a/sound/soc/codecs/adau1701.c b/sound/soc/codecs/adau1701.c
index de53c0d7bf10..3bad1bc8c00a 100644
--- a/sound/soc/codecs/adau1701.c
+++ b/sound/soc/codecs/adau1701.c
@@ -765,13 +765,14 @@ static struct snd_soc_codec_driver adau1701_codec_drv = {
.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),
-
+ .component_driver = {
+ .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,
};
diff --git a/sound/soc/codecs/adau1761-i2c.c b/sound/soc/codecs/adau1761-i2c.c
index 8de010f758cd..9e7f257f17f8 100644
--- a/sound/soc/codecs/adau1761-i2c.c
+++ b/sound/soc/codecs/adau1761-i2c.c
@@ -31,7 +31,7 @@ static int adau1761_i2c_probe(struct i2c_client *client,
static int adau1761_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
+ adau17x1_remove(&client->dev);
return 0;
}
diff --git a/sound/soc/codecs/adau1761-spi.c b/sound/soc/codecs/adau1761-spi.c
index d9171245bd9f..a0b214be759a 100644
--- a/sound/soc/codecs/adau1761-spi.c
+++ b/sound/soc/codecs/adau1761-spi.c
@@ -48,7 +48,7 @@ static int adau1761_spi_probe(struct spi_device *spi)
static int adau1761_spi_remove(struct spi_device *spi)
{
- snd_soc_unregister_codec(&spi->dev);
+ adau17x1_remove(&spi->dev);
return 0;
}
diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c
index b95d29dbd13d..3bc3cc559dde 100644
--- a/sound/soc/codecs/adau1761.c
+++ b/sound/soc/codecs/adau1761.c
@@ -719,12 +719,14 @@ static const struct snd_soc_codec_driver adau1761_codec_driver = {
.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),
+ .component_driver = {
+ .controls = adau1761_controls,
+ .num_controls = ARRAY_SIZE(adau1761_controls),
+ .dapm_widgets = adau1x61_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1x61_dapm_widgets),
+ .dapm_routes = adau1x61_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1x61_dapm_routes),
+ },
};
#define ADAU1761_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
diff --git a/sound/soc/codecs/adau1781-i2c.c b/sound/soc/codecs/adau1781-i2c.c
index 06cbca84cf02..7b9d1802d159 100644
--- a/sound/soc/codecs/adau1781-i2c.c
+++ b/sound/soc/codecs/adau1781-i2c.c
@@ -31,7 +31,7 @@ static int adau1781_i2c_probe(struct i2c_client *client,
static int adau1781_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
+ adau17x1_remove(&client->dev);
return 0;
}
diff --git a/sound/soc/codecs/adau1781-spi.c b/sound/soc/codecs/adau1781-spi.c
index 3d965a01b99c..9b233544d2e8 100644
--- a/sound/soc/codecs/adau1781-spi.c
+++ b/sound/soc/codecs/adau1781-spi.c
@@ -48,7 +48,7 @@ static int adau1781_spi_probe(struct spi_device *spi)
static int adau1781_spi_remove(struct spi_device *spi)
{
- snd_soc_unregister_codec(&spi->dev);
+ adau17x1_remove(&spi->dev);
return 0;
}
diff --git a/sound/soc/codecs/adau1781.c b/sound/soc/codecs/adau1781.c
index bc1bb56dae63..546071c6c0d0 100644
--- a/sound/soc/codecs/adau1781.c
+++ b/sound/soc/codecs/adau1781.c
@@ -432,12 +432,14 @@ static const struct snd_soc_codec_driver adau1781_codec_driver = {
.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),
+ .component_driver = {
+ .controls = adau1781_controls,
+ .num_controls = ARRAY_SIZE(adau1781_controls),
+ .dapm_widgets = adau1781_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1781_dapm_widgets),
+ .dapm_routes = adau1781_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1781_dapm_routes),
+ },
};
#define ADAU1781_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | \
diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c
index fcf05b254ecd..439aa3ff1f99 100644
--- a/sound/soc/codecs/adau17x1.c
+++ b/sound/soc/codecs/adau17x1.c
@@ -9,6 +9,7 @@
#include <linux/module.h>
#include <linux/init.h>
+#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/slab.h>
#include <sound/core.h>
@@ -23,6 +24,7 @@
#include "sigmadsp.h"
#include "adau17x1.h"
+#include "adau-utils.h"
static const char * const adau17x1_capture_mixer_boost_text[] = {
"Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3",
@@ -302,6 +304,116 @@ bool adau17x1_has_dsp(struct adau *adau)
}
EXPORT_SYMBOL_GPL(adau17x1_has_dsp);
+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);
+ 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_codec_get_dapm(dai->codec);
+ struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+ 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;
+ /* Fall-through */
+ 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)
{
@@ -311,10 +423,19 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
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;
+ /* Fall-through */
+ case ADAU17X1_CLK_SRC_PLL:
freq = adau->pll_freq;
- else
+ break;
+ default:
freq = adau->sysclk;
+ break;
+ }
if (freq % params_rate(params) != 0)
return -EINVAL;
@@ -386,93 +507,6 @@ 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)
{
@@ -857,6 +891,10 @@ 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);
@@ -879,6 +917,7 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
const char *firmware_name)
{
struct adau *adau;
+ int ret;
if (IS_ERR(regmap))
return PTR_ERR(regmap);
@@ -887,6 +926,30 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
if (!adau)
return -ENOMEM;
+ adau->mclk = devm_clk_get(dev, "mclk");
+ if (IS_ERR(adau->mclk)) {
+ if (PTR_ERR(adau->mclk) != -ENOENT)
+ return PTR_ERR(adau->mclk);
+ /* Clock is optional (for the driver) */
+ adau->mclk = NULL;
+ } else 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;
@@ -910,6 +973,16 @@ 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);
+
+ snd_soc_unregister_codec(dev);
+ if (adau->mclk)
+ 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 5ae87a084d97..bf04b7efee40 100644
--- a/sound/soc/codecs/adau17x1.h
+++ b/sound/soc/codecs/adau17x1.h
@@ -22,13 +22,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;
@@ -52,6 +57,7 @@ int adau17x1_add_routes(struct snd_soc_codec *codec);
int adau17x1_probe(struct device *dev, struct regmap *regmap,
enum adau17x1_type type, void (*switch_mode)(struct device *dev),
const char *firmware_name);
+void adau17x1_remove(struct device *dev);
int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
enum adau17x1_micbias_voltage micbias);
bool adau17x1_readable_register(struct device *dev, unsigned int reg);
diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c
index 9bdd15f408c1..b319db6a69f8 100644
--- a/sound/soc/codecs/adau1977.c
+++ b/sound/soc/codecs/adau1977.c
@@ -873,12 +873,14 @@ static struct snd_soc_codec_driver adau1977_codec_driver = {
.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),
+ .component_driver = {
+ .controls = adau1977_snd_controls,
+ .num_controls = ARRAY_SIZE(adau1977_snd_controls),
+ .dapm_widgets = adau1977_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau1977_dapm_widgets),
+ .dapm_routes = adau1977_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau1977_dapm_routes),
+ },
};
static int adau1977_setup_micbias(struct adau1977 *adau1977)
diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c
new file mode 100644
index 000000000000..6384c5491de8
--- /dev/null
+++ b/sound/soc/codecs/adau7002.c
@@ -0,0 +1,82 @@
+/*
+ * ADAU7002 Stereo PDM-to-I2S/TDM converter driver
+ *
+ * Copyright 2014-2016 Analog Devices
+ * Author: Lars-Peter Clausen <lars@metafoo.de>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/platform_device.h>
+
+#include <sound/soc.h>
+
+static const struct snd_soc_dapm_widget adau7002_widgets[] = {
+ SND_SOC_DAPM_INPUT("PDM_DAT"),
+ SND_SOC_DAPM_REGULATOR_SUPPLY("IOVDD", 0, 0),
+};
+
+static const struct snd_soc_dapm_route adau7002_routes[] = {
+ { "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_codec_driver adau7002_codec_driver = {
+ .component_driver = {
+ .dapm_widgets = adau7002_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adau7002_widgets),
+ .dapm_routes = adau7002_routes,
+ .num_dapm_routes = ARRAY_SIZE(adau7002_routes),
+ },
+};
+
+static int adau7002_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev, &adau7002_codec_driver,
+ &adau7002_dai, 1);
+}
+
+static int adau7002_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id adau7002_dt_ids[] = {
+ { .compatible = "adi,adau7002", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, adau7002_dt_ids);
+#endif
+
+static struct platform_driver adau7002_driver = {
+ .driver = {
+ .name = "adau7002",
+ .of_match_table = of_match_ptr(adau7002_dt_ids),
+ },
+ .probe = adau7002_probe,
+ .remove = adau7002_remove,
+};
+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/adav80x.c b/sound/soc/codecs/adav80x.c
index acff8d62059c..6e793ebb5883 100644
--- a/sound/soc/codecs/adav80x.c
+++ b/sound/soc/codecs/adav80x.c
@@ -834,12 +834,14 @@ static struct snd_soc_codec_driver adav80x_codec_driver = {
.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),
+ .component_driver = {
+ .controls = adav80x_controls,
+ .num_controls = ARRAY_SIZE(adav80x_controls),
+ .dapm_widgets = adav80x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(adav80x_dapm_widgets),
+ .dapm_routes = adav80x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(adav80x_dapm_routes),
+ },
};
int adav80x_bus_probe(struct device *dev, struct regmap *regmap)
diff --git a/sound/soc/codecs/ads117x.c b/sound/soc/codecs/ads117x.c
index c5be1bdc2c9a..90c756d183b4 100644
--- a/sound/soc/codecs/ads117x.c
+++ b/sound/soc/codecs/ads117x.c
@@ -59,10 +59,12 @@ static struct snd_soc_dai_driver ads117x_dai = {
};
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),
+ .component_driver = {
+ .dapm_widgets = ads117x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ads117x_dapm_widgets),
+ .dapm_routes = ads117x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ads117x_dapm_routes),
+ },
};
static int ads117x_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/ak4104.c b/sound/soc/codecs/ak4104.c
index 595d02d7602c..1a9d233c94d0 100644
--- a/sound/soc/codecs/ak4104.c
+++ b/sound/soc/codecs/ak4104.c
@@ -163,7 +163,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
@@ -245,10 +248,12 @@ static struct snd_soc_codec_driver soc_codec_device_ak4104 = {
.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),
+ .component_driver = {
+ .dapm_widgets = ak4104_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets),
+ .dapm_routes = ak4104_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak4104_dapm_routes),
+ }
};
static const struct regmap_config ak4104_regmap = {
diff --git a/sound/soc/codecs/ak4535.c b/sound/soc/codecs/ak4535.c
index 54428c64387b..66cfffde9a12 100644
--- a/sound/soc/codecs/ak4535.c
+++ b/sound/soc/codecs/ak4535.c
@@ -395,12 +395,14 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
.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),
+ .component_driver = {
+ .controls = ak4535_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4535_snd_controls),
+ .dapm_widgets = ak4535_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
+ .dapm_routes = ak4535_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ak4535_audio_map),
+ },
};
static int ak4535_i2c_probe(struct i2c_client *i2c,
diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c
index 298dedc05140..b92c548b9d29 100644
--- a/sound/soc/codecs/ak4554.c
+++ b/sound/soc/codecs/ak4554.c
@@ -65,10 +65,12 @@ static struct snd_soc_dai_driver ak4554_dai = {
};
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),
+ .component_driver = {
+ .dapm_widgets = ak4554_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4554_dapm_widgets),
+ .dapm_routes = ak4554_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak4554_dapm_routes),
+ },
};
static int ak4554_soc_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
index 647f69de6baa..e819dd8c82fd 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -146,6 +146,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[] = {
@@ -436,23 +437,35 @@ static struct snd_soc_dai_driver ak4613_dai = {
.symmetric_rates = 1,
};
-static int ak4613_resume(struct snd_soc_codec *codec)
+static int ak4613_suspend(struct snd_soc_codec *codec)
{
struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
+ return 0;
+}
+
+static int ak4613_resume(struct snd_soc_codec *codec)
+{
+ struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+ regcache_cache_only(regmap, false);
return regcache_sync(regmap);
}
static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
+ .suspend = ak4613_suspend,
.resume = ak4613_resume,
.set_bias_level = ak4613_set_bias_level,
- .controls = ak4613_snd_controls,
- .num_controls = ARRAY_SIZE(ak4613_snd_controls),
- .dapm_widgets = ak4613_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets),
- .dapm_routes = ak4613_intercon,
- .num_dapm_routes = ARRAY_SIZE(ak4613_intercon),
+ .component_driver = {
+ .controls = ak4613_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4613_snd_controls),
+ .dapm_widgets = ak4613_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4613_dapm_widgets),
+ .dapm_routes = ak4613_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4613_intercon),
+ },
};
static void ak4613_parse_of(struct ak4613_priv *priv,
@@ -530,7 +543,6 @@ static int ak4613_i2c_remove(struct i2c_client *client)
static struct i2c_driver ak4613_i2c_driver = {
.driver = {
.name = "ak4613-codec",
- .owner = THIS_MODULE,
.of_match_table = ak4613_of_match,
},
.probe = ak4613_i2c_probe,
diff --git a/sound/soc/codecs/ak4641.c b/sound/soc/codecs/ak4641.c
index b14176f8d884..c91717d08513 100644
--- a/sound/soc/codecs/ak4641.c
+++ b/sound/soc/codecs/ak4641.c
@@ -505,12 +505,14 @@ static struct snd_soc_dai_driver ak4641_dai[] = {
};
static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
- .controls = ak4641_snd_controls,
- .num_controls = ARRAY_SIZE(ak4641_snd_controls),
- .dapm_widgets = ak4641_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets),
- .dapm_routes = ak4641_audio_map,
- .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
+ .component_driver = {
+ .controls = ak4641_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4641_snd_controls),
+ .dapm_widgets = ak4641_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4641_dapm_widgets),
+ .dapm_routes = ak4641_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(ak4641_audio_map),
+ },
.set_bias_level = ak4641_set_bias_level,
.suspend_bias_off = true,
};
diff --git a/sound/soc/codecs/ak4642.c b/sound/soc/codecs/ak4642.c
index 4d8b9e49e8d6..2609f95b7d19 100644
--- a/sound/soc/codecs/ak4642.c
+++ b/sound/soc/codecs/ak4642.c
@@ -523,15 +523,23 @@ static struct snd_soc_dai_driver ak4642_dai = {
.symmetric_rates = 1,
};
-static int ak4642_resume(struct snd_soc_codec *codec)
+static int ak4642_suspend(struct snd_soc_codec *codec)
{
struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+ regcache_cache_only(regmap, true);
regcache_mark_dirty(regmap);
- regcache_sync(regmap);
return 0;
}
+static int ak4642_resume(struct snd_soc_codec *codec)
+{
+ struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+ regcache_cache_only(regmap, false);
+ regcache_sync(regmap);
+ return 0;
+}
static int ak4642_probe(struct snd_soc_codec *codec)
{
struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
@@ -544,14 +552,17 @@ static int ak4642_probe(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
.probe = ak4642_probe,
+ .suspend = ak4642_suspend,
.resume = ak4642_resume,
.set_bias_level = ak4642_set_bias_level,
- .controls = ak4642_snd_controls,
- .num_controls = ARRAY_SIZE(ak4642_snd_controls),
- .dapm_widgets = ak4642_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(ak4642_dapm_widgets),
- .dapm_routes = ak4642_intercon,
- .num_dapm_routes = ARRAY_SIZE(ak4642_intercon),
+ .component_driver = {
+ .controls = ak4642_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4642_snd_controls),
+ .dapm_widgets = ak4642_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4642_dapm_widgets),
+ .dapm_routes = ak4642_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4642_intercon),
+ },
};
static const struct regmap_config ak4642_regmap = {
diff --git a/sound/soc/codecs/ak4671.c b/sound/soc/codecs/ak4671.c
index c73a9f6914b6..6088afaabf62 100644
--- a/sound/soc/codecs/ak4671.c
+++ b/sound/soc/codecs/ak4671.c
@@ -612,12 +612,14 @@ static struct snd_soc_dai_driver ak4671_dai = {
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),
+ .component_driver = {
+ .controls = ak4671_snd_controls,
+ .num_controls = ARRAY_SIZE(ak4671_snd_controls),
+ .dapm_widgets = ak4671_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak4671_dapm_widgets),
+ .dapm_routes = ak4671_intercon,
+ .num_dapm_routes = ARRAY_SIZE(ak4671_intercon),
+ },
};
static const struct regmap_config ak4671_regmap = {
diff --git a/sound/soc/codecs/ak5386.c b/sound/soc/codecs/ak5386.c
index afa95360826d..0ef2df223336 100644
--- a/sound/soc/codecs/ak5386.c
+++ b/sound/soc/codecs/ak5386.c
@@ -74,10 +74,12 @@ static struct snd_soc_codec_driver soc_codec_ak5386 = {
.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),
+ .component_driver = {
+ .dapm_widgets = ak5386_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets),
+ .dapm_routes = ak5386_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes),
+ },
};
static int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai,
diff --git a/sound/soc/codecs/alc5632.c b/sound/soc/codecs/alc5632.c
index 4d3ba33eb6f9..adb80d8719bd 100644
--- a/sound/soc/codecs/alc5632.c
+++ b/sound/soc/codecs/alc5632.c
@@ -1072,12 +1072,14 @@ static const struct snd_soc_codec_driver soc_codec_device_alc5632 = {
.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),
+ .component_driver = {
+ .controls = alc5632_snd_controls,
+ .num_controls = ARRAY_SIZE(alc5632_snd_controls),
+ .dapm_widgets = alc5632_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(alc5632_dapm_widgets),
+ .dapm_routes = alc5632_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(alc5632_dapm_routes),
+ },
};
static const struct regmap_config alc5632_regmap = {
diff --git a/sound/soc/codecs/arizona.c b/sound/soc/codecs/arizona.c
index 664a8c044ffb..846ca079845f 100644
--- a/sound/soc/codecs/arizona.c
+++ b/sound/soc/codecs/arizona.c
@@ -85,30 +85,9 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
{
struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
- struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
- bool manual_ena = false;
int val;
- switch (arizona->type) {
- case WM5102:
- switch (arizona->rev) {
- case 0:
- break;
- default:
- manual_ena = true;
- break;
- }
- default:
- break;
- }
-
switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- if (!priv->spk_ena && manual_ena) {
- regmap_write_async(arizona->regmap, 0x4f5, 0x25a);
- priv->spk_ena_pending = true;
- }
- break;
case SND_SOC_DAPM_POST_PMU:
val = snd_soc_read(codec, ARIZONA_INTERRUPT_RAW_STATUS_3);
if (val & ARIZONA_SPK_OVERHEAT_STS) {
@@ -120,38 +99,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,12 +159,14 @@ 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)
{
@@ -324,6 +284,17 @@ int arizona_init_gpio(struct snd_soc_codec *codec)
}
EXPORT_SYMBOL_GPL(arizona_init_gpio);
+int arizona_init_notifiers(struct snd_soc_codec *codec)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+
+ BLOCKING_INIT_NOTIFIER_HEAD(&arizona->notifier);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_notifiers);
+
const char * const arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
"None",
"Tone Generator 1",
@@ -619,7 +590,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",
};
@@ -648,7 +619,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"
};
@@ -676,7 +647,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",
};
@@ -810,6 +781,14 @@ const struct soc_enum arizona_output_anc_src[] = {
};
EXPORT_SYMBOL_GPL(arizona_output_anc_src);
+const struct snd_kcontrol_new arizona_voice_trigger_switch[] = {
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 0, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 1, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 2, 1, 0),
+ SOC_DAPM_SINGLE("Switch", SND_SOC_NOPM, 3, 1, 0),
+};
+EXPORT_SYMBOL_GPL(arizona_voice_trigger_switch);
+
static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
{
struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
@@ -887,6 +866,7 @@ int arizona_out_ev(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 arizona *arizona = priv->arizona;
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
@@ -900,6 +880,18 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
priv->out_up_pending++;
priv->out_up_delay += 17;
break;
+ case ARIZONA_OUT4L_ENA_SHIFT:
+ case ARIZONA_OUT4R_ENA_SHIFT:
+ priv->out_up_pending++;
+ switch (arizona->type) {
+ case WM5102:
+ case WM8997:
+ break;
+ default:
+ priv->out_up_delay += 10;
+ break;
+ }
+ break;
default:
break;
}
@@ -912,8 +904,12 @@ 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) {
+ if (!priv->out_up_pending && priv->out_up_delay) {
+ dev_dbg(codec->dev, "Power up delay: %d\n",
+ priv->out_up_delay);
msleep(priv->out_up_delay);
priv->out_up_delay = 0;
}
@@ -934,6 +930,21 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
priv->out_down_pending++;
priv->out_down_delay++;
break;
+ case ARIZONA_OUT4L_ENA_SHIFT:
+ case ARIZONA_OUT4R_ENA_SHIFT:
+ priv->out_down_pending++;
+ switch (arizona->type) {
+ case WM5102:
+ case WM8997:
+ break;
+ case WM8998:
+ case WM1814:
+ priv->out_down_delay += 5;
+ break;
+ default:
+ priv->out_down_delay++;
+ break;
+ }
default:
break;
}
@@ -946,8 +957,12 @@ 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) {
+ if (!priv->out_down_pending && priv->out_down_delay) {
+ dev_dbg(codec->dev, "Power down delay: %d\n",
+ priv->out_down_delay);
msleep(priv->out_down_delay);
priv->out_down_delay = 0;
}
@@ -1943,8 +1958,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;
@@ -2211,13 +2226,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);
@@ -2231,21 +2246,24 @@ 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);
}
/*
@@ -2256,6 +2274,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) {
@@ -2278,6 +2300,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.
@@ -2291,14 +2316,14 @@ 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)
regmap_update_bits_async(arizona->regmap, fll->base + 0x11,
ARIZONA_FLL1_SYNC_ENA,
ARIZONA_FLL1_SYNC_ENA);
+ 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,
@@ -2573,6 +2598,30 @@ int arizona_lhpf_coeff_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(arizona_lhpf_coeff_put);
+int arizona_register_notifier(struct snd_soc_codec *codec,
+ struct notifier_block *nb,
+ int (*notify)(struct notifier_block *nb,
+ unsigned long action, void *data))
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+
+ nb->notifier_call = notify;
+
+ return blocking_notifier_chain_register(&arizona->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(arizona_register_notifier);
+
+int arizona_unregister_notifier(struct snd_soc_codec *codec,
+ struct notifier_block *nb)
+{
+ struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+ struct arizona *arizona = priv->arizona;
+
+ return blocking_notifier_chain_unregister(&arizona->notifier, nb);
+}
+EXPORT_SYMBOL_GPL(arizona_unregister_notifier);
+
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 ce0531b8c632..850aa338ba29 100644
--- a/sound/soc/codecs/arizona.h
+++ b/sound/soc/codecs/arizona.h
@@ -63,6 +63,9 @@
#define ARIZONA_DVFS_SR1_RQ 0x001
#define ARIZONA_DVFS_ADSP1_RQ 0x100
+/* Notifier events */
+#define ARIZONA_NOTIFY_VOICE_TRIGGER 0x1
+
struct arizona;
struct wm_adsp;
@@ -87,14 +90,15 @@ 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;
};
+struct arizona_voice_trigger_info {
+ int core;
+};
+
#define ARIZONA_NUM_MIXER_INPUTS 104
extern const unsigned int arizona_mixer_tlv[];
@@ -186,20 +190,21 @@ 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, 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, \
@@ -248,6 +253,8 @@ extern const struct soc_enum arizona_anc_input_src[];
extern const struct soc_enum arizona_anc_ng_enum;
extern const struct soc_enum arizona_output_anc_src[];
+extern const struct snd_kcontrol_new arizona_voice_trigger_switch[];
+
extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol,
int event);
@@ -306,6 +313,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source,
extern int arizona_init_spk(struct snd_soc_codec *codec);
extern int arizona_init_gpio(struct snd_soc_codec *codec);
extern int arizona_init_mono(struct snd_soc_codec *codec);
+extern int arizona_init_notifiers(struct snd_soc_codec *codec);
extern int arizona_free_spk(struct snd_soc_codec *codec);
@@ -317,4 +325,13 @@ int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
+
+extern int arizona_register_notifier(struct snd_soc_codec *codec,
+ struct notifier_block *nb,
+ int (*notify)(struct notifier_block *nb,
+ unsigned long action,
+ void *data));
+extern int arizona_unregister_notifier(struct snd_soc_codec *codec,
+ struct notifier_block *nb);
+
#endif
diff --git a/sound/soc/codecs/bt-sco.c b/sound/soc/codecs/bt-sco.c
index b084ad113e96..8014e697d540 100644
--- a/sound/soc/codecs/bt-sco.c
+++ b/sound/soc/codecs/bt-sco.c
@@ -25,35 +25,56 @@ static const struct snd_soc_dapm_route bt_sco_routes[] = {
{ "TX", NULL, "Playback" },
};
-static struct snd_soc_dai_driver bt_sco_dai = {
- .name = "bt-sco-pcm",
- .playback = {
- .stream_name = "Playback",
- .channels_min = 1,
- .channels_max = 1,
- .rates = SNDRV_PCM_RATE_8000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
- },
- .capture = {
- .stream_name = "Capture",
- .channels_min = 1,
- .channels_max = 1,
- .rates = SNDRV_PCM_RATE_8000,
- .formats = SNDRV_PCM_FMTBIT_S16_LE,
+static struct snd_soc_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),
+ .component_driver = {
+ .dapm_widgets = bt_sco_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(bt_sco_widgets),
+ .dapm_routes = bt_sco_routes,
+ .num_dapm_routes = ARRAY_SIZE(bt_sco_routes),
+ },
};
static int bt_sco_probe(struct platform_device *pdev)
{
return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_bt_sco,
- &bt_sco_dai, 1);
+ bt_sco_dai, ARRAY_SIZE(bt_sco_dai));
}
static int bt_sco_remove(struct platform_device *pdev)
@@ -77,6 +98,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);
diff --git a/sound/soc/codecs/cq93vc.c b/sound/soc/codecs/cq93vc.c
index 1c895a53001d..7a2d9a2ee427 100644
--- a/sound/soc/codecs/cq93vc.c
+++ b/sound/soc/codecs/cq93vc.c
@@ -131,8 +131,10 @@ static struct regmap *cq93vc_get_regmap(struct device *dev)
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),
+ .component_driver = {
+ .controls = cq93vc_snd_controls,
+ .num_controls = ARRAY_SIZE(cq93vc_snd_controls),
+ },
};
static int cq93vc_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/cs35l32.c b/sound/soc/codecs/cs35l32.c
index 287d13740be4..7e9806206648 100644
--- a/sound/soc/codecs/cs35l32.c
+++ b/sound/soc/codecs/cs35l32.c
@@ -231,13 +231,14 @@ static int cs35l32_codec_set_sysclk(struct snd_soc_codec *codec,
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),
+ .component_driver = {
+ .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),
+ },
};
/* Current and threshold powerup sequence Pg37 in datasheet */
diff --git a/sound/soc/codecs/cs35l33.c b/sound/soc/codecs/cs35l33.c
new file mode 100644
index 000000000000..6df29fa30fb9
--- /dev/null
+++ b/sound/soc/codecs/cs35l33.c
@@ -0,0 +1,1304 @@
+/*
+ * cs35l33.c -- CS35L33 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <paul.handrigan@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/kernel.h>
+#include <linux/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.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_gpio.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include "cs35l33.h"
+
+#define CS35L33_BOOT_DELAY 50
+
+struct cs35l33_private {
+ struct snd_soc_codec *codec;
+ 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ 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(codec->dev, "Amp calibration done\n");
+ }
+ dev_dbg(codec->dev, "Amp turned on\n");
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ dev_dbg(codec->dev, "Amp turned off\n");
+ break;
+ default:
+ dev_err(codec->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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "BST turned on\n");
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ dev_dbg(codec->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(codec->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(codec->dev, "BST and SDIN turned off\n");
+ break;
+ default:
+ dev_err(codec->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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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(codec->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(codec->dev, "SDOUT turned off\n");
+ break;
+ default:
+ dev_err(codec->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_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ unsigned int val;
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ 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_codec *codec = codec_dai->codec;
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
+ CS35L33_MS_MASK, CS35L33_MS_MASK);
+ dev_dbg(codec->dev, "Audio port in master mode\n");
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ regmap_update_bits(priv->regmap, CS35L33_ADSP_CTL,
+ CS35L33_MS_MASK, 0);
+ dev_dbg(codec->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(codec->dev, "Audio port in TDM mode\n");
+ break;
+ case SND_SOC_DAIFMT_I2S:
+ priv->is_tdm_mode = false;
+ dev_dbg(codec->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_codec *codec = dai->codec;
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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(codec->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_codec *codec = dai->codec;
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ 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_codec *codec = dai->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+ 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(codec->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(codec->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(codec->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(codec->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(codec->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_codec_set_sysclk(struct snd_soc_codec *codec,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+
+ 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(codec->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_rates = 1,
+};
+
+static int cs35l33_set_hg_data(struct snd_soc_codec *codec,
+ struct cs35l33_pdata *pdata)
+{
+ struct cs35l33_hg *hg_config = &pdata->hg_config;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct cs35l33_private *priv = snd_soc_codec_get_drvdata(codec);
+
+ 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_codec *codec, unsigned int bst)
+{
+ struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0, steps = 0;
+
+ /* Boost current in uA */
+ if (bst > 3600000 || bst < 1850000) {
+ dev_err(codec->dev, "Invalid boost current %d\n", bst);
+ ret = -EINVAL;
+ goto err;
+ }
+
+ if (bst % 15625) {
+ dev_err(codec->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_codec *codec)
+{
+ struct cs35l33_private *cs35l33 = snd_soc_codec_get_drvdata(codec);
+
+ cs35l33->codec = codec;
+ pm_runtime_get_sync(codec->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(codec, cs35l33->pdata.boost_ipk);
+
+ if (cs35l33->enable_soft_ramp) {
+ snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+ CS35L33_DIGSFT, CS35L33_DIGSFT);
+ snd_soc_update_bits(codec, CS35L33_DAC_CTL,
+ CS35L33_DSR_RATE, cs35l33->pdata.ramp_rate);
+ } else {
+ snd_soc_update_bits(codec, 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_update_bits(codec, CS35L33_ADC_CTL,
+ CS35L33_IMON_SCALE, cs35l33->pdata.imon_adc_scale);
+
+ cs35l33_set_hg_data(codec, &(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(codec->dev);
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l33 = {
+ .probe = cs35l33_probe,
+
+ .set_bias_level = cs35l33_set_bias_level,
+ .set_sysclk = cs35l33_codec_set_sysclk,
+
+ .component_driver = {
+ .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),
+ },
+ .idle_bias_off = true,
+};
+
+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_RBTREE,
+ .use_single_rw = true,
+};
+
+static int __maybe_unused cs35l33_runtime_resume(struct device *dev)
+{
+ struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
+ int ret;
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ if (cs35l33->reset_gpio)
+ 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);
+
+ if (cs35l33->reset_gpio)
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 1);
+
+ msleep(CS35L33_BOOT_DELAY);
+
+ ret = regcache_sync(cs35l33->regmap);
+ if (ret != 0) {
+ dev_err(dev, "Failed to restore register cache\n");
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regcache_cache_only(cs35l33->regmap, true);
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return ret;
+}
+
+static int __maybe_unused cs35l33_runtime_suspend(struct device *dev)
+{
+ struct cs35l33_private *cs35l33 = dev_get_drvdata(dev);
+
+ dev_dbg(dev, "%s\n", __func__);
+
+ /* redo the calibration in next power up */
+ cs35l33->amp_cal = false;
+
+ regcache_cache_only(cs35l33->regmap, true);
+ regcache_mark_dirty(cs35l33->regmap);
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return 0;
+}
+
+static const struct dev_pm_ops cs35l33_pm_ops = {
+ SET_RUNTIME_PM_OPS(cs35l33_runtime_suspend,
+ cs35l33_runtime_resume,
+ NULL)
+};
+
+static int cs35l33_get_hg_data(const struct device_node *np,
+ struct cs35l33_pdata *pdata)
+{
+ struct device_node *hg;
+ struct cs35l33_hg *hg_config = &pdata->hg_config;
+ u32 val32;
+
+ hg = of_get_child_by_name(np, "cirrus,hg-algo");
+ hg_config->enable_hg_algo = hg ? true : false;
+
+ if (hg_config->enable_hg_algo) {
+ if (of_property_read_u32(hg, "cirrus,mem-depth", &val32) >= 0)
+ hg_config->mem_depth = val32;
+ if (of_property_read_u32(hg, "cirrus,release-rate",
+ &val32) >= 0)
+ hg_config->release_rate = val32;
+ if (of_property_read_u32(hg, "cirrus,ldo-thld", &val32) >= 0)
+ hg_config->ldo_thld = val32;
+ if (of_property_read_u32(hg, "cirrus,ldo-path-disable",
+ &val32) >= 0)
+ hg_config->ldo_path_disable = val32;
+ if (of_property_read_u32(hg, "cirrus,ldo-entry-delay",
+ &val32) >= 0)
+ hg_config->ldo_entry_delay = val32;
+
+ hg_config->vp_hg_auto = of_property_read_bool(hg,
+ "cirrus,vp-hg-auto");
+
+ if (of_property_read_u32(hg, "cirrus,vp-hg", &val32) >= 0)
+ hg_config->vp_hg = val32;
+ if (of_property_read_u32(hg, "cirrus,vp-hg-rate", &val32) >= 0)
+ hg_config->vp_hg_rate = val32;
+ if (of_property_read_u32(hg, "cirrus,vp-hg-va", &val32) >= 0)
+ hg_config->vp_hg_va = val32;
+ }
+
+ of_node_put(hg);
+
+ return 0;
+}
+
+static irqreturn_t cs35l33_irq_thread(int irq, void *data)
+{
+ struct cs35l33_private *cs35l33 = data;
+ struct snd_soc_codec *codec = cs35l33->codec;
+ 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(codec->dev, "Amp short error\n");
+ if (!(current_val & CS35L33_AMP_SHORT)) {
+ dev_dbg(codec->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(codec->dev, "Cal error\n");
+
+ /* redo the calibration in next power up */
+ cs35l33->amp_cal = false;
+
+ if (!(current_val & CS35L33_CAL_ERR)) {
+ dev_dbg(codec->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(codec->dev, "Over temperature error\n");
+ if (!(current_val & CS35L33_OTE)) {
+ dev_dbg(codec->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(codec->dev, "Over temperature warning\n");
+ if (!(current_val & CS35L33_OTW)) {
+ dev_dbg(codec->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(codec->dev, "ERROR: ADSPCLK Interrupt\n");
+
+ if (CS35L33_MCLK_ERR & sticky_val1)
+ dev_err(codec->dev, "ERROR: MCLK Interrupt\n");
+
+ if (CS35L33_VMON_OVFL & sticky_val2)
+ dev_err(codec->dev,
+ "ERROR: VMON Overflow Interrupt\n");
+
+ if (CS35L33_IMON_OVFL & sticky_val2)
+ dev_err(codec->dev,
+ "ERROR: IMON Overflow Interrupt\n");
+
+ if (CS35L33_VPMON_OVFL & sticky_val2)
+ dev_err(codec->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,
+ const struct i2c_device_id *id)
+{
+ 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-gpios", 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;
+ }
+
+ if (cs35l33->reset_gpio)
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 1);
+
+ msleep(CS35L33_BOOT_DELAY);
+ regcache_cache_only(cs35l33->regmap, false);
+
+ /* initialize codec */
+ ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_AB, &reg);
+ devid = (reg & 0xFF) << 12;
+ ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_CD, &reg);
+ devid |= (reg & 0xFF) << 4;
+ ret = regmap_read(cs35l33->regmap, CS35L33_DEVID_E, &reg);
+ devid |= (reg & 0xF0) >> 4;
+
+ if (devid != CS35L33_CHIP_ID) {
+ dev_err(&i2c_client->dev,
+ "CS35L33 Device ID (%X). Expected ID %X\n",
+ devid, CS35L33_CHIP_ID);
+ 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 = snd_soc_register_codec(&i2c_client->dev,
+ &soc_codec_dev_cs35l33, &cs35l33_dai, 1);
+ if (ret < 0) {
+ dev_err(&i2c_client->dev, "%s: Register codec failed\n",
+ __func__);
+ goto err_enable;
+ }
+
+ return 0;
+
+err_enable:
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return ret;
+}
+
+static int cs35l33_i2c_remove(struct i2c_client *client)
+{
+ struct cs35l33_private *cs35l33 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+
+ if (cs35l33->reset_gpio)
+ gpiod_set_value_cansleep(cs35l33->reset_gpio, 0);
+
+ pm_runtime_disable(&client->dev);
+ regulator_bulk_disable(cs35l33->num_core_supplies,
+ cs35l33->core_supplies);
+
+ return 0;
+}
+
+static const struct of_device_id cs35l33_of_match[] = {
+ { .compatible = "cirrus,cs35l33", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l33_of_match);
+
+static const struct i2c_device_id cs35l33_id[] = {
+ {"cs35l33", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l33_id);
+
+static struct i2c_driver cs35l33_i2c_driver = {
+ .driver = {
+ .name = "cs35l33",
+ .pm = &cs35l33_pm_ops,
+ .of_match_table = cs35l33_of_match,
+
+ },
+ .id_table = cs35l33_id,
+ .probe = cs35l33_i2c_probe,
+ .remove = cs35l33_i2c_remove,
+
+};
+module_i2c_driver(cs35l33_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L33 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <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..c045737d1a5f
--- /dev/null
+++ b/sound/soc/codecs/cs35l33.h
@@ -0,0 +1,221 @@
+/*
+ * cs35l33.h -- CS35L33 ALSA SoC audio driver
+ *
+ * Copyright 2016 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 __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/cs4265.c b/sound/soc/codecs/cs4265.c
index 55db19ddc5ff..fd966bb851cb 100644
--- a/sound/soc/codecs/cs4265.c
+++ b/sound/soc/codecs/cs4265.c
@@ -547,13 +547,14 @@ 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),
+ .component_driver = {
+ .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),
+ },
};
static const struct regmap_config cs4265_regmap = {
diff --git a/sound/soc/codecs/cs4270.c b/sound/soc/codecs/cs4270.c
index e07807d96b68..18baea2f7d65 100644
--- a/sound/soc/codecs/cs4270.c
+++ b/sound/soc/codecs/cs4270.c
@@ -617,12 +617,14 @@ static const struct snd_soc_codec_driver soc_codec_device_cs4270 = {
.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),
+ .component_driver = {
+ .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),
+ },
};
/*
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 0c0010b25421..8c0f3b89b5bc 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -645,12 +645,14 @@ static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
.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),
+ .component_driver = {
+ .controls = cs4271_snd_controls,
+ .num_controls = ARRAY_SIZE(cs4271_snd_controls),
+ .dapm_widgets = cs4271_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs4271_dapm_widgets),
+ .dapm_routes = cs4271_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs4271_dapm_routes),
+ },
};
static int cs4271_common_probe(struct device *dev,
diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c
index 35488f14e237..96cfe38943cd 100644
--- a/sound/soc/codecs/cs42l51.c
+++ b/sound/soc/codecs/cs42l51.c
@@ -507,12 +507,14 @@ static int cs42l51_codec_probe(struct snd_soc_codec *codec)
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),
+ .component_driver = {
+ .controls = cs42l51_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42l51_snd_controls),
+ .dapm_widgets = cs42l51_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42l51_dapm_widgets),
+ .dapm_routes = cs42l51_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs42l51_routes),
+ },
};
const struct regmap_config cs42l51_regmap = {
diff --git a/sound/soc/codecs/cs42l52.c b/sound/soc/codecs/cs42l52.c
index 47b97fcefb0b..0d9c4a57301b 100644
--- a/sound/soc/codecs/cs42l52.c
+++ b/sound/soc/codecs/cs42l52.c
@@ -1056,13 +1056,14 @@ static const struct snd_soc_codec_driver soc_codec_dev_cs42l52 = {
.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),
+ .component_driver = {
+ .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),
+ },
};
/* Current and threshold powerup sequence Pg37 */
diff --git a/sound/soc/codecs/cs42l56.c b/sound/soc/codecs/cs42l56.c
index eec1ff853b98..54c1768bc818 100644
--- a/sound/soc/codecs/cs42l56.c
+++ b/sound/soc/codecs/cs42l56.c
@@ -1121,13 +1121,14 @@ static const struct snd_soc_codec_driver soc_codec_dev_cs42l56 = {
.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),
+ .component_driver = {
+ .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),
+ },
};
static const struct regmap_config cs42l56_regmap = {
diff --git a/sound/soc/codecs/cs42l73.c b/sound/soc/codecs/cs42l73.c
index 42a8fd4e1f9b..71ba5605495f 100644
--- a/sound/soc/codecs/cs42l73.c
+++ b/sound/soc/codecs/cs42l73.c
@@ -794,7 +794,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 +844,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 */
@@ -1257,13 +1257,14 @@ static const struct snd_soc_codec_driver soc_codec_dev_cs42l73 = {
.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),
+ .component_driver = {
+ .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),
+ },
};
static const struct regmap_config cs42l73_regmap = {
diff --git a/sound/soc/codecs/cs42xx8.c b/sound/soc/codecs/cs42xx8.c
index 1179101b2b05..b4d87379d2bc 100644
--- a/sound/soc/codecs/cs42xx8.c
+++ b/sound/soc/codecs/cs42xx8.c
@@ -411,12 +411,14 @@ 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),
+ .component_driver = {
+ .controls = cs42xx8_snd_controls,
+ .num_controls = ARRAY_SIZE(cs42xx8_snd_controls),
+ .dapm_widgets = cs42xx8_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs42xx8_dapm_widgets),
+ .dapm_routes = cs42xx8_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(cs42xx8_dapm_routes),
+ },
};
const struct cs42xx8_driver_data cs42448_data = {
diff --git a/sound/soc/codecs/cs4349.c b/sound/soc/codecs/cs4349.c
index 0ac8fc5ed4ae..231ca935cdf3 100644
--- a/sound/soc/codecs/cs4349.c
+++ b/sound/soc/codecs/cs4349.c
@@ -256,13 +256,14 @@ static struct snd_soc_dai_driver cs4349_dai = {
};
static struct snd_soc_codec_driver soc_codec_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),
+ .component_driver = {
+ .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),
+ },
};
static const struct regmap_config cs4349_regmap = {
diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c
index 5ec5a682d186..5b22564f037c 100644
--- a/sound/soc/codecs/cs47l24.c
+++ b/sound/soc/codecs/cs47l24.c
@@ -359,6 +359,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 |
@@ -741,6 +746,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" },
@@ -799,7 +814,6 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
{ "AIF3 Capture", NULL, "SYSCLK" },
{ "Voice Control DSP", NULL, "DSP3" },
- { "Voice Control DSP", NULL, "SYSCLK" },
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@@ -808,7 +822,6 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
{ "IN2R PGA", NULL, "IN2R" },
{ "Audio Trace DSP", NULL, "DSP2" },
- { "Audio Trace DSP", NULL, "SYSCLK" },
ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
@@ -899,10 +912,16 @@ 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,
@@ -1067,6 +1086,7 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
{
struct cs47l24_priv *priv = data;
struct arizona *arizona = priv->core.arizona;
+ struct arizona_voice_trigger_info info;
int serviced = 0;
int i, ret;
@@ -1074,6 +1094,12 @@ static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
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) {
@@ -1096,6 +1122,7 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
arizona_init_spk(codec);
arizona_init_gpio(codec);
arizona_init_mono(codec);
+ arizona_init_notifiers(codec);
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
"ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
@@ -1171,12 +1198,14 @@ static struct snd_soc_codec_driver soc_codec_dev_cs47l24 = {
.set_sysclk = arizona_set_sysclk,
.set_pll = cs47l24_set_fll,
- .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),
+ .component_driver = {
+ .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 struct snd_compr_ops cs47l24_compr_ops = {
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
new file mode 100644
index 000000000000..cb47fb595ff4
--- /dev/null
+++ b/sound/soc/codecs/cs53l30.c
@@ -0,0 +1,1144 @@
+/*
+ * 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>
+ *
+ * This program is free software; you can 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/i2c.h>
+#include <linux/module.h>
+#include <linux/of_gpio.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"
+
+#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, 0x18, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC1B PGA Volume",
+ CS53L30_ADC1B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC2A PGA Volume",
+ CS53L30_ADC2A_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+ SOC_SINGLE_SX_TLV("ADC2B PGA Volume",
+ CS53L30_ADC2B_AFE_CTL, 0, 0x34, 0x18, pga_tlv),
+
+ SOC_SINGLE_SX_TLV("ADC1A Digital Volume",
+ CS53L30_ADC1A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC1B Digital Volume",
+ CS53L30_ADC1B_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC2A Digital Volume",
+ CS53L30_ADC2A_DIG_VOL, 0, 0xA0, 0x0C, dig_tlv),
+ SOC_SINGLE_SX_TLV("ADC2B Digital Volume",
+ CS53L30_ADC2B_DIG_VOL, 0, 0xA0, 0x0C, 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_codec_get_drvdata(dai->codec);
+ 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_codec_get_drvdata(dai->codec);
+ u8 aspcfg = 0, aspctl1 = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ aspcfg |= CS53L30_ASP_MS;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* DAI mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ /* Set TDM_PDN to turn off TDM mode -- Reset default */
+ aspctl1 |= CS53L30_ASP_TDM_PDN;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ /*
+ * Clear TDM_PDN to turn on TDM mode; Use ASP_SCLK_INV = 0
+ * with SHIFT_LEFT = 1 combination as Figure 4-13 shows in
+ * the CS53L30 datasheet
+ */
+ aspctl1 |= CS53L30_SHIFT_LEFT;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* Check to see if the SCLK is inverted */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_NF:
+ case SND_SOC_DAIFMT_IB_IF:
+ aspcfg ^= CS53L30_ASP_SCLK_INV;
+ break;
+ default:
+ break;
+ }
+
+ regmap_update_bits(priv->regmap, CS53L30_ASPCFG_CTL,
+ CS53L30_ASP_MS | CS53L30_ASP_SCLK_INV, aspcfg);
+
+ regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1,
+ CS53L30_ASP_TDM_PDN | CS53L30_SHIFT_LEFT, aspctl1);
+
+ return 0;
+}
+
+static int cs53l30_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ 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_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg;
+ int i, inter_max_check, ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+ case SND_SOC_BIAS_PREPARE:
+ if (dapm->bias_level == SND_SOC_BIAS_STANDBY)
+ regmap_update_bits(priv->regmap, CS53L30_PWRCTL,
+ CS53L30_PDN_LP_MASK, 0);
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (dapm->bias_level == SND_SOC_BIAS_OFF) {
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret) {
+ dev_err(codec->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_codec_get_drvdata(dai->codec);
+ u8 val = tristate ? CS53L30_ASP_3ST : 0;
+
+ return regmap_update_bits(priv->regmap, CS53L30_ASP_CTL1,
+ CS53L30_ASP_3ST_MASK, val);
+}
+
+static unsigned int const cs53l30_src_rates[] = {
+ 8000, 11025, 12000, 16000, 22050, 24000, 32000, 44100, 48000
+};
+
+static struct snd_pcm_hw_constraint_list src_constraints = {
+ .count = ARRAY_SIZE(cs53l30_src_rates),
+ .list = cs53l30_src_rates,
+};
+
+static int cs53l30_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &src_constraints);
+
+ return 0;
+}
+
+/*
+ * Note: CS53L30 counts the slot number per byte while ASoC counts the slot
+ * number per slot_width. So there is a difference between the slots of ASoC
+ * and the slots of CS53L30.
+ */
+static int cs53l30_set_dai_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(dai->codec);
+ 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_codec_get_drvdata(dai->codec);
+
+ if (priv->mute_gpio)
+ gpiod_set_value_cansleep(priv->mute_gpio, mute);
+
+ return 0;
+}
+
+/* SNDRV_PCM_RATE_KNOT -> 12000, 24000 Hz, limit with constraint list */
+#define CS53L30_RATES (SNDRV_PCM_RATE_8000_48000 | SNDRV_PCM_RATE_KNOT)
+
+#define CS53L30_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+ SNDRV_PCM_FMTBIT_S24_LE)
+
+static const struct snd_soc_dai_ops cs53l30_ops = {
+ .startup = cs53l30_pcm_startup,
+ .hw_params = cs53l30_pcm_hw_params,
+ .set_fmt = cs53l30_set_dai_fmt,
+ .set_sysclk = cs53l30_set_sysclk,
+ .set_tristate = cs53l30_set_tristate,
+ .set_tdm_slot = cs53l30_set_dai_tdm_slot,
+ .mute_stream = cs53l30_mute_stream,
+};
+
+static struct snd_soc_dai_driver cs53l30_dai = {
+ .name = "cs53l30",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = CS53L30_RATES,
+ .formats = CS53L30_FORMATS,
+ },
+ .ops = &cs53l30_ops,
+ .symmetric_rates = 1,
+};
+
+static int cs53l30_codec_probe(struct snd_soc_codec *codec)
+{
+ struct cs53l30_private *priv = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ 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 struct snd_soc_codec_driver cs53l30_driver = {
+ .probe = cs53l30_codec_probe,
+ .set_bias_level = cs53l30_set_bias_level,
+ .idle_bias_off = true,
+
+ .component_driver = {
+ .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),
+ },
+};
+
+static struct regmap_config cs53l30_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS53L30_MAX_REGISTER,
+ .reg_defaults = cs53l30_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(cs53l30_reg_defaults),
+ .volatile_reg = cs53l30_volatile_register,
+ .writeable_reg = cs53l30_writeable_register,
+ .readable_reg = cs53l30_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static int cs53l30_i2c_probe(struct i2c_client *client,
+ const struct i2c_device_id *id)
+{
+ const struct device_node *np = client->dev.of_node;
+ struct device *dev = &client->dev;
+ struct cs53l30_private *cs53l30;
+ unsigned int devid = 0;
+ unsigned int reg;
+ int ret = 0, i;
+ 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;
+ }
+
+ if (cs53l30->reset_gpio)
+ 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 */
+ ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_AB, &reg);
+ devid = reg << 12;
+
+ ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_CD, &reg);
+ devid |= reg << 4;
+
+ ret = regmap_read(cs53l30->regmap, CS53L30_DEVID_E, &reg);
+ devid |= (reg & 0xF0) >> 4;
+
+ 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(dev, "mclk");
+ if (IS_ERR(cs53l30->mclk)) {
+ if (PTR_ERR(cs53l30->mclk) != -ENOENT) {
+ ret = PTR_ERR(cs53l30->mclk);
+ goto error;
+ }
+ /* Otherwise mark the mclk pointer to NULL */
+ cs53l30->mclk = NULL;
+ }
+
+ /* 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 = snd_soc_register_codec(dev, &cs53l30_driver, &cs53l30_dai, 1);
+ if (ret) {
+ dev_err(dev, "failed to register codec: %d\n", ret);
+ goto error;
+ }
+
+ return 0;
+
+error:
+ regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+ return ret;
+}
+
+static int cs53l30_i2c_remove(struct i2c_client *client)
+{
+ struct cs53l30_private *cs53l30 = i2c_get_clientdata(client);
+
+ snd_soc_unregister_codec(&client->dev);
+
+ /* Hold down reset */
+ if (cs53l30->reset_gpio)
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 0);
+
+ regulator_bulk_disable(ARRAY_SIZE(cs53l30->supplies),
+ cs53l30->supplies);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int cs53l30_runtime_suspend(struct device *dev)
+{
+ struct cs53l30_private *cs53l30 = dev_get_drvdata(dev);
+
+ regcache_cache_only(cs53l30->regmap, true);
+
+ /* Hold down reset */
+ if (cs53l30->reset_gpio)
+ 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;
+ }
+
+ if (cs53l30->reset_gpio)
+ gpiod_set_value_cansleep(cs53l30->reset_gpio, 1);
+
+ regcache_cache_only(cs53l30->regmap, false);
+ ret = regcache_sync(cs53l30->regmap);
+ if (ret) {
+ dev_err(dev, "failed to synchronize regcache: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops cs53l30_runtime_pm = {
+ SET_RUNTIME_PM_OPS(cs53l30_runtime_suspend, cs53l30_runtime_resume,
+ NULL)
+};
+
+static const struct of_device_id cs53l30_of_match[] = {
+ { .compatible = "cirrus,cs53l30", },
+ {},
+};
+
+MODULE_DEVICE_TABLE(of, cs53l30_of_match);
+
+static const struct i2c_device_id cs53l30_id[] = {
+ { "cs53l30", 0 },
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs53l30_id);
+
+static struct i2c_driver cs53l30_i2c_driver = {
+ .driver = {
+ .name = "cs53l30",
+ .pm = &cs53l30_runtime_pm,
+ },
+ .id_table = cs53l30_id,
+ .probe = cs53l30_i2c_probe,
+ .remove = cs53l30_i2c_remove,
+};
+
+module_i2c_driver(cs53l30_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS53L30 driver");
+MODULE_AUTHOR("Paul Handrigan, Cirrus Logic Inc, <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..5e39da568749
--- /dev/null
+++ b/sound/soc/codecs/cs53l30.h
@@ -0,0 +1,459 @@
+/*
+ * ALSA SoC CS53L30 codec driver
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Paul Handrigan <Paul.Handrigan@cirrus.com>,
+ * 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.
+ *
+ */
+
+#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..2c12471e42a6 100644
--- a/sound/soc/codecs/cx20442.c
+++ b/sound/soc/codecs/cx20442.c
@@ -226,6 +226,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;
@@ -406,10 +407,12 @@ static struct snd_soc_codec_driver cx20442_codec_dev = {
.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),
+ .component_driver = {
+ .dapm_widgets = cx20442_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cx20442_dapm_widgets),
+ .dapm_routes = cx20442_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cx20442_audio_map),
+ },
};
static int cx20442_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/da7210.c b/sound/soc/codecs/da7210.c
index af23a61b7b28..17053dfc94cf 100644
--- a/sound/soc/codecs/da7210.c
+++ b/sound/soc/codecs/da7210.c
@@ -1167,13 +1167,14 @@ static int da7210_probe(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver soc_codec_dev_da7210 = {
.probe = da7210_probe,
- .controls = da7210_snd_controls,
- .num_controls = ARRAY_SIZE(da7210_snd_controls),
-
- .dapm_widgets = da7210_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(da7210_dapm_widgets),
- .dapm_routes = da7210_audio_map,
- .num_dapm_routes = ARRAY_SIZE(da7210_audio_map),
+ .component_driver = {
+ .controls = da7210_snd_controls,
+ .num_controls = ARRAY_SIZE(da7210_snd_controls),
+ .dapm_widgets = da7210_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da7210_dapm_widgets),
+ .dapm_routes = da7210_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da7210_audio_map),
+ },
};
#if IS_ENABLED(CONFIG_I2C)
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index e5527bc570ae..12da55882c06 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -750,11 +750,18 @@ static int da7213_dai_event(struct snd_soc_dapm_widget *w,
snd_soc_update_bits(codec, DA7213_PC_COUNT,
DA7213_PC_FREERUN_MASK, 0);
- /* Slave mode, if SRM not enabled no need for status checks */
+ /* If SRM not enabled then nothing more to do */
pll_ctrl = snd_soc_read(codec, 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_write(codec, 0xF0, 0x8B);
+ snd_soc_write(codec, 0xF2, 0x03);
+ snd_soc_write(codec, 0xF0, 0x00);
+ }
+
/* Check SRM has locked */
do {
pll_status = snd_soc_read(codec, DA7213_PLL_STATUS);
@@ -771,6 +778,14 @@ static int da7213_dai_event(struct snd_soc_dapm_widget *w,
return 0;
case SND_SOC_DAPM_POST_PMD:
+ /* Revert 32KHz PLL lock udpates if applied previously */
+ pll_ctrl = snd_soc_read(codec, DA7213_PLL_CTRL);
+ if (pll_ctrl & DA7213_PLL_32K_MODE) {
+ snd_soc_write(codec, 0xF0, 0x8B);
+ snd_soc_write(codec, 0xF2, 0x01);
+ snd_soc_write(codec, 0xF0, 0x00);
+ }
+
/* PC free-running */
snd_soc_update_bits(codec, DA7213_PC_COUNT,
DA7213_PC_FREERUN_MASK,
@@ -1247,8 +1262,8 @@ static int da7213_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
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,
@@ -1297,10 +1312,13 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai,
switch (clk_id) {
case DA7213_CLKSRC_MCLK:
- da7213->mclk_squarer_en = false;
+ snd_soc_update_bits(codec, DA7213_PLL_CTRL,
+ DA7213_PLL_MCLK_SQR_EN, 0);
break;
case DA7213_CLKSRC_MCLK_SQR:
- da7213->mclk_squarer_en = true;
+ snd_soc_update_bits(codec, 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);
@@ -1324,7 +1342,7 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai,
return 0;
}
-/* Supported PLL input frequencies are 5MHz - 54MHz. */
+/* Supported PLL input frequencies are 32KHz, 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)
{
@@ -1336,22 +1354,26 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
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) {
+ if (!da7213->master) {
+ dev_err(codec->dev,
+ "32KHz only valid if codec is clock master\n");
+ return -EINVAL;
+ }
+
/* 32KHz PLL Mode */
indiv_bits = DA7213_PLL_INDIV_9_TO_18_MHZ;
indiv = DA7213_PLL_INDIV_9_TO_18_MHZ_VAL;
+ source = DA7213_SYSCLK_PLL_32KHZ;
freq_ref = 3750000;
- pll_ctrl |= DA7213_PLL_32K_MODE;
+
} else {
- /* 5 - 54MHz MCLK */
if (da7213->mclk_rate < 5000000) {
- goto pll_err;
+ dev_err(codec->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;
@@ -1365,32 +1387,44 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
indiv_bits = DA7213_PLL_INDIV_36_TO_54_MHZ;
indiv = DA7213_PLL_INDIV_36_TO_54_MHZ_VAL;
} else {
- goto pll_err;
+ dev_err(codec->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_update_bits(codec, 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(codec->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(codec->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;
@@ -1405,14 +1439,19 @@ static int da7213_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
/* Enable PLL */
pll_ctrl |= DA7213_PLL_EN;
- snd_soc_write(codec, DA7213_PLL_CTRL, pll_ctrl);
+ snd_soc_update_bits(codec, 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_write(codec, 0xF0, 0x8B);
+ snd_soc_write(codec, 0xF1, 0x03);
+ snd_soc_write(codec, 0xF1, 0x01);
+ snd_soc_write(codec, 0xF0, 0x00);
+ }
return 0;
-
-pll_err:
- dev_err(codec_dai->dev, "Unsupported PLL input frequency %d\n",
- da7213->mclk_rate);
- return -EINVAL;
}
/* DAI operations */
@@ -1454,11 +1493,10 @@ static int da7213_set_bias_level(struct snd_soc_codec *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) {
- /* MCLK */
+ case SND_SOC_BIAS_PREPARE:
+ /* Enable MCLK for transition to ON state */
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
if (da7213->mclk) {
ret = clk_prepare_enable(da7213->mclk);
if (ret) {
@@ -1467,21 +1505,24 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec,
return ret;
}
}
-
+ }
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
/* Enable VMID reference & master bias */
snd_soc_update_bits(codec, DA7213_REFERENCES,
DA7213_VMID_EN | DA7213_BIAS_EN,
DA7213_VMID_EN | DA7213_BIAS_EN);
+ } else {
+ /* Remove MCLK */
+ if (da7213->mclk)
+ clk_disable_unprepare(da7213->mclk);
}
break;
case SND_SOC_BIAS_OFF:
/* Disable VMID reference & master bias */
snd_soc_update_bits(codec, DA7213_REFERENCES,
DA7213_VMID_EN | DA7213_BIAS_EN, 0);
-
- /* MCLK */
- if (da7213->mclk)
- clk_disable_unprepare(da7213->mclk);
break;
}
return 0;
@@ -1605,9 +1646,6 @@ static int da7213_probe(struct snd_soc_codec *codec)
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_update_bits(codec, DA7213_PC_COUNT, DA7213_PC_FREERUN_MASK,
DA7213_PC_FREERUN_MASK);
@@ -1740,13 +1778,14 @@ static struct snd_soc_codec_driver soc_codec_dev_da7213 = {
.probe = da7213_probe,
.set_bias_level = da7213_set_bias_level,
- .controls = da7213_snd_controls,
- .num_controls = ARRAY_SIZE(da7213_snd_controls),
-
- .dapm_widgets = da7213_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(da7213_dapm_widgets),
- .dapm_routes = da7213_audio_map,
- .num_dapm_routes = ARRAY_SIZE(da7213_audio_map),
+ .component_driver = {
+ .controls = da7213_snd_controls,
+ .num_controls = ARRAY_SIZE(da7213_snd_controls),
+ .dapm_widgets = da7213_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da7213_dapm_widgets),
+ .dapm_routes = da7213_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da7213_audio_map),
+ },
};
static const struct regmap_config da7213_regmap_config = {
diff --git a/sound/soc/codecs/da7213.h b/sound/soc/codecs/da7213.h
index fbb7a356a501..16ef56f77cd4 100644
--- a/sound/soc/codecs/da7213.h
+++ b/sound/soc/codecs/da7213.h
@@ -172,6 +172,7 @@
#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)
@@ -499,8 +500,6 @@
#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
@@ -515,6 +514,13 @@ enum da7213_clk_src {
DA7213_CLKSRC_MCLK_SQR,
};
+enum da7213_sys_clk {
+ DA7213_SYSCLK_MCLK = 0,
+ DA7213_SYSCLK_PLL,
+ DA7213_SYSCLK_PLL_SRM,
+ DA7213_SYSCLK_PLL_32KHZ
+};
+
/* Codec private data */
struct da7213_priv {
struct regmap *regmap;
@@ -522,8 +528,6 @@ struct da7213_priv {
unsigned int mclk_rate;
int clk_src;
bool master;
- bool mclk_squarer_en;
- bool srm_en;
bool alc_calib_auto;
bool alc_en;
struct da7213_platform_data *pdata;
diff --git a/sound/soc/codecs/da7218.c b/sound/soc/codecs/da7218.c
index 99ce23e113bf..c69e97654fc6 100644
--- a/sound/soc/codecs/da7218.c
+++ b/sound/soc/codecs/da7218.c
@@ -1819,7 +1819,7 @@ static int da7218_set_dai_sysclk(struct snd_soc_dai *codec_dai,
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;
@@ -1866,11 +1866,8 @@ static int da7218_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
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_9_TO_18_MHZ;
- indiv = DA7218_PLL_INDIV_9_TO_18_MHZ_VAL;
- } else if (da7218->mclk_rate < 2000000) {
+ /* Verify 2MHz - 54MHz MCLK provided, and set input divider */
+ if (da7218->mclk_rate < 2000000) {
dev_err(codec->dev, "PLL input clock %d below valid range\n",
da7218->mclk_rate);
return -EINVAL;
@@ -1911,9 +1908,6 @@ 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");
return -EINVAL;
@@ -2589,20 +2583,22 @@ static int da7218_set_bias_level(struct snd_soc_codec *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) {
- /* MCLK */
+ case SND_SOC_BIAS_PREPARE:
+ /* Enable MCLK for transition to ON state */
+ if (snd_soc_codec_get_bias_level(codec) == 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(codec->dev, "Failed to enable mclk\n");
return ret;
}
}
+ }
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
/* Master bias */
snd_soc_update_bits(codec, DA7218_REFERENCES,
DA7218_BIAS_EN_MASK,
@@ -2612,6 +2608,10 @@ static int da7218_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, 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:
@@ -2625,10 +2625,6 @@ static int da7218_set_bias_level(struct snd_soc_codec *codec,
snd_soc_update_bits(codec, DA7218_REFERENCES,
DA7218_BIAS_EN_MASK, 0);
}
-
- /* MCLK */
- if (da7218->mclk)
- clk_disable_unprepare(da7218->mclk);
break;
}
@@ -3045,13 +3041,14 @@ static struct snd_soc_codec_driver soc_codec_dev_da7218 = {
.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),
+ .component_driver = {
+ .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),
+ },
};
diff --git a/sound/soc/codecs/da7218.h b/sound/soc/codecs/da7218.h
index 477cd37723cf..4f7ec21069a4 100644
--- a/sound/soc/codecs/da7218.h
+++ b/sound/soc/codecs/da7218.h
@@ -888,7 +888,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
@@ -1371,7 +1370,6 @@ enum da7218_sys_clk {
DA7218_SYSCLK_MCLK = 0,
DA7218_SYSCLK_PLL,
DA7218_SYSCLK_PLL_SRM,
- DA7218_SYSCLK_PLL_32KHZ
};
enum da7218_dev_id {
diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c
index 9459593eef13..2b8914dd5990 100644
--- a/sound/soc/codecs/da7219-aad.c
+++ b/sound/soc/codecs/da7219-aad.c
@@ -13,8 +13,9 @@
#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>
@@ -114,13 +115,38 @@ static void da7219_aad_hptest_work(struct work_struct *work)
struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
u16 tonegen_freq_hptest;
- u8 accdet_cfg8;
- int report = 0;
+ u8 pll_srm_sts, gain_ramp_ctrl, accdet_cfg8;
+ int report = 0, ret = 0;
/* Lock DAPM and any Kcontrols that are affected by this test */
snd_soc_dapm_mutex_lock(dapm);
mutex_lock(&da7219->lock);
+ /* Ensure MCLK is available for HP test procedure */
+ if (da7219->mclk) {
+ ret = clk_prepare_enable(da7219->mclk);
+ if (ret) {
+ dev_err(codec->dev, "Failed to enable mclk - %d\n", ret);
+ mutex_unlock(&da7219->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.
+ */
+ pll_srm_sts = snd_soc_read(codec, DA7219_PLL_SRM_STS);
+ if (pll_srm_sts & DA7219_PLL_SRM_STS_MCLK)
+ tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ);
+ 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_read(codec, DA7219_GAIN_RAMP_CTRL);
+ snd_soc_write(codec, DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_X8);
+
/* Bypass cache so it saves current settings */
regcache_cache_bypass(da7219->regmap, true);
@@ -183,9 +209,15 @@ static void da7219_aad_hptest_work(struct work_struct *work)
snd_soc_write(codec, DA7219_HP_R_CTRL,
DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
+ /*
+ * 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);
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,
@@ -244,12 +276,26 @@ static void da7219_aad_hptest_work(struct work_struct *work)
snd_soc_update_bits(codec, 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_write(codec, 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,
DA7219_HP_L_AMP_OE_MASK);
snd_soc_update_bits(codec, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK,
DA7219_HP_R_AMP_OE_MASK);
+ /* Remove MCLK, if previously enabled */
+ if (da7219->mclk)
+ clk_disable_unprepare(da7219->mclk);
+
mutex_unlock(&da7219->lock);
snd_soc_dapm_mutex_unlock(dapm);
@@ -382,11 +428,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 snd_soc_codec *codec, u32 val)
{
switch (val) {
case 2800:
@@ -400,7 +446,7 @@ static enum da7219_aad_micbias_pulse_lvl
}
static enum da7219_aad_btn_cfg
- da7219_aad_of_btn_cfg(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_btn_cfg(struct snd_soc_codec *codec, u32 val)
{
switch (val) {
case 2:
@@ -424,7 +470,7 @@ static enum da7219_aad_btn_cfg
}
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 snd_soc_codec *codec, u32 val)
{
switch (val) {
case 200:
@@ -442,7 +488,7 @@ static enum da7219_aad_mic_det_thr
}
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 snd_soc_codec *codec, u32 val)
{
switch (val) {
case 5:
@@ -468,7 +514,7 @@ static enum da7219_aad_jack_ins_deb
}
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 snd_soc_codec *codec, const char *str)
{
if (!strcmp(str, "32ms_64ms")) {
return DA7219_AAD_JACK_DET_RATE_32_64MS;
@@ -485,7 +531,7 @@ static enum da7219_aad_jack_det_rate
}
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 snd_soc_codec *codec, u32 val)
{
switch (val) {
case 1:
@@ -503,7 +549,7 @@ static enum da7219_aad_jack_rem_deb
}
static enum da7219_aad_btn_avg
- da7219_aad_of_btn_avg(struct snd_soc_codec *codec, u32 val)
+ da7219_aad_fw_btn_avg(struct snd_soc_codec *codec, u32 val)
{
switch (val) {
case 1:
@@ -521,7 +567,7 @@ static enum da7219_aad_btn_avg
}
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 snd_soc_codec *codec, u32 val)
{
switch (val) {
case 1:
@@ -538,97 +584,96 @@ static enum da7219_aad_adc_1bit_rpt
}
}
-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 snd_soc_codec *codec)
{
- struct device_node *np = codec->dev->of_node;
- struct device_node *aad_np = of_find_node_by_name(np, "da7219_aad");
+ struct device *dev = codec->dev;
+ 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;
+ 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(codec, 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(codec, 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(codec, fw_val32);
else
aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_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(codec, 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-det-rate", &fw_str))
aad_pdata->jack_det_rate =
- da7219_aad_of_jack_det_rate(codec, of_str);
+ da7219_aad_fw_jack_det_rate(codec, 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(codec, 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(codec, 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(codec, fw_val32);
else
aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
-out:
- of_node_put(aad_np);
-
return aad_pdata;
}
@@ -752,6 +797,62 @@ static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
/*
+ * Suspend/Resume
+ */
+
+void da7219_aad_suspend(struct snd_soc_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ u8 micbias_ctrl;
+
+ if (da7219_aad->jack) {
+ /* Disable jack detection during suspend */
+ snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_ACCDET_EN_MASK, 0);
+
+ /*
+ * If we have a 4-pole jack inserted, then micbias will be
+ * enabled. We can disable micbias here, and keep a note to
+ * re-enable it on resume. If jack removal occurred during
+ * suspend then this will be dealt with through the IRQ handler.
+ */
+ if (da7219_aad->jack_inserted) {
+ micbias_ctrl = snd_soc_read(codec, 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_codec *codec)
+{
+ struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+ struct da7219_aad_priv *da7219_aad = da7219->aad;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ 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_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+ DA7219_ACCDET_EN_MASK,
+ DA7219_ACCDET_EN_MASK);
+ }
+}
+
+
+/*
* Init/Exit
*/
@@ -769,9 +870,9 @@ int da7219_aad_init(struct snd_soc_codec *codec)
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);
+ /* Handle any DT/ACPI/platform data */
+ if (da7219->pdata && !da7219->pdata->aad_pdata)
+ da7219->pdata->aad_pdata = da7219_aad_fw_to_pdata(codec);
da7219_aad_handle_pdata(codec);
diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h
index 4fccf677cd06..117a3d7ccd31 100644
--- a/sound/soc/codecs/da7219-aad.h
+++ b/sound/soc/codecs/da7219-aad.h
@@ -176,8 +176,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,
@@ -199,12 +201,17 @@ struct da7219_aad_priv {
struct work_struct hptest_work;
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);
+/* Suspend/Resume */
+void da7219_aad_suspend(struct snd_soc_codec *codec);
+void da7219_aad_resume(struct snd_soc_codec *codec);
+
/* Init/Exit */
int da7219_aad_init(struct snd_soc_codec *codec);
void da7219_aad_exit(struct snd_soc_codec *codec);
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
index 5c93899f1f0e..1152aa5e7c39 100644
--- a/sound/soc/codecs/da7219.c
+++ b/sound/soc/codecs/da7219.c
@@ -15,6 +15,7 @@
#include <linux/clk.h>
#include <linux/i2c.h>
#include <linux/of_device.h>
+#include <linux/property.h>
#include <linux/regmap.h>
#include <linux/slab.h>
#include <linux/pm.h>
@@ -800,7 +801,7 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w,
++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");
@@ -1418,7 +1419,7 @@ static struct snd_soc_dai_driver da7219_dai = {
/*
- * DT
+ * DT/ACPI
*/
static const struct of_device_id da7219_of_match[] = {
@@ -1434,7 +1435,7 @@ static const struct acpi_device_id da7219_acpi_match[] = {
MODULE_DEVICE_TABLE(acpi, da7219_acpi_match);
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:
@@ -1450,13 +1451,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;
@@ -1465,29 +1466,31 @@ 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 snd_soc_codec *codec)
{
- struct device_node *np = codec->dev->of_node;
+ struct device *dev = codec->dev;
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");
+
+ 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;
@@ -1507,11 +1510,10 @@ static int da7219_set_bias_level(struct snd_soc_codec *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) {
- /* MCLK */
+ case SND_SOC_BIAS_PREPARE:
+ /* Enable MCLK for transition to ON state */
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_STANDBY) {
if (da7219->mclk) {
ret = clk_prepare_enable(da7219->mclk);
if (ret) {
@@ -1520,22 +1522,28 @@ static int da7219_set_bias_level(struct snd_soc_codec *codec,
return ret;
}
}
+ }
+ break;
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
/* Master bias */
snd_soc_update_bits(codec, DA7219_REFERENCES,
DA7219_BIAS_EN_MASK,
DA7219_BIAS_EN_MASK);
+
+ if (snd_soc_codec_get_bias_level(codec) == 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)
+ /* Only disable master bias if we're not a wake-up source */
+ if (!da7219->wakeup_source)
snd_soc_update_bits(codec, DA7219_REFERENCES,
DA7219_BIAS_EN_MASK, 0);
- /* MCLK */
- if (da7219->mclk)
- clk_disable_unprepare(da7219->mclk);
break;
}
@@ -1598,6 +1606,8 @@ static void da7219_handle_pdata(struct snd_soc_codec *codec)
if (pdata) {
u8 micbias_lvl = 0;
+ da7219->wakeup_source = pdata->wakeup_source;
+
/* Mic Bias voltages */
switch (pdata->micbias_lvl) {
case DA7219_MICBIAS_1_6V:
@@ -1662,11 +1672,10 @@ static int da7219_probe(struct snd_soc_codec *codec)
break;
}
- /* Handle DT/Platform data */
- if (codec->dev->of_node)
- da7219->pdata = da7219_of_to_pdata(codec);
- else
- da7219->pdata = dev_get_platdata(codec->dev);
+ /* Handle DT/ACPI/Platform data */
+ da7219->pdata = dev_get_platdata(codec->dev);
+ if (!da7219->pdata)
+ da7219->pdata = da7219_fw_to_pdata(codec);
da7219_handle_pdata(codec);
@@ -1733,11 +1742,11 @@ static int da7219_suspend(struct snd_soc_codec *codec)
{
struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ /* Suspend AAD if we're not a wake-up source */
+ if (!da7219->wakeup_source)
+ da7219_aad_suspend(codec);
- /* Put device into standby mode if jack detection disabled */
- if (!da7219->aad->jack)
- snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, 0);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
return 0;
}
@@ -1746,13 +1755,12 @@ static int da7219_resume(struct snd_soc_codec *codec)
{
struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
- /* 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);
-
snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+ /* Resume AAD if previously suspended */
+ if (!da7219->wakeup_source)
+ da7219_aad_resume(codec);
+
return 0;
}
#else
@@ -1767,13 +1775,14 @@ static struct snd_soc_codec_driver soc_codec_dev_da7219 = {
.resume = da7219_resume,
.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),
+ .component_driver = {
+ .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),
+ },
};
@@ -1921,7 +1930,8 @@ static int da7219_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct da7219_priv *da7219;
- int ret;
+ unsigned int system_active, system_status;
+ int i, ret;
da7219 = devm_kzalloc(&i2c->dev, sizeof(struct da7219_priv),
GFP_KERNEL);
@@ -1937,6 +1947,37 @@ static int da7219_i2c_probe(struct i2c_client *i2c,
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 codec */
+ 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);
+
+ regcache_cache_bypass(da7219->regmap, false);
+
ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da7219,
&da7219_dai, 1);
if (ret < 0) {
diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h
index ff2a2f02ce40..66d3bad86739 100644
--- a/sound/soc/codecs/da7219.h
+++ b/sound/soc/codecs/da7219.h
@@ -224,6 +224,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 +577,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 */
@@ -770,6 +773,10 @@
/* SRM */
#define DA7219_SRM_CHECK_RETRIES 8
+/* System Controller */
+#define DA7219_SYS_STAT_CHECK_RETRIES 6
+#define DA7219_SYS_STAT_CHECK_DELAY 50
+
enum da7219_clk_src {
DA7219_CLKSRC_MCLK = 0,
DA7219_CLKSRC_MCLK_SQR,
@@ -796,6 +803,7 @@ struct da7219_priv {
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;
diff --git a/sound/soc/codecs/da732x.c b/sound/soc/codecs/da732x.c
index 461506a4ca6a..c1cc1c1c28f2 100644
--- a/sound/soc/codecs/da732x.c
+++ b/sound/soc/codecs/da732x.c
@@ -1501,12 +1501,14 @@ static int da732x_set_bias_level(struct snd_soc_codec *codec,
static struct snd_soc_codec_driver soc_codec_dev_da732x = {
.set_bias_level = da732x_set_bias_level,
- .controls = da732x_snd_controls,
- .num_controls = ARRAY_SIZE(da732x_snd_controls),
- .dapm_widgets = da732x_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(da732x_dapm_widgets),
- .dapm_routes = da732x_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(da732x_dapm_routes),
+ .component_driver = {
+ .controls = da732x_snd_controls,
+ .num_controls = ARRAY_SIZE(da732x_snd_controls),
+ .dapm_widgets = da732x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da732x_dapm_widgets),
+ .dapm_routes = da732x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(da732x_dapm_routes),
+ },
.set_pll = da732x_set_dai_pll,
};
diff --git a/sound/soc/codecs/da9055.c b/sound/soc/codecs/da9055.c
index 0b2ede8db978..4efb5f897a0c 100644
--- a/sound/soc/codecs/da9055.c
+++ b/sound/soc/codecs/da9055.c
@@ -1455,13 +1455,14 @@ static struct snd_soc_codec_driver soc_codec_dev_da9055 = {
.probe = da9055_probe,
.set_bias_level = da9055_set_bias_level,
- .controls = da9055_snd_controls,
- .num_controls = ARRAY_SIZE(da9055_snd_controls),
-
- .dapm_widgets = da9055_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(da9055_dapm_widgets),
- .dapm_routes = da9055_audio_map,
- .num_dapm_routes = ARRAY_SIZE(da9055_audio_map),
+ .component_driver = {
+ .controls = da9055_snd_controls,
+ .num_controls = ARRAY_SIZE(da9055_snd_controls),
+ .dapm_widgets = da9055_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(da9055_dapm_widgets),
+ .dapm_routes = da9055_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(da9055_audio_map),
+ },
};
static const struct regmap_config da9055_regmap_config = {
diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c
index fde53251c047..c82b9dc41e9a 100644
--- a/sound/soc/codecs/dmic.c
+++ b/sound/soc/codecs/dmic.c
@@ -51,10 +51,12 @@ static const struct snd_soc_dapm_route intercon[] = {
};
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),
+ .component_driver = {
+ .dapm_widgets = dmic_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ },
};
static int dmic_dev_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index 2086d7107622..37722194b107 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -823,12 +823,14 @@ static struct snd_soc_codec_driver es8328_codec_driver = {
.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),
+ .component_driver = {
+ .controls = es8328_snd_controls,
+ .num_controls = ARRAY_SIZE(es8328_snd_controls),
+ .dapm_widgets = es8328_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es8328_dapm_widgets),
+ .dapm_routes = es8328_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es8328_dapm_routes),
+ },
};
int es8328_probe(struct device *dev, struct regmap *regmap)
diff --git a/sound/soc/codecs/gtm601.c b/sound/soc/codecs/gtm601.c
index 0b80052996d3..926b1a4e37d4 100644
--- a/sound/soc/codecs/gtm601.c
+++ b/sound/soc/codecs/gtm601.c
@@ -52,10 +52,12 @@ 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),
+ .component_driver = {
+ .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 int gtm601_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index 181cd3bf0b92..c602c4960924 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -614,7 +614,7 @@ static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
(!pin->eld.eld_valid)) {
dev_warn(&hdac->hdac.dev,
- "Failed: montior present? %d ELD valid?: %d for pin: %d\n",
+ "Failed: monitor present? %d ELD valid?: %d for pin: %d\n",
pin->eld.monitor_present, pin->eld.eld_valid, pin->nid);
return 0;
@@ -1124,8 +1124,10 @@ static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
}
hdac_hdmi_parse_eld(edev, pin);
- print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
- pin->eld.eld_buffer, pin->eld.eld_size);
+ print_hex_dump_debug("ELD: ",
+ DUMP_PREFIX_OFFSET, 16, 1,
+ pin->eld.eld_buffer, pin->eld.eld_size,
+ true);
} else {
pin->eld.monitor_present = false;
pin->eld.eld_valid = false;
@@ -1474,6 +1476,11 @@ static int hdmi_codec_probe(struct snd_soc_codec *codec)
* exit, we call pm_runtime_suspend() so that will do for us
*/
hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev));
+ if (!hlink) {
+ dev_err(&edev->hdac.dev, "hdac link not found\n");
+ return -EIO;
+ }
+
snd_hdac_ext_bus_link_get(edev->ebus, hlink);
ret = create_fill_widget_route_map(dapm);
@@ -1634,6 +1641,11 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
/* hold the ref while we probe */
hlink = snd_hdac_ext_bus_get_link(edev->ebus, dev_name(&edev->hdac.dev));
+ if (!hlink) {
+ dev_err(&edev->hdac.dev, "hdac link not found\n");
+ return -EIO;
+ }
+
snd_hdac_ext_bus_link_get(edev->ebus, hlink);
hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
@@ -1744,6 +1756,11 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
}
hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
+ if (!hlink) {
+ dev_err(dev, "hdac link not found\n");
+ return -EIO;
+ }
+
snd_hdac_ext_bus_link_put(ebus, hlink);
return 0;
@@ -1765,6 +1782,11 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
return 0;
hlink = snd_hdac_ext_bus_get_link(ebus, dev_name(dev));
+ if (!hlink) {
+ dev_err(dev, "hdac link not found\n");
+ return -EIO;
+ }
+
snd_hdac_ext_bus_link_get(ebus, hlink);
err = snd_hdac_display_power(bus, true);
@@ -1796,6 +1818,7 @@ static const struct dev_pm_ops hdac_hdmi_pm = {
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),
{}
};
diff --git a/sound/soc/codecs/hdmi-codec.c b/sound/soc/codecs/hdmi-codec.c
index 8e36e883e453..b904492d7744 100644
--- a/sound/soc/codecs/hdmi-codec.c
+++ b/sound/soc/codecs/hdmi-codec.c
@@ -24,6 +24,15 @@
#include <drm/drm_crtc.h> /* This is only to get MAX_ELD_BYTES */
+struct hdmi_device {
+ struct device *dev;
+ struct list_head list;
+ int cnt;
+};
+#define pos_to_hdmi_device(pos) container_of((pos), struct hdmi_device, list)
+LIST_HEAD(hdmi_device_list);
+
+#define DAI_NAME_SIZE 16
struct hdmi_codec_priv {
struct hdmi_codec_pdata hcd;
struct snd_soc_dai_driver *daidrv;
@@ -112,7 +121,7 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
return ret;
if (hcp->hcd.ops->audio_startup) {
- ret = hcp->hcd.ops->audio_startup(dai->dev->parent);
+ ret = hcp->hcd.ops->audio_startup(dai->dev->parent, hcp->hcd.data);
if (ret) {
mutex_lock(&hcp->current_stream_lock);
hcp->current_stream = NULL;
@@ -122,8 +131,8 @@ static int hdmi_codec_startup(struct snd_pcm_substream *substream,
}
if (hcp->hcd.ops->get_eld) {
- ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->eld,
- sizeof(hcp->eld));
+ ret = hcp->hcd.ops->get_eld(dai->dev->parent, hcp->hcd.data,
+ hcp->eld, sizeof(hcp->eld));
if (!ret) {
ret = snd_pcm_hw_constraint_eld(substream->runtime,
@@ -144,7 +153,7 @@ static void hdmi_codec_shutdown(struct snd_pcm_substream *substream,
WARN_ON(hcp->current_stream != substream);
- hcp->hcd.ops->audio_shutdown(dai->dev->parent);
+ hcp->hcd.ops->audio_shutdown(dai->dev->parent, hcp->hcd.data);
mutex_lock(&hcp->current_stream_lock);
hcp->current_stream = NULL;
@@ -195,8 +204,8 @@ static int hdmi_codec_hw_params(struct snd_pcm_substream *substream,
hp.sample_rate = params_rate(params);
hp.channels = params_channels(params);
- return hcp->hcd.ops->hw_params(dai->dev->parent, &hcp->daifmt[dai->id],
- &hp);
+ return hcp->hcd.ops->hw_params(dai->dev->parent, hcp->hcd.data,
+ &hcp->daifmt[dai->id], &hp);
}
static int hdmi_codec_set_fmt(struct snd_soc_dai *dai,
@@ -280,7 +289,8 @@ static int hdmi_codec_digital_mute(struct snd_soc_dai *dai, int mute)
dev_dbg(dai->dev, "%s()\n", __func__);
if (hcp->hcd.ops->digital_mute)
- return hcp->hcd.ops->digital_mute(dai->dev->parent, mute);
+ return hcp->hcd.ops->digital_mute(dai->dev->parent,
+ hcp->hcd.data, mute);
return 0;
}
@@ -319,7 +329,6 @@ static const struct snd_soc_dai_ops hdmi_dai_ops = {
SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S32_BE)
static struct snd_soc_dai_driver hdmi_i2s_dai = {
- .name = "i2s-hifi",
.id = DAI_ID_I2S,
.playback = {
.stream_name = "Playback",
@@ -333,7 +342,6 @@ static struct snd_soc_dai_driver hdmi_i2s_dai = {
};
static const struct snd_soc_dai_driver hdmi_spdif_dai = {
- .name = "spdif-hifi",
.id = DAI_ID_SPDIF,
.playback = {
.stream_name = "Playback",
@@ -345,13 +353,37 @@ static const struct snd_soc_dai_driver hdmi_spdif_dai = {
.ops = &hdmi_dai_ops,
};
+static char hdmi_dai_name[][DAI_NAME_SIZE] = {
+ "hdmi-hifi.0",
+ "hdmi-hifi.1",
+ "hdmi-hifi.2",
+ "hdmi-hifi.3",
+};
+
+static int hdmi_of_xlate_dai_name(struct snd_soc_component *component,
+ struct of_phandle_args *args,
+ const char **dai_name)
+{
+ int id = args->args[0];
+
+ if (id < ARRAY_SIZE(hdmi_dai_name)) {
+ *dai_name = hdmi_dai_name[id];
+ return 0;
+ }
+
+ return -EAGAIN;
+}
+
static struct snd_soc_codec_driver hdmi_codec = {
- .controls = hdmi_controls,
- .num_controls = ARRAY_SIZE(hdmi_controls),
- .dapm_widgets = hdmi_widgets,
- .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
- .dapm_routes = hdmi_routes,
- .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
+ .component_driver = {
+ .controls = hdmi_controls,
+ .num_controls = ARRAY_SIZE(hdmi_controls),
+ .dapm_widgets = hdmi_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
+ .dapm_routes = hdmi_routes,
+ .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
+ .of_xlate_dai_name = hdmi_of_xlate_dai_name,
+ },
};
static int hdmi_codec_probe(struct platform_device *pdev)
@@ -359,6 +391,8 @@ static int hdmi_codec_probe(struct platform_device *pdev)
struct hdmi_codec_pdata *hcd = pdev->dev.platform_data;
struct device *dev = &pdev->dev;
struct hdmi_codec_priv *hcp;
+ struct hdmi_device *hd;
+ struct list_head *pos;
int dai_count, i = 0;
int ret;
@@ -380,6 +414,31 @@ static int hdmi_codec_probe(struct platform_device *pdev)
if (!hcp)
return -ENOMEM;
+ hd = NULL;
+ list_for_each(pos, &hdmi_device_list) {
+ struct hdmi_device *tmp = pos_to_hdmi_device(pos);
+
+ if (tmp->dev == dev->parent) {
+ hd = tmp;
+ break;
+ }
+ }
+
+ if (!hd) {
+ hd = devm_kzalloc(dev, sizeof(*hd), GFP_KERNEL);
+ if (!hd)
+ return -ENOMEM;
+
+ hd->dev = dev->parent;
+
+ list_add_tail(&hd->list, &hdmi_device_list);
+ }
+
+ if (hd->cnt >= ARRAY_SIZE(hdmi_dai_name)) {
+ dev_err(dev, "too many hdmi codec are deteced\n");
+ return -EINVAL;
+ }
+
hcp->hcd = *hcd;
mutex_init(&hcp->current_stream_lock);
@@ -392,11 +451,14 @@ static int hdmi_codec_probe(struct platform_device *pdev)
hcp->daidrv[i] = hdmi_i2s_dai;
hcp->daidrv[i].playback.channels_max =
hcd->max_i2s_channels;
+ hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++];
i++;
}
- if (hcd->spdif)
+ if (hcd->spdif) {
hcp->daidrv[i] = hdmi_spdif_dai;
+ hcp->daidrv[i].name = hdmi_dai_name[hd->cnt++];
+ }
ret = snd_soc_register_codec(dev, &hdmi_codec, hcp->daidrv,
dai_count);
diff --git a/sound/soc/codecs/inno_rk3036.c b/sound/soc/codecs/inno_rk3036.c
index 9b6e8840a1b5..b918ba5c8ce5 100644
--- a/sound/soc/codecs/inno_rk3036.c
+++ b/sound/soc/codecs/inno_rk3036.c
@@ -380,12 +380,14 @@ static struct snd_soc_codec_driver rk3036_codec_driver = {
.probe = rk3036_codec_probe,
.remove = rk3036_codec_remove,
.set_bias_level = rk3036_codec_set_bias_level,
- .controls = rk3036_codec_dapm_controls,
- .num_controls = ARRAY_SIZE(rk3036_codec_dapm_controls),
- .dapm_routes = rk3036_codec_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes),
- .dapm_widgets = rk3036_codec_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets),
+ .component_driver = {
+ .controls = rk3036_codec_dapm_controls,
+ .num_controls = ARRAY_SIZE(rk3036_codec_dapm_controls),
+ .dapm_routes = rk3036_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rk3036_codec_dapm_routes),
+ .dapm_widgets = rk3036_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rk3036_codec_dapm_widgets),
+ },
};
static const struct regmap_config rk3036_codec_regmap_config = {
diff --git a/sound/soc/codecs/isabelle.c b/sound/soc/codecs/isabelle.c
index be448373d39a..a4b0eded984a 100644
--- a/sound/soc/codecs/isabelle.c
+++ b/sound/soc/codecs/isabelle.c
@@ -1089,12 +1089,14 @@ 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),
+ .component_driver = {
+ .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,
};
diff --git a/sound/soc/codecs/jz4740.c b/sound/soc/codecs/jz4740.c
index 1f5ab99956ed..0290fab383da 100644
--- a/sound/soc/codecs/jz4740.c
+++ b/sound/soc/codecs/jz4740.c
@@ -298,12 +298,14 @@ static struct snd_soc_codec_driver soc_codec_dev_jz4740_codec = {
.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),
+ .component_driver = {
+ .controls = jz4740_codec_controls,
+ .num_controls = ARRAY_SIZE(jz4740_codec_controls),
+ .dapm_widgets = jz4740_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(jz4740_codec_dapm_widgets),
+ .dapm_routes = jz4740_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(jz4740_codec_dapm_routes),
+ },
};
static const struct regmap_config jz4740_codec_regmap_config = {
diff --git a/sound/soc/codecs/l3.c b/sound/soc/codecs/l3.c
index 5353af58862c..a10ea3c716c6 100644
--- a/sound/soc/codecs/l3.c
+++ b/sound/soc/codecs/l3.c
@@ -20,6 +20,8 @@
#include <linux/module.h>
#include <linux/kernel.h>
#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/gpio.h>
#include <sound/l3.h>
@@ -32,11 +34,11 @@ static void sendbyte(struct l3_pins *adap, unsigned int byte)
int i;
for (i = 0; i < 8; i++) {
- adap->setclk(0);
+ adap->setclk(adap, 0);
udelay(adap->data_hold);
- adap->setdat(byte & 1);
+ adap->setdat(adap, byte & 1);
udelay(adap->data_setup);
- adap->setclk(1);
+ adap->setclk(adap, 1);
udelay(adap->clock_high);
byte >>= 1;
}
@@ -55,10 +57,10 @@ static void sendbytes(struct l3_pins *adap, const u8 *buf,
for (i = 0; i < len; i++) {
if (i) {
udelay(adap->mode_hold);
- adap->setmode(0);
+ adap->setmode(adap, 0);
udelay(adap->mode);
}
- adap->setmode(1);
+ adap->setmode(adap, 1);
udelay(adap->mode_setup);
sendbyte(adap, buf[i]);
}
@@ -66,26 +68,71 @@ static void sendbytes(struct l3_pins *adap, const u8 *buf,
int l3_write(struct l3_pins *adap, u8 addr, u8 *data, int len)
{
- adap->setclk(1);
- adap->setdat(1);
- adap->setmode(1);
+ adap->setclk(adap, 1);
+ adap->setdat(adap, 1);
+ adap->setmode(adap, 1);
udelay(adap->mode);
- adap->setmode(0);
+ adap->setmode(adap, 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);
+ adap->setclk(adap, 1);
+ adap->setdat(adap, 1);
+ adap->setmode(adap, 0);
return len;
}
EXPORT_SYMBOL_GPL(l3_write);
+
+static void l3_set_clk(struct l3_pins *adap, int val)
+{
+ gpio_set_value(adap->gpio_clk, val);
+}
+
+static void l3_set_data(struct l3_pins *adap, int val)
+{
+ gpio_set_value(adap->gpio_data, val);
+}
+
+static void l3_set_mode(struct l3_pins *adap, int val)
+{
+ gpio_set_value(adap->gpio_mode, val);
+}
+
+int l3_set_gpio_ops(struct device *dev, struct l3_pins *adap)
+{
+ int ret;
+
+ if (!adap->use_gpios)
+ return -EINVAL;
+
+ ret = devm_gpio_request_one(dev, adap->gpio_data,
+ GPIOF_OUT_INIT_LOW, "l3_data");
+ if (ret < 0)
+ return ret;
+ adap->setdat = l3_set_data;
+
+ ret = devm_gpio_request_one(dev, adap->gpio_clk,
+ GPIOF_OUT_INIT_LOW, "l3_clk");
+ if (ret < 0)
+ return ret;
+ adap->setclk = l3_set_clk;
+
+ ret = devm_gpio_request_one(dev, adap->gpio_mode,
+ GPIOF_OUT_INIT_LOW, "l3_mode");
+ if (ret < 0)
+ return ret;
+ adap->setmode = l3_set_mode;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(l3_set_gpio_ops);
+
MODULE_DESCRIPTION("L3 bit-banging driver");
MODULE_AUTHOR("Christian Pellegrin <chripell@evolware.org>");
MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/lm49453.c b/sound/soc/codecs/lm49453.c
index 9af5640e3446..8d413c2677cc 100644
--- a/sound/soc/codecs/lm49453.c
+++ b/sound/soc/codecs/lm49453.c
@@ -1391,12 +1391,14 @@ 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),
+ .component_driver = {
+ .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,
};
diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c
index fc22804cabc5..72f77455582e 100644
--- a/sound/soc/codecs/max98088.c
+++ b/sound/soc/codecs/max98088.c
@@ -1704,12 +1704,14 @@ static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
.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),
+ .component_driver = {
+ .controls = max98088_snd_controls,
+ .num_controls = ARRAY_SIZE(max98088_snd_controls),
+ .dapm_widgets = max98088_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98088_dapm_widgets),
+ .dapm_routes = max98088_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98088_audio_map),
+ },
};
static int max98088_i2c_probe(struct i2c_client *i2c,
diff --git a/sound/soc/codecs/max98095.c b/sound/soc/codecs/max98095.c
index 3577003f39cf..6f8a757876ed 100644
--- a/sound/soc/codecs/max98095.c
+++ b/sound/soc/codecs/max98095.c
@@ -2108,12 +2108,14 @@ static struct snd_soc_codec_driver soc_codec_dev_max98095 = {
.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),
+ .component_driver = {
+ .controls = max98095_snd_controls,
+ .num_controls = ARRAY_SIZE(max98095_snd_controls),
+ .dapm_widgets = max98095_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98095_dapm_widgets),
+ .dapm_routes = max98095_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98095_audio_map),
+ },
};
static int max98095_i2c_probe(struct i2c_client *i2c,
diff --git a/sound/soc/codecs/max98357a.c b/sound/soc/codecs/max98357a.c
index 5b1dfb1518fb..6a6b68a4cb52 100644
--- a/sound/soc/codecs/max98357a.c
+++ b/sound/soc/codecs/max98357a.c
@@ -74,10 +74,12 @@ static int max98357a_codec_probe(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver max98357a_codec_driver = {
.probe = max98357a_codec_probe,
- .dapm_widgets = max98357a_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(max98357a_dapm_widgets),
- .dapm_routes = max98357a_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(max98357a_dapm_routes),
+ .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),
+ },
};
static const struct snd_soc_dai_ops max98357a_dai_ops = {
diff --git a/sound/soc/codecs/max98371.c b/sound/soc/codecs/max98371.c
index cf0a39bb631a..781be9ba8dba 100644
--- a/sound/soc/codecs/max98371.c
+++ b/sound/soc/codecs/max98371.c
@@ -412,6 +412,7 @@ static int max98371_i2c_remove(struct i2c_client *client)
static const struct i2c_device_id max98371_i2c_id[] = {
{ "max98371", 0 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, max98371_i2c_id);
@@ -425,7 +426,6 @@ MODULE_DEVICE_TABLE(of, max98371_of_match);
static struct i2c_driver max98371_i2c_driver = {
.driver = {
.name = "max98371",
- .owner = THIS_MODULE,
.pm = NULL,
.of_match_table = of_match_ptr(max98371_of_match),
},
diff --git a/sound/soc/codecs/max9850.c b/sound/soc/codecs/max9850.c
index c14a79d026a1..0610840733d1 100644
--- a/sound/soc/codecs/max9850.c
+++ b/sound/soc/codecs/max9850.c
@@ -306,12 +306,14 @@ static struct snd_soc_codec_driver soc_codec_dev_max9850 = {
.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),
+ .component_driver = {
+ .controls = max9850_controls,
+ .num_controls = ARRAY_SIZE(max9850_controls),
+ .dapm_widgets = max9850_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max9850_dapm_widgets),
+ .dapm_routes = max9850_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max9850_dapm_routes),
+ },
};
static int max9850_i2c_probe(struct i2c_client *i2c,
diff --git a/sound/soc/codecs/max98504.c b/sound/soc/codecs/max98504.c
new file mode 100644
index 000000000000..a7320e709890
--- /dev/null
+++ b/sound/soc/codecs/max98504.c
@@ -0,0 +1,383 @@
+/*
+ * MAX98504 ALSA SoC Audio driver
+ *
+ * Copyright 2013 - 2014 Maxim Integrated Products
+ * Copyright 2016 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#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 struct reg_default max98504_reg_defaults[] = {
+ { 0x01, 0},
+ { 0x02, 0},
+ { 0x03, 0},
+ { 0x04, 0},
+ { 0x10, 0},
+ { 0x11, 0},
+ { 0x12, 0},
+ { 0x13, 0},
+ { 0x14, 0},
+ { 0x15, 0},
+ { 0x16, 0},
+ { 0x17, 0},
+ { 0x18, 0},
+ { 0x19, 0},
+ { 0x1A, 0},
+ { 0x20, 0},
+ { 0x21, 0},
+ { 0x22, 0},
+ { 0x23, 0},
+ { 0x24, 0},
+ { 0x25, 0},
+ { 0x26, 0},
+ { 0x27, 0},
+ { 0x28, 0},
+ { 0x30, 0},
+ { 0x31, 0},
+ { 0x32, 0},
+ { 0x33, 0},
+ { 0x34, 0},
+ { 0x35, 0},
+ { 0x36, 0},
+ { 0x37, 0},
+ { 0x38, 0},
+ { 0x39, 0},
+ { 0x40, 0},
+ { 0x41, 0},
+};
+
+static bool max98504_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98504_INTERRUPT_STATUS:
+ case MAX98504_INTERRUPT_FLAGS:
+ case MAX98504_INTERRUPT_FLAG_CLEARS:
+ case MAX98504_WATCHDOG_CLEAR:
+ case MAX98504_GLOBAL_ENABLE:
+ case MAX98504_SOFTWARE_RESET:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool max98504_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98504_SOFTWARE_RESET:
+ case MAX98504_WATCHDOG_CLEAR:
+ case MAX98504_INTERRUPT_FLAG_CLEARS:
+ return false;
+ default:
+ return true;
+ }
+}
+
+static int max98504_pcm_rx_ev(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE,
+ max98504->pcm_rx_channels);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_write(max98504->regmap, MAX98504_PCM_RX_ENABLE, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int max98504_component_probe(struct snd_soc_component *c)
+{
+ struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+ struct regmap *map = max98504->regmap;
+ int ret;
+
+ ret = regulator_bulk_enable(MAX98504_NUM_SUPPLIES, max98504->supplies);
+ if (ret < 0)
+ return ret;
+
+ regmap_write(map, MAX98504_SOFTWARE_RESET, 0x1);
+ msleep(20);
+
+ if (!max98504->brownout_enable)
+ return 0;
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_ENABLE, 0x1);
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_1,
+ (max98504->brownout_threshold & 0x1f) << 3 |
+ (max98504->brownout_attenuation & 0x3));
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_2,
+ max98504->brownout_attack_hold & 0xff);
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_3,
+ max98504->brownout_timed_hold & 0xff);
+
+ regmap_write(map, MAX98504_PVDD_BROWNOUT_CONFIG_4,
+ max98504->brownout_release_rate & 0xff);
+
+ return 0;
+}
+
+static void max98504_component_remove(struct snd_soc_component *c)
+{
+ struct max98504_priv *max98504 = snd_soc_component_get_drvdata(c);
+
+ regulator_bulk_disable(MAX98504_NUM_SUPPLIES, max98504->supplies);
+}
+
+static const char *spk_source_mux_text[] = {
+ "PCM Monomix", "Analog In", "PDM Left", "PDM Right"
+};
+
+static const struct soc_enum spk_source_mux_enum =
+ SOC_ENUM_SINGLE(MAX98504_SPEAKER_SOURCE_SELECT,
+ 0, ARRAY_SIZE(spk_source_mux_text),
+ spk_source_mux_text);
+
+static const struct snd_kcontrol_new spk_source_mux =
+ SOC_DAPM_ENUM("SPK Source", spk_source_mux_enum);
+
+static const struct snd_soc_dapm_route max98504_dapm_routes[] = {
+ { "SPKOUT", NULL, "Global Enable" },
+ { "SPK Source", "PCM Monomix", "DAC PCM" },
+ { "SPK Source", "Analog In", "AIN" },
+ { "SPK Source", "PDM Left", "DAC PDM" },
+ { "SPK Source", "PDM Right", "DAC PDM" },
+};
+
+static const struct snd_soc_dapm_widget max98504_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("Global Enable", MAX98504_GLOBAL_ENABLE,
+ 0, 0, NULL, 0),
+ SND_SOC_DAPM_INPUT("AIN"),
+ SND_SOC_DAPM_AIF_OUT("AIF2OUTL", "AIF2 Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_OUT("AIF2OUTR", "AIF2 Capture", 1, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("DAC PCM", NULL, SND_SOC_NOPM, 0, 0,
+ max98504_pcm_rx_ev,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_DAC("DAC PDM", NULL, MAX98504_PDM_RX_ENABLE, 0, 0),
+ SND_SOC_DAPM_MUX("SPK Source", SND_SOC_NOPM, 0, 0, &spk_source_mux),
+ SND_SOC_DAPM_REG(snd_soc_dapm_spk, "SPKOUT",
+ MAX98504_SPEAKER_ENABLE, 0, 1, 1, 0),
+};
+
+static int max98504_set_tdm_slot(struct snd_soc_dai *dai,
+ unsigned int tx_mask, unsigned int rx_mask,
+ int slots, int slot_width)
+{
+ struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
+ struct regmap *map = max98504->regmap;
+
+
+ switch (dai->id) {
+ case MAX98504_DAI_ID_PCM:
+ regmap_write(map, MAX98504_PCM_TX_ENABLE, tx_mask);
+ max98504->pcm_rx_channels = rx_mask;
+ break;
+
+ case MAX98504_DAI_ID_PDM:
+ regmap_write(map, MAX98504_PDM_TX_ENABLE, tx_mask);
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ return 0;
+}
+static int max98504_set_channel_map(struct snd_soc_dai *dai,
+ unsigned int tx_num, unsigned int *tx_slot,
+ unsigned int rx_num, unsigned int *rx_slot)
+{
+ struct max98504_priv *max98504 = snd_soc_dai_get_drvdata(dai);
+ struct regmap *map = max98504->regmap;
+ unsigned int i, sources = 0;
+
+ for (i = 0; i < tx_num; i++)
+ if (tx_slot[i])
+ sources |= (1 << i);
+
+ switch (dai->id) {
+ case MAX98504_DAI_ID_PCM:
+ regmap_write(map, MAX98504_PCM_TX_CHANNEL_SOURCES,
+ sources);
+ break;
+
+ case MAX98504_DAI_ID_PDM:
+ regmap_write(map, MAX98504_PDM_TX_CONTROL, sources);
+ break;
+ default:
+ WARN_ON(1);
+ }
+
+ regmap_write(map, MAX98504_MEASUREMENT_ENABLE, sources ? 0x3 : 0x01);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98504_dai_ops = {
+ .set_tdm_slot = max98504_set_tdm_slot,
+ .set_channel_map = max98504_set_channel_map,
+};
+
+#define MAX98504_FORMATS (SNDRV_PCM_FMTBIT_S8|SNDRV_PCM_FMTBIT_S16_LE|\
+ SNDRV_PCM_FMTBIT_S24_LE|SNDRV_PCM_FMTBIT_S32_LE)
+#define MAX98504_PDM_RATES (SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|\
+ SNDRV_PCM_RATE_32000|SNDRV_PCM_RATE_44100|\
+ SNDRV_PCM_RATE_48000|SNDRV_PCM_RATE_88200|\
+ SNDRV_PCM_RATE_96000)
+
+static struct snd_soc_dai_driver max98504_dai[] = {
+ /* TODO: Add the PCM interface definitions */
+ {
+ .name = "max98504-aif2",
+ .id = MAX98504_DAI_ID_PDM,
+ .playback = {
+ .stream_name = "AIF2 Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98504_PDM_RATES,
+ .formats = MAX98504_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AIF2 Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98504_PDM_RATES,
+ .formats = MAX98504_FORMATS,
+ },
+ .ops = &max98504_dai_ops,
+ },
+};
+
+static const struct snd_soc_component_driver max98504_component_driver = {
+ .probe = max98504_component_probe,
+ .remove = max98504_component_remove,
+ .dapm_widgets = max98504_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98504_dapm_widgets),
+ .dapm_routes = max98504_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(max98504_dapm_routes),
+};
+
+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,
+ const struct i2c_device_id *id)
+{
+ 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..afbefad2d5ce
--- /dev/null
+++ b/sound/soc/codecs/max98504.h
@@ -0,0 +1,59 @@
+/*
+ * MAX98504 ALSA SoC Audio driver
+ *
+ * Copyright 2011 - 2012 Maxim Integrated Products
+ * Copyright 2016 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 version 2 as
+ * published by the Free Software Foundation.
+ */
+#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/max9860.c b/sound/soc/codecs/max9860.c
new file mode 100644
index 000000000000..499bdbfd0a2d
--- /dev/null
+++ b/sound/soc/codecs/max9860.c
@@ -0,0 +1,755 @@
+/*
+ * 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
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/kernel.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/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_codec *codec = dai->codec;
+ struct max9860_priv *max9860 = snd_soc_codec_get_drvdata(codec);
+ u8 master;
+ u8 ifc1a = 0;
+ u8 ifc1b = 0;
+ u8 sysclk = 0;
+ unsigned long n;
+ int ret;
+
+ dev_dbg(codec->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_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ master = 0;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ 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(codec->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(codec->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;
+ /* fall through */
+ case SND_SOC_DAIFMT_IB_NF:
+ ifc1a ^= MAX9860_DBCI;
+ ifc1b ^= MAX9860_ABCI;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ dev_dbg(codec->dev, "IFC1A %02x\n", ifc1a);
+ ret = regmap_write(max9860->regmap, MAX9860_IFC1A, ifc1a);
+ if (ret) {
+ dev_err(codec->dev, "Failed to set IFC1A: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(codec->dev, "IFC1B %02x\n", ifc1b);
+ ret = regmap_write(max9860->regmap, MAX9860_IFC1B, ifc1b);
+ if (ret) {
+ dev_err(codec->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(codec->dev, "SYSCLK %02x\n", sysclk);
+ ret = regmap_write(max9860->regmap,
+ MAX9860_SYSCLK, sysclk);
+ if (ret) {
+ dev_err(codec->dev, "Failed to set SYSCLK: %d\n", ret);
+ return ret;
+ }
+ dev_dbg(codec->dev, "N %lu\n", n);
+ ret = regmap_write(max9860->regmap,
+ MAX9860_AUDIOCLKHIGH, n >> 8);
+ if (ret) {
+ dev_err(codec->dev, "Failed to set NHI: %d\n", ret);
+ return ret;
+ }
+ ret = regmap_write(max9860->regmap,
+ MAX9860_AUDIOCLKLOW, n & 0xff);
+ if (ret) {
+ dev_err(codec->dev, "Failed to set NLO: %d\n", ret);
+ return ret;
+ }
+
+ if (!master) {
+ dev_dbg(codec->dev, "Enable PLL\n");
+ ret = regmap_update_bits(max9860->regmap, MAX9860_AUDIOCLKHIGH,
+ MAX9860_PLL, MAX9860_PLL);
+ if (ret) {
+ dev_err(codec->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_codec *codec = dai->codec;
+ struct max9860_priv *max9860 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBS_CFS:
+ 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_rates = 1,
+};
+
+static int max9860_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct max9860_priv *max9860 = dev_get_drvdata(codec->dev);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ ret = regmap_update_bits(max9860->regmap, MAX9860_PWRMAN,
+ MAX9860_SHDN, MAX9860_SHDN);
+ if (ret) {
+ dev_err(codec->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(codec->dev, "Failed to request SHDN: %d\n",
+ ret);
+ return ret;
+ }
+ break;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver max9860_codec_driver = {
+ .set_bias_level = max9860_set_bias_level,
+ .idle_bias_off = true,
+
+ .component_driver = {
+ .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),
+ },
+};
+
+#ifdef CONFIG_PM
+static int max9860_suspend(struct device *dev)
+{
+ struct max9860_priv *max9860 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK,
+ MAX9860_PSCLK, MAX9860_PSCLK_OFF);
+ if (ret) {
+ dev_err(dev, "Failed to disable clock: %d\n", ret);
+ return ret;
+ }
+
+ regulator_disable(max9860->dvddio);
+
+ return 0;
+}
+
+static int max9860_resume(struct device *dev)
+{
+ struct max9860_priv *max9860 = dev_get_drvdata(dev);
+ int ret;
+
+ ret = regulator_enable(max9860->dvddio);
+ if (ret) {
+ dev_err(dev, "Failed to enable DVDDIO: %d\n", ret);
+ return ret;
+ }
+
+ regcache_cache_only(max9860->regmap, false);
+ ret = regcache_sync(max9860->regmap);
+ if (ret) {
+ dev_err(dev, "Failed to sync cache: %d\n", ret);
+ return ret;
+ }
+
+ ret = regmap_update_bits(max9860->regmap, MAX9860_SYSCLK,
+ MAX9860_PSCLK, max9860->psclk);
+ if (ret) {
+ dev_err(dev, "Failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+#endif
+
+static const struct dev_pm_ops max9860_pm_ops = {
+ SET_RUNTIME_PM_OPS(max9860_suspend, max9860_resume, NULL)
+};
+
+static int max9860_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ 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)) {
+ ret = PTR_ERR(max9860->dvddio);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get DVDDIO supply: %d\n", ret);
+ return ret;
+ }
+
+ max9860->dvddio_nb.notifier_call = max9860_dvddio_event;
+
+ ret = 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);
+ if (ret != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get MCLK: %d\n", ret);
+ 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 = snd_soc_register_codec(dev, &max9860_codec_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 int max9860_remove(struct i2c_client *i2c)
+{
+ struct device *dev = &i2c->dev;
+ struct max9860_priv *max9860 = dev_get_drvdata(dev);
+
+ snd_soc_unregister_codec(dev);
+ pm_runtime_disable(dev);
+ regulator_disable(max9860->dvddio);
+ return 0;
+}
+
+static const struct i2c_device_id max9860_i2c_id[] = {
+ { "max9860", },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, max9860_i2c_id);
+
+static const struct of_device_id max9860_of_match[] = {
+ { .compatible = "maxim,max9860", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max9860_of_match);
+
+static struct i2c_driver max9860_i2c_driver = {
+ .probe = max9860_probe,
+ .remove = max9860_remove,
+ .id_table = max9860_i2c_id,
+ .driver = {
+ .name = "max9860",
+ .of_match_table = max9860_of_match,
+ .pm = &max9860_pm_ops,
+ },
+};
+
+module_i2c_driver(max9860_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC MAX9860 Mono Audio Voice Codec driver");
+MODULE_AUTHOR("Peter Rosin <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..22041bd67a7d
--- /dev/null
+++ b/sound/soc/codecs/max9860.h
@@ -0,0 +1,162 @@
+/*
+ * Driver for the MAX9860 Mono Audio Voice Codec
+ *
+ * Author: Peter Rosin <peda@axentia.s>
+ * Copyright 2016 Axentia Technologies
+ *
+ * This program is free software; you can 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_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
index 2a22fddeb6af..42e2e407e287 100755..100644
--- a/sound/soc/codecs/max9867.c
+++ b/sound/soc/codecs/max9867.c
@@ -38,11 +38,10 @@ static DECLARE_TLV_DB_SCALE(max9860_capture_tlv, -600, 200, 0);
static DECLARE_TLV_DB_SCALE(max9860_mic_tlv, 2000, 100, 1);
static DECLARE_TLV_DB_SCALE(max9860_adc_left_tlv, -1200, 100, 1);
static DECLARE_TLV_DB_SCALE(max9860_adc_right_tlv, -1200, 100, 1);
-static const unsigned int max98088_micboost_tlv[] = {
- TLV_DB_RANGE_HEAD(2),
+static const SNDRV_CTL_TLVD_DECLARE_DB_RANGE(max98088_micboost_tlv,
0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
-};
+);
static const struct snd_kcontrol_new max9867_snd_controls[] = {
SOC_DOUBLE_R("Master Playback Volume", MAX9867_LEFTVOL,
@@ -417,12 +416,14 @@ static int max9867_probe(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver max9867_codec = {
.probe = max9867_probe,
- .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),
+ .component_driver = {
+ .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),
+ },
};
static bool max9867_volatile_register(struct device *dev, unsigned int reg)
diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h
index 65590b4ad62a..65590b4ad62a 100755..100644
--- a/sound/soc/codecs/max9867.h
+++ b/sound/soc/codecs/max9867.h
diff --git a/sound/soc/codecs/max9877.h b/sound/soc/codecs/max9877.h
index 6da72290ac58..368343f29dd0 100644
--- a/sound/soc/codecs/max9877.h
+++ b/sound/soc/codecs/max9877.h
@@ -32,6 +32,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..327eaa25c9bd 100644
--- a/sound/soc/codecs/max98925.c
+++ b/sound/soc/codecs/max98925.c
@@ -540,12 +540,14 @@ static int max98925_probe(struct snd_soc_codec *codec)
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),
+ .component_driver = {
+ .controls = max98925_snd_controls,
+ .num_controls = ARRAY_SIZE(max98925_snd_controls),
+ .dapm_routes = max98925_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98925_audio_map),
+ .dapm_widgets = max98925_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98925_dapm_widgets),
+ },
};
static const struct regmap_config max98925_regmap = {
diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c
index 8d14adae5cc5..1eff7e0b092e 100644
--- a/sound/soc/codecs/max98926.c
+++ b/sound/soc/codecs/max98926.c
@@ -347,7 +347,7 @@ static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai,
max98926_set_sense_data(max98926);
break;
default:
- dev_err(codec->dev, "DAI clock mode unsupported");
+ dev_err(codec->dev, "DAI clock mode unsupported\n");
return -EINVAL;
}
@@ -364,7 +364,7 @@ static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai,
invert = MAX98926_DAI_BCI_MASK | MAX98926_DAI_WCI_MASK;
break;
default:
- dev_err(codec->dev, "DAI invert mode unsupported");
+ dev_err(codec->dev, "DAI invert mode unsupported\n");
return -EINVAL;
}
@@ -408,7 +408,7 @@ static int max98926_dai_hw_params(struct snd_pcm_substream *substream,
max98926->ch_size = 32;
break;
default:
- dev_dbg(codec->dev, "format unsupported %d",
+ dev_dbg(codec->dev, "format unsupported %d\n",
params_format(params));
return -EINVAL;
}
@@ -498,12 +498,14 @@ static int max98926_probe(struct snd_soc_codec *codec)
static struct snd_soc_codec_driver soc_codec_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),
+ .component_driver = {
+ .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),
+ },
};
static const struct regmap_config max98926_regmap = {
diff --git a/sound/soc/codecs/mc13783.c b/sound/soc/codecs/mc13783.c
index 3e770cbe7f0f..90562703dcfd 100644
--- a/sound/soc/codecs/mc13783.c
+++ b/sound/soc/codecs/mc13783.c
@@ -737,12 +737,14 @@ 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),
+ .component_driver = {
+ .controls = mc13783_control_list,
+ .num_controls = ARRAY_SIZE(mc13783_control_list),
+ .dapm_widgets = mc13783_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mc13783_dapm_widgets),
+ .dapm_routes = mc13783_routes,
+ .num_dapm_routes = ARRAY_SIZE(mc13783_routes),
+ },
};
static int __init mc13783_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/ml26124.c b/sound/soc/codecs/ml26124.c
index f561c78b9e0e..69e5e18880c5 100644
--- a/sound/soc/codecs/ml26124.c
+++ b/sound/soc/codecs/ml26124.c
@@ -541,12 +541,14 @@ 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),
+ .component_driver = {
+ .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),
+ },
};
static const struct regmap_config ml26124_i2c_regmap = {
diff --git a/sound/soc/codecs/nau8810.c b/sound/soc/codecs/nau8810.c
new file mode 100644
index 000000000000..e45518629968
--- /dev/null
+++ b/sound/soc/codecs/nau8810.c
@@ -0,0 +1,884 @@
+/*
+ * nau8810.c -- NAU8810 ALSA Soc Audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ *
+ * Author: David Lin <ctlin0@nuvoton.com>
+ *
+ * Based on WM8974.c
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ int i, reg, reg_val;
+ u16 *val;
+
+ val = (u16 *)ucontrol->value.bytes.data;
+ reg = NAU8810_REG_EQ1;
+ for (i = 0; i < params->max / sizeof(u16); i++) {
+ regmap_read(nau8810->regmap, reg + i, &reg_val);
+ /* conversion of 16-bit integers between native CPU format
+ * and big endian format
+ */
+ reg_val = cpu_to_be16(reg_val);
+ memcpy(val + i, &reg_val, sizeof(reg_val));
+ }
+
+ return 0;
+}
+
+/* The EQ parameters put function is to make configuration of 5 band equalizer
+ * control. These configuration includes central frequency, equalizer gain,
+ * cut-off frequency, bandwidth control, and equalizer path.
+ * The regmap raw write can't work here because regmap doesn't provide
+ * register and value format for register with address 7 bits and value 9 bits.
+ * Therefore, the driver makes value format according to the endianness of
+ * bytes type control element and writes data to codec.
+ */
+static int nau8810_eq_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+ void *data;
+ u16 *val, value;
+ int i, reg, ret;
+
+ data = kmemdup(ucontrol->value.bytes.data,
+ params->max, GFP_KERNEL | GFP_DMA);
+ if (!data)
+ return -ENOMEM;
+
+ val = (u16 *)data;
+ reg = NAU8810_REG_EQ1;
+ for (i = 0; i < params->max / sizeof(u16); i++) {
+ /* conversion of 16-bit integers between native CPU format
+ * and big endian format
+ */
+ value = be16_to_cpu(*(val + i));
+ ret = regmap_write(nau8810->regmap, reg + i, value);
+ if (ret) {
+ dev_err(codec->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("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("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_inpga_mute[] = {
+ SOC_DAPM_SINGLE("PGA Mute Switch", NAU8810_REG_PGAGAIN,
+ NAU8810_PGAMT_SFT, 1, 0),
+};
+
+/* Input PGA */
+static const struct snd_kcontrol_new nau8810_inpga[] = {
+ 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),
+};
+
+/* Mic Input boost vol */
+static const struct snd_kcontrol_new nau8810_mic_boost_controls =
+ SOC_DAPM_SINGLE("Mic Volume", NAU8810_REG_ADCBOOST,
+ NAU8810_PMICBSTGAIN_SFT, 0x7, 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ unsigned int value;
+
+ regmap_read(nau8810->regmap, NAU8810_REG_CLOCK, &value);
+ return (value & NAU8810_CLKM_MASK);
+}
+
+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", "HiFi Playback", NAU8810_REG_POWER3,
+ NAU8810_DAC_EN_SFT, 0),
+ SND_SOC_DAPM_ADC("ADC", "HiFi 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_inpga_mute,
+ ARRAY_SIZE(nau8810_inpga_mute)),
+
+ 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("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", "PCM Playback Switch", "DAC"},
+ {"Mono Mixer", "Line Bypass Switch", "Input Boost Stage"},
+
+ /* Speaker output mixer */
+ {"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", NULL, "Input PGA"},
+ {"Input Boost Stage", NULL, "MICP"},
+
+ /* Input PGA */
+ {"Input PGA", NULL, "Mic Bias"},
+ {"Input PGA", "MicN Switch", "MICN"},
+ {"Input PGA", "MicP Switch", "MICP"},
+
+ /* 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_codec *codec = dai->codec;
+ struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+
+ 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 nau88l0_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 = 256 * fs * 4 * nau8810_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(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_codec *codec = codec_dai->codec;
+ struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ struct regmap *map = nau8810->regmap;
+ struct nau8810_pll *pll_param = &nau8810->pll;
+ int ret, fs;
+
+ fs = freq_out / 256;
+ ret = nau88l0_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_codec *codec = codec_dai->codec;
+ struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ u16 ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl2_val |= NAU8810_CLKIO_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8810_AIFMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8810_AIFMT_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8810_AIFMT_PCM_A;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ ctrl1_val |= NAU8810_BCLKP_IB | NAU8810_FSP_IF;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8810_BCLKP_IB;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ ctrl1_val |= NAU8810_FSP_IF;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_IFACE,
+ NAU8810_AIFMT_MASK | NAU8810_FSP_IF |
+ NAU8810_BCLKP_IB, ctrl1_val);
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
+ NAU8810_CLKIO_MASK, ctrl2_val);
+
+ return 0;
+}
+
+static int nau8810_mclk_clkdiv(struct nau8810 *nau8810, int rate)
+{
+ int i, sclk, imclk = rate * 256, div = 0;
+
+ if (!nau8810->sysclk) {
+ dev_err(nau8810->dev, "Make mclk div configuration fail because of invalid system clock\n");
+ return -EINVAL;
+ }
+
+ /* Configure the master clock prescaler div to make system
+ * clock to approximate the internal master clock (IMCLK);
+ * and large or equal to IMCLK.
+ */
+ for (i = 1; i < ARRAY_SIZE(nau8810_mclk_scaler); i++) {
+ sclk = (nau8810->sysclk * 10) /
+ nau8810_mclk_scaler[i];
+ if (sclk < imclk)
+ break;
+ div = i;
+ }
+ dev_dbg(nau8810->dev,
+ "master clock prescaler %x for fs %d\n", div, rate);
+
+ /* master clock from MCLK and disable PLL */
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
+ NAU8810_MCLKSEL_MASK, (div << NAU8810_MCLKSEL_SFT));
+ regmap_update_bits(nau8810->regmap, NAU8810_REG_CLOCK,
+ NAU8810_CLKM_MASK, NAU8810_CLKM_MCLK);
+
+ return 0;
+}
+
+static int nau8810_pcm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ int val_len = 0, val_rate = 0, ret = 0;
+
+ 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_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct nau8810 *nau8810 = snd_soc_codec_get_drvdata(codec);
+ 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_codec_get_bias_level(codec) == 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_rates = 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 struct snd_soc_codec_driver nau8810_codec_driver = {
+ .set_bias_level = nau8810_set_bias_level,
+ .suspend_bias_off = true,
+
+ .component_driver = {
+ .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),
+ },
+};
+
+static int nau8810_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ 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 snd_soc_register_codec(dev,
+ &nau8810_codec_driver, &nau8810_dai, 1);
+}
+
+static int nau8810_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+
+ return 0;
+}
+
+static const struct i2c_device_id nau8810_i2c_id[] = {
+ { "nau8810", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8810_i2c_id);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8810_of_match[] = {
+ { .compatible = "nuvoton,nau8810", },
+ { }
+};
+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,
+ .remove = nau8810_i2c_remove,
+ .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..df882658ca91
--- /dev/null
+++ b/sound/soc/codecs/nau8810.h
@@ -0,0 +1,281 @@
+/*
+ * NAU8810 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: David Lin <ctlin0@nuvoton.com>
+ *
+ * This program is free software; you can 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 __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_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_NMICPGA_SFT 1
+
+/* 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_PMICBSTGAIN_SFT 4
+#define NAU8810_PGABST_SFT 8
+
+/* NAU8810_REG_SPKMIX (0x32) */
+#define NAU8810_DACSPK_SFT 0
+#define NAU8810_BYPSPK_SFT 1
+
+/* 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_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/nau8825.c b/sound/soc/codecs/nau8825.c
index 683769f0f246..e643be91d762 100644
--- a/sound/soc/codecs/nau8825.c
+++ b/sound/soc/codecs/nau8825.c
@@ -18,6 +18,7 @@
#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,10 +31,22 @@
#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 LOG10_MAGIC 646456993
+#define GAIN_AUGMENT 22500
+#define SIDETONE_BASE 207000
+
+
+static int nau8825_configure_sysclk(struct nau8825 *nau8825,
+ int clk_id, unsigned int freq);
+
struct nau8825_fll {
int mclk_src;
int ratio;
@@ -156,6 +169,643 @@ 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, 0 },
+ { NAU8825_REG_HSVOL_CTRL, 0 },
+ { NAU8825_REG_DACL_CTRL, 0 },
+ { NAU8825_REG_DACR_CTRL, 0 },
+};
+
+static const unsigned short logtable[256] = {
+ 0x0000, 0x0171, 0x02e0, 0x044e, 0x05ba, 0x0725, 0x088e, 0x09f7,
+ 0x0b5d, 0x0cc3, 0x0e27, 0x0f8a, 0x10eb, 0x124b, 0x13aa, 0x1508,
+ 0x1664, 0x17bf, 0x1919, 0x1a71, 0x1bc8, 0x1d1e, 0x1e73, 0x1fc6,
+ 0x2119, 0x226a, 0x23ba, 0x2508, 0x2656, 0x27a2, 0x28ed, 0x2a37,
+ 0x2b80, 0x2cc8, 0x2e0f, 0x2f54, 0x3098, 0x31dc, 0x331e, 0x345f,
+ 0x359f, 0x36de, 0x381b, 0x3958, 0x3a94, 0x3bce, 0x3d08, 0x3e41,
+ 0x3f78, 0x40af, 0x41e4, 0x4319, 0x444c, 0x457f, 0x46b0, 0x47e1,
+ 0x4910, 0x4a3f, 0x4b6c, 0x4c99, 0x4dc5, 0x4eef, 0x5019, 0x5142,
+ 0x526a, 0x5391, 0x54b7, 0x55dc, 0x5700, 0x5824, 0x5946, 0x5a68,
+ 0x5b89, 0x5ca8, 0x5dc7, 0x5ee5, 0x6003, 0x611f, 0x623a, 0x6355,
+ 0x646f, 0x6588, 0x66a0, 0x67b7, 0x68ce, 0x69e4, 0x6af8, 0x6c0c,
+ 0x6d20, 0x6e32, 0x6f44, 0x7055, 0x7165, 0x7274, 0x7383, 0x7490,
+ 0x759d, 0x76aa, 0x77b5, 0x78c0, 0x79ca, 0x7ad3, 0x7bdb, 0x7ce3,
+ 0x7dea, 0x7ef0, 0x7ff6, 0x80fb, 0x81ff, 0x8302, 0x8405, 0x8507,
+ 0x8608, 0x8709, 0x8809, 0x8908, 0x8a06, 0x8b04, 0x8c01, 0x8cfe,
+ 0x8dfa, 0x8ef5, 0x8fef, 0x90e9, 0x91e2, 0x92db, 0x93d2, 0x94ca,
+ 0x95c0, 0x96b6, 0x97ab, 0x98a0, 0x9994, 0x9a87, 0x9b7a, 0x9c6c,
+ 0x9d5e, 0x9e4f, 0x9f3f, 0xa02e, 0xa11e, 0xa20c, 0xa2fa, 0xa3e7,
+ 0xa4d4, 0xa5c0, 0xa6ab, 0xa796, 0xa881, 0xa96a, 0xaa53, 0xab3c,
+ 0xac24, 0xad0c, 0xadf2, 0xaed9, 0xafbe, 0xb0a4, 0xb188, 0xb26c,
+ 0xb350, 0xb433, 0xb515, 0xb5f7, 0xb6d9, 0xb7ba, 0xb89a, 0xb97a,
+ 0xba59, 0xbb38, 0xbc16, 0xbcf4, 0xbdd1, 0xbead, 0xbf8a, 0xc065,
+ 0xc140, 0xc21b, 0xc2f5, 0xc3cf, 0xc4a8, 0xc580, 0xc658, 0xc730,
+ 0xc807, 0xc8de, 0xc9b4, 0xca8a, 0xcb5f, 0xcc34, 0xcd08, 0xcddc,
+ 0xceaf, 0xcf82, 0xd054, 0xd126, 0xd1f7, 0xd2c8, 0xd399, 0xd469,
+ 0xd538, 0xd607, 0xd6d6, 0xd7a4, 0xd872, 0xd93f, 0xda0c, 0xdad9,
+ 0xdba5, 0xdc70, 0xdd3b, 0xde06, 0xded0, 0xdf9a, 0xe063, 0xe12c,
+ 0xe1f5, 0xe2bd, 0xe385, 0xe44c, 0xe513, 0xe5d9, 0xe69f, 0xe765,
+ 0xe82a, 0xe8ef, 0xe9b3, 0xea77, 0xeb3b, 0xebfe, 0xecc1, 0xed83,
+ 0xee45, 0xef06, 0xefc8, 0xf088, 0xf149, 0xf209, 0xf2c8, 0xf387,
+ 0xf446, 0xf505, 0xf5c3, 0xf680, 0xf73e, 0xf7fb, 0xf8b7, 0xf973,
+ 0xfa2f, 0xfaea, 0xfba5, 0xfc60, 0xfd1a, 0xfdd4, 0xfe8e, 0xff47
+};
+
+/**
+ * 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.
+ * Acquires the semaphore without jiffies. If no more tasks are allowed
+ * to acquire the semaphore, calling this function will put the task to
+ * sleep until the semaphore is released.
+ * 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.
+ */
+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 semaphone timeout\n");
+ } else {
+ ret = down_interruptible(&nau8825->xtalk_sem);
+ if (ret < 0)
+ dev_warn(nau8825->dev, "Acquire semaphone 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;
+}
+
+/**
+ * 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);
+}
+
+/**
+ * Computes log10 of a value; the result is round off to 3 decimal. This func-
+ * tion takes reference to dvb-math. The source code locates as the following.
+ * Linux/drivers/media/dvb-core/dvb_math.c
+ *
+ * return log10(value) * 1000
+ */
+static u32 nau8825_intlog10_dec3(u32 value)
+{
+ u32 msb, logentry, significand, interpolation, log10val;
+ u64 log2val;
+
+ /* first detect the msb (count begins at 0) */
+ msb = fls(value) - 1;
+ /**
+ * now we use a logtable after the following method:
+ *
+ * log2(2^x * y) * 2^24 = x * 2^24 + log2(y) * 2^24
+ * where x = msb and therefore 1 <= y < 2
+ * first y is determined by shifting the value left
+ * so that msb is bit 31
+ * 0x00231f56 -> 0x8C7D5800
+ * the result is y * 2^31 -> "significand"
+ * then the highest 9 bits are used for a table lookup
+ * the highest bit is discarded because it's always set
+ * the highest nine bits in our example are 100011000
+ * so we would use the entry 0x18
+ */
+ significand = value << (31 - msb);
+ logentry = (significand >> 23) & 0xff;
+ /**
+ * last step we do is interpolation because of the
+ * limitations of the log table the error is that part of
+ * the significand which isn't used for lookup then we
+ * compute the ratio between the error and the next table entry
+ * and interpolate it between the log table entry used and the
+ * next one the biggest error possible is 0x7fffff
+ * (in our example it's 0x7D5800)
+ * needed value for next table entry is 0x800000
+ * so the interpolation is
+ * (error / 0x800000) * (logtable_next - logtable_current)
+ * in the implementation the division is moved to the end for
+ * better accuracy there is also an overflow correction if
+ * logtable_next is 256
+ */
+ interpolation = ((significand & 0x7fffff) *
+ ((logtable[(logentry + 1) & 0xff] -
+ logtable[logentry]) & 0xffff)) >> 15;
+
+ log2val = ((msb << 24) + (logtable[logentry] << 8) + interpolation);
+ /**
+ * log10(x) = log2(x) * log10(2)
+ */
+ log10val = (log2val * LOG10_MAGIC) >> 31;
+ /**
+ * the result is round off to 3 decimal
+ */
+ return log10val / ((1 << 24) / 1000);
+}
+
+/**
+ * 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 (unlikely(sig_org == 0) || unlikely(sig_cros == 0)) {
+ WARN_ON(1);
+ 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;
+
+ /* 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);
+}
+
+static void nau8825_xtalk_restore(struct nau8825 *nau8825)
+{
+ int i, volume;
+
+ /* Restore register values from backup table; When the driver restores
+ * the headphone volumem, it needs recover to original level gradually
+ * with 3dB per step for less pop noise.
+ */
+ for (i = 0; i < ARRAY_SIZE(nau8825_xtalk_baktab); i++) {
+ if (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);
+ }
+}
+
+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_DRV_MASK |
+ NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_MASTER |
+ (0x2 << NAU8825_I2S_DRV_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 */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+ NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL, 0);
+}
+
+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 */
+ 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);
+ /* 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)
+{
+ /* 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_DRV_MASK |
+ NAU8825_I2S_BLK_DIV_MASK, NAU8825_I2S_MS_SLAVE);
+ /* Restore value of specific register for cross talk */
+ nau8825_xtalk_restore(nau8825);
+}
+
+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);
+ 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 xtalk_protect is true, that means the process is still
+ * on going. The driver forces to cancel the cross talk task and
+ * restores the configuration to original status.
+ */
+ if (nau8825->xtalk_protect) {
+ cancel_work_sync(&nau8825->xtalk_work);
+ nau8825_xtalk_clean(nau8825);
+ }
+ /* 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) {
@@ -217,12 +867,36 @@ 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_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 nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ 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)
{
@@ -270,6 +944,54 @@ static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w,
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"
};
@@ -306,6 +1028,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] */
@@ -338,7 +1064,9 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0,
NULL, 0),
- 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),
@@ -459,6 +1187,8 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream,
struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
unsigned int val_len = 0;
+ nau8825_sema_acquire(nau8825, 2 * HZ);
+
switch (params_width(params)) {
case 16:
val_len |= NAU8825_I2S_DL_16;
@@ -479,6 +1209,9 @@ static int nau8825_hw_params(struct snd_pcm_substream *substream,
regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
NAU8825_I2S_DL_MASK, val_len);
+ /* Release the semaphone. */
+ nau8825_sema_release(nau8825);
+
return 0;
}
@@ -488,6 +1221,8 @@ static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
unsigned int ctrl1_val = 0, ctrl2_val = 0;
+ nau8825_sema_acquire(nau8825, 2 * HZ);
+
switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
case SND_SOC_DAIFMT_CBM_CFM:
ctrl2_val |= NAU8825_I2S_MS_MASTER;
@@ -536,6 +1271,9 @@ 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 semaphone. */
+ nau8825_sema_release(nau8825);
+
return 0;
}
@@ -592,9 +1330,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);
@@ -602,24 +1337,21 @@ 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)
{
- /* 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);
-
/* this will restart the entire jack detection process including MIC/GND
* switching and create interrupts. We have to go from 0 to 1 and back
* to 0 to restart.
@@ -630,11 +1362,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 */
@@ -644,6 +1395,69 @@ 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 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);
+
+ /* 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)
@@ -676,6 +1490,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:
@@ -773,6 +1592,36 @@ 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->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 < 0)
+ nau8825->xtalk_protect = false;
+ }
+ /* Startup cross talk detection process */
+ 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);
@@ -780,6 +1629,37 @@ 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) {
+ 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)
@@ -787,7 +1667,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;
@@ -921,11 +1806,16 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
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);
}
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,
@@ -944,18 +1834,15 @@ static int nau8825_codec_probe(struct snd_soc_codec *codec)
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 int nau8825_codec_remove(struct snd_soc_codec *codec)
+{
+ struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+ /* Cancel and reset cross tak suppresstion detection funciton */
+ nau8825_xtalk_cancel(nau8825);
return 0;
}
@@ -973,8 +1860,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.
@@ -999,23 +1886,28 @@ 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;
+ 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);
+ 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;
@@ -1025,7 +1917,8 @@ 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);
regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
NAU8825_FLL_RATIO_MASK, fll_param->ratio);
/* FLL 16-bit fractional input */
@@ -1038,10 +1931,25 @@ static void nau8825_fll_apply(struct nau8825 *nau8825,
NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div);
/* 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) {
+ 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_SDM_EN);
+ } else {
+ 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, 0);
+ }
}
/* freq_out must be 256*Fs in order to achieve the best performance */
@@ -1069,6 +1977,45 @@ 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 = 0;
+
+ 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);
+}
+
static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
unsigned int freq)
{
@@ -1076,40 +2023,106 @@ 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 semaphone 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, 2 * 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 semaphone. */
+ 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 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_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 semaphone 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, 2 * HZ);
+ regmap_update_bits(regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_MCLK);
+ /* Release the semaphone. */
+ 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 semaphone 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, 2 * HZ);
+ regmap_update_bits(regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_BLK);
+ /* Release the semaphone. */
+ 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 semaphone 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, 2 * HZ);
+ regmap_update_bits(regmap, NAU8825_REG_FLL3,
+ NAU8825_FLL_CLK_SRC_MASK, NAU8825_FLL_CLK_SRC_FS);
+ /* Release the semaphone. */
+ nau8825_sema_release(nau8825);
if (nau8825->mclk_freq) {
clk_disable_unprepare(nau8825->mclk);
@@ -1135,6 +2148,31 @@ static int nau8825_set_sysclk(struct snd_soc_codec *codec, int clk_id,
return nau8825_configure_sysclk(nau8825, clk_id, freq);
}
+static int nau8825_resume_setup(struct nau8825 *nau8825)
+{
+ struct regmap *regmap = nau8825->regmap;
+
+ /* Close clock when jack type detection at manual mode */
+ nau8825_configure_sysclk(nau8825, NAU8825_CLK_DIS, 0);
+
+ /* Clear all interruption status */
+ nau8825_int_status_clear_all(regmap);
+
+ /* Enable both insertion and ejection interruptions, and then
+ * bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN |
+ NAU8825_IRQ_EJECT_EN | NAU8825_IRQ_INSERT_EN,
+ NAU8825_IRQ_OUTPUT_EN | NAU8825_IRQ_HEADSET_COMPLETE_EN);
+ regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+ NAU8825_JACK_DET_DB_BYPASS, NAU8825_JACK_DET_DB_BYPASS);
+ regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_DIS_CTRL,
+ NAU8825_IRQ_INSERT_DIS | NAU8825_IRQ_EJECT_DIS, 0);
+
+ return 0;
+}
+
static int nau8825_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
@@ -1157,10 +2195,22 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec,
return ret;
}
}
+ /* Setup codec configuration after resume */
+ nau8825_resume_setup(nau8825);
}
break;
case SND_SOC_BIAS_OFF:
+ /* Cancel and reset cross talk detection funciton */
+ nau8825_xtalk_cancel(nau8825);
+ /* Turn off all interruptions before system shutdown. Keep the
+ * interruption quiet before resume setup completes.
+ */
+ regmap_write(nau8825->regmap,
+ NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff);
+ /* Disable ADC needed for interruptions at audo mode */
+ regmap_update_bits(nau8825->regmap, NAU8825_REG_ENA_CTRL,
+ NAU8825_ENABLE_ADC, 0);
if (nau8825->mclk_freq)
clk_disable_unprepare(nau8825->mclk);
break;
@@ -1168,57 +2218,37 @@ static int nau8825_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
-#ifdef CONFIG_PM
-static int nau8825_suspend(struct snd_soc_codec *codec)
+static int __maybe_unused nau8825_suspend(struct snd_soc_codec *codec)
{
struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
disable_irq(nau8825->irq);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
regcache_cache_only(nau8825->regmap, true);
regcache_mark_dirty(nau8825->regmap);
return 0;
}
-static int nau8825_resume(struct snd_soc_codec *codec)
+static int __maybe_unused nau8825_resume(struct snd_soc_codec *codec)
{
struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+ int ret;
- /* The chip may lose power and reset in S3. regcache_sync restores
- * register values including configurations for sysclk, irq, and
- * jack/button detection.
- */
regcache_cache_only(nau8825->regmap, false);
regcache_sync(nau8825->regmap);
-
- /* Check the jack plug status directly. If the headset is unplugged
- * during S3 when the chip has no power, there will be no jack
- * detection irq even after the nau8825_restart_jack_detection below,
- * because the chip just thinks no headset has ever been plugged in.
- */
- if (!nau8825_is_jack_inserted(nau8825->regmap)) {
- nau8825_eject_jack(nau8825);
- snd_soc_jack_report(nau8825->jack, 0, SND_JACK_HEADSET);
- }
-
+ nau8825->xtalk_protect = true;
+ ret = nau8825_sema_acquire(nau8825, 0);
+ if (ret < 0)
+ nau8825->xtalk_protect = false;
enable_irq(nau8825->irq);
- /* Run jack detection to check the type (OMTP or CTIA) of the headset
- * if there is one. This handles the case where a different type of
- * headset is plugged in during S3. This triggers an IRQ iff a headset
- * is already plugged in.
- */
- nau8825_restart_jack_detection(nau8825->regmap);
-
return 0;
}
-#else
-#define nau8825_suspend NULL
-#define nau8825_resume NULL
-#endif
static struct snd_soc_codec_driver nau8825_codec_driver = {
.probe = nau8825_codec_probe,
+ .remove = nau8825_codec_remove,
.set_sysclk = nau8825_set_sysclk,
.set_pll = nau8825_set_pll,
.set_bias_level = nau8825_set_bias_level,
@@ -1226,12 +2256,14 @@ static struct snd_soc_codec_driver nau8825_codec_driver = {
.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),
+ .component_driver = {
+ .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 void nau8825_reset_chip(struct regmap *regmap)
@@ -1318,22 +2350,8 @@ static int nau8825_read_device_properties(struct device *dev,
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);
-
ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL,
nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
"nau8825", nau8825);
@@ -1370,6 +2388,13 @@ static int nau8825_i2c_probe(struct i2c_client *i2c,
return PTR_ERR(nau8825->regmap);
nau8825->dev = dev;
nau8825->irq = i2c->irq;
+ /* Initiate parameters, semaphone and work queue which are needed in
+ * cross talk suppression measurment function.
+ */
+ nau8825->xtalk_state = NAU8825_XTALK_DONE;
+ nau8825->xtalk_protect = false;
+ sema_init(&nau8825->xtalk_sem, 1);
+ INIT_WORK(&nau8825->xtalk_work, nau8825_xtalk_work);
nau8825_print_device_properties(nau8825);
@@ -1405,6 +2430,7 @@ static const struct i2c_device_id nau8825_i2c_ids[] = {
{ "nau8825", 0 },
{ }
};
+MODULE_DEVICE_TABLE(i2c, nau8825_i2c_ids);
#ifdef CONFIG_OF
static const struct of_device_id nau8825_of_ids[] = {
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
index 8ceb5f385478..1c63e2abafa9 100644
--- a/sound/soc/codecs/nau8825.h
+++ b/sound/soc/codecs/nau8825.h
@@ -93,12 +93,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) */
@@ -113,20 +122,28 @@
/* FLL3 (0x06) */
#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)
/* 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)
/* HSD_CTRL (0xc) */
#define NAU8825_HSD_AUTO_MODE (1 << 6)
@@ -136,6 +153,7 @@
/* 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
@@ -145,9 +163,11 @@
/* INTERRUPT_MASK (0xf) */
#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)
@@ -168,6 +188,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
@@ -217,10 +238,21 @@
/* I2S_PCM_CTRL2 (0x1d) */
#define NAU8825_I2S_TRISTATE (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */
+#define NAU8825_I2S_DRV_SFT 12
+#define NAU8825_I2S_DRV_MASK (0x3 << NAU8825_I2S_DRV_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
+
+/* 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_SYNC_DOWN_SFT 0
@@ -239,22 +271,72 @@
#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) */
@@ -263,7 +345,12 @@
#define NAU8825_SOFTWARE_ID_NAU8825 0x0
/* BIAS_ADJ (0x66) */
-#define NAU8825_BIAS_TESTDAC_EN (0x3 << 8)
+#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)
@@ -282,6 +369,11 @@
#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
@@ -318,8 +410,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 {
@@ -328,6 +433,8 @@ 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 irq;
int mclk_freq; /* 0 - mclk is disabled */
int button_pressed;
@@ -346,6 +453,12 @@ 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 nau8825_enable_jack_detect(struct snd_soc_codec *codec,
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
index 58325234285c..0b14efab6280 100644
--- a/sound/soc/codecs/pcm1681.c
+++ b/sound/soc/codecs/pcm1681.c
@@ -73,7 +73,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);
@@ -289,12 +289,14 @@ static const struct regmap_config pcm1681_regmap = {
};
static struct snd_soc_codec_driver soc_codec_dev_pcm1681 = {
- .controls = pcm1681_controls,
- .num_controls = ARRAY_SIZE(pcm1681_controls),
- .dapm_widgets = pcm1681_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(pcm1681_dapm_widgets),
- .dapm_routes = pcm1681_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(pcm1681_dapm_routes),
+ .component_driver = {
+ .controls = pcm1681_controls,
+ .num_controls = ARRAY_SIZE(pcm1681_controls),
+ .dapm_widgets = pcm1681_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm1681_dapm_widgets),
+ .dapm_routes = pcm1681_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm1681_dapm_routes),
+ },
};
static const struct i2c_device_id pcm1681_i2c_id[] = {
diff --git a/sound/soc/codecs/pcm179x.c b/sound/soc/codecs/pcm179x.c
index 06a66579ca6d..b813a154ddd9 100644
--- a/sound/soc/codecs/pcm179x.c
+++ b/sound/soc/codecs/pcm179x.c
@@ -59,7 +59,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;
@@ -206,12 +206,14 @@ const struct regmap_config pcm179x_regmap_config = {
EXPORT_SYMBOL_GPL(pcm179x_regmap_config);
static struct snd_soc_codec_driver soc_codec_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),
+ .component_driver = {
+ .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),
+ },
};
int pcm179x_common_init(struct device *dev, struct regmap *regmap)
diff --git a/sound/soc/codecs/pcm3008.c b/sound/soc/codecs/pcm3008.c
index 8fb445f33f6f..708af05486f6 100644
--- a/sound/soc/codecs/pcm3008.c
+++ b/sound/soc/codecs/pcm3008.c
@@ -99,10 +99,12 @@ 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),
+ .component_driver = {
+ .dapm_widgets = pcm3008_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets),
+ .dapm_routes = pcm3008_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes),
+ },
};
static int pcm3008_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c
index 992a77edcd5d..39bc02d5bc5d 100644
--- a/sound/soc/codecs/pcm3168a.c
+++ b/sound/soc/codecs/pcm3168a.c
@@ -599,12 +599,14 @@ 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)
+ .component_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)
+ },
};
int pcm3168a_probe(struct device *dev, struct regmap *regmap)
diff --git a/sound/soc/codecs/pcm5102a.c b/sound/soc/codecs/pcm5102a.c
index ed515677409b..8ba322a00363 100644
--- a/sound/soc/codecs/pcm5102a.c
+++ b/sound/soc/codecs/pcm5102a.c
@@ -57,7 +57,6 @@ static struct platform_driver pcm5102a_codec_driver = {
.remove = pcm5102a_remove,
.driver = {
.name = "pcm5102a-codec",
- .owner = THIS_MODULE,
.of_match_table = pcm5102a_of_match,
},
};
diff --git a/sound/soc/codecs/pcm512x.c b/sound/soc/codecs/pcm512x.c
index 047c48953a20..72b19e62f626 100644
--- a/sound/soc/codecs/pcm512x.c
+++ b/sound/soc/codecs/pcm512x.c
@@ -1348,12 +1348,14 @@ 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),
+ .component_driver = {
+ .controls = pcm512x_controls,
+ .num_controls = ARRAY_SIZE(pcm512x_controls),
+ .dapm_widgets = pcm512x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(pcm512x_dapm_widgets),
+ .dapm_routes = pcm512x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(pcm512x_dapm_routes),
+ },
};
static const struct regmap_range_cfg pcm512x_range = {
diff --git a/sound/soc/codecs/rt286.c b/sound/soc/codecs/rt286.c
index 1bd31644a782..9c365a7f758d 100644
--- a/sound/soc/codecs/rt286.c
+++ b/sound/soc/codecs/rt286.c
@@ -1053,12 +1053,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt286 = {
.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),
+ .component_driver = {
+ .controls = rt286_snd_controls,
+ .num_controls = ARRAY_SIZE(rt286_snd_controls),
+ .dapm_widgets = rt286_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt286_dapm_widgets),
+ .dapm_routes = rt286_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt286_dapm_routes),
+ },
};
static const struct regmap_config rt286_regmap = {
@@ -1100,6 +1102,13 @@ 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")
+ }
+ },
+
{ }
};
diff --git a/sound/soc/codecs/rt298.c b/sound/soc/codecs/rt298.c
index f80cfe4d2ef2..55558643166f 100644
--- a/sound/soc/codecs/rt298.c
+++ b/sound/soc/codecs/rt298.c
@@ -1095,12 +1095,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt298 = {
.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),
+ .component_driver = {
+ .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 regmap_config rt298_regmap = {
diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c
new file mode 100644
index 000000000000..09103aab0cb2
--- /dev/null
+++ b/sound/soc/codecs/rt5514-spi.c
@@ -0,0 +1,447 @@
+/*
+ * rt5514-spi.c -- RT5514 SPI driver
+ *
+ * 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>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_qos.h>
+#include <linux/sysfs.h>
+#include <linux/clk.h>
+#include <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"
+
+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;
+ size_t dma_offset;
+ size_t dsp_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;
+
+ 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 (rt5514_dsp->buf_size - rt5514_dsp->dsp_offset < period_bytes)
+ period_bytes = rt5514_dsp->buf_size - rt5514_dsp->dsp_offset;
+
+ 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->dma_offset += period_bytes;
+ if (rt5514_dsp->dma_offset >= runtime->dma_bytes)
+ rt5514_dsp->dma_offset = 0;
+
+ rt5514_dsp->dsp_offset += period_bytes;
+
+ snd_pcm_period_elapsed(rt5514_dsp->substream);
+
+ if (rt5514_dsp->dsp_offset < rt5514_dsp->buf_size)
+ schedule_delayed_work(&rt5514_dsp->copy_work, 5);
+done:
+ mutex_unlock(&rt5514_dsp->dma_lock);
+}
+
+/* PCM for streaming audio from the DSP buffer */
+static int rt5514_spi_pcm_open(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_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ int ret;
+
+ mutex_lock(&rt5514_dsp->dma_lock);
+ ret = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ rt5514_dsp->substream = substream;
+ mutex_unlock(&rt5514_dsp->dma_lock);
+
+ return ret;
+}
+
+static int rt5514_spi_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+
+ 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 snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int rt5514_spi_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+ u8 buf[8];
+
+ rt5514_dsp->dma_offset = 0;
+ rt5514_dsp->dsp_offset = 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_RP, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_rp = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ rt5514_spi_burst_read(RT5514_BUFFER_VOICE_SIZE, (u8 *)&buf,
+ sizeof(buf));
+ rt5514_dsp->buf_size = buf[0] | buf[1] << 8 | buf[2] << 16 |
+ buf[3] << 24;
+
+ return 0;
+}
+
+static int rt5514_spi_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+
+ if (cmd == SNDRV_PCM_TRIGGER_START) {
+ 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);
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t rt5514_spi_pcm_pointer(
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct rt5514_dsp *rt5514_dsp =
+ snd_soc_platform_get_drvdata(rtd->platform);
+
+ return bytes_to_frames(runtime, rt5514_dsp->dma_offset);
+}
+
+static const struct snd_pcm_ops rt5514_spi_pcm_ops = {
+ .open = rt5514_spi_pcm_open,
+ .hw_params = rt5514_spi_hw_params,
+ .hw_free = rt5514_spi_hw_free,
+ .trigger = rt5514_spi_trigger,
+ .prepare = rt5514_spi_prepare,
+ .pointer = rt5514_spi_pcm_pointer,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+ .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static int rt5514_spi_pcm_probe(struct snd_soc_platform *platform)
+{
+ struct rt5514_dsp *rt5514_dsp;
+
+ rt5514_dsp = devm_kzalloc(platform->dev, sizeof(*rt5514_dsp),
+ GFP_KERNEL);
+
+ 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_platform_set_drvdata(platform, rt5514_dsp);
+
+ return 0;
+}
+
+static struct snd_soc_platform_driver rt5514_spi_platform = {
+ .probe = rt5514_spi_pcm_probe,
+ .ops = &rt5514_spi_pcm_ops,
+};
+
+static const struct snd_soc_component_driver rt5514_spi_dai_component = {
+ .name = "rt5514-spi-dai",
+};
+
+/**
+ * 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;
+}
+
+/**
+ * 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_platform(&spi->dev, &rt5514_spi_platform);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to register platform.\n");
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&spi->dev,
+ &rt5514_spi_dai_component,
+ &rt5514_spi_dai, 1);
+ if (ret < 0) {
+ dev_err(&spi->dev, "Failed to register component.\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+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",
+ .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..f69b1cdf2f9b
--- /dev/null
+++ b/sound/soc/codecs/rt5514-spi.h
@@ -0,0 +1,38 @@
+/*
+ * rt5514-spi.h -- RT5514 driver
+ *
+ * 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 __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 0x18001034
+#define RT5514_BUFFER_VOICE_LIMIT 0x18001038
+#define RT5514_BUFFER_VOICE_RP 0x1800103c
+#define RT5514_BUFFER_VOICE_SIZE 0x18001040
+
+/* 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
index 879bf60f4965..f24b7cfd3a89 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -30,6 +30,9 @@
#include "rl6231.h"
#include "rt5514.h"
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+#include "rt5514-spi.h"
+#endif
static const struct reg_sequence rt5514_i2c_patch[] = {
{0x1800101c, 0x00000000},
@@ -110,6 +113,35 @@ static const struct reg_default rt5514_reg[] = {
{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) {
@@ -246,17 +278,87 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv,
8, 8, TLV_DB_SCALE_ITEM(1700, 0, 0)
);
-static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0);
+
+static 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_dsp_voice_wake_up_put(struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+ struct rt5514_priv *rt5514 = snd_soc_component_get_drvdata(component);
+ struct snd_soc_codec *codec = rt5514->codec;
+ const struct firmware *fw = NULL;
+
+ if (ucontrol->value.integer.value[0] == rt5514->dsp_enabled)
+ return 0;
+
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ rt5514->dsp_enabled = ucontrol->value.integer.value[0];
+
+ if (rt5514->dsp_enabled) {
+ rt5514_enable_dsp_prepare(rt5514);
+
+ request_firmware(&fw, RT5514_FIRMWARE1, codec->dev);
+ if (fw) {
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_write(0x4ff60000, fw->data,
+ ((fw->size/8)+1)*8);
+#else
+ dev_err(codec->dev, "There is no SPI driver for"
+ " loading the firmware\n");
+#endif
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ request_firmware(&fw, RT5514_FIRMWARE2, codec->dev);
+ if (fw) {
+#if defined(CONFIG_SND_SOC_RT5514_SPI)
+ rt5514_spi_burst_write(0x4ffc0000, fw->data,
+ ((fw->size/8)+1)*8);
+#else
+ dev_err(codec->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);
+ } 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 0;
+}
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, 127, 0,
+ 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, 127, 0,
+ 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*/
@@ -365,6 +467,35 @@ static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
return 0;
}
+static int rt5514_pre_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /**
+ * 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:
+ return 0;
+ }
+
+ return 0;
+}
+
static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
/* Input Lines */
SND_SOC_DAPM_INPUT("DMIC1L"),
@@ -472,6 +603,8 @@ static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
/* Audio Interface */
SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_PRE("DAPM Pre", rt5514_pre_event),
};
static const struct snd_soc_dapm_route rt5514_dapm_routes[] = {
@@ -799,10 +932,41 @@ static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
return 0;
}
+static int rt5514_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_PREPARE:
+ if (IS_ERR(rt5514->mclk))
+ break;
+
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+ clk_disable_unprepare(rt5514->mclk);
+ } else {
+ ret = clk_prepare_enable(rt5514->mclk);
+ if (ret)
+ return ret;
+ }
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
static int rt5514_probe(struct snd_soc_codec *codec)
{
struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+ rt5514->mclk = devm_clk_get(codec->dev, "mclk");
+ if (PTR_ERR(rt5514->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
rt5514->codec = codec;
return 0;
@@ -858,12 +1022,15 @@ struct snd_soc_dai_driver rt5514_dai[] = {
static struct snd_soc_codec_driver soc_codec_dev_rt5514 = {
.probe = rt5514_probe,
.idle_bias_off = true,
- .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),
+ .set_bias_level = rt5514_set_bias_level,
+ .component_driver = {
+ .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),
+ },
};
static const struct regmap_config rt5514_i2c_regmap = {
@@ -871,7 +1038,6 @@ static const struct regmap_config rt5514_i2c_regmap = {
.reg_bits = 32,
.val_bits = 32,
- .max_register = RT5514_DSP_MAPPING | RT5514_VENDOR_ID2,
.readable_reg = rt5514_i2c_readable_register,
.cache_type = REGCACHE_NONE,
@@ -944,7 +1110,7 @@ static int rt5514_i2c_probe(struct i2c_client *i2c,
return -ENODEV;
}
- ret = regmap_register_patch(rt5514->i2c_regmap, rt5514_i2c_patch,
+ 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",
diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h
index 6ad8a612f659..229de0e2c88c 100644
--- a/sound/soc/codecs/rt5514.h
+++ b/sound/soc/codecs/rt5514.h
@@ -12,6 +12,8 @@
#ifndef __RT5514_H__
#define __RT5514_H__
+#include <linux/clk.h>
+
#define RT5514_DEVICE_ID 0x10ec5514
#define RT5514_RESET 0x2000
@@ -194,8 +196,8 @@
#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 (0x7f << 0)
-#define RT5514_AD_GAIN_SFT 0
+#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)
@@ -225,6 +227,9 @@
#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,
@@ -240,6 +245,7 @@ enum {
struct rt5514_priv {
struct snd_soc_codec *codec;
struct regmap *i2c_regmap, *regmap;
+ struct clk *mclk;
int sysclk;
int sysclk_src;
int lrck;
@@ -247,6 +253,7 @@ struct rt5514_priv {
int pll_src;
int pll_in;
int pll_out;
+ int dsp_enabled;
};
#endif /* __RT5514_H__ */
diff --git a/sound/soc/codecs/rt5616.c b/sound/soc/codecs/rt5616.c
index f527b5b2817b..d1f273b24991 100644
--- a/sound/soc/codecs/rt5616.c
+++ b/sound/soc/codecs/rt5616.c
@@ -294,8 +294,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),
@@ -303,7 +302,7 @@ 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 */
@@ -1267,14 +1266,14 @@ static int rt5616_resume(struct snd_soc_codec *codec)
#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 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,
@@ -1302,12 +1301,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5616 = {
.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),
+ .component_driver = {
+ .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 regmap_config rt5616_regmap = {
diff --git a/sound/soc/codecs/rt5631.c b/sound/soc/codecs/rt5631.c
index 1be2bab7dee3..0e418089c053 100644
--- a/sound/soc/codecs/rt5631.c
+++ b/sound/soc/codecs/rt5631.c
@@ -1657,12 +1657,14 @@ 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),
+ .component_driver = {
+ .controls = rt5631_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5631_snd_controls),
+ .dapm_widgets = rt5631_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5631_dapm_widgets),
+ .dapm_routes = rt5631_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5631_dapm_routes),
+ },
};
static const struct i2c_device_id rt5631_i2c_id[] = {
diff --git a/sound/soc/codecs/rt5640.c b/sound/soc/codecs/rt5640.c
index 09e8988bbb2d..3cc1135fc2cd 100644
--- a/sound/soc/codecs/rt5640.c
+++ b/sound/soc/codecs/rt5640.c
@@ -1870,6 +1870,9 @@ static int rt5640_set_dai_sysclk(struct snd_soc_dai *dai,
case RT5640_SCLK_S_PLL1:
reg_val |= RT5640_SCLK_SRC_PLL1;
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);
return -EINVAL;
@@ -2261,12 +2264,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5640 = {
.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),
+ .component_driver = {
+ .controls = rt5640_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5640_snd_controls),
+ .dapm_widgets = rt5640_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5640_dapm_widgets),
+ .dapm_routes = rt5640_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5640_dapm_routes),
+ },
};
static const struct regmap_config rt5640_regmap = {
diff --git a/sound/soc/codecs/rt5640.h b/sound/soc/codecs/rt5640.h
index 58b664b06c16..90c88711c72a 100644
--- a/sound/soc/codecs/rt5640.h
+++ b/sound/soc/codecs/rt5640.h
@@ -984,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)
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index 3c6594da6c9c..10c2a564a715 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -63,6 +63,7 @@ static const struct reg_sequence init_list[] = {
{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[] = {
@@ -157,7 +158,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 +254,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 +315,7 @@ static const struct reg_default rt5650_reg[] = {
{ 0x83, 0x0000 },
{ 0x84, 0x0000 },
{ 0x85, 0x0000 },
- { 0x8a, 0x0000 },
+ { 0x8a, 0x0120 },
{ 0x8e, 0x0004 },
{ 0x8f, 0x1100 },
{ 0x90, 0x0646 },
@@ -440,6 +441,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:
@@ -740,6 +742,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,
@@ -806,6 +816,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),
};
/**
@@ -3471,12 +3484,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5645 = {
.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),
+ .component_driver = {
+ .controls = rt5645_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5645_snd_controls),
+ .dapm_widgets = rt5645_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5645_dapm_widgets),
+ .dapm_routes = rt5645_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5645_dapm_routes),
+ },
};
static const struct regmap_config rt5645_regmap = {
@@ -3531,6 +3546,7 @@ MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
static const struct acpi_device_id rt5645_acpi_match[] = {
{ "10EC5645", 0 },
{ "10EC5650", 0 },
+ { "10EC5640", 0 },
{},
};
MODULE_DEVICE_TABLE(acpi, rt5645_acpi_match);
@@ -3561,6 +3577,12 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Setzer"),
},
},
+ {
+ .ident = "Microsoft Surface 3",
+ .matches = {
+ DMI_MATCH(DMI_PRODUCT_NAME, "Surface 3"),
+ },
+ },
{ }
};
diff --git a/sound/soc/codecs/rt5645.h b/sound/soc/codecs/rt5645.h
index 205e0715c99a..cfc5f97549eb 100644
--- a/sound/soc/codecs/rt5645.h
+++ b/sound/soc/codecs/rt5645.h
@@ -2018,6 +2018,9 @@
/* 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
diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c
index 7a6197042423..f5d34153e21f 100644
--- a/sound/soc/codecs/rt5651.c
+++ b/sound/soc/codecs/rt5651.c
@@ -1712,12 +1712,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5651 = {
.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),
+ .component_driver = {
+ .controls = rt5651_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5651_snd_controls),
+ .dapm_widgets = rt5651_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5651_dapm_widgets),
+ .dapm_routes = rt5651_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5651_dapm_routes),
+ },
};
static const struct regmap_config rt5651_regmap = {
diff --git a/sound/soc/codecs/rt5659.c b/sound/soc/codecs/rt5659.c
index 1b30914c2d91..db54550aed60 100644
--- a/sound/soc/codecs/rt5659.c
+++ b/sound/soc/codecs/rt5659.c
@@ -9,6 +9,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/clk.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
#include <linux/init.h>
@@ -3565,7 +3566,9 @@ static int rt5659_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
static int rt5659_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
struct rt5659_priv *rt5659 = snd_soc_codec_get_drvdata(codec);
+ int ret;
switch (level) {
case SND_SOC_BIAS_PREPARE:
@@ -3582,6 +3585,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 (dapm->bias_level == SND_SOC_BIAS_OFF) {
+ ret = clk_prepare_enable(rt5659->mclk);
+ if (ret) {
+ dev_err(codec->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 +3605,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:
@@ -3722,12 +3737,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5659 = {
.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),
+ .component_driver = {
+ .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),
+ },
};
@@ -4020,6 +4037,15 @@ 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(&i2c->dev, "mclk");
+ if (IS_ERR(rt5659->mclk)) {
+ if (PTR_ERR(rt5659->mclk) != -ENOENT)
+ return PTR_ERR(rt5659->mclk);
+ /* Otherwise mark the mclk pointer to NULL */
+ rt5659->mclk = NULL;
+ }
+
rt5659_calibrate(rt5659);
/* line in diff mode*/
@@ -4163,6 +4189,9 @@ static int rt5659_i2c_probe(struct i2c_client *i2c,
if (ret)
dev_err(&i2c->dev, "Failed to reguest IRQ: %d\n", ret);
+ /* Enable IRQ output for GPIO1 pin any way */
+ regmap_update_bits(rt5659->regmap, RT5659_GPIO_CTRL_1,
+ RT5659_GP1_PIN_MASK, RT5659_GP1_PIN_IRQ);
}
return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5659,
diff --git a/sound/soc/codecs/rt5659.h b/sound/soc/codecs/rt5659.h
index d31c9e5bcec8..8f1aeef08489 100644
--- a/sound/soc/codecs/rt5659.h
+++ b/sound/soc/codecs/rt5659.h
@@ -180,9 +180,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
@@ -1796,6 +1796,7 @@ struct rt5659_priv {
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;
diff --git a/sound/soc/codecs/rt5660.c b/sound/soc/codecs/rt5660.c
new file mode 100644
index 000000000000..9f0933ced804
--- /dev/null
+++ b/sound/soc/codecs/rt5660.c
@@ -0,0 +1,1353 @@
+/*
+ * rt5660.c -- RT5660 ALSA SoC audio codec driver
+ *
+ * Copyright 2016 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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of.h>
+#include <linux/of_gpio.h>
+#include <linux/platform_device.h>
+#include <linux/spi/spi.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Failed to set DMIC clock\n");
+ else
+ snd_soc_update_bits(codec, 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ unsigned int val;
+
+ val = snd_soc_read(codec, 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 const SOC_ENUM_SINGLE_DECL(rt5660_if1_dac_enum,
+ RT5660_DIG_INF1_DATA, RT5660_IF1_DAC_IN_SFT, rt5660_data_select);
+
+static const 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, 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_update_bits(codec, 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_codec *codec = dai->codec;
+ struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->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(codec->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_update_bits(codec, RT5660_I2S1_SDP, RT5660_I2S_DL_MASK,
+ val_len);
+ snd_soc_update_bits(codec, RT5660_ADDA_CLK1, mask_clk, val_clk);
+ break;
+
+ default:
+ dev_err(codec->dev, "Invalid dai->id: %d\n", dai->id);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5660_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ rt5660->master[dai->id] = 1;
+ break;
+
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT5660_I2S_MS_S;
+ rt5660->master[dai->id] = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+
+ case SND_SOC_DAIFMT_IB_NF:
+ reg_val |= RT5660_I2S_BP_INV;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ break;
+
+ case SND_SOC_DAIFMT_LEFT_J:
+ reg_val |= RT5660_I2S_DF_LEFT;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5660_I2S_DF_PCM_A;
+ break;
+
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5660_I2S_DF_PCM_B;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ switch (dai->id) {
+ case RT5660_AIF1:
+ snd_soc_update_bits(codec, RT5660_I2S1_SDP,
+ RT5660_I2S_MS_MASK | RT5660_I2S_BP_MASK |
+ RT5660_I2S_DF_MASK, reg_val);
+ break;
+
+ default:
+ dev_err(codec->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_codec *codec = dai->codec;
+ struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, 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_codec *codec = dai->codec;
+ struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "PLL disabled\n");
+
+ rt5660->pll_in = 0;
+ rt5660->pll_out = 0;
+ snd_soc_update_bits(codec, RT5660_GLB_CLK,
+ RT5660_SCLK_SRC_MASK, RT5660_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ switch (source) {
+ case RT5660_PLL1_S_MCLK:
+ snd_soc_update_bits(codec, RT5660_GLB_CLK,
+ RT5660_PLL1_SRC_MASK, RT5660_PLL1_SRC_MCLK);
+ break;
+
+ case RT5660_PLL1_S_BCLK:
+ snd_soc_update_bits(codec, RT5660_GLB_CLK,
+ RT5660_PLL1_SRC_MASK, RT5660_PLL1_SRC_BCLK1);
+ break;
+
+ default:
+ dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+ pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+ pll_code.n_code, pll_code.k_code);
+
+ snd_soc_write(codec, RT5660_PLL_CTRL1,
+ pll_code.n_code << RT5660_PLL_N_SFT | pll_code.k_code);
+ snd_soc_write(codec, 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_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ snd_soc_update_bits(codec, RT5660_GEN_CTRL1,
+ RT5660_DIG_GATE_CTRL, RT5660_DIG_GATE_CTRL);
+
+ if (IS_ERR(rt5660->mclk))
+ break;
+
+ if (snd_soc_codec_get_bias_level(codec) == 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_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ snd_soc_update_bits(codec, 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_update_bits(codec, RT5660_PWR_ANLG1,
+ RT5660_PWR_FV1 | RT5660_PWR_FV2,
+ RT5660_PWR_FV1 | RT5660_PWR_FV2);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, RT5660_GEN_CTRL1,
+ RT5660_DIG_GATE_CTRL, 0);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5660_probe(struct snd_soc_codec *codec)
+{
+ struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+
+ rt5660->codec = codec;
+
+ return 0;
+}
+
+static int rt5660_remove(struct snd_soc_codec *codec)
+{
+ return snd_soc_write(codec, RT5660_RESET, 0);
+}
+
+#ifdef CONFIG_PM
+static int rt5660_suspend(struct snd_soc_codec *codec)
+{
+ struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt5660->regmap, true);
+ regcache_mark_dirty(rt5660->regmap);
+
+ return 0;
+}
+
+static int rt5660_resume(struct snd_soc_codec *codec)
+{
+ struct rt5660_priv *rt5660 = snd_soc_codec_get_drvdata(codec);
+
+ if (rt5660->pdata.poweroff_codec_in_suspend)
+ usleep_range(350000, 400000);
+
+ 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 struct snd_soc_codec_driver soc_codec_dev_rt5660 = {
+ .probe = rt5660_probe,
+ .remove = rt5660_remove,
+ .suspend = rt5660_suspend,
+ .resume = rt5660_resume,
+ .set_bias_level = rt5660_set_bias_level,
+ .idle_bias_off = true,
+ .component_driver = {
+ .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),
+ },
+};
+
+static const struct regmap_config rt5660_regmap = {
+ .reg_bits = 8,
+ .val_bits = 16,
+ .use_single_rw = 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_RBTREE,
+ .reg_defaults = rt5660_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5660_reg),
+ .ranges = rt5660_ranges,
+ .num_ranges = ARRAY_SIZE(rt5660_ranges),
+};
+
+static const struct i2c_device_id rt5660_i2c_id[] = {
+ { "rt5660", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5660_i2c_id);
+
+static const struct of_device_id rt5660_of_match[] = {
+ { .compatible = "realtek,rt5660", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5660_of_match);
+
+static const struct acpi_device_id rt5660_acpi_match[] = {
+ { "10EC5660", 0 },
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, rt5660_acpi_match);
+
+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,
+ const struct i2c_device_id *id)
+{
+ struct rt5660_platform_data *pdata = dev_get_platdata(&i2c->dev);
+ struct rt5660_priv *rt5660;
+ int ret;
+ unsigned int val;
+
+ rt5660 = devm_kzalloc(&i2c->dev, sizeof(struct rt5660_priv),
+ GFP_KERNEL);
+
+ if (rt5660 == NULL)
+ return -ENOMEM;
+
+ /* Check if MCLK provided */
+ rt5660->mclk = devm_clk_get(&i2c->dev, "mclk");
+ if (PTR_ERR(rt5660->mclk) == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ i2c_set_clientdata(i2c, rt5660);
+
+ if (pdata)
+ rt5660->pdata = *pdata;
+ else if (i2c->dev.of_node)
+ rt5660_parse_dt(rt5660, &i2c->dev);
+
+ rt5660->regmap = devm_regmap_init_i2c(i2c, &rt5660_regmap);
+ if (IS_ERR(rt5660->regmap)) {
+ ret = PTR_ERR(rt5660->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ regmap_read(rt5660->regmap, RT5660_VENDOR_ID2, &val);
+ if (val != RT5660_DEVICE_ID) {
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt5660\n", val);
+ return -ENODEV;
+ }
+
+ regmap_write(rt5660->regmap, RT5660_RESET, 0);
+
+ ret = regmap_register_patch(rt5660->regmap, rt5660_patch,
+ ARRAY_SIZE(rt5660_patch));
+ if (ret != 0)
+ dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+ if (rt5660->pdata.dmic1_data_pin) {
+ regmap_update_bits(rt5660->regmap, RT5660_GPIO_CTRL1,
+ RT5660_GP1_PIN_MASK, RT5660_GP1_PIN_DMIC1_SCL);
+
+ if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_GPIO2)
+ regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1,
+ RT5660_SEL_DMIC_DATA_MASK,
+ RT5660_SEL_DMIC_DATA_GPIO2);
+ else if (rt5660->pdata.dmic1_data_pin == RT5660_DMIC1_DATA_IN1P)
+ regmap_update_bits(rt5660->regmap, RT5660_DMIC_CTRL1,
+ RT5660_SEL_DMIC_DATA_MASK,
+ RT5660_SEL_DMIC_DATA_IN1P);
+ }
+
+ return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5660,
+ rt5660_dai, ARRAY_SIZE(rt5660_dai));
+}
+
+static int rt5660_i2c_remove(struct i2c_client *i2c)
+{
+ snd_soc_unregister_codec(&i2c->dev);
+
+ return 0;
+}
+
+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,
+ .remove = rt5660_i2c_remove,
+ .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..6cdb9269ec9e
--- /dev/null
+++ b/sound/soc/codecs/rt5660.h
@@ -0,0 +1,847 @@
+/*
+ * rt5660.h -- RT5660 ALSA SoC audio driver
+ *
+ * Copyright 2016 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 _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_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_codec *codec;
+ 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..01a18d88f1eb
--- /dev/null
+++ b/sound/soc/codecs/rt5663.c
@@ -0,0 +1,3218 @@
+/*
+ * rt5663.c -- RT5668/RT5663 ALSA SoC audio codec driver
+ *
+ * Copyright 2016 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.
+ */
+#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/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 RT5668_DEVICE_ID 0x6451
+#define RT5663_DEVICE_ID 0x6406
+
+enum {
+ CODEC_TYPE_RT5668,
+ CODEC_TYPE_RT5663,
+};
+
+struct rt5663_priv {
+ struct snd_soc_codec *codec;
+ struct regmap *regmap;
+ struct delayed_work jack_detect_work;
+ struct snd_soc_jack *hs_jack;
+ struct timer_list btn_check_timer;
+
+ int codec_type;
+ int sysclk;
+ int sysclk_src;
+ int lrck;
+
+ int pll_src;
+ int pll_in;
+ int pll_out;
+
+ int jack_type;
+};
+
+static const struct reg_default rt5668_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, 0x42c1 },
+ { 0x0016, 0x0000 },
+ { 0x0018, 0x000b },
+ { 0x0019, 0xafaf },
+ { 0x001c, 0x2f2f },
+ { 0x001f, 0x0000 },
+ { 0x0022, 0x5757 },
+ { 0x0023, 0x0039 },
+ { 0x0026, 0xc0c0 },
+ { 0x0029, 0x8080 },
+ { 0x002a, 0xa0a0 },
+ { 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, 0x0008 },
+ { 0x0087, 0x0000 },
+ { 0x008a, 0x0000 },
+ { 0x008b, 0x0000 },
+ { 0x008c, 0x0003 },
+ { 0x008e, 0x0004 },
+ { 0x008f, 0x1000 },
+ { 0x0090, 0x0646 },
+ { 0x0091, 0x0e3e },
+ { 0x0092, 0x1071 },
+ { 0x0093, 0x0000 },
+ { 0x0094, 0x0080 },
+ { 0x0097, 0x0000 },
+ { 0x0098, 0x0000 },
+ { 0x009a, 0x0000 },
+ { 0x009f, 0x0000 },
+ { 0x00ae, 0x2000 },
+ { 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, 0x6724 },
+ { 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, 0xa0a0 },
+ { 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, 0x0f00 },
+ { 0x0118, 0x0006 },
+ { 0x0125, 0x2224 },
+ { 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, 0x0020 },
+ { 0x0162, 0x0080 },
+ { 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, 0x0000 },
+ { 0x02fc, 0x0000 },
+ { 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_STA:
+ 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_STA:
+ 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 rt5668_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5663_RESET:
+ case RT5668_CBJ_TYPE_2:
+ case RT5668_PDM_OUT_CTL:
+ case RT5668_PDM_I2C_DATA_CTL1:
+ case RT5668_PDM_I2C_DATA_CTL4:
+ case RT5668_ALC_BK_GAIN:
+ case RT5663_PLL_2:
+ case RT5663_MICBIAS_1:
+ case RT5663_ADC_EQ_1:
+ case RT5663_INT_ST_1:
+ case RT5668_GPIO_STA:
+ case RT5663_IL_CMD_1:
+ case RT5663_IL_CMD_5:
+ case RT5668_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 RT5668_MONO_DYNA_6:
+ case RT5668_STO1_SIL_DET:
+ case RT5668_MONOL_SIL_DET:
+ case RT5668_MONOR_SIL_DET:
+ case RT5668_STO2_DAC_SIL:
+ case RT5668_MONO_AMP_CAL_ST1:
+ case RT5668_MONO_AMP_CAL_ST2:
+ case RT5668_MONO_AMP_CAL_ST3:
+ case RT5668_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 RT5668_HP_CALIB_ST10:
+ case RT5668_HP_CALIB_ST11:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool rt5668_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case RT5668_LOUT_CTRL:
+ case RT5668_HP_AMP_2:
+ case RT5668_MONO_OUT:
+ case RT5668_MONO_GAIN:
+ case RT5668_AEC_BST:
+ case RT5668_IN1_IN2:
+ case RT5668_IN3_IN4:
+ case RT5668_INL1_INR1:
+ case RT5668_CBJ_TYPE_2:
+ case RT5668_CBJ_TYPE_3:
+ case RT5668_CBJ_TYPE_4:
+ case RT5668_CBJ_TYPE_5:
+ case RT5668_CBJ_TYPE_8:
+ case RT5668_DAC3_DIG_VOL:
+ case RT5668_DAC3_CTRL:
+ case RT5668_MONO_ADC_DIG_VOL:
+ case RT5668_STO2_ADC_DIG_VOL:
+ case RT5668_MONO_ADC_BST_GAIN:
+ case RT5668_STO2_ADC_BST_GAIN:
+ case RT5668_SIDETONE_CTRL:
+ case RT5668_MONO1_ADC_MIXER:
+ case RT5668_STO2_ADC_MIXER:
+ case RT5668_MONO_DAC_MIXER:
+ case RT5668_DAC2_SRC_CTRL:
+ case RT5668_IF_3_4_DATA_CTL:
+ case RT5668_IF_5_DATA_CTL:
+ case RT5668_PDM_OUT_CTL:
+ case RT5668_PDM_I2C_DATA_CTL1:
+ case RT5668_PDM_I2C_DATA_CTL2:
+ case RT5668_PDM_I2C_DATA_CTL3:
+ case RT5668_PDM_I2C_DATA_CTL4:
+ case RT5668_RECMIX1_NEW:
+ case RT5668_RECMIX1L_0:
+ case RT5668_RECMIX1L:
+ case RT5668_RECMIX1R_0:
+ case RT5668_RECMIX1R:
+ case RT5668_RECMIX2_NEW:
+ case RT5668_RECMIX2_L_2:
+ case RT5668_RECMIX2_R:
+ case RT5668_RECMIX2_R_2:
+ case RT5668_CALIB_REC_LR:
+ case RT5668_ALC_BK_GAIN:
+ case RT5668_MONOMIX_GAIN:
+ case RT5668_MONOMIX_IN_GAIN:
+ case RT5668_OUT_MIXL_GAIN:
+ case RT5668_OUT_LMIX_IN_GAIN:
+ case RT5668_OUT_RMIX_IN_GAIN:
+ case RT5668_OUT_RMIX_IN_GAIN1:
+ case RT5668_LOUT_MIXER_CTRL:
+ case RT5668_PWR_VOL:
+ case RT5668_ADCDAC_RST:
+ case RT5668_I2S34_SDP:
+ case RT5668_I2S5_SDP:
+ case RT5668_TDM_5:
+ case RT5668_TDM_6:
+ case RT5668_TDM_7:
+ case RT5668_TDM_8:
+ case RT5668_ASRC_3:
+ case RT5668_ASRC_6:
+ case RT5668_ASRC_7:
+ case RT5668_PLL_TRK_13:
+ case RT5668_I2S_M_CLK_CTL:
+ case RT5668_FDIV_I2S34_M_CLK:
+ case RT5668_FDIV_I2S34_M_CLK2:
+ case RT5668_FDIV_I2S5_M_CLK:
+ case RT5668_FDIV_I2S5_M_CLK2:
+ case RT5668_IRQ_4:
+ case RT5668_GPIO_3:
+ case RT5668_GPIO_4:
+ case RT5668_GPIO_STA:
+ case RT5668_HP_AMP_DET1:
+ case RT5668_HP_AMP_DET2:
+ case RT5668_HP_AMP_DET3:
+ case RT5668_MID_BD_HP_AMP:
+ case RT5668_LOW_BD_HP_AMP:
+ case RT5668_SOF_VOL_ZC2:
+ case RT5668_ADC_STO2_ADJ1:
+ case RT5668_ADC_STO2_ADJ2:
+ case RT5668_A_JD_CTRL:
+ case RT5668_JD1_TRES_CTRL:
+ case RT5668_JD2_TRES_CTRL:
+ case RT5668_JD_CTRL2:
+ case RT5668_DUM_REG_2:
+ case RT5668_DUM_REG_3:
+ case RT5663_VENDOR_ID:
+ case RT5663_VENDOR_ID_1:
+ case RT5663_VENDOR_ID_2:
+ case RT5668_DACADC_DIG_VOL2:
+ case RT5668_DIG_IN_PIN2:
+ case RT5668_PAD_DRV_CTL1:
+ case RT5668_SOF_RAM_DEPOP:
+ case RT5668_VOL_TEST:
+ case RT5668_TEST_MODE_3:
+ case RT5668_TEST_MODE_4:
+ case RT5663_STO_DRE_9:
+ case RT5668_MONO_DYNA_1:
+ case RT5668_MONO_DYNA_2:
+ case RT5668_MONO_DYNA_3:
+ case RT5668_MONO_DYNA_4:
+ case RT5668_MONO_DYNA_5:
+ case RT5668_MONO_DYNA_6:
+ case RT5668_STO1_SIL_DET:
+ case RT5668_MONOL_SIL_DET:
+ case RT5668_MONOR_SIL_DET:
+ case RT5668_STO2_DAC_SIL:
+ case RT5668_PWR_SAV_CTL1:
+ case RT5668_PWR_SAV_CTL2:
+ case RT5668_PWR_SAV_CTL3:
+ case RT5668_PWR_SAV_CTL4:
+ case RT5668_PWR_SAV_CTL5:
+ case RT5668_PWR_SAV_CTL6:
+ case RT5668_MONO_AMP_CAL1:
+ case RT5668_MONO_AMP_CAL2:
+ case RT5668_MONO_AMP_CAL3:
+ case RT5668_MONO_AMP_CAL4:
+ case RT5668_MONO_AMP_CAL5:
+ case RT5668_MONO_AMP_CAL6:
+ case RT5668_MONO_AMP_CAL7:
+ case RT5668_MONO_AMP_CAL_ST1:
+ case RT5668_MONO_AMP_CAL_ST2:
+ case RT5668_MONO_AMP_CAL_ST3:
+ case RT5668_MONO_AMP_CAL_ST4:
+ case RT5668_MONO_AMP_CAL_ST5:
+ case RT5668_HP_IMP_SEN_13:
+ case RT5668_HP_IMP_SEN_14:
+ case RT5668_HP_IMP_SEN_6:
+ case RT5668_HP_IMP_SEN_7:
+ case RT5668_HP_IMP_SEN_8:
+ case RT5668_HP_IMP_SEN_9:
+ case RT5668_HP_IMP_SEN_10:
+ case RT5668_HP_LOGIC_3:
+ case RT5668_HP_CALIB_ST10:
+ case RT5668_HP_CALIB_ST11:
+ case RT5668_PRO_REG_TBL_4:
+ case RT5668_PRO_REG_TBL_5:
+ case RT5668_PRO_REG_TBL_6:
+ case RT5668_PRO_REG_TBL_7:
+ case RT5668_PRO_REG_TBL_8:
+ case RT5668_PRO_REG_TBL_9:
+ case RT5668_SAR_ADC_INL_1:
+ case RT5668_SAR_ADC_INL_2:
+ case RT5668_SAR_ADC_INL_3:
+ case RT5668_SAR_ADC_INL_4:
+ case RT5668_SAR_ADC_INL_5:
+ case RT5668_SAR_ADC_INL_6:
+ case RT5668_SAR_ADC_INL_7:
+ case RT5668_SAR_ADC_INL_8:
+ case RT5668_SAR_ADC_INL_9:
+ case RT5668_SAR_ADC_INL_10:
+ case RT5668_SAR_ADC_INL_11:
+ case RT5668_SAR_ADC_INL_12:
+ case RT5668_DRC_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_HD_CTRL_1:
+ case RT5668_DRC1_HD_CTRL_2:
+ case RT5668_DRC1_PRI_REG_1:
+ case RT5668_DRC1_PRI_REG_2:
+ case RT5668_DRC1_PRI_REG_3:
+ case RT5668_DRC1_PRI_REG_4:
+ case RT5668_DRC1_PRI_REG_5:
+ case RT5668_DRC1_PRI_REG_6:
+ case RT5668_DRC1_PRI_REG_7:
+ case RT5668_DRC1_PRI_REG_8:
+ case RT5668_ALC_PGA_CTL_1:
+ case RT5668_ALC_PGA_CTL_2:
+ case RT5668_ALC_PGA_CTL_3:
+ case RT5668_ALC_PGA_CTL_4:
+ case RT5668_ALC_PGA_CTL_5:
+ case RT5668_ALC_PGA_CTL_6:
+ case RT5668_ALC_PGA_CTL_7:
+ case RT5668_ALC_PGA_CTL_8:
+ case RT5668_ALC_PGA_REG_1:
+ case RT5668_ALC_PGA_REG_2:
+ case RT5668_ALC_PGA_REG_3:
+ case RT5668_ADC_EQ_RECOV_1:
+ case RT5668_ADC_EQ_RECOV_2:
+ case RT5668_ADC_EQ_RECOV_3:
+ case RT5668_ADC_EQ_RECOV_4:
+ case RT5668_ADC_EQ_RECOV_5:
+ case RT5668_ADC_EQ_RECOV_6:
+ case RT5668_ADC_EQ_RECOV_7:
+ case RT5668_ADC_EQ_RECOV_8:
+ case RT5668_ADC_EQ_RECOV_9:
+ case RT5668_ADC_EQ_RECOV_10:
+ case RT5668_ADC_EQ_RECOV_11:
+ case RT5668_ADC_EQ_RECOV_12:
+ case RT5668_ADC_EQ_RECOV_13:
+ case RT5668_VID_HIDDEN:
+ case RT5668_VID_CUSTOMER:
+ case RT5668_SCAN_MODE:
+ case RT5668_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_STA:
+ 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(rt5668_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_codec *codec,
+ bool enable)
+{
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+
+ if (enable) {
+ snd_soc_update_bits(codec, RT5663_IL_CMD_6,
+ RT5668_EN_4BTN_INL_MASK, RT5668_EN_4BTN_INL_EN);
+ /* reset in-line command */
+ snd_soc_update_bits(codec, RT5663_IL_CMD_6,
+ RT5668_RESET_4BTN_INL_MASK,
+ RT5668_RESET_4BTN_INL_RESET);
+ snd_soc_update_bits(codec, RT5663_IL_CMD_6,
+ RT5668_RESET_4BTN_INL_MASK,
+ RT5668_RESET_4BTN_INL_NOR);
+ switch (rt5663->codec_type) {
+ case CODEC_TYPE_RT5668:
+ snd_soc_update_bits(codec, RT5663_IRQ_3,
+ RT5668_EN_IRQ_INLINE_MASK,
+ RT5668_EN_IRQ_INLINE_NOR);
+ break;
+ case CODEC_TYPE_RT5663:
+ snd_soc_update_bits(codec, RT5663_IRQ_2,
+ RT5663_EN_IRQ_INLINE_MASK,
+ RT5663_EN_IRQ_INLINE_NOR);
+ break;
+ default:
+ dev_err(codec->dev, "Unknown CODEC_TYPE\n");
+ }
+ } else {
+ switch (rt5663->codec_type) {
+ case CODEC_TYPE_RT5668:
+ snd_soc_update_bits(codec, RT5663_IRQ_3,
+ RT5668_EN_IRQ_INLINE_MASK,
+ RT5668_EN_IRQ_INLINE_BYP);
+ break;
+ case CODEC_TYPE_RT5663:
+ snd_soc_update_bits(codec, RT5663_IRQ_2,
+ RT5663_EN_IRQ_INLINE_MASK,
+ RT5663_EN_IRQ_INLINE_BYP);
+ break;
+ default:
+ dev_err(codec->dev, "Unknown CODEC_TYPE\n");
+ }
+ snd_soc_update_bits(codec, RT5663_IL_CMD_6,
+ RT5668_EN_4BTN_INL_MASK, RT5668_EN_4BTN_INL_DIS);
+ /* reset in-line command */
+ snd_soc_update_bits(codec, RT5663_IL_CMD_6,
+ RT5668_RESET_4BTN_INL_MASK,
+ RT5668_RESET_4BTN_INL_RESET);
+ snd_soc_update_bits(codec, RT5663_IL_CMD_6,
+ RT5668_RESET_4BTN_INL_MASK,
+ RT5668_RESET_4BTN_INL_NOR);
+ }
+}
+
+/**
+ * rt5668_jack_detect - Detect headset.
+ * @codec: SoC audio codec device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+
+static int rt5668_jack_detect(struct snd_soc_codec *codec, int jack_insert)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct rt5663_priv *rt5668 = snd_soc_codec_get_drvdata(codec);
+ int val, i = 0, sleep_time[5] = {300, 150, 100, 50, 30};
+
+ dev_dbg(codec->dev, "%s jack_insert:%d\n", __func__, jack_insert);
+ if (jack_insert) {
+ snd_soc_write(codec, RT5668_CBJ_TYPE_2, 0x8040);
+ snd_soc_write(codec, RT5668_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_update_bits(codec, RT5663_RC_CLK,
+ RT5668_DIG_1M_CLK_MASK, RT5668_DIG_1M_CLK_EN);
+ snd_soc_update_bits(codec, RT5663_RECMIX, 0x8, 0x8);
+
+ while (i < 5) {
+ msleep(sleep_time[i]);
+ val = snd_soc_read(codec, RT5668_CBJ_TYPE_2) & 0x0003;
+ if (val == 0x1 || val == 0x2 || val == 0x3)
+ break;
+ dev_dbg(codec->dev, "%s: MX-0011 val=%x sleep %d\n",
+ __func__, val, sleep_time[i]);
+ i++;
+ }
+ dev_dbg(codec->dev, "%s val = %d\n", __func__, val);
+ switch (val) {
+ case 1:
+ case 2:
+ rt5668->jack_type = SND_JACK_HEADSET;
+ rt5663_enable_push_button_irq(codec, 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);
+ rt5668->jack_type = SND_JACK_HEADPHONE;
+ break;
+ }
+ } else {
+ snd_soc_update_bits(codec, RT5663_RECMIX, 0x8, 0x0);
+
+ if (rt5668->jack_type == SND_JACK_HEADSET) {
+ rt5663_enable_push_button_irq(codec, 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);
+ }
+ rt5668->jack_type = 0;
+ }
+
+ dev_dbg(codec->dev, "jack_type = %d\n", rt5668->jack_type);
+ return rt5668->jack_type;
+}
+
+/**
+ * rt5663_jack_detect - Detect headset.
+ * @codec: SoC audio codec device.
+ * @jack_insert: Jack insert or not.
+ *
+ * Detect whether is headset or not when jack inserted.
+ *
+ * Returns detect status.
+ */
+static int rt5663_jack_detect(struct snd_soc_codec *codec, int jack_insert)
+{
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ int val, i = 0, sleep_time[5] = {300, 150, 100, 50, 30};
+
+ dev_dbg(codec->dev, "%s jack_insert:%d\n", __func__, jack_insert);
+
+ if (jack_insert) {
+ snd_soc_update_bits(codec, RT5663_DIG_MISC,
+ RT5668_DIG_GATE_CTRL_MASK, RT5668_DIG_GATE_CTRL_EN);
+ snd_soc_update_bits(codec, RT5663_HP_CHARGE_PUMP_1,
+ RT5663_SI_HP_MASK | RT5668_OSW_HP_L_MASK |
+ RT5668_OSW_HP_R_MASK, RT5663_SI_HP_EN |
+ RT5668_OSW_HP_L_DIS | RT5668_OSW_HP_R_DIS);
+ snd_soc_update_bits(codec, 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_update_bits(codec, 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_update_bits(codec, RT5663_IL_CMD_2,
+ RT5663_PWR_MIC_DET_MASK, RT5663_PWR_MIC_DET_ON);
+ /* BST1 power on for JD */
+ snd_soc_update_bits(codec, RT5663_PWR_ANLG_2,
+ RT5668_PWR_BST1_MASK, RT5668_PWR_BST1_ON);
+ snd_soc_update_bits(codec, 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_update_bits(codec, RT5663_PWR_ANLG_1,
+ RT5668_PWR_MB_MASK | RT5668_LDO1_DVO_MASK |
+ RT5668_AMP_HP_MASK, RT5668_PWR_MB |
+ RT5668_LDO1_DVO_0_9V | RT5668_AMP_HP_3X);
+ snd_soc_update_bits(codec, RT5663_AUTO_1MRC_CLK,
+ RT5668_IRQ_POW_SAV_MASK, RT5668_IRQ_POW_SAV_EN);
+ snd_soc_update_bits(codec, RT5663_IRQ_1,
+ RT5663_EN_IRQ_JD1_MASK, RT5663_EN_IRQ_JD1_EN);
+ while (i < 5) {
+ msleep(sleep_time[i]);
+ val = snd_soc_read(codec, RT5663_EM_JACK_TYPE_2) &
+ 0x0003;
+ i++;
+ if (val == 0x1 || val == 0x2 || val == 0x3)
+ break;
+ dev_dbg(codec->dev, "%s: MX-00e7 val=%x sleep %d\n",
+ __func__, val, sleep_time[i]);
+ }
+ dev_dbg(codec->dev, "%s val = %d\n", __func__, val);
+ switch (val) {
+ case 1:
+ case 2:
+ rt5663->jack_type = SND_JACK_HEADSET;
+ rt5663_enable_push_button_irq(codec, true);
+ break;
+ default:
+ rt5663->jack_type = SND_JACK_HEADPHONE;
+ break;
+ }
+ } else {
+ if (rt5663->jack_type == SND_JACK_HEADSET)
+ rt5663_enable_push_button_irq(codec, false);
+ rt5663->jack_type = 0;
+ }
+
+ dev_dbg(codec->dev, "jack_type = %d\n", rt5663->jack_type);
+ return rt5663->jack_type;
+}
+
+static int rt5663_button_detect(struct snd_soc_codec *codec)
+{
+ int btn_type, val;
+
+ val = snd_soc_read(codec, RT5663_IL_CMD_5);
+ dev_dbg(codec->dev, "%s: val=0x%x\n", __func__, val);
+ btn_type = val & 0xfff0;
+ snd_soc_write(codec, RT5663_IL_CMD_5, val);
+
+ return btn_type;
+}
+
+static irqreturn_t rt5663_irq(int irq, void *data)
+{
+ struct rt5663_priv *rt5663 = data;
+
+ dev_dbg(rt5663->codec->dev, "%s IRQ queue work\n", __func__);
+
+ queue_delayed_work(system_wq, &rt5663->jack_detect_work,
+ msecs_to_jiffies(250));
+
+ return IRQ_HANDLED;
+}
+
+int rt5663_set_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *hs_jack)
+{
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+
+ rt5663->hs_jack = hs_jack;
+
+ rt5663_irq(0, rt5663);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5663_set_jack_detect);
+
+static bool rt5663_check_jd_status(struct snd_soc_codec *codec)
+{
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ int val = snd_soc_read(codec, RT5663_INT_ST_1);
+
+ dev_dbg(codec->dev, "%s val=%x\n", __func__, val);
+
+ /* JD1 */
+ switch (rt5663->codec_type) {
+ case CODEC_TYPE_RT5668:
+ return !(val & 0x2000);
+ case CODEC_TYPE_RT5663:
+ return !(val & 0x1000);
+ default:
+ dev_err(codec->dev, "Unknown CODEC_TYPE\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_codec *codec = rt5663->codec;
+ int btn_type, report = 0;
+
+ if (!codec)
+ return;
+
+ if (rt5663_check_jd_status(codec)) {
+ /* jack in */
+ if (rt5663->jack_type == 0) {
+ /* jack was out, report jack type */
+ switch (rt5663->codec_type) {
+ case CODEC_TYPE_RT5668:
+ report = rt5668_jack_detect(rt5663->codec, 1);
+ break;
+ case CODEC_TYPE_RT5663:
+ report = rt5663_jack_detect(rt5663->codec, 1);
+ break;
+ default:
+ dev_err(codec->dev, "Unknown CODEC_TYPE\n");
+ }
+ } else {
+ /* jack is already in, report button event */
+ report = SND_JACK_HEADSET;
+ btn_type = rt5663_button_detect(rt5663->codec);
+ /**
+ * 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->codec->dev,
+ "Unexpected button code 0x%04x\n",
+ btn_type);
+ break;
+ }
+ /* button release or spurious interrput*/
+ if (btn_type == 0)
+ report = rt5663->jack_type;
+ }
+ } else {
+ /* jack out */
+ switch (rt5663->codec_type) {
+ case CODEC_TYPE_RT5668:
+ report = rt5668_jack_detect(rt5663->codec, 0);
+ break;
+ case CODEC_TYPE_RT5663:
+ report = rt5663_jack_detect(rt5663->codec, 0);
+ break;
+ default:
+ dev_err(codec->dev, "Unknown CODEC_TYPE\n");
+ }
+ }
+ dev_dbg(codec->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 const struct snd_kcontrol_new rt5663_snd_controls[] = {
+ /* DAC Digital Volume */
+ SOC_DOUBLE_TLV("DAC Playback Volume", RT5663_STO1_DAC_DIG_VOL,
+ RT5668_DAC_L1_VOL_SHIFT + 1, RT5668_DAC_R1_VOL_SHIFT + 1,
+ 87, 0, dac_vol_tlv),
+ /* ADC Digital Volume Control */
+ SOC_DOUBLE("ADC Capture Switch", RT5663_STO1_ADC_DIG_VOL,
+ RT5668_ADC_L_MUTE_SHIFT, RT5668_ADC_R_MUTE_SHIFT, 1, 1),
+ SOC_DOUBLE_TLV("ADC Capture Volume", RT5663_STO1_ADC_DIG_VOL,
+ RT5668_ADC_L_VOL_SHIFT + 1, RT5668_ADC_R_VOL_SHIFT + 1,
+ 63, 0, adc_vol_tlv),
+};
+
+static const struct snd_kcontrol_new rt5668_specific_controls[] = {
+ /* Headphone Output Volume */
+ SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5663_HP_LCH_DRE,
+ RT5663_HP_RCH_DRE, RT5668_GAIN_HP_SHIFT, 15, 1,
+ rt5668_hp_vol_tlv),
+ /* Mic Boost Volume */
+ SOC_SINGLE_TLV("IN1 Capture Volume", RT5668_AEC_BST,
+ RT5668_GAIN_CBJ_SHIFT, 8, 0, in_bst_tlv),
+};
+
+static const struct snd_kcontrol_new rt5663_specific_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),
+ /* 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 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ val = snd_soc_read(codec, 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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+
+ if (rt5663->codec_type == CODEC_TYPE_RT5668) {
+ switch (w->shift) {
+ case RT5668_ADC_STO1_ASRC_SHIFT:
+ reg = RT5668_ASRC_3;
+ shift = RT5668_AD_STO1_TRACK_SHIFT;
+ break;
+ case RT5668_DAC_STO1_ASRC_SHIFT:
+ reg = RT5663_ASRC_2;
+ shift = RT5668_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_read(codec, 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_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ int da_asrc_en, ad_asrc_en;
+
+ da_asrc_en = (snd_soc_read(codec, RT5663_ASRC_2) &
+ RT5663_DA_STO1_TRACK_MASK) ? 1 : 0;
+ switch (rt5663->codec_type) {
+ case CODEC_TYPE_RT5668:
+ ad_asrc_en = (snd_soc_read(codec, RT5668_ASRC_3) &
+ RT5668_AD_STO1_TRACK_MASK) ? 1 : 0;
+ break;
+ case CODEC_TYPE_RT5663:
+ ad_asrc_en = (snd_soc_read(codec, RT5663_ASRC_2) &
+ RT5663_AD_STO1_TRACK_MASK) ? 1 : 0;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown CODEC_TYPE\n");
+ return 1;
+ }
+
+ if (da_asrc_en || ad_asrc_en)
+ if (rt5663->sysclk > rt5663->lrck * 384)
+ return 1;
+
+ dev_err(codec->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
+ * @codec: SoC audio codec device.
+ * @filter_mask: mask of filters.
+ * @clk_src: clock source
+ *
+ * The ASRC function is for asynchronous MCLK and LRCK. Also, since RT5668 can
+ * only support standard 32fs or 64fs i2s format, ASRC should be enabled to
+ * support special i2s clock format such as Intel's 100fs(100 * sampling rate).
+ * ASRC function will track i2s clock and generate a corresponding system clock
+ * for codec. This function provides an API to select the clock source for a
+ * set of filters specified by the mask. And the 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_codec *codec,
+ unsigned int filter_mask, unsigned int clk_src)
+{
+ struct rt5663_priv *rt5668 = snd_soc_codec_get_drvdata(codec);
+ 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 |= RT5668_DA_STO1_TRACK_MASK;
+ asrc2_value |= clk_src << RT5668_DA_STO1_TRACK_SHIFT;
+ }
+
+ if (filter_mask & RT5663_AD_STEREO_FILTER) {
+ switch (rt5668->codec_type) {
+ case CODEC_TYPE_RT5668:
+ asrc3_mask |= RT5668_AD_STO1_TRACK_MASK;
+ asrc3_value |= clk_src << RT5668_AD_STO1_TRACK_SHIFT;
+ break;
+ case CODEC_TYPE_RT5663:
+ asrc2_mask |= RT5663_AD_STO1_TRACK_MASK;
+ asrc2_value |= clk_src << RT5663_AD_STO1_TRACK_SHIFT;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown CODEC_TYPE\n");
+ }
+ }
+
+ if (asrc2_mask)
+ snd_soc_update_bits(codec, RT5663_ASRC_2, asrc2_mask,
+ asrc2_value);
+
+ if (asrc3_mask)
+ snd_soc_update_bits(codec, RT5668_ASRC_3, asrc3_mask,
+ asrc3_value);
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(rt5663_sel_asrc_clk_src);
+
+/* Analog Mixer */
+static const struct snd_kcontrol_new rt5668_recmix1l[] = {
+ SOC_DAPM_SINGLE("BST2 Switch", RT5668_RECMIX1L,
+ RT5668_RECMIX1L_BST2_SHIFT, 1, 1),
+ SOC_DAPM_SINGLE("BST1 CBJ Switch", RT5668_RECMIX1L,
+ RT5668_RECMIX1L_BST1_CBJ_SHIFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5668_recmix1r[] = {
+ SOC_DAPM_SINGLE("BST2 Switch", RT5668_RECMIX1R,
+ RT5668_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,
+ RT5668_M_STO1_ADC_L1_SHIFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5663_STO1_ADC_MIXER,
+ RT5668_M_STO1_ADC_L2_SHIFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5668_sto1_adc_r_mix[] = {
+ SOC_DAPM_SINGLE("ADC1 Switch", RT5663_STO1_ADC_MIXER,
+ RT5668_M_STO1_ADC_R1_SHIFT, 1, 1),
+ SOC_DAPM_SINGLE("ADC2 Switch", RT5663_STO1_ADC_MIXER,
+ RT5668_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,
+ RT5668_M_ADCMIX_L_SHIFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC L Switch", RT5663_AD_DA_MIXER,
+ RT5668_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,
+ RT5668_M_ADCMIX_R_SHIFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R Switch", RT5663_AD_DA_MIXER,
+ RT5668_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,
+ RT5668_M_DAC_L1_STO_L_SHIFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R Switch", RT5663_STO_DAC_MIXER,
+ RT5668_M_DAC_R1_STO_L_SHIFT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5663_sto1_dac_r_mix[] = {
+ SOC_DAPM_SINGLE("DAC L Switch", RT5663_STO_DAC_MIXER,
+ RT5668_M_DAC_L1_STO_R_SHIFT, 1, 1),
+ SOC_DAPM_SINGLE("DAC R Switch", RT5663_STO_DAC_MIXER,
+ RT5668_M_DAC_R1_STO_R_SHIFT, 1, 1),
+};
+
+/* Out Switch */
+static const struct snd_kcontrol_new rt5668_hpo_switch =
+ SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5668_HP_AMP_2,
+ RT5668_EN_DAC_HPO_SHIFT, 1, 0);
+
+/* Stereo ADC source */
+static const char * const rt5668_sto1_adc_src[] = {
+ "ADC L", "ADC R"
+};
+
+static SOC_ENUM_SINGLE_DECL(rt5668_sto1_adcl_enum, RT5663_STO1_ADC_MIXER,
+ RT5668_STO1_ADC_L_SRC_SHIFT, rt5668_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adcl_mux =
+ SOC_DAPM_ENUM("STO1 ADC L Mux", rt5668_sto1_adcl_enum);
+
+static SOC_ENUM_SINGLE_DECL(rt5668_sto1_adcr_enum, RT5663_STO1_ADC_MIXER,
+ RT5668_STO1_ADC_R_SRC_SHIFT, rt5668_sto1_adc_src);
+
+static const struct snd_kcontrol_new rt5668_sto1_adcr_mux =
+ SOC_DAPM_ENUM("STO1 ADC R Mux", rt5668_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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ if (rt5663->codec_type == CODEC_TYPE_RT5668) {
+ snd_soc_update_bits(codec, RT5663_HP_CHARGE_PUMP_1,
+ RT5668_SEL_PM_HP_SHIFT, RT5668_SEL_PM_HP_HIGH);
+ snd_soc_update_bits(codec, RT5663_HP_LOGIC_2,
+ RT5668_HP_SIG_SRC1_MASK,
+ RT5668_HP_SIG_SRC1_SILENCE);
+ } else {
+ snd_soc_write(codec, RT5663_DEPOP_2, 0x3003);
+ snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x000b,
+ 0x000b);
+ snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030,
+ 0x0030);
+ snd_soc_update_bits(codec, RT5663_HP_CHARGE_PUMP_1,
+ RT5668_OVCD_HP_MASK, RT5668_OVCD_HP_DIS);
+ snd_soc_write(codec, RT5663_HP_CHARGE_PUMP_2, 0x1371);
+ snd_soc_write(codec, RT5663_HP_BIAS, 0xabba);
+ snd_soc_write(codec, RT5663_CHARGE_PUMP_1, 0x2224);
+ snd_soc_write(codec, RT5663_ANA_BIAS_CUR_1, 0x7766);
+ snd_soc_write(codec, RT5663_HP_BIAS, 0xafaa);
+ snd_soc_write(codec, RT5663_CHARGE_PUMP_2, 0x7777);
+ snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000,
+ 0x3000);
+ }
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ if (rt5663->codec_type == CODEC_TYPE_RT5668) {
+ snd_soc_update_bits(codec, RT5663_HP_LOGIC_2,
+ RT5668_HP_SIG_SRC1_MASK,
+ RT5668_HP_SIG_SRC1_REG);
+ } else {
+ snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x3000, 0x0);
+ snd_soc_update_bits(codec, RT5663_HP_CHARGE_PUMP_1,
+ RT5668_OVCD_HP_MASK, RT5668_OVCD_HP_EN);
+ snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x0030, 0x0);
+ snd_soc_update_bits(codec, RT5663_DEPOP_1, 0x000b,
+ 0x000b);
+ }
+ break;
+
+ default:
+ return 0;
+ }
+
+ return 0;
+}
+
+static int rt5668_bst2_power(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_update_bits(codec, RT5663_PWR_ANLG_2,
+ RT5668_PWR_BST2_MASK | RT5668_PWR_BST2_OP_MASK,
+ RT5668_PWR_BST2 | RT5668_PWR_BST2_OP);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_update_bits(codec, RT5663_PWR_ANLG_2,
+ RT5668_PWR_BST2_MASK | RT5668_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_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ snd_soc_write(codec, RT5663_PRE_DIV_GATING_1, 0xff00);
+ snd_soc_write(codec, RT5663_PRE_DIV_GATING_2, 0xfffc);
+ break;
+
+ case SND_SOC_DAPM_PRE_PMD:
+ snd_soc_write(codec, RT5663_PRE_DIV_GATING_1, 0x0000);
+ snd_soc_write(codec, 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, RT5668_PWR_PLL_SHIFT, 0,
+ NULL, 0),
+
+ /* micbias */
+ SND_SOC_DAPM_MICBIAS("MICBIAS1", RT5663_PWR_ANLG_2,
+ RT5668_PWR_MB1_SHIFT, 0),
+ SND_SOC_DAPM_MICBIAS("MICBIAS2", RT5663_PWR_ANLG_2,
+ RT5668_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,
+ RT5668_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,
+ RT5668_PWR_ADC_L1_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC Clock", RT5663_CHOP_ADC,
+ RT5668_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,
+ RT5668_PWR_ADC_S1F_SHIFT, 0, NULL, 0),
+
+ /* Digital Interface */
+ SND_SOC_DAPM_SUPPLY("I2S", RT5663_PWR_DIG_1, RT5668_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,
+ RT5668_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,
+ RT5668_PWR_DAC_L1_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("STO1 DAC R Power", RT5663_PWR_DIG_1,
+ RT5668_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_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 rt5668_specific_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("LDO2", RT5663_PWR_ANLG_3,
+ RT5668_PWR_LDO2_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5668_PWR_VOL,
+ RT5668_PWR_MIC_DET_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("LDO DAC", RT5663_PWR_DIG_1,
+ RT5668_PWR_LDO_DACREF_SHIFT, 0, NULL, 0),
+
+ /* ASRC */
+ SND_SOC_DAPM_SUPPLY("I2S ASRC", RT5663_ASRC_1,
+ RT5668_I2S1_ASRC_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DAC ASRC", RT5663_ASRC_1,
+ RT5668_DAC_STO1_ASRC_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("ADC ASRC", RT5663_ASRC_1,
+ RT5668_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,
+ RT5668_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,
+ rt5668_bst2_power, SND_SOC_DAPM_PRE_PMD |
+ SND_SOC_DAPM_POST_PMU),
+
+ /* REC Mixer */
+ SND_SOC_DAPM_MIXER("RECMIX1L", SND_SOC_NOPM, 0, 0, rt5668_recmix1l,
+ ARRAY_SIZE(rt5668_recmix1l)),
+ SND_SOC_DAPM_MIXER("RECMIX1R", SND_SOC_NOPM, 0, 0, rt5668_recmix1r,
+ ARRAY_SIZE(rt5668_recmix1r)),
+ SND_SOC_DAPM_SUPPLY("RECMIX1R Power", RT5663_PWR_ANLG_2,
+ RT5668_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,
+ RT5668_PWR_ADC_R1_SHIFT, 0, NULL, 0),
+
+ /* ADC Mux */
+ SND_SOC_DAPM_PGA("STO1 ADC L1", RT5663_STO1_ADC_MIXER,
+ RT5668_STO1_ADC_L1_SRC_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("STO1 ADC R1", RT5663_STO1_ADC_MIXER,
+ RT5668_STO1_ADC_R1_SRC_SHIFT, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("STO1 ADC L2", RT5663_STO1_ADC_MIXER,
+ RT5668_STO1_ADC_L2_SRC_SHIFT, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("STO1 ADC R2", RT5663_STO1_ADC_MIXER,
+ RT5668_STO1_ADC_R2_SRC_SHIFT, 1, NULL, 0),
+
+ SND_SOC_DAPM_MUX("STO1 ADC L Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adcl_mux),
+ SND_SOC_DAPM_MUX("STO1 ADC R Mux", SND_SOC_NOPM, 0, 0,
+ &rt5668_sto1_adcr_mux),
+
+ /* ADC Mix */
+ SND_SOC_DAPM_MIXER("STO1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+ rt5668_sto1_adc_r_mix, ARRAY_SIZE(rt5668_sto1_adc_r_mix)),
+
+ /* Analog DAC Clock */
+ SND_SOC_DAPM_SUPPLY("DAC Clock", RT5663_CHOP_DAC_L,
+ RT5668_CKGEN_DAC1_SHIFT, 0, NULL, 0),
+
+ /* Headphone out */
+ SND_SOC_DAPM_SWITCH("HPO Playback", SND_SOC_NOPM, 0, 0,
+ &rt5668_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,
+ RT5668_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", "DAC R Switch", "DAC R1" },
+ { "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", "DAC L Switch", "DAC L1" },
+ { "STO1 DAC MIXR", NULL, "STO1 DAC R Power" },
+ { "STO1 DAC MIXR", NULL, "STO1 DAC Filter" },
+
+ { "HP Amp", NULL, "DAC L" },
+ { "HP Amp", NULL, "DAC R" },
+};
+
+static const struct snd_soc_dapm_route rt5668_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_codec *codec = dai->codec;
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->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 = RT5668_I2S_DL_8;
+ break;
+ case 16:
+ val_len = RT5668_I2S_DL_16;
+ break;
+ case 20:
+ val_len = RT5668_I2S_DL_20;
+ break;
+ case 24:
+ val_len = RT5668_I2S_DL_24;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, RT5663_I2S1_SDP,
+ RT5668_I2S_DL_MASK, val_len);
+
+ snd_soc_update_bits(codec, RT5663_ADDA_CLK_1,
+ RT5668_I2S_PD1_MASK, pre_div << RT5668_I2S_PD1_SHIFT);
+
+ return 0;
+}
+
+static int rt5663_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int reg_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ reg_val |= RT5668_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 |= RT5668_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 |= RT5668_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ reg_val |= RT5668_I2S_DF_PCM_A;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ reg_val |= RT5668_I2S_DF_PCM_B;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, RT5663_I2S1_SDP, RT5668_I2S_MS_MASK |
+ RT5668_I2S_BP_MASK | RT5668_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_codec *codec = dai->codec;
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, RT5663_GLB_CLK, RT5668_SCLK_SRC_MASK,
+ reg_val);
+ rt5663->sysclk = freq;
+ rt5663->sysclk_src = clk_id;
+
+ dev_dbg(codec->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_codec *codec = dai->codec;
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ 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(codec->dev, "PLL disabled\n");
+
+ rt5663->pll_in = 0;
+ rt5663->pll_out = 0;
+ snd_soc_update_bits(codec, RT5663_GLB_CLK,
+ RT5663_SCLK_SRC_MASK, RT5663_SCLK_SRC_MCLK);
+ return 0;
+ }
+
+ switch (rt5663->codec_type) {
+ case CODEC_TYPE_RT5668:
+ mask = RT5668_PLL1_SRC_MASK;
+ shift = RT5668_PLL1_SRC_SHIFT;
+ break;
+ case CODEC_TYPE_RT5663:
+ mask = RT5663_PLL1_SRC_MASK;
+ shift = RT5663_PLL1_SRC_SHIFT;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown CODEC_TYPE\n");
+ return -EINVAL;
+ }
+
+ switch (source) {
+ case RT5663_PLL1_S_MCLK:
+ val = 0x0;
+ break;
+ case RT5663_PLL1_S_BCLK1:
+ val = 0x1;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown PLL source %d\n", source);
+ return -EINVAL;
+ }
+ snd_soc_update_bits(codec, RT5663_GLB_CLK, mask, (val << shift));
+
+ ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+ if (ret < 0) {
+ dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+ return ret;
+ }
+
+ dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n", pll_code.m_bp,
+ (pll_code.m_bp ? 0 : pll_code.m_code), pll_code.n_code,
+ pll_code.k_code);
+
+ snd_soc_write(codec, RT5663_PLL_1,
+ pll_code.n_code << RT5668_PLL_N_SHIFT | pll_code.k_code);
+ snd_soc_write(codec, RT5663_PLL_2,
+ (pll_code.m_bp ? 0 : pll_code.m_code) << RT5668_PLL_M_SHIFT |
+ pll_code.m_bp << RT5668_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_codec *codec = dai->codec;
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val = 0, reg;
+
+ if (rx_mask || tx_mask)
+ val |= RT5668_TDM_MODE_TDM;
+
+ switch (slots) {
+ case 4:
+ val |= RT5668_TDM_IN_CH_4;
+ val |= RT5668_TDM_OUT_CH_4;
+ break;
+ case 6:
+ val |= RT5668_TDM_IN_CH_6;
+ val |= RT5668_TDM_OUT_CH_6;
+ break;
+ case 8:
+ val |= RT5668_TDM_IN_CH_8;
+ val |= RT5668_TDM_OUT_CH_8;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width) {
+ case 20:
+ val |= RT5668_TDM_IN_LEN_20;
+ val |= RT5668_TDM_OUT_LEN_20;
+ break;
+ case 24:
+ val |= RT5668_TDM_IN_LEN_24;
+ val |= RT5668_TDM_OUT_LEN_24;
+ break;
+ case 32:
+ val |= RT5668_TDM_IN_LEN_32;
+ val |= RT5668_TDM_OUT_LEN_32;
+ break;
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (rt5663->codec_type) {
+ case CODEC_TYPE_RT5668:
+ reg = RT5663_TDM_2;
+ break;
+ case CODEC_TYPE_RT5663:
+ reg = RT5663_TDM_1;
+ break;
+ default:
+ dev_err(codec->dev, "Unknown CODEC_TYPE\n");
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, reg, RT5668_TDM_MODE_MASK |
+ RT5668_TDM_IN_CH_MASK | RT5668_TDM_OUT_CH_MASK |
+ RT5668_TDM_IN_LEN_MASK | RT5668_TDM_OUT_LEN_MASK, val);
+
+ return 0;
+}
+
+static int rt5663_set_bclk_ratio(struct snd_soc_dai *dai, unsigned int ratio)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg;
+
+ dev_dbg(codec->dev, "%s ratio = %d\n", __func__, ratio);
+
+ if (rt5663->codec_type == CODEC_TYPE_RT5668)
+ reg = RT5668_TDM_8;
+ else
+ reg = RT5663_TDM_5;
+
+ switch (ratio) {
+ case 32:
+ snd_soc_update_bits(codec, reg,
+ RT5663_TDM_LENGTN_MASK,
+ RT5663_TDM_LENGTN_16);
+ break;
+ case 40:
+ snd_soc_update_bits(codec, reg,
+ RT5663_TDM_LENGTN_MASK,
+ RT5663_TDM_LENGTN_20);
+ break;
+ case 48:
+ snd_soc_update_bits(codec, reg,
+ RT5663_TDM_LENGTN_MASK,
+ RT5663_TDM_LENGTN_24);
+ break;
+ case 64:
+ snd_soc_update_bits(codec, reg,
+ RT5663_TDM_LENGTN_MASK,
+ RT5663_TDM_LENGTN_32);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid ratio!\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int rt5663_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ snd_soc_update_bits(codec, RT5663_PWR_ANLG_1,
+ RT5668_PWR_FV1_MASK | RT5668_PWR_FV2_MASK,
+ RT5668_PWR_FV1 | RT5668_PWR_FV2);
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ if (rt5663->codec_type == CODEC_TYPE_RT5668) {
+ snd_soc_update_bits(codec, RT5663_DIG_MISC,
+ RT5668_DIG_GATE_CTRL_MASK,
+ RT5668_DIG_GATE_CTRL_EN);
+ snd_soc_update_bits(codec, RT5663_SIG_CLK_DET,
+ RT5668_EN_ANA_CLK_DET_MASK |
+ RT5668_PWR_CLK_DET_MASK,
+ RT5668_EN_ANA_CLK_DET_AUTO |
+ RT5668_PWR_CLK_DET_EN);
+ }
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (rt5663->codec_type == CODEC_TYPE_RT5668)
+ snd_soc_update_bits(codec, RT5663_DIG_MISC,
+ RT5668_DIG_GATE_CTRL_MASK,
+ RT5668_DIG_GATE_CTRL_DIS);
+ snd_soc_update_bits(codec, RT5663_PWR_ANLG_1,
+ RT5668_PWR_VREF1_MASK | RT5668_PWR_VREF2_MASK |
+ RT5668_PWR_FV1_MASK | RT5668_PWR_FV2_MASK |
+ RT5668_PWR_MB_MASK, RT5668_PWR_VREF1 |
+ RT5668_PWR_VREF2 | RT5668_PWR_MB);
+ usleep_range(10000, 10005);
+ if (rt5663->codec_type == CODEC_TYPE_RT5668) {
+ snd_soc_update_bits(codec, RT5663_SIG_CLK_DET,
+ RT5668_EN_ANA_CLK_DET_MASK |
+ RT5668_PWR_CLK_DET_MASK,
+ RT5668_EN_ANA_CLK_DET_DIS |
+ RT5668_PWR_CLK_DET_DIS);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ snd_soc_update_bits(codec, RT5663_PWR_ANLG_1,
+ RT5668_PWR_VREF1_MASK | RT5668_PWR_VREF2_MASK |
+ RT5668_PWR_FV1 | RT5668_PWR_FV2, 0x0);
+ break;
+
+ default:
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5663_probe(struct snd_soc_codec *codec)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+
+ rt5663->codec = codec;
+
+ switch (rt5663->codec_type) {
+ case CODEC_TYPE_RT5668:
+ snd_soc_dapm_new_controls(dapm,
+ rt5668_specific_dapm_widgets,
+ ARRAY_SIZE(rt5668_specific_dapm_widgets));
+ snd_soc_dapm_add_routes(dapm,
+ rt5668_specific_dapm_routes,
+ ARRAY_SIZE(rt5668_specific_dapm_routes));
+ snd_soc_add_codec_controls(codec, rt5668_specific_controls,
+ ARRAY_SIZE(rt5668_specific_controls));
+ break;
+ case CODEC_TYPE_RT5663:
+ 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_codec_controls(codec, rt5663_specific_controls,
+ ARRAY_SIZE(rt5663_specific_controls));
+ break;
+ }
+
+ return 0;
+}
+
+static int rt5663_remove(struct snd_soc_codec *codec)
+{
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+
+ regmap_write(rt5663->regmap, RT5663_RESET, 0);
+
+ return 0;
+}
+
+#ifdef CONFIG_PM
+static int rt5663_suspend(struct snd_soc_codec *codec)
+{
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt5663->regmap, true);
+ regcache_mark_dirty(rt5663->regmap);
+
+ return 0;
+}
+
+static int rt5663_resume(struct snd_soc_codec *codec)
+{
+ struct rt5663_priv *rt5663 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(rt5663->regmap, false);
+ regcache_sync(rt5663->regmap);
+
+ 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 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 struct snd_soc_codec_driver soc_codec_dev_rt5663 = {
+ .probe = rt5663_probe,
+ .remove = rt5663_remove,
+ .suspend = rt5663_suspend,
+ .resume = rt5663_resume,
+ .set_bias_level = rt5663_set_bias_level,
+ .idle_bias_off = true,
+ .component_driver = {
+ .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),
+ }
+};
+
+static const struct regmap_config rt5668_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .use_single_rw = true,
+ .max_register = 0x07fa,
+ .volatile_reg = rt5668_volatile_register,
+ .readable_reg = rt5668_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = rt5668_reg,
+ .num_reg_defaults = ARRAY_SIZE(rt5668_reg),
+};
+
+static const struct regmap_config rt5663_regmap = {
+ .reg_bits = 16,
+ .val_bits = 16,
+ .use_single_rw = true,
+ .max_register = 0x03f3,
+ .volatile_reg = rt5663_volatile_register,
+ .readable_reg = rt5663_readable_register,
+ .cache_type = REGCACHE_RBTREE,
+ .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_rw = true,
+ .max_register = 0x03f3,
+ .cache_type = REGCACHE_NONE,
+};
+
+static const struct i2c_device_id rt5663_i2c_id[] = {
+ { "rt5668", 0 },
+ { "rt5663", 0 },
+ {}
+};
+MODULE_DEVICE_TABLE(i2c, rt5663_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt5663_of_match[] = {
+ { .compatible = "realtek,rt5668", },
+ { .compatible = "realtek,rt5663", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, rt5663_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static struct acpi_device_id rt5663_acpi_match[] = {
+ { "10EC5668", 0},
+ { "10EC5663", 0},
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, rt5663_acpi_match);
+#endif
+
+static void rt5668_calibrate(struct rt5663_priv *rt5668)
+{
+ regmap_write(rt5668->regmap, RT5663_BIAS_CUR_8, 0xa402);
+ regmap_write(rt5668->regmap, RT5663_PWR_DIG_1, 0x0100);
+ regmap_write(rt5668->regmap, RT5663_RECMIX, 0x4040);
+ regmap_write(rt5668->regmap, RT5663_DIG_MISC, 0x0001);
+ regmap_write(rt5668->regmap, RT5663_RC_CLK, 0x0380);
+ regmap_write(rt5668->regmap, RT5663_GLB_CLK, 0x8000);
+ regmap_write(rt5668->regmap, RT5663_ADDA_CLK_1, 0x1000);
+ regmap_write(rt5668->regmap, RT5663_CHOP_DAC_L, 0x3030);
+ regmap_write(rt5668->regmap, RT5663_CALIB_ADC, 0x3c05);
+ regmap_write(rt5668->regmap, RT5663_PWR_ANLG_1, 0xa23e);
+ msleep(40);
+ regmap_write(rt5668->regmap, RT5663_PWR_ANLG_1, 0xf23e);
+ regmap_write(rt5668->regmap, RT5663_HP_CALIB_2, 0x0321);
+ regmap_write(rt5668->regmap, RT5663_HP_CALIB_1, 0xfc00);
+ msleep(500);
+}
+
+static void rt5663_calibrate(struct rt5663_priv *rt5668)
+{
+ int value, count;
+
+ regmap_write(rt5668->regmap, RT5663_RC_CLK, 0x0280);
+ regmap_write(rt5668->regmap, RT5663_GLB_CLK, 0x8000);
+ regmap_write(rt5668->regmap, RT5663_DIG_MISC, 0x8001);
+ regmap_write(rt5668->regmap, RT5663_VREF_RECMIX, 0x0032);
+ regmap_write(rt5668->regmap, RT5663_PWR_ANLG_1, 0xa2be);
+ msleep(20);
+ regmap_write(rt5668->regmap, RT5663_PWR_ANLG_1, 0xf2be);
+ regmap_write(rt5668->regmap, RT5663_PWR_DIG_2, 0x8400);
+ regmap_write(rt5668->regmap, RT5663_CHOP_ADC, 0x3000);
+ regmap_write(rt5668->regmap, RT5663_DEPOP_1, 0x003b);
+ regmap_write(rt5668->regmap, RT5663_PWR_DIG_1, 0x8df8);
+ regmap_write(rt5668->regmap, RT5663_PWR_ANLG_2, 0x0003);
+ regmap_write(rt5668->regmap, RT5663_PWR_ANLG_3, 0x018c);
+ regmap_write(rt5668->regmap, RT5663_ADDA_CLK_1, 0x1111);
+ regmap_write(rt5668->regmap, RT5663_PRE_DIV_GATING_1, 0xffff);
+ regmap_write(rt5668->regmap, RT5663_PRE_DIV_GATING_2, 0xffff);
+ regmap_write(rt5668->regmap, RT5663_DEPOP_2, 0x3003);
+ regmap_write(rt5668->regmap, RT5663_DEPOP_1, 0x003b);
+ regmap_write(rt5668->regmap, RT5663_HP_CHARGE_PUMP_1, 0x1e32);
+ regmap_write(rt5668->regmap, RT5663_HP_CHARGE_PUMP_2, 0x1371);
+ regmap_write(rt5668->regmap, RT5663_DACREF_LDO, 0x3b0b);
+ regmap_write(rt5668->regmap, RT5663_STO_DAC_MIXER, 0x2080);
+ regmap_write(rt5668->regmap, RT5663_BYPASS_STO_DAC, 0x000c);
+ regmap_write(rt5668->regmap, RT5663_HP_BIAS, 0xabba);
+ regmap_write(rt5668->regmap, RT5663_CHARGE_PUMP_1, 0x2224);
+ regmap_write(rt5668->regmap, RT5663_HP_OUT_EN, 0x8088);
+ regmap_write(rt5668->regmap, RT5663_STO_DRE_9, 0x0017);
+ regmap_write(rt5668->regmap, RT5663_STO_DRE_10, 0x0017);
+ regmap_write(rt5668->regmap, RT5663_STO1_ADC_MIXER, 0x4040);
+ regmap_write(rt5668->regmap, RT5663_RECMIX, 0x0005);
+ regmap_write(rt5668->regmap, RT5663_ADDA_RST, 0xc000);
+ regmap_write(rt5668->regmap, RT5663_STO1_HPF_ADJ1, 0x3320);
+ regmap_write(rt5668->regmap, RT5663_HP_CALIB_2, 0x00c9);
+ regmap_write(rt5668->regmap, RT5663_DUMMY_1, 0x004c);
+ regmap_write(rt5668->regmap, RT5663_ANA_BIAS_CUR_1, 0x7766);
+ regmap_write(rt5668->regmap, RT5663_BIAS_CUR_8, 0x4702);
+ msleep(200);
+ regmap_write(rt5668->regmap, RT5663_HP_CALIB_1, 0x0069);
+ regmap_write(rt5668->regmap, RT5663_HP_CALIB_3, 0x06c2);
+ regmap_write(rt5668->regmap, RT5663_HP_CALIB_1_1, 0x7b00);
+ regmap_write(rt5668->regmap, RT5663_HP_CALIB_1_1, 0xfb00);
+ count = 0;
+ while (true) {
+ regmap_read(rt5668->regmap, RT5663_HP_CALIB_1_1, &value);
+ if (value & 0x8000)
+ usleep_range(10000, 10005);
+ else
+ break;
+
+ if (count > 200)
+ return;
+ count++;
+ }
+}
+
+static int rt5663_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct rt5663_priv *rt5663;
+ int ret;
+ 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);
+
+ 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);
+ return ret;
+ }
+ regmap_read(regmap, RT5663_VENDOR_ID_2, &val);
+ switch (val) {
+ case RT5668_DEVICE_ID:
+ rt5663->regmap = devm_regmap_init_i2c(i2c, &rt5668_regmap);
+ rt5663->codec_type = CODEC_TYPE_RT5668;
+ break;
+ case RT5663_DEVICE_ID:
+ rt5663->regmap = devm_regmap_init_i2c(i2c, &rt5663_regmap);
+ rt5663->codec_type = CODEC_TYPE_RT5663;
+ break;
+ default:
+ dev_err(&i2c->dev,
+ "Device with ID register %#x is not rt5663 or rt5668\n",
+ val);
+ return -ENODEV;
+ }
+
+ if (IS_ERR(rt5663->regmap)) {
+ ret = PTR_ERR(rt5663->regmap);
+ dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+ ret);
+ return ret;
+ }
+
+ /* reset and calibrate */
+ regmap_write(rt5663->regmap, RT5663_RESET, 0);
+ regcache_cache_bypass(rt5663->regmap, true);
+ switch (rt5663->codec_type) {
+ case CODEC_TYPE_RT5668:
+ rt5668_calibrate(rt5663);
+ break;
+ case CODEC_TYPE_RT5663:
+ 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");
+
+ /* GPIO1 as IRQ */
+ regmap_update_bits(rt5663->regmap, RT5663_GPIO_1, RT5668_GP1_PIN_MASK,
+ RT5668_GP1_PIN_IRQ);
+ /* 4btn inline command debounce */
+ regmap_update_bits(rt5663->regmap, RT5663_IL_CMD_5,
+ RT5668_4BTN_CLK_DEB_MASK, RT5668_4BTN_CLK_DEB_65MS);
+
+ switch (rt5663->codec_type) {
+ case CODEC_TYPE_RT5668:
+ regmap_write(rt5663->regmap, RT5663_BIAS_CUR_8, 0xa402);
+ /* JD1 */
+ regmap_update_bits(rt5663->regmap, RT5663_AUTO_1MRC_CLK,
+ RT5668_IRQ_POW_SAV_MASK | RT5668_IRQ_POW_SAV_JD1_MASK,
+ RT5668_IRQ_POW_SAV_EN | RT5668_IRQ_POW_SAV_JD1_EN);
+ regmap_update_bits(rt5663->regmap, RT5663_PWR_ANLG_2,
+ RT5668_PWR_JD1_MASK, RT5668_PWR_JD1);
+ regmap_update_bits(rt5663->regmap, RT5663_IRQ_1,
+ RT5668_EN_CB_JD_MASK, RT5668_EN_CB_JD_EN);
+
+ regmap_update_bits(rt5663->regmap, RT5663_HP_LOGIC_2,
+ RT5668_HP_SIG_SRC1_MASK, RT5668_HP_SIG_SRC1_REG);
+ regmap_update_bits(rt5663->regmap, RT5663_RECMIX,
+ RT5668_VREF_BIAS_MASK | RT5668_CBJ_DET_MASK |
+ RT5668_DET_TYPE_MASK, RT5668_VREF_BIAS_REG |
+ RT5668_CBJ_DET_EN | RT5668_DET_TYPE_QFN);
+ /* Set GPIO4 and GPIO8 as input for combo jack */
+ regmap_update_bits(rt5663->regmap, RT5663_GPIO_2,
+ RT5668_GP4_PIN_CONF_MASK, RT5668_GP4_PIN_CONF_INPUT);
+ regmap_update_bits(rt5663->regmap, RT5668_GPIO_3,
+ RT5668_GP8_PIN_CONF_MASK, RT5668_GP8_PIN_CONF_INPUT);
+ regmap_update_bits(rt5663->regmap, RT5663_PWR_ANLG_1,
+ RT5668_LDO1_DVO_MASK | RT5668_AMP_HP_MASK,
+ RT5668_LDO1_DVO_0_9V | RT5668_AMP_HP_3X);
+ break;
+ case CODEC_TYPE_RT5663:
+ regmap_write(rt5663->regmap, RT5663_VREF_RECMIX, 0x0032);
+ regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xa2be);
+ msleep(20);
+ regmap_write(rt5663->regmap, RT5663_PWR_ANLG_1, 0xf2be);
+ regmap_update_bits(rt5663->regmap, RT5663_GPIO_2,
+ RT5663_GP1_PIN_CONF_MASK, RT5663_GP1_PIN_CONF_OUTPUT);
+ /* DACREF LDO control */
+ regmap_update_bits(rt5663->regmap, RT5663_DACREF_LDO, 0x3e0e,
+ 0x3a0a);
+ 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);
+
+ if (i2c->irq) {
+ ret = request_irq(i2c->irq, rt5663_irq,
+ IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
+ | IRQF_ONESHOT, "rt5663", rt5663);
+ if (ret)
+ dev_err(&i2c->dev, "%s Failed to reguest IRQ: %d\n",
+ __func__, ret);
+ }
+
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5663,
+ rt5663_dai, ARRAY_SIZE(rt5663_dai));
+
+ if (ret) {
+ if (i2c->irq)
+ free_irq(i2c->irq, rt5663);
+ }
+
+ return ret;
+}
+
+static int rt5663_i2c_remove(struct i2c_client *i2c)
+{
+ struct rt5663_priv *rt5663 = i2c_get_clientdata(i2c);
+
+ if (i2c->irq)
+ free_irq(i2c->irq, rt5663);
+
+ snd_soc_unregister_codec(&i2c->dev);
+
+ return 0;
+}
+
+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..2cc8f28080f6
--- /dev/null
+++ b/sound/soc/codecs/rt5663.h
@@ -0,0 +1,1121 @@
+/*
+ * rt5663.h -- RT5663 ALSA SoC audio driver
+ *
+ * Copyright 2016 Realtek Microelectronics
+ * 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 __RT5663_H__
+#define __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 RT5668_LOUT_CTRL 0x0001
+#define RT5668_HP_AMP_2 0x0003
+#define RT5668_MONO_OUT 0x0004
+#define RT5668_MONO_GAIN 0x0007
+
+#define RT5668_AEC_BST 0x000b
+#define RT5668_IN1_IN2 0x000c
+#define RT5668_IN3_IN4 0x000d
+#define RT5668_INL1_INR1 0x000f
+#define RT5668_CBJ_TYPE_2 0x0011
+#define RT5668_CBJ_TYPE_3 0x0012
+#define RT5668_CBJ_TYPE_4 0x0013
+#define RT5668_CBJ_TYPE_5 0x0014
+#define RT5668_CBJ_TYPE_8 0x0017
+
+/* I/O - ADC/DAC/DMIC */
+#define RT5668_DAC3_DIG_VOL 0x001a
+#define RT5668_DAC3_CTRL 0x001b
+#define RT5668_MONO_ADC_DIG_VOL 0x001d
+#define RT5668_STO2_ADC_DIG_VOL 0x001e
+#define RT5668_MONO_ADC_BST_GAIN 0x0020
+#define RT5668_STO2_ADC_BST_GAIN 0x0021
+#define RT5668_SIDETONE_CTRL 0x0024
+/* Mixer - D-D */
+#define RT5668_MONO1_ADC_MIXER 0x0027
+#define RT5668_STO2_ADC_MIXER 0x0028
+#define RT5668_MONO_DAC_MIXER 0x002b
+#define RT5668_DAC2_SRC_CTRL 0x002e
+#define RT5668_IF_3_4_DATA_CTL 0x002f
+#define RT5668_IF_5_DATA_CTL 0x0030
+#define RT5668_PDM_OUT_CTL 0x0031
+#define RT5668_PDM_I2C_DATA_CTL1 0x0032
+#define RT5668_PDM_I2C_DATA_CTL2 0x0033
+#define RT5668_PDM_I2C_DATA_CTL3 0x0034
+#define RT5668_PDM_I2C_DATA_CTL4 0x0035
+
+/*Mixer - Analog*/
+#define RT5668_RECMIX1_NEW 0x003a
+#define RT5668_RECMIX1L_0 0x003b
+#define RT5668_RECMIX1L 0x003c
+#define RT5668_RECMIX1R_0 0x003d
+#define RT5668_RECMIX1R 0x003e
+#define RT5668_RECMIX2_NEW 0x003f
+#define RT5668_RECMIX2_L_2 0x0041
+#define RT5668_RECMIX2_R 0x0042
+#define RT5668_RECMIX2_R_2 0x0043
+#define RT5668_CALIB_REC_LR 0x0044
+#define RT5668_ALC_BK_GAIN 0x0049
+#define RT5668_MONOMIX_GAIN 0x004a
+#define RT5668_MONOMIX_IN_GAIN 0x004b
+#define RT5668_OUT_MIXL_GAIN 0x004d
+#define RT5668_OUT_LMIX_IN_GAIN 0x004e
+#define RT5668_OUT_RMIX_IN_GAIN 0x004f
+#define RT5668_OUT_RMIX_IN_GAIN1 0x0050
+#define RT5668_LOUT_MIXER_CTRL 0x0052
+/* Power */
+#define RT5668_PWR_VOL 0x0067
+
+#define RT5668_ADCDAC_RST 0x006d
+/* Format - ADC/DAC */
+#define RT5668_I2S34_SDP 0x0071
+#define RT5668_I2S5_SDP 0x0072
+/* Format - TDM Control */
+#define RT5668_TDM_5 0x007c
+#define RT5668_TDM_6 0x007d
+#define RT5668_TDM_7 0x007e
+#define RT5668_TDM_8 0x007f
+
+/* Function - Analog */
+#define RT5668_ASRC_3 0x0085
+#define RT5668_ASRC_6 0x0088
+#define RT5668_ASRC_7 0x0089
+#define RT5668_PLL_TRK_13 0x0099
+#define RT5668_I2S_M_CLK_CTL 0x00a0
+#define RT5668_FDIV_I2S34_M_CLK 0x00a1
+#define RT5668_FDIV_I2S34_M_CLK2 0x00a2
+#define RT5668_FDIV_I2S5_M_CLK 0x00a3
+#define RT5668_FDIV_I2S5_M_CLK2 0x00a4
+
+/* Function - Digital */
+#define RT5668_IRQ_4 0x00b9
+#define RT5668_GPIO_3 0x00c2
+#define RT5668_GPIO_4 0x00c3
+#define RT5668_GPIO_STA 0x00c4
+#define RT5668_HP_AMP_DET1 0x00d0
+#define RT5668_HP_AMP_DET2 0x00d1
+#define RT5668_HP_AMP_DET3 0x00d2
+#define RT5668_MID_BD_HP_AMP 0x00d3
+#define RT5668_LOW_BD_HP_AMP 0x00d4
+#define RT5668_SOF_VOL_ZC2 0x00da
+#define RT5668_ADC_STO2_ADJ1 0x00ee
+#define RT5668_ADC_STO2_ADJ2 0x00ef
+/* General Control */
+#define RT5668_A_JD_CTRL 0x00f0
+#define RT5668_JD1_TRES_CTRL 0x00f1
+#define RT5668_JD2_TRES_CTRL 0x00f2
+#define RT5668_JD_CTRL2 0x00f7
+#define RT5668_DUM_REG_2 0x00fb
+#define RT5668_DUM_REG_3 0x00fc
+
+
+#define RT5668_DACADC_DIG_VOL2 0x0101
+#define RT5668_DIG_IN_PIN2 0x0133
+#define RT5668_PAD_DRV_CTL1 0x0136
+#define RT5668_SOF_RAM_DEPOP 0x0138
+#define RT5668_VOL_TEST 0x013f
+#define RT5668_TEST_MODE_3 0x0147
+#define RT5668_TEST_MODE_4 0x0148
+#define RT5668_MONO_DYNA_1 0x0170
+#define RT5668_MONO_DYNA_2 0x0171
+#define RT5668_MONO_DYNA_3 0x0172
+#define RT5668_MONO_DYNA_4 0x0173
+#define RT5668_MONO_DYNA_5 0x0174
+#define RT5668_MONO_DYNA_6 0x0175
+#define RT5668_STO1_SIL_DET 0x0190
+#define RT5668_MONOL_SIL_DET 0x0191
+#define RT5668_MONOR_SIL_DET 0x0192
+#define RT5668_STO2_DAC_SIL 0x0193
+#define RT5668_PWR_SAV_CTL1 0x0194
+#define RT5668_PWR_SAV_CTL2 0x0195
+#define RT5668_PWR_SAV_CTL3 0x0196
+#define RT5668_PWR_SAV_CTL4 0x0197
+#define RT5668_PWR_SAV_CTL5 0x0198
+#define RT5668_PWR_SAV_CTL6 0x0199
+#define RT5668_MONO_AMP_CAL1 0x01a0
+#define RT5668_MONO_AMP_CAL2 0x01a1
+#define RT5668_MONO_AMP_CAL3 0x01a2
+#define RT5668_MONO_AMP_CAL4 0x01a3
+#define RT5668_MONO_AMP_CAL5 0x01a4
+#define RT5668_MONO_AMP_CAL6 0x01a5
+#define RT5668_MONO_AMP_CAL7 0x01a6
+#define RT5668_MONO_AMP_CAL_ST1 0x01a7
+#define RT5668_MONO_AMP_CAL_ST2 0x01a8
+#define RT5668_MONO_AMP_CAL_ST3 0x01a9
+#define RT5668_MONO_AMP_CAL_ST4 0x01aa
+#define RT5668_MONO_AMP_CAL_ST5 0x01ab
+#define RT5668_HP_IMP_SEN_13 0x01b9
+#define RT5668_HP_IMP_SEN_14 0x01ba
+#define RT5668_HP_IMP_SEN_6 0x01bb
+#define RT5668_HP_IMP_SEN_7 0x01bc
+#define RT5668_HP_IMP_SEN_8 0x01bd
+#define RT5668_HP_IMP_SEN_9 0x01be
+#define RT5668_HP_IMP_SEN_10 0x01bf
+#define RT5668_HP_LOGIC_3 0x01dc
+#define RT5668_HP_CALIB_ST10 0x01f3
+#define RT5668_HP_CALIB_ST11 0x01f4
+#define RT5668_PRO_REG_TBL_4 0x0203
+#define RT5668_PRO_REG_TBL_5 0x0204
+#define RT5668_PRO_REG_TBL_6 0x0205
+#define RT5668_PRO_REG_TBL_7 0x0206
+#define RT5668_PRO_REG_TBL_8 0x0207
+#define RT5668_PRO_REG_TBL_9 0x0208
+#define RT5668_SAR_ADC_INL_1 0x0210
+#define RT5668_SAR_ADC_INL_2 0x0211
+#define RT5668_SAR_ADC_INL_3 0x0212
+#define RT5668_SAR_ADC_INL_4 0x0213
+#define RT5668_SAR_ADC_INL_5 0x0214
+#define RT5668_SAR_ADC_INL_6 0x0215
+#define RT5668_SAR_ADC_INL_7 0x0216
+#define RT5668_SAR_ADC_INL_8 0x0217
+#define RT5668_SAR_ADC_INL_9 0x0218
+#define RT5668_SAR_ADC_INL_10 0x0219
+#define RT5668_SAR_ADC_INL_11 0x021a
+#define RT5668_SAR_ADC_INL_12 0x021b
+#define RT5668_DRC_CTRL_1 0x02ff
+#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_HD_CTRL_1 0x0306
+#define RT5668_DRC1_HD_CTRL_2 0x0307
+#define RT5668_DRC1_PRI_REG_1 0x0310
+#define RT5668_DRC1_PRI_REG_2 0x0311
+#define RT5668_DRC1_PRI_REG_3 0x0312
+#define RT5668_DRC1_PRI_REG_4 0x0313
+#define RT5668_DRC1_PRI_REG_5 0x0314
+#define RT5668_DRC1_PRI_REG_6 0x0315
+#define RT5668_DRC1_PRI_REG_7 0x0316
+#define RT5668_DRC1_PRI_REG_8 0x0317
+#define RT5668_ALC_PGA_CTL_1 0x0330
+#define RT5668_ALC_PGA_CTL_2 0x0331
+#define RT5668_ALC_PGA_CTL_3 0x0332
+#define RT5668_ALC_PGA_CTL_4 0x0333
+#define RT5668_ALC_PGA_CTL_5 0x0334
+#define RT5668_ALC_PGA_CTL_6 0x0335
+#define RT5668_ALC_PGA_CTL_7 0x0336
+#define RT5668_ALC_PGA_CTL_8 0x0337
+#define RT5668_ALC_PGA_REG_1 0x0338
+#define RT5668_ALC_PGA_REG_2 0x0339
+#define RT5668_ALC_PGA_REG_3 0x033a
+#define RT5668_ADC_EQ_RECOV_1 0x03c0
+#define RT5668_ADC_EQ_RECOV_2 0x03c1
+#define RT5668_ADC_EQ_RECOV_3 0x03c2
+#define RT5668_ADC_EQ_RECOV_4 0x03c3
+#define RT5668_ADC_EQ_RECOV_5 0x03c4
+#define RT5668_ADC_EQ_RECOV_6 0x03c5
+#define RT5668_ADC_EQ_RECOV_7 0x03c6
+#define RT5668_ADC_EQ_RECOV_8 0x03c7
+#define RT5668_ADC_EQ_RECOV_9 0x03c8
+#define RT5668_ADC_EQ_RECOV_10 0x03c9
+#define RT5668_ADC_EQ_RECOV_11 0x03ca
+#define RT5668_ADC_EQ_RECOV_12 0x03cb
+#define RT5668_ADC_EQ_RECOV_13 0x03cc
+#define RT5668_VID_HIDDEN 0x03fe
+#define RT5668_VID_CUSTOMER 0x03ff
+#define RT5668_SCAN_MODE 0x07f0
+#define RT5668_I2C_BYPA 0x07fa
+
+/* Headphone Amp Control 2 (0x0003) */
+#define RT5668_EN_DAC_HPO_MASK (0x1 << 14)
+#define RT5668_EN_DAC_HPO_SHIFT 14
+#define RT5668_EN_DAC_HPO_DIS (0x0 << 14)
+#define RT5668_EN_DAC_HPO_EN (0x1 << 14)
+
+/*Headphone Amp L/R Analog Gain and Digital NG2 Gain Control (0x0005 0x0006)*/
+#define RT5668_GAIN_HP (0x1f << 8)
+#define RT5668_GAIN_HP_SHIFT 8
+
+/* AEC BST Control (0x000b) */
+#define RT5668_GAIN_CBJ_MASK (0xf << 8)
+#define RT5668_GAIN_CBJ_SHIFT 8
+
+/* IN1 Control / MIC GND REF (0x000c) */
+#define RT5668_IN1_DF_MASK (0x1 << 15)
+#define RT5668_IN1_DF_SHIFT 15
+
+/* Combo Jack and Type Detection Control 1 (0x0010) */
+#define RT5668_CBJ_DET_MASK (0x1 << 15)
+#define RT5668_CBJ_DET_SHIFT 15
+#define RT5668_CBJ_DET_DIS (0x0 << 15)
+#define RT5668_CBJ_DET_EN (0x1 << 15)
+#define RT5668_DET_TYPE_MASK (0x1 << 12)
+#define RT5668_DET_TYPE_SHIFT 12
+#define RT5668_DET_TYPE_WLCSP (0x0 << 12)
+#define RT5668_DET_TYPE_QFN (0x1 << 12)
+#define RT5668_VREF_BIAS_MASK (0x1 << 6)
+#define RT5668_VREF_BIAS_SHIFT 6
+#define RT5668_VREF_BIAS_FSM (0x0 << 6)
+#define RT5668_VREF_BIAS_REG (0x1 << 6)
+
+/* REC Left Mixer Control 2 (0x003c) */
+#define RT5668_RECMIX1L_BST1_CBJ (0x1 << 7)
+#define RT5668_RECMIX1L_BST1_CBJ_SHIFT 7
+#define RT5668_RECMIX1L_BST2 (0x1 << 4)
+#define RT5668_RECMIX1L_BST2_SHIFT 4
+
+/* REC Right Mixer Control 2 (0x003e) */
+#define RT5668_RECMIX1R_BST2 (0x1 << 4)
+#define RT5668_RECMIX1R_BST2_SHIFT 4
+
+/* DAC1 Digital Volume (0x0019) */
+#define RT5668_DAC_L1_VOL_MASK (0xff << 8)
+#define RT5668_DAC_L1_VOL_SHIFT 8
+#define RT5668_DAC_R1_VOL_MASK (0xff)
+#define RT5668_DAC_R1_VOL_SHIFT 0
+
+/* ADC Digital Volume Control (0x001c) */
+#define RT5668_ADC_L_MUTE_MASK (0x1 << 15)
+#define RT5668_ADC_L_MUTE_SHIFT 15
+#define RT5668_ADC_L_VOL_MASK (0x7f << 8)
+#define RT5668_ADC_L_VOL_SHIFT 8
+#define RT5668_ADC_R_MUTE_MASK (0x1 << 7)
+#define RT5668_ADC_R_MUTE_SHIFT 7
+#define RT5668_ADC_R_VOL_MASK (0x7f)
+#define RT5668_ADC_R_VOL_SHIFT 0
+
+/* Stereo ADC Mixer Control (0x0026) */
+#define RT5668_M_STO1_ADC_L1 (0x1 << 15)
+#define RT5668_M_STO1_ADC_L1_SHIFT 15
+#define RT5668_M_STO1_ADC_L2 (0x1 << 14)
+#define RT5668_M_STO1_ADC_L2_SHIFT 14
+#define RT5668_STO1_ADC_L1_SRC (0x1 << 13)
+#define RT5668_STO1_ADC_L1_SRC_SHIFT 13
+#define RT5668_STO1_ADC_L2_SRC (0x1 << 12)
+#define RT5668_STO1_ADC_L2_SRC_SHIFT 12
+#define RT5668_STO1_ADC_L_SRC (0x3 << 10)
+#define RT5668_STO1_ADC_L_SRC_SHIFT 10
+#define RT5668_M_STO1_ADC_R1 (0x1 << 7)
+#define RT5668_M_STO1_ADC_R1_SHIFT 7
+#define RT5668_M_STO1_ADC_R2 (0x1 << 6)
+#define RT5668_M_STO1_ADC_R2_SHIFT 6
+#define RT5668_STO1_ADC_R1_SRC (0x1 << 5)
+#define RT5668_STO1_ADC_R1_SRC_SHIFT 5
+#define RT5668_STO1_ADC_R2_SRC (0x1 << 4)
+#define RT5668_STO1_ADC_R2_SRC_SHIFT 4
+#define RT5668_STO1_ADC_R_SRC (0x3 << 2)
+#define RT5668_STO1_ADC_R_SRC_SHIFT 2
+
+/* ADC Mixer to DAC Mixer Control (0x0029) */
+#define RT5668_M_ADCMIX_L (0x1 << 15)
+#define RT5668_M_ADCMIX_L_SHIFT 15
+#define RT5668_M_DAC1_L (0x1 << 14)
+#define RT5668_M_DAC1_L_SHIFT 14
+#define RT5668_M_ADCMIX_R (0x1 << 7)
+#define RT5668_M_ADCMIX_R_SHIFT 7
+#define RT5668_M_DAC1_R (0x1 << 6)
+#define RT5668_M_DAC1_R_SHIFT 6
+
+/* Stereo DAC Mixer Control (0x002a) */
+#define RT5668_M_DAC_L1_STO_L (0x1 << 15)
+#define RT5668_M_DAC_L1_STO_L_SHIFT 15
+#define RT5668_M_DAC_R1_STO_L (0x1 << 13)
+#define RT5668_M_DAC_R1_STO_L_SHIFT 13
+#define RT5668_M_DAC_L1_STO_R (0x1 << 7)
+#define RT5668_M_DAC_L1_STO_R_SHIFT 7
+#define RT5668_M_DAC_R1_STO_R (0x1 << 5)
+#define RT5668_M_DAC_R1_STO_R_SHIFT 5
+
+/* Power Management for Digital 1 (0x0061) */
+#define RT5668_PWR_I2S1 (0x1 << 15)
+#define RT5668_PWR_I2S1_SHIFT 15
+#define RT5668_PWR_DAC_L1 (0x1 << 11)
+#define RT5668_PWR_DAC_L1_SHIFT 11
+#define RT5668_PWR_DAC_R1 (0x1 << 10)
+#define RT5668_PWR_DAC_R1_SHIFT 10
+#define RT5668_PWR_LDO_DACREF_MASK (0x1 << 8)
+#define RT5668_PWR_LDO_DACREF_SHIFT 8
+#define RT5668_PWR_LDO_DACREF_ON (0x1 << 8)
+#define RT5668_PWR_LDO_DACREF_DOWN (0x0 << 8)
+#define RT5668_PWR_LDO_SHIFT 8
+#define RT5668_PWR_ADC_L1 (0x1 << 4)
+#define RT5668_PWR_ADC_L1_SHIFT 4
+#define RT5668_PWR_ADC_R1 (0x1 << 3)
+#define RT5668_PWR_ADC_R1_SHIFT 3
+
+/* Power Management for Digital 2 (0x0062) */
+#define RT5668_PWR_ADC_S1F (0x1 << 15)
+#define RT5668_PWR_ADC_S1F_SHIFT 15
+#define RT5668_PWR_DAC_S1F (0x1 << 10)
+#define RT5668_PWR_DAC_S1F_SHIFT 10
+
+/* Power Management for Analog 1 (0x0063) */
+#define RT5668_PWR_VREF1 (0x1 << 15)
+#define RT5668_PWR_VREF1_MASK (0x1 << 15)
+#define RT5668_PWR_VREF1_SHIFT 15
+#define RT5668_PWR_FV1 (0x1 << 14)
+#define RT5668_PWR_FV1_MASK (0x1 << 14)
+#define RT5668_PWR_FV1_SHIFT 14
+#define RT5668_PWR_VREF2 (0x1 << 13)
+#define RT5668_PWR_VREF2_MASK (0x1 << 13)
+#define RT5668_PWR_VREF2_SHIFT 13
+#define RT5668_PWR_FV2 (0x1 << 12)
+#define RT5668_PWR_FV2_MASK (0x1 << 12)
+#define RT5668_PWR_FV2_SHIFT 12
+#define RT5668_PWR_MB (0x1 << 9)
+#define RT5668_PWR_MB_MASK (0x1 << 9)
+#define RT5668_PWR_MB_SHIFT 9
+#define RT5668_AMP_HP_MASK (0x3 << 2)
+#define RT5668_AMP_HP_SHIFT 2
+#define RT5668_AMP_HP_1X (0x0 << 2)
+#define RT5668_AMP_HP_3X (0x1 << 2)
+#define RT5668_AMP_HP_5X (0x3 << 2)
+#define RT5668_LDO1_DVO_MASK (0x3)
+#define RT5668_LDO1_DVO_SHIFT 0
+#define RT5668_LDO1_DVO_0_9V (0x0)
+#define RT5668_LDO1_DVO_1_0V (0x1)
+#define RT5668_LDO1_DVO_1_2V (0x2)
+#define RT5668_LDO1_DVO_1_4V (0x3)
+
+/* Power Management for Analog 2 (0x0064) */
+#define RT5668_PWR_BST1 (0x1 << 15)
+#define RT5668_PWR_BST1_MASK (0x1 << 15)
+#define RT5668_PWR_BST1_SHIFT 15
+#define RT5668_PWR_BST1_OFF (0x0 << 15)
+#define RT5668_PWR_BST1_ON (0x1 << 15)
+#define RT5668_PWR_BST2 (0x1 << 14)
+#define RT5668_PWR_BST2_MASK (0x1 << 14)
+#define RT5668_PWR_BST2_SHIFT 14
+#define RT5668_PWR_MB1 (0x1 << 11)
+#define RT5668_PWR_MB1_SHIFT 11
+#define RT5668_PWR_MB2 (0x1 << 10)
+#define RT5668_PWR_MB2_SHIFT 10
+#define RT5668_PWR_BST2_OP (0x1 << 6)
+#define RT5668_PWR_BST2_OP_MASK (0x1 << 6)
+#define RT5668_PWR_BST2_OP_SHIFT 6
+#define RT5668_PWR_JD1 (0x1 << 3)
+#define RT5668_PWR_JD1_MASK (0x1 << 3)
+#define RT5668_PWR_JD1_SHIFT 3
+#define RT5668_PWR_JD2 (0x1 << 2)
+#define RT5668_PWR_JD2_MASK (0x1 << 2)
+#define RT5668_PWR_JD2_SHIFT 2
+#define RT5668_PWR_RECMIX1 (0x1 << 1)
+#define RT5668_PWR_RECMIX1_SHIFT 1
+#define RT5668_PWR_RECMIX2 (0x1)
+#define RT5668_PWR_RECMIX2_SHIFT 0
+
+/* Power Management for Analog 3 (0x0065) */
+#define RT5668_PWR_CBJ_MASK (0x1 << 9)
+#define RT5668_PWR_CBJ_SHIFT 9
+#define RT5668_PWR_CBJ_OFF (0x0 << 9)
+#define RT5668_PWR_CBJ_ON (0x1 << 9)
+#define RT5668_PWR_PLL (0x1 << 6)
+#define RT5668_PWR_PLL_SHIFT 6
+#define RT5668_PWR_LDO2 (0x1 << 2)
+#define RT5668_PWR_LDO2_SHIFT 2
+
+/* Power Management for Volume (0x0067) */
+#define RT5668_PWR_MIC_DET (0x1 << 5)
+#define RT5668_PWR_MIC_DET_SHIFT 5
+
+/* MCLK and System Clock Detection Control (0x006b) */
+#define RT5668_EN_ANA_CLK_DET_MASK (0x1 << 15)
+#define RT5668_EN_ANA_CLK_DET_SHIFT 15
+#define RT5668_EN_ANA_CLK_DET_DIS (0x0 << 15)
+#define RT5668_EN_ANA_CLK_DET_AUTO (0x1 << 15)
+#define RT5668_PWR_CLK_DET_MASK (0x1)
+#define RT5668_PWR_CLK_DET_SHIFT 0
+#define RT5668_PWR_CLK_DET_DIS (0x0)
+#define RT5668_PWR_CLK_DET_EN (0x1)
+
+/* I2S1 Audio Serial Data Port Control (0x0070) */
+#define RT5668_I2S_MS_MASK (0x1 << 15)
+#define RT5668_I2S_MS_SHIFT 15
+#define RT5668_I2S_MS_M (0x0 << 15)
+#define RT5668_I2S_MS_S (0x1 << 15)
+#define RT5668_I2S_BP_MASK (0x1 << 8)
+#define RT5668_I2S_BP_SHIFT 8
+#define RT5668_I2S_BP_NOR (0x0 << 8)
+#define RT5668_I2S_BP_INV (0x1 << 8)
+#define RT5668_I2S_DL_MASK (0x3 << 4)
+#define RT5668_I2S_DL_SHIFT 4
+#define RT5668_I2S_DL_16 (0x0 << 4)
+#define RT5668_I2S_DL_20 (0x1 << 4)
+#define RT5668_I2S_DL_24 (0x2 << 4)
+#define RT5668_I2S_DL_8 (0x3 << 4)
+#define RT5668_I2S_DF_MASK (0x7)
+#define RT5668_I2S_DF_SHIFT 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_I2S_PD1_MASK (0x7 << 12)
+#define RT5668_I2S_PD1_SHIFT 12
+#define RT5668_M_I2S_DIV_MASK (0x7 << 8)
+#define RT5668_M_I2S_DIV_SHIFT 8
+#define RT5668_CLK_SRC_MASK (0x3 << 4)
+#define RT5668_CLK_SRC_MCLK (0x0 << 4)
+#define RT5668_CLK_SRC_PLL_OUT (0x1 << 4)
+#define RT5668_CLK_SRC_DIV (0x2 << 4)
+#define RT5668_CLK_SRC_RC (0x3 << 4)
+#define RT5668_DAC_OSR_MASK (0x3 << 2)
+#define RT5668_DAC_OSR_SHIFT 2
+#define RT5668_DAC_OSR_128 (0x0 << 2)
+#define RT5668_DAC_OSR_64 (0x1 << 2)
+#define RT5668_DAC_OSR_32 (0x2 << 2)
+#define RT5668_ADC_OSR_MASK (0x3)
+#define RT5668_ADC_OSR_SHIFT 0
+#define RT5668_ADC_OSR_128 (0x0)
+#define RT5668_ADC_OSR_64 (0x1)
+#define RT5668_ADC_OSR_32 (0x2)
+
+/* TDM1 control 1 (0x0078) */
+#define RT5668_TDM_MODE_MASK (0x1 << 15)
+#define RT5668_TDM_MODE_SHIFT 15
+#define RT5668_TDM_MODE_I2S (0x0 << 15)
+#define RT5668_TDM_MODE_TDM (0x1 << 15)
+#define RT5668_TDM_IN_CH_MASK (0x3 << 10)
+#define RT5668_TDM_IN_CH_SHIFT 10
+#define RT5668_TDM_IN_CH_2 (0x0 << 10)
+#define RT5668_TDM_IN_CH_4 (0x1 << 10)
+#define RT5668_TDM_IN_CH_6 (0x2 << 10)
+#define RT5668_TDM_IN_CH_8 (0x3 << 10)
+#define RT5668_TDM_OUT_CH_MASK (0x3 << 8)
+#define RT5668_TDM_OUT_CH_SHIFT 8
+#define RT5668_TDM_OUT_CH_2 (0x0 << 8)
+#define RT5668_TDM_OUT_CH_4 (0x1 << 8)
+#define RT5668_TDM_OUT_CH_6 (0x2 << 8)
+#define RT5668_TDM_OUT_CH_8 (0x3 << 8)
+#define RT5668_TDM_IN_LEN_MASK (0x3 << 6)
+#define RT5668_TDM_IN_LEN_SHIFT 6
+#define RT5668_TDM_IN_LEN_16 (0x0 << 6)
+#define RT5668_TDM_IN_LEN_20 (0x1 << 6)
+#define RT5668_TDM_IN_LEN_24 (0x2 << 6)
+#define RT5668_TDM_IN_LEN_32 (0x3 << 6)
+#define RT5668_TDM_OUT_LEN_MASK (0x3 << 4)
+#define RT5668_TDM_OUT_LEN_SHIFT 4
+#define RT5668_TDM_OUT_LEN_16 (0x0 << 4)
+#define RT5668_TDM_OUT_LEN_20 (0x1 << 4)
+#define RT5668_TDM_OUT_LEN_24 (0x2 << 4)
+#define RT5668_TDM_OUT_LEN_32 (0x3 << 4)
+
+/* Global Clock Control (0x0080) */
+#define RT5668_SCLK_SRC_MASK (0x3 << 14)
+#define RT5668_SCLK_SRC_SHIFT 14
+#define RT5668_SCLK_SRC_MCLK (0x0 << 14)
+#define RT5668_SCLK_SRC_PLL1 (0x1 << 14)
+#define RT5668_SCLK_SRC_RCCLK (0x2 << 14)
+#define RT5668_PLL1_SRC_MASK (0x7 << 8)
+#define RT5668_PLL1_SRC_SHIFT 8
+#define RT5668_PLL1_SRC_MCLK (0x0 << 8)
+#define RT5668_PLL1_SRC_BCLK1 (0x1 << 8)
+#define RT5668_PLL1_PD_MASK (0x1 << 4)
+#define RT5668_PLL1_PD_SHIFT 4
+
+#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_SHIFT 7
+#define RT5668_PLL_K_MAX 0x001f
+#define RT5668_PLL_K_MASK (RT5668_PLL_K_MAX)
+#define RT5668_PLL_K_SHIFT 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_SHIFT 12
+#define RT5668_PLL_M_BP (0x1 << 11)
+#define RT5668_PLL_M_BP_SHIFT 11
+
+/* PLL tracking mode 1 (0x0083) */
+#define RT5668_I2S1_ASRC_MASK (0x1 << 13)
+#define RT5668_I2S1_ASRC_SHIFT 13
+#define RT5668_DAC_STO1_ASRC_MASK (0x1 << 12)
+#define RT5668_DAC_STO1_ASRC_SHIFT 12
+#define RT5668_ADC_STO1_ASRC_MASK (0x1 << 4)
+#define RT5668_ADC_STO1_ASRC_SHIFT 4
+
+/* PLL tracking mode 2 (0x0084)*/
+#define RT5668_DA_STO1_TRACK_MASK (0x7 << 12)
+#define RT5668_DA_STO1_TRACK_SHIFT 12
+#define RT5668_DA_STO1_TRACK_SYSCLK (0x0 << 12)
+#define RT5668_DA_STO1_TRACK_I2S1 (0x1 << 12)
+
+/* PLL tracking mode 3 (0x0085)*/
+#define RT5668_AD_STO1_TRACK_MASK (0x7 << 12)
+#define RT5668_AD_STO1_TRACK_SHIFT 12
+#define RT5668_AD_STO1_TRACK_SYSCLK (0x0 << 12)
+#define RT5668_AD_STO1_TRACK_I2S1 (0x1 << 12)
+
+/* HPOUT Charge pump control 1 (0x0091) */
+#define RT5668_OSW_HP_L_MASK (0x1 << 11)
+#define RT5668_OSW_HP_L_SHIFT 11
+#define RT5668_OSW_HP_L_EN (0x1 << 11)
+#define RT5668_OSW_HP_L_DIS (0x0 << 11)
+#define RT5668_OSW_HP_R_MASK (0x1 << 10)
+#define RT5668_OSW_HP_R_SHIFT 10
+#define RT5668_OSW_HP_R_EN (0x1 << 10)
+#define RT5668_OSW_HP_R_DIS (0x0 << 10)
+#define RT5668_SEL_PM_HP_MASK (0x3 << 8)
+#define RT5668_SEL_PM_HP_SHIFT 8
+#define RT5668_SEL_PM_HP_0_6 (0x0 << 8)
+#define RT5668_SEL_PM_HP_0_9 (0x1 << 8)
+#define RT5668_SEL_PM_HP_1_8 (0x2 << 8)
+#define RT5668_SEL_PM_HP_HIGH (0x3 << 8)
+#define RT5668_OVCD_HP_MASK (0x1 << 2)
+#define RT5668_OVCD_HP_SHIFT 2
+#define RT5668_OVCD_HP_EN (0x1 << 2)
+#define RT5668_OVCD_HP_DIS (0x0 << 2)
+
+/* RC Clock Control (0x0094) */
+#define RT5668_DIG_25M_CLK_MASK (0x1 << 9)
+#define RT5668_DIG_25M_CLK_SHIFT 9
+#define RT5668_DIG_25M_CLK_DIS (0x0 << 9)
+#define RT5668_DIG_25M_CLK_EN (0x1 << 9)
+#define RT5668_DIG_1M_CLK_MASK (0x1 << 8)
+#define RT5668_DIG_1M_CLK_SHIFT 8
+#define RT5668_DIG_1M_CLK_DIS (0x0 << 8)
+#define RT5668_DIG_1M_CLK_EN (0x1 << 8)
+
+/* Auto Turn On 1M RC CLK (0x009f) */
+#define RT5668_IRQ_POW_SAV_MASK (0x1 << 15)
+#define RT5668_IRQ_POW_SAV_SHIFT 15
+#define RT5668_IRQ_POW_SAV_DIS (0x0 << 15)
+#define RT5668_IRQ_POW_SAV_EN (0x1 << 15)
+#define RT5668_IRQ_POW_SAV_JD1_MASK (0x1 << 14)
+#define RT5668_IRQ_POW_SAV_JD1_SHIFT 14
+#define RT5668_IRQ_POW_SAV_JD1_DIS (0x0 << 14)
+#define RT5668_IRQ_POW_SAV_JD1_EN (0x1 << 14)
+
+/* IRQ Control 1 (0x00b6) */
+#define RT5668_EN_CB_JD_MASK (0x1 << 3)
+#define RT5668_EN_CB_JD_SHIFT 3
+#define RT5668_EN_CB_JD_EN (0x1 << 3)
+#define RT5668_EN_CB_JD_DIS (0x0 << 3)
+
+/* IRQ Control 3 (0x00b8) */
+#define RT5668_EN_IRQ_INLINE_MASK (0x1 << 6)
+#define RT5668_EN_IRQ_INLINE_SHIFT 6
+#define RT5668_EN_IRQ_INLINE_BYP (0x0 << 6)
+#define RT5668_EN_IRQ_INLINE_NOR (0x1 << 6)
+
+/* GPIO Control 1 (0x00c0) */
+#define RT5668_GP1_PIN_MASK (0x1 << 15)
+#define RT5668_GP1_PIN_SHIFT 15
+#define RT5668_GP1_PIN_GPIO1 (0x0 << 15)
+#define RT5668_GP1_PIN_IRQ (0x1 << 15)
+
+/* GPIO Control 2 (0x00c1) */
+#define RT5668_GP4_PIN_CONF_MASK (0x1 << 5)
+#define RT5668_GP4_PIN_CONF_SHIFT 5
+#define RT5668_GP4_PIN_CONF_INPUT (0x0 << 5)
+#define RT5668_GP4_PIN_CONF_OUTPUT (0x1 << 5)
+
+/* GPIO Control 2 (0x00c2) */
+#define RT5668_GP8_PIN_CONF_MASK (0x1 << 13)
+#define RT5668_GP8_PIN_CONF_SHIFT 13
+#define RT5668_GP8_PIN_CONF_INPUT (0x0 << 13)
+#define RT5668_GP8_PIN_CONF_OUTPUT (0x1 << 13)
+
+/* 4 Buttons Inline Command Function 1 (0x00df) */
+#define RT5668_4BTN_CLK_DEB_MASK (0x3 << 2)
+#define RT5668_4BTN_CLK_DEB_SHIFT 2
+#define RT5668_4BTN_CLK_DEB_8MS (0x0 << 2)
+#define RT5668_4BTN_CLK_DEB_16MS (0x1 << 2)
+#define RT5668_4BTN_CLK_DEB_32MS (0x2 << 2)
+#define RT5668_4BTN_CLK_DEB_65MS (0x3 << 2)
+
+/* Inline Command Function 6 (0x00e0) */
+#define RT5668_EN_4BTN_INL_MASK (0x1 << 15)
+#define RT5668_EN_4BTN_INL_SHIFT 15
+#define RT5668_EN_4BTN_INL_DIS (0x0 << 15)
+#define RT5668_EN_4BTN_INL_EN (0x1 << 15)
+#define RT5668_RESET_4BTN_INL_MASK (0x1 << 14)
+#define RT5668_RESET_4BTN_INL_SHIFT 14
+#define RT5668_RESET_4BTN_INL_RESET (0x0 << 14)
+#define RT5668_RESET_4BTN_INL_NOR (0x1 << 14)
+
+/* Digital Misc Control (0x00fa) */
+#define RT5668_DIG_GATE_CTRL_MASK 0x1
+#define RT5668_DIG_GATE_CTRL_SHIFT (0)
+#define RT5668_DIG_GATE_CTRL_DIS 0x0
+#define RT5668_DIG_GATE_CTRL_EN 0x1
+
+/* Chopper and Clock control for DAC L (0x013a)*/
+#define RT5668_CKXEN_DAC1_MASK (0x1 << 13)
+#define RT5668_CKXEN_DAC1_SHIFT 13
+#define RT5668_CKGEN_DAC1_MASK (0x1 << 12)
+#define RT5668_CKGEN_DAC1_SHIFT 12
+
+/* Chopper and Clock control for ADC (0x013b)*/
+#define RT5668_CKXEN_ADCC_MASK (0x1 << 13)
+#define RT5668_CKXEN_ADCC_SHIFT 13
+#define RT5668_CKGEN_ADCC_MASK (0x1 << 12)
+#define RT5668_CKGEN_ADCC_SHIFT 12
+
+/* HP Behavior Logic Control 2 (0x01db) */
+#define RT5668_HP_SIG_SRC1_MASK (0x3)
+#define RT5668_HP_SIG_SRC1_SHIFT 0
+#define RT5668_HP_SIG_SRC1_HP_DC (0x0)
+#define RT5668_HP_SIG_SRC1_HP_CALIB (0x1)
+#define RT5668_HP_SIG_SRC1_REG (0x2)
+#define RT5668_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_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_STA 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_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
+
+/* RT5663: 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)
+
+/* RT5663: 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
+
+/* RT5663: 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)
+
+/* RT5663: 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)
+
+/* RT5663: 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)
+
+/* 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)
+
+/* RT5663: 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)
+
+/* RT5663: 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)
+
+/* RT5663: 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)
+
+/* RT5663: 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)
+
+/* RT5663: 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)
+
+/* RT5663: 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)
+
+/* RT5663: 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
+
+/* RT5663: Stereo Dynamic Range Enhancement Control 9 (0x0168, 0x0169)*/
+#define RT5663_DRE_GAIN_HP_MASK (0x1f)
+#define RT5663_DRE_GAIN_HP_SHIFT 0
+
+/* RT5663: 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)
+
+/* RT5663: Combo Jack Control (0x0251) */
+#define RT5663_GAIN_BST1_MASK (0xf)
+#define RT5663_GAIN_BST1_SHIFT 0
+
+/* RT5663: 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_set_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *hs_jack);
+int rt5663_sel_asrc_clk_src(struct snd_soc_codec *codec,
+ unsigned int filter_mask, unsigned int clk_src);
+
+#endif /* __RT5663_H__ */
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index 49a9e7049e2b..49caf1393aeb 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -55,6 +55,7 @@ static const struct reg_sequence init_list[] = {
{ RT5670_PR_BASE + 0x14, 0x9a8a },
{ RT5670_PR_BASE + 0x38, 0x3ba1 },
{ RT5670_PR_BASE + 0x3d, 0x3640 },
+ { 0x8a, 0x0123 },
};
static const struct reg_default rt5670_reg[] = {
@@ -131,7 +132,7 @@ static const struct reg_default rt5670_reg[] = {
{ 0x87, 0x0000 },
{ 0x88, 0x0000 },
{ 0x89, 0x0000 },
- { 0x8a, 0x0000 },
+ { 0x8a, 0x0123 },
{ 0x8b, 0x0000 },
{ 0x8c, 0x0003 },
{ 0x8d, 0x0000 },
@@ -619,7 +620,7 @@ static const struct snd_kcontrol_new rt5670_snd_controls[] = {
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),
@@ -2776,12 +2777,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5670 = {
.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),
+ .component_driver = {
+ .controls = rt5670_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5670_snd_controls),
+ .dapm_widgets = rt5670_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5670_dapm_widgets),
+ .dapm_routes = rt5670_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5670_dapm_routes),
+ },
};
static const struct regmap_config rt5670_regmap = {
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index da9483c1c6fb..abc802a5a479 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -9,6 +9,7 @@
* published by the Free Software Foundation.
*/
+#include <linux/acpi.h>
#include <linux/fs.h>
#include <linux/module.h>
#include <linux/moduleparam.h>
@@ -40,6 +41,15 @@
#define RT5677_PR_BASE (RT5677_PR_RANGE_BASE + (0 * RT5677_PR_SPACING))
+/* 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 regmap_range_cfg rt5677_ranges[] = {
{
.name = "PR",
@@ -4657,7 +4667,7 @@ static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
return regmap_irq_get_virq(data, irq);
}
-static struct gpio_chip rt5677_template_chip = {
+static const struct gpio_chip rt5677_template_chip = {
.label = "rt5677",
.owner = THIS_MODULE,
.direction_output = rt5677_gpio_direction_out,
@@ -4974,12 +4984,14 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5677 = {
.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),
+ .component_driver = {
+ .controls = rt5677_snd_controls,
+ .num_controls = ARRAY_SIZE(rt5677_snd_controls),
+ .dapm_widgets = rt5677_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rt5677_dapm_widgets),
+ .dapm_routes = rt5677_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rt5677_dapm_routes),
+ },
};
static const struct regmap_config rt5677_regmap_physical = {
@@ -5018,10 +5030,47 @@ static const struct regmap_config rt5677_regmap = {
static const struct i2c_device_id rt5677_i2c_id[] = {
{ "rt5677", RT5677 },
{ "rt5676", RT5676 },
+ { "RT5677CE:00", RT5677 },
{ }
};
MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
+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 void rt5677_read_acpi_properties(struct rt5677_priv *rt5677,
+ struct device *dev)
+{
+ int ret;
+ u32 val;
+
+ ret = acpi_dev_add_driver_gpios(ACPI_COMPANION(dev),
+ bdw_rt5677_gpios);
+ if (ret)
+ dev_warn(dev, "Failed to add driver gpios\n");
+
+ if (!device_property_read_u32(dev, "DCLK", &val))
+ rt5677->pdata.dmic2_clk_pin = val;
+
+ rt5677->pdata.in1_diff = device_property_read_bool(dev, "IN1");
+ rt5677->pdata.in2_diff = device_property_read_bool(dev, "IN2");
+ rt5677->pdata.lout1_diff = device_property_read_bool(dev, "OUT1");
+ rt5677->pdata.lout2_diff = device_property_read_bool(dev, "OUT2");
+ rt5677->pdata.lout3_diff = device_property_read_bool(dev, "OUT3");
+
+ device_property_read_u32(dev, "JD1", &rt5677->pdata.jd1_gpio);
+ device_property_read_u32(dev, "JD2", &rt5677->pdata.jd2_gpio);
+ device_property_read_u32(dev, "JD3", &rt5677->pdata.jd3_gpio);
+}
+
static void rt5677_read_device_properties(struct rt5677_priv *rt5677,
struct device *dev)
{
@@ -5127,8 +5176,12 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
if (pdata)
rt5677->pdata = *pdata;
- else
+ else if (i2c->dev.of_node)
rt5677_read_device_properties(rt5677, &i2c->dev);
+ else if (ACPI_HANDLE(&i2c->dev))
+ rt5677_read_acpi_properties(rt5677, &i2c->dev);
+ else
+ return -EINVAL;
/* pow-ldo2 and reset are optional. The codec pins may be statically
* connected on the board without gpios. If the gpio device property
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 08b40460663c..1589325855bc 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -38,7 +38,6 @@
/* 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 +46,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 },
@@ -92,35 +89,8 @@ static const char *supply_names[SGTL5000_SUPPLY_NUM] = {
"VDDD"
};
-#define LDO_CONSUMER_NAME "VDDD_LDO"
#define LDO_VOLTAGE 1200000
-
-static struct regulator_consumer_supply ldo_consumer[] = {
- REGULATOR_SUPPLY(LDO_CONSUMER_NAME, NULL),
-};
-
-static struct regulator_init_data ldo_init_data = {
- .constraints = {
- .min_uV = 1200000,
- .max_uV = 1200000,
- .valid_modes_mask = REGULATOR_MODE_NORMAL,
- .valid_ops_mask = REGULATOR_CHANGE_STATUS,
- },
- .num_consumer_supplies = 1,
- .consumer_supplies = &ldo_consumer[0],
-};
-
-/*
- * sgtl5000 internal ldo regulator,
- * enabled when VDDD not provided
- */
-struct ldo_regulator {
- struct regulator_desc desc;
- struct regulator_dev *dev;
- int voltage;
- void *codec_data;
- bool enabled;
-};
+#define LINREG_VDDD ((1600 - LDO_VOLTAGE / 1000) / 50)
enum sgtl5000_micbias_resistor {
SGTL5000_MICBIAS_OFF = 0,
@@ -135,7 +105,7 @@ 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;
@@ -415,6 +385,9 @@ static const DECLARE_TLV_DB_RANGE(mic_gain_tlv,
/* 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);
+
static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
/* SOC_DOUBLE_S8_TLV with invert */
{
@@ -438,11 +411,21 @@ static const struct snd_kcontrol_new sgtl5000_snd_controls[] = {
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),
};
/* mute the codec used by alsa core */
@@ -778,155 +761,6 @@ static int sgtl5000_pcm_hw_params(struct snd_pcm_substream *substream,
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:
@@ -940,42 +774,17 @@ static int ldo_regulator_remove(struct snd_soc_codec *codec)
static int sgtl5000_set_bias_level(struct snd_soc_codec *codec,
enum snd_soc_bias_level level)
{
- int ret;
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
-
switch (level) {
case SND_SOC_BIAS_ON:
case SND_SOC_BIAS_PREPARE:
- break;
case SND_SOC_BIAS_STANDBY:
- if (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;
- }
- }
-
+ snd_soc_update_bits(codec, 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);
+ snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
+ SGTL5000_REFTOP_POWERUP, 0);
break;
}
@@ -1113,7 +922,6 @@ 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)
{
@@ -1131,7 +939,9 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
vdda = regulator_get_voltage(sgtl5000->supplies[VDDA].consumer);
vddio = regulator_get_voltage(sgtl5000->supplies[VDDIO].consumer);
- vddd = regulator_get_voltage(sgtl5000->supplies[VDDD].consumer);
+ vddd = (sgtl5000->num_supplies > VDDD)
+ ? regulator_get_voltage(sgtl5000->supplies[VDDD].consumer)
+ : LDO_VOLTAGE;
vdda = vdda / 1000;
vddio = vddio / 1000;
@@ -1178,25 +988,6 @@ static int sgtl5000_set_power_regs(struct snd_soc_codec *codec)
snd_soc_write(codec, SGTL5000_CHIP_ANA_POWER, ana_pwr);
- /* set voltage to register */
- snd_soc_update_bits(codec, SGTL5000_CHIP_LINREG_CTRL,
- SGTL5000_LINREG_VDDD_MASK, 0x8);
-
- /*
- * if vddd linear reg has been enabled,
- * simple digital supply should be clear to get
- * proper VDDD voltage.
- */
- if (ana_pwr & SGTL5000_LINEREG_D_POWERUP)
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINREG_SIMPLE_POWERUP,
- 0);
- else
- snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
- SGTL5000_LINREG_SIMPLE_POWERUP |
- SGTL5000_STARTUP_POWERUP,
- 0);
-
/*
* set ADC/DAC VAG to vdda / 2,
* should stay in range (0.8v, 1.575v)
@@ -1256,78 +1047,43 @@ 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)
@@ -1335,10 +1091,6 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
int ret;
struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
- ret = sgtl5000_enable_regulators(codec);
- if (ret)
- return ret;
-
/* power up sgtl5000 */
ret = sgtl5000_set_power_regs(codec);
if (ret)
@@ -1389,25 +1141,11 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
return 0;
err:
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- ldo_regulator_remove(codec);
-
return ret;
}
static int sgtl5000_remove(struct snd_soc_codec *codec)
{
- struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
-
- regulator_bulk_disable(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- regulator_bulk_free(ARRAY_SIZE(sgtl5000->supplies),
- sgtl5000->supplies);
- ldo_regulator_remove(codec);
-
return 0;
}
@@ -1416,12 +1154,14 @@ static struct snd_soc_codec_driver sgtl5000_driver = {
.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),
+ .component_driver = {
+ .controls = sgtl5000_snd_controls,
+ .num_controls = ARRAY_SIZE(sgtl5000_snd_controls),
+ .dapm_widgets = sgtl5000_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sgtl5000_dapm_widgets),
+ .dapm_routes = sgtl5000_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sgtl5000_dapm_routes),
+ },
};
static const struct regmap_config sgtl5000_regmap = {
@@ -1448,8 +1188,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,10 +1198,10 @@ 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,
@@ -1470,16 +1211,23 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
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);
@@ -1488,21 +1236,25 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
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;
+ 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 +1268,44 @@ 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);
+
+ /* 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\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,12 +1347,8 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
}
}
- i2c_set_clientdata(client, sgtl5000);
-
/* 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,
&sgtl5000_driver, &sgtl5000_dai, 1);
@@ -1573,6 +1359,11 @@ 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;
}
@@ -1582,6 +1373,9 @@ static int sgtl5000_i2c_remove(struct i2c_client *client)
snd_soc_unregister_codec(&client->dev);
clk_disable_unprepare(sgtl5000->mclk);
+ regulator_bulk_disable(sgtl5000->num_supplies, sgtl5000->supplies);
+ regulator_bulk_free(sgtl5000->num_supplies, sgtl5000->supplies);
+
return 0;
}
diff --git a/sound/soc/codecs/sgtl5000.h b/sound/soc/codecs/sgtl5000.h
index 1c317de26176..22f3442af982 100644
--- a/sound/soc/codecs/sgtl5000.h
+++ b/sound/soc/codecs/sgtl5000.h
@@ -92,6 +92,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
@@ -325,6 +326,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
diff --git a/sound/soc/codecs/si476x.c b/sound/soc/codecs/si476x.c
index a8402d0af0ea..5344f4aa8fde 100644
--- a/sound/soc/codecs/si476x.c
+++ b/sound/soc/codecs/si476x.c
@@ -238,10 +238,12 @@ static struct regmap *si476x_get_regmap(struct device *dev)
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),
+ .component_driver = {
+ .dapm_widgets = si476x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets),
+ .dapm_routes = si476x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes),
+ },
};
static int si476x_platform_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/sn95031.c b/sound/soc/codecs/sn95031.c
index 3a7de0159f24..eae54c37cff9 100644
--- a/sound/soc/codecs/sn95031.c
+++ b/sound/soc/codecs/sn95031.c
@@ -888,12 +888,14 @@ static struct snd_soc_codec_driver sn95031_codec = {
.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),
+ .component_driver = {
+ .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)
diff --git a/sound/soc/codecs/spdif_receiver.c b/sound/soc/codecs/spdif_receiver.c
index 3ec41ccbf4e2..234f87b54838 100644
--- a/sound/soc/codecs/spdif_receiver.c
+++ b/sound/soc/codecs/spdif_receiver.c
@@ -38,10 +38,12 @@ static const struct snd_soc_dapm_route dir_routes[] = {
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),
+ .component_driver = {
+ .dapm_widgets = dir_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dir_widgets),
+ .dapm_routes = dir_routes,
+ .num_dapm_routes = ARRAY_SIZE(dir_routes),
+ },
};
static struct snd_soc_dai_driver dir_stub_dai = {
diff --git a/sound/soc/codecs/spdif_transmitter.c b/sound/soc/codecs/spdif_transmitter.c
index ef634a9ad673..ee367536a498 100644
--- a/sound/soc/codecs/spdif_transmitter.c
+++ b/sound/soc/codecs/spdif_transmitter.c
@@ -38,10 +38,12 @@ static const struct snd_soc_dapm_route dit_routes[] = {
};
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),
+ .component_driver = {
+ .dapm_widgets = dit_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dit_widgets),
+ .dapm_routes = dit_routes,
+ .num_dapm_routes = ARRAY_SIZE(dit_routes),
+ },
};
static struct snd_soc_dai_driver dit_stub_dai = {
diff --git a/sound/soc/codecs/ssm2518.c b/sound/soc/codecs/ssm2518.c
index e2e0bfa7ec20..38a85f3adc80 100644
--- a/sound/soc/codecs/ssm2518.c
+++ b/sound/soc/codecs/ssm2518.c
@@ -715,12 +715,14 @@ static struct snd_soc_codec_driver ssm2518_codec_driver = {
.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),
+ .component_driver = {
+ .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 regmap_config ssm2518_regmap_config = {
diff --git a/sound/soc/codecs/ssm2602.c b/sound/soc/codecs/ssm2602.c
index 4452fea0b118..993bde29ca1b 100644
--- a/sound/soc/codecs/ssm2602.c
+++ b/sound/soc/codecs/ssm2602.c
@@ -597,12 +597,14 @@ static struct snd_soc_codec_driver soc_codec_dev_ssm2602 = {
.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),
+ .component_driver = {
+ .controls = ssm260x_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm260x_snd_controls),
+ .dapm_widgets = ssm260x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm260x_dapm_widgets),
+ .dapm_routes = ssm260x_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm260x_routes),
+ },
};
static bool ssm2602_register_volatile(struct device *dev, unsigned int reg)
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
index 080c78e88e10..2bb5a11c9ba1 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -421,12 +421,14 @@ 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),
+ .component_driver = {
+ .controls = ssm4567_snd_controls,
+ .num_controls = ARRAY_SIZE(ssm4567_snd_controls),
+ .dapm_widgets = ssm4567_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(ssm4567_dapm_widgets),
+ .dapm_routes = ssm4567_routes,
+ .num_dapm_routes = ARRAY_SIZE(ssm4567_routes),
+ },
};
static const struct regmap_config ssm4567_regmap_config = {
diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c
index a9844b2ac829..0790ae8530d9 100644
--- a/sound/soc/codecs/sta32x.c
+++ b/sound/soc/codecs/sta32x.c
@@ -991,12 +991,14 @@ static const struct snd_soc_codec_driver sta32x_codec = {
.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),
+ .component_driver = {
+ .controls = sta32x_snd_controls,
+ .num_controls = ARRAY_SIZE(sta32x_snd_controls),
+ .dapm_widgets = sta32x_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sta32x_dapm_widgets),
+ .dapm_routes = sta32x_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sta32x_dapm_routes),
+ },
};
static const struct regmap_config sta32x_regmap = {
diff --git a/sound/soc/codecs/sta350.c b/sound/soc/codecs/sta350.c
index 33a4612f0a07..9644c20f44e3 100644
--- a/sound/soc/codecs/sta350.c
+++ b/sound/soc/codecs/sta350.c
@@ -1057,12 +1057,14 @@ static const struct snd_soc_codec_driver sta350_codec = {
.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),
+ .component_driver = {
+ .controls = sta350_snd_controls,
+ .num_controls = ARRAY_SIZE(sta350_snd_controls),
+ .dapm_widgets = sta350_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sta350_dapm_widgets),
+ .dapm_routes = sta350_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sta350_dapm_routes),
+ },
};
static const struct regmap_config sta350_regmap = {
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index 2cdaca943a8c..d4b384e4b266 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -317,8 +317,10 @@ 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),
+ .component_driver = {
+ .controls = sta529_snd_controls,
+ .num_controls = ARRAY_SIZE(sta529_snd_controls),
+ },
};
static const struct regmap_config sta529_regmap = {
diff --git a/sound/soc/codecs/stac9766.c b/sound/soc/codecs/stac9766.c
index 0945c51df003..27f30d352867 100644
--- a/sound/soc/codecs/stac9766.c
+++ b/sound/soc/codecs/stac9766.c
@@ -85,10 +85,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),
@@ -320,8 +320,10 @@ static int stac9766_codec_remove(struct snd_soc_codec *codec)
}
static struct snd_soc_codec_driver soc_codec_dev_stac9766 = {
- .controls = stac9766_snd_ac97_controls,
- .num_controls = ARRAY_SIZE(stac9766_snd_ac97_controls),
+ .component_driver = {
+ .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,
diff --git a/sound/soc/codecs/sti-sas.c b/sound/soc/codecs/sti-sas.c
index 160d61a66204..7b31ee9b82bc 100644
--- a/sound/soc/codecs/sti-sas.c
+++ b/sound/soc/codecs/sti-sas.c
@@ -591,11 +591,11 @@ static int sti_sas_driver_probe(struct platform_device *pdev)
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.component_driver.dapm_widgets = drvdata->dev_data->dapm_widgets;
+ sti_sas_driver.component_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;
+ sti_sas_driver.component_driver.dapm_routes = drvdata->dev_data->dapm_routes;
+ sti_sas_driver.component_driver.num_dapm_routes = drvdata->dev_data->num_dapm_routes;
/* Store context */
dev_set_drvdata(&pdev->dev, drvdata);
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index cc1d3981fa4b..baf455e8c2f7 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -667,12 +667,14 @@ static struct snd_soc_codec_driver soc_codec_dev_tas2552 = {
.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),
+ .component_driver = {
+ .controls = tas2552_snd_controls,
+ .num_controls = ARRAY_SIZE(tas2552_snd_controls),
+ .dapm_widgets = tas2552_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas2552_dapm_widgets),
+ .dapm_routes = tas2552_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(tas2552_audio_map),
+ },
};
static const struct regmap_config tas2552_regmap_config = {
diff --git a/sound/soc/codecs/tas5086.c b/sound/soc/codecs/tas5086.c
index d49d25d51957..b7de857abb16 100644
--- a/sound/soc/codecs/tas5086.c
+++ b/sound/soc/codecs/tas5086.c
@@ -387,7 +387,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(codec->dev, "Invalid MCLK / Fs ratio\n");
return -EINVAL;
}
@@ -890,12 +890,14 @@ static struct snd_soc_codec_driver soc_codec_dev_tas5086 = {
.remove = tas5086_remove,
.suspend = tas5086_soc_suspend,
.resume = tas5086_soc_resume,
- .controls = tas5086_controls,
- .num_controls = ARRAY_SIZE(tas5086_controls),
- .dapm_widgets = tas5086_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(tas5086_dapm_widgets),
- .dapm_routes = tas5086_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(tas5086_dapm_routes),
+ .component_driver = {
+ .controls = tas5086_controls,
+ .num_controls = ARRAY_SIZE(tas5086_controls),
+ .dapm_widgets = tas5086_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tas5086_dapm_widgets),
+ .dapm_routes = tas5086_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tas5086_dapm_routes),
+ },
};
static const struct i2c_device_id tas5086_i2c_id[] = {
diff --git a/sound/soc/codecs/tas571x.c b/sound/soc/codecs/tas571x.c
index b8d19b77bde9..df5e5cb33baa 100644
--- a/sound/soc/codecs/tas571x.c
+++ b/sound/soc/codecs/tas571x.c
@@ -28,6 +28,7 @@
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include <sound/tlv.h>
+#include <asm/unaligned.h>
#include "tas571x.h"
@@ -63,6 +64,10 @@ static int tas571x_register_size(struct tas571x_private *priv, unsigned int reg)
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;
@@ -135,6 +140,129 @@ 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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct i2c_client *i2c = to_i2c_client(codec->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_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+ struct i2c_client *i2c = to_i2c_client(codec->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);
@@ -241,6 +369,15 @@ static const struct snd_soc_dai_ops tas571x_dai_ops = {
.digital_mute = tas571x_mute,
};
+
+#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",
@@ -264,6 +401,16 @@ static const struct snd_kcontrol_new tas5711_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),
};
static const struct regmap_range tas571x_readonly_regs_range[] = {
@@ -340,6 +487,43 @@ static const struct snd_kcontrol_new tas5717_controls[] = {
TAS571X_SOFT_MUTE_REG,
TAS571X_SOFT_MUTE_CH1_SHIFT, TAS571X_SOFT_MUTE_CH2_SHIFT,
1, 1),
+
+ /*
+ * 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[] = {
@@ -350,6 +534,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 = {
@@ -470,10 +658,12 @@ 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),
+ .component_driver = {
+ .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 struct snd_soc_dai_driver tas571x_dai = {
@@ -566,8 +756,8 @@ static int tas571x_i2c_probe(struct i2c_client *client,
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;
+ priv->codec_driver.component_driver.controls = priv->chip->controls;
+ priv->codec_driver.component_driver.num_controls = priv->chip->num_controls;
if (priv->chip->vol_reg_size == 2) {
/*
diff --git a/sound/soc/codecs/tas571x.h b/sound/soc/codecs/tas571x.h
index cf800c364f0f..c45677bc26ad 100644
--- a/sound/soc/codecs/tas571x.h
+++ b/sound/soc/codecs/tas571x.h
@@ -52,4 +52,44 @@
#define TAS571X_CH4_SRC_SELECT_REG 0x21
#define TAS571X_PWM_MUX_REG 0x25
+/* 20-byte biquad registers */
+#define TAS5717_CH1_BQ0_REG 0x26
+#define TAS5717_CH1_BQ1_REG 0x27
+#define TAS5717_CH1_BQ2_REG 0x28
+#define TAS5717_CH1_BQ3_REG 0x29
+#define TAS5717_CH1_BQ4_REG 0x2a
+#define TAS5717_CH1_BQ5_REG 0x2b
+#define TAS5717_CH1_BQ6_REG 0x2c
+#define TAS5717_CH1_BQ7_REG 0x2d
+#define TAS5717_CH1_BQ8_REG 0x2e
+#define TAS5717_CH1_BQ9_REG 0x2f
+
+#define TAS5717_CH2_BQ0_REG 0x30
+#define TAS5717_CH2_BQ1_REG 0x31
+#define TAS5717_CH2_BQ2_REG 0x32
+#define TAS5717_CH2_BQ3_REG 0x33
+#define TAS5717_CH2_BQ4_REG 0x34
+#define TAS5717_CH2_BQ5_REG 0x35
+#define TAS5717_CH2_BQ6_REG 0x36
+#define TAS5717_CH2_BQ7_REG 0x37
+#define TAS5717_CH2_BQ8_REG 0x38
+#define TAS5717_CH2_BQ9_REG 0x39
+
+#define TAS5717_CH1_BQ10_REG 0x58
+#define TAS5717_CH1_BQ11_REG 0x59
+
+#define TAS5717_CH4_BQ0_REG 0x5a
+#define TAS5717_CH4_BQ1_REG 0x5b
+
+#define TAS5717_CH2_BQ10_REG 0x5c
+#define TAS5717_CH2_BQ11_REG 0x5d
+
+#define TAS5717_CH3_BQ0_REG 0x5e
+#define TAS5717_CH3_BQ1_REG 0x5f
+
+#define TAS5717_CH1_RIGHT_CH_MIX_REG 0x72
+#define TAS5717_CH1_LEFT_CH_MIX_REG 0x73
+#define TAS5717_CH2_LEFT_CH_MIX_REG 0x76
+#define TAS5717_CH2_RIGHT_CH_MIX_REG 0x77
+
#endif /* _TAS571X_H */
diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c
index f54fb46b77c2..c65b917598d2 100644
--- a/sound/soc/codecs/tas5720.c
+++ b/sound/soc/codecs/tas5720.c
@@ -489,12 +489,14 @@ static struct snd_soc_codec_driver soc_codec_dev_tas5720 = {
.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),
+ .component_driver = {
+ .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),
+ },
};
/* PCM rates supported by the TAS5720 driver */
diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c
index cb5310d89c0f..95e0a7abeb7a 100644
--- a/sound/soc/codecs/tfa9879.c
+++ b/sound/soc/codecs/tfa9879.c
@@ -231,13 +231,14 @@ static const struct snd_soc_dapm_route tfa9879_dapm_routes[] = {
};
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),
+ .component_driver = {
+ .controls = tfa9879_controls,
+ .num_controls = ARRAY_SIZE(tfa9879_controls),
+ .dapm_widgets = tfa9879_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tfa9879_dapm_widgets),
+ .dapm_routes = tfa9879_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tfa9879_dapm_routes),
+ },
};
static const struct regmap_config tfa9879_regmap = {
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index cd8c02b6e4de..410cae0f2060 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -583,12 +583,14 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = {
.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),
+ .component_driver = {
+ .controls = tlv320aic23_snd_controls,
+ .num_controls = ARRAY_SIZE(tlv320aic23_snd_controls),
+ .dapm_widgets = tlv320aic23_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
+ .dapm_routes = tlv320aic23_intercon,
+ .num_dapm_routes = ARRAY_SIZE(tlv320aic23_intercon),
+ },
};
int tlv320aic23_probe(struct device *dev, struct regmap *regmap)
diff --git a/sound/soc/codecs/tlv320aic26.c b/sound/soc/codecs/tlv320aic26.c
index 2c904d7150ad..14aa96d41719 100644
--- a/sound/soc/codecs/tlv320aic26.c
+++ b/sound/soc/codecs/tlv320aic26.c
@@ -321,12 +321,14 @@ static int aic26_probe(struct snd_soc_codec *codec)
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),
+ .component_driver = {
+ .controls = aic26_snd_controls,
+ .num_controls = ARRAY_SIZE(aic26_snd_controls),
+ .dapm_widgets = tlv320aic26_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tlv320aic26_dapm_widgets),
+ .dapm_routes = tlv320aic26_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(tlv320aic26_dapm_routes),
+ },
};
static const struct regmap_config aic26_regmap = {
diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c
index 3c5e1df01c19..be1a64bfd320 100644
--- a/sound/soc/codecs/tlv320aic31xx.c
+++ b/sound/soc/codecs/tlv320aic31xx.c
@@ -273,10 +273,20 @@ 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),
+};
+
+static const struct snd_kcontrol_new aic31xx_snd_controls[] = {
SOC_SINGLE_TLV("ADC Fine Capture Volume", AIC31XX_ADCFGA, 4, 4, 1,
adc_fgain_tlv),
@@ -286,14 +296,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[] = {
@@ -397,17 +399,28 @@ static int aic31xx_dapm_power_event(struct snd_soc_dapm_widget *w,
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);
@@ -457,7 +470,7 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static const struct snd_soc_dapm_widget aic31xx_dapm_widgets[] = {
+static const struct snd_soc_dapm_widget common31xx_dapm_widgets[] = {
SND_SOC_DAPM_AIF_IN("DAC IN", "DAC Playback", 0, SND_SOC_NOPM, 0, 0),
SND_SOC_DAPM_MUX("DAC Left Input",
@@ -473,14 +486,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,
@@ -494,10 +500,34 @@ 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),
+
+ /* 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,
@@ -507,24 +537,25 @@ 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"),
-
- /* Inputs */
- SND_SOC_DAPM_INPUT("MIC1LP"),
- SND_SOC_DAPM_INPUT("MIC1RP"),
- SND_SOC_DAPM_INPUT("MIC1LM"),
+ /* 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)),
};
static const struct snd_soc_dapm_widget aic311x_dapm_widgets[] = {
@@ -554,7 +585,7 @@ 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"},
@@ -565,6 +596,31 @@ aic31xx_audio_map[] = {
{"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"},
@@ -595,16 +651,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
@@ -633,6 +679,13 @@ static int aic31xx_add_controls(struct snd_soc_codec *codec)
int ret = 0;
struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
+ if (!(aic31xx->pdata.codec_type & DAC31XX_BIT))
+ ret = snd_soc_add_codec_controls(
+ codec, aic31xx_snd_controls,
+ ARRAY_SIZE(aic31xx_snd_controls));
+ if (ret)
+ return ret;
+
if (aic31xx->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT)
ret = snd_soc_add_codec_controls(
codec, aic311x_snd_controls,
@@ -651,6 +704,30 @@ static int aic31xx_add_widgets(struct snd_soc_codec *codec)
struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
int ret = 0;
+ if (aic31xx->pdata.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->pdata.codec_type & AIC31XX_STEREO_CLASS_D_BIT) {
ret = snd_soc_dapm_new_controls(
dapm, aic311x_dapm_widgets,
@@ -1114,12 +1191,14 @@ static struct snd_soc_codec_driver soc_codec_driver_aic31xx = {
.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),
+ .component_driver = {
+ .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),
+ },
};
static const struct snd_soc_dai_ops aic31xx_dai_ops = {
@@ -1129,19 +1208,34 @@ static const struct snd_soc_dai_ops aic31xx_dai_ops = {
.digital_mute = aic31xx_dac_mute,
};
+static struct snd_soc_dai_driver dac31xx_dai_driver[] = {
+ {
+ .name = "tlv32dac31xx-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = AIC31XX_RATES,
+ .formats = AIC31XX_FORMATS,
+ },
+ .ops = &aic31xx_dai_ops,
+ .symmetric_rates = 1,
+ }
+};
+
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,
@@ -1259,9 +1353,16 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c,
if (ret)
return ret;
- return snd_soc_register_codec(&i2c->dev, &soc_codec_driver_aic31xx,
- aic31xx_dai_driver,
- ARRAY_SIZE(aic31xx_dai_driver));
+ if (aic31xx->pdata.codec_type & DAC31XX_BIT)
+ return snd_soc_register_codec(&i2c->dev,
+ &soc_codec_driver_aic31xx,
+ dac31xx_dai_driver,
+ ARRAY_SIZE(dac31xx_dai_driver));
+ else
+ return snd_soc_register_codec(&i2c->dev,
+ &soc_codec_driver_aic31xx,
+ aic31xx_dai_driver,
+ ARRAY_SIZE(aic31xx_dai_driver));
}
static int aic31xx_i2c_remove(struct i2c_client *i2c)
@@ -1277,6 +1378,7 @@ static const struct i2c_device_id aic31xx_i2c_id[] = {
{ "tlv320aic3110", AIC3110 },
{ "tlv320aic3120", AIC3120 },
{ "tlv320aic3111", AIC3111 },
+ { "tlv320dac3100", DAC3100 },
{ }
};
MODULE_DEVICE_TABLE(i2c, aic31xx_i2c_id);
diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h
index fe16c34607bb..5acd5b69fb83 100644
--- a/sound/soc/codecs/tlv320aic31xx.h
+++ b/sound/soc/codecs/tlv320aic31xx.h
@@ -24,12 +24,14 @@
#define AIC31XX_STEREO_CLASS_D_BIT 0x1
#define AIC31XX_MINIDSP_BIT 0x2
+#define DAC31XX_BIT 0x4
enum aic31xx_type {
AIC3100 = 0,
AIC3110 = AIC31XX_STEREO_CLASS_D_BIT,
AIC3120 = AIC31XX_MINIDSP_BIT,
AIC3111 = (AIC31XX_STEREO_CLASS_D_BIT | AIC31XX_MINIDSP_BIT),
+ DAC3100 = DAC31XX_BIT,
};
struct aic31xx_pdata {
@@ -38,141 +40,143 @@ struct aic31xx_pdata {
int micbias_vg;
};
+#define AIC31XX_REG(page, reg) ((page * 128) + reg)
+
/* Page Control Register */
-#define AIC31XX_PAGECTL 0x00
+#define AIC31XX_PAGECTL AIC31XX_REG(0, 0)
/* Page 0 Registers */
/* Software reset register */
-#define AIC31XX_RESET 0x01
+#define AIC31XX_RESET AIC31XX_REG(0, 1)
/* OT FLAG register */
-#define AIC31XX_OT_FLAG 0x03
+#define AIC31XX_OT_FLAG AIC31XX_REG(0, 3)
/* Clock clock Gen muxing, Multiplexers*/
-#define AIC31XX_CLKMUX 0x04
+#define AIC31XX_CLKMUX AIC31XX_REG(0, 4)
/* PLL P and R-VAL register */
-#define AIC31XX_PLLPR 0x05
+#define AIC31XX_PLLPR AIC31XX_REG(0, 5)
/* PLL J-VAL register */
-#define AIC31XX_PLLJ 0x06
+#define AIC31XX_PLLJ AIC31XX_REG(0, 6)
/* PLL D-VAL MSB register */
-#define AIC31XX_PLLDMSB 0x07
+#define AIC31XX_PLLDMSB AIC31XX_REG(0, 7)
/* PLL D-VAL LSB register */
-#define AIC31XX_PLLDLSB 0x08
+#define AIC31XX_PLLDLSB AIC31XX_REG(0, 8)
/* DAC NDAC_VAL register*/
-#define AIC31XX_NDAC 0x0B
+#define AIC31XX_NDAC AIC31XX_REG(0, 11)
/* DAC MDAC_VAL register */
-#define AIC31XX_MDAC 0x0C
+#define AIC31XX_MDAC AIC31XX_REG(0, 12)
/* DAC OSR setting register 1, MSB value */
-#define AIC31XX_DOSRMSB 0x0D
+#define AIC31XX_DOSRMSB AIC31XX_REG(0, 13)
/* DAC OSR setting register 2, LSB value */
-#define AIC31XX_DOSRLSB 0x0E
-#define AIC31XX_MINI_DSP_INPOL 0x10
+#define AIC31XX_DOSRLSB AIC31XX_REG(0, 14)
+#define AIC31XX_MINI_DSP_INPOL AIC31XX_REG(0, 16)
/* Clock setting register 8, PLL */
-#define AIC31XX_NADC 0x12
+#define AIC31XX_NADC AIC31XX_REG(0, 18)
/* Clock setting register 9, PLL */
-#define AIC31XX_MADC 0x13
+#define AIC31XX_MADC AIC31XX_REG(0, 19)
/* ADC Oversampling (AOSR) Register */
-#define AIC31XX_AOSR 0x14
+#define AIC31XX_AOSR AIC31XX_REG(0, 20)
/* Clock setting register 9, Multiplexers */
-#define AIC31XX_CLKOUTMUX 0x19
+#define AIC31XX_CLKOUTMUX AIC31XX_REG(0, 25)
/* Clock setting register 10, CLOCKOUT M divider value */
-#define AIC31XX_CLKOUTMVAL 0x1A
+#define AIC31XX_CLKOUTMVAL AIC31XX_REG(0, 26)
/* Audio Interface Setting Register 1 */
-#define AIC31XX_IFACE1 0x1B
+#define AIC31XX_IFACE1 AIC31XX_REG(0, 27)
/* Audio Data Slot Offset Programming */
-#define AIC31XX_DATA_OFFSET 0x1C
+#define AIC31XX_DATA_OFFSET AIC31XX_REG(0, 28)
/* Audio Interface Setting Register 2 */
-#define AIC31XX_IFACE2 0x1D
+#define AIC31XX_IFACE2 AIC31XX_REG(0, 29)
/* Clock setting register 11, BCLK N Divider */
-#define AIC31XX_BCLKN 0x1E
+#define AIC31XX_BCLKN AIC31XX_REG(0, 30)
/* Audio Interface Setting Register 3, Secondary Audio Interface */
-#define AIC31XX_IFACESEC1 0x1F
+#define AIC31XX_IFACESEC1 AIC31XX_REG(0, 31)
/* Audio Interface Setting Register 4 */
-#define AIC31XX_IFACESEC2 0x20
+#define AIC31XX_IFACESEC2 AIC31XX_REG(0, 32)
/* Audio Interface Setting Register 5 */
-#define AIC31XX_IFACESEC3 0x21
+#define AIC31XX_IFACESEC3 AIC31XX_REG(0, 33)
/* I2C Bus Condition */
-#define AIC31XX_I2C 0x22
+#define AIC31XX_I2C AIC31XX_REG(0, 34)
/* ADC FLAG */
-#define AIC31XX_ADCFLAG 0x24
+#define AIC31XX_ADCFLAG AIC31XX_REG(0, 36)
/* DAC Flag Registers */
-#define AIC31XX_DACFLAG1 0x25
-#define AIC31XX_DACFLAG2 0x26
+#define AIC31XX_DACFLAG1 AIC31XX_REG(0, 37)
+#define AIC31XX_DACFLAG2 AIC31XX_REG(0, 38)
/* Sticky Interrupt flag (overflow) */
-#define AIC31XX_OFFLAG 0x27
+#define AIC31XX_OFFLAG AIC31XX_REG(0, 39)
/* Sticy DAC Interrupt flags */
-#define AIC31XX_INTRDACFLAG 0x2C
+#define AIC31XX_INTRDACFLAG AIC31XX_REG(0, 44)
/* Sticy ADC Interrupt flags */
-#define AIC31XX_INTRADCFLAG 0x2D
+#define AIC31XX_INTRADCFLAG AIC31XX_REG(0, 45)
/* DAC Interrupt flags 2 */
-#define AIC31XX_INTRDACFLAG2 0x2E
+#define AIC31XX_INTRDACFLAG2 AIC31XX_REG(0, 46)
/* ADC Interrupt flags 2 */
-#define AIC31XX_INTRADCFLAG2 0x2F
+#define AIC31XX_INTRADCFLAG2 AIC31XX_REG(0, 47)
/* INT1 interrupt control */
-#define AIC31XX_INT1CTRL 0x30
+#define AIC31XX_INT1CTRL AIC31XX_REG(0, 48)
/* INT2 interrupt control */
-#define AIC31XX_INT2CTRL 0x31
+#define AIC31XX_INT2CTRL AIC31XX_REG(0, 49)
/* GPIO1 control */
-#define AIC31XX_GPIO1 0x33
+#define AIC31XX_GPIO1 AIC31XX_REG(0, 50)
-#define AIC31XX_DACPRB 0x3C
+#define AIC31XX_DACPRB AIC31XX_REG(0, 60)
/* ADC Instruction Set Register */
-#define AIC31XX_ADCPRB 0x3D
+#define AIC31XX_ADCPRB AIC31XX_REG(0, 61)
/* DAC channel setup register */
-#define AIC31XX_DACSETUP 0x3F
+#define AIC31XX_DACSETUP AIC31XX_REG(0, 63)
/* DAC Mute and volume control register */
-#define AIC31XX_DACMUTE 0x40
+#define AIC31XX_DACMUTE AIC31XX_REG(0, 64)
/* Left DAC channel digital volume control */
-#define AIC31XX_LDACVOL 0x41
+#define AIC31XX_LDACVOL AIC31XX_REG(0, 65)
/* Right DAC channel digital volume control */
-#define AIC31XX_RDACVOL 0x42
+#define AIC31XX_RDACVOL AIC31XX_REG(0, 66)
/* Headset detection */
-#define AIC31XX_HSDETECT 0x43
+#define AIC31XX_HSDETECT AIC31XX_REG(0, 67)
/* ADC Digital Mic */
-#define AIC31XX_ADCSETUP 0x51
+#define AIC31XX_ADCSETUP AIC31XX_REG(0, 81)
/* ADC Digital Volume Control Fine Adjust */
-#define AIC31XX_ADCFGA 0x52
+#define AIC31XX_ADCFGA AIC31XX_REG(0, 82)
/* ADC Digital Volume Control Coarse Adjust */
-#define AIC31XX_ADCVOL 0x53
+#define AIC31XX_ADCVOL AIC31XX_REG(0, 83)
/* Page 1 Registers */
/* Headphone drivers */
-#define AIC31XX_HPDRIVER 0x9F
+#define AIC31XX_HPDRIVER AIC31XX_REG(1, 31)
/* Class-D Speakear Amplifier */
-#define AIC31XX_SPKAMP 0xA0
+#define AIC31XX_SPKAMP AIC31XX_REG(1, 32)
/* HP Output Drivers POP Removal Settings */
-#define AIC31XX_HPPOP 0xA1
+#define AIC31XX_HPPOP AIC31XX_REG(1, 33)
/* Output Driver PGA Ramp-Down Period Control */
-#define AIC31XX_SPPGARAMP 0xA2
+#define AIC31XX_SPPGARAMP AIC31XX_REG(1, 34)
/* DAC_L and DAC_R Output Mixer Routing */
-#define AIC31XX_DACMIXERROUTE 0xA3
+#define AIC31XX_DACMIXERROUTE AIC31XX_REG(1, 35)
/* Left Analog Vol to HPL */
-#define AIC31XX_LANALOGHPL 0xA4
+#define AIC31XX_LANALOGHPL AIC31XX_REG(1, 36)
/* Right Analog Vol to HPR */
-#define AIC31XX_RANALOGHPR 0xA5
+#define AIC31XX_RANALOGHPR AIC31XX_REG(1, 37)
/* Left Analog Vol to SPL */
-#define AIC31XX_LANALOGSPL 0xA6
+#define AIC31XX_LANALOGSPL AIC31XX_REG(1, 38)
/* Right Analog Vol to SPR */
-#define AIC31XX_RANALOGSPR 0xA7
+#define AIC31XX_RANALOGSPR AIC31XX_REG(1, 39)
/* HPL Driver */
-#define AIC31XX_HPLGAIN 0xA8
+#define AIC31XX_HPLGAIN AIC31XX_REG(1, 40)
/* HPR Driver */
-#define AIC31XX_HPRGAIN 0xA9
+#define AIC31XX_HPRGAIN AIC31XX_REG(1, 41)
/* SPL Driver */
-#define AIC31XX_SPLGAIN 0xAA
+#define AIC31XX_SPLGAIN AIC31XX_REG(1, 42)
/* SPR Driver */
-#define AIC31XX_SPRGAIN 0xAB
+#define AIC31XX_SPRGAIN AIC31XX_REG(1, 43)
/* HP Driver Control */
-#define AIC31XX_HPCONTROL 0xAC
+#define AIC31XX_HPCONTROL AIC31XX_REG(1, 44)
/* MIC Bias Control */
-#define AIC31XX_MICBIAS 0xAE
+#define AIC31XX_MICBIAS AIC31XX_REG(1, 46)
/* MIC PGA*/
-#define AIC31XX_MICPGA 0xAF
+#define AIC31XX_MICPGA AIC31XX_REG(1, 47)
/* Delta-Sigma Mono ADC Channel Fine-Gain Input Selection for P-Terminal */
-#define AIC31XX_MICPGAPI 0xB0
+#define AIC31XX_MICPGAPI AIC31XX_REG(1, 48)
/* ADC Input Selection for M-Terminal */
-#define AIC31XX_MICPGAMI 0xB1
+#define AIC31XX_MICPGAMI AIC31XX_REG(1, 49)
/* Input CM Settings */
-#define AIC31XX_MICPGACM 0xB2
+#define AIC31XX_MICPGACM AIC31XX_REG(1, 50)
/* Bits, masks and shifts */
diff --git a/sound/soc/codecs/tlv320aic32x4.c b/sound/soc/codecs/tlv320aic32x4.c
index 85d4978d0384..28fdfc5ec544 100644
--- a/sound/soc/codecs/tlv320aic32x4.c
+++ b/sound/soc/codecs/tlv320aic32x4.c
@@ -797,12 +797,14 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
.set_bias_level = aic32x4_set_bias_level,
.suspend_bias_off = true,
- .controls = aic32x4_snd_controls,
- .num_controls = ARRAY_SIZE(aic32x4_snd_controls),
- .dapm_widgets = aic32x4_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets),
- .dapm_routes = aic32x4_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes),
+ .component_driver = {
+ .controls = aic32x4_snd_controls,
+ .num_controls = ARRAY_SIZE(aic32x4_snd_controls),
+ .dapm_widgets = aic32x4_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets),
+ .dapm_routes = aic32x4_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes),
+ },
};
static int aic32x4_parse_dt(struct aic32x4_priv *aic32x4,
diff --git a/sound/soc/codecs/tlv320aic3x.c b/sound/soc/codecs/tlv320aic3x.c
index a564759845f9..5a8d96ec058c 100644
--- a/sound/soc/codecs/tlv320aic3x.c
+++ b/sound/soc/codecs/tlv320aic3x.c
@@ -1670,12 +1670,14 @@ static struct snd_soc_codec_driver soc_codec_dev_aic3x = {
.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),
+ .component_driver = {
+ .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),
+ },
};
/*
diff --git a/sound/soc/codecs/tlv320dac33.c b/sound/soc/codecs/tlv320dac33.c
index f7a6ce7e5fb1..7bcf01efdf9a 100644
--- a/sound/soc/codecs/tlv320dac33.c
+++ b/sound/soc/codecs/tlv320dac33.c
@@ -90,7 +90,6 @@ 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 regulator_bulk_data supplies[DAC33_NUM_SUPPLIES];
@@ -771,7 +770,7 @@ 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;
}
@@ -1127,7 +1126,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:
@@ -1135,7 +1134,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:
@@ -1410,14 +1409,6 @@ static int dac33_soc_probe(struct snd_soc_codec *codec)
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);
}
}
@@ -1437,7 +1428,7 @@ static int dac33_soc_remove(struct snd_soc_codec *codec)
if (dac33->irq >= 0) {
free_irq(dac33->irq, dac33->codec);
- destroy_workqueue(dac33->dac33_wq);
+ flush_work(&dac33->work);
}
return 0;
}
@@ -1453,12 +1444,14 @@ static struct snd_soc_codec_driver soc_codec_dev_tlv320dac33 = {
.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),
+ .component_driver = {
+ .controls = dac33_snd_controls,
+ .num_controls = ARRAY_SIZE(dac33_snd_controls),
+ .dapm_widgets = dac33_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dac33_dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ },
};
#define DAC33_RATES (SNDRV_PCM_RATE_44100 | \
diff --git a/sound/soc/codecs/tpa6130a2.c b/sound/soc/codecs/tpa6130a2.c
index 11d85c5c787a..2e014c80d113 100644
--- a/sound/soc/codecs/tpa6130a2.c
+++ b/sound/soc/codecs/tpa6130a2.c
@@ -32,6 +32,7 @@
#include <sound/tlv.h>
#include <linux/of.h>
#include <linux/of_gpio.h>
+#include <linux/regmap.h>
#include "tpa6130a2.h"
@@ -40,134 +41,52 @@ 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;
enum tpa_model id;
};
-static int tpa6130a2_i2c_read(int reg)
-{
- struct tpa6130a2_data *data;
- int val;
-
- if (WARN_ON(!tpa6130a2_client))
- return -EINVAL;
- data = i2c_get_clientdata(tpa6130a2_client);
-
- /* If powered off, return the cached value */
- if (data->power_state) {
- val = i2c_smbus_read_byte_data(tpa6130a2_client, reg);
- if (val < 0)
- dev_err(&tpa6130a2_client->dev, "Read failed\n");
- else
- data->regs[reg] = val;
- } else {
- val = data->regs[reg];
- }
-
- return val;
-}
-
-static int tpa6130a2_i2c_write(int reg, u8 value)
-{
- struct tpa6130a2_data *data;
- int val = 0;
-
- if (WARN_ON(!tpa6130a2_client))
- return -EINVAL;
- data = i2c_get_clientdata(tpa6130a2_client);
-
- if (data->power_state) {
- val = i2c_smbus_write_byte_data(tpa6130a2_client, reg, value);
- if (val < 0) {
- dev_err(&tpa6130a2_client->dev, "Write failed\n");
- return val;
- }
- }
-
- /* Either powered on or off, we save the context */
- data->regs[reg] = value;
-
- return val;
-}
-
-static u8 tpa6130a2_read(int reg)
-{
- struct tpa6130a2_data *data;
-
- if (WARN_ON(!tpa6130a2_client))
- return 0;
- data = i2c_get_clientdata(tpa6130a2_client);
-
- return data->regs[reg];
-}
-
-static int tpa6130a2_initialize(void)
-{
- struct tpa6130a2_data *data;
- int i, ret = 0;
-
- if (WARN_ON(!tpa6130a2_client))
- return -EINVAL;
- data = i2c_get_clientdata(tpa6130a2_client);
-
- for (i = 1; i < TPA6130A2_REG_VERSION; i++) {
- ret = tpa6130a2_i2c_write(i, data->regs[i]);
- if (ret < 0)
- break;
- }
-
- return ret;
-}
-
-static int tpa6130a2_power(u8 power)
+static int tpa6130a2_power(struct tpa6130a2_data *data, bool enable)
{
- 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;
+ int ret = 0, ret2;
- 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");
+ /* Sync registers */
+ regcache_cache_only(data->regmap, false);
+ ret = regcache_sync(data->regmap);
+ if (ret != 0) {
+ dev_err(data->dev,
+ "Failed to sync registers: %d\n", ret);
+ regcache_cache_only(data->regmap, true);
if (data->power_gpio >= 0)
gpio_set_value(data->power_gpio, 0);
- regulator_disable(data->supply);
- data->power_state = 0;
- goto exit;
+ 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)
@@ -175,84 +94,28 @@ static int tpa6130a2_power(u8 power)
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);
-
- ucontrol->value.integer.value[0] =
- (tpa6130a2_read(reg) >> shift) & mask;
-
- if (invert)
- ucontrol->value.integer.value[0] =
- max - ucontrol->value.integer.value[0];
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct tpa6130a2_data *data = snd_soc_component_get_drvdata(c);
- 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 +136,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,85 +148,79 @@ 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 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,
const struct i2c_device_id *id)
@@ -374,6 +230,7 @@ static int tpa6130a2_probe(struct i2c_client *client,
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,6 +239,12 @@ static int tpa6130a2_probe(struct i2c_client *client,
if (!data)
return -ENOMEM;
+ data->dev = dev;
+
+ data->regmap = devm_regmap_init_i2c(client, &tpa6130a2_regmap_config);
+ if (IS_ERR(data->regmap))
+ return PTR_ERR(data->regmap);
+
if (pdata) {
data->power_gpio = pdata->power_gpio;
} else if (np) {
@@ -392,26 +255,17 @@ static int tpa6130a2_probe(struct i2c_client *client,
return -ENODEV;
}
- tpa6130a2_client = client;
-
- i2c_set_clientdata(tpa6130a2_client, data);
+ i2c_set_clientdata(client, data);
data->id = id->driver_data;
- mutex_init(&data->mutex);
-
- /* Set default register values */
- data->regs[TPA6130A2_REG_CONTROL] = TPA6130A2_SWS;
- data->regs[TPA6130A2_REG_VOL_MUTE] = TPA6130A2_MUTE_R |
- TPA6130A2_MUTE_L;
-
if (data->power_gpio >= 0) {
ret = devm_gpio_request(dev, data->power_gpio,
"tpa6130a2 enable");
if (ret < 0) {
dev_err(dev, "Failed to request power GPIO (%d)\n",
data->power_gpio);
- goto err_gpio;
+ return ret;
}
gpio_direction_output(data->power_gpio, 0);
}
@@ -432,39 +286,27 @@ 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 0;
-
-err_gpio:
- tpa6130a2_client = NULL;
-
- return ret;
-}
-
-static int tpa6130a2_remove(struct i2c_client *client)
-{
- tpa6130a2_power(0);
- tpa6130a2_client = NULL;
+ return ret;
- return 0;
+ return devm_snd_soc_register_component(&client->dev,
+ &tpa6130a2_component_driver, NULL, 0);
}
static const struct i2c_device_id tpa6130a2_id[] = {
@@ -489,7 +331,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,
};
diff --git a/sound/soc/codecs/tpa6130a2.h b/sound/soc/codecs/tpa6130a2.h
index 417444020ba6..f19cad5d4172 100644
--- a/sound/soc/codecs/tpa6130a2.h
+++ b/sound/soc/codecs/tpa6130a2.h
@@ -30,19 +30,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 +57,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/twl4030.c b/sound/soc/codecs/twl4030.c
index a5a4e9f75c57..a2104d68169d 100644
--- a/sound/soc/codecs/twl4030.c
+++ b/sound/soc/codecs/twl4030.c
@@ -2199,12 +2199,14 @@ static struct snd_soc_codec_driver soc_codec_dev_twl4030 = {
.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),
+ .component_driver = {
+ .controls = twl4030_snd_controls,
+ .num_controls = ARRAY_SIZE(twl4030_snd_controls),
+ .dapm_widgets = twl4030_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(twl4030_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ },
};
static int twl4030_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 1f7081043566..748036e851ea 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -1156,12 +1156,14 @@ static struct snd_soc_codec_driver soc_codec_dev_twl6040 = {
.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),
+ .component_driver = {
+ .controls = twl6040_snd_controls,
+ .num_controls = ARRAY_SIZE(twl6040_snd_controls),
+ .dapm_widgets = twl6040_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(twl6040_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ },
};
static int twl6040_codec_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/uda134x.c b/sound/soc/codecs/uda134x.c
index e4c694c758b8..5fdee874406d 100644
--- a/sound/soc/codecs/uda134x.c
+++ b/sound/soc/codecs/uda134x.c
@@ -523,10 +523,12 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
.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),
+ .component_driver = {
+ .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 = {
@@ -544,6 +546,7 @@ static int uda134x_codec_probe(struct platform_device *pdev)
{
struct uda134x_platform_data *pd = pdev->dev.platform_data;
struct uda134x_priv *uda134x;
+ int ret;
if (!pd) {
dev_err(&pdev->dev, "Missing L3 bitbang function\n");
@@ -557,6 +560,12 @@ static int uda134x_codec_probe(struct platform_device *pdev)
uda134x->pd = pd;
platform_set_drvdata(pdev, uda134x);
+ if (pd->l3.use_gpios) {
+ ret = l3_set_gpio_ops(&pdev->dev, &uda134x->pd->l3);
+ if (ret < 0)
+ return ret;
+ }
+
uda134x->regmap = devm_regmap_init(&pdev->dev, NULL, pd,
&uda134x_regmap_config);
if (IS_ERR(uda134x->regmap))
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 35f0469ebb16..533e3bb444e4 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -765,12 +765,14 @@ static struct snd_soc_codec_driver soc_codec_dev_uda1380 = {
.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),
+ .component_driver = {
+ .controls = uda1380_snd_controls,
+ .num_controls = ARRAY_SIZE(uda1380_snd_controls),
+ .dapm_widgets = uda1380_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(uda1380_dapm_widgets),
+ .dapm_routes = uda1380_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(uda1380_dapm_routes),
+ },
};
#if IS_ENABLED(CONFIG_I2C)
diff --git a/sound/soc/codecs/wl1273.c b/sound/soc/codecs/wl1273.c
index 1b79778098d2..fcffb6e707d9 100644
--- a/sound/soc/codecs/wl1273.c
+++ b/sound/soc/codecs/wl1273.c
@@ -484,12 +484,14 @@ 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),
+ .component_driver = {
+ .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)
diff --git a/sound/soc/codecs/wm0010.c b/sound/soc/codecs/wm0010.c
index e3c34bdc2772..0eb5dcf4c29d 100644
--- a/sound/soc/codecs/wm0010.c
+++ b/sound/soc/codecs/wm0010.c
@@ -789,16 +789,18 @@ static int wm0010_set_sysclk(struct snd_soc_codec *codec, int source,
static int wm0010_probe(struct snd_soc_codec *codec);
-static struct snd_soc_codec_driver soc_codec_dev_wm0010 = {
+static const 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),
+ .component_driver = {
+ .dapm_widgets = wm0010_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm0010_dapm_widgets),
+ .dapm_routes = wm0010_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm0010_dapm_routes),
+ },
};
#define WM0010_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
diff --git a/sound/soc/codecs/wm1250-ev1.c b/sound/soc/codecs/wm1250-ev1.c
index ec45c5b220b1..cf5f0580df6a 100644
--- a/sound/soc/codecs/wm1250-ev1.c
+++ b/sound/soc/codecs/wm1250-ev1.c
@@ -141,12 +141,13 @@ 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),
-
+static const struct snd_soc_codec_driver soc_codec_dev_wm1250_ev1 = {
+ .component_driver = {
+ .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,
};
diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c
index a67ea10f41a1..23cde3a0dc11 100644
--- a/sound/soc/codecs/wm2000.c
+++ b/sound/soc/codecs/wm2000.c
@@ -581,7 +581,7 @@ static int wm2000_anc_transition(struct wm2000_priv *wm2000,
if (anc_transitions[i].dest == ANC_OFF)
clk_disable_unprepare(wm2000->mclk);
- return ret;
+ return 0;
}
static int wm2000_anc_set_mode(struct wm2000_priv *wm2000)
@@ -799,18 +799,20 @@ static int wm2000_remove(struct snd_soc_codec *codec)
return wm2000_anc_transition(wm2000, ANC_OFF);
}
-static struct snd_soc_codec_driver soc_codec_dev_wm2000 = {
+static const 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),
+ .component_driver = {
+ .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),
+ },
};
static int wm2000_i2c_probe(struct i2c_client *i2c,
diff --git a/sound/soc/codecs/wm2200.c b/sound/soc/codecs/wm2200.c
index fd1439ecb50a..606bf88abfc4 100644
--- a/sound/soc/codecs/wm2200.c
+++ b/sound/soc/codecs/wm2200.c
@@ -2103,7 +2103,7 @@ static struct snd_soc_dai_driver wm2200_dai = {
.ops = &wm2200_dai_ops,
};
-static struct snd_soc_codec_driver soc_codec_wm2200 = {
+static const struct snd_soc_codec_driver soc_codec_wm2200 = {
.probe = wm2200_probe,
.idle_bias_off = true,
@@ -2111,12 +2111,14 @@ static struct snd_soc_codec_driver soc_codec_wm2200 = {
.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),
+ .component_driver = {
+ .controls = wm2200_snd_controls,
+ .num_controls = ARRAY_SIZE(wm2200_snd_controls),
+ .dapm_widgets = wm2200_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm2200_dapm_widgets),
+ .dapm_routes = wm2200_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm2200_dapm_routes),
+ },
};
static irqreturn_t wm2200_irq(int irq, void *data)
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 512a9d25fe6f..560575000cc5 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2285,7 +2285,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,
@@ -2381,7 +2381,7 @@ static int wm5100_remove(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm5100 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm5100 = {
.probe = wm5100_probe,
.remove = wm5100_remove,
@@ -2390,12 +2390,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5100 = {
.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),
+ .component_driver = {
+ .controls = wm5100_snd_controls,
+ .num_controls = ARRAY_SIZE(wm5100_snd_controls),
+ .dapm_widgets = wm5100_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm5100_dapm_widgets),
+ .dapm_routes = wm5100_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm5100_dapm_routes),
+ },
};
static const struct regmap_config wm5100_regmap = {
diff --git a/sound/soc/codecs/wm5102.c b/sound/soc/codecs/wm5102.c
index da60e3fe5ee7..93876c6d48ee 100644
--- a/sound/soc/codecs/wm5102.c
+++ b/sound/soc/codecs/wm5102.c
@@ -1521,6 +1521,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" },
@@ -1600,7 +1610,6 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
{ "Slim3 Capture", NULL, "SYSCLK" },
{ "Audio Trace DSP", NULL, "DSP1" },
- { "Audio Trace DSP", NULL, "SYSCLK" },
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@@ -1713,6 +1722,7 @@ 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" },
};
@@ -1872,7 +1882,7 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
.capture = {
.stream_name = "Audio Trace CPU",
.channels_min = 1,
- .channels_max = 6,
+ .channels_max = 4,
.rates = WM5102_RATES,
.formats = WM5102_FORMATS,
},
@@ -1989,7 +1999,7 @@ static struct regmap *wm5102_get_regmap(struct device *dev)
return priv->core.arizona->regmap;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm5102 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm5102 = {
.probe = wm5102_codec_probe,
.remove = wm5102_codec_remove,
.get_regmap = wm5102_get_regmap,
@@ -1999,12 +2009,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5102 = {
.set_sysclk = arizona_set_sysclk,
.set_pll = wm5102_set_fll,
- .controls = wm5102_snd_controls,
- .num_controls = ARRAY_SIZE(wm5102_snd_controls),
- .dapm_widgets = wm5102_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(wm5102_dapm_widgets),
- .dapm_routes = wm5102_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(wm5102_dapm_routes),
+ .component_driver = {
+ .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 struct snd_compr_ops wm5102_compr_ops = {
diff --git a/sound/soc/codecs/wm5110.c b/sound/soc/codecs/wm5110.c
index b5820e4d5471..06bae3b23fce 100644
--- a/sound/soc/codecs/wm5110.c
+++ b/sound/soc/codecs/wm5110.c
@@ -1104,6 +1104,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 |
@@ -1723,6 +1728,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" },
@@ -1739,6 +1745,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" },
@@ -1826,10 +1842,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" },
- { "Audio Trace DSP", NULL, "SYSCLK" },
{ "IN1L PGA", NULL, "IN1L" },
{ "IN1R PGA", NULL, "IN1R" },
@@ -1997,10 +2011,16 @@ 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,
@@ -2222,6 +2242,7 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
{
struct wm5110_priv *priv = data;
struct arizona *arizona = priv->core.arizona;
+ struct arizona_voice_trigger_info info;
int serviced = 0;
int i, ret;
@@ -2229,6 +2250,12 @@ static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
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) {
@@ -2251,6 +2278,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
arizona_init_spk(codec);
arizona_init_gpio(codec);
arizona_init_mono(codec);
+ arizona_init_notifiers(codec);
ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
"ADSP2 Compressed IRQ", wm5110_adsp2_irq,
@@ -2327,7 +2355,7 @@ static struct regmap *wm5110_get_regmap(struct device *dev)
return priv->core.arizona->regmap;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
.probe = wm5110_codec_probe,
.remove = wm5110_codec_remove,
.get_regmap = wm5110_get_regmap,
@@ -2337,12 +2365,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5110 = {
.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),
+ .component_driver = {
+ .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 = {
diff --git a/sound/soc/codecs/wm8350.c b/sound/soc/codecs/wm8350.c
index ffbf3df8ae97..2efc5b41ad0f 100644
--- a/sound/soc/codecs/wm8350.c
+++ b/sound/soc/codecs/wm8350.c
@@ -1587,19 +1587,21 @@ static struct regmap *wm8350_get_regmap(struct device *dev)
return wm8350->regmap;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8350 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8350_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8350_snd_controls),
+ .dapm_widgets = wm8350_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8350_dapm_widgets),
+ .dapm_routes = wm8350_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8350_dapm_routes),
+ },
};
static int wm8350_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm8400.c b/sound/soc/codecs/wm8400.c
index b1d346aa4696..6c59fb933bd6 100644
--- a/sound/soc/codecs/wm8400.c
+++ b/sound/soc/codecs/wm8400.c
@@ -1332,19 +1332,21 @@ static struct regmap *wm8400_get_regmap(struct device *dev)
return wm8400->regmap;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8400 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8400_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8400_snd_controls),
+ .dapm_widgets = wm8400_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8400_dapm_widgets),
+ .dapm_routes = wm8400_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8400_dapm_routes),
+ },
};
static int wm8400_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm8510.c b/sound/soc/codecs/wm8510.c
index 99e40e629cca..119ceac684ae 100644
--- a/sound/soc/codecs/wm8510.c
+++ b/sound/soc/codecs/wm8510.c
@@ -581,17 +581,19 @@ static int wm8510_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8510 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8510_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8510_snd_controls),
+ .dapm_widgets = wm8510_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8510_dapm_widgets),
+ .dapm_routes = wm8510_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8510_dapm_routes),
+ },
};
static const struct of_device_id wm8510_of_match[] = {
diff --git a/sound/soc/codecs/wm8523.c b/sound/soc/codecs/wm8523.c
index aa287a3965e7..deb2e075428e 100644
--- a/sound/soc/codecs/wm8523.c
+++ b/sound/soc/codecs/wm8523.c
@@ -413,17 +413,19 @@ static int wm8523_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8523 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8523_controls,
+ .num_controls = ARRAY_SIZE(wm8523_controls),
+ .dapm_widgets = wm8523_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8523_dapm_widgets),
+ .dapm_routes = wm8523_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8523_dapm_routes),
+ },
};
static const struct of_device_id wm8523_of_match[] = {
diff --git a/sound/soc/codecs/wm8580.c b/sound/soc/codecs/wm8580.c
index 66602bf02f6e..faa7287a5253 100644
--- a/sound/soc/codecs/wm8580.c
+++ b/sound/soc/codecs/wm8580.c
@@ -899,17 +899,19 @@ static int wm8580_remove(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8580 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8580_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8580_snd_controls),
+ .dapm_widgets = wm8580_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8580_dapm_widgets),
+ .dapm_routes = wm8580_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8580_dapm_routes),
+ },
};
static const struct of_device_id wm8580_of_match[] = {
diff --git a/sound/soc/codecs/wm8711.c b/sound/soc/codecs/wm8711.c
index c759ec068e97..2b376c9c99af 100644
--- a/sound/soc/codecs/wm8711.c
+++ b/sound/soc/codecs/wm8711.c
@@ -367,17 +367,19 @@ static int wm8711_probe(struct snd_soc_codec *codec)
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8711 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8711_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8711_snd_controls),
+ .dapm_widgets = wm8711_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8711_dapm_widgets),
+ .dapm_routes = wm8711_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8711_intercon),
+ },
};
static const struct of_device_id wm8711_of_match[] = {
diff --git a/sound/soc/codecs/wm8727.c b/sound/soc/codecs/wm8727.c
index bb25a75f92a2..7fde077a014b 100644
--- a/sound/soc/codecs/wm8727.c
+++ b/sound/soc/codecs/wm8727.c
@@ -53,11 +53,13 @@ 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_codec_driver soc_codec_dev_wm8727 = {
+ .component_driver = {
+ .dapm_widgets = wm8727_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8727_dapm_widgets),
+ .dapm_routes = wm8727_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8727_dapm_routes),
+ },
};
static int wm8727_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm8728.c b/sound/soc/codecs/wm8728.c
index 1564e6926527..797cc6e7c70f 100644
--- a/sound/soc/codecs/wm8728.c
+++ b/sound/soc/codecs/wm8728.c
@@ -211,16 +211,18 @@ static struct snd_soc_dai_driver wm8728_dai = {
.ops = &wm8728_dai_ops,
};
-static struct snd_soc_codec_driver soc_codec_dev_wm8728 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8728_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8728_snd_controls),
+ .dapm_widgets = wm8728_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8728_dapm_widgets),
+ .dapm_routes = wm8728_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8728_intercon),
+ },
};
static const struct of_device_id wm8728_of_match[] = {
diff --git a/sound/soc/codecs/wm8731.c b/sound/soc/codecs/wm8731.c
index 4bcf5f8ece50..4f9a1eb28120 100644
--- a/sound/soc/codecs/wm8731.c
+++ b/sound/soc/codecs/wm8731.c
@@ -358,6 +358,9 @@ static int wm8731_hw_params(struct snd_pcm_substream *substream,
case 24:
iface |= 0x0008;
break;
+ case 32:
+ iface |= 0x000c;
+ break;
}
wm8731_set_deemph(codec);
@@ -541,7 +544,7 @@ 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,
@@ -625,16 +628,18 @@ err_regulator_enable:
return ret;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8731 = {
+static const 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),
+ .component_driver = {
+ .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),
+ },
};
static const struct of_device_id wm8731_of_match[] = {
diff --git a/sound/soc/codecs/wm8737.c b/sound/soc/codecs/wm8737.c
index e7807601e675..f0cb1c4afe3c 100644
--- a/sound/soc/codecs/wm8737.c
+++ b/sound/soc/codecs/wm8737.c
@@ -573,17 +573,19 @@ err_get:
return ret;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8737 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8737_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8737_snd_controls),
+ .dapm_widgets = wm8737_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8737_dapm_widgets),
+ .dapm_routes = intercon,
+ .num_dapm_routes = ARRAY_SIZE(intercon),
+ },
};
static const struct of_device_id wm8737_of_match[] = {
diff --git a/sound/soc/codecs/wm8741.c b/sound/soc/codecs/wm8741.c
index 36ef91fe0511..565d477cd790 100644
--- a/sound/soc/codecs/wm8741.c
+++ b/sound/soc/codecs/wm8741.c
@@ -497,15 +497,17 @@ static int wm8741_remove(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8741 = {
+static const 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),
+ .component_driver = {
+ .dapm_widgets = wm8741_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8741_dapm_widgets),
+ .dapm_routes = wm8741_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8741_dapm_routes),
+ },
};
static const struct of_device_id wm8741_of_match[] = {
diff --git a/sound/soc/codecs/wm8750.c b/sound/soc/codecs/wm8750.c
index bd9dcd2161bc..0da2bbaf06d1 100644
--- a/sound/soc/codecs/wm8750.c
+++ b/sound/soc/codecs/wm8750.c
@@ -708,17 +708,19 @@ static int wm8750_probe(struct snd_soc_codec *codec)
return ret;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8750 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8750_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8750_snd_controls),
+ .dapm_widgets = wm8750_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8750_dapm_widgets),
+ .dapm_routes = wm8750_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8750_dapm_routes),
+ },
};
static const struct of_device_id wm8750_of_match[] = {
diff --git a/sound/soc/codecs/wm8753.c b/sound/soc/codecs/wm8753.c
index 6f1024f48b19..9bdf5447f6f6 100644
--- a/sound/soc/codecs/wm8753.c
+++ b/sound/soc/codecs/wm8753.c
@@ -32,7 +32,6 @@
*/
#include <linux/module.h>
-#include <linux/moduleparam.h>
#include <linux/kernel.h>
#include <linux/init.h>
#include <linux/delay.h>
@@ -486,7 +485,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"),
@@ -1479,18 +1478,20 @@ static int wm8753_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8753 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8753_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8753_snd_controls),
+ .dapm_widgets = wm8753_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8753_dapm_widgets),
+ .dapm_routes = wm8753_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8753_dapm_routes),
+ },
};
static const struct of_device_id wm8753_of_match[] = {
diff --git a/sound/soc/codecs/wm8770.c b/sound/soc/codecs/wm8770.c
index df6178464b00..d6edcbbdec12 100644
--- a/sound/soc/codecs/wm8770.c
+++ b/sound/soc/codecs/wm8770.c
@@ -608,17 +608,19 @@ err_reg_enable:
return ret;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8770 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8770_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8770_snd_controls),
+ .dapm_widgets = wm8770_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8770_dapm_widgets),
+ .dapm_routes = wm8770_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8770_intercon),
+ },
};
static const struct of_device_id wm8770_of_match[] = {
diff --git a/sound/soc/codecs/wm8776.c b/sound/soc/codecs/wm8776.c
index 5af44f9a8cf2..ae30480b3976 100644
--- a/sound/soc/codecs/wm8776.c
+++ b/sound/soc/codecs/wm8776.c
@@ -425,17 +425,19 @@ static int wm8776_probe(struct snd_soc_codec *codec)
return ret;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8776 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8776_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8776_snd_controls),
+ .dapm_widgets = wm8776_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8776_dapm_widgets),
+ .dapm_routes = routes,
+ .num_dapm_routes = ARRAY_SIZE(routes),
+ },
};
static const struct of_device_id wm8776_of_match[] = {
diff --git a/sound/soc/codecs/wm8782.c b/sound/soc/codecs/wm8782.c
index fb55fd845d27..bcda21018505 100644
--- a/sound/soc/codecs/wm8782.c
+++ b/sound/soc/codecs/wm8782.c
@@ -50,11 +50,13 @@ static struct snd_soc_dai_driver wm8782_dai = {
},
};
-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 const struct snd_soc_codec_driver soc_codec_dev_wm8782 = {
+ .component_driver = {
+ .dapm_widgets = wm8782_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8782_dapm_widgets),
+ .dapm_routes = wm8782_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8782_dapm_routes),
+ },
};
static int wm8782_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm8804.c b/sound/soc/codecs/wm8804.c
index 8d914702cae4..af95d648265b 100644
--- a/sound/soc/codecs/wm8804.c
+++ b/sound/soc/codecs/wm8804.c
@@ -545,10 +545,12 @@ static struct snd_soc_dai_driver wm8804_dai = {
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),
+ .component_driver = {
+ .dapm_widgets = wm8804_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8804_dapm_widgets),
+ .dapm_routes = wm8804_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8804_dapm_routes),
+ },
};
const struct regmap_config wm8804_regmap_config = {
diff --git a/sound/soc/codecs/wm8900.c b/sound/soc/codecs/wm8900.c
index 5d8dca88d612..c77b49a29311 100644
--- a/sound/soc/codecs/wm8900.c
+++ b/sound/soc/codecs/wm8900.c
@@ -1208,18 +1208,20 @@ static int wm8900_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8900 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8900_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8900_snd_controls),
+ .dapm_widgets = wm8900_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8900_dapm_widgets),
+ .dapm_routes = wm8900_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8900_dapm_routes),
+ },
};
static const struct regmap_config wm8900_regmap = {
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index a26ca490cf31..6e887c2c42b1 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -1830,7 +1830,7 @@ static void wm8903_gpio_set(struct gpio_chip *chip, unsigned offset, int value)
!!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,
@@ -1874,18 +1874,20 @@ static void wm8903_free_gpio(struct wm8903_priv *wm8903)
}
#endif
-static struct snd_soc_codec_driver soc_codec_dev_wm8903 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8903_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8903_snd_controls),
+ .dapm_widgets = wm8903_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8903_dapm_widgets),
+ .dapm_routes = wm8903_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8903_intercon),
+ },
};
static const struct regmap_config wm8903_regmap = {
diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c
index edd7a7709194..4fd350e8420d 100644
--- a/sound/soc/codecs/wm8904.c
+++ b/sound/soc/codecs/wm8904.c
@@ -2086,7 +2086,7 @@ static int wm8904_remove(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8904 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm8904 = {
.probe = wm8904_probe,
.remove = wm8904_remove,
.set_bias_level = wm8904_set_bias_level,
diff --git a/sound/soc/codecs/wm8940.c b/sound/soc/codecs/wm8940.c
index f6f9395ea38e..b5935625feeb 100644
--- a/sound/soc/codecs/wm8940.c
+++ b/sound/soc/codecs/wm8940.c
@@ -723,17 +723,19 @@ static int wm8940_probe(struct snd_soc_codec *codec)
return ret;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8940 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8940_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8940_snd_controls),
+ .dapm_widgets = wm8940_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8940_dapm_widgets),
+ .dapm_routes = wm8940_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8940_dapm_routes),
+ },
};
static const struct regmap_config wm8940_regmap = {
@@ -743,6 +745,7 @@ 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_RBTREE,
.readable_reg = wm8940_readable_register,
.volatile_reg = wm8940_volatile_register,
diff --git a/sound/soc/codecs/wm8955.c b/sound/soc/codecs/wm8955.c
index 9db00d53abe7..1edc7b1df31d 100644
--- a/sound/soc/codecs/wm8955.c
+++ b/sound/soc/codecs/wm8955.c
@@ -940,17 +940,19 @@ err_enable:
return ret;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8955 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8955_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8955_snd_controls),
+ .dapm_widgets = wm8955_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8955_dapm_widgets),
+ .dapm_routes = wm8955_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8955_dapm_routes),
+ },
};
static const struct regmap_config wm8955_regmap = {
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index d7f444f87460..3bf081a7e450 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -226,11 +226,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,
@@ -1264,7 +1263,7 @@ static int wm8960_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
.probe = wm8960_probe,
.set_bias_level = wm8960_set_bias_level,
.suspend_bias_off = true,
diff --git a/sound/soc/codecs/wm8961.c b/sound/soc/codecs/wm8961.c
index e30446a04740..e23ceac76015 100644
--- a/sound/soc/codecs/wm8961.c
+++ b/sound/soc/codecs/wm8961.c
@@ -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 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8961_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8961_snd_controls),
+ .dapm_widgets = wm8961_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8961_dapm_widgets),
+ .dapm_routes = audio_paths,
+ .num_dapm_routes = ARRAY_SIZE(audio_paths),
+ },
};
static const struct regmap_config wm8961_regmap = {
diff --git a/sound/soc/codecs/wm8962.c b/sound/soc/codecs/wm8962.c
index f3109da24769..fd2731d171dd 100644
--- a/sound/soc/codecs/wm8962.c
+++ b/sound/soc/codecs/wm8962.c
@@ -3357,7 +3357,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,
@@ -3479,7 +3479,7 @@ static int wm8962_remove(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8962 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm8962 = {
.probe = wm8962_probe,
.remove = wm8962_remove,
.set_bias_level = wm8962_set_bias_level,
@@ -3713,7 +3713,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);
}
diff --git a/sound/soc/codecs/wm8971.c b/sound/soc/codecs/wm8971.c
index 2cdde32c43c6..887d31cf3945 100644
--- a/sound/soc/codecs/wm8971.c
+++ b/sound/soc/codecs/wm8971.c
@@ -649,17 +649,19 @@ static int wm8971_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8971 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8971_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8971_snd_controls),
+ .dapm_widgets = wm8971_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8971_dapm_widgets),
+ .dapm_routes = wm8971_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8971_dapm_routes),
+ },
};
static const struct regmap_config wm8971_regmap = {
diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c
index dc8c3b1ebb6f..d414ddd6e197 100644
--- a/sound/soc/codecs/wm8974.c
+++ b/sound/soc/codecs/wm8974.c
@@ -676,17 +676,19 @@ static int wm8974_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8974_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8974_snd_controls),
+ .dapm_widgets = wm8974_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8974_dapm_widgets),
+ .dapm_routes = wm8974_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8974_dapm_routes),
+ },
};
static int wm8974_i2c_probe(struct i2c_client *i2c,
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index d36d6001fbb7..90b2d418ef60 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -993,18 +993,20 @@ static int wm8978_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8978 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8978_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8978_snd_controls),
+ .dapm_widgets = wm8978_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8978_dapm_widgets),
+ .dapm_routes = wm8978_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8978_dapm_routes),
+ },
};
static const struct regmap_config wm8978_regmap_config = {
diff --git a/sound/soc/codecs/wm8983.c b/sound/soc/codecs/wm8983.c
index 0c002a5712cb..bfdbe72ee687 100644
--- a/sound/soc/codecs/wm8983.c
+++ b/sound/soc/codecs/wm8983.c
@@ -976,16 +976,18 @@ static struct snd_soc_dai_driver wm8983_dai = {
.symmetric_rates = 1
};
-static struct snd_soc_codec_driver soc_codec_dev_wm8983 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8983_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8983_snd_controls),
+ .dapm_widgets = wm8983_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8983_dapm_widgets),
+ .dapm_routes = wm8983_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm8983_audio_map),
+ },
};
static const struct regmap_config wm8983_regmap = {
diff --git a/sound/soc/codecs/wm8985.c b/sound/soc/codecs/wm8985.c
index 6ac76fe116b0..05344f974ff3 100644
--- a/sound/soc/codecs/wm8985.c
+++ b/sound/soc/codecs/wm8985.c
@@ -1,10 +1,13 @@
/*
- * 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>
*
+ * WM8758 support:
+ * Copyright: 2016 Barix AG
+ * Author: Petr Kulhavy <petr@barix.com>
+ *
* This program is free software; you can redistribute it and/or modify
* it under the terms of the GNU General Public License version 2 as
* published by the Free Software Foundation.
@@ -40,6 +43,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 +189,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 +298,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 +364,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 +384,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 +425,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 +434,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 +450,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 +477,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 +485,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 +537,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,6 +556,38 @@ 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_codec *codec)
+{
+ struct wm8985_priv *wm8985 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ 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_codec_controls(codec, 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)
@@ -999,6 +1065,8 @@ static int wm8985_probe(struct snd_soc_codec *codec)
snd_soc_update_bits(codec, WM8985_BIAS_CTRL, WM8985_BIASCUT,
WM8985_BIASCUT);
+ wm8985_add_widgets(codec);
+
return 0;
err_reg_enable:
@@ -1037,17 +1105,19 @@ static struct snd_soc_dai_driver wm8985_dai = {
.symmetric_rates = 1
};
-static struct snd_soc_codec_driver soc_codec_dev_wm8985 = {
+static const 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),
+ .component_driver = {
+ .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),
+ },
};
static const struct regmap_config wm8985_regmap = {
@@ -1074,6 +1144,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);
@@ -1115,6 +1187,8 @@ static int wm8985_i2c_probe(struct i2c_client *i2c,
i2c_set_clientdata(i2c, wm8985);
+ wm8985->dev_type = id->driver_data;
+
wm8985->regmap = devm_regmap_init_i2c(i2c, &wm8985_regmap);
if (IS_ERR(wm8985->regmap)) {
ret = PTR_ERR(wm8985->regmap);
@@ -1135,7 +1209,8 @@ static int wm8985_i2c_remove(struct i2c_client *i2c)
}
static const struct i2c_device_id wm8985_i2c_id[] = {
- { "wm8985", 0 },
+ { "wm8985", WM8985 },
+ { "wm8758", WM8758 },
{ }
};
MODULE_DEVICE_TABLE(i2c, wm8985_i2c_id);
@@ -1183,6 +1258,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..41b1048e3c97 100644
--- a/sound/soc/codecs/wm8985.h
+++ b/sound/soc/codecs/wm8985.h
@@ -290,6 +290,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 +304,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 +658,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 +699,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 +808,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 +832,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 +1055,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..b0d0219532f2 100644
--- a/sound/soc/codecs/wm8988.c
+++ b/sound/soc/codecs/wm8988.c
@@ -817,12 +817,14 @@ static const struct snd_soc_codec_driver soc_codec_dev_wm8988 = {
.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),
+ .component_driver = {
+ .controls = wm8988_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8988_snd_controls),
+ .dapm_widgets = wm8988_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8988_dapm_widgets),
+ .dapm_routes = wm8988_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8988_dapm_routes),
+ },
};
static const struct regmap_config wm8988_regmap = {
diff --git a/sound/soc/codecs/wm8990.c b/sound/soc/codecs/wm8990.c
index 23ecd30d8bca..a8945001e696 100644
--- a/sound/soc/codecs/wm8990.c
+++ b/sound/soc/codecs/wm8990.c
@@ -1294,17 +1294,19 @@ static int wm8990_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8990 = {
+static const 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),
+ .component_driver = {
+ .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 = {
diff --git a/sound/soc/codecs/wm8991.c b/sound/soc/codecs/wm8991.c
index c9ee0ac6a654..802c0694e5c3 100644
--- a/sound/soc/codecs/wm8991.c
+++ b/sound/soc/codecs/wm8991.c
@@ -111,14 +111,24 @@ 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)
@@ -398,7 +408,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[] = {
@@ -1232,16 +1242,18 @@ static struct snd_soc_dai_driver wm8991_dai = {
.ops = &wm8991_ops
};
-static struct snd_soc_codec_driver soc_codec_dev_wm8991 = {
+static const 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),
+ .component_driver = {
+ .controls = wm8991_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8991_snd_controls),
+ .dapm_widgets = wm8991_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8991_dapm_widgets),
+ .dapm_routes = wm8991_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8991_dapm_routes),
+ },
};
static const struct regmap_config wm8991_regmap = {
diff --git a/sound/soc/codecs/wm8993.c b/sound/soc/codecs/wm8993.c
index 8668c4c391b0..195f7bf6eb22 100644
--- a/sound/soc/codecs/wm8993.c
+++ b/sound/soc/codecs/wm8993.c
@@ -1613,7 +1613,7 @@ static const struct regmap_config wm8993_regmap = {
.num_reg_defaults = ARRAY_SIZE(wm8993_reg_defaults),
};
-static struct snd_soc_codec_driver soc_codec_dev_wm8993 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm8993 = {
.probe = wm8993_probe,
.suspend = wm8993_suspend,
.resume = wm8993_resume,
diff --git a/sound/soc/codecs/wm8994.c b/sound/soc/codecs/wm8994.c
index a18aecb49935..3896523b71e9 100644
--- a/sound/soc/codecs/wm8994.c
+++ b/sound/soc/codecs/wm8994.c
@@ -4439,7 +4439,7 @@ static struct regmap *wm8994_get_regmap(struct device *dev)
return control->regmap;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8994 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm8994 = {
.probe = wm8994_codec_probe,
.remove = wm8994_codec_remove,
.suspend = wm8994_codec_suspend,
diff --git a/sound/soc/codecs/wm8995.c b/sound/soc/codecs/wm8995.c
index 24500bafb0a8..19b08a5cae62 100644
--- a/sound/soc/codecs/wm8995.c
+++ b/sound/soc/codecs/wm8995.c
@@ -2192,12 +2192,14 @@ static const struct snd_soc_codec_driver soc_codec_dev_wm8995 = {
.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),
+ .component_driver = {
+ .controls = wm8995_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8995_snd_controls),
+ .dapm_widgets = wm8995_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8995_dapm_widgets),
+ .dapm_routes = wm8995_intercon,
+ .num_dapm_routes = ARRAY_SIZE(wm8995_intercon),
+ },
};
static const struct regmap_config wm8995_regmap = {
diff --git a/sound/soc/codecs/wm8996.c b/sound/soc/codecs/wm8996.c
index a73044251218..8affa4969120 100644
--- a/sound/soc/codecs/wm8996.c
+++ b/sound/soc/codecs/wm8996.c
@@ -2184,7 +2184,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,
@@ -2684,18 +2684,20 @@ static int wm8996_remove(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8996 = {
+static const 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),
+ .component_driver = {
+ .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,
};
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
index 6b0785b5a5c5..2f2821b3382f 100644
--- a/sound/soc/codecs/wm8997.c
+++ b/sound/soc/codecs/wm8997.c
@@ -1095,7 +1095,7 @@ static struct regmap *wm8997_get_regmap(struct device *dev)
return priv->core.arizona->regmap;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8997 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm8997 = {
.probe = wm8997_codec_probe,
.remove = wm8997_codec_remove,
.get_regmap = wm8997_get_regmap,
@@ -1105,12 +1105,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8997 = {
.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),
+ .component_driver = {
+ .controls = wm8997_snd_controls,
+ .num_controls = ARRAY_SIZE(wm8997_snd_controls),
+ .dapm_widgets = wm8997_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm8997_dapm_widgets),
+ .dapm_routes = wm8997_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(wm8997_dapm_routes),
+ },
};
static int wm8997_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
index 449f66636205..bcc2e1060a6c 100644
--- a/sound/soc/codecs/wm8998.c
+++ b/sound/soc/codecs/wm8998.c
@@ -966,6 +966,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,6 +1176,7 @@ 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" },
};
@@ -1350,7 +1361,7 @@ static struct regmap *wm8998_get_regmap(struct device *dev)
return priv->core.arizona->regmap;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm8998 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm8998 = {
.probe = wm8998_codec_probe,
.remove = wm8998_codec_remove,
.get_regmap = wm8998_get_regmap,
@@ -1360,12 +1371,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8998 = {
.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),
+ .component_driver = {
+ .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 int wm8998_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm9081.c b/sound/soc/codecs/wm9081.c
index 363b3b667616..856867ec2813 100644
--- a/sound/soc/codecs/wm9081.c
+++ b/sound/soc/codecs/wm9081.c
@@ -1274,7 +1274,7 @@ static int wm9081_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm9081 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm9081 = {
.probe = wm9081_probe,
.set_sysclk = wm9081_set_sysclk,
@@ -1282,12 +1282,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9081 = {
.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),
+ .component_driver = {
+ .controls = wm9081_snd_controls,
+ .num_controls = ARRAY_SIZE(wm9081_snd_controls),
+ .dapm_widgets = wm9081_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9081_dapm_widgets),
+ .dapm_routes = wm9081_audio_paths,
+ .num_dapm_routes = ARRAY_SIZE(wm9081_audio_paths),
+ },
};
static const struct regmap_config wm9081_regmap = {
diff --git a/sound/soc/codecs/wm9090.c b/sound/soc/codecs/wm9090.c
index 5d737290f547..5a131385cb2f 100644
--- a/sound/soc/codecs/wm9090.c
+++ b/sound/soc/codecs/wm9090.c
@@ -550,7 +550,7 @@ static int wm9090_probe(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm9090 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm9090 = {
.probe = wm9090_probe,
.set_bias_level = wm9090_set_bias_level,
.suspend_bias_off = true,
diff --git a/sound/soc/codecs/wm9705.c b/sound/soc/codecs/wm9705.c
index 744842c76a60..dcdd055db57b 100644
--- a/sound/soc/codecs/wm9705.c
+++ b/sound/soc/codecs/wm9705.c
@@ -352,7 +352,7 @@ static int wm9705_soc_remove(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
.probe = wm9705_soc_probe,
.remove = wm9705_soc_remove,
.suspend = wm9705_soc_suspend,
@@ -364,12 +364,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
.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),
+ .component_driver = {
+ .controls = wm9705_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls),
+ .dapm_widgets = wm9705_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
+ .dapm_routes = wm9705_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9705_audio_map),
+ },
};
static int wm9705_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c
index 488a92224249..557709eac698 100644
--- a/sound/soc/codecs/wm9712.c
+++ b/sound/soc/codecs/wm9712.c
@@ -669,7 +669,7 @@ static int wm9712_soc_remove(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
+static const struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
.probe = wm9712_soc_probe,
.remove = wm9712_soc_remove,
.resume = wm9712_soc_resume,
@@ -682,12 +682,14 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
.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),
+ .component_driver = {
+ .controls = wm9712_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls),
+ .dapm_widgets = wm9712_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
+ .dapm_routes = wm9712_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9712_audio_map),
+ },
};
static int wm9712_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm9713.c b/sound/soc/codecs/wm9713.c
index 9849643ef809..e4301ddb1b84 100644
--- a/sound/soc/codecs/wm9713.c
+++ b/sound/soc/codecs/wm9713.c
@@ -1235,19 +1235,21 @@ static int wm9713_soc_remove(struct snd_soc_codec *codec)
return 0;
}
-static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
+static const 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),
+ .component_driver = {
+ .controls = wm9713_snd_ac97_controls,
+ .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls),
+ .dapm_widgets = wm9713_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
+ .dapm_routes = wm9713_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(wm9713_audio_map),
+ },
};
static int wm9713_probe(struct platform_device *pdev)
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index a07bd7c2c587..b943dde8dbe5 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -394,6 +394,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" },
@@ -406,6 +407,7 @@ static const struct {
.compr_direction = SND_COMPRESS_CAPTURE,
.num_caps = ARRAY_SIZE(ctrl_caps),
.caps = ctrl_caps,
+ .voice_trigger = true,
},
[WM_ADSP_FW_ASR] = { .file = "asr" },
[WM_ADSP_FW_TRACE] = {
@@ -478,7 +480,7 @@ static ssize_t wm_adsp_debugfs_wmfw_read(struct file *file,
mutex_lock(&dsp->pwr_lock);
- if (!dsp->wmfw_file_name || !dsp->running)
+ if (!dsp->wmfw_file_name || !dsp->booted)
ret = 0;
else
ret = simple_read_from_buffer(user_buf, count, ppos,
@@ -498,7 +500,7 @@ static ssize_t wm_adsp_debugfs_bin_read(struct file *file,
mutex_lock(&dsp->pwr_lock);
- if (!dsp->bin_file_name || !dsp->running)
+ if (!dsp->bin_file_name || !dsp->booted)
ret = 0;
else
ret = simple_read_from_buffer(user_buf, count, ppos,
@@ -552,6 +554,9 @@ static void wm_adsp2_init_debugfs(struct wm_adsp *dsp,
if (!root)
goto err;
+ if (!debugfs_create_bool("booted", S_IRUGO, root, &dsp->booted))
+ goto err;
+
if (!debugfs_create_bool("running", S_IRUGO, root, &dsp->running))
goto err;
@@ -635,7 +640,7 @@ static int wm_adsp_fw_put(struct snd_kcontrol *kcontrol,
mutex_lock(&dsp[e->shift_l].pwr_lock);
- if (dsp[e->shift_l].running || dsp[e->shift_l].compr)
+ if (dsp[e->shift_l].booted || dsp[e->shift_l].compr)
ret = -EBUSY;
else
dsp[e->shift_l].fw = ucontrol->value.enumerated.item[0];
@@ -787,7 +792,7 @@ static int wm_coeff_put(struct snd_kcontrol *kctl,
memcpy(ctl->cache, p, ctl->len);
ctl->set = 1;
- if (ctl->enabled)
+ if (ctl->enabled && ctl->dsp->running)
ret = wm_coeff_write_control(ctl, p, ctl->len);
mutex_unlock(&ctl->dsp->pwr_lock);
@@ -809,7 +814,7 @@ static int wm_coeff_tlv_put(struct snd_kcontrol *kctl,
ret = -EFAULT;
} else {
ctl->set = 1;
- if (ctl->enabled)
+ if (ctl->enabled && ctl->dsp->running)
ret = wm_coeff_write_control(ctl, ctl->cache, size);
}
@@ -869,12 +874,12 @@ static int wm_coeff_get(struct snd_kcontrol *kctl,
mutex_lock(&ctl->dsp->pwr_lock);
if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
- if (ctl->enabled)
+ if (ctl->enabled && ctl->dsp->running)
ret = wm_coeff_read_control(ctl, p, ctl->len);
else
ret = -EPERM;
} else {
- if (!ctl->flags && ctl->enabled)
+ if (!ctl->flags && ctl->enabled && ctl->dsp->running)
ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
memcpy(p, ctl->cache, ctl->len);
@@ -896,12 +901,12 @@ static int wm_coeff_tlv_get(struct snd_kcontrol *kctl,
mutex_lock(&ctl->dsp->pwr_lock);
if (ctl->flags & WMFW_CTL_FLAG_VOLATILE) {
- if (ctl->enabled)
+ if (ctl->enabled && ctl->dsp->running)
ret = wm_coeff_read_control(ctl, ctl->cache, size);
else
ret = -EPERM;
} else {
- if (!ctl->flags && ctl->enabled)
+ if (!ctl->flags && ctl->enabled && ctl->dsp->running)
ret = wm_coeff_read_control(ctl, ctl->cache, size);
}
@@ -2164,13 +2169,20 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err_ena;
+ dsp->booted = true;
+
/* Start the core running */
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
ADSP1_CORE_ENA | ADSP1_START,
ADSP1_CORE_ENA | ADSP1_START);
+
+ dsp->running = true;
break;
case SND_SOC_DAPM_PRE_PMD:
+ dsp->running = false;
+ dsp->booted = false;
+
/* Halt the core */
regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
ADSP1_CORE_ENA | ADSP1_START, 0);
@@ -2225,7 +2237,7 @@ static int wm_adsp2_ena(struct wm_adsp *dsp)
if (val & ADSP2_RAM_RDY)
break;
- msleep(1);
+ usleep_range(250, 500);
}
if (!(val & ADSP2_RAM_RDY)) {
@@ -2247,6 +2259,11 @@ static void wm_adsp2_boot_work(struct work_struct *work)
mutex_lock(&dsp->pwr_lock);
+ ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_MEM_ENA, ADSP2_MEM_ENA);
+ if (ret != 0)
+ goto err_mutex;
+
ret = wm_adsp2_ena(dsp);
if (ret != 0)
goto err_mutex;
@@ -2268,13 +2285,14 @@ static void wm_adsp2_boot_work(struct work_struct *work)
if (ret != 0)
goto err_ena;
- /* Sync set controls */
- ret = wm_coeff_sync_controls(dsp);
+ dsp->booted = true;
+
+ /* Turn DSP back off until we are ready to run */
+ ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, 0);
if (ret != 0)
goto err_ena;
- dsp->running = true;
-
mutex_unlock(&dsp->pwr_lock);
return;
@@ -2305,6 +2323,7 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
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_coeff_ctl *ctl;
dsp->card = codec->component.card;
@@ -2313,6 +2332,24 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
wm_adsp2_set_dspclk(dsp, freq);
queue_work(system_unbound_wq, &dsp->boot_work);
break;
+ case SND_SOC_DAPM_PRE_PMD:
+ wm_adsp_debugfs_clear(dsp);
+
+ dsp->fw_id = 0;
+ dsp->fw_id_version = 0;
+
+ dsp->booted = false;
+
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_MEM_ENA, 0);
+
+ list_for_each_entry(ctl, &dsp->ctl_list, list)
+ ctl->enabled = 0;
+
+ wm_adsp_free_alg_regions(dsp);
+
+ adsp_dbg(dsp, "Shutdown complete\n");
+ break;
default:
break;
}
@@ -2327,16 +2364,24 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
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_coeff_ctl *ctl;
int ret;
switch (event) {
case SND_SOC_DAPM_POST_PMU:
flush_work(&dsp->boot_work);
- if (!dsp->running)
+ if (!dsp->booted)
return -EIO;
+ ret = wm_adsp2_ena(dsp);
+ if (ret != 0)
+ goto err;
+
+ /* Sync set controls */
+ ret = wm_coeff_sync_controls(dsp);
+ if (ret != 0)
+ goto err;
+
ret = regmap_update_bits(dsp->regmap,
dsp->base + ADSP2_CONTROL,
ADSP2_CORE_ENA | ADSP2_START,
@@ -2344,6 +2389,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
+ dsp->running = true;
+
mutex_lock(&dsp->pwr_lock);
if (wm_adsp_fw[dsp->fw].num_caps != 0)
@@ -2359,32 +2406,25 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
mutex_lock(&dsp->pwr_lock);
- wm_adsp_debugfs_clear(dsp);
-
- dsp->fw_id = 0;
- dsp->fw_id_version = 0;
dsp->running = false;
regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA | ADSP2_CORE_ENA |
- ADSP2_START, 0);
+ ADSP2_CORE_ENA | ADSP2_START, 0);
/* Make sure DMAs are quiesced */
+ regmap_write(dsp->regmap, dsp->base + ADSP2_RDMA_CONFIG_1, 0);
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);
- list_for_each_entry(ctl, &dsp->ctl_list, list)
- ctl->enabled = 0;
-
- wm_adsp_free_alg_regions(dsp);
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, 0);
if (wm_adsp_fw[dsp->fw].num_caps != 0)
wm_adsp_buffer_free(dsp);
mutex_unlock(&dsp->pwr_lock);
- adsp_dbg(dsp, "Shutdown complete\n");
+ adsp_dbg(dsp, "Execution stopped\n");
break;
default:
@@ -2998,6 +3038,9 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
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);
@@ -3037,12 +3080,8 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
buf = compr->buf;
- if (!compr->buf) {
- ret = -ENXIO;
- goto out;
- }
-
- if (compr->buf->error) {
+ if (!compr->buf || compr->buf->error) {
+ snd_compr_stop_error(stream, SNDRV_PCM_STATE_XRUN);
ret = -EIO;
goto out;
}
@@ -3060,8 +3099,12 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
*/
if (buf->avail < wm_adsp_compr_frag_words(compr)) {
ret = wm_adsp_buffer_get_error(buf);
- if (ret < 0)
+ if (ret < 0) {
+ if (compr->buf->error)
+ snd_compr_stop_error(stream,
+ SNDRV_PCM_STATE_XRUN);
goto out;
+ }
ret = wm_adsp_buffer_reenable_irq(buf);
if (ret < 0) {
@@ -3156,11 +3199,10 @@ static int wm_adsp_compr_read(struct wm_adsp_compr *compr,
adsp_dbg(dsp, "Requested read of %zu bytes\n", count);
- if (!compr->buf)
- return -ENXIO;
-
- if (compr->buf->error)
+ if (!compr->buf || compr->buf->error) {
+ snd_compr_stop_error(compr->stream, SNDRV_PCM_STATE_XRUN);
return -EIO;
+ }
count /= WM_ADSP_DATA_WORD_SIZE;
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index feb61e2c4bb4..362dd7ce60d8 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -19,6 +19,10 @@
#include "wmfw.h"
+/* Return values for wm_adsp_compr_handle_irq */
+#define WM_ADSP_COMPR_OK 0
+#define WM_ADSP_COMPR_VOICE_TRIGGER 1
+
struct wm_adsp_region {
int type;
unsigned int base;
@@ -57,6 +61,8 @@ struct wm_adsp {
int fw;
int fw_ver;
+
+ bool booted;
bool running;
struct list_head ctl_list;
@@ -81,9 +87,10 @@ struct wm_adsp {
wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
#define WM_ADSP2(wname, num, event_fn) \
-{ .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \
+{ .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, \
.event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c
index 0f66fda2c772..3c5a9804d3f5 100644
--- a/sound/soc/davinci/davinci-mcasp.c
+++ b/sound/soc/davinci/davinci-mcasp.c
@@ -1218,7 +1218,7 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
snd_mask_none(&nfmt);
- for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
+ for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
if (snd_mask_test(fmt, i)) {
uint sbits = snd_pcm_format_width(i);
int ppm;
@@ -1513,8 +1513,9 @@ static struct davinci_mcasp_pdata am33xx_mcasp_pdata = {
};
static struct davinci_mcasp_pdata dra7_mcasp_pdata = {
- .tx_dma_offset = 0x200,
- .rx_dma_offset = 0x284,
+ /* The CFG port offset will be calculated if it is needed */
+ .tx_dma_offset = 0,
+ .rx_dma_offset = 0,
.version = MCASP_VERSION_4,
};
@@ -1598,7 +1599,14 @@ static struct davinci_mcasp_pdata *davinci_mcasp_set_pdata_from_of(
pdata = pdev->dev.platform_data;
return pdata;
} else if (match) {
- pdata = (struct davinci_mcasp_pdata*) match->data;
+ pdata = devm_kmemdup(&pdev->dev, match->data, sizeof(*pdata),
+ GFP_KERNEL);
+ if (!pdata) {
+ dev_err(&pdev->dev,
+ "Failed to allocate memory for pdata\n");
+ ret = -ENOMEM;
+ return pdata;
+ }
} else {
/* control shouldn't reach here. something is wrong */
ret = -EINVAL;
@@ -1734,6 +1742,52 @@ static int davinci_mcasp_get_dma_type(struct davinci_mcasp *mcasp)
return PCM_EDMA;
}
+static u32 davinci_mcasp_txdma_offset(struct davinci_mcasp_pdata *pdata)
+{
+ int i;
+ u32 offset = 0;
+
+ if (pdata->version != MCASP_VERSION_4)
+ return pdata->tx_dma_offset;
+
+ for (i = 0; i < pdata->num_serializer; i++) {
+ if (pdata->serial_dir[i] == TX_MODE) {
+ if (!offset) {
+ offset = DAVINCI_MCASP_TXBUF_REG(i);
+ } else {
+ pr_err("%s: Only one serializer allowed!\n",
+ __func__);
+ break;
+ }
+ }
+ }
+
+ return offset;
+}
+
+static u32 davinci_mcasp_rxdma_offset(struct davinci_mcasp_pdata *pdata)
+{
+ int i;
+ u32 offset = 0;
+
+ if (pdata->version != MCASP_VERSION_4)
+ return pdata->rx_dma_offset;
+
+ for (i = 0; i < pdata->num_serializer; i++) {
+ if (pdata->serial_dir[i] == RX_MODE) {
+ if (!offset) {
+ offset = DAVINCI_MCASP_RXBUF_REG(i);
+ } else {
+ pr_err("%s: Only one serializer allowed!\n",
+ __func__);
+ break;
+ }
+ }
+ }
+
+ return offset;
+}
+
static int davinci_mcasp_probe(struct platform_device *pdev)
{
struct snd_dmaengine_dai_dma_data *dma_data;
@@ -1862,7 +1916,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
if (dat)
dma_data->addr = dat->start;
else
- dma_data->addr = mem->start + pdata->tx_dma_offset;
+ dma_data->addr = mem->start + davinci_mcasp_txdma_offset(pdata);
dma = &mcasp->dma_request[SNDRV_PCM_STREAM_PLAYBACK];
res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
@@ -1883,7 +1937,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
if (dat)
dma_data->addr = dat->start;
else
- dma_data->addr = mem->start + pdata->rx_dma_offset;
+ dma_data->addr =
+ mem->start + davinci_mcasp_rxdma_offset(pdata);
dma = &mcasp->dma_request[SNDRV_PCM_STREAM_CAPTURE];
res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
diff --git a/sound/soc/davinci/davinci-mcasp.h b/sound/soc/davinci/davinci-mcasp.h
index 1e8787fb3fb7..afddc8010c54 100644
--- a/sound/soc/davinci/davinci-mcasp.h
+++ b/sound/soc/davinci/davinci-mcasp.h
@@ -85,9 +85,9 @@
(n << 2))
/* Transmit Buffer for Serializer n */
-#define DAVINCI_MCASP_TXBUF_REG 0x200
+#define DAVINCI_MCASP_TXBUF_REG(n) (0x200 + (n << 2))
/* Receive Buffer for Serializer n */
-#define DAVINCI_MCASP_RXBUF_REG 0x280
+#define DAVINCI_MCASP_RXBUF_REG(n) (0x280 + (n << 2))
/* McASP FIFO Registers */
#define DAVINCI_MCASP_V2_AFIFO_BASE (0x1010)
diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig
index d50e08517dce..c297efe43861 100644
--- a/sound/soc/dwc/Kconfig
+++ b/sound/soc/dwc/Kconfig
@@ -7,4 +7,13 @@ config SND_DESIGNWARE_I2S
Synopsys desigwnware I2S device. The device supports upto
maximum of 8 channels each for play and record.
+config SND_DESIGNWARE_PCM
+ tristate "PCM PIO extension for I2S driver"
+ depends on SND_DESIGNWARE_I2S
+ help
+ Say Y, M 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.
diff --git a/sound/soc/dwc/Makefile b/sound/soc/dwc/Makefile
index 319371f690f4..38f1ca31c5fa 100644
--- a/sound/soc/dwc/Makefile
+++ b/sound/soc/dwc/Makefile
@@ -1,3 +1,5 @@
# SYNOPSYS Platform Support
obj-$(CONFIG_SND_DESIGNWARE_I2S) += designware_i2s.o
-
+ifdef CONFIG_SND_DESIGNWARE_PCM
+obj-$(CONFIG_SND_DESIGNWARE_I2S) += designware_pcm.o
+endif
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/designware_i2s.c
index 0db69b7e9617..2998954a1c74 100644
--- a/sound/soc/dwc/designware_i2s.c
+++ b/sound/soc/dwc/designware_i2s.c
@@ -24,90 +24,7 @@
#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;
- u32 fifo_th;
-
- /* 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);
-};
+#include "local.h"
static inline void i2s_write_reg(void __iomem *io_base, int reg, u32 val)
{
@@ -145,51 +62,115 @@ static inline void i2s_clear_irqs(struct dw_i2s_dev *dev, u32 stream)
}
}
-static void i2s_start(struct dw_i2s_dev *dev,
- struct snd_pcm_substream *substream)
+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)
{
- struct i2s_clk_config_data *config = &dev->config;
u32 i, irq;
- i2s_write_reg(dev->i2s_base, IER, 1);
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
- for (i = 0; i < (config->chan_nr / 2); i++) {
+ 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);
}
- i2s_write_reg(dev->i2s_base, ITER, 1);
} else {
- for (i = 0; i < (config->chan_nr / 2); i++) {
+ 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);
}
- i2s_write_reg(dev->i2s_base, IRER, 1);
+ }
+}
+
+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. Record mode not supported in PIO mode */
+ if (isr[i] & ISR_RXDA)
+ irq_valid = true;
+
+ /* Error Handling: TX */
+ if (isr[i] & ISR_TXFO) {
+ dev_err(dev->dev, "TX overrun (ch_id=%d)\n", i);
+ irq_valid = true;
+ }
+
+ /* Error Handling: TX */
+ if (isr[i] & ISR_RXFO) {
+ dev_err(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_start(struct dw_i2s_dev *dev,
+ struct snd_pcm_substream *substream)
+{
+ struct i2s_clk_config_data *config = &dev->config;
+
+ i2s_write_reg(dev->i2s_base, IER, 1);
+ i2s_enable_irqs(dev, substream->stream, config->chan_nr);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ i2s_write_reg(dev->i2s_base, ITER, 1);
+ else
+ 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) {
+ 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 {
+ 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);
- }
- }
+ i2s_disable_irqs(dev, substream->stream, 8);
if (!dev->active) {
i2s_write_reg(dev->i2s_base, CER, 0);
@@ -223,7 +204,7 @@ static int dw_i2s_startup(struct snd_pcm_substream *substream,
static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
{
- u32 ch_reg, irq;
+ u32 ch_reg;
struct i2s_clk_config_data *config = &dev->config;
@@ -235,16 +216,12 @@ static void dw_i2s_config(struct dw_i2s_dev *dev, int stream)
dev->xfer_resolution);
i2s_write_reg(dev->i2s_base, TFCR(ch_reg),
dev->fifo_th - 1);
- 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),
dev->fifo_th - 1);
- 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);
}
@@ -278,7 +255,7 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
break;
default:
- dev_err(dev->dev, "designware-i2s: unsuppted PCM fmt");
+ dev_err(dev->dev, "designware-i2s: unsupported PCM fmt");
return -EINVAL;
}
@@ -600,7 +577,6 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev,
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;
@@ -611,7 +587,6 @@ static int dw_configure_dai_by_dt(struct dw_i2s_dev *dev,
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;
@@ -626,7 +601,7 @@ 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;
+ int ret, irq;
struct snd_soc_dai_driver *dw_i2s_dai;
const char *clk_id;
@@ -651,6 +626,16 @@ static int dw_i2s_probe(struct platform_device *pdev)
dev->dev = &pdev->dev;
+ irq = platform_get_irq(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");
+ return ret;
+ }
+ }
+
dev->i2s_reg_comp1 = I2S_COMP_PARAM_1;
dev->i2s_reg_comp2 = I2S_COMP_PARAM_2;
if (pdata) {
@@ -697,12 +682,24 @@ static int dw_i2s_probe(struct platform_device *pdev)
if (!pdata) {
ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
- if (ret) {
+ if (ret == -EPROBE_DEFER) {
+ dev_err(&pdev->dev,
+ "failed to register PCM, deferring probe\n");
+ return ret;
+ } else if (ret) {
dev_err(&pdev->dev,
- "Could not register PCM: %d\n", ret);
- goto err_clk_disable;
+ "Could not register DMA PCM: %d\n"
+ "falling back to PIO mode\n", ret);
+ ret = dw_pcm_register(pdev);
+ if (ret) {
+ dev_err(&pdev->dev,
+ "Could not register PIO PCM: %d\n",
+ ret);
+ goto err_clk_disable;
+ }
}
}
+
pm_runtime_enable(&pdev->dev);
return 0;
diff --git a/sound/soc/dwc/designware_pcm.c b/sound/soc/dwc/designware_pcm.c
new file mode 100644
index 000000000000..4a83a22fa3cb
--- /dev/null
+++ b/sound/soc/dwc/designware_pcm.c
@@ -0,0 +1,225 @@
+/*
+ * 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 + LRBR_LTHR(0)); \
+ iowrite32(p[tx_ptr][1], dev->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; \
+}
+
+dw_pcm_tx_fn(16);
+dw_pcm_tx_fn(32);
+
+#undef dw_pcm_tx_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_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,
+};
+
+void dw_pcm_push_tx(struct dw_i2s_dev *dev)
+{
+ struct snd_pcm_substream *tx_substream;
+ bool tx_active, period_elapsed;
+
+ rcu_read_lock();
+ tx_substream = rcu_dereference(dev->tx_substream);
+ tx_active = tx_substream && snd_pcm_running(tx_substream);
+ if (tx_active) {
+ unsigned int tx_ptr = READ_ONCE(dev->tx_ptr);
+ unsigned int new_tx_ptr = dev->tx_fn(dev, tx_substream->runtime,
+ tx_ptr, &period_elapsed);
+ cmpxchg(&dev->tx_ptr, tx_ptr, new_tx_ptr);
+
+ if (period_elapsed)
+ snd_pcm_period_elapsed(tx_substream);
+ }
+ rcu_read_unlock();
+}
+EXPORT_SYMBOL_GPL(dw_pcm_push_tx);
+
+static int dw_pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+ 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_pcm_substream *substream)
+{
+ synchronize_rcu();
+ return 0;
+}
+
+static int dw_pcm_hw_params(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;
+ int ret;
+
+ 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;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ dev->tx_fn = dw_pcm_tx_32;
+ break;
+ default:
+ dev_err(dev->dev, "invalid format\n");
+ return -EINVAL;
+ }
+
+ if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK) {
+ dev_err(dev->dev, "only playback is available\n");
+ return -EINVAL;
+ }
+
+ ret = snd_pcm_lib_malloc_pages(substream,
+ params_buffer_bytes(hw_params));
+ if (ret < 0)
+ return ret;
+ else
+ return 0;
+}
+
+static int dw_pcm_hw_free(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+
+static int dw_pcm_trigger(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:
+ WRITE_ONCE(dev->tx_ptr, 0);
+ rcu_assign_pointer(dev->tx_substream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ rcu_assign_pointer(dev->tx_substream, NULL);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static snd_pcm_uframes_t dw_pcm_pointer(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 = READ_ONCE(dev->tx_ptr);
+
+ return pos < runtime->buffer_size ? pos : 0;
+}
+
+static int dw_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+ size_t size = dw_pcm_hardware.buffer_bytes_max;
+
+ return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm,
+ SNDRV_DMA_TYPE_CONTINUOUS,
+ snd_dma_continuous_data(GFP_KERNEL), size, size);
+}
+
+static void dw_pcm_free(struct snd_pcm *pcm)
+{
+ snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static const struct snd_pcm_ops dw_pcm_ops = {
+ .open = dw_pcm_open,
+ .close = dw_pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = dw_pcm_hw_params,
+ .hw_free = dw_pcm_hw_free,
+ .trigger = dw_pcm_trigger,
+ .pointer = dw_pcm_pointer,
+};
+
+static const struct snd_soc_platform_driver dw_pcm_platform = {
+ .pcm_new = dw_pcm_new,
+ .pcm_free = dw_pcm_free,
+ .ops = &dw_pcm_ops,
+};
+
+int dw_pcm_register(struct platform_device *pdev)
+{
+ return devm_snd_soc_register_platform(&pdev->dev, &dw_pcm_platform);
+}
+EXPORT_SYMBOL_GPL(dw_pcm_register);
diff --git a/sound/soc/dwc/local.h b/sound/soc/dwc/local.h
new file mode 100644
index 000000000000..68afd7577343
--- /dev/null
+++ b/sound/soc/dwc/local.h
@@ -0,0 +1,128 @@
+/*
+ * 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
+
+/* 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)
+
+/* 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;
+ u32 fifo_th;
+
+ /* 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 (TX) */
+ bool use_pio;
+ struct snd_pcm_substream __rcu *tx_substream;
+ unsigned int (*tx_fn)(struct dw_i2s_dev *dev,
+ struct snd_pcm_runtime *runtime, unsigned int tx_ptr,
+ bool *period_elapsed);
+ unsigned int tx_ptr;
+};
+
+#if IS_ENABLED(CONFIG_SND_DESIGNWARE_PCM)
+void dw_pcm_push_tx(struct dw_i2s_dev *dev);
+int dw_pcm_register(struct platform_device *pdev);
+#else
+void dw_pcm_push_tx(struct dw_i2s_dev *dev) { }
+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 35aabf9dc503..19bdcac71775 100644
--- a/sound/soc/fsl/Kconfig
+++ b/sound/soc/fsl/Kconfig
@@ -4,6 +4,7 @@ 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 REGMAP_MMIO
select SND_SOC_GENERIC_DMAENGINE_PCM
help
diff --git a/sound/soc/fsl/fsl_asrc.c b/sound/soc/fsl/fsl_asrc.c
index c1a0e01cb8e7..1d82f68305c3 100644
--- a/sound/soc/fsl/fsl_asrc.c
+++ b/sound/soc/fsl/fsl_asrc.c
@@ -726,7 +726,7 @@ 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,
};
/**
@@ -879,7 +879,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
}
}
- if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx35-asrc")) {
+ if (of_device_is_compatible(np, "fsl,imx35-asrc")) {
asrc_priv->channel_bits = 3;
clk_map[IN] = input_clk_map_imx35;
clk_map[OUT] = output_clk_map_imx35;
@@ -892,7 +892,7 @@ static int fsl_asrc_probe(struct platform_device *pdev)
ret = fsl_asrc_init(asrc_priv);
if (ret) {
dev_err(&pdev->dev, "failed to init asrc %d\n", ret);
- return -EINVAL;
+ return ret;
}
asrc_priv->channel_avail = 10;
@@ -901,14 +901,14 @@ static int fsl_asrc_probe(struct platform_device *pdev)
&asrc_priv->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);
if (ret) {
dev_err(&pdev->dev, "failed to get output width\n");
- return -EINVAL;
+ return ret;
}
if (asrc_priv->asrc_width != 16 && asrc_priv->asrc_width != 24) {
@@ -933,8 +933,6 @@ static int fsl_asrc_probe(struct platform_device *pdev)
return ret;
}
- dev_info(&pdev->dev, "driver registered\n");
-
return 0;
}
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index ffc000bc1f15..dc30d780f874 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -322,7 +322,7 @@ 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 = {
+static const 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,
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index 26a90e12ede4..38bfd46f4ad8 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -77,19 +77,19 @@ static irqreturn_t esai_isr(int irq, void *devid)
regmap_read(esai_priv->regmap, REG_ESAI_ESR, &esr);
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");
if (esr & ESAI_ESR_TFE_MASK)
- dev_warn(&pdev->dev, "isr: Transmition underrun\n");
+ dev_warn(&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");
@@ -781,7 +781,7 @@ 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_probe(struct platform_device *pdev)
diff --git a/sound/soc/fsl/fsl_sai.c b/sound/soc/fsl/fsl_sai.c
index 2147994ab46f..9fadf7e31c5f 100644
--- a/sound/soc/fsl/fsl_sai.c
+++ b/sound/soc/fsl/fsl_sai.c
@@ -801,8 +801,8 @@ static int fsl_sai_probe(struct platform_device *pdev)
sai->pdev = pdev;
- if (of_device_is_compatible(pdev->dev.of_node, "fsl,imx6sx-sai") ||
- of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai"))
+ if (of_device_is_compatible(np, "fsl,imx6sx-sai") ||
+ of_device_is_compatible(np, "fsl,imx6ul-sai"))
sai->sai_on_imx = true;
sai->is_lsb_first = of_property_read_bool(np, "lsb-first");
@@ -883,7 +883,7 @@ static int fsl_sai_probe(struct platform_device *pdev)
}
if (of_find_property(np, "fsl,sai-mclk-direction-output", NULL) &&
- of_device_is_compatible(pdev->dev.of_node, "fsl,imx6ul-sai")) {
+ of_device_is_compatible(np, "fsl,imx6ul-sai")) {
gpr = syscon_regmap_lookup_by_compatible("fsl,imx6ul-iomuxc-gpr");
if (IS_ERR(gpr)) {
dev_err(&pdev->dev, "cannot find iomuxc registers\n");
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
index 151849f79863..1ff467c9598a 100644
--- a/sound/soc/fsl/fsl_spdif.c
+++ b/sound/soc/fsl/fsl_spdif.c
@@ -172,7 +172,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;
}
@@ -1103,7 +1103,7 @@ static const struct regmap_config fsl_spdif_regmap_config = {
.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,
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index 632ecc0e3956..50349437d961 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -182,7 +182,7 @@ static const struct regmap_config fsl_ssi_regconfig = {
.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 {
@@ -952,16 +952,16 @@ static int _fsl_ssi_set_dai_fmt(struct device *dev,
ssi_private->i2s_mode = CCSR_SSI_SCR_NET;
switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
case SND_SOC_DAIFMT_I2S:
+ 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_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));
break;
case SND_SOC_DAIFMT_CBM_CFM:
ssi_private->i2s_mode |= CCSR_SSI_SCR_I2S_MODE_SLAVE;
diff --git a/sound/soc/generic/Kconfig b/sound/soc/generic/Kconfig
index 610f61251640..d023959b8cd6 100644
--- a/sound/soc/generic/Kconfig
+++ b/sound/soc/generic/Kconfig
@@ -1,4 +1,16 @@
+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
+
+config SND_SIMPLE_SCU_CARD
+ tristate "ASoC Simple SCU sound card support"
+ depends on OF
+ select SND_SIMPLE_CARD_UTILS
+ help
+ This option enables generic simple SCU sound card support.
+ It supports DPCM of multi CPU single Codec system.
diff --git a/sound/soc/generic/Makefile b/sound/soc/generic/Makefile
index 9c3b246792bf..ee750f3023ba 100644
--- a/sound/soc/generic/Makefile
+++ b/sound/soc/generic/Makefile
@@ -1,3 +1,7 @@
+snd-soc-simple-card-utils-objs := simple-card-utils.o
snd-soc-simple-card-objs := simple-card.o
+snd-soc-simple-scu-card-objs := simple-scu-card.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_SIMPLE_SCU_CARD) += snd-soc-simple-scu-card.o
diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c
new file mode 100644
index 000000000000..1cb39309f5d5
--- /dev/null
+++ b/sound/soc/generic/simple-card-utils.c
@@ -0,0 +1,244 @@
+/*
+ * simple-card-core.c
+ *
+ * Copyright (c) 2016 Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <sound/simple_card_utils.h>
+
+int asoc_simple_card_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;
+ int prefix_len = prefix ? strlen(prefix) : 0;
+ unsigned int daifmt;
+
+ daifmt = snd_soc_of_parse_daifmt(node, prefix,
+ &bitclkmaster, &framemaster);
+ daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+
+ if (prefix_len && !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_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;
+ }
+
+ of_node_put(bitclkmaster);
+ of_node_put(framemaster);
+
+ *retfmt = daifmt;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_parse_daifmt);
+
+int asoc_simple_card_set_dailink_name(struct device *dev,
+ struct snd_soc_dai_link *dai_link,
+ const char *fmt, ...)
+{
+ 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 ret;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_set_dailink_name);
+
+int asoc_simple_card_parse_card_name(struct snd_soc_card *card,
+ char *prefix)
+{
+ char prop[128];
+ int ret;
+
+ snprintf(prop, sizeof(prop), "%sname", prefix);
+
+ /* Parse the card name from DT */
+ ret = snd_soc_of_parse_card_name(card, prop);
+ if (ret < 0)
+ return ret;
+
+ if (!card->name && card->dai_link)
+ card->name = card->dai_link->name;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_parse_card_name);
+
+int asoc_simple_card_parse_clk(struct device_node *node,
+ struct device_node *dai_of_node,
+ struct asoc_simple_dai *simple_dai)
+{
+ 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 = of_clk_get(node, 0);
+ 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;
+ } else {
+ clk = of_clk_get(dai_of_node, 0);
+ if (!IS_ERR(clk))
+ simple_dai->sysclk = clk_get_rate(clk);
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk);
+
+int asoc_simple_card_parse_dai(struct device_node *node,
+ struct device_node **dai_of_node,
+ const char **dai_name,
+ const char *list_name,
+ const char *cells_name,
+ int *is_single_link)
+{
+ struct of_phandle_args args;
+ int ret;
+
+ if (!node)
+ return 0;
+
+ /*
+ * 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, list_name, cells_name, 0, &args);
+ if (ret)
+ return ret;
+
+ /* Get dai->name */
+ if (dai_name) {
+ ret = snd_soc_of_get_dai_name(node, dai_name);
+ if (ret < 0)
+ return ret;
+ }
+
+ *dai_of_node = args.np;
+
+ if (is_single_link)
+ *is_single_link = !args.args_count;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_parse_dai);
+
+int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
+ struct asoc_simple_dai *simple_dai)
+{
+ int ret;
+
+ if (simple_dai->sysclk) {
+ ret = snd_soc_dai_set_sysclk(dai, 0, simple_dai->sysclk, 0);
+ if (ret && ret != -ENOTSUPP) {
+ dev_err(dai->dev, "simple-card: set_sysclk error\n");
+ return ret;
+ }
+ }
+
+ 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");
+ return ret;
+ }
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_init_dai);
+
+int asoc_simple_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link)
+{
+ if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name)
+ return -EINVAL;
+
+ /* Assumes platform == cpu */
+ if (!dai_link->platform_of_node)
+ dai_link->platform_of_node = dai_link->cpu_of_node;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_dailink);
+
+void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
+ 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)
+ dai_link->cpu_dai_name = NULL;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_cpu);
+
+int asoc_simple_card_clean_reference(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;
+}
+EXPORT_SYMBOL_GPL(asoc_simple_card_clean_reference);
+
+/* 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 466492b7d4f5..f608f8d23f3d 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -21,6 +21,12 @@
#include <sound/soc-dai.h>
#include <sound/soc.h>
+struct asoc_simple_jack {
+ struct snd_soc_jack jack;
+ struct snd_soc_jack_pin pin;
+ struct snd_soc_jack_gpio gpio;
+};
+
struct simple_card_data {
struct snd_soc_card snd_card;
struct simple_dai_props {
@@ -29,29 +35,92 @@ struct simple_card_data {
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 */
+ struct asoc_simple_jack hp_jack;
+ struct asoc_simple_jack mic_jack;
+ struct snd_soc_dai_link *dai_link;
};
#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_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
+#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
+
+#define DAI "sound-dai"
+#define CELL "#sound-dai-cells"
+#define PREFIX "simple-audio-card,"
+
+#define asoc_simple_card_init_hp(card, sjack, prefix)\
+ asoc_simple_card_init_jack(card, sjack, 1, prefix)
+#define asoc_simple_card_init_mic(card, sjack, prefix)\
+ asoc_simple_card_init_jack(card, sjack, 0, prefix)
+static int asoc_simple_card_init_jack(struct snd_soc_card *card,
+ struct asoc_simple_jack *sjack,
+ int is_hp, char *prefix)
+{
+ struct device *dev = card->dev;
+ enum of_gpio_flags flags;
+ char prop[128];
+ char *pin_name;
+ char *gpio_name;
+ int mask;
+ int det;
+
+ sjack->gpio.gpio = -ENOENT;
+
+ if (is_hp) {
+ snprintf(prop, sizeof(prop), "%shp-det-gpio", prefix);
+ pin_name = "Headphones";
+ gpio_name = "Headphone detection";
+ mask = SND_JACK_HEADPHONE;
+ } else {
+ snprintf(prop, sizeof(prop), "%smic-det-gpio", prefix);
+ pin_name = "Mic Jack";
+ gpio_name = "Mic detection";
+ mask = SND_JACK_MICROPHONE;
+ }
+
+ det = of_get_named_gpio_flags(dev->of_node, prop, 0, &flags);
+ if (det == -EPROBE_DEFER)
+ return -EPROBE_DEFER;
+
+ if (gpio_is_valid(det)) {
+ sjack->pin.pin = pin_name;
+ sjack->pin.mask = mask;
+
+ sjack->gpio.name = gpio_name;
+ sjack->gpio.report = mask;
+ sjack->gpio.gpio = det;
+ sjack->gpio.invert = !!(flags & OF_GPIO_ACTIVE_LOW);
+ sjack->gpio.debounce_time = 150;
+
+ snd_soc_card_jack_new(card, pin_name, mask,
+ &sjack->jack,
+ &sjack->pin, 1);
+
+ snd_soc_jack_add_gpios(&sjack->jack, 1,
+ &sjack->gpio);
+ }
+
+ return 0;
+}
+
+static void asoc_simple_card_remove_jack(struct asoc_simple_jack *sjack)
+{
+ if (gpio_is_valid(sjack->gpio.gpio))
+ snd_soc_jack_free_gpios(&sjack->jack, 1, &sjack->gpio);
+}
static int asoc_simple_card_startup(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];
+ simple_priv_to_props(priv, rtd->num);
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);
@@ -64,7 +133,7 @@ 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];
+ simple_priv_to_props(priv, rtd->num);
clk_disable_unprepare(dai_props->cpu_dai.clk);
@@ -78,7 +147,8 @@ static int asoc_simple_card_hw_params(struct snd_pcm_substream *substream,
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];
+ struct simple_dai_props *dai_props =
+ simple_priv_to_props(priv, rtd->num);
unsigned int mclk, mclk_fs = 0;
int ret = 0;
@@ -110,210 +180,31 @@ static struct snd_soc_ops asoc_simple_card_ops = {
.hw_params = asoc_simple_card_hw_params,
};
-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 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,
-};
-
-static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
- struct asoc_simple_dai *set)
-{
- 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 (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 = 0;
-
-err:
- return ret;
-}
-
static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
{
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 simple_dai_props *dai_props =
+ simple_priv_to_props(priv, rtd->num);
int ret;
- dai_props = &priv->dai_props[rtd->num];
- ret = __asoc_simple_card_dai_init(codec, &dai_props->codec_dai);
+ ret = asoc_simple_card_init_dai(codec, &dai_props->codec_dai);
if (ret < 0)
return ret;
- ret = __asoc_simple_card_dai_init(cpu, &dai_props->cpu_dai);
+ ret = asoc_simple_card_init_dai(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;
-}
-
-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)
-{
- struct of_phandle_args args;
- struct clk *clk;
- u32 val;
- 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;
-
- *p_node = args.np;
-
- if (args_count)
- *args_count = args.args_count;
-
- /* Get dai->name */
- ret = snd_soc_of_get_dai_name(np, name);
+ ret = asoc_simple_card_init_hp(rtd->card, &priv->hp_jack, PREFIX);
if (ret < 0)
return ret;
- /* 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)
+ ret = asoc_simple_card_init_mic(rtd->card, &priv->hp_jack, PREFIX);
+ if (ret < 0)
return ret;
- /*
- * 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;
- }
-
- dai->sysclk = clk_get_rate(clk);
- dai->clk = clk;
- } else if (!of_property_read_u32(np, "system-clock-frequency", &val)) {
- dai->sysclk = val;
- } else {
- clk = of_clk_get(args.np, 0);
- if (!IS_ERR(clk))
- dai->sysclk = clk_get_rate(clk);
- }
-
- return 0;
-}
-
-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;
-
- daifmt = snd_soc_of_parse_daifmt(node, prefix,
- &bitclkmaster, &framemaster);
- daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
-
- 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");
-
- 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;
- }
-
- dai_link->dai_fmt = daifmt;
-
- of_node_put(bitclkmaster);
- of_node_put(framemaster);
-
return 0;
}
@@ -325,18 +216,18 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
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 asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
+ struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
struct device_node *cpu = NULL;
struct device_node *plat = NULL;
struct device_node *codec = NULL;
- char *name;
char prop[128];
char *prefix = "";
- int ret, cpu_args;
- u32 val;
+ int ret, single_cpu;
/* For single DAI link & old style of DT node */
if (is_top_level_node)
- prefix = "simple-audio-card,";
+ prefix = PREFIX;
snprintf(prop, sizeof(prop), "%scpu", prefix);
cpu = of_get_child_by_name(node, prop);
@@ -353,56 +244,59 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
goto dai_link_of_err;
}
- ret = asoc_simple_card_parse_daifmt(node, priv,
- codec, prefix, idx);
+ ret = asoc_simple_card_parse_daifmt(dev, node, codec,
+ prefix, &dai_link->dai_fmt);
if (ret < 0)
goto dai_link_of_err;
- if (!of_property_read_u32(node, "mclk-fs", &val))
- dai_props->mclk_fs = val;
+ of_property_read_u32(node, "mclk-fs", &dai_props->mclk_fs);
- 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 = asoc_simple_card_parse_cpu(cpu, dai_link,
+ DAI, CELL, &single_cpu);
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 = asoc_simple_card_parse_codec(codec, dai_link, DAI, CELL);
if (ret < 0)
goto dai_link_of_err;
- if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
- ret = -EINVAL;
+ ret = asoc_simple_card_parse_platform(plat, dai_link, DAI, CELL);
+ if (ret < 0)
+ goto dai_link_of_err;
+
+ ret = snd_soc_of_parse_tdm_slot(cpu, &cpu_dai->tx_slot_mask,
+ &cpu_dai->rx_slot_mask,
+ &cpu_dai->slots,
+ &cpu_dai->slot_width);
+ if (ret < 0)
goto dai_link_of_err;
- }
- if (plat) {
- struct of_phandle_args args;
+ ret = snd_soc_of_parse_tdm_slot(codec, &codec_dai->tx_slot_mask,
+ &codec_dai->rx_slot_mask,
+ &codec_dai->slots,
+ &codec_dai->slot_width);
+ if (ret < 0)
+ goto dai_link_of_err;
- 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 = asoc_simple_card_parse_clk_cpu(cpu, dai_link, cpu_dai);
+ if (ret < 0)
+ goto dai_link_of_err;
- /* 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;
+ ret = asoc_simple_card_parse_clk_codec(codec, dai_link, codec_dai);
+ if (ret < 0)
+ goto dai_link_of_err;
+
+ ret = asoc_simple_card_canonicalize_dailink(dai_link);
+ if (ret < 0)
+ goto dai_link_of_err;
+
+ ret = asoc_simple_card_set_dailink_name(dev, dai_link,
+ "%s-%s",
+ dai_link->cpu_dai_name,
+ dai_link->codec_dai_name);
+ if (ret < 0)
goto dai_link_of_err;
- }
- 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;
@@ -415,17 +309,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
dai_link->codec_dai_name,
dai_props->codec_dai.sysclk);
- /*
- * 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 (!cpu_args)
- dai_link->cpu_dai_name = NULL;
+ asoc_simple_card_canonicalize_cpu(dai_link, single_cpu);
dai_link_of_err:
of_node_put(cpu);
@@ -434,46 +318,69 @@ dai_link_of_err:
return ret;
}
+static int asoc_simple_card_parse_aux_devs(struct device_node *node,
+ struct simple_card_data *priv)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *aux_node;
+ int i, n, len;
+
+ if (!of_find_property(node, PREFIX "aux-devs", &len))
+ return 0; /* Ok to have no aux-devs */
+
+ n = len / sizeof(__be32);
+ if (n <= 0)
+ return -EINVAL;
+
+ priv->snd_card.aux_dev = devm_kzalloc(dev,
+ n * sizeof(*priv->snd_card.aux_dev), GFP_KERNEL);
+ if (!priv->snd_card.aux_dev)
+ return -ENOMEM;
+
+ for (i = 0; i < n; i++) {
+ aux_node = of_parse_phandle(node, PREFIX "aux-devs", i);
+ if (!aux_node)
+ return -EINVAL;
+ priv->snd_card.aux_dev[i].codec_of_node = aux_node;
+ }
+
+ priv->snd_card.num_aux_devs = n;
+ return 0;
+}
+
static int asoc_simple_card_parse_of(struct device_node *node,
struct simple_card_data *priv)
{
struct device *dev = simple_priv_to_dev(priv);
- enum of_gpio_flags flags;
- u32 val;
+ struct device_node *dai_link;
int ret;
if (!node)
return -EINVAL;
- /* Parse the card name from DT */
- snd_soc_of_parse_card_name(&priv->snd_card, "simple-audio-card,name");
+ dai_link = of_get_child_by_name(node, PREFIX "dai-link");
/* The off-codec widgets */
- if (of_property_read_bool(node, "simple-audio-card,widgets")) {
+ if (of_property_read_bool(node, PREFIX "widgets")) {
ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
- "simple-audio-card,widgets");
+ PREFIX "widgets");
if (ret)
- return ret;
+ goto card_parse_end;
}
/* DAPM routes */
- if (of_property_read_bool(node, "simple-audio-card,routing")) {
+ if (of_property_read_bool(node, PREFIX "routing")) {
ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
- "simple-audio-card,routing");
+ PREFIX "routing");
if (ret)
- return ret;
+ goto card_parse_end;
}
/* 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;
-
- dev_dbg(dev, "New simple-card: %s\n", priv->snd_card.name ?
- priv->snd_card.name : "");
+ of_property_read_u32(node, PREFIX "mclk-fs", &priv->mclk_fs);
/* Single/Muti DAI link(s) & New style of DT node */
- if (of_get_child_by_name(node, "simple-audio-card,dai-link")) {
+ if (dai_link) {
struct device_node *np = NULL;
int i = 0;
@@ -483,7 +390,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
i, false);
if (ret < 0) {
of_node_put(np);
- return ret;
+ goto card_parse_end;
}
i++;
}
@@ -491,80 +398,55 @@ static int asoc_simple_card_parse_of(struct device_node *node,
/* 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;
+ goto card_parse_end;
}
- 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;
-
- 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;
-
- if (!priv->snd_card.name)
- priv->snd_card.name = priv->snd_card.dai_link->name;
+ ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
+ if (ret < 0)
+ goto card_parse_end;
- return 0;
-}
+ ret = asoc_simple_card_parse_aux_devs(node, priv);
-/* Decrease the reference count of the device nodes */
-static int asoc_simple_card_unref(struct snd_soc_card *card)
-{
- struct snd_soc_dai_link *dai_link;
- int num_links;
+card_parse_end:
+ of_node_put(dai_link);
- 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;
+ return ret;
}
static int asoc_simple_card_probe(struct platform_device *pdev)
{
struct simple_card_data *priv;
struct snd_soc_dai_link *dai_link;
+ struct simple_dai_props *dai_props;
struct device_node *np = pdev->dev.of_node;
struct device *dev = &pdev->dev;
- int num_links, ret;
+ int num, 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);
+ if (np && of_get_child_by_name(np, PREFIX "dai-link"))
+ num = of_get_child_count(np);
else
- num_links = 1;
+ num = 1;
/* 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)
+ dai_props = devm_kzalloc(dev, sizeof(*dai_props) * num, GFP_KERNEL);
+ dai_link = devm_kzalloc(dev, sizeof(*dai_link) * num, GFP_KERNEL);
+ if (!dai_props || !dai_link)
return -ENOMEM;
+ priv->dai_props = dai_props;
+ priv->dai_link = dai_link;
+
+ /* 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;
+
if (np && of_device_is_available(np)) {
ret = asoc_simple_card_parse_of(np, priv);
@@ -605,7 +487,6 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
sizeof(priv->dai_props->cpu_dai));
memcpy(&priv->dai_props->codec_dai, &cinfo->codec_dai,
sizeof(priv->dai_props->codec_dai));
-
}
snd_soc_card_set_drvdata(&priv->snd_card, priv);
@@ -613,9 +494,9 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
if (ret >= 0)
return ret;
-
err:
- asoc_simple_card_unref(&priv->snd_card);
+ asoc_simple_card_clean_reference(&priv->snd_card);
+
return ret;
}
@@ -624,14 +505,10 @@ 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);
+ asoc_simple_card_remove_jack(&priv->hp_jack);
+ asoc_simple_card_remove_jack(&priv->mic_jack);
- return asoc_simple_card_unref(card);
+ return asoc_simple_card_clean_reference(card);
}
static const struct of_device_id asoc_simple_of_match[] = {
@@ -653,6 +530,6 @@ static struct platform_driver asoc_simple_card = {
module_platform_driver(asoc_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/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c
new file mode 100644
index 000000000000..b9973a56bcb0
--- /dev/null
+++ b/sound/soc/generic/simple-scu-card.c
@@ -0,0 +1,345 @@
+/*
+ * ASoC simple SCU sound card support
+ *
+ * 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>
+#include <sound/simple_card_utils.h>
+
+struct asoc_simple_card_priv {
+ struct snd_soc_card snd_card;
+ struct snd_soc_codec_conf codec_conf;
+ struct asoc_simple_dai *dai_props;
+ struct snd_soc_dai_link *dai_link;
+ u32 convert_rate;
+ u32 convert_channels;
+};
+
+#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 DAI "sound-dai"
+#define CELL "#sound-dai-cells"
+#define PREFIX "simple-audio-card,"
+
+static int asoc_simple_card_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct asoc_simple_dai *dai_props =
+ simple_priv_to_props(priv, rtd->num);
+
+ return clk_prepare_enable(dai_props->clk);
+}
+
+static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct asoc_simple_dai *dai_props =
+ simple_priv_to_props(priv, rtd->num);
+
+ clk_disable_unprepare(dai_props->clk);
+}
+
+static struct snd_soc_ops asoc_simple_card_ops = {
+ .startup = asoc_simple_card_startup,
+ .shutdown = asoc_simple_card_shutdown,
+};
+
+static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct asoc_simple_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ struct snd_soc_dai *dai;
+ struct snd_soc_dai_link *dai_link;
+ struct asoc_simple_dai *dai_props;
+ int num = rtd->num;
+
+ dai_link = simple_priv_to_link(priv, num);
+ dai_props = simple_priv_to_props(priv, num);
+ dai = dai_link->dynamic ?
+ rtd->cpu_dai :
+ rtd->codec_dai;
+
+ return asoc_simple_card_init_dai(dai, dai_props);
+}
+
+static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct asoc_simple_card_priv *priv = 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);
+
+ if (priv->convert_rate)
+ rate->min =
+ rate->max = priv->convert_rate;
+
+ if (priv->convert_channels)
+ channels->min =
+ channels->max = priv->convert_channels;
+
+ return 0;
+}
+
+static int asoc_simple_card_parse_links(struct device_node *np,
+ struct asoc_simple_card_priv *priv,
+ unsigned int daifmt,
+ int idx, bool is_fe)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
+ struct asoc_simple_dai *dai_props = simple_priv_to_props(priv, idx);
+ int ret;
+
+ if (is_fe) {
+ int is_single_links = 0;
+
+ /* 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;
+
+ ret = asoc_simple_card_parse_cpu(np, dai_link, DAI, CELL,
+ &is_single_links);
+ if (ret)
+ return ret;
+
+ ret = asoc_simple_card_parse_clk_cpu(np, dai_link, dai_props);
+ if (ret < 0)
+ return ret;
+
+ ret = asoc_simple_card_set_dailink_name(dev, dai_link,
+ "fe.%s",
+ dai_link->cpu_dai_name);
+ if (ret < 0)
+ return ret;
+
+ asoc_simple_card_canonicalize_cpu(dai_link, is_single_links);
+ } else {
+ /* 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 = asoc_simple_card_be_hw_params_fixup;
+
+ ret = asoc_simple_card_parse_codec(np, dai_link, DAI, CELL);
+ if (ret < 0)
+ return ret;
+
+ ret = asoc_simple_card_parse_clk_codec(np, dai_link, dai_props);
+ if (ret < 0)
+ return ret;
+
+ ret = asoc_simple_card_set_dailink_name(dev, dai_link,
+ "be.%s",
+ dai_link->codec_dai_name);
+ if (ret < 0)
+ return ret;
+
+ snd_soc_of_parse_audio_prefix(&priv->snd_card,
+ &priv->codec_conf,
+ dai_link->codec_of_node,
+ PREFIX "prefix");
+ }
+
+ 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;
+
+ ret = asoc_simple_card_canonicalize_dailink(dai_link);
+ if (ret < 0)
+ return ret;
+
+ dai_link->dai_fmt = daifmt;
+ dai_link->dpcm_playback = 1;
+ dai_link->dpcm_capture = 1;
+ dai_link->ops = &asoc_simple_card_ops;
+ dai_link->init = asoc_simple_card_dai_init;
+
+ dev_dbg(dev, "\t%s / %04x / %d\n",
+ dai_link->name,
+ dai_link->dai_fmt,
+ dai_props->sysclk);
+
+ return 0;
+}
+
+static int asoc_simple_card_dai_link_of(struct device_node *node,
+ struct asoc_simple_card_priv *priv)
+{
+ struct device *dev = simple_priv_to_dev(priv);
+ struct device_node *np;
+ unsigned int daifmt = 0;
+ int ret, i;
+ bool is_fe;
+
+ /* find 1st codec */
+ np = of_get_child_by_name(node, PREFIX "codec");
+ if (!np)
+ return -ENODEV;
+
+ ret = asoc_simple_card_parse_daifmt(dev, node, np,
+ PREFIX, &daifmt);
+ if (ret < 0)
+ return ret;
+
+ i = 0;
+ for_each_child_of_node(node, np) {
+ is_fe = false;
+ if (strcmp(np->name, PREFIX "cpu") == 0)
+ is_fe = true;
+
+ ret = asoc_simple_card_parse_links(np, priv, daifmt, i, is_fe);
+ if (ret < 0)
+ return ret;
+ i++;
+ }
+
+ return 0;
+}
+
+static int asoc_simple_card_parse_of(struct device_node *node,
+ struct asoc_simple_card_priv *priv,
+ struct device *dev)
+{
+ struct asoc_simple_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;
+
+ /* 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;
+
+ ret = snd_soc_of_parse_audio_routing(&priv->snd_card, PREFIX "routing");
+ if (ret < 0)
+ return ret;
+
+ /* sampling rate convert */
+ of_property_read_u32(node, PREFIX "convert-rate", &priv->convert_rate);
+
+ /* channels transfer */
+ of_property_read_u32(node, PREFIX "convert-channels", &priv->convert_channels);
+
+ ret = asoc_simple_card_dai_link_of(node, priv);
+ if (ret < 0)
+ return ret;
+
+ ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
+ if (ret < 0)
+ return ret;
+
+ dev_dbg(dev, "New card: %s\n",
+ priv->snd_card.name ? priv->snd_card.name : "");
+ dev_dbg(dev, "convert_rate %d\n", priv->convert_rate);
+ dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
+
+ return 0;
+}
+
+static int asoc_simple_card_probe(struct platform_device *pdev)
+{
+ struct asoc_simple_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 = asoc_simple_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:
+ asoc_simple_card_clean_reference(&priv->snd_card);
+
+ return ret;
+}
+
+static int asoc_simple_card_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+ return asoc_simple_card_clean_reference(card);
+}
+
+static const struct of_device_id asoc_simple_of_match[] = {
+ { .compatible = "renesas,rsrc-card", },
+ { .compatible = "simple-scu-audio-card", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, asoc_simple_of_match);
+
+static struct platform_driver asoc_simple_card = {
+ .driver = {
+ .name = "simple-scu-audio-card",
+ .of_match_table = asoc_simple_of_match,
+ },
+ .probe = asoc_simple_card_probe,
+ .remove = asoc_simple_card_remove,
+};
+
+module_platform_driver(asoc_simple_card);
+
+MODULE_ALIAS("platform:asoc-simple-scu-card");
+MODULE_LICENSE("GPL v2");
+MODULE_DESCRIPTION("ASoC Simple SCU Sound Card");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
diff --git a/sound/soc/img/pistachio-internal-dac.c b/sound/soc/img/pistachio-internal-dac.c
index 162a0fd68c7b..53e11c6d4e22 100644
--- a/sound/soc/img/pistachio-internal-dac.c
+++ b/sound/soc/img/pistachio-internal-dac.c
@@ -134,12 +134,14 @@ static int pistachio_internal_dac_codec_probe(struct snd_soc_codec *codec)
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),
+ .component_driver = {
+ .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 int pistachio_internal_dac_probe(struct platform_device *pdev)
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 91c15abb625e..26eb5a0a5575 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -7,7 +7,7 @@ config SND_MFLD_MACHINE
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
+ Say Y if you have such a device.
If unsure select "N".
config SND_SST_MFLD_PLATFORM
@@ -25,7 +25,7 @@ config SND_SST_IPC_ACPI
tristate
select SND_SST_IPC
select SND_SOC_INTEL_SST
- depends on ACPI
+ select IOSF_MBI
config SND_SOC_INTEL_SST
tristate
@@ -33,6 +33,12 @@ config SND_SOC_INTEL_SST
select SND_SOC_INTEL_SST_MATCH if ACPI
depends on (X86 || COMPILE_TEST)
+# firmware stuff depends DW_DMAC_CORE; since there is no depends-on from
+# the reverse selection, each machine driver needs to select
+# SND_SOC_INTEL_SST_FIRMWARE carefully depending on DW_DMAC_CORE
+config SND_SOC_INTEL_SST_FIRMWARE
+ tristate
+
config SND_SOC_INTEL_SST_ACPI
tristate
@@ -48,16 +54,33 @@ config SND_SOC_INTEL_BAYTRAIL
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
+ depends on DW_DMAC_CORE
select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_SST_FIRMWARE
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
+ Say Y if you have such a device.
If unsure select "N".
+config SND_SOC_INTEL_BXT_DA7219_MAX98357A_MACH
+ tristate "ASoC Audio driver for Broxton with DA7219 and MAX98357A in I2S Mode"
+ depends on X86 && ACPI && I2C
+ select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_SKYLAKE
+ select SND_SOC_DA7219
+ select SND_SOC_MAX98357A
+ select SND_SOC_DMIC
+ select SND_SOC_HDAC_HDMI
+ select SND_HDA_DSP_LOADER
+ help
+ This adds support for ASoC machine driver for Broxton-P platforms
+ with DA7219 + MAX98357A I2S audio codec.
+ Say Y if you have such a device.
+ If unsure select "N".
+
config SND_SOC_INTEL_BXT_RT298_MACH
tristate "ASoC Audio driver for Broxton with RT298 I2S mode"
depends on X86 && ACPI && I2C
@@ -70,49 +93,63 @@ config SND_SOC_INTEL_BXT_RT298_MACH
help
This adds support for ASoC machine driver for Broxton platforms
with RT286 I2S audio codec.
- Say Y if you have such a device
+ 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_SST_IPC_ACPI = n)
+ depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n)
select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_SST_FIRMWARE
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
+ 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 && (SND_SST_IPC_ACPI = n)
+ depends on DW_DMAC_CORE && (SND_SST_IPC_ACPI = n)
select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_SST_FIRMWARE
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_BDW_RT5677_MACH
+ tristate "ASoC Audio driver for Intel Broadwell with RT5677 codec"
+ depends on X86_INTEL_LPSS && GPIOLIB && I2C && DW_DMAC
+ depends on DW_DMAC_CORE=y
+ select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_HASWELL
+ select SND_SOC_RT5677
+ help
+ This adds support for Intel Broadwell platform based boards with
+ the RT5677 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
+ depends on DW_DMAC_CORE
select SND_SOC_INTEL_SST
+ select SND_SOC_INTEL_SST_FIRMWARE
select SND_SOC_INTEL_HASWELL
select SND_SOC_RT286
help
This adds support for the Wilcatpoint Audio DSP on Intel(R) Broadwell
Ultrabook platforms.
- Say Y if you have such a device
+ 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
+ depends on X86 && I2C && ACPI
select SND_SOC_RT5640
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
@@ -120,12 +157,12 @@ config SND_SOC_INTEL_BYTCR_RT5640_MACH
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
+ 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
+ depends on X86 && I2C && ACPI
select SND_SOC_RT5651
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
@@ -133,12 +170,12 @@ config SND_SOC_INTEL_BYTCR_RT5651_MACH
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
+ 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
+ depends on X86_INTEL_LPSS && I2C && ACPI
select SND_SOC_RT5670
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
@@ -146,12 +183,12 @@ config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
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
+ 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
+ depends on X86_INTEL_LPSS && I2C && ACPI
select SND_SOC_RT5645
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
@@ -163,16 +200,16 @@ config SND_SOC_INTEL_CHT_BSW_RT5645_MACH
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
+ depends on X86_INTEL_LPSS && I2C && ACPI
select SND_SOC_MAX98090
select SND_SOC_TS3A227E
select SND_SST_MFLD_PLATFORM
select SND_SST_IPC_ACPI
select SND_SOC_INTEL_SST_MATCH if 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".
+ 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
@@ -192,7 +229,7 @@ config SND_SOC_INTEL_SKL_RT286_MACH
help
This adds support for ASoC machine driver for Skylake platforms
with RT286 I2S audio codec.
- Say Y if you have such a device
+ Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
@@ -207,7 +244,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
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
+ Say Y if you have such a device.
If unsure select "N".
config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
@@ -222,5 +259,5 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
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
+ Say Y if you have such a device.
If unsure select "N".
diff --git a/sound/soc/intel/atom/sst-atom-controls.c b/sound/soc/intel/atom/sst-atom-controls.c
index 98720a93de8a..0838478c4c3f 100644
--- a/sound/soc/intel/atom/sst-atom-controls.c
+++ b/sound/soc/intel/atom/sst-atom-controls.c
@@ -1,4 +1,4 @@
-/*
+ /*
* sst-atom-controls.c - Intel MID Platform driver DPCM ALSA controls for Mrfld
*
* Copyright (C) 2013-14 Intel Corp
@@ -534,6 +534,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,
@@ -674,6 +675,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 +686,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" }, \
@@ -713,6 +716,7 @@ SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls);
SST_SBA_DECLARE_MIX_CONTROLS(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
@@ -931,17 +935,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_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;
@@ -1047,8 +1060,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),
@@ -1103,6 +1118,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 +1166,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 +1238,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
diff --git a/sound/soc/intel/atom/sst-atom-controls.h b/sound/soc/intel/atom/sst-atom-controls.h
index e0113112f668..351d81469685 100644
--- a/sound/soc/intel/atom/sst-atom-controls.h
+++ b/sound/soc/intel/atom/sst-atom-controls.h
@@ -35,6 +35,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 +65,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 +83,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 +109,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 +129,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),
diff --git a/sound/soc/intel/atom/sst-mfld-platform-compress.c b/sound/soc/intel/atom/sst-mfld-platform-compress.c
index 395168986462..1bead81bb510 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-compress.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-compress.c
@@ -182,24 +182,29 @@ 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,
diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
index 52ed434cbca6..25c6d87c818e 100644
--- a/sound/soc/intel/atom/sst-mfld-platform-pcm.c
+++ b/sound/soc/intel/atom/sst-mfld-platform-pcm.c
@@ -670,7 +670,7 @@ static snd_pcm_uframes_t sst_platform_pcm_pointer
return str_info->buffer_ptr;
}
-static struct snd_pcm_ops sst_platform_ops = {
+static const struct snd_pcm_ops sst_platform_ops = {
.open = sst_platform_open,
.ioctl = snd_pcm_lib_ioctl,
.trigger = sst_platform_pcm_trigger,
diff --git a/sound/soc/intel/atom/sst/sst.c b/sound/soc/intel/atom/sst/sst.c
index a4b458e77089..9b6e27385dc9 100644
--- a/sound/soc/intel/atom/sst/sst.c
+++ b/sound/soc/intel/atom/sst/sst.c
@@ -190,7 +190,8 @@ int sst_driver_ops(struct intel_sst_drv *sst)
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;
};
}
@@ -441,7 +442,7 @@ 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;
}
}
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 3bc4b63b2f9d..ba5c0d71720a 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -28,6 +28,7 @@
#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>
@@ -38,6 +39,8 @@
#include <acpi/platform/aclinux.h>
#include <acpi/actypes.h>
#include <acpi/acpi_bus.h>
+#include <asm/cpu_device_id.h>
+#include <asm/iosf_mbi.h>
#include "../sst-mfld-platform.h"
#include "../../common/sst-dsp.h"
#include "../../common/sst-acpi.h"
@@ -112,6 +115,28 @@ 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
+};
+
static struct sst_platform_info byt_rvp_platform_data = {
.probe_data = &byt_fwparse_info,
.ipc_info = &byt_ipc_info,
@@ -141,7 +166,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,
@@ -153,7 +178,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
ctx->iram = devm_ioremap_nocache(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;
}
@@ -163,7 +188,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
ctx->dram = devm_ioremap_nocache(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;
}
@@ -172,7 +197,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
ctx->shim = devm_ioremap_nocache(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;
}
@@ -185,7 +210,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
ctx->mailbox = devm_ioremap_nocache(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,7 +220,7 @@ 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;
@@ -204,7 +229,7 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
ctx->ddr = devm_ioremap_nocache(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;
}
@@ -214,6 +239,46 @@ static int sst_platform_get_resources(struct intel_sst_drv *ctx)
return 0;
}
+
+static int is_byt_cr(struct device *dev, bool *bytcr)
+{
+ int status = 0;
+
+ if (IS_ENABLED(CONFIG_IOSF_MBI)) {
+ static const struct x86_cpu_id cpu_ids[] = {
+ { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
+ {}
+ };
+ u32 bios_status;
+
+ if (!x86_match_cpu(cpu_ids) || !iosf_mbi_available()) {
+ /* bail silently */
+ return 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))
+ *bytcr = true;
+ else
+ dev_info(dev, "BYT-CR not detected\n");
+ }
+ } else {
+ dev_info(dev, "IOSF_MBI not enabled, no BYT-CR detection\n");
+ }
+ return status;
+}
+
+
static int sst_acpi_probe(struct platform_device *pdev)
{
struct device *dev = &pdev->dev;
@@ -225,11 +290,12 @@ static int sst_acpi_probe(struct platform_device *pdev)
struct platform_device *plat_dev;
struct sst_platform_info *pdata;
unsigned int dev_id;
+ bool bytcr = false;
id = acpi_match_device(dev->driver->acpi_match_table, dev);
if (!id)
return -ENODEV;
- dev_dbg(dev, "for %s", id->id);
+ dev_dbg(dev, "for %s\n", id->id);
mach = (struct sst_acpi_mach *)id->driver_data;
mach = sst_acpi_find_machine(mach);
@@ -237,6 +303,9 @@ static int sst_acpi_probe(struct platform_device *pdev)
dev_err(dev, "No matching machine driver found\n");
return -ENODEV;
}
+ if (mach->machine_quirk)
+ mach = mach->machine_quirk(mach);
+
pdata = mach->pdata;
ret = kstrtouint(id->id, 16, &dev_id);
@@ -247,6 +316,18 @@ static int sst_acpi_probe(struct platform_device *pdev)
dev_dbg(dev, "ACPI device id: %x\n", dev_id);
+ ret = sst_alloc_drv_context(&ctx, dev, dev_id);
+ if (ret < 0)
+ return ret;
+
+ ret = is_byt_cr(dev, &bytcr);
+ if (!((ret < 0) || (bytcr == false))) {
+ dev_info(dev, "Detected Baytrail-CR platform\n");
+
+ /* override resource info */
+ byt_rvp_platform_data.res_info = &bytcr_res_info;
+ }
+
plat_dev = platform_device_register_data(dev, pdata->platform, -1,
NULL, 0);
if (IS_ERR(plat_dev)) {
@@ -267,10 +348,6 @@ 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);
@@ -320,6 +397,44 @@ static int sst_acpi_remove(struct platform_device *pdev)
return 0;
}
+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 sst_acpi_mach cht_surface_mach = {
+ "10EC5640", "cht-bsw-rt5645", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
+ &chv_platform_data };
+
+static struct sst_acpi_mach *cht_quirk(void *arg)
+{
+ struct sst_acpi_mach *mach = arg;
+
+ dmi_check_system(cht_table);
+
+ if (cht_machine_id == CHT_SURFACE_MACH)
+ return &cht_surface_mach;
+ else
+ return mach;
+}
+
static struct sst_acpi_mach sst_acpi_bytcr[] = {
{"10EC5640", "bytcr_rt5640", "intel/fw_sst_0f28.bin", "bytcr_rt5640", NULL,
&byt_rvp_platform_data },
@@ -343,7 +458,7 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
{"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
&chv_platform_data },
/* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
- {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", NULL,
+ {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", cht_quirk,
&chv_platform_data },
{},
diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c
index 8afa6fe7b0b0..bfc889950bb2 100644
--- a/sound/soc/intel/atom/sst/sst_ipc.c
+++ b/sound/soc/intel/atom/sst/sst_ipc.c
@@ -267,6 +267,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)
diff --git a/sound/soc/intel/atom/sst/sst_pvt.c b/sound/soc/intel/atom/sst/sst_pvt.c
index adb32fefd693..b1e6b8f34a6a 100644
--- a/sound/soc/intel/atom/sst/sst_pvt.c
+++ b/sound/soc/intel/atom/sst/sst_pvt.c
@@ -279,17 +279,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:
diff --git a/sound/soc/intel/baytrail/sst-baytrail-ipc.c b/sound/soc/intel/baytrail/sst-baytrail-ipc.c
index 5bbaa667bec1..c8455b47388b 100644
--- a/sound/soc/intel/baytrail/sst-baytrail-ipc.c
+++ b/sound/soc/intel/baytrail/sst-baytrail-ipc.c
@@ -184,15 +184,9 @@ struct sst_byt {
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;
+ return IPC_HEADER_MSG_ID(msg_id) | IPC_HEADER_STR_ID(str_id) |
+ IPC_HEADER_LARGE(large) | IPC_HEADER_DATA(data) |
+ SST_BYT_IPCX_BUSY;
}
static inline u16 sst_byt_header_msg_id(u64 header)
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index a8506774f510..5639f10774e6 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -1,7 +1,9 @@
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-bdw-rt5677-mach-objs := bdw-rt5677.o
snd-soc-sst-broadwell-objs := broadwell.o
+snd-soc-sst-bxt-da7219_max98357a-objs := bxt_da7219_max98357a.o
snd-soc-sst-bxt-rt298-objs := bxt_rt298.o
snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
@@ -15,8 +17,10 @@ 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_BXT_DA7219_MAX98357A_MACH) += snd-soc-sst-bxt-da7219_max98357a.o
obj-$(CONFIG_SND_SOC_INTEL_BXT_RT298_MACH) += snd-soc-sst-bxt-rt298.o
obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.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_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c
new file mode 100644
index 000000000000..547e6705bf6d
--- /dev/null
+++ b/sound/soc/intel/boards/bdw-rt5677.c
@@ -0,0 +1,347 @@
+/*
+ * ASoC machine driver for Intel Broadwell platforms with RT5677 codec
+ *
+ * Copyright (c) 2014, The Chromium OS Authors. 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/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 "../common/sst-dsp.h"
+#include "../haswell/sst-haswell-ipc.h"
+
+#include "../../codecs/rt5677.h"
+
+struct bdw_rt5677_priv {
+ struct gpio_desc *gpio_hp_en;
+ struct snd_soc_codec *codec;
+};
+
+static int bdw_rt5677_event_hp(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 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"},
+};
+
+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,
+};
+
+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 */
+ snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+ SNDRV_PCM_HW_PARAM_FIRST_MASK],
+ 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 = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ 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 struct snd_soc_ops bdw_rt5677_ops = {
+ .hw_params = bdw_rt5677_hw_params,
+};
+
+static int bdw_rt5677_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;
+}
+
+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_codec *codec = rtd->codec;
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ /* Enable codec ASRC function for Stereo DAC/Stereo1 ADC/DMIC/I2S1.
+ * The ASRC clock source is clk_i2s1_asrc.
+ */
+ rt5677_sel_asrc_clk_src(codec, RT5677_DA_STEREO_FILTER |
+ RT5677_AD_STEREO1_FILTER | RT5677_I2S1_SOURCE,
+ RT5677_CLK_SEL_I2S1_ASRC);
+
+ /* Request rt5677 GPIO for headphone amp control */
+ bdw_rt5677->gpio_hp_en = devm_gpiod_get_index(codec->dev,
+ "headphone-enable", 0, 0);
+ if (IS_ERR(bdw_rt5677->gpio_hp_en)) {
+ dev_err(codec->dev, "Can't find HP_AMP_SHDN_L gpio\n");
+ return PTR_ERR(bdw_rt5677->gpio_hp_en);
+ }
+ gpiod_direction_output(bdw_rt5677->gpio_hp_en, 0);
+
+ /* Create and initialize headphone jack */
+ if (!snd_soc_card_jack_new(rtd->card, "Headphone Jack",
+ SND_JACK_HEADPHONE, &headphone_jack,
+ &headphone_jack_pin, 1)) {
+ headphone_jack_gpio.gpiod_dev = codec->dev;
+ if (snd_soc_jack_add_gpios(&headphone_jack, 1,
+ &headphone_jack_gpio))
+ dev_err(codec->dev, "Can't add headphone jack gpio\n");
+ } else {
+ dev_err(codec->dev, "Can't create headphone jack\n");
+ }
+
+ /* Create and initialize mic jack */
+ if (!snd_soc_card_jack_new(rtd->card, "Mic Jack",
+ SND_JACK_MICROPHONE, &mic_jack,
+ &mic_jack_pin, 1)) {
+ mic_jack_gpio.gpiod_dev = codec->dev;
+ if (snd_soc_jack_add_gpios(&mic_jack, 1, &mic_jack_gpio))
+ dev_err(codec->dev, "Can't add mic jack gpio\n");
+ } else {
+ dev_err(codec->dev, "Can't create mic jack\n");
+ }
+ bdw_rt5677->codec = codec;
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
+ return 0;
+}
+
+/* broadwell digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link bdw_rt5677_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 = bdw_rt5677_rtd_init,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST
+ },
+ .dpcm_capture = 1,
+ .dpcm_playback = 1,
+ },
+
+ /* Back End DAI links */
+ {
+ /* SSP0 - Codec */
+ .name = "Codec",
+ .id = 0,
+ .cpu_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "snd-soc-dummy",
+ .no_pcm = 1,
+ .codec_name = "i2c-RT5677CE:00",
+ .codec_dai_name = "rt5677-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 = broadwell_ssp0_fixup,
+ .ops = &bdw_rt5677_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .init = bdw_rt5677_init,
+ },
+};
+
+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->codec) {
+ dapm = snd_soc_codec_get_dapm(bdw_rt5677->codec);
+ 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->codec) {
+ dapm = snd_soc_codec_get_dapm(bdw_rt5677->codec);
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS1");
+ }
+ return 0;
+}
+
+/* ASoC machine driver for Broadwell DSP + RT5677 */
+static struct snd_soc_card bdw_rt5677_card = {
+ .name = "bdw-rt5677",
+ .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;
+
+ 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) {
+ dev_err(&pdev->dev, "Can't allocate bdw_rt5677\n");
+ return -ENOMEM;
+ }
+
+ 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",
+ },
+};
+
+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/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c
new file mode 100644
index 000000000000..6532b8f0ab2f
--- /dev/null
+++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c
@@ -0,0 +1,523 @@
+/*
+ * Intel Broxton-P I2S Machine Driver
+ *
+ * Copyright (C) 2016, Intel Corporation. All rights reserved.
+ *
+ * Modified from:
+ * Intel Skylake I2S Machine 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.
+ *
+ * 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/hdac_hdmi.h"
+#include "../../codecs/da7219.h"
+#include "../../codecs/da7219-aad.h"
+
+#define BXT_DIALOG_CODEC_DAI "da7219-hifi"
+#define BXT_MAXIM_CODEC_DAI "HiFi"
+#define DUAL_CHANNEL 2
+
+static struct snd_soc_jack broxton_headset;
+
+enum {
+ BXT_DPCM_AUDIO_PB = 0,
+ BXT_DPCM_AUDIO_CP,
+ BXT_DPCM_AUDIO_REF_CP,
+ BXT_DPCM_AUDIO_DMIC_CP,
+ BXT_DPCM_AUDIO_HDMI1_PB,
+ BXT_DPCM_AUDIO_HDMI2_PB,
+ BXT_DPCM_AUDIO_HDMI3_PB,
+};
+
+static const struct snd_kcontrol_new broxton_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 broxton_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_SPK("HDMI1", NULL),
+ SND_SOC_DAPM_SPK("HDMI2", NULL),
+ SND_SOC_DAPM_SPK("HDMI3", NULL),
+};
+
+static const struct snd_soc_dapm_route broxton_map[] = {
+ /* HP jack connectors - unknown if we have jack detection */
+ {"Headphone Jack", NULL, "HPL"},
+ {"Headphone Jack", NULL, "HPR"},
+
+ /* speaker */
+ {"Spk", NULL, "Speaker"},
+
+ /* other jacks */
+ {"MIC", NULL, "Headset Mic"},
+
+ /* digital mics */
+ {"DMic", NULL, "SoC DMIC"},
+
+ /* CODEC BE connections */
+ {"HiFi Playback", NULL, "ssp5 Tx"},
+ {"ssp5 Tx", NULL, "codec0_out"},
+
+ {"Playback", NULL, "ssp1 Tx"},
+ {"ssp1 Tx", NULL, "codec1_out"},
+
+ {"codec0_in", NULL, "ssp1 Rx"},
+ {"ssp1 Rx", NULL, "Capture"},
+
+ {"HDMI1", NULL, "hif5 Output"},
+ {"HDMI2", NULL, "hif6 Output"},
+ {"HDMI3", NULL, "hif7 Output"},
+
+ {"hifi3", NULL, "iDisp3 Tx"},
+ {"iDisp3 Tx", NULL, "iDisp3_out"},
+ {"hifi2", NULL, "iDisp2 Tx"},
+ {"iDisp2 Tx", NULL, "iDisp2_out"},
+ {"hifi1", NULL, "iDisp1 Tx"},
+ {"iDisp1 Tx", NULL, "iDisp1_out"},
+
+ /* DMIC */
+ {"dmic01_hifi", NULL, "DMIC01 Rx"},
+ {"DMIC01 Rx", NULL, "DMIC AIF"},
+};
+
+static int broxton_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 convert the FE rate to 48k, stereo */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = DUAL_CHANNEL;
+
+ /* set SSP to 24 bit */
+ snd_mask_none(fmt);
+ snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE);
+
+ return 0;
+}
+
+static int broxton_da7219_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(rtd->card, "Headset Jack",
+ SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+ SND_JACK_BTN_2 | SND_JACK_BTN_3, &broxton_headset,
+ NULL, 0);
+ if (ret) {
+ dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret);
+ return ret;
+ }
+
+ da7219_aad_jack_det(codec, &broxton_headset);
+
+ snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
+
+ return ret;
+}
+
+static int broxton_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_dai *dai = rtd->codec_dai;
+
+ return hdac_hdmi_jack_init(dai, BXT_DPCM_AUDIO_HDMI1_PB + dai->id);
+}
+
+static int broxton_da7219_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[] = {
+ DUAL_CHANNEL,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int bxt_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 = DUAL_CHANNEL;
+ 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 broxton_da7219_fe_ops = {
+ .startup = bxt_fe_startup,
+};
+
+static int broxton_da7219_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,
+ DA7219_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,
+ DA7219_SYSCLK_PLL_SRM, 0, DA7219_PLL_FREQ_OUT_98304);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret);
+ return -EIO;
+ }
+
+ return ret;
+}
+
+static int broxton_da7219_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;
+ int ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ DA7219_SYSCLK_MCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret);
+ return -EIO;
+ }
+
+ return ret;
+}
+
+static struct snd_soc_ops broxton_da7219_ops = {
+ .hw_params = broxton_da7219_hw_params,
+ .hw_free = broxton_da7219_hw_free,
+};
+
+static int broxton_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 = DUAL_CHANNEL;
+
+ return 0;
+}
+
+static int broxton_dmic_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);
+
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
+}
+
+static const struct snd_soc_ops broxton_dmic_ops = {
+ .startup = broxton_dmic_startup,
+};
+
+static const unsigned int rates_16000[] = {
+ 16000,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_16000 = {
+ .count = ARRAY_SIZE(rates_16000),
+ .list = rates_16000,
+};
+
+static int broxton_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 broxton_refcap_ops = {
+ .startup = broxton_refcap_startup,
+};
+
+/* broxton digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link broxton_dais[] = {
+ /* Front End DAI links */
+ [BXT_DPCM_AUDIO_PB] =
+ {
+ .name = "Bxt Audio Port",
+ .stream_name = "Audio",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "0000:00:0e.0",
+ .dynamic = 1,
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .nonatomic = 1,
+ .init = broxton_da7219_fe_init,
+ .trigger = {
+ SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+ .dpcm_playback = 1,
+ .ops = &broxton_da7219_fe_ops,
+ },
+ [BXT_DPCM_AUDIO_CP] =
+ {
+ .name = "Bxt Audio Capture Port",
+ .stream_name = "Audio Record",
+ .cpu_dai_name = "System Pin",
+ .platform_name = "0000:00:0e.0",
+ .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 = &broxton_da7219_fe_ops,
+ },
+ [BXT_DPCM_AUDIO_REF_CP] =
+ {
+ .name = "Bxt 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:0e.0",
+ .init = NULL,
+ .dpcm_capture = 1,
+ .ignore_suspend = 1,
+ .nonatomic = 1,
+ .dynamic = 1,
+ .ops = &broxton_refcap_ops,
+ },
+ [BXT_DPCM_AUDIO_DMIC_CP]
+ {
+ .name = "Bxt 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:0e.0",
+ .init = NULL,
+ .dpcm_capture = 1,
+ .nonatomic = 1,
+ .dynamic = 1,
+ .ops = &broxton_dmic_ops,
+ },
+ [BXT_DPCM_AUDIO_HDMI1_PB] =
+ {
+ .name = "Bxt HDMI Port1",
+ .stream_name = "Hdmi1",
+ .cpu_dai_name = "HDMI1 Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:0e.0",
+ .dpcm_playback = 1,
+ .init = NULL,
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+ [BXT_DPCM_AUDIO_HDMI2_PB] =
+ {
+ .name = "Bxt HDMI Port2",
+ .stream_name = "Hdmi2",
+ .cpu_dai_name = "HDMI2 Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:0e.0",
+ .dpcm_playback = 1,
+ .init = NULL,
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+ [BXT_DPCM_AUDIO_HDMI3_PB] =
+ {
+ .name = "Bxt HDMI Port3",
+ .stream_name = "Hdmi3",
+ .cpu_dai_name = "HDMI3 Pin",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .platform_name = "0000:00:0e.0",
+ .dpcm_playback = 1,
+ .init = NULL,
+ .nonatomic = 1,
+ .dynamic = 1,
+ },
+ /* Back End DAI links */
+ {
+ /* SSP5 - Codec */
+ .name = "SSP5-Codec",
+ .id = 0,
+ .cpu_dai_name = "SSP5 Pin",
+ .platform_name = "0000:00:0e.0",
+ .no_pcm = 1,
+ .codec_name = "MX98357A:00",
+ .codec_dai_name = BXT_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 = broxton_ssp_fixup,
+ .dpcm_playback = 1,
+ },
+ {
+ /* SSP1 - Codec */
+ .name = "SSP1-Codec",
+ .id = 1,
+ .cpu_dai_name = "SSP1 Pin",
+ .platform_name = "0000:00:0e.0",
+ .no_pcm = 1,
+ .codec_name = "i2c-DLGS7219:00",
+ .codec_dai_name = BXT_DIALOG_CODEC_DAI,
+ .init = broxton_da7219_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 = broxton_ssp_fixup,
+ .ops = &broxton_da7219_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ {
+ .name = "dmic01",
+ .id = 2,
+ .cpu_dai_name = "DMIC01 Pin",
+ .codec_name = "dmic-codec",
+ .codec_dai_name = "dmic-hifi",
+ .platform_name = "0000:00:0e.0",
+ .ignore_suspend = 1,
+ .be_hw_params_fixup = broxton_dmic_fixup,
+ .dpcm_capture = 1,
+ .no_pcm = 1,
+ },
+ {
+ .name = "iDisp1",
+ .id = 3,
+ .cpu_dai_name = "iDisp1 Pin",
+ .codec_name = "ehdaudio0D2",
+ .codec_dai_name = "intel-hdmi-hifi1",
+ .platform_name = "0000:00:0e.0",
+ .init = broxton_hdmi_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ },
+ {
+ .name = "iDisp2",
+ .id = 4,
+ .cpu_dai_name = "iDisp2 Pin",
+ .codec_name = "ehdaudio0D2",
+ .codec_dai_name = "intel-hdmi-hifi2",
+ .platform_name = "0000:00:0e.0",
+ .init = broxton_hdmi_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ },
+ {
+ .name = "iDisp3",
+ .id = 5,
+ .cpu_dai_name = "iDisp3 Pin",
+ .codec_name = "ehdaudio0D2",
+ .codec_dai_name = "intel-hdmi-hifi3",
+ .platform_name = "0000:00:0e.0",
+ .init = broxton_hdmi_init,
+ .dpcm_playback = 1,
+ .no_pcm = 1,
+ },
+};
+
+/* broxton audio machine driver for SPT + da7219 */
+static struct snd_soc_card broxton_audio_card = {
+ .name = "bxtda7219max",
+ .owner = THIS_MODULE,
+ .dai_link = broxton_dais,
+ .num_links = ARRAY_SIZE(broxton_dais),
+ .controls = broxton_controls,
+ .num_controls = ARRAY_SIZE(broxton_controls),
+ .dapm_widgets = broxton_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(broxton_widgets),
+ .dapm_routes = broxton_map,
+ .num_dapm_routes = ARRAY_SIZE(broxton_map),
+ .fully_routed = true,
+};
+
+static int broxton_audio_probe(struct platform_device *pdev)
+{
+ broxton_audio_card.dev = &pdev->dev;
+ return devm_snd_soc_register_card(&pdev->dev, &broxton_audio_card);
+}
+
+static struct platform_driver broxton_audio = {
+ .probe = broxton_audio_probe,
+ .driver = {
+ .name = "bxt_da7219_max98357a_i2s",
+ .pm = &snd_soc_pm_ops,
+ },
+};
+module_platform_driver(broxton_audio)
+
+/* Module information */
+MODULE_DESCRIPTION("Audio Machine driver-DA7219 & MAX98357A in I2S mode");
+MODULE_AUTHOR("Sathyanarayana Nujella <sathyanarayana.nujella@intel.com>");
+MODULE_AUTHOR("Rohit Ainapure <rohit.m.ainapure@intel.com>");
+MODULE_AUTHOR("Harsha Priya <harshapriya.n@intel.com>");
+MODULE_AUTHOR("Conrad Cooke <conrad.cooke@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bxt_da7219_max98357a_i2s");
diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c
index f4787515c0ed..d610bdca1608 100644
--- a/sound/soc/intel/boards/bxt_rt298.c
+++ b/sound/soc/intel/boards/bxt_rt298.c
@@ -33,6 +33,7 @@ enum {
BXT_DPCM_AUDIO_PB = 0,
BXT_DPCM_AUDIO_CP,
BXT_DPCM_AUDIO_REF_CP,
+ BXT_DPCM_AUDIO_DMIC_CP,
BXT_DPCM_AUDIO_HDMI1_PB,
BXT_DPCM_AUDIO_HDMI2_PB,
BXT_DPCM_AUDIO_HDMI3_PB,
@@ -88,6 +89,7 @@ static const struct snd_soc_dapm_route broxton_rt298_map[] = {
/* CODEC BE connections */
{ "AIF1 Playback", NULL, "ssp5 Tx"},
{ "ssp5 Tx", NULL, "codec0_out"},
+ { "ssp5 Tx", NULL, "codec1_out"},
{ "codec0_in", NULL, "ssp5 Rx" },
{ "ssp5 Rx", NULL, "AIF1 Capture" },
@@ -104,6 +106,17 @@ static const struct snd_soc_dapm_route broxton_rt298_map[] = {
};
+static int broxton_rt298_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 broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)
{
struct snd_soc_codec *codec = rtd->codec;
@@ -118,6 +131,9 @@ static int broxton_rt298_codec_init(struct snd_soc_pcm_runtime *rtd)
return ret;
rt298_mic_detect(codec, &broxton_headset);
+
+ snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
+
return 0;
}
@@ -169,10 +185,93 @@ static struct snd_soc_ops broxton_rt298_ops = {
.hw_params = broxton_rt298_hw_params,
};
+static unsigned int rates[] = {
+ 48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_rates = {
+ .count = ARRAY_SIZE(rates),
+ .list = rates,
+ .mask = 0,
+};
+
+static int broxton_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 broxton_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 broxton_dmic_ops = {
+ .startup = broxton_dmic_startup,
+};
+
+static unsigned int channels[] = {
+ 2,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_channels = {
+ .count = ARRAY_SIZE(channels),
+ .list = channels,
+ .mask = 0,
+};
+
+static int bxt_fe_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ /*
+ * on this platform for PCM device we support:
+ * 48Khz
+ * stereo
+ */
+
+ runtime->hw.channels_max = 2;
+ 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 broxton_rt286_fe_ops = {
+ .startup = bxt_fe_startup,
+};
+
/* broxton digital audio interface glue - connects codec <--> CPU */
static struct snd_soc_dai_link broxton_rt298_dais[] = {
/* Front End DAI links */
- [BXT_DPCM_AUDIO_PB]
+ [BXT_DPCM_AUDIO_PB] =
{
.name = "Bxt Audio Port",
.stream_name = "Audio",
@@ -182,10 +281,12 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.dynamic = 1,
.codec_name = "snd-soc-dummy",
.codec_dai_name = "snd-soc-dummy-dai",
+ .init = broxton_rt298_fe_init,
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_playback = 1,
+ .ops = &broxton_rt286_fe_ops,
},
- [BXT_DPCM_AUDIO_CP]
+ [BXT_DPCM_AUDIO_CP] =
{
.name = "Bxt Audio Capture Port",
.stream_name = "Audio Record",
@@ -197,8 +298,9 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.codec_dai_name = "snd-soc-dummy-dai",
.trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
.dpcm_capture = 1,
+ .ops = &broxton_rt286_fe_ops,
},
- [BXT_DPCM_AUDIO_REF_CP]
+ [BXT_DPCM_AUDIO_REF_CP] =
{
.name = "Bxt Audio Reference cap",
.stream_name = "refcap",
@@ -211,7 +313,21 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.nonatomic = 1,
.dynamic = 1,
},
- [BXT_DPCM_AUDIO_HDMI1_PB]
+ [BXT_DPCM_AUDIO_DMIC_CP] =
+ {
+ .name = "Bxt 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:0e.0",
+ .init = NULL,
+ .dpcm_capture = 1,
+ .nonatomic = 1,
+ .dynamic = 1,
+ .ops = &broxton_dmic_ops,
+ },
+ [BXT_DPCM_AUDIO_HDMI1_PB] =
{
.name = "Bxt HDMI Port1",
.stream_name = "Hdmi1",
@@ -224,7 +340,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.nonatomic = 1,
.dynamic = 1,
},
- [BXT_DPCM_AUDIO_HDMI2_PB]
+ [BXT_DPCM_AUDIO_HDMI2_PB] =
{
.name = "Bxt HDMI Port2",
.stream_name = "Hdmi2",
@@ -237,7 +353,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.nonatomic = 1,
.dynamic = 1,
},
- [BXT_DPCM_AUDIO_HDMI3_PB]
+ [BXT_DPCM_AUDIO_HDMI3_PB] =
{
.name = "Bxt HDMI Port3",
.stream_name = "Hdmi3",
@@ -276,6 +392,7 @@ static struct snd_soc_dai_link broxton_rt298_dais[] = {
.codec_name = "dmic-codec",
.codec_dai_name = "dmic-hifi",
.platform_name = "0000:00:0e.0",
+ .be_hw_params_fixup = broxton_dmic_fixup,
.ignore_suspend = 1,
.dpcm_capture = 1,
.no_pcm = 1,
@@ -341,6 +458,7 @@ static struct platform_driver broxton_audio = {
.probe = broxton_audio_probe,
.driver = {
.name = "bxt_alc298s_i2s",
+ .pm = &snd_soc_pm_ops,
},
};
module_platform_driver(broxton_audio)
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index 88efb62439ba..bff77a1f27fc 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -24,6 +24,9 @@
#include <linux/device.h>
#include <linux/dmi.h>
#include <linux/slab.h>
+#include <asm/cpu_device_id.h>
+#include <asm/platform_sst_audio.h>
+#include <linux/clk.h>
#include <sound/pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -31,42 +34,153 @@
#include "../../codecs/rt5640.h"
#include "../atom/sst-atom-controls.h"
#include "../common/sst-acpi.h"
+#include "../common/sst-dsp.h"
enum {
BYT_RT5640_DMIC1_MAP,
BYT_RT5640_DMIC2_MAP,
BYT_RT5640_IN1_MAP,
+ BYT_RT5640_IN3_MAP,
};
#define BYT_RT5640_MAP(quirk) ((quirk) & 0xff)
#define BYT_RT5640_DMIC_EN BIT(16)
+#define BYT_RT5640_MONO_SPEAKER BIT(17)
+#define BYT_RT5640_DIFF_MIC BIT(18) /* defaut 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)
+
+struct byt_rt5640_private {
+ struct clk *mclk;
+};
static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
- BYT_RT5640_DMIC_EN;
+ BYT_RT5640_DMIC_EN |
+ BYT_RT5640_MCLK_EN;
+
+static void log_quirks(struct device *dev)
+{
+ if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_DMIC1_MAP)
+ dev_info(dev, "quirk DMIC1_MAP enabled");
+ if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_DMIC2_MAP)
+ dev_info(dev, "quirk DMIC2_MAP enabled");
+ if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_IN1_MAP)
+ dev_info(dev, "quirk IN1_MAP enabled");
+ if (BYT_RT5640_MAP(byt_rt5640_quirk) == BYT_RT5640_IN3_MAP)
+ dev_info(dev, "quirk IN3_MAP enabled");
+ if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN)
+ dev_info(dev, "quirk DMIC enabled");
+ if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER)
+ dev_info(dev, "quirk MONO_SPEAKER enabled");
+ if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC)
+ dev_info(dev, "quirk DIFF_MIC enabled");
+ if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2)
+ dev_info(dev, "quirk SSP2_AIF2 enabled");
+ if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1)
+ dev_info(dev, "quirk SSP0_AIF1 enabled");
+ if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)
+ dev_info(dev, "quirk SSP0_AIF2 enabled");
+ if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN)
+ dev_info(dev, "quirk MCLK_EN enabled");
+ if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ)
+ dev_info(dev, "quirk MCLK_25MHZ enabled");
+}
+
+
+#define BYT_CODEC_DAI1 "rt5640-aif1"
+#define BYT_CODEC_DAI2 "rt5640-aif2"
+
+static inline struct snd_soc_dai *byt_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, BYT_CODEC_DAI1,
+ strlen(BYT_CODEC_DAI1)))
+ return rtd->codec_dai;
+ if (!strncmp(rtd->codec_dai->name, BYT_CODEC_DAI2,
+ strlen(BYT_CODEC_DAI2)))
+ 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;
+ struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ codec_dai = byt_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)) {
+ if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk) {
+ ret = clk_prepare_enable(priv->mclk);
+ if (ret < 0) {
+ dev_err(card->dev,
+ "could not configure MCLK state");
+ return ret;
+ }
+ }
+ ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
+ 48000 * 512,
+ SND_SOC_CLOCK_IN);
+ } 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,
+ 0,
+ SND_SOC_CLOCK_IN);
+ if (!ret) {
+ if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->mclk)
+ 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 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),
+ 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"},
+ {"Internal Mic", NULL, "Platform Clock"},
+ {"Speaker", 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[] = {
@@ -82,6 +196,59 @@ static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
{"IN1P", NULL, "Internal Mic"},
};
+static const struct snd_soc_dapm_route byt_rt5640_intmic_in3_map[] = {
+ {"Internal Mic", NULL, "MICBIAS1"},
+ {"IN3P", NULL, "Internal Mic"},
+};
+
+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"},
+};
+
+static const struct snd_soc_dapm_route byt_rt5640_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_rt5640_stereo_spk_map[] = {
+ {"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, "SPOLP"},
+ {"Speaker", NULL, "SPOLN"},
+};
+
static const struct snd_kcontrol_new byt_rt5640_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -96,19 +263,46 @@ static int byt_rt5640_aif1_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *codec_dai = rtd->codec_dai;
int ret;
- snd_soc_dai_set_bclk_ratio(codec_dai, 50);
-
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;
}
- ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
- params_rate(params) * 50,
- params_rate(params) * 512);
+ 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,
+ params_rate(params) * 32,
+ params_rate(params) * 512);
+ } else {
+ /* 2x15 bit slots on SSP2 */
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ RT5640_PLL1_S_BCLK1,
+ params_rate(params) * 50,
+ params_rate(params) * 512);
+ }
+ } else {
+ if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ RT5640_PLL1_S_MCLK,
+ 25000000,
+ params_rate(params) * 512);
+ } else {
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ RT5640_PLL1_S_MCLK,
+ 19200000,
+ params_rate(params) * 512);
+ }
+ }
+
if (ret < 0) {
dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
return ret;
@@ -127,27 +321,73 @@ 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"),
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TA"),
+ },
+ .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ .callback = byt_rt5640_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "ASUSTeK COMPUTER INC."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "T100TAF"),
},
- .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
+ .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_MONO_SPEAKER |
+ BYT_RT5640_DIFF_MIC |
+ BYT_RT5640_SSP0_AIF2 |
+ BYT_RT5640_MCLK_EN
+ ),
},
{
.callback = byt_rt5640_quirk_cb,
.matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "DellInc."),
- DMI_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "DellInc."),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "Venue 8 Pro 5830"),
},
.driver_data = (unsigned long *)(BYT_RT5640_DMIC2_MAP |
+ BYT_RT5640_DMIC_EN |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ .callback = byt_rt5640_quirk_cb,
+ .matches = {
+ DMI_EXACT_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
+ DMI_EXACT_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"),
+ },
+ .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_MCLK_EN),
+ },
+ {
+ .callback = byt_rt5640_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Circuitco"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Minnowboard Max B3 PLATFORM"),
+ },
+ .driver_data = (unsigned long *)(BYT_RT5640_DMIC1_MAP |
BYT_RT5640_DMIC_EN),
},
{
.callback = byt_rt5640_quirk_cb,
.matches = {
- DMI_MATCH(DMI_SYS_VENDOR, "Hewlett-Packard"),
- DMI_MATCH(DMI_PRODUCT_NAME, "HP ElitePad 1000 G2"),
+ DMI_MATCH(DMI_BOARD_VENDOR, "TECLAST"),
+ DMI_MATCH(DMI_BOARD_NAME, "tPAD"),
},
- .driver_data = (unsigned long *)BYT_RT5640_IN1_MAP,
+ .driver_data = (unsigned long *)(BYT_RT5640_IN3_MAP |
+ BYT_RT5640_MCLK_EN |
+ BYT_RT5640_SSP0_AIF1),
+ },
+ {
+ .callback = byt_rt5640_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "Acer"),
+ DMI_MATCH(DMI_PRODUCT_NAME, "Aspire SW5-012"),
+ },
+ .driver_data = (unsigned long *)(BYT_RT5640_IN1_MAP |
+ BYT_RT5640_MCLK_EN |
+ BYT_RT5640_SSP0_AIF1),
+
},
{}
};
@@ -158,13 +398,18 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
struct snd_soc_codec *codec = runtime->codec;
struct snd_soc_card *card = runtime->card;
const struct snd_soc_dapm_route *custom_map;
+ struct byt_rt5640_private *priv = snd_soc_card_get_drvdata(card);
int num_routes;
card->dapm.idle_bias_off = true;
rt5640_sel_asrc_clk_src(codec,
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,
@@ -179,6 +424,10 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
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_DMIC2_MAP:
custom_map = byt_rt5640_intmic_dmic2_map;
num_routes = ARRAY_SIZE(byt_rt5640_intmic_dmic2_map);
@@ -192,6 +441,43 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
if (ret)
return ret;
+ if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) {
+ ret = snd_soc_dapm_add_routes(&card->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(&card->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(&card->dapm,
+ byt_rt5640_ssp0_aif2_map,
+ ARRAY_SIZE(byt_rt5640_ssp0_aif2_map));
+ } else {
+ ret = snd_soc_dapm_add_routes(&card->dapm,
+ byt_rt5640_ssp2_aif1_map,
+ ARRAY_SIZE(byt_rt5640_ssp2_aif1_map));
+ }
+ if (ret)
+ return ret;
+
+ if (byt_rt5640_quirk & BYT_RT5640_MONO_SPEAKER) {
+ ret = snd_soc_dapm_add_routes(&card->dapm,
+ byt_rt5640_mono_spk_map,
+ ARRAY_SIZE(byt_rt5640_mono_spk_map));
+ } else {
+ ret = snd_soc_dapm_add_routes(&card->dapm,
+ byt_rt5640_stereo_spk_map,
+ ARRAY_SIZE(byt_rt5640_stereo_spk_map));
+ }
+ if (ret)
+ return ret;
+
+ if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) {
+ snd_soc_update_bits(codec, RT5640_IN1_IN2, RT5640_IN_DF1,
+ RT5640_IN_DF1);
+ }
+
if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
ret = rt5640_dmic_enable(codec, 0, 0);
if (ret)
@@ -201,6 +487,30 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
snd_soc_dapm_ignore_suspend(&card->dapm, "Headphone");
snd_soc_dapm_ignore_suspend(&card->dapm, "Speaker");
+ if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && priv->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(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;
}
@@ -221,34 +531,63 @@ static int byt_rt5640_codec_fixup(struct snd_soc_pcm_runtime *rtd,
SNDRV_PCM_HW_PARAM_CHANNELS);
int ret;
- /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+ /* The DSP will covert 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);
+
+ /*
+ * 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(rtd->cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_IF |
+ SND_SOC_DAIFMT_CBS_CFS
+ );
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set format to I2S, err %d\n", ret);
+ return ret;
+ }
- /*
- * 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(rtd->cpu_dai,
- SND_SOC_DAIFMT_I2S |
- SND_SOC_DAIFMT_NB_IF |
- SND_SOC_DAIFMT_CBS_CFS
- );
- 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, 16);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+ return ret;
+ }
- ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2, 24);
- 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, 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(rtd->cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_IF |
+ SND_SOC_DAIFMT_CBS_CFS
+ );
+ 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);
+ if (ret < 0) {
+ dev_err(rtd->dev, "can't set I2S config, err %d\n", ret);
+ return ret;
+ }
+ }
return 0;
}
@@ -305,10 +644,10 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
{
.name = "SSP2-Codec",
.id = 1,
- .cpu_dai_name = "ssp2-port",
+ .cpu_dai_name = "ssp2-port", /* overwritten for ssp0 routing */
.platform_name = "sst-mfld-platform",
.no_pcm = 1,
- .codec_dai_name = "rt5640-aif1",
+ .codec_dai_name = "rt5640-aif1", /* changed w/ quirk */
.codec_name = "i2c-10EC5640:00", /* overwritten with HID */
.dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
| SND_SOC_DAIFMT_CBS_CFS,
@@ -335,6 +674,21 @@ static struct snd_soc_card byt_rt5640_card = {
};
static char byt_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
+static char byt_rt5640_codec_aif_name[12]; /* = "rt5640-aif[1|2]" */
+static char byt_rt5640_cpu_dai_name[10]; /* = "ssp[0|2]-port" */
+
+static bool is_valleyview(void)
+{
+ static const struct x86_cpu_id cpu_ids[] = {
+ { X86_VENDOR_INTEL, 6, 55 }, /* Valleyview, Bay Trail */
+ {}
+ };
+
+ if (!x86_match_cpu(cpu_ids))
+ return false;
+ return true;
+}
+
static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
{
@@ -343,10 +697,16 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
const char *i2c_name = NULL;
int i;
int dai_index;
+ struct byt_rt5640_private *priv;
+
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
+ if (!priv)
+ return -ENOMEM;
/* register the soc card */
byt_rt5640_card.dev = &pdev->dev;
mach = byt_rt5640_card.dev->platform_data;
+ snd_soc_card_set_drvdata(&byt_rt5640_card, priv);
/* fix index of codec dai */
dai_index = MERR_DPCM_COMPR + 1;
@@ -366,8 +726,57 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
byt_rt5640_dais[dai_index].codec_name = byt_rt5640_codec_name;
}
+ /*
+ * swap SSP0 if bytcr is detected
+ * (will be overridden if DMI quirk is detected)
+ */
+ if (is_valleyview()) {
+ struct sst_platform_info *p_info = mach->pdata;
+ const struct sst_res_info *res_info = p_info->res_info;
+
+ /* TODO: use CHAN package info from BIOS to detect AIF1/AIF2 */
+ if (res_info->acpi_ipc_irq_index == 0) {
+ byt_rt5640_quirk |= BYT_RT5640_SSP0_AIF2;
+ }
+ }
+
/* check quirks before creating card */
dmi_check_system(byt_rt5640_quirk_table);
+ log_quirks(&pdev->dev);
+
+ if ((byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) ||
+ (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
+
+ /* fixup codec aif name */
+ snprintf(byt_rt5640_codec_aif_name,
+ sizeof(byt_rt5640_codec_aif_name),
+ "%s", "rt5640-aif2");
+
+ byt_rt5640_dais[dai_index].codec_dai_name =
+ byt_rt5640_codec_aif_name;
+ }
+
+ if ((byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) ||
+ (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2)) {
+
+ /* fixup cpu dai name name */
+ snprintf(byt_rt5640_cpu_dai_name,
+ sizeof(byt_rt5640_cpu_dai_name),
+ "%s", "ssp0-port");
+
+ byt_rt5640_dais[dai_index].cpu_dai_name =
+ byt_rt5640_cpu_dai_name;
+ }
+
+ if ((byt_rt5640_quirk & BYT_RT5640_MCLK_EN) && (is_valleyview())) {
+ priv->mclk = devm_clk_get(&pdev->dev, "pmc_plt_clk_3");
+ if (IS_ERR(priv->mclk)) {
+ dev_err(&pdev->dev,
+ "Failed to get MCLK from pmc_plt_clk_3: %ld\n",
+ PTR_ERR(priv->mclk));
+ return PTR_ERR(priv->mclk);
+ }
+ }
ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card);
diff --git a/sound/soc/intel/boards/cht_bsw_rt5645.c b/sound/soc/intel/boards/cht_bsw_rt5645.c
index d7ef292c402d..56056ed7fcfd 100644
--- a/sound/soc/intel/boards/cht_bsw_rt5645.c
+++ b/sound/soc/intel/boards/cht_bsw_rt5645.c
@@ -30,6 +30,7 @@
#include <sound/jack.h>
#include "../../codecs/rt5645.h"
#include "../atom/sst-atom-controls.h"
+#include "../common/sst-acpi.h"
#define CHT_PLAT_CLK_3_HZ 19200000
#define CHT_CODEC_DAI "rt5645-aif1"
@@ -340,10 +341,13 @@ 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},
{"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
};
+static char cht_rt5640_codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
+
static int snd_cht_mc_probe(struct platform_device *pdev)
{
int ret_val = 0;
@@ -351,6 +355,9 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
struct cht_mc_private *drv;
struct snd_soc_card *card = snd_soc_cards[0].soc_card;
char codec_name[16];
+ struct sst_acpi_mach *mach;
+ const char *i2c_name = NULL;
+ int dai_index = 0;
drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
if (!drv)
@@ -366,12 +373,23 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
}
}
card->dev = &pdev->dev;
+ mach = card->dev->platform_data;
sprintf(codec_name, "i2c-%s:00", drv->acpi_card->codec_id);
/* set correct codec name */
for (i = 0; i < ARRAY_SIZE(cht_dailink); i++)
- if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00"))
+ if (!strcmp(card->dai_link[i].codec_name, "i2c-10EC5645:00")) {
card->dai_link[i].codec_name = kstrdup(codec_name, GFP_KERNEL);
+ dai_index = i;
+ }
+
+ /* fixup codec name based on HID */
+ i2c_name = sst_acpi_find_name_from_hid(mach->id);
+ if (i2c_name != NULL) {
+ snprintf(cht_rt5640_codec_name, sizeof(cht_rt5640_codec_name),
+ "%s%s", "i2c-", i2c_name);
+ cht_dailink[dai_index].codec_name = cht_rt5640_codec_name;
+ }
snd_soc_card_set_drvdata(card, drv);
ret_val = devm_snd_soc_register_card(&pdev->dev, card);
diff --git a/sound/soc/intel/boards/skl_nau88l25_max98357a.c b/sound/soc/intel/boards/skl_nau88l25_max98357a.c
index d2808652b974..25db5be7fdfa 100644
--- a/sound/soc/intel/boards/skl_nau88l25_max98357a.c
+++ b/sound/soc/intel/boards/skl_nau88l25_max98357a.c
@@ -23,12 +23,15 @@
#include <sound/soc.h>
#include "../../codecs/nau8825.h"
#include "../../codecs/hdac_hdmi.h"
+#include "../skylake/skl.h"
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
#define SKL_MAXIM_CODEC_DAI "HiFi"
+#define DMIC_CH(p) p->list[p->count-1]
static struct snd_soc_jack skylake_headset;
static struct snd_soc_card skylake_audio_card;
+static const struct snd_pcm_hw_constraint_list *dmic_constraints;
struct skl_hdmi_pcm {
struct list_head head;
@@ -339,7 +342,7 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
- if (params_channels(params) == 2)
+ if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2)
channels->min = channels->max = 2;
else
channels->min = channels->max = 4;
@@ -357,13 +360,23 @@ static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
.mask = 0,
};
+static const unsigned int dmic_2ch[] = {
+ 2,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_dmic_2ch = {
+ .count = ARRAY_SIZE(dmic_2ch),
+ .list = dmic_2ch,
+ .mask = 0,
+};
+
static int skylake_dmic_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- runtime->hw.channels_max = 4;
+ runtime->hw.channels_max = DMIC_CH(dmic_constraints);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_dmic_channels);
+ dmic_constraints);
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
@@ -382,8 +395,22 @@ static struct snd_pcm_hw_constraint_list constraints_16000 = {
.list = rates_16000,
};
+static const unsigned int ch_mono[] = {
+ 1,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_refcap = {
+ .count = ARRAY_SIZE(ch_mono),
+ .list = ch_mono,
+};
+
static int skylake_refcap_startup(struct snd_pcm_substream *substream)
{
+ substream->runtime->hw.channels_max = 1;
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_refcap);
+
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&constraints_16000);
@@ -610,6 +637,7 @@ static struct snd_soc_card skylake_audio_card = {
static int skylake_audio_probe(struct platform_device *pdev)
{
struct skl_nau8825_private *ctx;
+ struct skl_machine_pdata *pdata;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
if (!ctx)
@@ -620,15 +648,27 @@ static int skylake_audio_probe(struct platform_device *pdev)
skylake_audio_card.dev = &pdev->dev;
snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
+ pdata = dev_get_drvdata(&pdev->dev);
+ if (pdata)
+ dmic_constraints = pdata->dmic_num == 2 ?
+ &constraints_dmic_2ch : &constraints_dmic_channels;
+
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
}
+static const struct platform_device_id skl_board_ids[] = {
+ { .name = "skl_n88l25_m98357a" },
+ { .name = "kbl_n88l25_m98357a" },
+ { }
+};
+
static struct platform_driver skylake_audio = {
.probe = skylake_audio_probe,
.driver = {
- .name = "skl_nau88l25_max98357a_i2s",
+ .name = "skl_n88l25_m98357a",
.pm = &snd_soc_pm_ops,
},
+ .id_table = skl_board_ids,
};
module_platform_driver(skylake_audio)
@@ -637,4 +677,5 @@ module_platform_driver(skylake_audio)
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");
+MODULE_ALIAS("platform:skl_n88l25_m98357a");
+MODULE_ALIAS("platform:kbl_n88l25_m98357a");
diff --git a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
index e19aa99c4f72..69c5d5da4e86 100644
--- a/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
+++ b/sound/soc/intel/boards/skl_nau88l25_ssm4567.c
@@ -27,12 +27,15 @@
#include <sound/pcm_params.h>
#include "../../codecs/nau8825.h"
#include "../../codecs/hdac_hdmi.h"
+#include "../skylake/skl.h"
#define SKL_NUVOTON_CODEC_DAI "nau8825-hifi"
#define SKL_SSM_CODEC_DAI "ssm4567-hifi"
+#define DMIC_CH(p) p->list[p->count-1]
static struct snd_soc_jack skylake_headset;
static struct snd_soc_card skylake_audio_card;
+static const struct snd_pcm_hw_constraint_list *dmic_constraints;
struct skl_hdmi_pcm {
struct list_head head;
@@ -367,7 +370,7 @@ static int skylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd,
{
struct snd_interval *channels = hw_param_interval(params,
SNDRV_PCM_HW_PARAM_CHANNELS);
- if (params_channels(params) == 2)
+ if (params_channels(params) == 2 || DMIC_CH(dmic_constraints) == 2)
channels->min = channels->max = 2;
else
channels->min = channels->max = 4;
@@ -405,13 +408,23 @@ static struct snd_pcm_hw_constraint_list constraints_dmic_channels = {
.mask = 0,
};
+static const unsigned int dmic_2ch[] = {
+ 2,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_dmic_2ch = {
+ .count = ARRAY_SIZE(dmic_2ch),
+ .list = dmic_2ch,
+ .mask = 0,
+};
+
static int skylake_dmic_startup(struct snd_pcm_substream *substream)
{
struct snd_pcm_runtime *runtime = substream->runtime;
- runtime->hw.channels_max = 4;
+ runtime->hw.channels_max = DMIC_CH(dmic_constraints);
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
- &constraints_dmic_channels);
+ dmic_constraints);
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
@@ -430,8 +443,22 @@ static struct snd_pcm_hw_constraint_list constraints_16000 = {
.list = rates_16000,
};
+static const unsigned int ch_mono[] = {
+ 1,
+};
+
+static const struct snd_pcm_hw_constraint_list constraints_refcap = {
+ .count = ARRAY_SIZE(ch_mono),
+ .list = ch_mono,
+};
+
static int skylake_refcap_startup(struct snd_pcm_substream *substream)
{
+ substream->runtime->hw.channels_max = 1;
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_CHANNELS,
+ &constraints_refcap);
+
return snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
&constraints_16000);
@@ -662,6 +689,7 @@ static struct snd_soc_card skylake_audio_card = {
static int skylake_audio_probe(struct platform_device *pdev)
{
struct skl_nau88125_private *ctx;
+ struct skl_machine_pdata *pdata;
ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC);
if (!ctx)
@@ -672,15 +700,27 @@ static int skylake_audio_probe(struct platform_device *pdev)
skylake_audio_card.dev = &pdev->dev;
snd_soc_card_set_drvdata(&skylake_audio_card, ctx);
+ pdata = dev_get_drvdata(&pdev->dev);
+ if (pdata)
+ dmic_constraints = pdata->dmic_num == 2 ?
+ &constraints_dmic_2ch : &constraints_dmic_channels;
+
return devm_snd_soc_register_card(&pdev->dev, &skylake_audio_card);
}
+static const struct platform_device_id skl_board_ids[] = {
+ { .name = "skl_n88l25_s4567" },
+ { .name = "kbl_n88l25_s4567" },
+ { }
+};
+
static struct platform_driver skylake_audio = {
.probe = skylake_audio_probe,
.driver = {
- .name = "skl_nau88l25_ssm4567_i2s",
+ .name = "skl_n88l25_s4567",
.pm = &snd_soc_pm_ops,
},
+ .id_table = skl_board_ids,
};
module_platform_driver(skylake_audio)
@@ -693,4 +733,5 @@ 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");
+MODULE_ALIAS("platform:skl_n88l25_s4567");
+MODULE_ALIAS("platform:kbl_n88l25_s4567");
diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c
index 426b48233fdb..88c61e8cb87f 100644
--- a/sound/soc/intel/boards/skl_rt286.c
+++ b/sound/soc/intel/boards/skl_rt286.c
@@ -505,12 +505,20 @@ static int skylake_audio_probe(struct platform_device *pdev)
return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
}
+static const struct platform_device_id skl_board_ids[] = {
+ { .name = "skl_alc286s_i2s" },
+ { .name = "kbl_alc286s_i2s" },
+ { }
+};
+
static struct platform_driver skylake_audio = {
.probe = skylake_audio_probe,
.driver = {
.name = "skl_alc286s_i2s",
.pm = &snd_soc_pm_ops,
},
+ .id_table = skl_board_ids,
+
};
module_platform_driver(skylake_audio)
@@ -520,3 +528,4 @@ 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");
+MODULE_ALIAS("platform:kbl_alc286s_i2s");
diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile
index fbbb25c2ceed..1a35149bcad7 100644
--- a/sound/soc/intel/common/Makefile
+++ b/sound/soc/intel/common/Makefile
@@ -2,9 +2,9 @@ snd-soc-sst-dsp-objs := sst-dsp.o
snd-soc-sst-acpi-objs := sst-acpi.o
snd-soc-sst-match-objs := sst-match-acpi.o
snd-soc-sst-ipc-objs := sst-ipc.o
-
-snd-soc-sst-dsp-$(CONFIG_DW_DMAC_CORE) += sst-firmware.o
+snd-soc-sst-firmware-objs := sst-firmware.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_INTEL_SST_MATCH) += snd-soc-sst-match.o
+obj-$(CONFIG_SND_SOC_INTEL_SST_FIRMWARE) += snd-soc-sst-firmware.o
diff --git a/sound/soc/intel/common/sst-acpi.c b/sound/soc/intel/common/sst-acpi.c
index 2c5eda14d510..1285cc597b6b 100644
--- a/sound/soc/intel/common/sst-acpi.c
+++ b/sound/soc/intel/common/sst-acpi.c
@@ -199,6 +199,7 @@ static struct sst_acpi_desc sst_acpi_haswell_desc = {
static struct sst_acpi_mach broadwell_machines[] = {
{ "INT343A", "broadwell-audio", "intel/IntcSST2.bin", NULL, NULL, NULL },
+ { "RT5677CE", "bdw-rt5677", "intel/IntcSST2.bin", NULL, NULL, NULL },
{}
};
diff --git a/sound/soc/intel/common/sst-acpi.h b/sound/soc/intel/common/sst-acpi.h
index 8398cb227ba9..5d2949324d0e 100644
--- a/sound/soc/intel/common/sst-acpi.h
+++ b/sound/soc/intel/common/sst-acpi.h
@@ -20,7 +20,7 @@
#if IS_ENABLED(CONFIG_ACPI)
const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]);
#else
-inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
+static inline const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
{
return NULL;
}
@@ -40,6 +40,6 @@ struct sst_acpi_mach {
/* board name */
const char *board;
- void (*machine_quirk)(void);
+ struct sst_acpi_mach * (*machine_quirk)(void *arg);
void *pdata;
};
diff --git a/sound/soc/intel/common/sst-dsp-priv.h b/sound/soc/intel/common/sst-dsp-priv.h
index 97dc1ae05e69..d13c84364c3c 100644
--- a/sound/soc/intel/common/sst-dsp-priv.h
+++ b/sound/soc/intel/common/sst-dsp-priv.h
@@ -383,10 +383,6 @@ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
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
index b5bbdf4fe93a..c00ede4ea4d7 100644
--- a/sound/soc/intel/common/sst-dsp.c
+++ b/sound/soc/intel/common/sst-dsp.c
@@ -285,7 +285,7 @@ int sst_dsp_register_poll(struct sst_dsp *ctx, u32 offset, u32 mask,
}
reg = sst_dsp_shim_read_unlocked(ctx, offset);
- dev_info(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation,
+ dev_dbg(ctx->dev, "FW Poll Status: reg=%#x %s %s\n", reg, operation,
(time < timeout) ? "successful" : "timedout");
ret = time < timeout ? 0 : -ETIME;
@@ -420,73 +420,6 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
}
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");
diff --git a/sound/soc/intel/common/sst-dsp.h b/sound/soc/intel/common/sst-dsp.h
index 0b84c719ec48..859f0de00339 100644
--- a/sound/soc/intel/common/sst-dsp.h
+++ b/sound/soc/intel/common/sst-dsp.h
@@ -216,7 +216,7 @@ struct sst_pdata {
void *dsp;
};
-#ifdef CONFIG_DW_DMAC_CORE
+#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
/* Initialization */
struct sst_dsp *sst_dsp_new(struct device *dev,
struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c
index 25993527370b..a086c35f91bb 100644
--- a/sound/soc/intel/common/sst-firmware.c
+++ b/sound/soc/intel/common/sst-firmware.c
@@ -1211,3 +1211,71 @@ u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
}
}
EXPORT_SYMBOL_GPL(sst_dsp_get_offset);
+
+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);
+
+MODULE_DESCRIPTION("Intel SST Firmware Loader");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/intel/haswell/sst-haswell-pcm.c b/sound/soc/intel/haswell/sst-haswell-pcm.c
index 994256b39b9c..9e4094e2c6e3 100644
--- a/sound/soc/intel/haswell/sst-haswell-pcm.c
+++ b/sound/soc/intel/haswell/sst-haswell-pcm.c
@@ -819,7 +819,6 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
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);
@@ -872,7 +871,7 @@ out:
return ret;
}
-static struct snd_pcm_ops hsw_pcm_ops = {
+static const struct snd_pcm_ops hsw_pcm_ops = {
.open = hsw_pcm_open,
.close = hsw_pcm_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/soc/intel/skylake/Makefile b/sound/soc/intel/skylake/Makefile
index c28f5d0e1d99..60fbc9bbe473 100644
--- a/sound/soc/intel/skylake/Makefile
+++ b/sound/soc/intel/skylake/Makefile
@@ -5,6 +5,6 @@ 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 bxt-sst.o
+ skl-sst.o bxt-sst.o skl-sst-utils.o
obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl-ipc.o
diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c
index 965ce40ce752..1d251d59bcb9 100644
--- a/sound/soc/intel/skylake/bxt-sst.c
+++ b/sound/soc/intel/skylake/bxt-sst.c
@@ -23,6 +23,7 @@
#include "../common/sst-dsp.h"
#include "../common/sst-dsp-priv.h"
#include "skl-sst-ipc.h"
+#include "skl-tplg-interface.h"
#define BXT_BASEFW_TIMEOUT 3000
#define BXT_INIT_TIMEOUT 500
@@ -37,11 +38,81 @@
#define BXT_ADSP_SRAM1_BASE 0xA0000
+#define BXT_INSTANCE_ID 0
+#define BXT_BASE_FW_MODULE_ID 0
+
+#define BXT_ADSP_FW_BIN_HDR_OFFSET 0x2000
+
static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)
{
return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE);
}
+static int
+bxt_load_library(struct sst_dsp *ctx, struct skl_dfw_manifest *minfo)
+{
+ struct snd_dma_buffer dmab;
+ struct skl_sst *skl = ctx->thread_context;
+ const struct firmware *fw = NULL;
+ struct firmware stripped_fw;
+ int ret = 0, i, dma_id, stream_tag;
+
+ /* library indices start from 1 to N. 0 represents base FW */
+ for (i = 1; i < minfo->lib_count; i++) {
+ ret = request_firmware(&fw, minfo->lib[i].name, ctx->dev);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Request lib %s failed:%d\n",
+ minfo->lib[i].name, ret);
+ return ret;
+ }
+
+ if (skl->is_first_boot) {
+ ret = snd_skl_parse_uuids(ctx, fw,
+ BXT_ADSP_FW_BIN_HDR_OFFSET, i);
+ if (ret < 0)
+ goto load_library_failed;
+ }
+
+ stripped_fw.data = fw->data;
+ stripped_fw.size = fw->size;
+ skl_dsp_strip_extended_manifest(&stripped_fw);
+
+ stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40,
+ stripped_fw.size, &dmab);
+ if (stream_tag <= 0) {
+ dev_err(ctx->dev, "Lib prepare DMA err: %x\n",
+ stream_tag);
+ ret = stream_tag;
+ goto load_library_failed;
+ }
+
+ dma_id = stream_tag - 1;
+ memcpy(dmab.area, stripped_fw.data, stripped_fw.size);
+
+ ctx->dsp_ops.trigger(ctx->dev, true, stream_tag);
+ ret = skl_sst_ipc_load_library(&skl->ipc, dma_id, i);
+ if (ret < 0)
+ dev_err(ctx->dev, "IPC Load Lib for %s fail: %d\n",
+ minfo->lib[i].name, ret);
+
+ ctx->dsp_ops.trigger(ctx->dev, false, stream_tag);
+ ctx->dsp_ops.cleanup(ctx->dev, &dmab, stream_tag);
+ release_firmware(fw);
+ fw = NULL;
+ }
+
+ return ret;
+
+load_library_failed:
+ release_firmware(fw);
+ return ret;
+}
+
+/*
+ * First boot sequence has some extra steps. Core 0 waits for power
+ * status on core 1, so power up core 1 also momentarily, keep it in
+ * reset/stall and then turn it off
+ */
static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
const void *fwdata, u32 fwsize)
{
@@ -49,7 +120,7 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
u32 reg;
stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40, fwsize, &ctx->dmab);
- if (stream_tag < 0) {
+ if (stream_tag <= 0) {
dev_err(ctx->dev, "Failed to prepare DMA FW loading err: %x\n",
stream_tag);
return stream_tag;
@@ -58,17 +129,27 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
ctx->dsp_ops.stream_tag = stream_tag;
memcpy(ctx->dmab.area, fwdata, fwsize);
- /* Purge FW request */
+ /* Step 1: Power up core 0 and core1 */
+ ret = skl_dsp_core_power_up(ctx, SKL_DSP_CORE0_MASK |
+ SKL_DSP_CORE_MASK(1));
+ if (ret < 0) {
+ dev_err(ctx->dev, "dsp core0/1 power up failed\n");
+ goto base_fw_load_failed;
+ }
+
+ /* Step 2: Purge FW request */
sst_dsp_shim_write(ctx, SKL_ADSP_REG_HIPCI, SKL_ADSP_REG_HIPCI_BUSY |
- BXT_IPC_PURGE_FW | (stream_tag - 1));
+ (BXT_IPC_PURGE_FW | ((stream_tag - 1) << 9)));
- ret = skl_dsp_enable_core(ctx);
+ /* Step 3: Unset core0 reset state & unstall/run core0 */
+ ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK);
if (ret < 0) {
- dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", ret);
+ dev_err(ctx->dev, "Start dsp core failed ret: %d\n", ret);
ret = -EIO;
goto base_fw_load_failed;
}
+ /* Step 4: Wait for DONE Bit */
for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
reg = sst_dsp_shim_read(ctx, SKL_ADSP_REG_HIPCIE);
@@ -88,10 +169,18 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
SKL_ADSP_REG_HIPCIE_DONE);
}
- /* enable Interrupt */
+ /* Step 5: power down core1 */
+ ret = skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1));
+ if (ret < 0) {
+ dev_err(ctx->dev, "dsp core1 power down failed\n");
+ goto base_fw_load_failed;
+ }
+
+ /* Step 6: Enable Interrupt */
skl_ipc_int_enable(ctx);
skl_ipc_op_int_enable(ctx);
+ /* Step 7: Wait for ROM init */
for (i = BXT_INIT_TIMEOUT; i > 0; --i) {
if (SKL_FW_INIT ==
(sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS) &
@@ -112,7 +201,8 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
base_fw_load_failed:
ctx->dsp_ops.cleanup(ctx->dev, &ctx->dmab, stream_tag);
- skl_dsp_disable_core(ctx);
+ skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1));
+ skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
return ret;
}
@@ -132,21 +222,40 @@ static int sst_transfer_fw_host_dma(struct sst_dsp *ctx)
static int bxt_load_base_firmware(struct sst_dsp *ctx)
{
- const struct firmware *fw = NULL;
+ struct firmware stripped_fw;
struct skl_sst *skl = ctx->thread_context;
int ret;
- ret = request_firmware(&fw, ctx->fw_name, ctx->dev);
+ ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
if (ret < 0) {
dev_err(ctx->dev, "Request firmware failed %d\n", ret);
goto sst_load_base_firmware_failed;
}
- ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
+ /* check for extended manifest */
+ if (ctx->fw == NULL)
+ goto sst_load_base_firmware_failed;
+
+ /* prase uuids on first boot */
+ if (skl->is_first_boot) {
+ ret = snd_skl_parse_uuids(ctx, ctx->fw, BXT_ADSP_FW_BIN_HDR_OFFSET, 0);
+ if (ret < 0)
+ goto sst_load_base_firmware_failed;
+ }
+
+ stripped_fw.data = ctx->fw->data;
+ stripped_fw.size = ctx->fw->size;
+ skl_dsp_strip_extended_manifest(&stripped_fw);
+
+ ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size);
/* Retry Enabling core and ROM load. Retry seemed to help */
if (ret < 0) {
- ret = sst_bxt_prepare_fw(ctx, fw->data, fw->size);
+ ret = sst_bxt_prepare_fw(ctx, stripped_fw.data, stripped_fw.size);
if (ret < 0) {
+ dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
+ sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
+ sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
+
dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret);
goto sst_load_base_firmware_failed;
}
@@ -159,83 +268,146 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx)
sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
- skl_dsp_disable_core(ctx);
+ skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
} else {
dev_dbg(ctx->dev, "Firmware download successful\n");
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 fail, FW Ready timeout\n");
- skl_dsp_disable_core(ctx);
+ skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
ret = -EIO;
} else {
- skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
ret = 0;
+ skl->fw_loaded = true;
}
}
sst_load_base_firmware_failed:
- release_firmware(fw);
+ release_firmware(ctx->fw);
return ret;
}
-static int bxt_set_dsp_D0(struct sst_dsp *ctx)
+static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
{
struct skl_sst *skl = ctx->thread_context;
int ret;
+ struct skl_ipc_dxstate_info dx;
+ unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
+ struct skl_dfw_manifest *minfo = &skl->manifest;
- skl->boot_complete = false;
+ if (skl->fw_loaded == false) {
+ skl->boot_complete = false;
+ ret = bxt_load_base_firmware(ctx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "reload fw failed: %d\n", ret);
+ return ret;
+ }
- ret = skl_dsp_enable_core(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "enable dsp core failed ret: %d\n", ret);
+ if (minfo->lib_count > 1) {
+ ret = bxt_load_library(ctx, minfo);
+ if (ret < 0) {
+ dev_err(ctx->dev, "reload libs failed: %d\n", ret);
+ return ret;
+ }
+ }
return ret;
}
- /* enable interrupt */
- skl_ipc_int_enable(ctx);
- skl_ipc_op_int_enable(ctx);
+ /* If core 0 is being turned on, turn on core 1 as well */
+ if (core_id == SKL_DSP_CORE0_ID)
+ ret = skl_dsp_core_power_up(ctx, core_mask |
+ SKL_DSP_CORE_MASK(1));
+ else
+ ret = skl_dsp_core_power_up(ctx, core_mask);
+
+ if (ret < 0)
+ goto err;
+
+ if (core_id == SKL_DSP_CORE0_ID) {
+
+ /*
+ * Enable interrupt after SPA is set and before
+ * DSP is unstalled
+ */
+ skl_ipc_int_enable(ctx);
+ skl_ipc_op_int_enable(ctx);
+ skl->boot_complete = false;
+ }
- ret = wait_event_timeout(skl->boot_wait, skl->boot_complete,
- msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
- if (ret == 0) {
- dev_err(ctx->dev, "ipc: error DSP boot timeout\n");
- dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
- sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
- sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
- return -EIO;
+ ret = skl_dsp_start_core(ctx, core_mask);
+ if (ret < 0)
+ goto err;
+
+ if (core_id == SKL_DSP_CORE0_ID) {
+ ret = wait_event_timeout(skl->boot_wait,
+ skl->boot_complete,
+ msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
+
+ /* If core 1 was turned on for booting core 0, turn it off */
+ skl_dsp_core_power_down(ctx, SKL_DSP_CORE_MASK(1));
+ if (ret == 0) {
+ dev_err(ctx->dev, "%s: DSP boot timeout\n", __func__);
+ dev_err(ctx->dev, "Error code=0x%x: FW status=0x%x\n",
+ sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE),
+ sst_dsp_shim_read(ctx, BXT_ADSP_FW_STATUS));
+ dev_err(ctx->dev, "Failed to set core0 to D0 state\n");
+ ret = -EIO;
+ goto err;
+ }
}
- skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+ /* Tell FW if additional core in now On */
+
+ if (core_id != SKL_DSP_CORE0_ID) {
+ dx.core_mask = core_mask;
+ dx.dx_mask = core_mask;
+
+ ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID,
+ BXT_BASE_FW_MODULE_ID, &dx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "IPC set_dx for core %d fail: %d\n",
+ core_id, ret);
+ goto err;
+ }
+ }
+
+ skl->cores.state[core_id] = SKL_DSP_RUNNING;
return 0;
+err:
+ if (core_id == SKL_DSP_CORE0_ID)
+ core_mask |= SKL_DSP_CORE_MASK(1);
+ skl_dsp_disable_core(ctx, core_mask);
+
+ return ret;
}
-static int bxt_set_dsp_D3(struct sst_dsp *ctx)
+static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
{
+ int ret;
struct skl_ipc_dxstate_info dx;
struct skl_sst *skl = ctx->thread_context;
- int ret = 0;
+ unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
- if (!is_skl_dsp_running(ctx))
- return ret;
-
- dx.core_mask = SKL_DSP_CORE0_MASK;
+ dx.core_mask = core_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, "Failed to set DSP to D3 state: %d\n", ret);
- return ret;
- }
+ dev_dbg(ctx->dev, "core mask=%x dx_mask=%x\n",
+ dx.core_mask, dx.dx_mask);
+
+ ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID,
+ BXT_BASE_FW_MODULE_ID, &dx);
+ if (ret < 0)
+ dev_err(ctx->dev,
+ "Failed to set DSP to D3:core id = %d;Continue reset\n",
+ core_id);
- ret = skl_dsp_disable_core(ctx);
+ ret = skl_dsp_disable_core(ctx, core_mask);
if (ret < 0) {
- dev_err(ctx->dev, "disbale dsp core failed: %d\n", ret);
- ret = -EIO;
+ dev_err(ctx->dev, "Failed to disable core %d\n", ret);
+ return ret;
}
-
- skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
+ skl->cores.state[core_id] = SKL_DSP_RESET;
return 0;
}
@@ -244,6 +416,7 @@ static struct skl_dsp_fw_ops bxt_fw_ops = {
.set_state_D3 = bxt_set_dsp_D3,
.load_fw = bxt_load_base_firmware,
.get_fw_errcode = bxt_get_errorcode,
+ .load_library = bxt_load_library,
};
static struct sst_ops skl_ops = {
@@ -274,6 +447,7 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
skl->dev = dev;
skl_dev.thread_context = skl;
+ INIT_LIST_HEAD(&skl->uuid_list);
skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
if (!skl->dsp) {
@@ -291,29 +465,52 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
sst_dsp_mailbox_init(sst, (BXT_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
SKL_ADSP_W0_UP_SZ, BXT_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
+ INIT_LIST_HEAD(&sst->module_list);
ret = skl_ipc_init(dev, skl);
if (ret)
return ret;
+ skl->cores.count = 2;
skl->boot_complete = false;
init_waitqueue_head(&skl->boot_wait);
+ skl->is_first_boot = true;
+
+ if (dsp)
+ *dsp = skl;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(bxt_sst_dsp_init);
+
+int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx)
+{
+ int ret;
+ struct sst_dsp *sst = ctx->dsp;
ret = sst->fw_ops.load_fw(sst);
if (ret < 0) {
- dev_err(dev, "Load base fw failed: %x", ret);
+ dev_err(dev, "Load base fw failed: %x\n", ret);
return ret;
}
- if (dsp)
- *dsp = skl;
+ skl_dsp_init_core_state(sst);
+
+ if (ctx->manifest.lib_count > 1) {
+ ret = sst->fw_ops.load_library(sst, &ctx->manifest);
+ if (ret < 0) {
+ dev_err(dev, "Load Library failed : %x\n", ret);
+ return ret;
+ }
+ }
+ ctx->is_first_boot = false;
return 0;
}
-EXPORT_SYMBOL_GPL(bxt_sst_dsp_init);
-
+EXPORT_SYMBOL_GPL(bxt_sst_init_fw);
void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
{
+ skl_freeup_uuid_list(ctx);
skl_ipc_free(&ctx->ipc);
ctx->dsp->cl_dev.ops.cl_cleanup_controller(ctx->dsp);
diff --git a/sound/soc/intel/skylake/skl-messages.c b/sound/soc/intel/skylake/skl-messages.c
index 226db84ba20f..805b7f2173f3 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -203,26 +203,35 @@ static const struct skl_dsp_ops dsp_ops[] = {
.id = 0x9d70,
.loader_ops = skl_get_loader_ops,
.init = skl_sst_dsp_init,
+ .init_fw = skl_sst_init_fw,
+ .cleanup = skl_sst_dsp_cleanup
+ },
+ {
+ .id = 0x9d71,
+ .loader_ops = skl_get_loader_ops,
+ .init = skl_sst_dsp_init,
+ .init_fw = skl_sst_init_fw,
.cleanup = skl_sst_dsp_cleanup
},
{
.id = 0x5a98,
.loader_ops = bxt_get_loader_ops,
.init = bxt_sst_dsp_init,
+ .init_fw = bxt_sst_init_fw,
.cleanup = bxt_sst_dsp_cleanup
},
};
-static int skl_get_dsp_ops(int pci_id)
+const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id)
{
int i;
for (i = 0; i < ARRAY_SIZE(dsp_ops); i++) {
if (dsp_ops[i].id == pci_id)
- return i;
+ return &dsp_ops[i];
}
- return -EINVAL;
+ return NULL;
}
int skl_init_dsp(struct skl *skl)
@@ -232,7 +241,8 @@ int skl_init_dsp(struct skl *skl)
struct hdac_bus *bus = ebus_to_hbus(ebus);
struct skl_dsp_loader_ops loader_ops;
int irq = bus->irq;
- int ret, index;
+ const struct skl_dsp_ops *ops;
+ int ret;
/* enable ppcap interrupt */
snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
@@ -245,18 +255,18 @@ int skl_init_dsp(struct skl *skl)
return -ENXIO;
}
- index = skl_get_dsp_ops(skl->pci->device);
- if (index < 0)
- return -EINVAL;
+ ops = skl_get_dsp_ops(skl->pci->device);
+ if (!ops)
+ return -EIO;
- loader_ops = dsp_ops[index].loader_ops();
- ret = dsp_ops[index].init(bus->dev, mmio_base, irq,
- skl->fw_name, loader_ops, &skl->skl_sst);
+ loader_ops = ops->loader_ops();
+ ret = ops->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;
@@ -267,16 +277,16 @@ int 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;
- int index;
+ const struct skl_dsp_ops *ops;
/* disable ppcap interrupt */
snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false);
- index = skl_get_dsp_ops(skl->pci->device);
- if (index < 0)
+ ops = skl_get_dsp_ops(skl->pci->device);
+ if (!ops)
return -EIO;
- dsp_ops[index].cleanup(bus->dev, ctx);
+ ops->cleanup(bus->dev, ctx);
if (ctx->dsp->addr.lpe)
iounmap(ctx->dsp->addr.lpe);
@@ -290,7 +300,7 @@ int skl_suspend_dsp(struct skl *skl)
int ret;
/* if ppcap is not supported return 0 */
- if (!skl->ebus.ppcap)
+ if (!skl->ebus.bus.ppcap)
return 0;
ret = skl_dsp_sleep(ctx->dsp);
@@ -310,13 +320,17 @@ int skl_resume_dsp(struct skl *skl)
int ret;
/* if ppcap is not supported return 0 */
- if (!skl->ebus.ppcap)
+ if (!skl->ebus.bus.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);
+ /* check if DSP 1st boot is done */
+ if (skl->skl_sst->is_first_boot == true)
+ return 0;
+
ret = skl_dsp_wake(ctx->dsp);
if (ret < 0)
return ret;
@@ -666,6 +680,7 @@ static u16 skl_get_module_param_size(struct skl_sst *ctx,
return param_size;
case SKL_MODULE_TYPE_BASE_OUTFMT:
+ case SKL_MODULE_TYPE_KPB:
return sizeof(struct skl_base_outfmt_cfg);
default:
@@ -719,6 +734,7 @@ static int skl_set_module_format(struct skl_sst *ctx,
break;
case SKL_MODULE_TYPE_BASE_OUTFMT:
+ case SKL_MODULE_TYPE_KPB:
skl_set_base_outfmt_format(ctx, module_config, *param_data);
break;
@@ -730,7 +746,7 @@ static int skl_set_module_format(struct skl_sst *ctx,
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,
+ print_hex_dump_debug("Module params:", DUMP_PREFIX_OFFSET, 8, 4,
*param_data, param_size, false);
return 0;
}
@@ -773,6 +789,7 @@ static int skl_alloc_queue(struct skl_module_pin *mpin,
mpin[i].in_use = true;
mpin[i].id.module_id = id.module_id;
mpin[i].id.instance_id = id.instance_id;
+ mpin[i].id.pvt_id = id.pvt_id;
mpin[i].tgt_mcfg = tgt_cfg;
return i;
}
@@ -796,6 +813,7 @@ static void skl_free_queue(struct skl_module_pin *mpin, int q_index)
mpin[q_index].in_use = false;
mpin[q_index].id.module_id = 0;
mpin[q_index].id.instance_id = 0;
+ mpin[q_index].id.pvt_id = 0;
}
mpin[q_index].pin_state = SKL_PIN_UNBIND;
mpin[q_index].tgt_mcfg = NULL;
@@ -836,7 +854,7 @@ int skl_init_module(struct skl_sst *ctx,
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);
+ mconfig->id.module_id, mconfig->id.pvt_id);
if (mconfig->pipe->state != SKL_PIPE_CREATED) {
dev_err(ctx->dev, "Pipe not created state= %d pipe_id= %d\n",
@@ -852,10 +870,11 @@ int skl_init_module(struct skl_sst *ctx,
}
msg.module_id = mconfig->id.module_id;
- msg.instance_id = mconfig->id.instance_id;
+ msg.instance_id = mconfig->id.pvt_id;
msg.ppl_instance_id = mconfig->pipe->ppl_id;
msg.param_data_size = module_config_size;
msg.core_id = mconfig->core_id;
+ msg.domain = mconfig->domain;
ret = skl_ipc_init_instance(&ctx->ipc, &msg, param_data);
if (ret < 0) {
@@ -872,9 +891,9 @@ 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);
+ __func__, src_module->id.module_id, src_module->id.pvt_id);
dev_dbg(ctx->dev, "%s: dst_module=%d dst_instacne=%d\n", __func__,
- dst_module->id.module_id, dst_module->id.instance_id);
+ dst_module->id.module_id, dst_module->id.pvt_id);
dev_dbg(ctx->dev, "src_module state = %d dst module state = %d\n",
src_module->m_state, dst_module->m_state);
@@ -921,9 +940,9 @@ int skl_unbind_modules(struct skl_sst *ctx,
return 0;
msg.module_id = src_mcfg->id.module_id;
- msg.instance_id = src_mcfg->id.instance_id;
+ msg.instance_id = src_mcfg->id.pvt_id;
msg.dst_module_id = dst_mcfg->id.module_id;
- msg.dst_instance_id = dst_mcfg->id.instance_id;
+ msg.dst_instance_id = dst_mcfg->id.pvt_id;
msg.bind = false;
ret = skl_ipc_bind_unbind(&ctx->ipc, &msg);
@@ -982,9 +1001,9 @@ int skl_bind_modules(struct skl_sst *ctx,
msg.src_queue, msg.dst_queue);
msg.module_id = src_mcfg->id.module_id;
- msg.instance_id = src_mcfg->id.instance_id;
+ msg.instance_id = src_mcfg->id.pvt_id;
msg.dst_module_id = dst_mcfg->id.module_id;
- msg.dst_instance_id = dst_mcfg->id.instance_id;
+ msg.dst_instance_id = dst_mcfg->id.pvt_id;
msg.bind = true;
ret = skl_ipc_bind_unbind(&ctx->ipc, &msg);
@@ -1046,7 +1065,7 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
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 is started, do stop the pipe in FW. */
if (pipe->state > SKL_PIPE_STARTED) {
ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED);
if (ret < 0) {
@@ -1055,18 +1074,20 @@ int skl_delete_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
}
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");
+ /* If pipe was not created in FW, do not try to delete it */
+ if (pipe->state < SKL_PIPE_CREATED)
+ return 0;
- pipe->state = SKL_PIPE_INVALID;
+ ret = skl_ipc_delete_pipeline(&ctx->ipc, pipe->ppl_id);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to delete pipeline\n");
+ return ret;
}
+ pipe->state = SKL_PIPE_INVALID;
+
return ret;
}
@@ -1125,7 +1146,30 @@ int skl_stop_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
return ret;
}
- pipe->state = SKL_PIPE_CREATED;
+ pipe->state = SKL_PIPE_PAUSED;
+
+ return 0;
+}
+
+/*
+ * Reset the pipeline by sending set pipe state IPC this will reset the DMA
+ * from the DSP side
+ */
+int skl_reset_pipe(struct skl_sst *ctx, struct skl_pipe *pipe)
+{
+ int ret;
+
+ /* 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_RESET);
+ if (ret < 0) {
+ dev_dbg(ctx->dev, "Failed to reset pipe ret=%d\n", ret);
+ return ret;
+ }
+
+ pipe->state = SKL_PIPE_RESET;
return 0;
}
@@ -1137,7 +1181,7 @@ int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size,
struct skl_ipc_large_config_msg msg;
msg.module_id = mcfg->id.module_id;
- msg.instance_id = mcfg->id.instance_id;
+ msg.instance_id = mcfg->id.pvt_id;
msg.param_data_size = size;
msg.large_param_id = param_id;
@@ -1150,7 +1194,7 @@ int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,
struct skl_ipc_large_config_msg msg;
msg.module_id = mcfg->id.module_id;
- msg.instance_id = mcfg->id.instance_id;
+ msg.instance_id = mcfg->id.pvt_id;
msg.param_data_size = size;
msg.large_param_id = param_id;
diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c
index 7d73648e5f9a..3f8e6f0b7eb5 100644
--- a/sound/soc/intel/skylake/skl-nhlt.c
+++ b/sound/soc/intel/skylake/skl-nhlt.c
@@ -17,6 +17,7 @@
* ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
*
*/
+#include <linux/pci.h>
#include "skl.h"
/* Unique identification for getting NHLT blobs */
@@ -149,6 +150,45 @@ struct nhlt_specific_cfg
return NULL;
}
+int skl_get_dmic_geo(struct skl *skl)
+{
+ struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
+ struct nhlt_endpoint *epnt;
+ struct nhlt_dmic_array_config *cfg;
+ struct device *dev = &skl->pci->dev;
+ unsigned int dmic_geo = 0;
+ u8 j;
+
+ epnt = (struct nhlt_endpoint *)nhlt->desc;
+
+ for (j = 0; j < nhlt->endpoint_count; j++) {
+ if (epnt->linktype == NHLT_LINK_DMIC) {
+ cfg = (struct nhlt_dmic_array_config *)
+ (epnt->config.caps);
+ switch (cfg->array_type) {
+ case NHLT_MIC_ARRAY_2CH_SMALL:
+ case NHLT_MIC_ARRAY_2CH_BIG:
+ dmic_geo |= MIC_ARRAY_2CH;
+ break;
+
+ case NHLT_MIC_ARRAY_4CH_1ST_GEOM:
+ case NHLT_MIC_ARRAY_4CH_L_SHAPED:
+ case NHLT_MIC_ARRAY_4CH_2ND_GEOM:
+ dmic_geo |= MIC_ARRAY_4CH;
+ break;
+
+ default:
+ dev_warn(dev, "undefined DMIC array_type 0x%0x\n",
+ cfg->array_type);
+
+ }
+ }
+ epnt = (struct nhlt_endpoint *)((u8 *)epnt + epnt->length);
+ }
+
+ return dmic_geo;
+}
+
static void skl_nhlt_trim_space(struct skl *skl)
{
char *s = skl->tplg_name;
diff --git a/sound/soc/intel/skylake/skl-nhlt.h b/sound/soc/intel/skylake/skl-nhlt.h
index 3769f9fefe2b..116534e7b3c5 100644
--- a/sound/soc/intel/skylake/skl-nhlt.h
+++ b/sound/soc/intel/skylake/skl-nhlt.h
@@ -103,4 +103,26 @@ struct nhlt_resource_desc {
u64 length;
} __packed;
+#define MIC_ARRAY_2CH 2
+#define MIC_ARRAY_4CH 4
+
+struct nhlt_tdm_config {
+ u8 virtual_slot;
+ u8 config_type;
+} __packed;
+
+struct nhlt_dmic_array_config {
+ struct nhlt_tdm_config tdm_config;
+ u8 array_type;
+} __packed;
+
+enum {
+ NHLT_MIC_ARRAY_2CH_SMALL = 0xa,
+ NHLT_MIC_ARRAY_2CH_BIG = 0xb,
+ NHLT_MIC_ARRAY_4CH_1ST_GEOM = 0xc,
+ NHLT_MIC_ARRAY_4CH_L_SHAPED = 0xd,
+ NHLT_MIC_ARRAY_4CH_2ND_GEOM = 0xe,
+ NHLT_MIC_ARRAY_VENDOR_DEFINED = 0xf,
+};
+
#endif
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index 7c81b31748ff..58c728662600 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -106,7 +106,7 @@ static void skl_set_pcm_constrains(struct hdac_ext_bus *ebus,
static enum hdac_ext_stream_type skl_get_host_stream_type(struct hdac_ext_bus *ebus)
{
- if (ebus->ppcap)
+ if ((ebus_to_hbus(ebus))->ppcap)
return HDAC_EXT_STREAM_TYPE_HOST;
else
return HDAC_EXT_STREAM_TYPE_COUPLED;
@@ -188,7 +188,7 @@ static int skl_get_format(struct snd_pcm_substream *substream,
struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
int format_val = 0;
- if (ebus->ppcap) {
+ if ((ebus_to_hbus(ebus))->ppcap) {
struct snd_pcm_runtime *runtime = substream->runtime;
format_val = snd_hdac_calc_stream_format(runtime->rate,
@@ -227,16 +227,25 @@ 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);
+ struct skl *skl = get_skl_ctx(dai->dev);
unsigned int format_val;
int err;
+ struct skl_module_cfg *mconfig;
dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
+ mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
+
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));
+ /* In case of XRUN recovery, reset the FW pipe to clean state */
+ if (mconfig && (substream->runtime->status->state ==
+ SNDRV_PCM_STATE_XRUN))
+ skl_reset_pipe(skl->skl_sst, mconfig->pipe);
+
err = snd_hdac_stream_set_params(hdac_stream(stream), format_val);
if (err < 0)
return err;
@@ -521,6 +530,8 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
struct skl_dma_params *dma_params;
struct snd_soc_dai *codec_dai = rtd->codec_dai;
struct hdac_ext_link *link;
+ struct skl *skl = get_skl_ctx(dai->dev);
+ struct skl_module_cfg *mconfig = NULL;
dma_params = (struct skl_dma_params *)
snd_soc_dai_get_dma_data(codec_dai, substream);
@@ -535,6 +546,12 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
snd_hdac_ext_link_stream_reset(link_dev);
+ /* In case of XRUN recovery, reset the FW pipe to clean state */
+ mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
+ if (mconfig && (substream->runtime->status->state ==
+ SNDRV_PCM_STATE_XRUN))
+ skl_reset_pipe(skl->skl_sst, mconfig->pipe);
+
snd_hdac_ext_link_stream_setup(link_dev, format_val);
snd_hdac_ext_link_set_stream_id(link, hdac_stream(link_dev)->stream_tag);
@@ -631,7 +648,8 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.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,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
},
.capture = {
.stream_name = "System Capture",
@@ -1003,57 +1021,17 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
{
struct hdac_ext_bus *ebus = get_bus_ctx(substream);
- if (!ebus->ppcap)
+ if (!(ebus_to_hbus(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 (hstream->bufsize == delay)
- delay = 0;
-
- 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)
+static snd_pcm_uframes_t skl_platform_pcm_pointer
+ (struct snd_pcm_substream *substream)
{
- struct hdac_stream *hstr = hdac_stream(hstream);
- struct snd_pcm_substream *substream = hstr->substream;
- struct hdac_ext_bus *ebus;
+ struct hdac_ext_stream *hstream = get_hdac_ext_stream(substream);
unsigned int pos;
- int delay;
/* use the position buffer as default */
pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream));
@@ -1061,23 +1039,7 @@ static unsigned int skl_get_position(struct hdac_ext_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));
+ return bytes_to_frames(substream->runtime, pos);
}
static u64 skl_adjust_codec_delay(struct snd_pcm_substream *substream,
@@ -1132,7 +1094,7 @@ static int skl_get_time_info(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_pcm_ops skl_platform_ops = {
+static const struct snd_pcm_ops skl_platform_ops = {
.open = skl_platform_open,
.ioctl = snd_pcm_lib_ioctl,
.trigger = skl_platform_pcm_trigger,
@@ -1177,12 +1139,67 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
return retval;
}
+static int skl_populate_modules(struct skl *skl)
+{
+ struct skl_pipeline *p;
+ struct skl_pipe_module *m;
+ struct snd_soc_dapm_widget *w;
+ struct skl_module_cfg *mconfig;
+ int ret;
+
+ list_for_each_entry(p, &skl->ppl_list, node) {
+ list_for_each_entry(m, &p->pipe->w_list, node) {
+
+ w = m->w;
+ mconfig = w->priv;
+
+ ret = snd_skl_get_module_info(skl->skl_sst, mconfig);
+ if (ret < 0) {
+ dev_err(skl->skl_sst->dev,
+ "query module info failed:%d\n", ret);
+ goto err;
+ }
+ }
+ }
+err:
+ return ret;
+}
+
static int skl_platform_soc_probe(struct snd_soc_platform *platform)
{
struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev);
+ struct skl *skl = ebus_to_skl(ebus);
+ const struct skl_dsp_ops *ops;
+ int ret;
+
+ pm_runtime_get_sync(platform->dev);
+ if ((ebus_to_hbus(ebus))->ppcap) {
+ ret = skl_tplg_init(platform, ebus);
+ if (ret < 0) {
+ dev_err(platform->dev, "Failed to init topology!\n");
+ return ret;
+ }
+ skl->platform = platform;
+
+ /* load the firmwares, since all is set */
+ ops = skl_get_dsp_ops(skl->pci->device);
+ if (!ops)
+ return -EIO;
+
+ if (skl->skl_sst->is_first_boot == false) {
+ dev_err(platform->dev, "DSP reports first boot done!!!\n");
+ return -EIO;
+ }
- if (ebus->ppcap)
- return skl_tplg_init(platform, ebus);
+ ret = ops->init_fw(platform->dev, skl->skl_sst);
+ if (ret < 0) {
+ dev_err(platform->dev, "Failed to boot first fw: %d\n", ret);
+ return ret;
+ }
+ skl_populate_modules(skl);
+ }
+ pm_runtime_mark_last_busy(platform->dev);
+ pm_runtime_put_autosuspend(platform->dev);
return 0;
}
diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c
index da2329d17f4d..efa2532114ba 100644
--- a/sound/soc/intel/skylake/skl-sst-cldma.c
+++ b/sound/soc/intel/skylake/skl-sst-cldma.c
@@ -341,14 +341,14 @@ int skl_cldma_prepare(struct sst_dsp *ctx)
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);
+ dev_err(ctx->dev, "Alloc buffer for base fw failed: %x\n", 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);
+ dev_err(ctx->dev, "Alloc buffer for blde failed: %x\n", ret);
ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_data);
return ret;
}
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c
index 13c19855ee1a..c3deefab65d6 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.c
+++ b/sound/soc/intel/skylake/skl-sst-dsp.c
@@ -34,33 +34,84 @@ void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state)
mutex_unlock(&ctx->mutex);
}
-static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx)
+/*
+ * Initialize core power state and usage count. To be called after
+ * successful first boot. Hence core 0 will be running and other cores
+ * will be reset
+ */
+void skl_dsp_init_core_state(struct sst_dsp *ctx)
+{
+ struct skl_sst *skl = ctx->thread_context;
+ int i;
+
+ skl->cores.state[SKL_DSP_CORE0_ID] = SKL_DSP_RUNNING;
+ skl->cores.usage_count[SKL_DSP_CORE0_ID] = 1;
+
+ for (i = SKL_DSP_CORE0_ID + 1; i < SKL_DSP_CORES_MAX; i++) {
+ skl->cores.state[i] = SKL_DSP_RESET;
+ skl->cores.usage_count[i] = 0;
+ }
+}
+
+/* Get the mask for all enabled cores */
+unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx)
+{
+ struct skl_sst *skl = ctx->thread_context;
+ unsigned int core_mask, en_cores_mask;
+ u32 val;
+
+ core_mask = SKL_DSP_CORES_MASK(skl->cores.count);
+
+ val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPCS);
+
+ /* Cores having CPA bit set */
+ en_cores_mask = (val & SKL_ADSPCS_CPA_MASK(core_mask)) >>
+ SKL_ADSPCS_CPA_SHIFT;
+
+ /* And cores having CRST bit cleared */
+ en_cores_mask &= (~val & SKL_ADSPCS_CRST_MASK(core_mask)) >>
+ SKL_ADSPCS_CRST_SHIFT;
+
+ /* And cores having CSTALL bit cleared */
+ en_cores_mask &= (~val & SKL_ADSPCS_CSTALL_MASK(core_mask)) >>
+ SKL_ADSPCS_CSTALL_SHIFT;
+ en_cores_mask &= core_mask;
+
+ dev_dbg(ctx->dev, "DSP enabled cores mask = %x\n", en_cores_mask);
+
+ return en_cores_mask;
+}
+
+static int
+skl_dsp_core_set_reset_state(struct sst_dsp *ctx, unsigned int core_mask)
{
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));
+ SKL_ADSP_REG_ADSPCS, SKL_ADSPCS_CRST_MASK(core_mask),
+ SKL_ADSPCS_CRST_MASK(core_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_ADSPCS_CRST_MASK(core_mask),
+ SKL_ADSPCS_CRST_MASK(core_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");
+ SKL_ADSPCS_CRST_MASK(core_mask)) !=
+ SKL_ADSPCS_CRST_MASK(core_mask)) {
+ dev_err(ctx->dev, "Set reset state failed: core_mask %x\n",
+ core_mask);
ret = -EIO;
}
return ret;
}
-static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
+int skl_dsp_core_unset_reset_state(
+ struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
@@ -68,152 +119,160 @@ static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
/* update bits */
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_CRST_MASK, 0);
+ SKL_ADSPCS_CRST_MASK(core_mask), 0);
/* 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_MASK(core_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");
+ SKL_ADSPCS_CRST_MASK(core_mask)) != 0) {
+ dev_err(ctx->dev, "Unset reset state failed: core_mask %x\n",
+ core_mask);
ret = -EIO;
}
return ret;
}
-static bool is_skl_dsp_core_enable(struct sst_dsp *ctx)
+static bool
+is_skl_dsp_core_enable(struct sst_dsp *ctx, unsigned int core_mask)
{
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)));
+ is_enable = ((val & SKL_ADSPCS_CPA_MASK(core_mask)) &&
+ (val & SKL_ADSPCS_SPA_MASK(core_mask)) &&
+ !(val & SKL_ADSPCS_CRST_MASK(core_mask)) &&
+ !(val & SKL_ADSPCS_CSTALL_MASK(core_mask)));
+
+ dev_dbg(ctx->dev, "DSP core(s) enabled? %d : core_mask %x\n",
+ is_enable, core_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)
+static int skl_dsp_reset_core(struct sst_dsp *ctx, unsigned int core_mask)
{
/* 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));
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
+ SKL_ADSPCS_CSTALL_MASK(core_mask),
+ SKL_ADSPCS_CSTALL_MASK(core_mask));
/* set reset state */
- return skl_dsp_core_set_reset_state(ctx);
+ return skl_dsp_core_set_reset_state(ctx, core_mask);
}
-static int skl_dsp_start_core(struct sst_dsp *ctx)
+int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask)
{
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");
+ ret = skl_dsp_core_unset_reset_state(ctx, core_mask);
+ if (ret < 0)
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");
+ dev_dbg(ctx->dev, "unstall/run core: core_mask = %x\n", core_mask);
+ sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
+ SKL_ADSPCS_CSTALL_MASK(core_mask), 0);
+
+ if (!is_skl_dsp_core_enable(ctx, core_mask)) {
+ skl_dsp_reset_core(ctx, core_mask);
+ dev_err(ctx->dev, "DSP start core failed: core_mask %x\n",
+ core_mask);
ret = -EIO;
}
return ret;
}
-static int skl_dsp_core_power_up(struct sst_dsp *ctx)
+int skl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask)
{
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));
+ SKL_ADSPCS_SPA_MASK(core_mask),
+ SKL_ADSPCS_SPA_MASK(core_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_ADSPCS_CPA_MASK(core_mask),
+ SKL_ADSPCS_CPA_MASK(core_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");
+ SKL_ADSPCS_CPA_MASK(core_mask)) !=
+ SKL_ADSPCS_CPA_MASK(core_mask)) {
+ dev_err(ctx->dev, "DSP core power up failed: core_mask %x\n",
+ core_mask);
ret = -EIO;
}
return ret;
}
-static int skl_dsp_core_power_down(struct sst_dsp *ctx)
+int skl_dsp_core_power_down(struct sst_dsp *ctx, unsigned int core_mask)
{
/* update bits */
sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_SPA_MASK, 0);
+ SKL_ADSPCS_SPA_MASK(core_mask), 0);
/* poll with timeout to check if operation successful */
return sst_dsp_register_poll(ctx,
SKL_ADSP_REG_ADSPCS,
- SKL_ADSPCS_CPA_MASK,
+ SKL_ADSPCS_CPA_MASK(core_mask),
0,
SKL_DSP_PD_TO,
"Power down");
}
-int skl_dsp_enable_core(struct sst_dsp *ctx)
+int skl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
/* power up */
- ret = skl_dsp_core_power_up(ctx);
+ ret = skl_dsp_core_power_up(ctx, core_mask);
if (ret < 0) {
- dev_dbg(ctx->dev, "dsp core power up failed\n");
+ dev_err(ctx->dev, "dsp core power up failed: core_mask %x\n",
+ core_mask);
return ret;
}
- return skl_dsp_start_core(ctx);
+ return skl_dsp_start_core(ctx, core_mask);
}
-int skl_dsp_disable_core(struct sst_dsp *ctx)
+int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask)
{
int ret;
- ret = skl_dsp_reset_core(ctx);
+ ret = skl_dsp_reset_core(ctx, core_mask);
if (ret < 0) {
- dev_err(ctx->dev, "dsp core reset failed\n");
+ dev_err(ctx->dev, "dsp core reset failed: core_mask %x\n",
+ core_mask);
return ret;
}
/* power down core*/
- ret = skl_dsp_core_power_down(ctx);
+ ret = skl_dsp_core_power_down(ctx, core_mask);
if (ret < 0) {
- dev_err(ctx->dev, "dsp core power down failed\n");
+ dev_err(ctx->dev, "dsp core power down fail mask %x: %d\n",
+ core_mask, ret);
return ret;
}
- if (is_skl_dsp_core_enable(ctx)) {
- dev_err(ctx->dev, "DSP core disable failed\n");
+ if (is_skl_dsp_core_enable(ctx, core_mask)) {
+ dev_err(ctx->dev, "dsp core disable fail mask %x: %d\n",
+ core_mask, ret);
ret = -EIO;
}
@@ -224,28 +283,25 @@ 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 (is_skl_dsp_core_enable(ctx, SKL_DSP_CORE0_MASK)) {
+ ret = skl_dsp_reset_core(ctx, SKL_DSP_CORE0_MASK);
if (ret < 0) {
- dev_err(ctx->dev, "dsp reset failed\n");
+ dev_err(ctx->dev, "dsp core0 reset fail: %d\n", ret);
return ret;
}
- ret = skl_dsp_start_core(ctx);
+ ret = skl_dsp_start_core(ctx, SKL_DSP_CORE0_MASK);
if (ret < 0) {
- dev_err(ctx->dev, "dsp start failed\n");
+ dev_err(ctx->dev, "dsp core0 start fail: %d\n", ret);
return ret;
}
} else {
- dev_dbg(ctx->dev, "disable and enable to make sure DSP is invalid state\n");
- ret = skl_dsp_disable_core(ctx);
-
+ ret = skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
if (ret < 0) {
- dev_err(ctx->dev, "dsp disable core failes\n");
+ dev_err(ctx->dev, "dsp core0 disable fail: %d\n", ret);
return ret;
}
- ret = skl_dsp_enable_core(ctx);
+ ret = skl_dsp_enable_core(ctx, SKL_DSP_CORE0_MASK);
}
return ret;
@@ -281,16 +337,74 @@ irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id)
return result;
}
+/*
+ * skl_dsp_get_core/skl_dsp_put_core will be called inside DAPM context
+ * within the dapm mutex. Hence no separate lock is used.
+ */
+int skl_dsp_get_core(struct sst_dsp *ctx, unsigned int core_id)
+{
+ struct skl_sst *skl = ctx->thread_context;
+ int ret = 0;
+
+ if (core_id >= skl->cores.count) {
+ dev_err(ctx->dev, "invalid core id: %d\n", core_id);
+ return -EINVAL;
+ }
+
+ if (skl->cores.state[core_id] == SKL_DSP_RESET) {
+ ret = ctx->fw_ops.set_state_D0(ctx, core_id);
+ if (ret < 0) {
+ dev_err(ctx->dev, "unable to get core%d\n", core_id);
+ return ret;
+ }
+ }
+
+ skl->cores.usage_count[core_id]++;
+
+ dev_dbg(ctx->dev, "core id %d state %d usage_count %d\n",
+ core_id, skl->cores.state[core_id],
+ skl->cores.usage_count[core_id]);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_dsp_get_core);
+
+int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id)
+{
+ struct skl_sst *skl = ctx->thread_context;
+ int ret = 0;
+
+ if (core_id >= skl->cores.count) {
+ dev_err(ctx->dev, "invalid core id: %d\n", core_id);
+ return -EINVAL;
+ }
+
+ if (--skl->cores.usage_count[core_id] == 0) {
+ ret = ctx->fw_ops.set_state_D3(ctx, core_id);
+ if (ret < 0) {
+ dev_err(ctx->dev, "unable to put core %d: %d\n",
+ core_id, ret);
+ skl->cores.usage_count[core_id]++;
+ }
+ }
+
+ dev_dbg(ctx->dev, "core id %d state %d usage_count %d\n",
+ core_id, skl->cores.state[core_id],
+ skl->cores.usage_count[core_id]);
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_dsp_put_core);
int skl_dsp_wake(struct sst_dsp *ctx)
{
- return ctx->fw_ops.set_state_D0(ctx);
+ return skl_dsp_get_core(ctx, SKL_DSP_CORE0_ID);
}
EXPORT_SYMBOL_GPL(skl_dsp_wake);
int skl_dsp_sleep(struct sst_dsp *ctx)
{
- return ctx->fw_ops.set_state_D3(ctx);
+ return skl_dsp_put_core(ctx, SKL_DSP_CORE0_ID);
}
EXPORT_SYMBOL_GPL(skl_dsp_sleep);
@@ -337,9 +451,7 @@ void skl_dsp_free(struct sst_dsp *dsp)
free_irq(dsp->irq, dsp);
skl_ipc_op_int_disable(dsp);
- skl_ipc_int_disable(dsp);
-
- skl_dsp_disable_core(dsp);
+ skl_dsp_disable_core(dsp, SKL_DSP_CORE0_MASK);
}
EXPORT_SYMBOL_GPL(skl_dsp_free);
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h
index deabe7308d3b..b9e71d051fb1 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.h
+++ b/sound/soc/intel/skylake/skl-sst-dsp.h
@@ -19,6 +19,8 @@
#include <linux/interrupt.h>
#include <sound/memalloc.h>
#include "skl-sst-cldma.h"
+#include "skl-tplg-interface.h"
+#include "skl-topology.h"
struct sst_dsp;
struct skl_sst;
@@ -76,35 +78,53 @@ struct sst_dsp_device;
#define SKL_ADSPIC_IPC 1
#define SKL_ADSPIS_IPC 1
+/* Core ID of core0 */
+#define SKL_DSP_CORE0_ID 0
+
+/* Mask for a given core index, c = 0.. number of supported cores - 1 */
+#define SKL_DSP_CORE_MASK(c) BIT(c)
+
+/*
+ * Core 0 mask = SKL_DSP_CORE_MASK(0); Defined separately
+ * since Core0 is primary core and it is used often
+ */
+#define SKL_DSP_CORE0_MASK BIT(0)
+
+/*
+ * Mask for a given number of cores
+ * nc = number of supported cores
+ */
+#define SKL_DSP_CORES_MASK(nc) GENMASK((nc - 1), 0)
+
/* 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 */
+
+/*
+ * Core Reset - asserted high
+ * CRST Mask for a given core mask pattern, cm
+ */
+#define SKL_ADSPCS_CRST_SHIFT 0
+#define SKL_ADSPCS_CRST_MASK(cm) ((cm) << SKL_ADSPCS_CRST_SHIFT)
+
+/*
+ * Core run/stall - when set to '1' core is stalled
+ * CSTALL Mask for a given core mask pattern, cm
+ */
+#define SKL_ADSPCS_CSTALL_SHIFT 8
+#define SKL_ADSPCS_CSTALL_MASK(cm) ((cm) << SKL_ADSPCS_CSTALL_SHIFT)
+
+/*
+ * Set Power Active - when set to '1' turn cores on
+ * SPA Mask for a given core mask pattern, cm
+ */
+#define SKL_ADSPCS_SPA_SHIFT 16
+#define SKL_ADSPCS_SPA_MASK(cm) ((cm) << SKL_ADSPCS_SPA_SHIFT)
+
+/*
+ * Current Power Active - power status of cores, set by hardware
+ * CPA Mask for a given core mask pattern, cm
+ */
+#define SKL_ADSPCS_CPA_SHIFT 24
+#define SKL_ADSPCS_CPA_MASK(cm) ((cm) << SKL_ADSPCS_CPA_SHIFT)
enum skl_dsp_states {
SKL_DSP_RUNNING = 1,
@@ -114,9 +134,11 @@ enum skl_dsp_states {
struct skl_dsp_fw_ops {
int (*load_fw)(struct sst_dsp *ctx);
/* FW module parser/loader */
+ int (*load_library)(struct sst_dsp *ctx,
+ struct skl_dfw_manifest *minfo);
int (*parse_fw)(struct sst_dsp *ctx);
- int (*set_state_D0)(struct sst_dsp *ctx);
- int (*set_state_D3)(struct sst_dsp *ctx);
+ int (*set_state_D0)(struct sst_dsp *ctx, unsigned int core_id);
+ int (*set_state_D3)(struct sst_dsp *ctx, unsigned int core_id);
unsigned int (*get_fw_errcode)(struct sst_dsp *ctx);
int (*load_mod)(struct sst_dsp *ctx, u16 mod_id, u8 *mod_name);
int (*unload_mod)(struct sst_dsp *ctx, u16 mod_id);
@@ -157,14 +179,26 @@ 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_enable_core(struct sst_dsp *ctx);
-int skl_dsp_disable_core(struct sst_dsp *ctx);
bool is_skl_dsp_running(struct sst_dsp *ctx);
+
+unsigned int skl_dsp_get_enabled_cores(struct sst_dsp *ctx);
+void skl_dsp_init_core_state(struct sst_dsp *ctx);
+int skl_dsp_enable_core(struct sst_dsp *ctx, unsigned int core_mask);
+int skl_dsp_disable_core(struct sst_dsp *ctx, unsigned int core_mask);
+int skl_dsp_core_power_up(struct sst_dsp *ctx, unsigned int core_mask);
+int skl_dsp_core_power_down(struct sst_dsp *ctx, unsigned int core_mask);
+int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx,
+ unsigned int core_mask);
+int skl_dsp_start_core(struct sst_dsp *ctx, unsigned int core_mask);
+
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_get_core(struct sst_dsp *ctx, unsigned int core_id);
+int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id);
+
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,
@@ -172,7 +206,23 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
int bxt_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);
+int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx);
+int bxt_sst_init_fw(struct device *dev, struct skl_sst *ctx);
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx);
+int snd_skl_get_module_info(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig);
+int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw,
+ unsigned int offset, int index);
+int skl_get_pvt_id(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig);
+int skl_put_pvt_id(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig);
+int skl_get_pvt_instance_id_map(struct skl_sst *ctx,
+ int module_id, int instance_id);
+void skl_freeup_uuid_list(struct skl_sst *ctx);
+
+int skl_dsp_strip_extended_manifest(struct firmware *fw);
+
#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
index 543460293b00..0bd01e62622c 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.c
+++ b/sound/soc/intel/skylake/skl-sst-ipc.c
@@ -114,6 +114,11 @@
#define IPC_CORE_ID(x) (((x) & IPC_CORE_ID_MASK) \
<< IPC_CORE_ID_SHIFT)
+#define IPC_DOMAIN_SHIFT 28
+#define IPC_DOMAIN_MASK 0x1
+#define IPC_DOMAIN(x) (((x) & IPC_DOMAIN_MASK) \
+ << IPC_DOMAIN_SHIFT)
+
/* Bind/Unbind message extension register */
#define IPC_DST_MOD_ID_SHIFT 0
#define IPC_DST_MOD_ID(x) (((x) & IPC_MOD_ID_MASK) \
@@ -190,6 +195,7 @@ enum skl_ipc_glb_type {
IPC_GLB_GET_PPL_CONTEXT_SIZE = 21,
IPC_GLB_SAVE_PPL = 22,
IPC_GLB_RESTORE_PPL = 23,
+ IPC_GLB_LOAD_LIBRARY = 24,
IPC_GLB_NOTIFY = 26,
IPC_GLB_MAX_IPC_MSG_NUMBER = 31 /* Maximum message number */
};
@@ -338,7 +344,7 @@ static int skl_ipc_process_notification(struct sst_generic_ipc *ipc,
break;
default:
- dev_err(ipc->dev, "ipc: Unhandled error msg=%x",
+ dev_err(ipc->dev, "ipc: Unhandled error msg=%x\n",
header.primary);
break;
}
@@ -363,7 +369,7 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
/* first process the header */
switch (reply) {
case IPC_GLB_REPLY_SUCCESS:
- dev_info(ipc->dev, "ipc FW reply %x: success\n", header.primary);
+ dev_dbg(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;
@@ -379,13 +385,13 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
break;
default:
- dev_err(ipc->dev, "Unknown ipc reply: 0x%x", reply);
+ dev_err(ipc->dev, "Unknown ipc reply: 0x%x\n", 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, "ipc FW reply: reply=%d\n", reply);
dev_err(ipc->dev, "FW Error Code: %u\n",
ipc->dsp->fw_ops.get_fw_errcode(ipc->dsp));
}
@@ -434,9 +440,9 @@ irqreturn_t skl_dsp_irq_thread_handler(int irq, void *context)
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",
+ dev_dbg(dsp->dev, "IPC irq: Firmware respond primary:%x\n",
header.primary);
- dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x",
+ dev_dbg(dsp->dev, "IPC irq: Firmware respond extension:%x\n",
header.extension);
if (IPC_GLB_NOTIFY_RSP_TYPE(header.primary)) {
@@ -692,7 +698,7 @@ int skl_ipc_init_instance(struct sst_generic_ipc *ipc,
/* 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,
+ print_hex_dump_debug("Param data:", DUMP_PREFIX_NONE,
16, 4, buffer, param_block_size, false);
header.primary = IPC_MSG_TARGET(IPC_MOD_MSG);
@@ -704,6 +710,7 @@ int skl_ipc_init_instance(struct sst_generic_ipc *ipc,
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);
+ header.extension |= IPC_DOMAIN(msg->domain);
dev_dbg(ipc->dev, "In %s primary =%x ext=%x\n", __func__,
header.primary, header.extension);
@@ -742,7 +749,7 @@ int skl_ipc_bind_unbind(struct sst_generic_ipc *ipc,
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");
+ dev_err(ipc->dev, "ipc: bind/unbind failed\n");
return ret;
}
@@ -902,3 +909,25 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
return ret;
}
EXPORT_SYMBOL_GPL(skl_ipc_get_large_config);
+
+int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc,
+ u8 dma_id, u8 table_id)
+{
+ struct skl_ipc_header header = {0};
+ u64 *ipc_header = (u64 *)(&header);
+ int ret = 0;
+
+ 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_LIBRARY);
+ header.primary |= IPC_MOD_INSTANCE_ID(table_id);
+ header.primary |= IPC_MOD_ID(dma_id);
+
+ ret = sst_ipc_tx_message_wait(ipc, *ipc_header, NULL, 0, NULL, 0);
+
+ if (ret < 0)
+ dev_err(ipc->dev, "ipc: load lib failed\n");
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(skl_sst_ipc_load_library);
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h
index d59d1ba62a43..0334ed4af031 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.h
+++ b/sound/soc/intel/skylake/skl-sst-ipc.h
@@ -45,6 +45,14 @@ struct skl_ipc_header {
u32 extension;
};
+#define SKL_DSP_CORES_MAX 2
+
+struct skl_dsp_cores {
+ unsigned int count;
+ enum skl_dsp_states state[SKL_DSP_CORES_MAX];
+ int usage_count[SKL_DSP_CORES_MAX];
+};
+
struct skl_sst {
struct device *dev;
struct sst_dsp *dsp;
@@ -58,8 +66,23 @@ struct skl_sst {
/* callback for miscbdge */
void (*enable_miscbdcge)(struct device *dev, bool enable);
- /*Is CGCTL.MISCBDCGE disabled*/
+ /* Is CGCTL.MISCBDCGE disabled */
bool miscbdcg_disabled;
+
+ /* Populate module information */
+ struct list_head uuid_list;
+
+ /* Is firmware loaded */
+ bool fw_loaded;
+
+ /* first boot ? */
+ bool is_first_boot;
+
+ /* multi-core */
+ struct skl_dsp_cores cores;
+
+ /* tplg manifest */
+ struct skl_dfw_manifest manifest;
};
struct skl_ipc_init_instance_msg {
@@ -68,6 +91,7 @@ struct skl_ipc_init_instance_msg {
u16 param_data_size;
u8 ppl_instance_id;
u8 core_id;
+ u8 domain;
};
struct skl_ipc_bind_unbind_msg {
@@ -128,6 +152,9 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
struct skl_ipc_large_config_msg *msg, u32 *param);
+int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc,
+ u8 dma_id, u8 table_id);
+
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);
@@ -136,5 +163,6 @@ 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);
+void skl_clear_module_cnt(struct sst_dsp *ctx);
#endif /* __SKL_IPC_H */
diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c
new file mode 100644
index 000000000000..8dc03039b311
--- /dev/null
+++ b/sound/soc/intel/skylake/skl-sst-utils.c
@@ -0,0 +1,407 @@
+/*
+ * skl-sst-utils.c - SKL sst utils functions
+ *
+ * Copyright (C) 2016 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 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/slab.h>
+#include <linux/uuid.h>
+#include "skl-sst-dsp.h"
+#include "../common/sst-dsp.h"
+#include "../common/sst-dsp-priv.h"
+#include "skl-sst-ipc.h"
+
+
+#define UUID_STR_SIZE 37
+#define DEFAULT_HASH_SHA256_LEN 32
+
+/* FW Extended Manifest Header id = $AE1 */
+#define SKL_EXT_MANIFEST_HEADER_MAGIC 0x31454124
+
+struct UUID {
+ u8 id[16];
+};
+
+union seg_flags {
+ u32 ul;
+ struct {
+ u32 contents : 1;
+ u32 alloc : 1;
+ u32 load : 1;
+ u32 read_only : 1;
+ u32 code : 1;
+ u32 data : 1;
+ u32 _rsvd0 : 2;
+ u32 type : 4;
+ u32 _rsvd1 : 4;
+ u32 length : 16;
+ } r;
+} __packed;
+
+struct segment_desc {
+ union seg_flags flags;
+ u32 v_base_addr;
+ u32 file_offset;
+};
+
+struct module_type {
+ u32 load_type : 4;
+ u32 auto_start : 1;
+ u32 domain_ll : 1;
+ u32 domain_dp : 1;
+ u32 rsvd : 25;
+} __packed;
+
+struct adsp_module_entry {
+ u32 struct_id;
+ u8 name[8];
+ struct UUID uuid;
+ struct module_type type;
+ u8 hash1[DEFAULT_HASH_SHA256_LEN];
+ u32 entry_point;
+ u16 cfg_offset;
+ u16 cfg_count;
+ u32 affinity_mask;
+ u16 instance_max_count;
+ u16 instance_bss_size;
+ struct segment_desc segments[3];
+} __packed;
+
+struct adsp_fw_hdr {
+ u32 id;
+ u32 len;
+ u8 name[8];
+ u32 preload_page_count;
+ u32 fw_image_flags;
+ u32 feature_mask;
+ u16 major;
+ u16 minor;
+ u16 hotfix;
+ u16 build;
+ u32 num_modules;
+ u32 hw_buf_base;
+ u32 hw_buf_length;
+ u32 load_offset;
+} __packed;
+
+#define MAX_INSTANCE_BUFF 2
+
+struct uuid_module {
+ uuid_le uuid;
+ int id;
+ int is_loadable;
+ int max_instance;
+ u64 pvt_id[MAX_INSTANCE_BUFF];
+ int *instance_id;
+
+ struct list_head list;
+};
+
+struct skl_ext_manifest_hdr {
+ u32 id;
+ u32 len;
+ u16 version_major;
+ u16 version_minor;
+ u32 entries;
+};
+
+int snd_skl_get_module_info(struct skl_sst *ctx,
+ struct skl_module_cfg *mconfig)
+{
+ struct uuid_module *module;
+ uuid_le *uuid_mod;
+
+ uuid_mod = (uuid_le *)mconfig->guid;
+
+ if (list_empty(&ctx->uuid_list)) {
+ dev_err(ctx->dev, "Module list is empty\n");
+ return -EINVAL;
+ }
+
+ list_for_each_entry(module, &ctx->uuid_list, list) {
+ if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
+ mconfig->id.module_id = module->id;
+ mconfig->is_loadable = module->is_loadable;
+
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_skl_get_module_info);
+
+static int skl_get_pvtid_map(struct uuid_module *module, int instance_id)
+{
+ int pvt_id;
+
+ for (pvt_id = 0; pvt_id < module->max_instance; pvt_id++) {
+ if (module->instance_id[pvt_id] == instance_id)
+ return pvt_id;
+ }
+ return -EINVAL;
+}
+
+int skl_get_pvt_instance_id_map(struct skl_sst *ctx,
+ int module_id, int instance_id)
+{
+ struct uuid_module *module;
+
+ list_for_each_entry(module, &ctx->uuid_list, list) {
+ if (module->id == module_id)
+ return skl_get_pvtid_map(module, instance_id);
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(skl_get_pvt_instance_id_map);
+
+static inline int skl_getid_32(struct uuid_module *module, u64 *val,
+ int word1_mask, int word2_mask)
+{
+ int index, max_inst, pvt_id;
+ u32 mask_val;
+
+ max_inst = module->max_instance;
+ mask_val = (u32)(*val >> word1_mask);
+
+ if (mask_val != 0xffffffff) {
+ index = ffz(mask_val);
+ pvt_id = index + word1_mask + word2_mask;
+ if (pvt_id <= (max_inst - 1)) {
+ *val |= 1 << (index + word1_mask);
+ return pvt_id;
+ }
+ }
+
+ return -EINVAL;
+}
+
+static inline int skl_pvtid_128(struct uuid_module *module)
+{
+ int j, i, word1_mask, word2_mask = 0, pvt_id;
+
+ for (j = 0; j < MAX_INSTANCE_BUFF; j++) {
+ word1_mask = 0;
+
+ for (i = 0; i < 2; i++) {
+ pvt_id = skl_getid_32(module, &module->pvt_id[j],
+ word1_mask, word2_mask);
+ if (pvt_id >= 0)
+ return pvt_id;
+
+ word1_mask += 32;
+ if ((word1_mask + word2_mask) >= module->max_instance)
+ return -EINVAL;
+ }
+
+ word2_mask += 64;
+ if (word2_mask >= module->max_instance)
+ return -EINVAL;
+ }
+
+ return -EINVAL;
+}
+
+/**
+ * skl_get_pvt_id: generate a private id for use as module id
+ *
+ * @ctx: driver context
+ * @mconfig: module configuration data
+ *
+ * This generates a 128 bit private unique id for a module TYPE so that
+ * module instance is unique
+ */
+int skl_get_pvt_id(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
+{
+ struct uuid_module *module;
+ uuid_le *uuid_mod;
+ int pvt_id;
+
+ uuid_mod = (uuid_le *)mconfig->guid;
+
+ list_for_each_entry(module, &ctx->uuid_list, list) {
+ if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
+
+ pvt_id = skl_pvtid_128(module);
+ if (pvt_id >= 0) {
+ module->instance_id[pvt_id] =
+ mconfig->id.instance_id;
+ return pvt_id;
+ }
+ }
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(skl_get_pvt_id);
+
+/**
+ * skl_put_pvt_id: free up the private id allocated
+ *
+ * @ctx: driver context
+ * @mconfig: module configuration data
+ *
+ * This frees a 128 bit private unique id previously generated
+ */
+int skl_put_pvt_id(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
+{
+ int i;
+ uuid_le *uuid_mod;
+ struct uuid_module *module;
+
+ uuid_mod = (uuid_le *)mconfig->guid;
+ list_for_each_entry(module, &ctx->uuid_list, list) {
+ if (uuid_le_cmp(*uuid_mod, module->uuid) == 0) {
+
+ if (mconfig->id.pvt_id != 0)
+ i = (mconfig->id.pvt_id) / 64;
+ else
+ i = 0;
+
+ module->pvt_id[i] &= ~(1 << (mconfig->id.pvt_id));
+ mconfig->id.pvt_id = -1;
+ return 0;
+ }
+ }
+
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(skl_put_pvt_id);
+
+/*
+ * Parse the firmware binary to get the UUID, module id
+ * and loadable flags
+ */
+int snd_skl_parse_uuids(struct sst_dsp *ctx, const struct firmware *fw,
+ unsigned int offset, int index)
+{
+ struct adsp_fw_hdr *adsp_hdr;
+ struct adsp_module_entry *mod_entry;
+ int i, num_entry, size;
+ uuid_le *uuid_bin;
+ const char *buf;
+ struct skl_sst *skl = ctx->thread_context;
+ struct uuid_module *module;
+ struct firmware stripped_fw;
+ unsigned int safe_file;
+
+ /* Get the FW pointer to derive ADSP header */
+ stripped_fw.data = fw->data;
+ stripped_fw.size = fw->size;
+
+ skl_dsp_strip_extended_manifest(&stripped_fw);
+
+ buf = stripped_fw.data;
+
+ /* check if we have enough space in file to move to header */
+ safe_file = sizeof(*adsp_hdr) + offset;
+ if (stripped_fw.size <= safe_file) {
+ dev_err(ctx->dev, "Small fw file size, No space for hdr\n");
+ return -EINVAL;
+ }
+
+ adsp_hdr = (struct adsp_fw_hdr *)(buf + offset);
+
+ /* check 1st module entry is in file */
+ safe_file += adsp_hdr->len + sizeof(*mod_entry);
+ if (stripped_fw.size <= safe_file) {
+ dev_err(ctx->dev, "Small fw file size, No module entry\n");
+ return -EINVAL;
+ }
+
+ mod_entry = (struct adsp_module_entry *)
+ (buf + offset + adsp_hdr->len);
+
+ num_entry = adsp_hdr->num_modules;
+
+ /* check all entries are in file */
+ safe_file += num_entry * sizeof(*mod_entry);
+ if (stripped_fw.size <= safe_file) {
+ dev_err(ctx->dev, "Small fw file size, No modules\n");
+ return -EINVAL;
+ }
+
+
+ /*
+ * Read the UUID(GUID) from FW Manifest.
+ *
+ * The 16 byte UUID format is: XXXXXXXX-XXXX-XXXX-XXXX-XXXXXXXXXX
+ * Populate the UUID table to store module_id and loadable flags
+ * for the module.
+ */
+
+ for (i = 0; i < num_entry; i++, mod_entry++) {
+ module = kzalloc(sizeof(*module), GFP_KERNEL);
+ if (!module)
+ return -ENOMEM;
+
+ uuid_bin = (uuid_le *)mod_entry->uuid.id;
+ memcpy(&module->uuid, uuid_bin, sizeof(module->uuid));
+
+ module->id = (i | (index << 12));
+ module->is_loadable = mod_entry->type.load_type;
+ module->max_instance = mod_entry->instance_max_count;
+ size = sizeof(int) * mod_entry->instance_max_count;
+ module->instance_id = devm_kzalloc(ctx->dev, size, GFP_KERNEL);
+ if (!module->instance_id) {
+ kfree(module);
+ return -ENOMEM;
+ }
+
+ list_add_tail(&module->list, &skl->uuid_list);
+
+ dev_dbg(ctx->dev,
+ "Adding uuid :%pUL mod id: %d Loadable: %d\n",
+ &module->uuid, module->id, module->is_loadable);
+ }
+
+ return 0;
+}
+
+void skl_freeup_uuid_list(struct skl_sst *ctx)
+{
+ struct uuid_module *uuid, *_uuid;
+
+ list_for_each_entry_safe(uuid, _uuid, &ctx->uuid_list, list) {
+ list_del(&uuid->list);
+ kfree(uuid);
+ }
+}
+
+/*
+ * some firmware binary contains some extended manifest. This needs
+ * to be stripped in that case before we load and use that image.
+ *
+ * Get the module id for the module by checking
+ * the table for the UUID for the module
+ */
+int skl_dsp_strip_extended_manifest(struct firmware *fw)
+{
+ struct skl_ext_manifest_hdr *hdr;
+
+ /* check if fw file is greater than header we are looking */
+ if (fw->size < sizeof(hdr)) {
+ pr_err("%s: Firmware file small, no hdr\n", __func__);
+ return -EINVAL;
+ }
+
+ hdr = (struct skl_ext_manifest_hdr *)fw->data;
+
+ if (hdr->id == SKL_EXT_MANIFEST_HEADER_MAGIC) {
+ fw->size -= hdr->len;
+ fw->data += hdr->len;
+ }
+
+ return 0;
+}
diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c
index 13ec8d53b526..8fc3178bc79c 100644
--- a/sound/soc/intel/skylake/skl-sst.c
+++ b/sound/soc/intel/skylake/skl-sst.c
@@ -68,10 +68,13 @@ static int skl_transfer_firmware(struct sst_dsp *ctx,
return ret;
}
+#define SKL_ADSP_FW_BIN_HDR_OFFSET 0x284
+
static int skl_load_base_firmware(struct sst_dsp *ctx)
{
int ret = 0, i;
struct skl_sst *skl = ctx->thread_context;
+ struct firmware stripped_fw;
u32 reg;
skl->boot_complete = false;
@@ -81,20 +84,36 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
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;
}
}
+ /* prase uuids on first boot */
+ if (skl->is_first_boot) {
+ ret = snd_skl_parse_uuids(ctx, ctx->fw, SKL_ADSP_FW_BIN_HDR_OFFSET, 0);
+ if (ret < 0) {
+ dev_err(ctx->dev, "UUID parsing err: %d\n", ret);
+ release_firmware(ctx->fw);
+ skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
+ return ret;
+ }
+ }
+
+ /* check for extended manifest */
+ stripped_fw.data = ctx->fw->data;
+ stripped_fw.size = ctx->fw->size;
+
+ skl_dsp_strip_extended_manifest(&stripped_fw);
+
ret = skl_dsp_boot(ctx);
if (ret < 0) {
- dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret);
+ dev_err(ctx->dev, "Boot dsp core failed ret: %d\n", 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);
+ dev_err(ctx->dev, "CL dma prepare failed : %d\n", ret);
goto skl_load_base_firmware_failed;
}
@@ -119,7 +138,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
goto transfer_firmware_failed;
}
- ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size);
+ ret = skl_transfer_firmware(ctx, stripped_fw.data, stripped_fw.size);
if (ret < 0) {
dev_err(ctx->dev, "Transfer firmware failed%d\n", ret);
goto transfer_firmware_failed;
@@ -133,67 +152,87 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
}
dev_dbg(ctx->dev, "Download firmware successful%d\n", ret);
- skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
+ skl->fw_loaded = true;
}
return 0;
transfer_firmware_failed:
ctx->cl_dev.ops.cl_cleanup_controller(ctx);
skl_load_base_firmware_failed:
- skl_dsp_disable_core(ctx);
+ skl_dsp_disable_core(ctx, SKL_DSP_CORE0_MASK);
release_firmware(ctx->fw);
ctx->fw = NULL;
return ret;
}
-static int skl_set_dsp_D0(struct sst_dsp *ctx)
+static int skl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
{
int ret;
+ struct skl_ipc_dxstate_info dx;
+ struct skl_sst *skl = ctx->thread_context;
+ unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
- ret = skl_load_base_firmware(ctx);
- if (ret < 0) {
- dev_err(ctx->dev, "unable to load firmware\n");
- return ret;
+ /* If core0 is being turned on, we need to load the FW */
+ if (core_id == SKL_DSP_CORE0_ID) {
+ 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);
+ /*
+ * If any core other than core 0 is being moved to D0, enable the
+ * core and send the set dx IPC for the core.
+ */
+ if (core_id != SKL_DSP_CORE0_ID) {
+ ret = skl_dsp_enable_core(ctx, core_mask);
+ if (ret < 0)
+ return ret;
+
+ dx.core_mask = core_mask;
+ dx.dx_mask = core_mask;
+
+ ret = skl_ipc_set_dx(&skl->ipc, SKL_INSTANCE_ID,
+ SKL_BASE_FW_MODULE_ID, &dx);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to set dsp to D0:core id= %d\n",
+ core_id);
+ skl_dsp_disable_core(ctx, core_mask);
+ }
+ }
+
+ skl->cores.state[core_id] = SKL_DSP_RUNNING;
return ret;
}
-static int skl_set_dsp_D3(struct sst_dsp *ctx)
+static int skl_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
{
int ret;
struct skl_ipc_dxstate_info dx;
struct skl_sst *skl = ctx->thread_context;
+ unsigned int core_mask = SKL_DSP_CORE_MASK(core_id);
- 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.core_mask = core_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;
+ dev_err(ctx->dev, "set Dx core %d fail: %d\n", core_id, ret);
+
+ if (core_id == SKL_DSP_CORE0_ID) {
+ /* 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);
}
- skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
+ ret = skl_dsp_disable_core(ctx, core_mask);
+ if (ret < 0)
+ return ret;
+
+ skl->cores.state[core_id] = SKL_DSP_RESET;
return ret;
}
@@ -360,6 +399,19 @@ static int skl_unload_module(struct sst_dsp *ctx, u16 mod_id)
return ret;
}
+void skl_clear_module_cnt(struct sst_dsp *ctx)
+{
+ struct skl_module_table *module;
+
+ if (list_empty(&ctx->module_list))
+ return;
+
+ list_for_each_entry(module, &ctx->module_list, list) {
+ module->usage_cnt = 0;
+ }
+}
+EXPORT_SYMBOL_GPL(skl_clear_module_cnt);
+
static void skl_clear_module_table(struct sst_dsp *ctx)
{
struct skl_module_table *module, *tmp;
@@ -409,6 +461,7 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
skl->dev = dev;
skl_dev.thread_context = skl;
+ INIT_LIST_HEAD(&skl->uuid_list);
skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
if (!skl->dsp) {
@@ -432,26 +485,38 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
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;
- }
+ skl->cores.count = 2;
+ skl->is_first_boot = true;
if (dsp)
*dsp = skl;
return ret;
-
-cleanup:
- skl_sst_dsp_cleanup(dev, skl);
- return ret;
}
EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
+int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx)
+{
+ int ret;
+ struct sst_dsp *sst = ctx->dsp;
+
+ ret = sst->fw_ops.load_fw(sst);
+ if (ret < 0) {
+ dev_err(dev, "Load base fw failed : %d\n", ret);
+ return ret;
+ }
+
+ skl_dsp_init_core_state(sst);
+ ctx->is_first_boot = false;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(skl_sst_init_fw);
+
void skl_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
{
skl_clear_module_table(ctx->dsp);
+ skl_freeup_uuid_list(ctx);
skl_ipc_free(&ctx->ipc);
ctx->dsp->ops->free(ctx->dsp);
if (ctx->boot_complete) {
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index 3e036b0349b9..b5b1934d8550 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -21,6 +21,7 @@
#include <linux/firmware.h>
#include <sound/soc.h>
#include <sound/soc-topology.h>
+#include <uapi/sound/snd_sst_tokens.h>
#include "skl-sst-dsp.h"
#include "skl-sst-ipc.h"
#include "skl-topology.h"
@@ -32,6 +33,8 @@
#define SKL_CH_FIXUP_MASK (1 << 0)
#define SKL_RATE_FIXUP_MASK (1 << 1)
#define SKL_FMT_FIXUP_MASK (1 << 2)
+#define SKL_IN_DIR_BIT_MASK BIT(0)
+#define SKL_PIN_COUNT_MASK GENMASK(7, 4)
/*
* SKL DSP driver modelling uses only few DAPM widgets so for rest we will
@@ -379,43 +382,6 @@ static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w,
}
/*
- * 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.
@@ -448,7 +414,7 @@ static int skl_tplg_set_module_params(struct snd_soc_dapm_widget *w,
if (bc->set_params == SKL_PARAM_SET) {
ret = skl_set_module_params(ctx,
- (u32 *)bc->params, bc->max,
+ (u32 *)bc->params, bc->size,
bc->param_id, mconfig);
if (ret < 0)
return ret;
@@ -483,7 +449,7 @@ static int skl_tplg_set_module_init_data(struct snd_soc_dapm_widget *w)
continue;
mconfig->formats_config.caps = (u32 *)&bc->params;
- mconfig->formats_config.caps_size = bc->max;
+ mconfig->formats_config.caps_size = bc->size;
break;
}
@@ -510,12 +476,18 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
w = w_module->w;
mconfig = w->priv;
+ /* check if module ids are populated */
+ if (mconfig->id.module_id < 0) {
+ dev_err(skl->skl_sst->dev,
+ "module %pUL id not populated\n",
+ (uuid_le *)mconfig->guid);
+ return -EIO;
+ }
+
/* check resource available */
if (!skl_is_pipe_mcps_avail(skl, mconfig))
return -ENOMEM;
- skl_tplg_alloc_pipe_mcps(skl, mconfig);
-
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);
@@ -533,12 +505,16 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
* FE/BE params
*/
skl_tplg_update_module_params(w, ctx);
-
+ mconfig->id.pvt_id = skl_get_pvt_id(ctx, mconfig);
+ if (mconfig->id.pvt_id < 0)
+ return ret;
skl_tplg_set_module_init_data(w);
ret = skl_init_module(ctx, mconfig);
- if (ret < 0)
+ if (ret < 0) {
+ skl_put_pvt_id(ctx, mconfig);
return ret;
-
+ }
+ skl_tplg_alloc_pipe_mcps(skl, mconfig);
ret = skl_tplg_set_module_params(w, ctx);
if (ret < 0)
return ret;
@@ -550,6 +526,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
struct skl_pipe *pipe)
{
+ int ret;
struct skl_pipe_module *w_module = NULL;
struct skl_module_cfg *mconfig = NULL;
@@ -557,9 +534,13 @@ static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
mconfig = w_module->w->priv;
if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod &&
- mconfig->m_state > SKL_MODULE_UNINIT)
- return ctx->dsp->fw_ops.unload_mod(ctx->dsp,
+ mconfig->m_state > SKL_MODULE_UNINIT) {
+ ret = ctx->dsp->fw_ops.unload_mod(ctx->dsp,
mconfig->id.module_id);
+ if (ret < 0)
+ return -EIO;
+ }
+ skl_put_pvt_id(ctx, mconfig);
}
/* no modules to unload in this path, so return */
@@ -591,9 +572,6 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
if (!skl_is_pipe_mem_avail(skl, mconfig))
return -ENOMEM;
- skl_tplg_alloc_pipe_mem(skl, mconfig);
- skl_tplg_alloc_pipe_mcps(skl, mconfig);
-
/*
* Create a list of modules for pipe.
* This list contains modules from source to sink
@@ -602,19 +580,8 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
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;
- }
+ skl_tplg_alloc_pipe_mem(skl, mconfig);
+ skl_tplg_alloc_pipe_mcps(skl, mconfig);
/* Init all pipe modules from source to sink */
ret = skl_tplg_init_pipe_modules(skl, s_pipe);
@@ -640,6 +607,26 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
return 0;
}
+static int skl_fill_sink_instance_id(struct skl_sst *ctx,
+ struct skl_algo_data *alg_data)
+{
+ struct skl_kpb_params *params = (struct skl_kpb_params *)alg_data->params;
+ struct skl_mod_inst_map *inst;
+ int i, pvt_id;
+
+ inst = params->map;
+
+ for (i = 0; i < params->num_modules; i++) {
+ pvt_id = skl_get_pvt_instance_id_map(ctx,
+ inst->mod_id, inst->inst_id);
+ if (pvt_id < 0)
+ return -EINVAL;
+ inst->inst_id = pvt_id;
+ inst++;
+ }
+ return 0;
+}
+
/*
* Some modules require params to be set after the module is bound to
* all pins connected.
@@ -688,6 +675,8 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w,
bc = (struct skl_algo_data *)sb->dobj.private;
if (bc->set_params == SKL_PARAM_BIND) {
+ if (mconfig->m_type == SKL_MODULE_TYPE_KPB)
+ skl_fill_sink_instance_id(ctx, bc);
ret = skl_set_module_params(ctx,
(u32 *)bc->params, bc->max,
bc->param_id, mconfig);
@@ -949,13 +938,17 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
struct skl_pipe *s_pipe = mconfig->pipe;
int ret = 0;
+ if (s_pipe->state == SKL_PIPE_INVALID)
+ return -EINVAL;
+
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 (mconfig->m_state >= SKL_MODULE_INIT_DONE)
+ skl_tplg_free_pipe_mcps(skl, dst_module);
if (src_module == NULL) {
src_module = dst_module;
continue;
@@ -1102,7 +1095,7 @@ static int skl_tplg_tlv_control_get(struct snd_kcontrol *kcontrol,
if (w->power)
skl_get_module_params(skl->skl_sst, (u32 *)bc->params,
- bc->max, bc->param_id, mconfig);
+ bc->size, bc->param_id, mconfig);
/* decrement size for TLV header */
size -= 2 * sizeof(u32);
@@ -1136,6 +1129,10 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
struct skl *skl = get_skl_ctx(w->dapm->dev);
if (ac->params) {
+ if (size > ac->max)
+ return -EINVAL;
+
+ ac->size = size;
/*
* if the param_is is of type Vendor, firmware expects actual
* parameter id and size from the control.
@@ -1151,7 +1148,7 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
if (w->power)
return skl_set_module_params(skl->skl_sst,
- (u32 *)ac->params, ac->max,
+ (u32 *)ac->params, ac->size,
ac->param_id, mconfig);
}
@@ -1159,6 +1156,39 @@ static int skl_tplg_tlv_control_set(struct snd_kcontrol *kcontrol,
}
/*
+ * Fill the dma id for host and link. In case of passthrough
+ * pipeline, this will both host and link in the same
+ * pipeline, so need to copy the link and host based on dev_type
+ */
+static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg,
+ struct skl_pipe_params *params)
+{
+ struct skl_pipe *pipe = mcfg->pipe;
+
+ if (pipe->passthru) {
+ switch (mcfg->dev_type) {
+ case SKL_DEVICE_HDALINK:
+ pipe->p_params->link_dma_id = params->link_dma_id;
+ break;
+
+ case SKL_DEVICE_HDAHOST:
+ pipe->p_params->host_dma_id = params->host_dma_id;
+ break;
+
+ default:
+ break;
+ }
+ pipe->p_params->s_fmt = params->s_fmt;
+ pipe->p_params->ch = params->ch;
+ pipe->p_params->s_freq = params->s_freq;
+ pipe->p_params->stream = params->stream;
+
+ } else {
+ memcpy(pipe->p_params, params, sizeof(*params));
+ }
+}
+
+/*
* 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
@@ -1168,10 +1198,9 @@ 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));
+ skl_tplg_fill_dma_id(mconfig, params);
if (params->stream == SNDRV_PCM_STREAM_PLAYBACK)
format = &mconfig->in_fmt[0];
@@ -1358,12 +1387,11 @@ 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));
+ skl_tplg_fill_dma_id(mconfig, params);
if (link_type == NHLT_LINK_HDA)
return 0;
@@ -1473,85 +1501,619 @@ static const struct snd_soc_tplg_bytes_ext_ops skl_tlv_ops[] = {
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)
+static int skl_tplg_fill_pipe_tkn(struct device *dev,
+ struct skl_pipe *pipe, u32 tkn,
+ u32 tkn_val)
{
- 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;
+ switch (tkn) {
+ case SKL_TKN_U32_PIPE_CONN_TYPE:
+ pipe->conn_type = tkn_val;
+ break;
+
+ case SKL_TKN_U32_PIPE_PRIORITY:
+ pipe->pipe_priority = tkn_val;
+ break;
+
+ case SKL_TKN_U32_PIPE_MEM_PGS:
+ pipe->memory_pages = tkn_val;
+ break;
+
+ default:
+ dev_err(dev, "Token not handled %d\n", tkn);
+ return -EINVAL;
}
+
+ return 0;
}
/*
- * 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
+ * Add pipeline by parsing the relevant tokens
+ * Return an existing pipe if the pipe already exists.
*/
-static struct skl_pipe *skl_tplg_add_pipe(struct device *dev,
- struct skl *skl, struct skl_dfw_pipe *dfw_pipe)
+static int skl_tplg_add_pipe(struct device *dev,
+ struct skl_module_cfg *mconfig, struct skl *skl,
+ struct snd_soc_tplg_vendor_value_elem *tkn_elem)
{
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;
+ if (ppl->pipe->ppl_id == tkn_elem->value) {
+ mconfig->pipe = ppl->pipe;
+ return EEXIST;
+ }
}
ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL);
if (!ppl)
- return NULL;
+ return -ENOMEM;
pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL);
if (!pipe)
- return NULL;
+ return -ENOMEM;
params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL);
if (!params)
- return NULL;
+ return -ENOMEM;
- 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;
+ pipe->ppl_id = tkn_elem->value;
INIT_LIST_HEAD(&pipe->w_list);
ppl->pipe = pipe;
list_add(&ppl->node, &skl->ppl_list);
- return ppl->pipe;
+ mconfig->pipe = pipe;
+ mconfig->pipe->state = SKL_PIPE_INVALID;
+
+ return 0;
+}
+
+static int skl_tplg_fill_pin(struct device *dev, u32 tkn,
+ struct skl_module_pin *m_pin,
+ int pin_index, u32 value)
+{
+ switch (tkn) {
+ case SKL_TKN_U32_PIN_MOD_ID:
+ m_pin[pin_index].id.module_id = value;
+ break;
+
+ case SKL_TKN_U32_PIN_INST_ID:
+ m_pin[pin_index].id.instance_id = value;
+ break;
+
+ default:
+ dev_err(dev, "%d Not a pin token\n", value);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+/*
+ * Parse for pin config specific tokens to fill up the
+ * module private data
+ */
+static int skl_tplg_fill_pins_info(struct device *dev,
+ struct skl_module_cfg *mconfig,
+ struct snd_soc_tplg_vendor_value_elem *tkn_elem,
+ int dir, int pin_count)
+{
+ int ret;
+ struct skl_module_pin *m_pin;
+
+ switch (dir) {
+ case SKL_DIR_IN:
+ m_pin = mconfig->m_in_pin;
+ break;
+
+ case SKL_DIR_OUT:
+ m_pin = mconfig->m_out_pin;
+ break;
+
+ default:
+ dev_err(dev, "Invalid direction value\n");
+ return -EINVAL;
+ }
+
+ ret = skl_tplg_fill_pin(dev, tkn_elem->token,
+ m_pin, pin_count, tkn_elem->value);
+
+ if (ret < 0)
+ return ret;
+
+ m_pin[pin_count].in_use = false;
+ m_pin[pin_count].pin_state = SKL_PIN_UNBIND;
+
+ return 0;
}
-static void skl_tplg_fill_fmt(struct skl_module_fmt *dst_fmt,
- struct skl_dfw_module_fmt *src_fmt,
- int pins)
+/*
+ * Fill up input/output module config format based
+ * on the direction
+ */
+static int skl_tplg_fill_fmt(struct device *dev,
+ struct skl_module_cfg *mconfig, u32 tkn,
+ u32 value, u32 dir, u32 pin_count)
+{
+ struct skl_module_fmt *dst_fmt;
+
+ switch (dir) {
+ case SKL_DIR_IN:
+ dst_fmt = mconfig->in_fmt;
+ dst_fmt += pin_count;
+ break;
+
+ case SKL_DIR_OUT:
+ dst_fmt = mconfig->out_fmt;
+ dst_fmt += pin_count;
+ break;
+
+ default:
+ dev_err(dev, "Invalid direction value\n");
+ return -EINVAL;
+ }
+
+ switch (tkn) {
+ case SKL_TKN_U32_FMT_CH:
+ dst_fmt->channels = value;
+ break;
+
+ case SKL_TKN_U32_FMT_FREQ:
+ dst_fmt->s_freq = value;
+ break;
+
+ case SKL_TKN_U32_FMT_BIT_DEPTH:
+ dst_fmt->bit_depth = value;
+ break;
+
+ case SKL_TKN_U32_FMT_SAMPLE_SIZE:
+ dst_fmt->valid_bit_depth = value;
+ break;
+
+ case SKL_TKN_U32_FMT_CH_CONFIG:
+ dst_fmt->ch_cfg = value;
+ break;
+
+ case SKL_TKN_U32_FMT_INTERLEAVE:
+ dst_fmt->interleaving_style = value;
+ break;
+
+ case SKL_TKN_U32_FMT_SAMPLE_TYPE:
+ dst_fmt->sample_type = value;
+ break;
+
+ case SKL_TKN_U32_FMT_CH_MAP:
+ dst_fmt->ch_map = value;
+ break;
+
+ default:
+ dev_err(dev, "Invalid token %d\n", tkn);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int skl_tplg_get_uuid(struct device *dev, struct skl_module_cfg *mconfig,
+ struct snd_soc_tplg_vendor_uuid_elem *uuid_tkn)
+{
+ if (uuid_tkn->token == SKL_TKN_UUID)
+ memcpy(&mconfig->guid, &uuid_tkn->uuid, 16);
+ else {
+ dev_err(dev, "Not an UUID token tkn %d\n", uuid_tkn->token);
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static void skl_tplg_fill_pin_dynamic_val(
+ struct skl_module_pin *mpin, u32 pin_count, u32 value)
{
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;
+ for (i = 0; i < pin_count; i++)
+ mpin[i].is_dynamic = value;
+}
+
+/*
+ * Parse tokens to fill up the module private data
+ */
+static int skl_tplg_get_token(struct device *dev,
+ struct snd_soc_tplg_vendor_value_elem *tkn_elem,
+ struct skl *skl, struct skl_module_cfg *mconfig)
+{
+ int tkn_count = 0;
+ int ret;
+ static int is_pipe_exists;
+ static int pin_index, dir;
+
+ if (tkn_elem->token > SKL_TKN_MAX)
+ return -EINVAL;
+
+ switch (tkn_elem->token) {
+ case SKL_TKN_U8_IN_QUEUE_COUNT:
+ mconfig->max_in_queue = tkn_elem->value;
+ mconfig->m_in_pin = devm_kzalloc(dev, mconfig->max_in_queue *
+ sizeof(*mconfig->m_in_pin),
+ GFP_KERNEL);
+ if (!mconfig->m_in_pin)
+ return -ENOMEM;
+
+ break;
+
+ case SKL_TKN_U8_OUT_QUEUE_COUNT:
+ mconfig->max_out_queue = tkn_elem->value;
+ mconfig->m_out_pin = devm_kzalloc(dev, mconfig->max_out_queue *
+ sizeof(*mconfig->m_out_pin),
+ GFP_KERNEL);
+
+ if (!mconfig->m_out_pin)
+ return -ENOMEM;
+
+ break;
+
+ case SKL_TKN_U8_DYN_IN_PIN:
+ if (!mconfig->m_in_pin)
+ return -ENOMEM;
+
+ skl_tplg_fill_pin_dynamic_val(mconfig->m_in_pin,
+ mconfig->max_in_queue, tkn_elem->value);
+
+ break;
+
+ case SKL_TKN_U8_DYN_OUT_PIN:
+ if (!mconfig->m_out_pin)
+ return -ENOMEM;
+
+ skl_tplg_fill_pin_dynamic_val(mconfig->m_out_pin,
+ mconfig->max_out_queue, tkn_elem->value);
+
+ break;
+
+ case SKL_TKN_U8_TIME_SLOT:
+ mconfig->time_slot = tkn_elem->value;
+ break;
+
+ case SKL_TKN_U8_CORE_ID:
+ mconfig->core_id = tkn_elem->value;
+
+ case SKL_TKN_U8_MOD_TYPE:
+ mconfig->m_type = tkn_elem->value;
+ break;
+
+ case SKL_TKN_U8_DEV_TYPE:
+ mconfig->dev_type = tkn_elem->value;
+ break;
+
+ case SKL_TKN_U8_HW_CONN_TYPE:
+ mconfig->hw_conn_type = tkn_elem->value;
+ break;
+
+ case SKL_TKN_U16_MOD_INST_ID:
+ mconfig->id.instance_id =
+ tkn_elem->value;
+ break;
+
+ case SKL_TKN_U32_MEM_PAGES:
+ mconfig->mem_pages = tkn_elem->value;
+ break;
+
+ case SKL_TKN_U32_MAX_MCPS:
+ mconfig->mcps = tkn_elem->value;
+ break;
+
+ case SKL_TKN_U32_OBS:
+ mconfig->obs = tkn_elem->value;
+ break;
+
+ case SKL_TKN_U32_IBS:
+ mconfig->ibs = tkn_elem->value;
+ break;
+
+ case SKL_TKN_U32_VBUS_ID:
+ mconfig->vbus_id = tkn_elem->value;
+ break;
+
+ case SKL_TKN_U32_PARAMS_FIXUP:
+ mconfig->params_fixup = tkn_elem->value;
+ break;
+
+ case SKL_TKN_U32_CONVERTER:
+ mconfig->converter = tkn_elem->value;
+ break;
+
+ case SKL_TKN_U32_PIPE_ID:
+ ret = skl_tplg_add_pipe(dev,
+ mconfig, skl, tkn_elem);
+
+ if (ret < 0)
+ return is_pipe_exists;
+
+ if (ret == EEXIST)
+ is_pipe_exists = 1;
+
+ break;
+
+ case SKL_TKN_U32_PIPE_CONN_TYPE:
+ case SKL_TKN_U32_PIPE_PRIORITY:
+ case SKL_TKN_U32_PIPE_MEM_PGS:
+ if (is_pipe_exists) {
+ ret = skl_tplg_fill_pipe_tkn(dev, mconfig->pipe,
+ tkn_elem->token, tkn_elem->value);
+ if (ret < 0)
+ return ret;
+ }
+
+ break;
+
+ /*
+ * SKL_TKN_U32_DIR_PIN_COUNT token has the value for both
+ * direction and the pin count. The first four bits represent
+ * direction and next four the pin count.
+ */
+ case SKL_TKN_U32_DIR_PIN_COUNT:
+ dir = tkn_elem->value & SKL_IN_DIR_BIT_MASK;
+ pin_index = (tkn_elem->value &
+ SKL_PIN_COUNT_MASK) >> 4;
+
+ break;
+
+ case SKL_TKN_U32_FMT_CH:
+ case SKL_TKN_U32_FMT_FREQ:
+ case SKL_TKN_U32_FMT_BIT_DEPTH:
+ case SKL_TKN_U32_FMT_SAMPLE_SIZE:
+ case SKL_TKN_U32_FMT_CH_CONFIG:
+ case SKL_TKN_U32_FMT_INTERLEAVE:
+ case SKL_TKN_U32_FMT_SAMPLE_TYPE:
+ case SKL_TKN_U32_FMT_CH_MAP:
+ ret = skl_tplg_fill_fmt(dev, mconfig, tkn_elem->token,
+ tkn_elem->value, dir, pin_index);
+
+ if (ret < 0)
+ return ret;
+
+ break;
+
+ case SKL_TKN_U32_PIN_MOD_ID:
+ case SKL_TKN_U32_PIN_INST_ID:
+ ret = skl_tplg_fill_pins_info(dev,
+ mconfig, tkn_elem, dir,
+ pin_index);
+ if (ret < 0)
+ return ret;
+
+ break;
+
+ case SKL_TKN_U32_CAPS_SIZE:
+ mconfig->formats_config.caps_size =
+ tkn_elem->value;
+
+ break;
+
+ case SKL_TKN_U32_PROC_DOMAIN:
+ mconfig->domain =
+ tkn_elem->value;
+
+ break;
+
+ case SKL_TKN_U8_IN_PIN_TYPE:
+ case SKL_TKN_U8_OUT_PIN_TYPE:
+ case SKL_TKN_U8_CONN_TYPE:
+ break;
+
+ default:
+ dev_err(dev, "Token %d not handled\n",
+ tkn_elem->token);
+ return -EINVAL;
}
+
+ tkn_count++;
+
+ return tkn_count;
+}
+
+/*
+ * Parse the vendor array for specific tokens to construct
+ * module private data
+ */
+static int skl_tplg_get_tokens(struct device *dev,
+ char *pvt_data, struct skl *skl,
+ struct skl_module_cfg *mconfig, int block_size)
+{
+ struct snd_soc_tplg_vendor_array *array;
+ struct snd_soc_tplg_vendor_value_elem *tkn_elem;
+ int tkn_count = 0, ret;
+ int off = 0, tuple_size = 0;
+
+ if (block_size <= 0)
+ return -EINVAL;
+
+ while (tuple_size < block_size) {
+ array = (struct snd_soc_tplg_vendor_array *)(pvt_data + off);
+
+ off += array->size;
+
+ switch (array->type) {
+ case SND_SOC_TPLG_TUPLE_TYPE_STRING:
+ dev_warn(dev, "no string tokens expected for skl tplg\n");
+ continue;
+
+ case SND_SOC_TPLG_TUPLE_TYPE_UUID:
+ ret = skl_tplg_get_uuid(dev, mconfig, array->uuid);
+ if (ret < 0)
+ return ret;
+
+ tuple_size += sizeof(*array->uuid);
+
+ continue;
+
+ default:
+ tkn_elem = array->value;
+ tkn_count = 0;
+ break;
+ }
+
+ while (tkn_count <= (array->num_elems - 1)) {
+ ret = skl_tplg_get_token(dev, tkn_elem,
+ skl, mconfig);
+
+ if (ret < 0)
+ return ret;
+
+ tkn_count = tkn_count + ret;
+ tkn_elem++;
+ }
+
+ tuple_size += tkn_count * sizeof(*tkn_elem);
+ }
+
+ return 0;
+}
+
+/*
+ * Every data block is preceded by a descriptor to read the number
+ * of data blocks, they type of the block and it's size
+ */
+static int skl_tplg_get_desc_blocks(struct device *dev,
+ struct snd_soc_tplg_vendor_array *array)
+{
+ struct snd_soc_tplg_vendor_value_elem *tkn_elem;
+
+ tkn_elem = array->value;
+
+ switch (tkn_elem->token) {
+ case SKL_TKN_U8_NUM_BLOCKS:
+ case SKL_TKN_U8_BLOCK_TYPE:
+ case SKL_TKN_U16_BLOCK_SIZE:
+ return tkn_elem->value;
+
+ default:
+ dev_err(dev, "Invalid descriptor token %d\n", tkn_elem->token);
+ break;
+ }
+
+ return -EINVAL;
+}
+
+/*
+ * Parse the private data for the token and corresponding value.
+ * The private data can have multiple data blocks. So, a data block
+ * is preceded by a descriptor for number of blocks and a descriptor
+ * for the type and size of the suceeding data block.
+ */
+static int skl_tplg_get_pvt_data(struct snd_soc_tplg_dapm_widget *tplg_w,
+ struct skl *skl, struct device *dev,
+ struct skl_module_cfg *mconfig)
+{
+ struct snd_soc_tplg_vendor_array *array;
+ int num_blocks, block_size = 0, block_type, off = 0;
+ char *data;
+ int ret;
+
+ /* Read the NUM_DATA_BLOCKS descriptor */
+ array = (struct snd_soc_tplg_vendor_array *)tplg_w->priv.data;
+ ret = skl_tplg_get_desc_blocks(dev, array);
+ if (ret < 0)
+ return ret;
+ num_blocks = ret;
+
+ off += array->size;
+ array = (struct snd_soc_tplg_vendor_array *)(tplg_w->priv.data + off);
+
+ /* Read the BLOCK_TYPE and BLOCK_SIZE descriptor */
+ while (num_blocks > 0) {
+ ret = skl_tplg_get_desc_blocks(dev, array);
+
+ if (ret < 0)
+ return ret;
+ block_type = ret;
+ off += array->size;
+
+ array = (struct snd_soc_tplg_vendor_array *)
+ (tplg_w->priv.data + off);
+
+ ret = skl_tplg_get_desc_blocks(dev, array);
+
+ if (ret < 0)
+ return ret;
+ block_size = ret;
+ off += array->size;
+
+ array = (struct snd_soc_tplg_vendor_array *)
+ (tplg_w->priv.data + off);
+
+ data = (tplg_w->priv.data + off);
+
+ if (block_type == SKL_TYPE_TUPLE) {
+ ret = skl_tplg_get_tokens(dev, data,
+ skl, mconfig, block_size);
+
+ if (ret < 0)
+ return ret;
+
+ --num_blocks;
+ } else {
+ if (mconfig->formats_config.caps_size > 0)
+ memcpy(mconfig->formats_config.caps, data,
+ mconfig->formats_config.caps_size);
+ --num_blocks;
+ }
+ }
+
+ return 0;
+}
+
+static void skl_clear_pin_config(struct snd_soc_platform *platform,
+ struct snd_soc_dapm_widget *w)
+{
+ int i;
+ struct skl_module_cfg *mconfig;
+ struct skl_pipe *pipe;
+
+ if (!strncmp(w->dapm->component->name, platform->component.name,
+ strlen(platform->component.name))) {
+ mconfig = w->priv;
+ pipe = mconfig->pipe;
+ for (i = 0; i < mconfig->max_in_queue; i++) {
+ mconfig->m_in_pin[i].in_use = false;
+ mconfig->m_in_pin[i].pin_state = SKL_PIN_UNBIND;
+ }
+ for (i = 0; i < mconfig->max_out_queue; i++) {
+ mconfig->m_out_pin[i].in_use = false;
+ mconfig->m_out_pin[i].pin_state = SKL_PIN_UNBIND;
+ }
+ pipe->state = SKL_PIPE_INVALID;
+ mconfig->m_state = SKL_MODULE_UNINIT;
+ }
+}
+
+void skl_cleanup_resources(struct skl *skl)
+{
+ struct skl_sst *ctx = skl->skl_sst;
+ struct snd_soc_platform *soc_platform = skl->platform;
+ struct snd_soc_dapm_widget *w;
+ struct snd_soc_card *card;
+
+ if (soc_platform == NULL)
+ return;
+
+ card = soc_platform->component.card;
+ if (!card || !card->instantiated)
+ return;
+
+ skl->resource.mem = 0;
+ skl->resource.mcps = 0;
+
+ list_for_each_entry(w, &card->widgets, list) {
+ if (is_skl_dsp_widget_type(w) && (w->priv != NULL))
+ skl_clear_pin_config(soc_platform, w);
+ }
+
+ skl_clear_module_cnt(ctx->dsp);
}
/*
@@ -1570,9 +2132,6 @@ static int skl_tplg_widget_load(struct snd_soc_component *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;
@@ -1583,72 +2142,17 @@ static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
return -ENOMEM;
w->priv = mconfig;
- memcpy(&mconfig->guid, &dfw_config->uuid, 16);
-
- 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;
-
- 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;
+ /*
+ * module binary can be loaded later, so set it to query when
+ * module is load for a use case
+ */
+ mconfig->id.module_id = -1;
+ /* Parse private data for tuples */
+ ret = skl_tplg_get_pvt_data(tplg_w, skl, bus->dev, mconfig);
+ if (ret < 0)
+ return ret;
bind_event:
if (tplg_w->event_type == 0) {
dev_dbg(bus->dev, "ASoC: No event handler required\n");
@@ -1683,6 +2187,7 @@ static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be,
ac->max = dfw_ac->max;
ac->param_id = dfw_ac->param_id;
ac->set_params = dfw_ac->set_params;
+ ac->size = dfw_ac->max;
if (ac->max) {
ac->params = (char *) devm_kzalloc(dev, ac->max, GFP_KERNEL);
@@ -1726,13 +2231,285 @@ static int skl_tplg_control_load(struct snd_soc_component *cmpnt,
return 0;
}
+static int skl_tplg_fill_str_mfest_tkn(struct device *dev,
+ struct snd_soc_tplg_vendor_string_elem *str_elem,
+ struct skl_dfw_manifest *minfo)
+{
+ int tkn_count = 0;
+ static int ref_count;
+
+ switch (str_elem->token) {
+ case SKL_TKN_STR_LIB_NAME:
+ if (ref_count > minfo->lib_count - 1) {
+ ref_count = 0;
+ return -EINVAL;
+ }
+
+ strncpy(minfo->lib[ref_count].name, str_elem->string,
+ ARRAY_SIZE(minfo->lib[ref_count].name));
+ ref_count++;
+ tkn_count++;
+ break;
+
+ default:
+ dev_err(dev, "Not a string token %d\n", str_elem->token);
+ break;
+ }
+
+ return tkn_count;
+}
+
+static int skl_tplg_get_str_tkn(struct device *dev,
+ struct snd_soc_tplg_vendor_array *array,
+ struct skl_dfw_manifest *minfo)
+{
+ int tkn_count = 0, ret;
+ struct snd_soc_tplg_vendor_string_elem *str_elem;
+
+ str_elem = (struct snd_soc_tplg_vendor_string_elem *)array->value;
+ while (tkn_count < array->num_elems) {
+ ret = skl_tplg_fill_str_mfest_tkn(dev, str_elem, minfo);
+ str_elem++;
+
+ if (ret < 0)
+ return ret;
+
+ tkn_count = tkn_count + ret;
+ }
+
+ return tkn_count;
+}
+
+static int skl_tplg_get_int_tkn(struct device *dev,
+ struct snd_soc_tplg_vendor_value_elem *tkn_elem,
+ struct skl_dfw_manifest *minfo)
+{
+ int tkn_count = 0;
+
+ switch (tkn_elem->token) {
+ case SKL_TKN_U32_LIB_COUNT:
+ minfo->lib_count = tkn_elem->value;
+ tkn_count++;
+ break;
+
+ default:
+ dev_err(dev, "Not a manifest token %d\n", tkn_elem->token);
+ return -EINVAL;
+ }
+
+ return tkn_count;
+}
+
+/*
+ * Fill the manifest structure by parsing the tokens based on the
+ * type.
+ */
+static int skl_tplg_get_manifest_tkn(struct device *dev,
+ char *pvt_data, struct skl_dfw_manifest *minfo,
+ int block_size)
+{
+ int tkn_count = 0, ret;
+ int off = 0, tuple_size = 0;
+ struct snd_soc_tplg_vendor_array *array;
+ struct snd_soc_tplg_vendor_value_elem *tkn_elem;
+
+ if (block_size <= 0)
+ return -EINVAL;
+
+ while (tuple_size < block_size) {
+ array = (struct snd_soc_tplg_vendor_array *)(pvt_data + off);
+ off += array->size;
+ switch (array->type) {
+ case SND_SOC_TPLG_TUPLE_TYPE_STRING:
+ ret = skl_tplg_get_str_tkn(dev, array, minfo);
+
+ if (ret < 0)
+ return ret;
+ tkn_count += ret;
+
+ tuple_size += tkn_count *
+ sizeof(struct snd_soc_tplg_vendor_string_elem);
+ continue;
+
+ case SND_SOC_TPLG_TUPLE_TYPE_UUID:
+ dev_warn(dev, "no uuid tokens for skl tplf manifest\n");
+ continue;
+
+ default:
+ tkn_elem = array->value;
+ tkn_count = 0;
+ break;
+ }
+
+ while (tkn_count <= array->num_elems - 1) {
+ ret = skl_tplg_get_int_tkn(dev,
+ tkn_elem, minfo);
+ if (ret < 0)
+ return ret;
+
+ tkn_count = tkn_count + ret;
+ tkn_elem++;
+ tuple_size += tkn_count *
+ sizeof(struct snd_soc_tplg_vendor_value_elem);
+ break;
+ }
+ tkn_count = 0;
+ }
+
+ return 0;
+}
+
+/*
+ * Parse manifest private data for tokens. The private data block is
+ * preceded by descriptors for type and size of data block.
+ */
+static int skl_tplg_get_manifest_data(struct snd_soc_tplg_manifest *manifest,
+ struct device *dev, struct skl_dfw_manifest *minfo)
+{
+ struct snd_soc_tplg_vendor_array *array;
+ int num_blocks, block_size = 0, block_type, off = 0;
+ char *data;
+ int ret;
+
+ /* Read the NUM_DATA_BLOCKS descriptor */
+ array = (struct snd_soc_tplg_vendor_array *)manifest->priv.data;
+ ret = skl_tplg_get_desc_blocks(dev, array);
+ if (ret < 0)
+ return ret;
+ num_blocks = ret;
+
+ off += array->size;
+ array = (struct snd_soc_tplg_vendor_array *)
+ (manifest->priv.data + off);
+
+ /* Read the BLOCK_TYPE and BLOCK_SIZE descriptor */
+ while (num_blocks > 0) {
+ ret = skl_tplg_get_desc_blocks(dev, array);
+
+ if (ret < 0)
+ return ret;
+ block_type = ret;
+ off += array->size;
+
+ array = (struct snd_soc_tplg_vendor_array *)
+ (manifest->priv.data + off);
+
+ ret = skl_tplg_get_desc_blocks(dev, array);
+
+ if (ret < 0)
+ return ret;
+ block_size = ret;
+ off += array->size;
+
+ array = (struct snd_soc_tplg_vendor_array *)
+ (manifest->priv.data + off);
+
+ data = (manifest->priv.data + off);
+
+ if (block_type == SKL_TYPE_TUPLE) {
+ ret = skl_tplg_get_manifest_tkn(dev, data, minfo,
+ block_size);
+
+ if (ret < 0)
+ return ret;
+
+ --num_blocks;
+ } else {
+ return -EINVAL;
+ }
+ }
+
+ return 0;
+}
+
+static int skl_manifest_load(struct snd_soc_component *cmpnt,
+ struct snd_soc_tplg_manifest *manifest)
+{
+ struct skl_dfw_manifest *minfo;
+ struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);
+ struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct skl *skl = ebus_to_skl(ebus);
+ int ret = 0;
+
+ /* proceed only if we have private data defined */
+ if (manifest->priv.size == 0)
+ return 0;
+
+ minfo = &skl->skl_sst->manifest;
+
+ skl_tplg_get_manifest_data(manifest, bus->dev, minfo);
+
+ if (minfo->lib_count > HDA_MAX_LIB) {
+ dev_err(bus->dev, "Exceeding max Library count. Got:%d\n",
+ minfo->lib_count);
+ ret = -EINVAL;
+ }
+
+ return ret;
+}
+
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),
+ .manifest = skl_manifest_load,
};
+/*
+ * 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_create_pipe_widget_list()
+ * helps to get the SKL type widgets in that pipeline
+ */
+static int skl_tplg_create_pipe_widget_list(struct snd_soc_platform *platform)
+{
+ struct snd_soc_dapm_widget *w;
+ struct skl_module_cfg *mcfg = NULL;
+ struct skl_pipe_module *p_module = NULL;
+ struct skl_pipe *pipe;
+
+ list_for_each_entry(w, &platform->component.card->widgets, list) {
+ if (is_skl_dsp_widget_type(w) && w->priv != NULL) {
+ mcfg = w->priv;
+ pipe = mcfg->pipe;
+
+ p_module = devm_kzalloc(platform->dev,
+ sizeof(*p_module), GFP_KERNEL);
+ if (!p_module)
+ return -ENOMEM;
+
+ p_module->w = w;
+ list_add_tail(&p_module->node, &pipe->w_list);
+ }
+ }
+
+ return 0;
+}
+
+static void skl_tplg_set_pipe_type(struct skl *skl, struct skl_pipe *pipe)
+{
+ struct skl_pipe_module *w_module;
+ struct snd_soc_dapm_widget *w;
+ struct skl_module_cfg *mconfig;
+ bool host_found = false, link_found = false;
+
+ list_for_each_entry(w_module, &pipe->w_list, node) {
+ w = w_module->w;
+ mconfig = w->priv;
+
+ if (mconfig->dev_type == SKL_DEVICE_HDAHOST)
+ host_found = true;
+ else if (mconfig->dev_type != SKL_DEVICE_NONE)
+ link_found = true;
+ }
+
+ if (host_found && link_found)
+ pipe->passthru = true;
+ else
+ pipe->passthru = false;
+}
+
/* This will be read from topology manifest, currently defined here */
#define SKL_MAX_MCPS 30000000
#define SKL_FW_MAX_MEM 1000000
@@ -1746,6 +2523,7 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
const struct firmware *fw;
struct hdac_bus *bus = ebus_to_hbus(ebus);
struct skl *skl = ebus_to_skl(ebus);
+ struct skl_pipeline *ppl;
ret = request_firmware(&fw, skl->tplg_name, bus->dev);
if (ret < 0) {
@@ -1775,6 +2553,12 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
skl->resource.max_mem = SKL_FW_MAX_MEM;
skl->tplg = fw;
+ ret = skl_tplg_create_pipe_widget_list(platform);
+ if (ret < 0)
+ return ret;
+
+ list_for_each_entry(ppl, &skl->ppl_list, node)
+ skl_tplg_set_pipe_type(skl, ppl->pipe);
return 0;
}
diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h
index e4b399cd7868..a519360f42a6 100644
--- a/sound/soc/intel/skylake/skl-topology.h
+++ b/sound/soc/intel/skylake/skl-topology.h
@@ -133,7 +133,7 @@ struct skl_i2s_config_blob {
struct skl_dma_control {
u32 node_id;
u32 config_length;
- u32 config_data[1];
+ u32 config_data[0];
} __packed;
struct skl_cpr_cfg {
@@ -215,9 +215,20 @@ struct skl_module_fmt {
struct skl_module_cfg;
+struct skl_mod_inst_map {
+ u16 mod_id;
+ u16 inst_id;
+};
+
+struct skl_kpb_params {
+ u32 num_modules;
+ struct skl_mod_inst_map map[0];
+};
+
struct skl_module_inst_id {
- u32 module_id;
+ int module_id;
u32 instance_id;
+ int pvt_id;
};
enum skl_module_pin_state {
@@ -244,7 +255,8 @@ enum skl_pipe_state {
SKL_PIPE_INVALID = 0,
SKL_PIPE_CREATED = 1,
SKL_PIPE_PAUSED = 2,
- SKL_PIPE_STARTED = 3
+ SKL_PIPE_STARTED = 3,
+ SKL_PIPE_RESET = 4
};
struct skl_pipe_module {
@@ -270,6 +282,7 @@ struct skl_pipe {
struct skl_pipe_params *p_params;
enum skl_pipe_state state;
struct list_head w_list;
+ bool passthru;
};
enum skl_module_state {
@@ -319,6 +332,7 @@ struct skl_algo_data {
u32 param_id;
u32 set_params;
u32 max;
+ u32 size;
char *params;
};
@@ -357,6 +371,8 @@ 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_reset_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
diff --git a/sound/soc/intel/skylake/skl-tplg-interface.h b/sound/soc/intel/skylake/skl-tplg-interface.h
index a32e5e9cc530..2f6281e056d6 100644
--- a/sound/soc/intel/skylake/skl-tplg-interface.h
+++ b/sound/soc/intel/skylake/skl-tplg-interface.h
@@ -80,7 +80,8 @@ enum skl_module_type {
SKL_MODULE_TYPE_UPDWMIX,
SKL_MODULE_TYPE_SRCINT,
SKL_MODULE_TYPE_ALGO,
- SKL_MODULE_TYPE_BASE_OUTFMT
+ SKL_MODULE_TYPE_BASE_OUTFMT,
+ SKL_MODULE_TYPE_KPB,
};
enum skl_core_affinity {
@@ -148,84 +149,34 @@ enum skl_module_param_type {
SKL_PARAM_BIND
};
-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 {
+struct skl_dfw_algo_data {
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;
+ u32 max;
+ char params[0];
} __packed;
-struct skl_dfw_module {
- u8 uuid[16];
-
- 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;
+#define LIB_NAME_LENGTH 128
+#define HDA_MAX_LIB 16
+
+struct lib_info {
+ char name[LIB_NAME_LENGTH];
} __packed;
-struct skl_dfw_algo_data {
- u32 set_params:2;
- u32 rsvd:30;
- u32 param_id;
- u32 max;
- char params[0];
+struct skl_dfw_manifest {
+ u32 lib_count;
+ struct lib_info lib[HDA_MAX_LIB];
} __packed;
+enum skl_tkn_dir {
+ SKL_DIR_IN,
+ SKL_DIR_OUT
+};
+
+enum skl_tuple_type {
+ SKL_TYPE_TUPLE,
+ SKL_TYPE_DATA
+};
+
#endif
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 06d8c263c68f..2989c164dafe 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -35,6 +35,8 @@
#include "skl-sst-dsp.h"
#include "skl-sst-ipc.h"
+static struct skl_machine_pdata skl_dmic_data;
+
/*
* initialize the PCI registers
*/
@@ -184,6 +186,7 @@ static int _skl_suspend(struct hdac_ext_bus *ebus)
{
struct skl *skl = ebus_to_skl(ebus);
struct hdac_bus *bus = ebus_to_hbus(ebus);
+ struct pci_dev *pci = to_pci_dev(bus->dev);
int ret;
snd_hdac_ext_bus_link_power_down_all(ebus);
@@ -193,9 +196,12 @@ static int _skl_suspend(struct hdac_ext_bus *ebus)
return ret;
snd_hdac_bus_stop_chip(bus);
+ update_pci_dword(pci, AZX_PCIREG_PGCTL,
+ AZX_PGCTL_LSRMD_MASK, AZX_PGCTL_LSRMD_MASK);
skl_enable_miscbdcge(bus->dev, false);
snd_hdac_bus_enter_link_reset(bus);
skl_enable_miscbdcge(bus->dev, true);
+ skl_cleanup_resources(skl);
return 0;
}
@@ -242,6 +248,7 @@ static int skl_suspend(struct device *dev)
ret = _skl_suspend(ebus);
if (ret < 0)
return ret;
+ skl->skl_sst->fw_loaded = false;
}
if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
@@ -397,6 +404,10 @@ static int skl_machine_device_register(struct skl *skl, void *driver_data)
platform_device_put(pdev);
return -EIO;
}
+
+ if (mach->pdata)
+ dev_set_drvdata(&pdev->dev, mach->pdata);
+
skl->i2s_dev = pdev;
return 0;
@@ -576,7 +587,7 @@ static int skl_first_init(struct hdac_ext_bus *ebus)
return -ENXIO;
}
- snd_hdac_ext_bus_parse_capabilities(ebus);
+ snd_hdac_bus_parse_capabilities(bus);
if (skl_acquire_irq(ebus, 0) < 0)
return -EBUSY;
@@ -657,17 +668,23 @@ static int skl_probe(struct pci_dev *pci,
skl->pci_id = pci->device;
+ device_disable_async_suspend(bus->dev);
+
skl->nhlt = skl_nhlt_init(bus->dev);
- if (skl->nhlt == NULL)
+ if (skl->nhlt == NULL) {
+ err = -ENODEV;
goto out_free;
+ }
skl_nhlt_update_topology_bin(skl);
pci_set_drvdata(skl->pci, ebus);
+ skl_dmic_data.dmic_num = skl_get_dmic_geo(skl);
+
/* check if dsp is there */
- if (ebus->ppcap) {
+ if (bus->ppcap) {
err = skl_machine_device_register(skl,
(void *)pci_id->driver_data);
if (err < 0)
@@ -681,7 +698,7 @@ static int skl_probe(struct pci_dev *pci,
skl->skl_sst->enable_miscbdcge = skl_enable_miscbdcge;
}
- if (ebus->mlcap)
+ if (bus->mlcap)
snd_hdac_ext_bus_get_ml_capabilities(ebus);
/* create device for soc dmic */
@@ -713,7 +730,7 @@ static int skl_probe(struct pci_dev *pci,
list_for_each_entry(hlink, &ebus->hlink_list, list)
snd_hdac_ext_bus_link_put(ebus, hlink);
- /*configure PM */
+ /* configure PM */
pm_runtime_put_noidle(bus->dev);
pm_runtime_allow(bus->dev);
@@ -766,8 +783,7 @@ 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);
+ release_firmware(skl->tplg);
if (pci_dev_run_wake(pci))
pm_runtime_get_noresume(&pci->dev);
@@ -786,15 +802,23 @@ static void skl_remove(struct pci_dev *pci)
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 },
+ { "INT343B", "skl_n88l25_s4567", "intel/dsp_fw_release.bin",
+ NULL, NULL, &skl_dmic_data },
+ { "MX98357A", "skl_n88l25_m98357a", "intel/dsp_fw_release.bin",
+ NULL, NULL, &skl_dmic_data },
{}
};
static struct sst_acpi_mach sst_bxtp_devdata[] = {
{ "INT343A", "bxt_alc298s_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL },
+ { "DLGS7219", "bxt_da7219_max98357a_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL },
+};
+
+static struct sst_acpi_mach sst_kbl_devdata[] = {
+ { "INT343A", "kbl_alc286s_i2s", "intel/dsp_fw_kbl.bin", NULL, NULL, NULL },
+ { "INT343B", "kbl_n88l25_s4567", "intel/dsp_fw_kbl.bin", NULL, NULL, &skl_dmic_data },
+ { "MX98357A", "kbl_n88l25_m98357a", "intel/dsp_fw_kbl.bin", NULL, NULL, &skl_dmic_data },
+ {}
};
/* PCI IDs */
@@ -805,6 +829,9 @@ static const struct pci_device_id skl_ids[] = {
/* BXT-P */
{ PCI_DEVICE(0x8086, 0x5a98),
.driver_data = (unsigned long)&sst_bxtp_devdata},
+ /* KBL */
+ { PCI_DEVICE(0x8086, 0x9D71),
+ .driver_data = (unsigned long)&sst_kbl_devdata},
{ 0, }
};
MODULE_DEVICE_TABLE(pci, skl_ids);
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index 4b4b3876aea9..5d4fbb094c48 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -48,6 +48,8 @@
#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094
#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
+#define AZX_PCIREG_PGCTL 0x44
+#define AZX_PGCTL_LSRMD_MASK (1 << 4)
#define AZX_PCIREG_CGCTL 0x48
#define AZX_CGCTL_MISCBDCGE_MASK (1 << 6)
@@ -65,6 +67,7 @@ struct skl {
unsigned int init_failed:1; /* delayed init failed */
struct platform_device *dmic_dev;
struct platform_device *i2s_dev;
+ struct snd_soc_platform *platform;
struct nhlt_acpi_table *nhlt; /* nhlt ptr */
struct skl_sst *skl_sst; /* sst skl ctx */
@@ -90,6 +93,11 @@ struct skl_dma_params {
u8 stream_tag;
};
+/* to pass dmic data */
+struct skl_machine_pdata {
+ u32 dmic_num;
+};
+
struct skl_dsp_ops {
int id;
struct skl_dsp_loader_ops (*loader_ops)(void);
@@ -97,6 +105,7 @@ struct skl_dsp_ops {
int irq, const char *fw_name,
struct skl_dsp_loader_ops loader_ops,
struct skl_sst **skl_sst);
+ int (*init_fw)(struct device *dev, struct skl_sst *ctx);
void (*cleanup)(struct device *dev, struct skl_sst *ctx);
};
@@ -108,9 +117,12 @@ void skl_nhlt_free(struct nhlt_acpi_table *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_get_dmic_geo(struct skl *skl);
int skl_nhlt_update_topology_bin(struct skl *skl);
int skl_init_dsp(struct skl *skl);
int skl_free_dsp(struct skl *skl);
int skl_suspend_dsp(struct skl *skl);
int skl_resume_dsp(struct skl *skl);
+void skl_cleanup_resources(struct skl *skl);
+const struct skl_dsp_ops *skl_get_dsp_ops(int pci_id);
#endif /* __SOUND_SOC_SKL_H */
diff --git a/sound/soc/kirkwood/kirkwood-dma.c b/sound/soc/kirkwood/kirkwood-dma.c
index dbfdfe99c69d..dafd22e874e9 100644
--- a/sound/soc/kirkwood/kirkwood-dma.c
+++ b/sound/soc/kirkwood/kirkwood-dma.c
@@ -242,7 +242,7 @@ static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
return count;
}
-static struct snd_pcm_ops kirkwood_dma_ops = {
+static const struct snd_pcm_ops kirkwood_dma_ops = {
.open = kirkwood_dma_open,
.close = kirkwood_dma_close,
.ioctl = snd_pcm_lib_ioctl,
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index 3abf51c07851..05cf809cf9e1 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -1,15 +1,40 @@
config SND_SOC_MEDIATEK
- tristate "ASoC support for Mediatek chip"
+ tristate
+
+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
+ 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_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 && I2C
+ depends on SND_SOC_MT8173 && I2C
select SND_SOC_MAX98090
help
This adds ASoC driver for Mediatek MT8173 boards
@@ -19,8 +44,9 @@ config SND_SOC_MT8173_MAX98090
config SND_SOC_MT8173_RT5650
tristate "ASoC Audio driver for MT8173 with RT5650 codec"
- depends on SND_SOC_MEDIATEK && I2C
+ 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.
@@ -29,7 +55,7 @@ config SND_SOC_MT8173_RT5650
config SND_SOC_MT8173_RT5650_RT5514
tristate "ASoC Audio driver for MT8173 with RT5650 RT5514 codecs"
- depends on SND_SOC_MEDIATEK && I2C
+ depends on SND_SOC_MT8173 && I2C
select SND_SOC_RT5645
select SND_SOC_RT5514
help
@@ -40,7 +66,7 @@ config SND_SOC_MT8173_RT5650_RT5514
config SND_SOC_MT8173_RT5650_RT5676
tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs"
- depends on SND_SOC_MEDIATEK && I2C
+ depends on SND_SOC_MT8173 && I2C
select SND_SOC_RT5645
select SND_SOC_RT5677
select SND_SOC_HDMI_CODEC
diff --git a/sound/soc/mediatek/Makefile b/sound/soc/mediatek/Makefile
index d486860c0a88..6bcab35dc828 100644
--- a/sound/soc/mediatek/Makefile
+++ b/sound/soc/mediatek/Makefile
@@ -1,7 +1,3 @@
-# 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) += 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
+obj-$(CONFIG_SND_SOC_MEDIATEK) += common/
+obj-$(CONFIG_SND_SOC_MT2701) += mt2701/
+obj-$(CONFIG_SND_SOC_MT8173) += mt8173/
diff --git a/sound/soc/mediatek/common/Makefile b/sound/soc/mediatek/common/Makefile
new file mode 100644
index 000000000000..a55d33bc7b01
--- /dev/null
+++ b/sound/soc/mediatek/common/Makefile
@@ -0,0 +1,16 @@
+#
+# Copyright (C) 2015 MediaTek 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.
+#
+
+# platform driver
+snd-soc-mtk-common-objs := mtk-afe-platform-driver.o mtk-afe-fe-dai.o
+obj-$(CONFIG_SND_SOC_MEDIATEK) += snd-soc-mtk-common.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..ac231d33d8fe
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.c
@@ -0,0 +1,380 @@
+/*
+ * mtk-afe-fe-dais.c -- Mediatek afe fe dai operator
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@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/pm_runtime.h>
+#include <linux/regmap.h>
+#include <sound/soc.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)
+{
+ if (reg < 0)
+ return 0;
+ return regmap_update_bits(map, reg, mask, val);
+}
+
+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 = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int memif_num = rtd->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;
+
+ 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 << memif->data->agent_disable_shift,
+ 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 = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+ int irq_id;
+
+ irq_id = memif->irq_usage;
+
+ mtk_regmap_update_bits(afe->regmap, memif->data->agent_disable_reg,
+ 1 << memif->data->agent_disable_shift,
+ 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 = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+ int msb_at_bit33 = 0;
+ int ret, fs = 0;
+
+ ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
+ if (ret < 0)
+ return ret;
+
+ msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
+ memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
+ memif->buffer_size = substream->runtime->dma_bytes;
+
+ /* start */
+ mtk_regmap_write(afe->regmap, memif->data->reg_ofs_base,
+ memif->phys_buf_addr);
+ /* end */
+ mtk_regmap_write(afe->regmap,
+ memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
+ memif->phys_buf_addr + memif->buffer_size - 1);
+
+ /* set MSB to 33-bit */
+ mtk_regmap_update_bits(afe->regmap, memif->data->msb_reg,
+ 1 << memif->data->msb_shift,
+ msb_at_bit33 << memif->data->msb_shift);
+
+ /* set channel */
+ if (memif->data->mono_shift >= 0) {
+ unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
+
+ mtk_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;
+
+ mtk_regmap_update_bits(afe->regmap, memif->data->fs_reg,
+ memif->data->fs_maskbit << memif->data->fs_shift,
+ fs << memif->data->fs_shift);
+
+ 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)
+{
+ return snd_pcm_lib_free_pages(substream);
+}
+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 = substream->private_data;
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->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;
+
+ 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:
+ if (memif->data->enable_shift >= 0)
+ mtk_regmap_update_bits(afe->regmap,
+ memif->data->enable_reg,
+ 1 << memif->data->enable_shift,
+ 1 << memif->data->enable_shift);
+
+ /* set irq counter */
+ mtk_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;
+
+ mtk_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 */
+ mtk_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:
+ mtk_regmap_update_bits(afe->regmap, memif->data->enable_reg,
+ 1 << memif->data->enable_shift, 0);
+ /* disable interrupt */
+ mtk_regmap_update_bits(afe->regmap, irq_data->irq_en_reg,
+ 1 << irq_data->irq_en_shift,
+ 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 0;
+ 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 = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+ int hd_audio = 0;
+
+ /* set hd mode */
+ switch (substream->runtime->format) {
+ case SNDRV_PCM_FORMAT_S16_LE:
+ hd_audio = 0;
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ hd_audio = 1;
+ break;
+ case SNDRV_PCM_FORMAT_S24_LE:
+ hd_audio = 1;
+ break;
+ default:
+ dev_err(afe->dev, "%s() error: unsupported format %d\n",
+ __func__, substream->runtime->format);
+ break;
+ }
+
+ mtk_regmap_update_bits(afe->regmap, memif->data->hd_reg,
+ 1 << memif->data->hd_shift,
+ hd_audio << memif->data->hd_shift);
+
+ 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);
+
+static DEFINE_MUTEX(irqs_lock);
+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_dai_suspend(struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ 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);
+
+ 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_dai_suspend);
+
+int mtk_afe_dai_resume(struct snd_soc_dai *dai)
+{
+ struct mtk_base_afe *afe = dev_get_drvdata(dai->dev);
+ struct device *dev = afe->dev;
+ struct regmap *regmap = afe->regmap;
+ int i = 0;
+
+ 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__);
+
+ 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_dai_resume);
+
+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..28cb17854da1
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-fe-dai.h
@@ -0,0 +1,45 @@
+/*
+ * mtk-afe-fe-dais.h -- Mediatek afe fe dai operator definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@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_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_dai_suspend(struct snd_soc_dai *dai);
+int mtk_afe_dai_resume(struct snd_soc_dai *dai);
+
+#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..82d439c15f4e
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.c
@@ -0,0 +1,90 @@
+/*
+ * mtk-afe-platform-driver.c -- Mediatek afe platform driver
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@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/dma-mapping.h>
+#include <sound/soc.h>
+
+#include "mtk-afe-platform-driver.h"
+#include "mtk-base-afe.h"
+
+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_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->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);
+}
+
+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;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+
+ size = afe->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);
+}
+
+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,
+};
+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..a973fc9253b4
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-afe-platform-driver.h
@@ -0,0 +1,23 @@
+/*
+ * mtk-afe-platform-driver.h -- Mediatek afe platform driver definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@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_PLATFORM_DRIVER_H_
+#define _MTK_AFE_PLATFORM_DRIVER_H_
+
+extern const struct snd_soc_platform_driver mtk_afe_pcm_platform;
+
+#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..3a78f6f17195
--- /dev/null
+++ b/sound/soc/mediatek/common/mtk-base-afe.h
@@ -0,0 +1,104 @@
+/*
+ * mtk-base-afe.h -- Mediatek base afe structure
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@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_BASE_AFE_H_
+#define _MTK_BASE_AFE_H_
+
+struct mtk_base_memif_data {
+ int id;
+ const char *name;
+ int reg_ofs_base;
+ int reg_ofs_cur;
+ int fs_reg;
+ int fs_shift;
+ int fs_maskbit;
+ int mono_reg;
+ int mono_shift;
+ int enable_reg;
+ int enable_shift;
+ int hd_reg;
+ int hd_shift;
+ int msb_reg;
+ int msb_shift;
+ int agent_disable_reg;
+ int agent_disable_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;
+};
+
+struct device;
+struct mtk_base_afe_memif;
+struct mtk_base_afe_irq;
+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;
+
+ 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);
+
+ 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;
+};
+
+struct mtk_base_afe_irq {
+ const struct mtk_base_irq_data *irq_data;
+ int irq_occupyed;
+};
+
+#endif
+
diff --git a/sound/soc/mediatek/mt2701/Makefile b/sound/soc/mediatek/mt2701/Makefile
new file mode 100644
index 000000000000..31c3d04d4942
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/Makefile
@@ -0,0 +1,19 @@
+#
+# Copyright (C) 2015 MediaTek 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.
+#
+
+# platform driver
+snd-soc-mt2701-afe-objs := 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
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..b815ecc6bbf6
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.c
@@ -0,0 +1,464 @@
+/*
+ * mt2701-afe-clock-ctrl.c -- Mediatek 2701 afe clock ctrl
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@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 <sound/soc.h>
+#include <linux/regmap.h>
+#include <linux/pm_runtime.h>
+
+#include "mt2701-afe-common.h"
+#include "mt2701-afe-clock-ctrl.h"
+
+static const char *aud_clks[MT2701_CLOCK_NUM] = {
+ [MT2701_AUD_INFRA_SYS_AUDIO] = "infra_sys_audio_clk",
+ [MT2701_AUD_AUD_MUX1_SEL] = "top_audio_mux1_sel",
+ [MT2701_AUD_AUD_MUX2_SEL] = "top_audio_mux2_sel",
+ [MT2701_AUD_AUD_MUX1_DIV] = "top_audio_mux1_div",
+ [MT2701_AUD_AUD_MUX2_DIV] = "top_audio_mux2_div",
+ [MT2701_AUD_AUD_48K_TIMING] = "top_audio_48k_timing",
+ [MT2701_AUD_AUD_44K_TIMING] = "top_audio_44k_timing",
+ [MT2701_AUD_AUDPLL_MUX_SEL] = "top_audpll_mux_sel",
+ [MT2701_AUD_APLL_SEL] = "top_apll_sel",
+ [MT2701_AUD_AUD1PLL_98M] = "top_aud1_pll_98M",
+ [MT2701_AUD_AUD2PLL_90M] = "top_aud2_pll_90M",
+ [MT2701_AUD_HADDS2PLL_98M] = "top_hadds2_pll_98M",
+ [MT2701_AUD_HADDS2PLL_294M] = "top_hadds2_pll_294M",
+ [MT2701_AUD_AUDPLL] = "top_audpll",
+ [MT2701_AUD_AUDPLL_D4] = "top_audpll_d4",
+ [MT2701_AUD_AUDPLL_D8] = "top_audpll_d8",
+ [MT2701_AUD_AUDPLL_D16] = "top_audpll_d16",
+ [MT2701_AUD_AUDPLL_D24] = "top_audpll_d24",
+ [MT2701_AUD_AUDINTBUS] = "top_audintbus_sel",
+ [MT2701_AUD_CLK_26M] = "clk_26m",
+ [MT2701_AUD_SYSPLL1_D4] = "top_syspll1_d4",
+ [MT2701_AUD_AUD_K1_SRC_SEL] = "top_aud_k1_src_sel",
+ [MT2701_AUD_AUD_K2_SRC_SEL] = "top_aud_k2_src_sel",
+ [MT2701_AUD_AUD_K3_SRC_SEL] = "top_aud_k3_src_sel",
+ [MT2701_AUD_AUD_K4_SRC_SEL] = "top_aud_k4_src_sel",
+ [MT2701_AUD_AUD_K5_SRC_SEL] = "top_aud_k5_src_sel",
+ [MT2701_AUD_AUD_K6_SRC_SEL] = "top_aud_k6_src_sel",
+ [MT2701_AUD_AUD_K1_SRC_DIV] = "top_aud_k1_src_div",
+ [MT2701_AUD_AUD_K2_SRC_DIV] = "top_aud_k2_src_div",
+ [MT2701_AUD_AUD_K3_SRC_DIV] = "top_aud_k3_src_div",
+ [MT2701_AUD_AUD_K4_SRC_DIV] = "top_aud_k4_src_div",
+ [MT2701_AUD_AUD_K5_SRC_DIV] = "top_aud_k5_src_div",
+ [MT2701_AUD_AUD_K6_SRC_DIV] = "top_aud_k6_src_div",
+ [MT2701_AUD_AUD_I2S1_MCLK] = "top_aud_i2s1_mclk",
+ [MT2701_AUD_AUD_I2S2_MCLK] = "top_aud_i2s2_mclk",
+ [MT2701_AUD_AUD_I2S3_MCLK] = "top_aud_i2s3_mclk",
+ [MT2701_AUD_AUD_I2S4_MCLK] = "top_aud_i2s4_mclk",
+ [MT2701_AUD_AUD_I2S5_MCLK] = "top_aud_i2s5_mclk",
+ [MT2701_AUD_AUD_I2S6_MCLK] = "top_aud_i2s6_mclk",
+ [MT2701_AUD_ASM_M_SEL] = "top_asm_m_sel",
+ [MT2701_AUD_ASM_H_SEL] = "top_asm_h_sel",
+ [MT2701_AUD_UNIVPLL2_D4] = "top_univpll2_d4",
+ [MT2701_AUD_UNIVPLL2_D2] = "top_univpll2_d2",
+ [MT2701_AUD_SYSPLL_D5] = "top_syspll_d5",
+};
+
+int mt2701_init_clock(struct mtk_base_afe *afe)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int i = 0;
+
+ for (i = 0; i < MT2701_CLOCK_NUM; i++) {
+ afe_priv->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
+ if (IS_ERR(aud_clks[i])) {
+ dev_warn(afe->dev, "%s devm_clk_get %s fail\n",
+ __func__, aud_clks[i]);
+ return PTR_ERR(aud_clks[i]);
+ }
+ }
+
+ return 0;
+}
+
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe)
+{
+ int ret = 0;
+
+ ret = mt2701_turn_on_a1sys_clock(afe);
+ if (ret) {
+ dev_err(afe->dev, "%s turn_on_a1sys_clock fail %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ ret = mt2701_turn_on_a2sys_clock(afe);
+ if (ret) {
+ dev_err(afe->dev, "%s turn_on_a2sys_clock fail %d\n",
+ __func__, ret);
+ mt2701_turn_off_a1sys_clock(afe);
+ return ret;
+ }
+
+ ret = mt2701_turn_on_afe_clock(afe);
+ if (ret) {
+ dev_err(afe->dev, "%s turn_on_afe_clock fail %d\n",
+ __func__, ret);
+ mt2701_turn_off_a1sys_clock(afe);
+ mt2701_turn_off_a2sys_clock(afe);
+ return ret;
+ }
+
+ regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+ AUDIO_TOP_CON0_A1SYS_A2SYS_ON,
+ AUDIO_TOP_CON0_A1SYS_A2SYS_ON);
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ AFE_DAC_CON0_AFE_ON,
+ AFE_DAC_CON0_AFE_ON);
+ regmap_write(afe->regmap, PWR2_TOP_CON,
+ PWR2_TOP_CON_INIT_VAL);
+ 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;
+}
+
+void mt2701_afe_disable_clock(struct mtk_base_afe *afe)
+{
+ mt2701_turn_off_afe_clock(afe);
+ mt2701_turn_off_a1sys_clock(afe);
+ mt2701_turn_off_a2sys_clock(afe);
+ regmap_update_bits(afe->regmap, ASYS_TOP_CON,
+ AUDIO_TOP_CON0_A1SYS_A2SYS_ON, 0);
+ regmap_update_bits(afe->regmap, AFE_DAC_CON0,
+ AFE_DAC_CON0_AFE_ON, 0);
+}
+
+int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int ret = 0;
+
+ /* Set Mux */
+ ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+ goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
+ }
+
+ ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL],
+ afe_priv->clocks[MT2701_AUD_AUD1PLL_98M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+ aud_clks[MT2701_AUD_AUD_MUX1_SEL],
+ aud_clks[MT2701_AUD_AUD1PLL_98M], ret);
+ goto A1SYS_CLK_AUD_MUX1_SEL_ERR;
+ }
+
+ /* Set Divider */
+ ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__,
+ aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+ ret);
+ goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
+ }
+
+ ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV],
+ MT2701_AUD_AUD_MUX1_DIV_RATE);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+ aud_clks[MT2701_AUD_AUD_MUX1_DIV],
+ MT2701_AUD_AUD_MUX1_DIV_RATE, ret);
+ goto A1SYS_CLK_AUD_MUX1_DIV_ERR;
+ }
+
+ /* Enable clock gate */
+ ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[MT2701_AUD_AUD_48K_TIMING], ret);
+ goto A1SYS_CLK_AUD_48K_ERR;
+ }
+
+ /* Enable infra audio */
+ ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+ goto A1SYS_CLK_INFRA_ERR;
+ }
+
+ return 0;
+
+A1SYS_CLK_INFRA_ERR:
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+A1SYS_CLK_AUD_48K_ERR:
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+A1SYS_CLK_AUD_MUX1_DIV_ERR:
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+A1SYS_CLK_AUD_MUX1_SEL_ERR:
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+
+ return ret;
+}
+
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_48K_TIMING]);
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_DIV]);
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+}
+
+int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int ret = 0;
+
+ /* Set Mux */
+ ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
+ goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
+ }
+
+ ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL],
+ afe_priv->clocks[MT2701_AUD_AUD2PLL_90M]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+ aud_clks[MT2701_AUD_AUD_MUX2_SEL],
+ aud_clks[MT2701_AUD_AUD2PLL_90M], ret);
+ goto A2SYS_CLK_AUD_MUX2_SEL_ERR;
+ }
+
+ /* Set Divider */
+ ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[MT2701_AUD_AUD_MUX2_DIV], ret);
+ goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
+ }
+
+ ret = clk_set_rate(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV],
+ MT2701_AUD_AUD_MUX2_DIV_RATE);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%d fail %d\n", __func__,
+ aud_clks[MT2701_AUD_AUD_MUX2_DIV],
+ MT2701_AUD_AUD_MUX2_DIV_RATE, ret);
+ goto A2SYS_CLK_AUD_MUX2_DIV_ERR;
+ }
+
+ /* Enable clock gate */
+ ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[MT2701_AUD_AUD_44K_TIMING], ret);
+ goto A2SYS_CLK_AUD_44K_ERR;
+ }
+
+ /* Enable infra audio */
+ ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+ goto A2SYS_CLK_INFRA_ERR;
+ }
+
+ return 0;
+
+A2SYS_CLK_INFRA_ERR:
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+A2SYS_CLK_AUD_44K_ERR:
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+A2SYS_CLK_AUD_MUX2_DIV_ERR:
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+A2SYS_CLK_AUD_MUX2_SEL_ERR:
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+
+ return ret;
+}
+
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_44K_TIMING]);
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_DIV]);
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+}
+
+int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+
+ /* enable INFRA_SYS */
+ ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[MT2701_AUD_INFRA_SYS_AUDIO], ret);
+ goto AFE_AUD_INFRA_ERR;
+ }
+
+ /* Set MT2701_AUD_AUDINTBUS to MT2701_AUD_SYSPLL1_D4 */
+ ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[MT2701_AUD_AUDINTBUS], ret);
+ goto AFE_AUD_AUDINTBUS_ERR;
+ }
+
+ ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_AUDINTBUS],
+ afe_priv->clocks[MT2701_AUD_SYSPLL1_D4]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+ aud_clks[MT2701_AUD_AUDINTBUS],
+ aud_clks[MT2701_AUD_SYSPLL1_D4], ret);
+ goto AFE_AUD_AUDINTBUS_ERR;
+ }
+
+ /* Set MT2701_AUD_ASM_H_SEL to MT2701_AUD_UNIVPLL2_D2 */
+ ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[MT2701_AUD_ASM_H_SEL], ret);
+ goto AFE_AUD_ASM_H_ERR;
+ }
+
+ ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_H_SEL],
+ afe_priv->clocks[MT2701_AUD_UNIVPLL2_D2]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+ aud_clks[MT2701_AUD_ASM_H_SEL],
+ aud_clks[MT2701_AUD_UNIVPLL2_D2], ret);
+ goto AFE_AUD_ASM_H_ERR;
+ }
+
+ /* Set MT2701_AUD_ASM_M_SEL to MT2701_AUD_UNIVPLL2_D4 */
+ ret = clk_prepare_enable(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[MT2701_AUD_ASM_M_SEL], ret);
+ goto AFE_AUD_ASM_M_ERR;
+ }
+
+ ret = clk_set_parent(afe_priv->clocks[MT2701_AUD_ASM_M_SEL],
+ afe_priv->clocks[MT2701_AUD_UNIVPLL2_D4]);
+ if (ret) {
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n", __func__,
+ aud_clks[MT2701_AUD_ASM_M_SEL],
+ aud_clks[MT2701_AUD_UNIVPLL2_D4], ret);
+ goto AFE_AUD_ASM_M_ERR;
+ }
+
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUDIO_TOP_CON0_PDN_AFE, 0);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUDIO_TOP_CON0_PDN_APLL_CK, 0);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_A1SYS, 0);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_A2SYS, 0);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_AFE_CONN, 0);
+
+ return 0;
+
+AFE_AUD_ASM_M_ERR:
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+AFE_AUD_ASM_H_ERR:
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+AFE_AUD_AUDINTBUS_ERR:
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+AFE_AUD_INFRA_ERR:
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+
+ return ret;
+}
+
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_INFRA_SYS_AUDIO]);
+
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_AUDINTBUS]);
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_H_SEL]);
+ clk_disable_unprepare(afe_priv->clocks[MT2701_AUD_ASM_M_SEL]);
+
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUDIO_TOP_CON0_PDN_AFE, AUDIO_TOP_CON0_PDN_AFE);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
+ AUDIO_TOP_CON0_PDN_APLL_CK,
+ AUDIO_TOP_CON0_PDN_APLL_CK);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_A1SYS,
+ AUDIO_TOP_CON4_PDN_A1SYS);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_A2SYS,
+ AUDIO_TOP_CON4_PDN_A2SYS);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_AFE_CONN,
+ AUDIO_TOP_CON4_PDN_AFE_CONN);
+}
+
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+ int mclk)
+{
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int ret;
+ int aud_src_div_id = MT2701_AUD_AUD_K1_SRC_DIV + id;
+ int aud_src_clk_id = MT2701_AUD_AUD_K1_SRC_SEL + id;
+
+ /* Set MCLK Kx_SRC_SEL(domain) */
+ ret = clk_prepare_enable(afe_priv->clocks[aud_src_clk_id]);
+ if (ret)
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[aud_src_clk_id], ret);
+
+ if (domain == 0) {
+ ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+ afe_priv->clocks[MT2701_AUD_AUD_MUX1_SEL]);
+ if (ret)
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[aud_src_clk_id],
+ aud_clks[MT2701_AUD_AUD_MUX1_SEL], ret);
+ } else {
+ ret = clk_set_parent(afe_priv->clocks[aud_src_clk_id],
+ afe_priv->clocks[MT2701_AUD_AUD_MUX2_SEL]);
+ if (ret)
+ dev_err(afe->dev, "%s clk_set_parent %s-%s fail %d\n",
+ __func__, aud_clks[aud_src_clk_id],
+ aud_clks[MT2701_AUD_AUD_MUX2_SEL], ret);
+ }
+ clk_disable_unprepare(afe_priv->clocks[aud_src_clk_id]);
+
+ /* Set MCLK Kx_SRC_DIV(divider) */
+ ret = clk_prepare_enable(afe_priv->clocks[aud_src_div_id]);
+ if (ret)
+ dev_err(afe->dev, "%s clk_prepare_enable %s fail %d\n",
+ __func__, aud_clks[aud_src_div_id], ret);
+
+ ret = clk_set_rate(afe_priv->clocks[aud_src_div_id], mclk);
+ if (ret)
+ dev_err(afe->dev, "%s clk_set_rate %s-%d fail %d\n", __func__,
+ aud_clks[aud_src_div_id], mclk, ret);
+ clk_disable_unprepare(afe_priv->clocks[aud_src_div_id]);
+}
+
+MODULE_DESCRIPTION("MT2701 afe clock control");
+MODULE_AUTHOR("Garlic Tseng <garlic.tseng@mediatek.com>");
+MODULE_LICENSE("GPL v2");
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..6497d570cf09
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-clock-ctrl.h
@@ -0,0 +1,38 @@
+/*
+ * mt2701-afe-clock-ctrl.h -- Mediatek 2701 afe clock ctrl definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@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 _MT2701_AFE_CLOCK_CTRL_H_
+#define _MT2701_AFE_CLOCK_CTRL_H_
+
+struct mtk_base_afe;
+
+int mt2701_init_clock(struct mtk_base_afe *afe);
+int mt2701_afe_enable_clock(struct mtk_base_afe *afe);
+void mt2701_afe_disable_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_a1sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a1sys_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_a2sys_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_a2sys_clock(struct mtk_base_afe *afe);
+
+int mt2701_turn_on_afe_clock(struct mtk_base_afe *afe);
+void mt2701_turn_off_afe_clock(struct mtk_base_afe *afe);
+
+void mt2701_mclk_configuration(struct mtk_base_afe *afe, int id, int domain,
+ int mclk);
+
+#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..c19430e98adf
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-common.h
@@ -0,0 +1,172 @@
+/*
+ * mt2701-afe-common.h -- Mediatek 2701 audio driver definitions
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@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 _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_STREAM_DIR_NUM (SNDRV_PCM_STREAM_LAST + 1)
+#define MT2701_PLL_DOMAIN_0_RATE 98304000
+#define MT2701_PLL_DOMAIN_1_RATE 90316800
+#define MT2701_AUD_AUD_MUX1_DIV_RATE (MT2701_PLL_DOMAIN_0_RATE / 2)
+#define MT2701_AUD_AUD_MUX2_DIV_RATE (MT2701_PLL_DOMAIN_1_RATE / 2)
+
+enum {
+ MT2701_I2S_1,
+ MT2701_I2S_2,
+ MT2701_I2S_3,
+ MT2701_I2S_4,
+ MT2701_I2S_NUM,
+};
+
+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_START,
+ MT2701_IRQ_ASYS_IRQ1 = MT2701_IRQ_ASYS_START,
+ MT2701_IRQ_ASYS_IRQ2,
+ MT2701_IRQ_ASYS_IRQ3,
+ MT2701_IRQ_ASYS_END,
+};
+
+/* 2701 clock def */
+enum audio_system_clock_type {
+ MT2701_AUD_INFRA_SYS_AUDIO,
+ MT2701_AUD_AUD_MUX1_SEL,
+ MT2701_AUD_AUD_MUX2_SEL,
+ MT2701_AUD_AUD_MUX1_DIV,
+ MT2701_AUD_AUD_MUX2_DIV,
+ MT2701_AUD_AUD_48K_TIMING,
+ MT2701_AUD_AUD_44K_TIMING,
+ MT2701_AUD_AUDPLL_MUX_SEL,
+ MT2701_AUD_APLL_SEL,
+ MT2701_AUD_AUD1PLL_98M,
+ MT2701_AUD_AUD2PLL_90M,
+ MT2701_AUD_HADDS2PLL_98M,
+ MT2701_AUD_HADDS2PLL_294M,
+ MT2701_AUD_AUDPLL,
+ MT2701_AUD_AUDPLL_D4,
+ MT2701_AUD_AUDPLL_D8,
+ MT2701_AUD_AUDPLL_D16,
+ MT2701_AUD_AUDPLL_D24,
+ MT2701_AUD_AUDINTBUS,
+ MT2701_AUD_CLK_26M,
+ MT2701_AUD_SYSPLL1_D4,
+ MT2701_AUD_AUD_K1_SRC_SEL,
+ MT2701_AUD_AUD_K2_SRC_SEL,
+ MT2701_AUD_AUD_K3_SRC_SEL,
+ MT2701_AUD_AUD_K4_SRC_SEL,
+ MT2701_AUD_AUD_K5_SRC_SEL,
+ MT2701_AUD_AUD_K6_SRC_SEL,
+ MT2701_AUD_AUD_K1_SRC_DIV,
+ MT2701_AUD_AUD_K2_SRC_DIV,
+ MT2701_AUD_AUD_K3_SRC_DIV,
+ MT2701_AUD_AUD_K4_SRC_DIV,
+ MT2701_AUD_AUD_K5_SRC_DIV,
+ MT2701_AUD_AUD_K6_SRC_DIV,
+ MT2701_AUD_AUD_I2S1_MCLK,
+ MT2701_AUD_AUD_I2S2_MCLK,
+ MT2701_AUD_AUD_I2S3_MCLK,
+ MT2701_AUD_AUD_I2S4_MCLK,
+ MT2701_AUD_AUD_I2S5_MCLK,
+ MT2701_AUD_AUD_I2S6_MCLK,
+ MT2701_AUD_ASM_M_SEL,
+ MT2701_AUD_ASM_H_SEL,
+ MT2701_AUD_UNIVPLL2_D4,
+ MT2701_AUD_UNIVPLL2_D2,
+ MT2701_AUD_SYSPLL_D5,
+ MT2701_CLOCK_NUM
+};
+
+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,
+};
+
+struct snd_pcm_substream;
+struct mtk_base_irq_data;
+
+struct mt2701_i2s_data {
+ int i2s_ctrl_reg;
+ int i2s_pwn_shift;
+ int i2s_asrc_fs_shift;
+ int i2s_asrc_fs_mask;
+};
+
+enum mt2701_i2s_dir {
+ I2S_OUT,
+ I2S_IN,
+ I2S_DIR_NUM,
+};
+
+struct mt2701_i2s_path {
+ int dai_id;
+ int mclk_rate;
+ int on[I2S_DIR_NUM];
+ int occupied[I2S_DIR_NUM];
+ const struct mt2701_i2s_data *i2s_data[2];
+};
+
+struct mt2701_afe_private {
+ struct clk *clocks[MT2701_CLOCK_NUM];
+ struct mt2701_i2s_path i2s_path[MT2701_I2S_NUM];
+ bool mrg_enable[MT2701_STREAM_DIR_NUM];
+};
+
+#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..34a6123480d3
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -0,0 +1,1656 @@
+/*
+ * 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>
+ *
+ * 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 "mt2701-afe-common.h"
+
+#include "mt2701-afe-clock-ctrl.h"
+#include "../common/mtk-afe-platform-driver.h"
+#include "../common/mtk-afe-fe-dai.h"
+
+#define AFE_IRQ_STATUS_BITS 0xff
+
+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 * 16,
+ .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 int mt2701_dai_num_to_i2s(struct mtk_base_afe *afe, int num)
+{
+ int val = num - MT2701_IO_I2S;
+
+ if (val < 0 || val >= MT2701_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 snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+ int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num;
+ int ret = 0;
+
+ if (i2s_num < 0)
+ return i2s_num;
+
+ /* enable mclk */
+ ret = clk_prepare_enable(afe_priv->clocks[clk_num]);
+ if (ret)
+ dev_err(afe->dev, "Failed to enable mclk for I2S: %d\n",
+ i2s_num);
+
+ return ret;
+}
+
+static int mt2701_afe_i2s_path_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai,
+ int dir_invert)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ 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;
+ const struct mt2701_i2s_data *i2s_data;
+ int stream_dir = substream->stream;
+
+ if (i2s_num < 0)
+ return i2s_num;
+
+ i2s_path = &afe_priv->i2s_path[i2s_num];
+
+ if (dir_invert) {
+ if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+ stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+ else
+ stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+ }
+ i2s_data = i2s_path->i2s_data[stream_dir];
+
+ i2s_path->on[stream_dir]--;
+ if (i2s_path->on[stream_dir] < 0) {
+ dev_warn(afe->dev, "i2s_path->on: %d, dir: %d\n",
+ i2s_path->on[stream_dir], stream_dir);
+ 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);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ 1 << i2s_data->i2s_pwn_shift,
+ 1 << i2s_data->i2s_pwn_shift);
+ return 0;
+}
+
+static void mt2701_afe_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ 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;
+ int clk_num = MT2701_AUD_AUD_I2S1_MCLK + i2s_num;
+
+ 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 I2S_UNSTART;
+
+ mt2701_afe_i2s_path_shutdown(substream, dai, 0);
+
+ /* need to disable i2s-out path when disable i2s-in */
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ mt2701_afe_i2s_path_shutdown(substream, dai, 1);
+
+I2S_UNSTART:
+ /* disable mclk */
+ clk_disable_unprepare(afe_priv->clocks[clk_num]);
+}
+
+static int mt2701_i2s_path_prepare_enable(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai,
+ int dir_invert)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ 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;
+ const struct mt2701_i2s_data *i2s_data;
+ struct snd_pcm_runtime * const runtime = substream->runtime;
+ int reg, fs, w_len = 1; /* now we support bck 64bits only */
+ int stream_dir = substream->stream;
+ unsigned int mask = 0, val = 0;
+
+ if (i2s_num < 0)
+ return i2s_num;
+
+ i2s_path = &afe_priv->i2s_path[i2s_num];
+
+ if (dir_invert) {
+ if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+ stream_dir = SNDRV_PCM_STREAM_CAPTURE;
+ else
+ stream_dir = SNDRV_PCM_STREAM_PLAYBACK;
+ }
+ i2s_data = i2s_path->i2s_data[stream_dir];
+
+ /* no need to enable if already done */
+ i2s_path->on[stream_dir]++;
+
+ if (i2s_path->on[stream_dir] != 1)
+ return 0;
+
+ fs = mt2701_afe_i2s_fs(runtime->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;
+ }
+
+ regmap_update_bits(afe->regmap, i2s_data->i2s_ctrl_reg, mask, val);
+
+ if (stream_dir == SNDRV_PCM_STREAM_PLAYBACK)
+ reg = ASMO_TIMING_CON1;
+ else
+ reg = ASMI_TIMING_CON1;
+
+ 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 */
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ 1 << i2s_data->i2s_pwn_shift,
+ 0 << i2s_data->i2s_pwn_shift);
+
+ /* 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)
+{
+ int clk_domain;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ 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;
+ int mclk_rate;
+
+ if (i2s_num < 0)
+ return i2s_num;
+
+ i2s_path = &afe_priv->i2s_path[i2s_num];
+ mclk_rate = i2s_path->mclk_rate;
+
+ if (i2s_path->occupied[substream->stream])
+ return -EBUSY;
+ i2s_path->occupied[substream->stream] = 1;
+
+ if (MT2701_PLL_DOMAIN_0_RATE % mclk_rate == 0) {
+ clk_domain = 0;
+ } else if (MT2701_PLL_DOMAIN_1_RATE % mclk_rate == 0) {
+ clk_domain = 1;
+ } else {
+ dev_err(dai->dev, "%s() bad mclk rate %d\n",
+ __func__, mclk_rate);
+ return -EINVAL;
+ }
+ mt2701_mclk_configuration(afe, i2s_num, clk_domain, mclk_rate);
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ mt2701_i2s_path_prepare_enable(substream, dai, 0);
+ } else {
+ /* need to enable i2s-out path when enable i2s-in */
+ /* prepare for another direction "out" */
+ mt2701_i2s_path_prepare_enable(substream, dai, 1);
+ /* prepare for "in" */
+ mt2701_i2s_path_prepare_enable(substream, dai, 0);
+ }
+
+ 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 = dev_get_drvdata(dai->dev);
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+ int i2s_num = mt2701_dai_num_to_i2s(afe, dai->id);
+
+ if (i2s_num < 0)
+ return i2s_num;
+
+ /* mclk */
+ if (dir == SND_SOC_CLOCK_IN) {
+ dev_warn(dai->dev,
+ "%s() warning: mt2701 doesn't support mclk input\n",
+ __func__);
+ return -EINVAL;
+ }
+ afe_priv->i2s_path[i2s_num].mclk_rate = freq;
+ return 0;
+}
+
+static int mt2701_btmrg_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mt2701_afe_private *afe_priv = afe->platform_priv;
+
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_MRGIF, 0);
+
+ 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 snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ int stream_fs;
+ u32 val, msk;
+
+ stream_fs = params_rate(params);
+
+ if ((stream_fs != 8000) && (stream_fs != 16000)) {
+ dev_err(afe->dev, "%s() btmgr not supprt this stream_fs %d\n",
+ __func__, 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 snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ 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);
+ regmap_update_bits(afe->regmap, AUDIO_TOP_CON4,
+ AUDIO_TOP_CON4_PDN_MRGIF,
+ AUDIO_TOP_CON4_PDN_MRGIF);
+ }
+ afe_priv->mrg_enable[substream->stream] = 0;
+}
+
+static int mt2701_simple_fe_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ int stream_dir = substream->stream;
+ int memif_num = rtd->cpu_dai->id;
+ struct mtk_base_afe_memif *memif_tmp;
+
+ /* 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, "%s memif is not available, stream_dir %d, memif_num %d\n",
+ __func__, stream_dir, memif_num);
+ 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 snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ 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 snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ 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 snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ 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 snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ 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 snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ 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 = substream->private_data;
+ int fs;
+
+ if (rtd->cpu_dai->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 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 = "PCM_multi",
+ .id = MT2701_MEMIF_DLM,
+ .suspend = mtk_afe_dai_suspend,
+ .resume = mtk_afe_dai_resume,
+ .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,
+ .suspend = mtk_afe_dai_suspend,
+ .resume = mtk_afe_dai_resume,
+ .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,
+ .suspend = mtk_afe_dai_suspend,
+ .resume = mtk_afe_dai_resume,
+ .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,
+ .suspend = mtk_afe_dai_suspend,
+ .resume = mtk_afe_dai_resume,
+ .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,
+ .suspend = mtk_afe_dai_suspend,
+ .resume = mtk_afe_dai_resume,
+ .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_rates = 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_rates = 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_rates = 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_rates = 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_rates = 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_o23_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I20 Switch", AFE_CONN23, 20, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_o24_mix[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("I21 Switch", AFE_CONN24, 21, 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_kcontrol_new mt2701_afe_multi_ch_out_i2s4[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Multich I2S4 Out Switch",
+ PWR2_TOP_CON, 19, 1, 0),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc0[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Asrc0 out Switch", AUDIO_TOP_CON4, 14, 1,
+ 1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc1[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Asrc1 out Switch", AUDIO_TOP_CON4, 15, 1,
+ 1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc2[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Asrc2 out Switch", PWR2_TOP_CON, 6, 1,
+ 1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc3[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Asrc3 out Switch", PWR2_TOP_CON, 7, 1,
+ 1),
+};
+
+static const struct snd_kcontrol_new mt2701_afe_multi_ch_out_asrc4[] = {
+ SOC_DAPM_SINGLE_AUTODISABLE("Asrc4 out Switch", PWR2_TOP_CON, 8, 1,
+ 1),
+};
+
+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)),
+
+ SND_SOC_DAPM_MIXER("ASRC_O0", SND_SOC_NOPM, 0, 0,
+ mt2701_afe_multi_ch_out_asrc0,
+ ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc0)),
+ SND_SOC_DAPM_MIXER("ASRC_O1", SND_SOC_NOPM, 0, 0,
+ mt2701_afe_multi_ch_out_asrc1,
+ ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc1)),
+ SND_SOC_DAPM_MIXER("ASRC_O2", SND_SOC_NOPM, 0, 0,
+ mt2701_afe_multi_ch_out_asrc2,
+ ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc2)),
+ SND_SOC_DAPM_MIXER("ASRC_O3", SND_SOC_NOPM, 0, 0,
+ mt2701_afe_multi_ch_out_asrc3,
+ ARRAY_SIZE(mt2701_afe_multi_ch_out_asrc3)),
+};
+
+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"},
+
+ {"ASRC_O0", "Asrc0 out Switch", "DLM"},
+ {"ASRC_O1", "Asrc1 out Switch", "DLM"},
+ {"ASRC_O2", "Asrc2 out Switch", "DLM"},
+ {"ASRC_O3", "Asrc3 out Switch", "DLM"},
+
+ {"I12I13", "Multich I2S0 Out Switch", "ASRC_O0"},
+ {"I14I15", "Multich I2S1 Out Switch", "ASRC_O1"},
+ {"I16I17", "Multich I2S2 Out Switch", "ASRC_O2"},
+ {"I18I19", "Multich I2S3 Out Switch", "ASRC_O3"},
+
+ { "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 const struct snd_soc_component_driver mt2701_afe_pcm_dai_component = {
+ .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),
+};
+
+static const struct mtk_base_memif_data memif_data[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,
+ .msb_shift = -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,
+ .msb_shift = -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,
+ .msb_shift = -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,
+ .msb_shift = -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,
+ .msb_shift = -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,
+ .msb_shift = -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,
+ .msb_shift = -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,
+ .msb_shift = -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,
+ .msb_shift = -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,
+ .msb_shift = -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,
+ .msb_shift = -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,
+ .msb_shift = -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,
+ .msb_shift = -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[MT2701_I2S_NUM][2] = {
+ {
+ {
+ .i2s_ctrl_reg = ASYS_I2SO1_CON,
+ .i2s_pwn_shift = 6,
+ .i2s_asrc_fs_shift = 0,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ {
+ .i2s_ctrl_reg = ASYS_I2SIN1_CON,
+ .i2s_pwn_shift = 0,
+ .i2s_asrc_fs_shift = 0,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ },
+ {
+ {
+ .i2s_ctrl_reg = ASYS_I2SO2_CON,
+ .i2s_pwn_shift = 7,
+ .i2s_asrc_fs_shift = 5,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ {
+ .i2s_ctrl_reg = ASYS_I2SIN2_CON,
+ .i2s_pwn_shift = 1,
+ .i2s_asrc_fs_shift = 5,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ },
+ {
+ {
+ .i2s_ctrl_reg = ASYS_I2SO3_CON,
+ .i2s_pwn_shift = 8,
+ .i2s_asrc_fs_shift = 10,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ {
+ .i2s_ctrl_reg = ASYS_I2SIN3_CON,
+ .i2s_pwn_shift = 2,
+ .i2s_asrc_fs_shift = 10,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ },
+ {
+ {
+ .i2s_ctrl_reg = ASYS_I2SO4_CON,
+ .i2s_pwn_shift = 9,
+ .i2s_asrc_fs_shift = 15,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ {
+ .i2s_ctrl_reg = ASYS_I2SIN4_CON,
+ .i2s_pwn_shift = 3,
+ .i2s_asrc_fs_shift = 15,
+ .i2s_asrc_fs_mask = 0x1f,
+
+ },
+ },
+};
+
+static const struct regmap_config mt2701_afe_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = AFE_END_ADDR,
+ .cache_type = REGCACHE_NONE,
+};
+
+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);
+
+ mt2701_afe_disable_clock(afe);
+ return 0;
+}
+
+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)
+{
+ int ret, i;
+ unsigned int irq_id;
+ struct mtk_base_afe *afe;
+ struct mt2701_afe_private *afe_priv;
+ struct resource *res;
+ struct device *dev;
+
+ ret = 0;
+ 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;
+
+ irq_id = platform_get_irq(pdev, 0);
+ if (!irq_id) {
+ dev_err(dev, "%s no irq found\n", dev->of_node->name);
+ return -ENXIO;
+ }
+ 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;
+ }
+
+ 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,
+ &mt2701_afe_regmap_config);
+ if (IS_ERR(afe->regmap))
+ 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[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 < MT2701_I2S_NUM; i++) {
+ afe_priv->i2s_path[i].i2s_data[I2S_OUT]
+ = &mt2701_i2s_data[i][I2S_OUT];
+ afe_priv->i2s_path[i].i2s_data[I2S_IN]
+ = &mt2701_i2s_data[i][I2S_IN];
+ }
+
+ 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(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev))
+ goto err_pm_disable;
+
+ ret = snd_soc_register_platform(&pdev->dev, &mtk_afe_pcm_platform);
+ if (ret) {
+ dev_warn(dev, "err_platform\n");
+ goto err_platform;
+ }
+
+ ret = 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_dai_component;
+ }
+
+ mt2701_afe_runtime_resume(&pdev->dev);
+
+ return 0;
+
+err_dai_component:
+ 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 mt2701_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))
+ mt2701_afe_runtime_suspend(&pdev->dev);
+
+ snd_soc_unregister_component(&pdev->dev);
+ snd_soc_unregister_platform(&pdev->dev);
+ /* disable afe clock */
+ mt2701_afe_disable_clock(afe);
+ return 0;
+}
+
+static const struct of_device_id mt2701_afe_pcm_dt_match[] = {
+ { .compatible = "mediatek,mt2701-audio", },
+ {},
+};
+MODULE_DEVICE_TABLE(of, mt2701_afe_pcm_dt_match);
+
+static const struct dev_pm_ops mt2701_afe_pm_ops = {
+ SET_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,
+#ifdef CONFIG_PM
+ .pm = &mt2701_afe_pm_ops,
+#endif
+ },
+ .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..1e7e8d43fd8a
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
@@ -0,0 +1,412 @@
+/*
+ * 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>
+ *
+ *
+ * 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 <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/of_gpio.h>
+
+#include "mt2701-afe-common.h"
+
+struct mt2701_cs42448_private {
+ int i2s1_in_mux;
+ int i2s1_in_mux_gpio_sel_1;
+ int i2s1_in_mux_gpio_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:
+ gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
+ gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
+ break;
+ case 1:
+ gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
+ gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 0);
+ break;
+ case 2:
+ gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 0);
+ gpio_set_value(priv->i2s1_in_mux_gpio_sel_2, 1);
+ break;
+ case 3:
+ gpio_set_value(priv->i2s1_in_mux_gpio_sel_1, 1);
+ gpio_set_value(priv->i2s1_in_mux_gpio_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 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 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 = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ 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 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,
+};
+
+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",
+ .cpu_dai_name = "PCM_multi",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ops = &mt2701_cs42448_48k_fe_ops,
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ },
+ [DAI_LINK_FE_PCM0_IN] = {
+ .name = "mt2701-cs42448-pcm0",
+ .stream_name = "mt2701-cs42448-pcm0-data-UL",
+ .cpu_dai_name = "PCM0",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ops = &mt2701_cs42448_48k_fe_ops,
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ [DAI_LINK_FE_PCM1_IN] = {
+ .name = "mt2701-cs42448-pcm1-data-UL",
+ .stream_name = "mt2701-cs42448-pcm1-data-UL",
+ .cpu_dai_name = "PCM1",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .trigger = {SND_SOC_DPCM_TRIGGER_POST,
+ SND_SOC_DPCM_TRIGGER_POST},
+ .ops = &mt2701_cs42448_48k_fe_ops,
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ [DAI_LINK_FE_BT_OUT] = {
+ .name = "mt2701-cs42448-pcm-BT-out",
+ .stream_name = "mt2701-cs42448-pcm-BT",
+ .cpu_dai_name = "PCM_BT_DL",
+ .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,
+ },
+ [DAI_LINK_FE_BT_IN] = {
+ .name = "mt2701-cs42448-pcm-BT-in",
+ .stream_name = "mt2701-cs42448-pcm-BT",
+ .cpu_dai_name = "PCM_BT_UL",
+ .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,
+ },
+ /* BE */
+ [DAI_LINK_BE_I2S0] = {
+ .name = "mt2701-cs42448-I2S0",
+ .cpu_dai_name = "I2S0",
+ .no_pcm = 1,
+ .codec_dai_name = "cs42448",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt2701_cs42448_be_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ [DAI_LINK_BE_I2S1] = {
+ .name = "mt2701-cs42448-I2S1",
+ .cpu_dai_name = "I2S1",
+ .no_pcm = 1,
+ .codec_dai_name = "cs42448",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt2701_cs42448_be_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ [DAI_LINK_BE_I2S2] = {
+ .name = "mt2701-cs42448-I2S2",
+ .cpu_dai_name = "I2S2",
+ .no_pcm = 1,
+ .codec_dai_name = "cs42448",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt2701_cs42448_be_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ [DAI_LINK_BE_I2S3] = {
+ .name = "mt2701-cs42448-I2S3",
+ .cpu_dai_name = "I2S3",
+ .no_pcm = 1,
+ .codec_dai_name = "cs42448",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt2701_cs42448_be_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+ [DAI_LINK_BE_MRG_BT] = {
+ .name = "mt2701-cs42448-MRG-BT",
+ .cpu_dai_name = "MRG BT",
+ .no_pcm = 1,
+ .codec_dai_name = "bt-sco-pcm-wb",
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+};
+
+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 mt2701_cs42448_private *priv =
+ devm_kzalloc(&pdev->dev, sizeof(struct mt2701_cs42448_private),
+ GFP_KERNEL);
+ struct device *dev = &pdev->dev;
+
+ if (!priv)
+ return -ENOMEM;
+
+ 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 (i = 0; i < card->num_links; i++) {
+ if (mt2701_cs42448_dai_links[i].platform_name)
+ continue;
+ mt2701_cs42448_dai_links[i].platform_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(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < card->num_links; i++) {
+ if (mt2701_cs42448_dai_links[i].codec_name)
+ continue;
+ mt2701_cs42448_dai_links[i].codec_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(&pdev->dev,
+ "Property 'audio-codec-bt-mrg' missing or invalid\n");
+ return -EINVAL;
+ }
+ mt2701_cs42448_dai_links[DAI_LINK_BE_MRG_BT].codec_of_node
+ = codec_node_bt_mrg;
+
+ 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;
+ }
+
+ priv->i2s1_in_mux_gpio_sel_1 =
+ of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio1", 0);
+ if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_1)) {
+ ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_1,
+ "i2s1_in_mux_gpio_sel_1");
+ if (ret)
+ dev_warn(&pdev->dev, "%s devm_gpio_request fail %d\n",
+ __func__, ret);
+ gpio_direction_output(priv->i2s1_in_mux_gpio_sel_1, 0);
+ }
+
+ priv->i2s1_in_mux_gpio_sel_2 =
+ of_get_named_gpio(dev->of_node, "i2s1-in-sel-gpio2", 0);
+ if (gpio_is_valid(priv->i2s1_in_mux_gpio_sel_2)) {
+ ret = devm_gpio_request(dev, priv->i2s1_in_mux_gpio_sel_2,
+ "i2s1_in_mux_gpio_sel_2");
+ if (ret)
+ dev_warn(&pdev->dev, "%s devm_gpio_request fail2 %d\n",
+ __func__, ret);
+ gpio_direction_output(priv->i2s1_in_mux_gpio_sel_2, 0);
+ }
+ snd_soc_card_set_drvdata(card, priv);
+
+ 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);
+ return ret;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id mt2701_cs42448_machine_dt_match[] = {
+ {.compatible = "mediatek,mt2701-cs42448-machine",},
+ {}
+};
+#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..bb62b1c55957
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-reg.h
@@ -0,0 +1,186 @@
+/*
+ * mt2701-reg.h -- Mediatek 2701 audio driver reg definition
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Garlic Tseng <garlic.tseng@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 _MT2701_REG_H_
+#define _MT2701_REG_H_
+
+#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 "mt2701-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_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
+
+/* AUDIO_TOP_CON0 (0x0000) */
+#define AUDIO_TOP_CON0_A1SYS_A2SYS_ON (0x3 << 0)
+#define AUDIO_TOP_CON0_PDN_AFE (0x1 << 2)
+#define AUDIO_TOP_CON0_PDN_APLL_CK (0x1 << 23)
+
+/* AUDIO_TOP_CON4 (0x0010) */
+#define AUDIO_TOP_CON4_I2SO1_PWN (0x1 << 6)
+#define AUDIO_TOP_CON4_PDN_A1SYS (0x1 << 21)
+#define AUDIO_TOP_CON4_PDN_A2SYS (0x1 << 22)
+#define AUDIO_TOP_CON4_PDN_AFE_CONN (0x1 << 23)
+#define AUDIO_TOP_CON4_PDN_MRGIF (0x1 << 25)
+
+/* 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_I2SO1_CON (0x061c) */
+#define ASYS_I2SO1_CON_FS (0x1f << 8)
+#define ASYS_I2SO1_CON_FS_SET(x) ((x) << 8)
+#define ASYS_I2SO1_CON_MULTI_CH (0x1 << 16)
+#define ASYS_I2SO1_CON_SIDEGEN (0x1 << 30)
+#define ASYS_I2SO1_CON_I2S_EN (0x1 << 0)
+/* 0:EIAJ 1:I2S */
+#define ASYS_I2SO1_CON_I2S_MODE (0x1 << 3)
+#define ASYS_I2SO1_CON_WIDE_MODE (0x1 << 1)
+#define ASYS_I2SO1_CON_WIDE_MODE_SET(x) ((x) << 1)
+
+/* PWR2_TOP_CON (0x0634) */
+#define PWR2_TOP_CON_INIT_VAL (0xffe1ffff)
+
+/* ASYS_IRQ_CLR (0x07c0) */
+#define ASYS_IRQ_CLR_ALL (0xffffffff)
+
+/* 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_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)
+
+#define AFE_END_ADDR 0x15e0
+#endif
diff --git a/sound/soc/mediatek/mt8173/Makefile b/sound/soc/mediatek/mt8173/Makefile
new file mode 100644
index 000000000000..0357b27d29f2
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/Makefile
@@ -0,0 +1,7 @@
+# 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..9a4837cc181a
--- /dev/null
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-common.h
@@ -0,0 +1,73 @@
+/*
+ * 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>
+ *
+ * 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 _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/mtk-afe-pcm.c b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
index 2b5df2ef51a3..8a643a35d3d4 100644
--- a/sound/soc/mediatek/mtk-afe-pcm.c
+++ b/sound/soc/mediatek/mt8173/mt8173-afe-pcm.c
@@ -1,5 +1,5 @@
/*
- * Mediatek ALSA SoC AFE platform driver
+ * Mediatek 8173 ALSA SoC AFE platform driver
*
* Copyright (c) 2015 MediaTek Inc.
* Author: Koro Chen <koro.chen@mediatek.com>
@@ -24,7 +24,10 @@
#include <linux/dma-mapping.h>
#include <linux/pm_runtime.h>
#include <sound/soc.h>
-#include "mtk-afe-common.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
@@ -81,7 +84,6 @@
#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) */
@@ -135,7 +137,7 @@ enum afe_tdm_ch_start {
AFE_TDM_CH_ZERO,
};
-static const unsigned int mtk_afe_backup_list[] = {
+static const unsigned int mt8173_afe_backup_list[] = {
AUDIO_TOP_CON0,
AFE_CONN1,
AFE_CONN2,
@@ -152,18 +154,11 @@ static const unsigned int mtk_afe_backup_list[] = {
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;
+struct mt8173_afe_private {
+ struct clk *clocks[MT8173_CLK_NUM];
};
-static const struct snd_pcm_hardware mtk_afe_hardware = {
+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,
@@ -174,59 +169,12 @@ static const struct snd_pcm_hardware mtk_afe_hardware = {
.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 {
+struct mt8173_afe_rate {
unsigned int rate;
unsigned int regvalue;
};
-static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
+static const struct mt8173_afe_rate mt8173_afe_i2s_rates[] = {
{ .rate = 8000, .regvalue = 0 },
{ .rate = 11025, .regvalue = 1 },
{ .rate = 12000, .regvalue = 2 },
@@ -242,21 +190,21 @@ static const struct mtk_afe_rate mtk_afe_i2s_rates[] = {
{ .rate = 192000, .regvalue = 14 },
};
-static int mtk_afe_i2s_fs(unsigned int sample_rate)
+static int mt8173_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;
+ 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 mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
+static int mt8173_afe_set_i2s(struct mtk_base_afe *afe, unsigned int rate)
{
unsigned int val;
- int fs = mtk_afe_i2s_fs(rate);
+ int fs = mt8173_afe_i2s_fs(rate);
if (fs < 0)
return -EINVAL;
@@ -281,7 +229,7 @@ static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
return 0;
}
-static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
+static void mt8173_afe_set_i2s_enable(struct mtk_base_afe *afe, bool enable)
{
unsigned int val;
@@ -296,8 +244,8 @@ static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
}
-static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
- struct clk *m_ck, struct clk *b_ck)
+static int mt8173_afe_dais_enable_clks(struct mtk_base_afe *afe,
+ struct clk *m_ck, struct clk *b_ck)
{
int ret;
@@ -319,9 +267,9 @@ static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
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)
+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;
@@ -343,8 +291,8 @@ static int mtk_afe_dais_set_clks(struct mtk_afe *afe,
return 0;
}
-static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
- struct clk *m_ck, struct clk *b_ck)
+static void mt8173_afe_dais_disable_clks(struct mtk_base_afe *afe,
+ struct clk *m_ck, struct clk *b_ck)
{
if (m_ck)
clk_disable_unprepare(m_ck);
@@ -352,102 +300,101 @@ static void mtk_afe_dais_disable_clks(struct mtk_afe *afe,
clk_disable_unprepare(b_ck);
}
-static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int mt8173_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);
+ struct mtk_base_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);
- mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S2_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)
+static void mt8173_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);
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
if (dai->active)
return;
- mtk_afe_set_i2s_enable(afe, false);
+ 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);
- mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL);
- mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL);
}
-static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int mt8173_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);
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mt8173_afe_private *afe_priv = afe->platform_priv;
int ret;
- mtk_afe_dais_set_clks(afe,
- afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256,
- NULL, 0);
- mtk_afe_dais_set_clks(afe,
- afe->clocks[MTK_CLK_I2S2_M], runtime->rate * 256,
- NULL, 0);
+ 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 = mtk_afe_set_i2s(afe, substream->runtime->rate);
+ ret = mt8173_afe_set_i2s(afe, substream->runtime->rate);
if (ret)
return ret;
- mtk_afe_set_i2s_enable(afe, true);
+ mt8173_afe_set_i2s_enable(afe, true);
return 0;
}
-static int mtk_afe_hdmi_startup(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int mt8173_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);
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mt8173_afe_private *afe_priv = afe->platform_priv;
if (dai->active)
return 0;
- mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
- afe->clocks[MTK_CLK_I2S3_B]);
+ mt8173_afe_dais_enable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+ afe_priv->clocks[MT8173_CLK_I2S3_B]);
return 0;
}
-static void mtk_afe_hdmi_shutdown(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static void mt8173_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);
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mt8173_afe_private *afe_priv = afe->platform_priv;
if (dai->active)
return;
- mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S3_M],
- afe->clocks[MTK_CLK_I2S3_B]);
+ mt8173_afe_dais_disable_clks(afe, afe_priv->clocks[MT8173_CLK_I2S3_M],
+ afe_priv->clocks[MT8173_CLK_I2S3_B]);
}
-static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int mt8173_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);
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mt8173_afe_private *afe_priv = afe->platform_priv;
+
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);
+ 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 |
@@ -498,11 +445,11 @@ static int mtk_afe_hdmi_prepare(struct snd_pcm_substream *substream,
return 0;
}
-static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
- struct snd_soc_dai *dai)
+static int mt8173_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);
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
dev_info(afe->dev, "%s cmd=%d %s\n", __func__, cmd, dai->name);
@@ -514,10 +461,14 @@ static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
/* 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);
+ 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);
@@ -537,284 +488,65 @@ static int mtk_afe_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
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)
+static int mt8173_memif_fs(struct snd_pcm_substream *substream,
+ unsigned int rate)
{
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 msb_at_bit33 = 0;
- 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));
+ struct mtk_base_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
+ struct mtk_base_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+ int fs;
- ret = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(params));
- if (ret < 0)
- return ret;
-
- msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
- 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 + AFE_BASE_END_OFFSET,
- memif->phys_buf_addr + memif->buffer_size - 1);
-
- /* set MSB to 33-bit */
- regmap_update_bits(afe->regmap, AFE_MEMIF_MSB,
- 1 << memif->data->msb_shift,
- msb_at_bit33 << memif->data->msb_shift);
-
- /* 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)) {
+ if (memif->data->id == MT8173_AFE_MEMIF_DAI ||
+ memif->data->id == MT8173_AFE_MEMIF_MOD_DAI) {
+ switch (rate) {
case 8000:
- val = 0;
+ fs = 0;
break;
case 16000:
- val = 1;
+ fs = 1;
break;
case 32000:
- val = 2;
+ fs = 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);
+ fs = mt8173_afe_i2s_fs(rate);
}
-
- return 0;
+ return fs;
}
-static int mtk_afe_dais_hw_free(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
+static int mt8173_irq_fs(struct snd_pcm_substream *substream, unsigned int rate)
{
- return snd_pcm_lib_free_pages(substream);
+ return mt8173_afe_i2s_fs(rate);
}
-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 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 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 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 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[] = {
+static struct snd_soc_dai_driver mt8173_afe_pcm_dais[] = {
/* FE DAIs: memory intefaces to CPU */
{
.name = "DL1", /* downlink 1 */
- .id = MTK_AFE_MEMIF_DL1,
+ .id = MT8173_AFE_MEMIF_DL1,
.suspend = mtk_afe_dai_suspend,
.resume = mtk_afe_dai_resume,
.playback = {
@@ -824,10 +556,10 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .ops = &mtk_afe_dai_ops,
+ .ops = &mtk_afe_fe_ops,
}, {
.name = "VUL", /* voice uplink */
- .id = MTK_AFE_MEMIF_VUL,
+ .id = MT8173_AFE_MEMIF_VUL,
.suspend = mtk_afe_dai_suspend,
.resume = mtk_afe_dai_resume,
.capture = {
@@ -837,11 +569,11 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .ops = &mtk_afe_dai_ops,
+ .ops = &mtk_afe_fe_ops,
}, {
/* BE DAIs */
.name = "I2S",
- .id = MTK_AFE_IO_I2S,
+ .id = MT8173_AFE_IO_I2S,
.playback = {
.stream_name = "I2S Playback",
.channels_min = 1,
@@ -856,16 +588,16 @@ static struct snd_soc_dai_driver mtk_afe_pcm_dais[] = {
.rates = SNDRV_PCM_RATE_8000_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .ops = &mtk_afe_i2s_ops,
+ .ops = &mt8173_afe_i2s_ops,
.symmetric_rates = 1,
},
};
-static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
+static struct snd_soc_dai_driver mt8173_afe_hdmi_dais[] = {
/* FE DAIs */
{
.name = "HDMI",
- .id = MTK_AFE_MEMIF_HDMI,
+ .id = MT8173_AFE_MEMIF_HDMI,
.suspend = mtk_afe_dai_suspend,
.resume = mtk_afe_dai_resume,
.playback = {
@@ -878,11 +610,11 @@ static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .ops = &mtk_afe_dai_ops,
+ .ops = &mtk_afe_fe_ops,
}, {
/* BE DAIs */
.name = "HDMIO",
- .id = MTK_AFE_IO_HDMI,
+ .id = MT8173_AFE_IO_HDMI,
.playback = {
.stream_name = "HDMIO Playback",
.channels_min = 2,
@@ -893,29 +625,29 @@ static struct snd_soc_dai_driver mtk_afe_hdmi_dais[] = {
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE,
},
- .ops = &mtk_afe_hdmi_ops,
+ .ops = &mt8173_afe_hdmi_ops,
},
};
-static const struct snd_kcontrol_new mtk_afe_o03_mix[] = {
+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 mtk_afe_o04_mix[] = {
+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 mtk_afe_o09_mix[] = {
+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 mtk_afe_o10_mix[] = {
+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 mtk_afe_pcm_widgets[] = {
+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),
@@ -925,16 +657,16 @@ static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
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)),
+ mt8173_afe_o03_mix, ARRAY_SIZE(mt8173_afe_o03_mix)),
SND_SOC_DAPM_MIXER("O04", SND_SOC_NOPM, 0, 0,
- mtk_afe_o04_mix, ARRAY_SIZE(mtk_afe_o04_mix)),
+ mt8173_afe_o04_mix, ARRAY_SIZE(mt8173_afe_o04_mix)),
SND_SOC_DAPM_MIXER("O09", SND_SOC_NOPM, 0, 0,
- mtk_afe_o09_mix, ARRAY_SIZE(mtk_afe_o09_mix)),
+ mt8173_afe_o09_mix, ARRAY_SIZE(mt8173_afe_o09_mix)),
SND_SOC_DAPM_MIXER("O10", SND_SOC_NOPM, 0, 0,
- mtk_afe_o10_mix, ARRAY_SIZE(mtk_afe_o10_mix)),
+ mt8173_afe_o10_mix, ARRAY_SIZE(mt8173_afe_o10_mix)),
};
-static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
+static const struct snd_soc_dapm_route mt8173_afe_pcm_routes[] = {
{"I05", NULL, "DL1"},
{"I06", NULL, "DL1"},
{"I2S Playback", NULL, "O03"},
@@ -953,140 +685,257 @@ static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
{ "O10", "I04 Switch", "I04" },
};
-static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = {
+static const struct snd_soc_dapm_route mt8173_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 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),
};
-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 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),
};
-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 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_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
+static const struct mtk_base_memif_data memif_data[MT8173_AFE_MEMIF_NUM] = {
{
.name = "DL1",
- .id = MTK_AFE_MEMIF_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,
+ .hd_shift = -1,
+ .enable_reg = AFE_DAC_CON0,
.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,
+ .msb_reg = AFE_MEMIF_MSB,
.msb_shift = 0,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
}, {
.name = "DL2",
- .id = MTK_AFE_MEMIF_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,
+ .hd_shift = -1,
+ .enable_reg = AFE_DAC_CON0,
.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,
+ .msb_reg = AFE_MEMIF_MSB,
.msb_shift = 1,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
}, {
.name = "VUL",
- .id = MTK_AFE_MEMIF_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,
+ .hd_shift = -1,
+ .enable_reg = AFE_DAC_CON0,
.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,
+ .msb_reg = AFE_MEMIF_MSB,
.msb_shift = 6,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
}, {
.name = "DAI",
- .id = MTK_AFE_MEMIF_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,
+ .hd_shift = -1,
+ .enable_reg = AFE_DAC_CON0,
.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,
+ .msb_reg = AFE_MEMIF_MSB,
.msb_shift = 5,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
}, {
.name = "AWB",
- .id = MTK_AFE_MEMIF_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,
+ .hd_shift = -1,
+ .enable_reg = AFE_DAC_CON0,
.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,
+ .msb_reg = AFE_MEMIF_MSB,
.msb_shift = 3,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
}, {
.name = "MOD_DAI",
- .id = MTK_AFE_MEMIF_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,
+ .hd_shift = -1,
+ .enable_reg = AFE_DAC_CON0,
.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,
+ .msb_reg = AFE_MEMIF_MSB,
.msb_shift = 4,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -1,
}, {
.name = "HDMI",
- .id = MTK_AFE_MEMIF_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,
+ .hd_shift = -1,
+ .enable_reg = -1,
.enable_shift = -1,
- .irq_reg_cnt = AFE_IRQ_CNT5,
+ .msb_reg = AFE_MEMIF_MSB,
+ .msb_shift = 8,
+ .agent_disable_reg = -1,
+ .agent_disable_shift = -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_shift = -1,
+ .irq_fs_maskbit = -1,
+ .irq_clr_reg = AFE_IRQ_CLR,
.irq_clr_shift = 4,
- .msb_shift = 8,
},
};
-static const struct regmap_config mtk_afe_regmap_config = {
+static const struct regmap_config mt8173_afe_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
@@ -1094,9 +943,9 @@ static const struct regmap_config mtk_afe_regmap_config = {
.cache_type = REGCACHE_NONE,
};
-static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
+static irqreturn_t mt8173_afe_irq_handler(int irq, void *dev_id)
{
- struct mtk_afe *afe = dev_id;
+ struct mtk_base_afe *afe = dev_id;
unsigned int reg_value;
int i, ret;
@@ -1107,10 +956,16 @@ static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
goto err_irq;
}
- for (i = 0; i < MTK_AFE_MEMIF_NUM; i++) {
- struct mtk_afe_memif *memif = &afe->memif[i];
+ for (i = 0; i < MT8173_AFE_MEMIF_NUM; i++) {
+ struct mtk_base_afe_memif *memif = &afe->memif[i];
+ struct mtk_base_afe_irq *irq;
- if (!(reg_value & (1 << memif->data->irq_clr_shift)))
+ if (memif->irq_usage < 0)
+ continue;
+
+ irq = &afe->irqs[memif->irq_usage];
+
+ if (!(reg_value & (1 << irq->irq_data->irq_clr_shift)))
continue;
snd_pcm_period_elapsed(memif->substream);
@@ -1118,14 +973,16 @@ static irqreturn_t mtk_afe_irq_handler(int irq, void *dev_id)
err_irq:
/* clear irq */
- regmap_write(afe->regmap, AFE_IRQ_CLR, reg_value & AFE_IRQ_STATUS_BITS);
+ 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)
+static int mt8173_afe_runtime_suspend(struct device *dev)
{
- struct mtk_afe *afe = dev_get_drvdata(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);
@@ -1134,38 +991,47 @@ static int mtk_afe_runtime_suspend(struct device *dev)
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]);
+ 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 mtk_afe_runtime_resume(struct device *dev)
+static int mt8173_afe_runtime_resume(struct device *dev)
{
- struct mtk_afe *afe = dev_get_drvdata(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->clocks[MTK_CLK_INFRASYS_AUD]);
+ ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
if (ret)
return ret;
- ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+ ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
if (ret)
goto err_infra;
- ret = clk_prepare_enable(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+ ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
if (ret)
goto err_top_aud_bus;
- ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK0]);
+ ret = clk_prepare_enable(afe_priv->clocks[MT8173_CLK_BCK0]);
if (ret)
goto err_top_aud;
- ret = clk_prepare_enable(afe->clocks[MTK_CLK_BCK1]);
+ 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);
@@ -1181,39 +1047,45 @@ static int mtk_afe_runtime_resume(struct device *dev)
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->clocks[MTK_CLK_BCK0]);
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_BCK0]);
err_top_aud:
- clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD]);
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD]);
err_top_aud_bus:
- clk_disable_unprepare(afe->clocks[MTK_CLK_TOP_PDN_AUD_BUS]);
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_TOP_PDN_AUD_BUS]);
err_infra:
- clk_disable_unprepare(afe->clocks[MTK_CLK_INFRASYS_AUD]);
+ clk_disable_unprepare(afe_priv->clocks[MT8173_CLK_INFRASYS_AUD]);
return ret;
}
-static int mtk_afe_init_audio_clk(struct mtk_afe *afe)
+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->clocks[i] = devm_clk_get(afe->dev, aud_clks[i]);
- if (IS_ERR(afe->clocks[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->clocks[i]);
+ return PTR_ERR(afe_priv->clocks[i]);
}
}
- clk_set_rate(afe->clocks[MTK_CLK_BCK0], 22579200); /* 22M */
- clk_set_rate(afe->clocks[MTK_CLK_BCK1], 24576000); /* 24M */
+ 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 mtk_afe_pcm_dev_probe(struct platform_device *pdev)
+static int mt8173_afe_pcm_dev_probe(struct platform_device *pdev)
{
int ret, i;
unsigned int irq_id;
- struct mtk_afe *afe;
+ struct mtk_base_afe *afe;
+ struct mt8173_afe_private *afe_priv;
struct resource *res;
ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
@@ -1224,6 +1096,12 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
if (!afe)
return -ENOMEM;
+ afe->platform_priv = devm_kzalloc(&pdev->dev, sizeof(*afe_priv),
+ GFP_KERNEL);
+ afe_priv = afe->platform_priv;
+ if (!afe_priv)
+ return -ENOMEM;
+
afe->dev = &pdev->dev;
irq_id = platform_get_irq(pdev, 0);
@@ -1231,7 +1109,7 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
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,
+ ret = devm_request_irq(afe->dev, irq_id, mt8173_afe_irq_handler,
0, "Afe_ISR_Handle", (void *)afe);
if (ret) {
dev_err(afe->dev, "could not request_irq\n");
@@ -1244,48 +1122,75 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
return PTR_ERR(afe->base_addr);
afe->regmap = devm_regmap_init_mmio(&pdev->dev, afe->base_addr,
- &mtk_afe_regmap_config);
+ &mt8173_afe_regmap_config);
if (IS_ERR(afe->regmap))
return PTR_ERR(afe->regmap);
/* initial audio related clock */
- ret = mtk_afe_init_audio_clk(afe);
+ ret = mt8173_afe_init_audio_clk(afe);
if (ret) {
- dev_err(afe->dev, "mtk_afe_init_audio_clk fail\n");
+ dev_err(afe->dev, "mt8173_afe_init_audio_clk fail\n");
return ret;
}
- for (i = 0; i < MTK_AFE_MEMIF_NUM; i++)
+ /* memif % irq initialize*/
+ afe->memif_size = MT8173_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 = MT8173_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->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(&pdev->dev);
if (!pm_runtime_enabled(&pdev->dev)) {
- ret = mtk_afe_runtime_resume(&pdev->dev);
+ ret = mt8173_afe_runtime_resume(&pdev->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 = 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));
+ &mt8173_afe_pcm_dai_component,
+ mt8173_afe_pcm_dais,
+ ARRAY_SIZE(mt8173_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));
+ &mt8173_afe_hdmi_dai_component,
+ mt8173_afe_hdmi_dais,
+ ARRAY_SIZE(mt8173_afe_hdmi_dais));
if (ret)
goto err_comp;
- dev_info(&pdev->dev, "MTK AFE driver initialized.\n");
+ dev_info(&pdev->dev, "MT8173 AFE driver initialized.\n");
return 0;
err_comp:
@@ -1297,38 +1202,38 @@ err_pm_disable:
return ret;
}
-static int mtk_afe_pcm_dev_remove(struct platform_device *pdev)
+static int mt8173_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);
+ mt8173_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[] = {
+static const struct of_device_id mt8173_afe_pcm_dt_match[] = {
{ .compatible = "mediatek,mt8173-afe-pcm", },
{ }
};
-MODULE_DEVICE_TABLE(of, mtk_afe_pcm_dt_match);
+MODULE_DEVICE_TABLE(of, mt8173_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 const struct dev_pm_ops mt8173_afe_pm_ops = {
+ SET_RUNTIME_PM_OPS(mt8173_afe_runtime_suspend,
+ mt8173_afe_runtime_resume, NULL)
};
-static struct platform_driver mtk_afe_pcm_driver = {
+static struct platform_driver mt8173_afe_pcm_driver = {
.driver = {
- .name = "mtk-afe-pcm",
- .of_match_table = mtk_afe_pcm_dt_match,
- .pm = &mtk_afe_pm_ops,
+ .name = "mt8173-afe-pcm",
+ .of_match_table = mt8173_afe_pcm_dt_match,
+ .pm = &mt8173_afe_pm_ops,
},
- .probe = mtk_afe_pcm_dev_probe,
- .remove = mtk_afe_pcm_dev_remove,
+ .probe = mt8173_afe_pcm_dev_probe,
+ .remove = mt8173_afe_pcm_dev_remove,
};
-module_platform_driver(mtk_afe_pcm_driver);
+module_platform_driver(mt8173_afe_pcm_driver);
MODULE_DESCRIPTION("Mediatek ALSA SoC AFE platform driver");
MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
diff --git a/sound/soc/mediatek/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c
index 71a1a35047ba..5524a2c727ec 100644
--- a/sound/soc/mediatek/mt8173-max98090.c
+++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c
@@ -18,7 +18,7 @@
#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;
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
index 58e083642dea..467f7049a288 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5514.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
@@ -19,7 +19,7 @@
#include <linux/of_gpio.h>
#include <sound/soc.h>
#include <sound/jack.h>
-#include "../codecs/rt5645.h"
+#include "../../codecs/rt5645.h"
#define MCLK_FOR_CODECS 12288000
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
index bb593926c62d..1b8b2a778845 100644
--- a/sound/soc/mediatek/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
@@ -19,8 +19,8 @@
#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
diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
index a27a6673dbe3..ba65f4157a7e 100644
--- a/sound/soc/mediatek/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -19,10 +19,24 @@
#include <linux/of_gpio.h>
#include <sound/soc.h>
#include <sound/jack.h>
-#include "../codecs/rt5645.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("Speaker", NULL),
SND_SOC_DAPM_MIC("Int Mic", NULL),
@@ -54,13 +68,29 @@ static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int mclk_clock;
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 (i = 0; i < rtd->num_codecs; i++) {
struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
- /* pll from mclk 12.288M */
- ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
+ /* pll from mclk */
+ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, mclk_clock,
params_rate(params) * 512);
if (ret)
return ret;
@@ -139,7 +169,9 @@ static struct snd_soc_dai_link_component mt8173_rt5650_codecs[] = {
enum {
DAI_LINK_PLAYBACK,
DAI_LINK_CAPTURE,
+ DAI_LINK_HDMI,
DAI_LINK_CODEC_I2S,
+ DAI_LINK_HDMI_I2S,
};
/* Digital audio interface glue - connects codec <---> CPU */
@@ -165,6 +197,16 @@ static struct snd_soc_dai_link mt8173_rt5650_dais[] = {
.dynamic = 1,
.dpcm_capture = 1,
},
+ [DAI_LINK_HDMI] = {
+ .name = "HDMI",
+ .stream_name = "HDMI PCM",
+ .cpu_dai_name = "HDMI",
+ .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,
+ },
/* Back End DAI links */
[DAI_LINK_CODEC_I2S] = {
.name = "Codec",
@@ -180,6 +222,13 @@ static struct snd_soc_dai_link mt8173_rt5650_dais[] = {
.dpcm_playback = 1,
.dpcm_capture = 1,
},
+ [DAI_LINK_HDMI_I2S] = {
+ .name = "HDMI BE",
+ .cpu_dai_name = "HDMIO",
+ .no_pcm = 1,
+ .codec_dai_name = "i2s-hifi",
+ .dpcm_playback = 1,
+ },
};
static struct snd_soc_card mt8173_rt5650_card = {
@@ -243,6 +292,24 @@ static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
mt8173_rt5650_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].codec_of_node =
+ of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
+ if (!mt8173_rt5650_dais[DAI_LINK_HDMI_I2S].codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
diff --git a/sound/soc/mediatek/mtk-afe-common.h b/sound/soc/mediatek/mtk-afe-common.h
deleted file mode 100644
index f341f623e887..000000000000
--- a/sound/soc/mediatek/mtk-afe-common.h
+++ /dev/null
@@ -1,101 +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;
- int msb_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/omap/Kconfig b/sound/soc/omap/Kconfig
index 5185a3844da9..f5451c78ede5 100644
--- a/sound/soc/omap/Kconfig
+++ b/sound/soc/omap/Kconfig
@@ -100,13 +100,14 @@ config SND_OMAP_SOC_OMAP_TWL4030
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 TWL6040_CORE && SND_OMAP_SOC && COMMON_CLK
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)
+ select CLK_TWL6040
help
Say Y if you want to add support for SoC audio on OMAP boards using
ABE and twl6040 codec. This driver currently supports:
diff --git a/sound/soc/omap/mcbsp.c b/sound/soc/omap/mcbsp.c
index 4a16e778966b..06fec5699cc8 100644
--- a/sound/soc/omap/mcbsp.c
+++ b/sound/soc/omap/mcbsp.c
@@ -221,7 +221,8 @@ void omap_mcbsp_config(struct omap_mcbsp *mcbsp,
/* Enable TX/RX sync error interrupts by default */
if (mcbsp->irq)
- MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN);
+ MCBSP_WRITE(mcbsp, IRQEN, RSYNCERREN | XSYNCERREN |
+ RUNDFLEN | ROVFLEN | XUNDFLEN | XOVFLEN);
}
/**
@@ -257,8 +258,8 @@ 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);
+ if (mcbsp->pdata->force_ick_on)
+ mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, true);
/* Disable Sidetone clock auto-gating for normal operation */
w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
@@ -287,8 +288,8 @@ static void omap_st_off(struct omap_mcbsp *mcbsp)
w = MCBSP_ST_READ(mcbsp, SYSCONFIG);
MCBSP_ST_WRITE(mcbsp, SYSCONFIG, w | ST_AUTOIDLE);
- if (mcbsp->pdata->enable_st_clock)
- mcbsp->pdata->enable_st_clock(mcbsp->id, 0);
+ if (mcbsp->pdata->force_ick_on)
+ mcbsp->pdata->force_ick_on(mcbsp->st_data->mcbsp_iclk, false);
}
static void omap_st_fir_write(struct omap_mcbsp *mcbsp, s16 *fir)
@@ -946,6 +947,13 @@ static int omap_st_add(struct omap_mcbsp *mcbsp, struct resource *res)
if (!st_data)
return -ENOMEM;
+ st_data->mcbsp_iclk = clk_get(mcbsp->dev, "ick");
+ if (IS_ERR(st_data->mcbsp_iclk)) {
+ dev_warn(mcbsp->dev,
+ "Failed to get ick, sidetone might be broken\n");
+ st_data->mcbsp_iclk = NULL;
+ }
+
st_data->io_base_st = devm_ioremap(mcbsp->dev, res->start,
resource_size(res));
if (!st_data->io_base_st)
@@ -1088,11 +1096,13 @@ err_thres:
return ret;
}
-void omap_mcbsp_sysfs_remove(struct omap_mcbsp *mcbsp)
+void omap_mcbsp_cleanup(struct omap_mcbsp *mcbsp)
{
if (mcbsp->pdata->buffer_size)
sysfs_remove_group(&mcbsp->dev->kobj, &additional_attr_group);
- if (mcbsp->st_data)
+ if (mcbsp->st_data) {
sysfs_remove_group(&mcbsp->dev->kobj, &sidetone_attr_group);
+ clk_put(mcbsp->st_data->mcbsp_iclk);
+ }
}
diff --git a/sound/soc/omap/mcbsp.h b/sound/soc/omap/mcbsp.h
index 96d1b086bcf8..61e93b1c185d 100644
--- a/sound/soc/omap/mcbsp.h
+++ b/sound/soc/omap/mcbsp.h
@@ -280,6 +280,7 @@ struct omap_mcbsp_reg_cfg {
struct omap_mcbsp_st_data {
void __iomem *io_base_st;
+ struct clk *mcbsp_iclk;
bool running;
bool enabled;
s16 taps[128]; /* Sidetone filter coefficients */
@@ -349,6 +350,6 @@ int omap_st_disable(struct omap_mcbsp *mcbsp);
int omap_st_is_enabled(struct omap_mcbsp *mcbsp);
int omap_mcbsp_init(struct platform_device *pdev);
-void omap_mcbsp_sysfs_remove(struct omap_mcbsp *mcbsp);
+void omap_mcbsp_cleanup(struct omap_mcbsp *mcbsp);
#endif /* __ASOC_MCBSP_H */
diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c
index 0843a68f277c..89fe95e877db 100644
--- a/sound/soc/omap/omap-abe-twl6040.c
+++ b/sound/soc/omap/omap-abe-twl6040.c
@@ -38,10 +38,10 @@
struct abe_twl6040 {
int jack_detection; /* board can detect jack events */
int mclk_freq; /* MCLK frequency speed for twl6040 */
-
- struct platform_device *dmic_codec_dev;
};
+struct platform_device *dmic_codec_dev;
+
static int omap_abe_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
@@ -258,8 +258,6 @@ static int omap_abe_probe(struct platform_device *pdev)
if (priv == NULL)
return -ENOMEM;
- priv->dmic_codec_dev = ERR_PTR(-EINVAL);
-
if (snd_soc_of_parse_card_name(card, "ti,model")) {
dev_err(&pdev->dev, "Card name is not provided\n");
return -ENODEV;
@@ -284,13 +282,6 @@ static int omap_abe_probe(struct platform_device *pdev)
num_links = 2;
abe_twl6040_dai_links[1].cpu_of_node = dai_node;
abe_twl6040_dai_links[1].platform_of_node = dai_node;
-
- priv->dmic_codec_dev = platform_device_register_simple(
- "dmic-codec", -1, NULL, 0);
- if (IS_ERR(priv->dmic_codec_dev)) {
- dev_err(&pdev->dev, "Can't instantiate dmic-codec\n");
- return PTR_ERR(priv->dmic_codec_dev);
- }
} else {
num_links = 1;
}
@@ -299,16 +290,14 @@ static int omap_abe_probe(struct platform_device *pdev)
of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq);
if (!priv->mclk_freq) {
dev_err(&pdev->dev, "MCLK frequency not provided\n");
- ret = -EINVAL;
- goto err_unregister;
+ return -EINVAL;
}
card->fully_routed = 1;
if (!priv->mclk_freq) {
dev_err(&pdev->dev, "MCLK frequency missing\n");
- ret = -ENODEV;
- goto err_unregister;
+ return -ENODEV;
}
card->dai_link = abe_twl6040_dai_links;
@@ -316,35 +305,14 @@ static int omap_abe_probe(struct platform_device *pdev)
snd_soc_card_set_drvdata(card, priv);
- ret = snd_soc_register_card(card);
- if (ret) {
- dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "devm_snd_soc_register_card() failed: %d\n",
ret);
- goto err_unregister;
- }
-
- return 0;
-
-err_unregister:
- if (!IS_ERR(priv->dmic_codec_dev))
- platform_device_unregister(priv->dmic_codec_dev);
return ret;
}
-static int omap_abe_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
-
- snd_soc_unregister_card(card);
-
- if (!IS_ERR(priv->dmic_codec_dev))
- platform_device_unregister(priv->dmic_codec_dev);
-
- return 0;
-}
-
static const struct of_device_id omap_abe_of_match[] = {
{.compatible = "ti,abe-twl6040", },
{ },
@@ -358,10 +326,35 @@ static struct platform_driver omap_abe_driver = {
.of_match_table = omap_abe_of_match,
},
.probe = omap_abe_probe,
- .remove = omap_abe_remove,
};
-module_platform_driver(omap_abe_driver);
+static int __init omap_abe_init(void)
+{
+ int ret;
+
+ dmic_codec_dev = platform_device_register_simple("dmic-codec", -1, NULL,
+ 0);
+ if (IS_ERR(dmic_codec_dev)) {
+ pr_err("%s: dmic-codec device registration failed\n", __func__);
+ return PTR_ERR(dmic_codec_dev);
+ }
+
+ ret = platform_driver_register(&omap_abe_driver);
+ if (ret) {
+ pr_err("%s: platform driver registration failed\n", __func__);
+ platform_device_unregister(dmic_codec_dev);
+ }
+
+ return ret;
+}
+module_init(omap_abe_init);
+
+static void __exit omap_abe_exit(void)
+{
+ platform_driver_unregister(&omap_abe_driver);
+ platform_device_unregister(dmic_codec_dev);
+}
+module_exit(omap_abe_exit);
MODULE_AUTHOR("Misael Lopez Cruz <misael.lopez@ti.com>");
MODULE_DESCRIPTION("ALSA SoC for OMAP boards with ABE and twl6040 codec");
diff --git a/sound/soc/omap/omap-hdmi-audio.c b/sound/soc/omap/omap-hdmi-audio.c
index 64425d352962..888133f9e65d 100644
--- a/sound/soc/omap/omap-hdmi-audio.c
+++ b/sound/soc/omap/omap-hdmi-audio.c
@@ -28,7 +28,6 @@
#include <sound/asoundef.h>
#include <sound/omap-pcm.h>
#include <sound/omap-hdmi-audio.h>
-#include <video/omapdss.h>
#define DRV_NAME "omap-hdmi-audio"
diff --git a/sound/soc/omap/omap-mcbsp.c b/sound/soc/omap/omap-mcbsp.c
index fd99d89de6a8..d018e966e533 100644
--- a/sound/soc/omap/omap-mcbsp.c
+++ b/sound/soc/omap/omap-mcbsp.c
@@ -788,6 +788,7 @@ static int asoc_mcbsp_probe(struct platform_device *pdev)
match = of_match_device(omap_mcbsp_of_match, &pdev->dev);
if (match) {
struct device_node *node = pdev->dev.of_node;
+ struct omap_mcbsp_platform_data *pdata_quirk = pdata;
int buffer_size;
pdata = devm_kzalloc(&pdev->dev,
@@ -799,6 +800,8 @@ static int asoc_mcbsp_probe(struct platform_device *pdev)
memcpy(pdata, match->data, sizeof(*pdata));
if (!of_property_read_u32(node, "ti,buffer-size", &buffer_size))
pdata->buffer_size = buffer_size;
+ if (pdata_quirk)
+ pdata->force_ick_on = pdata_quirk->force_ick_on;
} else if (!pdata) {
dev_err(&pdev->dev, "missing platform data.\n");
return -EINVAL;
@@ -832,7 +835,7 @@ static int asoc_mcbsp_remove(struct platform_device *pdev)
if (mcbsp->pdata->ops && mcbsp->pdata->ops->free)
mcbsp->pdata->ops->free(mcbsp->id);
- omap_mcbsp_sysfs_remove(mcbsp);
+ omap_mcbsp_cleanup(mcbsp);
clk_put(mcbsp->fclk);
diff --git a/sound/soc/omap/omap-mcpdm.c b/sound/soc/omap/omap-mcpdm.c
index b837265ac3e9..64609c77a79d 100644
--- a/sound/soc/omap/omap-mcpdm.c
+++ b/sound/soc/omap/omap-mcpdm.c
@@ -66,6 +66,9 @@ struct omap_mcpdm {
/* McPDM needs to be restarted due to runtime reconfiguration */
bool restart;
+ /* pm state for suspend/resume handling */
+ int pm_active_count;
+
struct snd_dmaengine_dai_dma_data dma_data[2];
};
@@ -173,6 +176,10 @@ static inline int omap_mcpdm_active(struct omap_mcpdm *mcpdm)
*/
static void omap_mcpdm_open_streams(struct omap_mcpdm *mcpdm)
{
+ u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
+
+ omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN);
+
omap_mcpdm_write(mcpdm, MCPDM_REG_IRQENABLE_SET,
MCPDM_DN_IRQ_EMPTY | MCPDM_DN_IRQ_FULL |
MCPDM_UP_IRQ_EMPTY | MCPDM_UP_IRQ_FULL);
@@ -258,12 +265,9 @@ static int omap_mcpdm_dai_startup(struct snd_pcm_substream *substream,
mutex_lock(&mcpdm->mutex);
- if (!dai->active) {
- u32 ctrl = omap_mcpdm_read(mcpdm, MCPDM_REG_CTRL);
-
- omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, ctrl | MCPDM_WD_EN);
+ if (!dai->active)
omap_mcpdm_open_streams(mcpdm);
- }
+
mutex_unlock(&mcpdm->mutex);
return 0;
@@ -390,8 +394,8 @@ static int omap_mcpdm_probe(struct snd_soc_dai *dai)
pm_runtime_get_sync(mcpdm->dev);
omap_mcpdm_write(mcpdm, MCPDM_REG_CTRL, 0x00);
- ret = devm_request_irq(mcpdm->dev, mcpdm->irq, omap_mcpdm_irq_handler,
- 0, "McPDM", (void *)mcpdm);
+ ret = request_irq(mcpdm->irq, omap_mcpdm_irq_handler, 0, "McPDM",
+ (void *)mcpdm);
pm_runtime_put_sync(mcpdm->dev);
@@ -416,17 +420,61 @@ static int omap_mcpdm_remove(struct snd_soc_dai *dai)
{
struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+ free_irq(mcpdm->irq, (void *)mcpdm);
pm_runtime_disable(mcpdm->dev);
return 0;
}
+#ifdef CONFIG_PM_SLEEP
+static int omap_mcpdm_suspend(struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+
+ if (dai->active) {
+ omap_mcpdm_stop(mcpdm);
+ omap_mcpdm_close_streams(mcpdm);
+ }
+
+ mcpdm->pm_active_count = 0;
+ while (pm_runtime_active(mcpdm->dev)) {
+ pm_runtime_put_sync(mcpdm->dev);
+ mcpdm->pm_active_count++;
+ }
+
+ return 0;
+}
+
+static int omap_mcpdm_resume(struct snd_soc_dai *dai)
+{
+ struct omap_mcpdm *mcpdm = snd_soc_dai_get_drvdata(dai);
+
+ if (mcpdm->pm_active_count) {
+ while (mcpdm->pm_active_count--)
+ pm_runtime_get_sync(mcpdm->dev);
+
+ if (dai->active) {
+ omap_mcpdm_open_streams(mcpdm);
+ omap_mcpdm_start(mcpdm);
+ }
+ }
+
+
+ return 0;
+}
+#else
+#define omap_mcpdm_suspend NULL
+#define omap_mcpdm_resume NULL
+#endif
+
#define OMAP_MCPDM_RATES (SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000)
#define OMAP_MCPDM_FORMATS SNDRV_PCM_FMTBIT_S32_LE
static struct snd_soc_dai_driver omap_mcpdm_dai = {
.probe = omap_mcpdm_probe,
.remove = omap_mcpdm_remove,
+ .suspend = omap_mcpdm_suspend,
+ .resume = omap_mcpdm_resume,
.probe_order = SND_SOC_COMP_ORDER_LATE,
.remove_order = SND_SOC_COMP_ORDER_EARLY,
.playback = {
diff --git a/sound/soc/omap/omap-pcm.c b/sound/soc/omap/omap-pcm.c
index a84f677234f0..94e9ff791f3a 100644
--- a/sound/soc/omap/omap-pcm.c
+++ b/sound/soc/omap/omap-pcm.c
@@ -58,7 +58,7 @@ static void omap_pcm_limit_supported_formats(void)
{
int i;
- for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
+ for (i = 0; i <= SNDRV_PCM_FORMAT_LAST; i++) {
switch (snd_pcm_format_physical_width(i)) {
case 8:
case 16:
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index 54949242bc70..a76845748a10 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -33,7 +33,6 @@
#include <sound/pcm.h>
#include <sound/soc.h>
#include <linux/platform_data/asoc-ti-mcbsp.h>
-#include "../codecs/tpa6130a2.h"
#include <asm/mach-types.h>
@@ -164,19 +163,6 @@ static int rx51_spk_event(struct snd_soc_dapm_widget *w,
return 0;
}
-static int rx51_hp_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);
-
- if (SND_SOC_DAPM_EVENT_ON(event))
- tpa6130a2_stereo_enable(codec, 1);
- else
- tpa6130a2_stereo_enable(codec, 0);
-
- return 0;
-}
-
static int rx51_get_input(struct snd_kcontrol *kcontrol,
struct snd_ctl_elem_value *ucontrol)
{
@@ -235,7 +221,7 @@ static struct snd_soc_jack_gpio rx51_av_jack_gpios[] = {
static const struct snd_soc_dapm_widget aic34_dapm_widgets[] = {
SND_SOC_DAPM_SPK("Ext Spk", rx51_spk_event),
SND_SOC_DAPM_MIC("DMic", NULL),
- SND_SOC_DAPM_HP("Headphone Jack", rx51_hp_event),
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
SND_SOC_DAPM_MIC("HS Mic", NULL),
SND_SOC_DAPM_LINE("FM Transmitter", NULL),
SND_SOC_DAPM_SPK("Earphone", NULL),
@@ -246,11 +232,14 @@ static const struct snd_soc_dapm_route audio_map[] = {
{"Ext Spk", NULL, "HPROUT"},
{"Ext Spk", NULL, "HPLCOM"},
{"Ext Spk", NULL, "HPRCOM"},
- {"Headphone Jack", NULL, "LLOUT"},
- {"Headphone Jack", NULL, "RLOUT"},
{"FM Transmitter", NULL, "LLOUT"},
{"FM Transmitter", NULL, "RLOUT"},
+ {"Headphone Jack", NULL, "TPA6130A2 HPLEFT"},
+ {"Headphone Jack", NULL, "TPA6130A2 HPRIGHT"},
+ {"TPA6130A2 LEFTIN", NULL, "LLOUT"},
+ {"TPA6130A2 RIGHTIN", NULL, "RLOUT"},
+
{"DMic Rate 64", NULL, "DMic"},
{"DMic", NULL, "Mic Bias"},
@@ -286,16 +275,10 @@ static const struct snd_kcontrol_new aic34_rx51_controls[] = {
static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
{
- struct snd_soc_codec *codec = rtd->codec;
struct snd_soc_card *card = rtd->card;
struct rx51_audio_pdata *pdata = snd_soc_card_get_drvdata(card);
int err;
- err = tpa6130a2_add_controls(codec);
- if (err < 0) {
- dev_err(card->dev, "Failed to add TPA6130A2 controls\n");
- return err;
- }
snd_soc_limit_volume(card, "TPA6130A2 Headphone Playback Volume", 42);
err = omap_mcbsp_st_add_controls(rtd, 2);
@@ -357,6 +340,10 @@ static struct snd_soc_aux_dev rx51_aux_dev[] = {
.name = "TLV320AIC34b",
.codec_name = "tlv320aic3x-codec.2-0019",
},
+ {
+ .name = "TPA61320A2",
+ .codec_name = "tpa6130a2.2-0060",
+ },
};
static struct snd_soc_codec_conf rx51_codec_conf[] = {
@@ -364,6 +351,10 @@ static struct snd_soc_codec_conf rx51_codec_conf[] = {
.dev_name = "tlv320aic3x-codec.2-0019",
.name_prefix = "b",
},
+ {
+ .dev_name = "tpa6130a2.2-0060",
+ .name_prefix = "TPA6130A2",
+ },
};
/* Audio card */
@@ -435,11 +426,10 @@ static int rx51_soc_probe(struct platform_device *pdev)
dev_err(&pdev->dev, "Headphone amplifier node is not provided\n");
return -EINVAL;
}
-
- /* TODO: tpa6130a2a driver supports only a single instance, so
- * this driver ignores the headphone-amplifier node for now.
- * It's already mandatory in the DT binding to be future proof.
- */
+ rx51_aux_dev[1].codec_name = NULL;
+ rx51_aux_dev[1].codec_of_node = dai_node;
+ rx51_codec_conf[1].dev_name = NULL;
+ rx51_codec_conf[1].of_node = dai_node;
}
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
diff --git a/sound/soc/qcom/apq8016_sbc.c b/sound/soc/qcom/apq8016_sbc.c
index 1289543c8fb2..07f91e918b23 100644
--- a/sound/soc/qcom/apq8016_sbc.c
+++ b/sound/soc/qcom/apq8016_sbc.c
@@ -85,6 +85,15 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card)
return ERR_PTR(ret);
}
+ /* DAPM routes */
+ if (of_property_read_bool(node, "qcom,audio-routing")) {
+ ret = snd_soc_of_parse_audio_routing(card,
+ "qcom,audio-routing");
+ if (ret)
+ return ERR_PTR(ret);
+ }
+
+
/* Populate links */
num_links = of_get_child_count(node);
@@ -147,6 +156,15 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card)
return data;
}
+static const struct snd_soc_dapm_widget apq8016_sbc_dapm_widgets[] = {
+
+ 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)
{
struct device *dev = &pdev->dev;
@@ -159,6 +177,8 @@ static int apq8016_sbc_platform_probe(struct platform_device *pdev)
return -ENOMEM;
card->dev = dev;
+ card->dapm_widgets = apq8016_sbc_dapm_widgets;
+ card->num_dapm_widgets = ARRAY_SIZE(apq8016_sbc_dapm_widgets);
data = apq8016_sbc_parse_of(card);
if (IS_ERR(data)) {
dev_err(&pdev->dev, "Error resolving dai links: %ld\n",
diff --git a/sound/soc/qcom/lpass-platform.c b/sound/soc/qcom/lpass-platform.c
index db000c6987a1..e2ff538a8aa5 100644
--- a/sound/soc/qcom/lpass-platform.c
+++ b/sound/soc/qcom/lpass-platform.c
@@ -84,9 +84,9 @@ static int lpass_platform_pcmops_hw_params(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_pcm_data *pcm_data = drvdata->private_data;
struct lpass_variant *v = drvdata->variant;
snd_pcm_format_t format = params_format(params);
unsigned int channels = params_channels(params);
@@ -177,9 +177,9 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
static int lpass_platform_pcmops_hw_free(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_pcm_data *pcm_data = drvdata->private_data;
struct lpass_variant *v = drvdata->variant;
unsigned int reg;
int ret;
@@ -201,9 +201,9 @@ static int lpass_platform_pcmops_prepare(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_pcm_data *pcm_data = drvdata->private_data;
struct lpass_variant *v = drvdata->variant;
int ret, ch, dir = substream->stream;
@@ -255,9 +255,9 @@ static int lpass_platform_pcmops_trigger(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_pcm_data *pcm_data = drvdata->private_data;
struct lpass_variant *v = drvdata->variant;
int ret, ch, dir = substream->stream;
@@ -331,9 +331,9 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
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_pcm_data *pcm_data = drvdata->private_data;
struct lpass_variant *v = drvdata->variant;
unsigned int base_addr, curr_addr;
int ret, ch, dir = substream->stream;
@@ -372,7 +372,7 @@ static int lpass_platform_pcmops_mmap(struct snd_pcm_substream *substream,
runtime->dma_bytes);
}
-static struct snd_pcm_ops lpass_platform_pcm_ops = {
+static const 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,
@@ -483,7 +483,7 @@ static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
return -ENOMEM;
data->i2s_port = cpu_dai->driver->id;
- snd_soc_pcm_set_drvdata(soc_runtime, data);
+ drvdata->private_data = data;
psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
if (psubstream) {
@@ -570,8 +570,8 @@ static void lpass_platform_pcm_free(struct snd_pcm *pcm)
substream = pcm->streams[i].substream;
if (substream) {
rt = substream->private_data;
- data = snd_soc_pcm_get_drvdata(rt);
drvdata = snd_soc_platform_get_drvdata(rt->platform);
+ data = drvdata->private_data;
ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
? data->rdma_ch
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 30714ad1e138..35b3cea8207d 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -58,6 +58,8 @@ struct lpass_data {
/* 8016 specific */
struct clk *pcnoc_mport_clk;
struct clk *pcnoc_sway_clk;
+
+ void *private_data;
};
/* Vairant data per each SOC */
diff --git a/sound/soc/rockchip/Kconfig b/sound/soc/rockchip/Kconfig
index f1e0c703e0d2..c783f9a22595 100644
--- a/sound/soc/rockchip/Kconfig
+++ b/sound/soc/rockchip/Kconfig
@@ -41,3 +41,15 @@ config SND_SOC_ROCKCHIP_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_RK3399_GRU_SOUND
+ tristate "ASoC support multiple codecs for Rockchip RK3399 GRU boards"
+ depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP && SPI
+ select SND_SOC_ROCKCHIP_I2S
+ select SND_SOC_MAX98357A
+ select SND_SOC_RT5514
+ select SND_SOC_DA7219
+ select SND_SOC_RT5514_SPI
+ help
+ Say Y or M here if you want to add support multiple codecs for SoC
+ audio on Rockchip RK3399 GRU boards.
diff --git a/sound/soc/rockchip/Makefile b/sound/soc/rockchip/Makefile
index c0bf560125f3..84e5c7c700e7 100644
--- a/sound/soc/rockchip/Makefile
+++ b/sound/soc/rockchip/Makefile
@@ -7,6 +7,8 @@ obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
snd-soc-rockchip-max98090-objs := rockchip_max98090.o
snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o
+snd-soc-rk3399-gru-sound-objs := 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_RK3399_GRU_SOUND) += snd-soc-rk3399-gru-sound.o
diff --git a/sound/soc/rockchip/rk3399_gru_sound.c b/sound/soc/rockchip/rk3399_gru_sound.c
new file mode 100644
index 000000000000..9ed735a6cf49
--- /dev/null
+++ b/sound/soc/rockchip/rk3399_gru_sound.c
@@ -0,0 +1,397 @@
+/*
+ * Rockchip machine ASoC driver for boards using MAX98357A/RT5514/DA7219
+ *
+ * Copyright (c) 2016, 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 <linux/spi/spi.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/da7219-aad.h"
+#include "../codecs/rt5514.h"
+
+#define DRV_NAME "rk3399-gru-sound"
+
+#define SOUND_FS 256
+
+unsigned int rt5514_dmic_delay;
+
+static struct snd_soc_jack rockchip_sound_jack;
+
+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_MIC("Int Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route rockchip_dapm_routes[] = {
+ /* Input Lines */
+ {"MIC", NULL, "Headset Mic"},
+ {"DMIC1L", NULL, "Int Mic"},
+ {"DMIC1R", NULL, "Int Mic"},
+
+ /* Output Lines */
+ {"Headphones", NULL, "HPL"},
+ {"Headphones", NULL, "HPR"},
+ {"Speakers", NULL, "Speaker"},
+};
+
+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("Int Mic"),
+};
+
+static int rockchip_sound_max98357a_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ unsigned int mclk;
+ int ret;
+
+ /* max98357a supports these sample rates */
+ switch (params_rate(params)) {
+ case 8000:
+ case 16000:
+ case 48000:
+ case 96000:
+ mclk = params_rate(params) * SOUND_FS;
+ break;
+ default:
+ dev_err(rtd->card->dev, "%s() doesn't support this sample rate: %d\n",
+ __func__, params_rate(params));
+ return -EINVAL;
+ }
+
+ ret = snd_soc_dai_set_sysclk(rtd->cpu_dai, 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 = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ 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(rt5514_dmic_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 = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ 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 int rockchip_sound_da7219_init(struct snd_soc_pcm_runtime *rtd)
+{
+ struct snd_soc_codec *codec = rtd->codec_dais[0]->codec;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ 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(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, NULL, 0);
+
+ 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_MEDIA);
+ 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);
+
+ da7219_aad_jack_det(codec, &rockchip_sound_jack);
+
+ return 0;
+}
+
+static struct snd_soc_ops rockchip_sound_max98357a_ops = {
+ .hw_params = rockchip_sound_max98357a_hw_params,
+};
+
+static struct snd_soc_ops rockchip_sound_rt5514_ops = {
+ .hw_params = rockchip_sound_rt5514_hw_params,
+};
+
+static struct snd_soc_ops rockchip_sound_da7219_ops = {
+ .hw_params = rockchip_sound_da7219_hw_params,
+};
+
+enum {
+ DAILINK_MAX98357A,
+ DAILINK_RT5514,
+ DAILINK_DA7219,
+ DAILINK_RT5514_DSP,
+};
+
+#define DAILINK_ENTITIES (DAILINK_DA7219 + 1)
+
+static struct snd_soc_dai_link rockchip_dailinks[] = {
+ [DAILINK_MAX98357A] = {
+ .name = "MAX98357A",
+ .stream_name = "MAX98357A PCM",
+ .codec_dai_name = "HiFi",
+ .ops = &rockchip_sound_max98357a_ops,
+ /* set max98357a as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ },
+ [DAILINK_RT5514] = {
+ .name = "RT5514",
+ .stream_name = "RT5514 PCM",
+ .codec_dai_name = "rt5514-aif1",
+ .ops = &rockchip_sound_rt5514_ops,
+ /* set rt5514 as slave */
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+ },
+ [DAILINK_DA7219] = {
+ .name = "DA7219",
+ .stream_name = "DA7219 PCM",
+ .codec_dai_name = "da7219-hifi",
+ .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_CBS_CFS,
+ },
+ /* RT5514 DSP for voice wakeup via spi bus */
+ [DAILINK_RT5514_DSP] = {
+ .name = "RT5514 DSP",
+ .stream_name = "Wake on Voice",
+ .codec_name = "snd-soc-dummy",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ },
+};
+
+static struct snd_soc_card rockchip_sound_card = {
+ .name = "rk3399-gru-sound",
+ .owner = THIS_MODULE,
+ .dai_link = rockchip_dailinks,
+ .num_links = ARRAY_SIZE(rockchip_dailinks),
+ .dapm_widgets = rockchip_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(rockchip_dapm_widgets),
+ .dapm_routes = rockchip_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(rockchip_dapm_routes),
+ .controls = rockchip_controls,
+ .num_controls = ARRAY_SIZE(rockchip_controls),
+};
+
+static int rockchip_sound_match_stub(struct device *dev, void *data)
+{
+ return 1;
+}
+
+static int rockchip_sound_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &rockchip_sound_card;
+ struct device_node *cpu_node;
+ struct device *dev;
+ struct device_driver *drv;
+ int i, ret;
+
+ cpu_node = of_parse_phandle(pdev->dev.of_node, "rockchip,cpu", 0);
+ if (!cpu_node) {
+ dev_err(&pdev->dev, "Property 'rockchip,cpu' missing or invalid\n");
+ return -EINVAL;
+ }
+
+ for (i = 0; i < DAILINK_ENTITIES; i++) {
+ rockchip_dailinks[i].platform_of_node = cpu_node;
+ rockchip_dailinks[i].cpu_of_node = cpu_node;
+
+ rockchip_dailinks[i].codec_of_node =
+ of_parse_phandle(pdev->dev.of_node, "rockchip,codec", i);
+ if (!rockchip_dailinks[i].codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property[%d] 'rockchip,codec' missing or invalid\n", i);
+ return -EINVAL;
+ }
+ }
+
+ /**
+ * To acquire the spi driver of the rt5514 and set the dai-links names
+ * for soc_bind_dai_link
+ */
+ drv = driver_find("rt5514", &spi_bus_type);
+ if (!drv) {
+ dev_err(&pdev->dev, "Can not find the rt5514 driver at the spi bus\n");
+ return -EINVAL;
+ }
+
+ dev = driver_find_device(drv, NULL, NULL, rockchip_sound_match_stub);
+ if (!dev) {
+ dev_err(&pdev->dev, "Can not find the rt5514 device\n");
+ return -ENODEV;
+ }
+
+ /* Set DMIC delay */
+ ret = device_property_read_u32(&pdev->dev, "dmic-delay",
+ &rt5514_dmic_delay);
+ if (ret) {
+ rt5514_dmic_delay = 0;
+ dev_dbg(&pdev->dev,
+ "no optional property 'dmic-delay' found, default: no delay\n");
+ }
+
+ rockchip_dailinks[DAILINK_RT5514_DSP].cpu_name = kstrdup_const(dev_name(dev), GFP_KERNEL);
+ rockchip_dailinks[DAILINK_RT5514_DSP].cpu_dai_name = kstrdup_const(dev_name(dev), GFP_KERNEL);
+ rockchip_dailinks[DAILINK_RT5514_DSP].platform_name = kstrdup_const(dev_name(dev), GFP_KERNEL);
+
+ 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);
+
+ return ret;
+}
+
+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 574c6af28c06..974915cb4c4f 100644
--- a/sound/soc/rockchip/rockchip_i2s.c
+++ b/sound/soc/rockchip/rockchip_i2s.c
@@ -11,8 +11,10 @@
*/
#include <linux/module.h>
+#include <linux/mfd/syscon.h>
#include <linux/delay.h>
#include <linux/of_gpio.h>
+#include <linux/of_device.h>
#include <linux/clk.h>
#include <linux/pm_runtime.h>
#include <linux/regmap.h>
@@ -23,6 +25,11 @@
#define DRV_NAME "rockchip-i2s"
+struct rk_i2s_pins {
+ u32 reg_offset;
+ u32 shift;
+};
+
struct rk_i2s_dev {
struct device *dev;
@@ -33,6 +40,7 @@ struct rk_i2s_dev {
struct snd_dmaengine_dai_dma_data playback_dma_data;
struct regmap *regmap;
+ struct regmap *grf;
/*
* Used to indicate the tx/rx status.
@@ -42,12 +50,14 @@ struct rk_i2s_dev {
bool tx_start;
bool rx_start;
bool is_master_mode;
+ const struct rk_i2s_pins *pins;
};
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;
@@ -64,7 +74,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)
@@ -300,14 +317,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_rates && rtd->dai_link->symmetric_rates)
+ val = I2S_CKR_TRCM_TXONLY;
regmap_update_bits(i2s->regmap, I2S_CKR,
I2S_CKR_TRCM_MASK,
@@ -485,9 +526,23 @@ 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[] = {
+ { .compatible = "rockchip,rk3066-i2s", },
+ { .compatible = "rockchip,rk3188-i2s", },
+ { .compatible = "rockchip,rk3288-i2s", },
+ { .compatible = "rockchip,rk3399-i2s", .data = &rk3399_i2s_pins },
+ {},
+};
+
static int rockchip_i2s_probe(struct platform_device *pdev)
{
struct device_node *node = pdev->dev.of_node;
+ const struct of_device_id *of_id;
struct rk_i2s_dev *i2s;
struct snd_soc_dai_driver *soc_dai;
struct resource *res;
@@ -501,6 +556,17 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
return -ENOMEM;
}
+ i2s->dev = &pdev->dev;
+
+ i2s->grf = syscon_regmap_lookup_by_phandle(node, "rockchip,grf");
+ if (!IS_ERR(i2s->grf)) {
+ of_id = of_match_device(rockchip_i2s_match, &pdev->dev);
+ if (!of_id || !of_id->data)
+ return -EINVAL;
+
+ i2s->pins = of_id->data;
+ }
+
/* try to prepare related clocks */
i2s->hclk = devm_clk_get(&pdev->dev, "i2s_hclk");
if (IS_ERR(i2s->hclk)) {
@@ -540,7 +606,6 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
i2s->capture_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
i2s->capture_dma_data.maxburst = 4;
- i2s->dev = &pdev->dev;
dev_set_drvdata(&pdev->dev, i2s);
pm_runtime_enable(&pdev->dev);
@@ -606,14 +671,6 @@ static int rockchip_i2s_remove(struct platform_device *pdev)
return 0;
}
-static const struct of_device_id rockchip_i2s_match[] = {
- { .compatible = "rockchip,rk3066-i2s", },
- { .compatible = "rockchip,rk3188-i2s", },
- { .compatible = "rockchip,rk3288-i2s", },
- { .compatible = "rockchip,rk3399-i2s", },
- {},
-};
-
static const struct dev_pm_ops rockchip_i2s_pm_ops = {
SET_RUNTIME_PM_OPS(i2s_runtime_suspend, i2s_runtime_resume,
NULL)
diff --git a/sound/soc/rockchip/rockchip_i2s.h b/sound/soc/rockchip/rockchip_i2s.h
index dc6e2c74d088..31f11fd25393 100644
--- a/sound/soc/rockchip/rockchip_i2s.h
+++ b/sound/soc/rockchip/rockchip_i2s.h
@@ -81,8 +81,8 @@
#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)
@@ -236,4 +236,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_max98090.c b/sound/soc/rockchip/rockchip_max98090.c
index 543610282cdb..e70ffad07184 100644
--- a/sound/soc/rockchip/rockchip_max98090.c
+++ b/sound/soc/rockchip/rockchip_max98090.c
@@ -34,13 +34,18 @@
#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,
},
+
};
static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
@@ -53,7 +58,7 @@ static const struct snd_soc_dapm_widget rk_dapm_widgets[] = {
static const struct snd_soc_dapm_route rk_audio_map[] = {
{"IN34", NULL, "Headset Mic"},
{"IN34", NULL, "MICBIAS"},
- {"MICBIAS", NULL, "Headset Mic"},
+ {"Headset Mic", NULL, "MICBIAS"},
{"DMICL", NULL, "Int Mic"},
{"Headphone", NULL, "HPL"},
{"Headphone", NULL, "HPR"},
@@ -114,43 +119,27 @@ static int rk_aif1_hw_params(struct snd_pcm_substream *substream,
return ret;
}
-static int rk_init(struct snd_soc_pcm_runtime *runtime)
-{
- /* 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));
-}
-
-static int rk_98090_headset_init(struct snd_soc_component *component)
-{
- return ts3a227e_enable_jack_detect(component, &headset_jack);
-}
-
static struct snd_soc_ops rk_aif1_ops = {
.hw_params = rk_aif1_hw_params,
};
-static struct snd_soc_aux_dev rk_98090_headset_dev = {
- .name = "Headset Chip",
- .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 int rk_98090_headset_init(struct snd_soc_component *component);
+
+static struct snd_soc_aux_dev rk_98090_headset_dev = {
+ .name = "Headset Chip",
+ .init = rk_98090_headset_init,
+};
+
static struct snd_soc_card snd_soc_card_rk = {
.name = "ROCKCHIP-I2S",
.owner = THIS_MODULE,
@@ -166,6 +155,26 @@ static struct snd_soc_card snd_soc_card_rk = {
.num_controls = ARRAY_SIZE(rk_mc_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(&snd_soc_card_rk, "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 snd_rk_mc_probe(struct platform_device *pdev)
{
int ret = 0;
diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
index 100781e37848..fa8101d1e16f 100644
--- a/sound/soc/rockchip/rockchip_spdif.c
+++ b/sound/soc/rockchip/rockchip_spdif.c
@@ -65,6 +65,7 @@ static int __maybe_unused 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);
@@ -88,7 +89,16 @@ static int __maybe_unused rk_spdif_runtime_resume(struct device *dev)
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,
@@ -101,21 +111,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:
@@ -139,7 +135,6 @@ 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,
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index 78baa26e938b..f6023b46c107 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -1,12 +1,14 @@
-config SND_SOC_SAMSUNG
+menuconfig SND_SOC_SAMSUNG
tristate "ASoC support for Samsung"
depends on (PLAT_SAMSUNG || ARCH_EXYNOS)
select SND_SOC_GENERIC_DMAENGINE_PCM
- help
+ ---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.
+if SND_SOC_SAMSUNG
+
config SND_S3C24XX_I2S
tristate
@@ -18,22 +20,22 @@ config SND_S3C2412_SOC_I2S
select SND_S3C_I2SV2_SOC
config SND_SAMSUNG_PCM
- tristate
+ tristate "Samsung PCM interface support"
config SND_SAMSUNG_AC97
tristate
select SND_SOC_AC97_BUS
config SND_SAMSUNG_SPDIF
- tristate
+ tristate "Samsung SPDIF transmitter support"
select SND_SOC_SPDIF
config SND_SAMSUNG_I2S
- tristate
+ tristate "Samsung I2S interface support"
config SND_SOC_SAMSUNG_NEO1973_WM8753
tristate "Audio support for Openmoko Neo1973 Smartphones (GTA02)"
- depends on SND_SOC_SAMSUNG && MACH_NEO1973_GTA02
+ depends on MACH_NEO1973_GTA02
select SND_S3C24XX_I2S
select SND_SOC_WM8753
select SND_SOC_BT_SCO
@@ -43,7 +45,7 @@ config SND_SOC_SAMSUNG_NEO1973_WM8753
config SND_SOC_SAMSUNG_JIVE_WM8750
tristate "SoC I2S Audio support for Jive"
- depends on SND_SOC_SAMSUNG && MACH_JIVE && I2C
+ depends on MACH_JIVE && I2C
select SND_SOC_WM8750
select SND_S3C2412_SOC_I2S
help
@@ -51,7 +53,7 @@ config SND_SOC_SAMSUNG_JIVE_WM8750
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 MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110
depends on I2C
select SND_SOC_WM8580
select SND_SAMSUNG_I2S
@@ -60,7 +62,6 @@ config SND_SOC_SAMSUNG_SMDK_WM8580
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
@@ -70,7 +71,7 @@ config SND_SOC_SAMSUNG_SMDK_WM8994
config SND_SOC_SAMSUNG_SMDK2443_WM9710
tristate "SoC AC97 Audio support for SMDK2443 - WM9710"
- depends on SND_SOC_SAMSUNG && MACH_SMDK2443
+ depends on MACH_SMDK2443
select AC97_BUS
select SND_SOC_AC97_CODEC
select SND_SAMSUNG_AC97
@@ -80,7 +81,7 @@ config SND_SOC_SAMSUNG_SMDK2443_WM9710
config SND_SOC_SAMSUNG_LN2440SBC_ALC650
tristate "SoC AC97 Audio support for LN2440SBC - ALC650"
- depends on SND_SOC_SAMSUNG && ARCH_S3C24XX
+ depends on ARCH_S3C24XX
select AC97_BUS
select SND_SOC_AC97_CODEC
select SND_SAMSUNG_AC97
@@ -90,7 +91,7 @@ config SND_SOC_SAMSUNG_LN2440SBC_ALC650
config SND_SOC_SAMSUNG_S3C24XX_UDA134X
tristate "SoC I2S Audio support UDA134X wired to a S3C24XX"
- depends on SND_SOC_SAMSUNG && ARCH_S3C24XX
+ depends on ARCH_S3C24XX
select SND_S3C24XX_I2S
select SND_SOC_L3
select SND_SOC_UDA134X
@@ -102,21 +103,21 @@ config SND_SOC_SAMSUNG_SIMTEC
config SND_SOC_SAMSUNG_SIMTEC_TLV320AIC23
tristate "SoC I2S Audio support for TLV320AIC23 on Simtec boards"
- depends on SND_SOC_SAMSUNG && ARCH_S3C24XX && I2C
+ depends on 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
+ depends on 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
+ depends on ARCH_H1940 && I2C
select SND_S3C24XX_I2S
select SND_SOC_UDA1380
help
@@ -124,7 +125,7 @@ config SND_SOC_SAMSUNG_H1940_UDA1380
config SND_SOC_SAMSUNG_RX1950_UDA1380
tristate "Audio support for the HP iPAQ RX1950"
- depends on SND_SOC_SAMSUNG && MACH_RX1950 && I2C
+ depends on MACH_RX1950 && I2C
select SND_S3C24XX_I2S
select SND_SOC_UDA1380
help
@@ -132,7 +133,7 @@ config SND_SOC_SAMSUNG_RX1950_UDA1380
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)
+ depends on MACH_SMDK6410 || MACH_SMDKC100 || MACH_SMDKV210 || MACH_SMDKC110
select SND_SOC_WM9713
select SND_SAMSUNG_AC97
help
@@ -140,20 +141,19 @@ config SND_SOC_SAMSUNG_SMDK_WM9713
config SND_SOC_SMARTQ
tristate "SoC I2S Audio support for SmartQ board"
- depends on SND_SOC_SAMSUNG && MACH_SMARTQ && I2C
+ depends on 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 MACH_SMDKV210 || MACH_SMDKC110
depends on I2C
select SND_SOC_WM8580
select SND_SAMSUNG_PCM
@@ -162,7 +162,6 @@ config SND_SOC_SMDK_WM8580_PCM
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 +171,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 +181,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 && I2C && SPI_MASTER
depends on MACH_WLF_CRAGG_6410 || COMPILE_TEST
select SND_SAMSUNG_I2S
select SND_SOC_WM5102
@@ -200,7 +199,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 +207,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 +215,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 +223,10 @@ 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"
- 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.
-
config SND_SOC_ARNDALE_RT5631_ALC5631
tristate "Audio support for RT5631(ALC5631) on Arndale Board"
- depends on SND_SOC_SAMSUNG && I2C
+ depends on 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..5d03f5ce6916 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -43,7 +43,6 @@ 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
obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
@@ -69,5 +68,4 @@ 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
diff --git a/sound/soc/samsung/ac97.c b/sound/soc/samsung/ac97.c
index 4a7a503fe13c..97d6700b1009 100644
--- a/sound/soc/samsung/ac97.c
+++ b/sound/soc/samsung/ac97.c
@@ -38,16 +38,16 @@ struct s3c_ac97_info {
};
static struct s3c_ac97_info s3c_ac97;
-static struct s3c_dma_params s3c_ac97_pcm_out = {
- .dma_size = 4,
+static struct snd_dmaengine_dai_dma_data s3c_ac97_pcm_out = {
+ .addr_width = 4,
};
-static struct s3c_dma_params s3c_ac97_pcm_in = {
- .dma_size = 4,
+static struct snd_dmaengine_dai_dma_data s3c_ac97_pcm_in = {
+ .addr_width = 4,
};
-static struct s3c_dma_params s3c_ac97_mic_in = {
- .dma_size = 4,
+static struct snd_dmaengine_dai_dma_data s3c_ac97_mic_in = {
+ .addr_width = 4,
};
static void s3c_ac97_activate(struct snd_ac97 *ac97)
@@ -74,7 +74,7 @@ static void s3c_ac97_activate(struct snd_ac97 *ac97)
writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
- pr_err("AC97: Unable to activate!");
+ pr_err("AC97: Unable to activate!\n");
}
static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
@@ -100,7 +100,7 @@ static unsigned short s3c_ac97_read(struct snd_ac97 *ac97,
writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
- pr_err("AC97: Unable to read!");
+ pr_err("AC97: Unable to read!\n");
stat = readl(s3c_ac97.regs + S3C_AC97_STAT);
addr = (stat >> 16) & 0x7f;
@@ -137,7 +137,7 @@ static void s3c_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
writel(ac_glbctrl, s3c_ac97.regs + S3C_AC97_GLBCTRL);
if (!wait_for_completion_timeout(&s3c_ac97.done, HZ))
- pr_err("AC97: Unable to write!");
+ pr_err("AC97: Unable to write!\n");
ac_codec_cmd = readl(s3c_ac97.regs + S3C_AC97_CODEC_CMD);
ac_codec_cmd |= S3C_AC97_CODEC_CMD_READ;
@@ -273,14 +273,14 @@ static const struct snd_soc_dai_ops s3c_ac97_mic_dai_ops = {
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);
+ snd_soc_dai_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);
+ snd_soc_dai_init_dma_data(dai, NULL, &s3c_ac97_mic_in);
return 0;
}
@@ -346,12 +346,12 @@ static int s3c_ac97_probe(struct platform_device *pdev)
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;
+ s3c_ac97_pcm_out.filter_data = ac97_pdata->dma_playback;
+ s3c_ac97_pcm_out.addr = mem_res->start + S3C_AC97_PCM_DATA;
+ s3c_ac97_pcm_in.filter_data = ac97_pdata->dma_capture;
+ s3c_ac97_pcm_in.addr = mem_res->start + S3C_AC97_PCM_DATA;
+ s3c_ac97_mic_in.filter_data = ac97_pdata->dma_capture_mic;
+ s3c_ac97_mic_in.addr = mem_res->start + S3C_AC97_MIC_DATA;
init_completion(&s3c_ac97.done);
mutex_init(&s3c_ac97.lock);
@@ -389,7 +389,8 @@ static int s3c_ac97_probe(struct platform_device *pdev)
goto err5;
ret = samsung_asoc_dma_platform_register(&pdev->dev,
- ac97_pdata->dma_filter);
+ ac97_pdata->dma_filter,
+ NULL, NULL);
if (ret) {
dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
goto err5;
diff --git a/sound/soc/samsung/dma.h b/sound/soc/samsung/dma.h
index a7616cc9b39e..7ae580d677c8 100644
--- a/sound/soc/samsung/dma.h
+++ b/sound/soc/samsung/dma.h
@@ -1,6 +1,4 @@
/*
- * 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
@@ -9,24 +7,15 @@
* 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);
+#endif /* _SAMSUNG_DMA_H */
diff --git a/sound/soc/samsung/dmaengine.c b/sound/soc/samsung/dmaengine.c
index 063125937311..9104c98deeb7 100644
--- a/sound/soc/samsung/dmaengine.c
+++ b/sound/soc/samsung/dmaengine.c
@@ -16,57 +16,35 @@
*/
#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 snd_dmaengine_dai_dma_data *playback_data = NULL;
- struct snd_dmaengine_dai_dma_data *capture_data = NULL;
+ unsigned int flags = SND_DMAENGINE_PCM_FLAG_COMPAT;
+ 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;
-int samsung_asoc_dma_platform_register(struct device *dev,
- dma_filter_fn filter)
-{
- samsung_dmaengine_pcm_config.compat_filter_fn = filter;
+ if (dev->of_node) {
+ pcm_conf->chan_names[SNDRV_PCM_STREAM_PLAYBACK] = tx;
+ pcm_conf->chan_names[SNDRV_PCM_STREAM_CAPTURE] = rx;
+ } else {
+ flags |= SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME;
+ }
- 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, flags);
}
EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 70a2559b63f9..7e32cf4581f8 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -18,6 +18,7 @@
#include <linux/io.h>
#include <linux/module.h>
#include <linux/of.h>
+#include <linux/of_device.h>
#include <linux/of_gpio.h>
#include <linux/pm_runtime.h>
@@ -86,9 +87,9 @@ struct i2s_dai {
/* Driver for this DAI */
struct snd_soc_dai_driver i2s_dai_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;
@@ -691,15 +692,15 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
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:
@@ -753,7 +754,7 @@ static int i2s_hw_params(struct snd_pcm_substream *substream,
writel(mod, i2s->addr + I2SMOD);
spin_unlock_irqrestore(i2s->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);
@@ -990,10 +991,10 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
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,
+ snd_soc_dai_init_dma_data(dai, &other->sec_dai->dma_playback,
NULL);
} else {
- samsung_asoc_init_dma_data(dai, &i2s->dma_playback,
+ snd_soc_dai_init_dma_data(dai, &i2s->dma_playback,
&i2s->dma_capture);
if (i2s->quirks & QUIRK_NEED_RSTCLR)
@@ -1001,7 +1002,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
if (i2s->quirks & QUIRK_SUPPORTS_IDMA)
idma_reg_addr_init(i2s->addr,
- i2s->sec_dai->idma_playback.dma_addr);
+ i2s->sec_dai->idma_playback.addr);
}
/* Reset any constraint on RFS and BFS */
@@ -1106,19 +1107,9 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
return i2s;
}
-static const struct of_device_id exynos_i2s_match[];
-
-static inline const struct samsung_i2s_dai_data *samsung_i2s_get_driver_data(
- struct platform_device *pdev)
+static void i2s_free_sec_dai(struct i2s_dai *i2s)
{
- 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;
- }
+ platform_device_del(i2s->pdev);
}
#ifdef CONFIG_PM
@@ -1233,9 +1224,13 @@ static int samsung_i2s_probe(struct platform_device *pdev)
const struct samsung_i2s_dai_data *i2s_dai_data;
int 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
+ i2s_dai_data = (struct samsung_i2s_dai_data *)
+ platform_get_device_id(pdev)->driver_data;
+ /* Call during the secondary interface registration */
if (i2s_dai_data->dai_type == TYPE_SEC) {
sec_dai = dev_get_drvdata(&pdev->dev);
if (!sec_dai) {
@@ -1249,7 +1244,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
return ret;
return samsung_asoc_dma_platform_register(&pdev->dev,
- sec_dai->filter);
+ sec_dai->filter, "tx-sec", NULL);
}
pri_dai = i2s_alloc_dai(pdev, false);
@@ -1267,8 +1262,8 @@ static int samsung_i2s_probe(struct platform_device *pdev)
return -EINVAL;
}
- pri_dai->dma_playback.slave = i2s_pdata->dma_playback;
- pri_dai->dma_capture.slave = i2s_pdata->dma_capture;
+ 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;
if (&i2s_pdata->type)
@@ -1307,12 +1302,12 @@ static int samsung_i2s_probe(struct platform_device *pdev)
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->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->quirks = quirks;
pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs;
@@ -1323,44 +1318,59 @@ static int samsung_i2s_probe(struct platform_device *pdev)
sec_dai = i2s_alloc_dai(pdev, true);
if (!sec_dai) {
dev_err(&pdev->dev, "Unable to alloc I2S_sec\n");
- return -ENOMEM;
+ ret = -ENOMEM;
+ 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";
+ 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->dma_playback.addr_width = 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->idma_playback.addr = idma_addr;
sec_dai->pri_dai = pri_dai;
pri_dai->sec_dai = sec_dai;
}
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_disable_clk;
}
- devm_snd_soc_register_component(&pri_dai->pdev->dev,
+ ret = devm_snd_soc_register_component(&pri_dai->pdev->dev,
&samsung_i2s_component,
&pri_dai->i2s_dai_drv, 1);
+ if (ret < 0)
+ goto err_free_dai;
+
+ ret = samsung_asoc_dma_platform_register(&pdev->dev, pri_dai->filter,
+ NULL, NULL);
+ if (ret < 0)
+ goto err_free_dai;
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(pdev);
+ if (!ret)
+ return 0;
- return i2s_register_clock_provider(pdev);
+ pm_runtime_disable(&pdev->dev);
+err_free_dai:
+ if (sec_dai)
+ i2s_free_sec_dai(sec_dai);
+err_disable_clk:
+ clk_disable_unprepare(pri_dai->clk);
+ return ret;
}
static int samsung_i2s_remove(struct platform_device *pdev)
@@ -1477,10 +1487,6 @@ static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = {
.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,
};
@@ -1492,9 +1498,6 @@ static const struct platform_device_id samsung_i2s_driver_ids[] = {
}, {
.name = "samsung-i2s-sec",
.driver_data = (kernel_ulong_t)&samsung_dai_type_sec,
- }, {
- .name = "samsung-i2sv4",
- .driver_data = (kernel_ulong_t)&i2sv5_dai_type,
},
{},
};
diff --git a/sound/soc/samsung/idma.c b/sound/soc/samsung/idma.c
index 4ed29ffc1c54..3e408158625d 100644
--- a/sound/soc/samsung/idma.c
+++ b/sound/soc/samsung/idma.c
@@ -22,7 +22,6 @@
#include "i2s.h"
#include "idma.h"
-#include "dma.h"
#include "i2s-regs.h"
#define ST_RUNNING (1<<0)
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..43e367a9acc3 100644
--- a/sound/soc/samsung/pcm.c
+++ b/sound/soc/samsung/pcm.c
@@ -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,
},
};
@@ -552,15 +552,13 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
}
clk_prepare_enable(pcm->pclk);
- 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;
}
@@ -576,7 +574,8 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
goto err5;
}
- ret = samsung_asoc_dma_platform_register(&pdev->dev, filter);
+ ret = samsung_asoc_dma_platform_register(&pdev->dev, filter,
+ NULL, NULL);
if (ret) {
dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
goto err5;
diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c
index b6ab3fc5789e..644f186fd35c 100644
--- a/sound/soc/samsung/s3c-i2s-v2.c
+++ b/sound/soc/samsung/s3c-i2s-v2.c
@@ -24,7 +24,6 @@
#include "regs-i2s-v2.h"
#include "s3c-i2s-v2.h"
-#include "dma.h"
#undef S3C_IIS_V2_SUPPORTED
@@ -268,7 +267,7 @@ static int s3c2412_i2s_set_fmt(struct snd_soc_dai *cpu_dai,
iismod &= ~S3C2412_IISMOD_SLAVE;
break;
default:
- pr_err("unknwon master/slave format\n");
+ pr_err("unknown master/slave format\n");
return -EINVAL;
}
@@ -302,7 +301,7 @@ static int s3c_i2sv2_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
struct s3c_i2sv2_info *i2s = to_info(dai);
- struct s3c_dma_params *dma_data;
+ struct snd_dmaengine_dai_dma_data *dma_data;
u32 iismod;
pr_debug("Entered %s\n", __func__);
diff --git a/sound/soc/samsung/s3c-i2s-v2.h b/sound/soc/samsung/s3c-i2s-v2.h
index d0684145ed1f..182d80564e37 100644
--- a/sound/soc/samsung/s3c-i2s-v2.h
+++ b/sound/soc/samsung/s3c-i2s-v2.h
@@ -60,8 +60,8 @@ struct s3c_i2sv2_info {
unsigned char master;
- 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;
u32 suspend_iismod;
u32 suspend_iiscon;
diff --git a/sound/soc/samsung/s3c2412-i2s.c b/sound/soc/samsung/s3c2412-i2s.c
index 204029d12f5b..3e89fbc0c51d 100644
--- a/sound/soc/samsung/s3c2412-i2s.c
+++ b/sound/soc/samsung/s3c2412-i2s.c
@@ -34,14 +34,14 @@
#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 snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_out = {
+ .chan_name = "tx",
+ .addr_width = 4,
};
-static struct s3c_dma_params s3c2412_i2s_pcm_stereo_in = {
- .ch_name = "rx",
- .dma_size = 4,
+static struct snd_dmaengine_dai_dma_data s3c2412_i2s_pcm_stereo_in = {
+ .chan_name = "rx",
+ .addr_width = 4,
};
static struct s3c_i2sv2_info s3c2412_i2s;
@@ -52,8 +52,8 @@ static int s3c2412_i2s_probe(struct snd_soc_dai *dai)
pr_debug("Entered %s\n", __func__);
- samsung_asoc_init_dma_data(dai, &s3c2412_i2s_pcm_stereo_out,
- &s3c2412_i2s_pcm_stereo_in);
+ snd_soc_dai_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)
@@ -163,10 +163,10 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev)
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;
+ s3c2412_i2s_pcm_stereo_out.addr = res->start + S3C2412_IISTXD;
+ s3c2412_i2s_pcm_stereo_out.filter_data = pdata->dma_playback;
+ s3c2412_i2s_pcm_stereo_in.addr = res->start + S3C2412_IISRXD;
+ s3c2412_i2s_pcm_stereo_in.filter_data = pdata->dma_capture;
ret = s3c_i2sv2_register_component(&pdev->dev, -1,
&s3c2412_i2s_component,
@@ -177,7 +177,8 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev)
}
ret = samsung_asoc_dma_platform_register(&pdev->dev,
- pdata->dma_filter);
+ pdata->dma_filter,
+ NULL, NULL);
if (ret)
pr_err("failed to register the DMA: %d\n", ret);
diff --git a/sound/soc/samsung/s3c24xx-i2s.c b/sound/soc/samsung/s3c24xx-i2s.c
index b3a475d73ba7..c78a936a3099 100644
--- a/sound/soc/samsung/s3c24xx-i2s.c
+++ b/sound/soc/samsung/s3c24xx-i2s.c
@@ -32,14 +32,14 @@
#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 snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_out = {
+ .chan_name = "tx",
+ .addr_width = 2,
};
-static struct s3c_dma_params s3c24xx_i2s_pcm_stereo_in = {
- .ch_name = "rx",
- .dma_size = 2,
+static struct snd_dmaengine_dai_dma_data s3c24xx_i2s_pcm_stereo_in = {
+ .chan_name = "rx",
+ .addr_width = 2,
};
struct s3c24xx_i2s_info {
@@ -360,8 +360,8 @@ 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);
+ snd_soc_dai_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)) {
@@ -469,10 +469,10 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
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;
+ s3c24xx_i2s_pcm_stereo_out.addr = res->start + S3C2410_IISFIFO;
+ s3c24xx_i2s_pcm_stereo_out.filter_data = pdata->dma_playback;
+ s3c24xx_i2s_pcm_stereo_in.addr = res->start + S3C2410_IISFIFO;
+ s3c24xx_i2s_pcm_stereo_in.filter_data = pdata->dma_capture;
ret = devm_snd_soc_register_component(&pdev->dev,
&s3c24xx_i2s_component, &s3c24xx_i2s_dai, 1);
@@ -482,7 +482,8 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
}
ret = samsung_asoc_dma_platform_register(&pdev->dev,
- pdata->dma_filter);
+ pdata->dma_filter,
+ NULL, NULL);
if (ret)
pr_err("failed to register the dma: %d\n", ret);
diff --git a/sound/soc/samsung/s3c24xx_uda134x.c b/sound/soc/samsung/s3c24xx_uda134x.c
index 50849e137fc0..7853fbe6ccc9 100644
--- a/sound/soc/samsung/s3c24xx_uda134x.c
+++ b/sound/soc/samsung/s3c24xx_uda134x.c
@@ -54,28 +54,27 @@ static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
};
#endif
-static struct platform_device *s3c24xx_uda134x_snd_device;
-
static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
{
- int ret = 0;
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
#ifdef ENFORCE_RATES
struct snd_pcm_runtime *runtime = substream->runtime;
#endif
+ int ret = 0;
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");
+ xtal = clk_get(rtd->dev, "xtal");
if (IS_ERR(xtal)) {
- printk(KERN_ERR "%s cannot get xtal\n", __func__);
+ dev_err(rtd->dev, "%s cannot get xtal\n", __func__);
ret = PTR_ERR(xtal);
} else {
- pclk = clk_get(&s3c24xx_uda134x_snd_device->dev,
- "pclk");
+ pclk = clk_get(cpu_dai->dev, "iis");
if (IS_ERR(pclk)) {
- printk(KERN_ERR "%s cannot get pclk\n",
- __func__);
+ dev_err(rtd->dev, "%s cannot get pclk\n",
+ __func__);
clk_put(xtal);
ret = PTR_ERR(pclk);
}
@@ -101,8 +100,8 @@ static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
SNDRV_PCM_HW_PARAM_RATE,
&hw_constraints_rates);
if (ret < 0)
- printk(KERN_ERR "%s cannot set constraints\n",
- __func__);
+ dev_err(rtd->dev, "%s cannot set constraints\n",
+ __func__);
#endif
}
return ret;
@@ -111,7 +110,6 @@ static int s3c24xx_uda134x_startup(struct snd_pcm_substream *substream)
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);
@@ -158,18 +156,19 @@ static int s3c24xx_uda134x_hw_params(struct snd_pcm_substream *substream,
clk_source = S3C24XX_CLKSRC_PCLK;
div = bi % 33;
}
- pr_debug("%s desired rate %lu, %d\n", __func__, rate, bi);
+
+ dev_dbg(rtd->dev, "%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);
+
+ dev_dbg(rtd->dev, "%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);
+ dev_err(rtd->dev, "effective frequency too different "
+ "from desired (%ld%%)\n", err * 100 / rate);
return -EINVAL;
}
@@ -226,115 +225,27 @@ static struct snd_soc_card snd_soc_s3c24xx_uda134x = {
.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)
{
+ struct snd_soc_card *card = &snd_soc_s3c24xx_uda134x;
int ret;
- printk(KERN_INFO "S3C24XX_UDA134X SoC Audio driver\n");
+ platform_set_drvdata(pdev, card);
+ card->dev = &pdev->dev;
- 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);
- }
+ ret = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret)
+ dev_err(&pdev->dev, "failed to register card: %d\n", ret);
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>");
diff --git a/sound/soc/samsung/smdk_wm8580pcm.c b/sound/soc/samsung/smdk_wm8580pcm.c
index 6deec5234c92..a6d223310c67 100644
--- a/sound/soc/samsung/smdk_wm8580pcm.c
+++ b/sound/soc/samsung/smdk_wm8580pcm.c
@@ -16,7 +16,6 @@
#include <asm/mach-types.h>
#include "../codecs/wm8580.h"
-#include "dma.h"
#include "pcm.h"
/*
diff --git a/sound/soc/samsung/smdk_wm8994pcm.c b/sound/soc/samsung/smdk_wm8994pcm.c
index b1c89ec2d999..2e621496be8b 100644
--- a/sound/soc/samsung/smdk_wm8994pcm.c
+++ b/sound/soc/samsung/smdk_wm8994pcm.c
@@ -15,7 +15,6 @@
#include <sound/pcm_params.h>
#include "../codecs/wm8994.h"
-#include "dma.h"
#include "pcm.h"
/*
diff --git a/sound/soc/samsung/spdif.c b/sound/soc/samsung/spdif.c
index 4687f521197c..26c1fbed4d35 100644
--- a/sound/soc/samsung/spdif.c
+++ b/sound/soc/samsung/spdif.c
@@ -90,10 +90,10 @@ 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 *to_info(struct snd_soc_dai *cpu_dai)
@@ -179,7 +179,7 @@ static int spdif_hw_params(struct snd_pcm_substream *substream,
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct samsung_spdif_info *spdif = to_info(rtd->cpu_dai);
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;
@@ -425,17 +425,18 @@ static int spdif_probe(struct platform_device *pdev)
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);
if (ret) {
dev_err(&pdev->dev, "failed to register DMA: %d\n", ret);
goto err4;
diff --git a/sound/soc/sh/Kconfig b/sound/soc/sh/Kconfig
index c9902a6d6fa0..6db6405d952f 100644
--- a/sound/soc/sh/Kconfig
+++ b/sound/soc/sh/Kconfig
@@ -42,11 +42,6 @@ config SND_SOC_RCAR
help
This option enables R-Car SRU/SCU/SSIU/SSI sound support
-config SND_SOC_RSRC_CARD
- tristate "Renesas Sampling Rate Convert Sound Card"
- help
- This option enables simple sound if you need sampling rate convert
-
##
## Boards
##
diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile
index a89ddf758695..9c3d5aed99d1 100644
--- a/sound/soc/sh/rcar/Makefile
+++ b/sound/soc/sh/rcar/Makefile
@@ -1,5 +1,2 @@
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
index 49354d17ea55..2145957d0229 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -33,11 +33,15 @@ struct rsnd_adg {
struct clk *clkout[CLKOUTMAX];
struct clk_onecell_data onecell;
struct rsnd_mod mod;
+ u32 flags;
int rbga_rate_for_441khz; /* RBGA */
int rbgb_rate_for_48khz; /* RBGB */
};
+#define LRCLK_ASYNC (1 << 0)
+#define adg_mode_flags(adg) (adg->flags)
+
#define for_each_rsnd_clk(pos, adg, i) \
for (i = 0; \
(i < CLKMAX) && \
@@ -355,6 +359,16 @@ found_clock:
rsnd_adg_set_ssi_clk(ssi_mod, data);
+ if (!(adg_mode_flags(adg) & LRCLK_ASYNC)) {
+ struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+ u32 ckr = 0;
+
+ if (0 == (rate % 8000))
+ ckr = 0x80000000;
+
+ rsnd_mod_bset(adg_mod, SSICKR, 0x80000000, ckr);
+ }
+
dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
rsnd_mod_name(ssi_mod), rsnd_mod_id(ssi_mod),
data, rate);
@@ -518,7 +532,7 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
}
}
- rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr);
+ rsnd_mod_bset(adg_mod, SSICKR, 0x80FF0000, ckr);
rsnd_mod_write(adg_mod, BRRA, rbga);
rsnd_mod_write(adg_mod, BRRB, rbgb);
@@ -532,6 +546,7 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
{
struct rsnd_adg *adg;
struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *np = dev->of_node;
adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
if (!adg) {
@@ -545,6 +560,9 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
rsnd_adg_get_clkin(priv, adg);
rsnd_adg_get_clkout(priv, adg);
+ if (of_get_property(np, "clkout-lr-asynchronous", NULL))
+ adg->flags = LRCLK_ASYNC;
+
priv->adg = adg;
return 0;
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 3351a701c60e..f18141098b50 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -110,6 +110,7 @@ MODULE_DEVICE_TABLE(of, rsnd_of_match);
/*
* rsnd_mod functions
*/
+#ifdef DEBUG
void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
{
if (mod->type != type) {
@@ -120,6 +121,7 @@ void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
rsnd_mod_name(mod), rsnd_mod_id(mod));
}
}
+#endif
char *rsnd_mod_name(struct rsnd_mod *mod)
{
@@ -574,6 +576,7 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
rsnd_dai_stream_init(io, substream);
ret = rsnd_dai_call(init, io, priv);
@@ -590,6 +593,7 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
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);
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
index 46c0ba7b6414..7d2fdf8dd188 100644
--- a/sound/soc/sh/rcar/gen.c
+++ b/sound/soc/sh/rcar/gen.c
@@ -206,7 +206,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
*/
static int rsnd_gen2_probe(struct rsnd_priv *priv)
{
- const static struct rsnd_regmap_field_conf conf_ssiu[] = {
+ static const 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),
@@ -221,7 +221,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
RSND_GEN_M_REG(SSI_INT_ENABLE, 0x18, 0x80),
};
- const static struct rsnd_regmap_field_conf conf_scu[] = {
+ static const 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),
@@ -308,7 +308,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
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[] = {
+ static const 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),
@@ -328,7 +328,7 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
RSND_GEN_S_REG(SRCOUT_TIMSEL4, 0x58),
RSND_GEN_S_REG(CMDOUT_TIMSEL, 0x5c),
};
- const static struct rsnd_regmap_field_conf conf_ssi[] = {
+ static const 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),
@@ -359,14 +359,14 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
static int rsnd_gen1_probe(struct rsnd_priv *priv)
{
- const static struct rsnd_regmap_field_conf conf_adg[] = {
+ static const 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[] = {
+ static const 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),
diff --git a/sound/soc/sh/rcar/rsrc-card.c b/sound/soc/sh/rcar/rsrc-card.c
deleted file mode 100644
index 1bc7ecfc42a9..000000000000
--- a/sound/soc/sh/rcar/rsrc-card.c
+++ /dev/null
@@ -1,529 +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;
- u32 convert_channels;
-};
-
-#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))
-
-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);
- struct snd_interval *channels = hw_param_interval(params,
- SNDRV_PCM_HW_PARAM_CHANNELS);
-
- if (priv->convert_rate)
- rate->min =
- rate->max = priv->convert_rate;
-
- if (priv->convert_channels)
- channels->min =
- channels->max = priv->convert_channels;
-
- 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 = of_device_get_match_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 = of_device_get_match_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);
-
- /* channels transfer */
- of_property_read_u32(node, "convert-channels", &priv->convert_channels);
-
- dev_dbg(dev, "New rsrc-audio-card: %s\n",
- priv->snd_card.name ? priv->snd_card.name : "");
- dev_dbg(dev, "SRC : convert_rate %d\n", priv->convert_rate);
- dev_dbg(dev, "CTU : convert_channels %d\n", priv->convert_channels);
-
- 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
index e39f916d0f2f..969a5169de25 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -226,8 +226,12 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
ifscr = 0;
fsrate = 0;
if (fin != fout) {
+ u64 n;
+
ifscr = 1;
- fsrate = 0x0400000 / fout * fin;
+ n = (u64)0x0400000 * fin;
+ do_div(n, fout);
+ fsrate = n;
}
/*
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 5f848f054745..6cb6db005fc4 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -928,7 +928,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
}
ops = &rsnd_ssi_non_ops;
- if (of_get_property(np, "pio-transfer", NULL))
+ if (of_property_read_bool(np, "pio-transfer"))
ops = &rsnd_ssi_pio_ops;
else
ops = &rsnd_ssi_dma_ops;
diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c
index bc4a55bb3fd9..6c8b0b0c56ec 100644
--- a/sound/soc/soc-ac97.c
+++ b/sound/soc/soc-ac97.c
@@ -116,7 +116,7 @@ static int snd_soc_ac97_gpio_direction_out(struct gpio_chip *chip,
return snd_soc_update_bits(codec, 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,
diff --git a/sound/soc/soc-compress.c b/sound/soc/soc-compress.c
index 875733c52953..bf7b52fce597 100644
--- a/sound/soc/soc-compress.c
+++ b/sound/soc/soc-compress.c
@@ -121,7 +121,7 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
dpcm_be_disconnect(fe, stream);
fe->dpcm[stream].runtime = NULL;
- goto fe_err;
+ goto path_err;
}
dpcm_clear_pending_state(fe, stream);
@@ -136,6 +136,8 @@ static int soc_compr_open_fe(struct snd_compr_stream *cstream)
return 0;
+path_err:
+ dpcm_path_put(&list);
fe_err:
if (fe->dai_link->compr_ops && fe->dai_link->compr_ops->shutdown)
fe->dai_link->compr_ops->shutdown(cstream);
@@ -530,14 +532,15 @@ static int soc_compr_pointer(struct snd_compr_stream *cstream,
{
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->pointer)
- platform->driver->compr_ops->pointer(cstream, tstamp);
+ ret = platform->driver->compr_ops->pointer(cstream, tstamp);
mutex_unlock(&rtd->pcm_mutex);
- return 0;
+ return ret;
}
static int soc_compr_copy(struct snd_compr_stream *cstream,
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 16369cad4803..c0bbcd903261 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -1056,7 +1056,7 @@ static int soc_bind_dai_link(struct snd_soc_card *card,
if (!rtd->platform) {
dev_err(card->dev, "ASoC: platform %s not registered\n",
dai_link->platform_name);
- return -EPROBE_DEFER;
+ goto _err_defer;
}
soc_add_pcm_runtime(card, rtd);
@@ -2083,14 +2083,13 @@ static int soc_cleanup_card_resources(struct snd_soc_card *card)
/* remove auxiliary devices */
soc_remove_aux_devices(card);
+ snd_soc_dapm_free(&card->dapm);
soc_cleanup_card_debugfs(card);
/* remove the card */
if (card->remove)
card->remove(card);
- snd_soc_dapm_free(&card->dapm);
-
snd_card_free(card->snd_card);
return 0;
@@ -3333,19 +3332,6 @@ int snd_soc_register_codec(struct device *dev,
if (ret)
goto err_free;
- if (codec_drv->controls) {
- codec->component.controls = codec_drv->controls;
- codec->component.num_controls = codec_drv->num_controls;
- }
- if (codec_drv->dapm_widgets) {
- codec->component.dapm_widgets = codec_drv->dapm_widgets;
- codec->component.num_dapm_widgets = codec_drv->num_dapm_widgets;
- }
- if (codec_drv->dapm_routes) {
- codec->component.dapm_routes = codec_drv->dapm_routes;
- codec->component.num_dapm_routes = codec_drv->num_dapm_routes;
- }
-
if (codec_drv->probe)
codec->component.probe = snd_soc_codec_drv_probe;
if (codec_drv->remove)
@@ -3733,7 +3719,7 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
* SND_SOC_DAIFMT_CLOCK_MASK area
*/
snprintf(prop, sizeof(prop), "%scontinuous-clock", prefix);
- if (of_get_property(np, prop, NULL))
+ if (of_property_read_bool(np, prop))
format |= SND_SOC_DAIFMT_CONT;
else
format |= SND_SOC_DAIFMT_GATED;
diff --git a/sound/soc/soc-dapm.c b/sound/soc/soc-dapm.c
index c4464858bf01..3bbe32ee4630 100644
--- a/sound/soc/soc-dapm.c
+++ b/sound/soc/soc-dapm.c
@@ -823,6 +823,7 @@ static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w,
case snd_soc_dapm_switch:
case snd_soc_dapm_mixer:
case snd_soc_dapm_pga:
+ case snd_soc_dapm_out_drv:
wname_in_long_name = true;
kcname_in_long_name = true;
break;
@@ -1073,7 +1074,11 @@ static int dapm_widget_list_create(struct snd_soc_dapm_widget_list **list,
*/
static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
struct list_head *list, enum snd_soc_dapm_direction dir,
- int (*fn)(struct snd_soc_dapm_widget *, struct list_head *))
+ int (*fn)(struct snd_soc_dapm_widget *, struct list_head *,
+ bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
+ enum snd_soc_dapm_direction)),
+ bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
+ enum snd_soc_dapm_direction))
{
enum snd_soc_dapm_direction rdir = SND_SOC_DAPM_DIR_REVERSE(dir);
struct snd_soc_dapm_path *path;
@@ -1088,6 +1093,11 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
if (list)
list_add_tail(&widget->work_list, list);
+ if (custom_stop_condition && custom_stop_condition(widget, dir)) {
+ widget->endpoints[dir] = 1;
+ return widget->endpoints[dir];
+ }
+
if ((widget->is_ep & SND_SOC_DAPM_DIR_TO_EP(dir)) && widget->connected) {
widget->endpoints[dir] = snd_soc_dapm_suspend_check(widget);
return widget->endpoints[dir];
@@ -1106,7 +1116,7 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
if (path->connect) {
path->walking = 1;
- con += fn(path->node[dir], list);
+ con += fn(path->node[dir], list, custom_stop_condition);
path->walking = 0;
}
}
@@ -1119,23 +1129,37 @@ static __always_inline int is_connected_ep(struct snd_soc_dapm_widget *widget,
/*
* Recursively check for a completed path to an active or physically connected
* output widget. Returns number of complete paths.
+ *
+ * Optionally, can be supplied with a function acting as a stopping condition.
+ * This function takes the dapm widget currently being examined and the walk
+ * direction as an arguments, it should return true if the walk should be
+ * stopped and false otherwise.
*/
static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
- struct list_head *list)
+ struct list_head *list,
+ bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,
+ enum snd_soc_dapm_direction))
{
return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_OUT,
- is_connected_output_ep);
+ is_connected_output_ep, custom_stop_condition);
}
/*
* Recursively check for a completed path to an active or physically connected
* input widget. Returns number of complete paths.
+ *
+ * Optionally, can be supplied with a function acting as a stopping condition.
+ * This function takes the dapm widget currently being examined and the walk
+ * direction as an arguments, it should return true if the walk should be
+ * stopped and false otherwise.
*/
static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
- struct list_head *list)
+ struct list_head *list,
+ bool (*custom_stop_condition)(struct snd_soc_dapm_widget *i,
+ enum snd_soc_dapm_direction))
{
return is_connected_ep(widget, list, SND_SOC_DAPM_DIR_IN,
- is_connected_input_ep);
+ is_connected_input_ep, custom_stop_condition);
}
/**
@@ -1143,15 +1167,24 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
* @dai: the soc DAI.
* @stream: stream direction.
* @list: list of active widgets for this stream.
+ * @custom_stop_condition: (optional) a function meant to stop the widget graph
+ * walk based on custom logic.
*
- * Queries DAPM graph as to whether an valid audio stream path exists for
+ * Queries DAPM graph as to whether a valid audio stream path exists for
* the initial stream specified by name. This takes into account
* current mixer and mux kcontrol settings. Creates list of valid widgets.
*
+ * Optionally, can be supplied with a function acting as a stopping condition.
+ * This function takes the dapm widget currently being examined and the walk
+ * direction as an arguments, it should return true if the walk should be
+ * stopped and false otherwise.
+ *
* Returns the number of valid paths or negative error.
*/
int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
- struct snd_soc_dapm_widget_list **list)
+ struct snd_soc_dapm_widget_list **list,
+ bool (*custom_stop_condition)(struct snd_soc_dapm_widget *,
+ enum snd_soc_dapm_direction))
{
struct snd_soc_card *card = dai->component->card;
struct snd_soc_dapm_widget *w;
@@ -1171,9 +1204,11 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
}
if (stream == SNDRV_PCM_STREAM_PLAYBACK)
- paths = is_connected_output_ep(dai->playback_widget, &widgets);
+ paths = is_connected_output_ep(dai->playback_widget, &widgets,
+ custom_stop_condition);
else
- paths = is_connected_input_ep(dai->capture_widget, &widgets);
+ paths = is_connected_input_ep(dai->capture_widget, &widgets,
+ custom_stop_condition);
/* Drop starting point */
list_del(widgets.next);
@@ -1260,16 +1295,15 @@ static int dapm_widget_power_check(struct snd_soc_dapm_widget *w)
return w->new_power;
}
-/* Generic check to see if a widget should be powered.
- */
+/* Generic check to see if a widget should be powered. */
static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
{
int in, out;
DAPM_UPDATE_STAT(w, power_checks);
- in = is_connected_input_ep(w, NULL);
- out = is_connected_output_ep(w, NULL);
+ in = is_connected_input_ep(w, NULL, NULL);
+ out = is_connected_output_ep(w, NULL, NULL);
return out != 0 && in != 0;
}
@@ -1612,7 +1646,7 @@ static void dapm_pre_sequence_async(void *data, async_cookie_t cookie)
struct snd_soc_dapm_context *d = data;
int ret;
- /* If we're off and we're not supposed to be go into STANDBY */
+ /* If we're off and we're not supposed to go into STANDBY */
if (d->bias_level == SND_SOC_BIAS_OFF &&
d->target_bias_level != SND_SOC_BIAS_OFF) {
if (d->dev)
@@ -1764,7 +1798,7 @@ static bool dapm_idle_bias_off(struct snd_soc_dapm_context *dapm)
* A complete path is a route that has valid endpoints i.e.:-
*
* o DAC to output pin.
- * o Input Pin to ADC.
+ * o Input pin to ADC.
* o Input pin to Output pin (bypass, sidetone)
* o DAC to ADC (loopback).
*/
@@ -1928,8 +1962,8 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
in = 0;
out = 0;
} else {
- in = is_connected_input_ep(w, NULL);
- out = is_connected_output_ep(w, NULL);
+ in = is_connected_input_ep(w, NULL, NULL);
+ out = is_connected_output_ep(w, NULL, NULL);
}
ret = snprintf(buf, PAGE_SIZE, "%s: %s%s in %d out %d",
@@ -2080,7 +2114,7 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
* soc_dapm_connect_path() - Connects or disconnects a path
* @path: The path to update
* @connect: The new connect state of the path. True if the path is connected,
- * false if it is disconneted.
+ * false if it is disconnected.
* @reason: The reason why the path changed (for debugging only)
*/
static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,
@@ -2199,7 +2233,7 @@ static ssize_t dapm_widget_show_component(struct snd_soc_component *cmpnt,
if (w->dapm != dapm)
continue;
- /* only display widgets that burnm power */
+ /* only display widgets that burn power */
switch (w->id) {
case snd_soc_dapm_hp:
case snd_soc_dapm_mic:
@@ -2427,7 +2461,7 @@ static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
switch (w->id) {
case snd_soc_dapm_input:
- /* On a fully routed card a input is never a source */
+ /* On a fully routed card an input is never a source */
if (w->dapm->card->fully_routed)
return;
ep = SND_SOC_DAPM_EP_SOURCE;
@@ -3015,6 +3049,9 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
}
mutex_unlock(&card->dapm_mutex);
+ if (ret)
+ return ret;
+
if (invert)
ucontrol->value.integer.value[0] = max - val;
else
@@ -3166,7 +3203,7 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
if (e->shift_l != e->shift_r) {
if (item[1] > e->items)
return -EINVAL;
- val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_l;
+ val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
mask |= e->mask << e->shift_r;
}
@@ -3282,6 +3319,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
mutex_unlock(&dapm->card->dapm_mutex);
return w;
}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_new_control);
struct snd_soc_dapm_widget *
snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
@@ -3410,7 +3448,7 @@ snd_soc_dapm_new_control_unlocked(struct snd_soc_dapm_context *dapm,
w->endpoints[dir] = -1;
}
- /* machine layer set ups unconnected pins and insertions */
+ /* machine layer sets up unconnected pins and insertions */
w->connected = 1;
return w;
}
@@ -3458,6 +3496,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
const struct snd_soc_pcm_stream *config = w->params + w->params_select;
struct snd_pcm_substream substream;
struct snd_pcm_hw_params *params = NULL;
+ struct snd_pcm_runtime *runtime = NULL;
u64 fmt;
int ret;
@@ -3506,6 +3545,14 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
memset(&substream, 0, sizeof(substream));
+ /* Allocate a dummy snd_pcm_runtime for startup() and other ops() */
+ runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+ if (!runtime) {
+ ret = -ENOMEM;
+ goto out;
+ }
+ substream.runtime = runtime;
+
switch (event) {
case SND_SOC_DAPM_PRE_PMU:
substream.stream = SNDRV_PCM_STREAM_CAPTURE;
@@ -3571,6 +3618,7 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
}
out:
+ kfree(runtime);
kfree(params);
return ret;
}
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
index a513a34a51d2..9fc1a7bb8b95 100644
--- a/sound/soc/soc-ops.c
+++ b/sound/soc/soc-ops.c
@@ -77,7 +77,7 @@ int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
item = snd_soc_enum_val_to_item(e, val);
ucontrol->value.enumerated.item[0] = item;
if (e->shift_l != e->shift_r) {
- val = (reg_val >> e->shift_l) & e->mask;
+ val = (reg_val >> e->shift_r) & e->mask;
item = snd_soc_enum_val_to_item(e, val);
ucontrol->value.enumerated.item[1] = item;
}
diff --git a/sound/soc/soc-pcm.c b/sound/soc/soc-pcm.c
index aa99dac31b3b..d56a16a0f6fa 100644
--- a/sound/soc/soc-pcm.c
+++ b/sound/soc/soc-pcm.c
@@ -1287,6 +1287,46 @@ static int widget_in_list(struct snd_soc_dapm_widget_list *list,
return 0;
}
+static bool dpcm_end_walk_at_be(struct snd_soc_dapm_widget *widget,
+ enum snd_soc_dapm_direction dir)
+{
+ struct snd_soc_card *card = widget->dapm->card;
+ struct snd_soc_pcm_runtime *rtd;
+ int i;
+
+ if (dir == SND_SOC_DAPM_DIR_OUT) {
+ list_for_each_entry(rtd, &card->rtd_list, list) {
+ if (!rtd->dai_link->no_pcm)
+ continue;
+
+ if (rtd->cpu_dai->playback_widget == widget)
+ return true;
+
+ for (i = 0; i < rtd->num_codecs; ++i) {
+ struct snd_soc_dai *dai = rtd->codec_dais[i];
+ if (dai->playback_widget == widget)
+ return true;
+ }
+ }
+ } else { /* SND_SOC_DAPM_DIR_IN */
+ list_for_each_entry(rtd, &card->rtd_list, list) {
+ if (!rtd->dai_link->no_pcm)
+ continue;
+
+ if (rtd->cpu_dai->capture_widget == widget)
+ return true;
+
+ for (i = 0; i < rtd->num_codecs; ++i) {
+ struct snd_soc_dai *dai = rtd->codec_dais[i];
+ if (dai->capture_widget == widget)
+ return true;
+ }
+ }
+ }
+
+ return false;
+}
+
int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
int stream, struct snd_soc_dapm_widget_list **list)
{
@@ -1294,7 +1334,8 @@ int dpcm_path_get(struct snd_soc_pcm_runtime *fe,
int paths;
/* get number of valid DAI paths and their widgets */
- paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list);
+ paths = snd_soc_dapm_dai_get_connected_widgets(cpu_dai, stream, list,
+ dpcm_end_walk_at_be);
dev_dbg(fe->dev, "ASoC: found %d audio %s paths\n", paths,
stream ? "capture" : "playback");
@@ -1653,6 +1694,9 @@ static int dpcm_apply_symmetry(struct snd_pcm_substream *fe_substream,
struct snd_soc_pcm_runtime *rtd = be_substream->private_data;
int i;
+ if (rtd->dai_link->be_hw_params_fixup)
+ continue;
+
if (soc_pcm_has_symmetry(be_substream))
be_substream->runtime->hw.info |= SNDRV_PCM_INFO_JOINT_DUPLEX;
@@ -1749,7 +1793,7 @@ int dpcm_be_dai_shutdown(struct snd_soc_pcm_runtime *fe, int stream)
continue;
dev_dbg(be->dev, "ASoC: close BE %s\n",
- dpcm->fe->dai_link->name);
+ be->dai_link->name);
soc_pcm_close(be_substream);
be_substream->runtime = NULL;
@@ -1815,7 +1859,7 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
continue;
dev_dbg(be->dev, "ASoC: hw_free BE %s\n",
- dpcm->fe->dai_link->name);
+ be->dai_link->name);
soc_pcm_hw_free(be_substream);
@@ -1893,7 +1937,7 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
continue;
dev_dbg(be->dev, "ASoC: hw_params BE %s\n",
- dpcm->fe->dai_link->name);
+ be->dai_link->name);
ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params);
if (ret < 0) {
@@ -1973,7 +2017,7 @@ static int dpcm_do_trigger(struct snd_soc_dpcm *dpcm,
int ret;
dev_dbg(dpcm->be->dev, "ASoC: trigger BE %s cmd %d\n",
- dpcm->fe->dai_link->name, cmd);
+ dpcm->be->dai_link->name, cmd);
ret = soc_pcm_trigger(substream, cmd);
if (ret < 0)
@@ -2188,7 +2232,7 @@ int dpcm_be_dai_prepare(struct snd_soc_pcm_runtime *fe, int stream)
continue;
dev_dbg(be->dev, "ASoC: prepare BE %s\n",
- dpcm->fe->dai_link->name);
+ be->dai_link->name);
ret = soc_pcm_prepare(be_substream);
if (ret < 0) {
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index ee7f15aa46fc..6b05047a4134 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -48,9 +48,10 @@
#define SOC_TPLG_PASS_PCM_DAI 4
#define SOC_TPLG_PASS_GRAPH 5
#define SOC_TPLG_PASS_PINS 6
+#define SOC_TPLG_PASS_BE_DAI 7
#define SOC_TPLG_PASS_START SOC_TPLG_PASS_MANIFEST
-#define SOC_TPLG_PASS_END SOC_TPLG_PASS_PINS
+#define SOC_TPLG_PASS_END SOC_TPLG_PASS_BE_DAI
struct soc_tplg {
const struct firmware *fw;
@@ -1475,6 +1476,7 @@ widget:
if (widget == NULL) {
dev_err(tplg->dev, "ASoC: failed to create widget %s controls\n",
w->name);
+ ret = -ENOMEM;
goto hdr_err;
}
@@ -1554,6 +1556,25 @@ static void set_stream_info(struct snd_soc_pcm_stream *stream,
stream->rate_min = caps->rate_min;
stream->rate_max = caps->rate_max;
stream->formats = caps->formats;
+ stream->sig_bits = caps->sig_bits;
+}
+
+static void set_dai_flags(struct snd_soc_dai_driver *dai_drv,
+ unsigned int flag_mask, unsigned int flags)
+{
+ if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES)
+ dai_drv->symmetric_rates =
+ flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_RATES ? 1 : 0;
+
+ if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS)
+ dai_drv->symmetric_channels =
+ flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_CHANNELS ?
+ 1 : 0;
+
+ if (flag_mask & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS)
+ dai_drv->symmetric_samplebits =
+ flags & SND_SOC_TPLG_DAI_FLGBIT_SYMMETRIC_SAMPLEBITS ?
+ 1 : 0;
}
static int soc_tplg_dai_create(struct soc_tplg *tplg,
@@ -1690,8 +1711,96 @@ static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
return 0;
}
+/* *
+ * soc_tplg_be_dai_config - Find and configure an existing BE DAI.
+ * @tplg: topology context
+ * @be: topology BE DAI configs.
+ *
+ * The BE dai should already be registered by the platform driver. The
+ * platform driver should specify the BE DAI name and ID for matching.
+ */
+static int soc_tplg_be_dai_config(struct soc_tplg *tplg,
+ struct snd_soc_tplg_be_dai *be)
+{
+ struct snd_soc_dai_link_component dai_component = {0};
+ struct snd_soc_dai *dai;
+ struct snd_soc_dai_driver *dai_drv;
+ struct snd_soc_pcm_stream *stream;
+ struct snd_soc_tplg_stream_caps *caps;
+ int ret;
+
+ dai_component.dai_name = be->dai_name;
+ dai = snd_soc_find_dai(&dai_component);
+ if (!dai) {
+ dev_err(tplg->dev, "ASoC: BE DAI %s not registered\n",
+ be->dai_name);
+ return -EINVAL;
+ }
+
+ if (be->dai_id != dai->id) {
+ dev_err(tplg->dev, "ASoC: BE DAI %s id mismatch\n",
+ be->dai_name);
+ return -EINVAL;
+ }
+
+ dai_drv = dai->driver;
+ if (!dai_drv)
+ return -EINVAL;
+
+ if (be->playback) {
+ stream = &dai_drv->playback;
+ caps = &be->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
+ set_stream_info(stream, caps);
+ }
+
+ if (be->capture) {
+ stream = &dai_drv->capture;
+ caps = &be->caps[SND_SOC_TPLG_STREAM_CAPTURE];
+ set_stream_info(stream, caps);
+ }
+
+ if (be->flag_mask)
+ set_dai_flags(dai_drv, be->flag_mask, be->flags);
+
+ /* pass control to component driver for optional further init */
+ ret = soc_tplg_dai_load(tplg, dai_drv);
+ if (ret < 0) {
+ dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int soc_tplg_be_dai_elems_load(struct soc_tplg *tplg,
+ struct snd_soc_tplg_hdr *hdr)
+{
+ struct snd_soc_tplg_be_dai *be;
+ int count = hdr->count;
+ int i;
+
+ if (tplg->pass != SOC_TPLG_PASS_BE_DAI)
+ return 0;
+
+ /* config the existing BE DAIs */
+ for (i = 0; i < count; i++) {
+ be = (struct snd_soc_tplg_be_dai *)tplg->pos;
+ if (be->size != sizeof(*be)) {
+ dev_err(tplg->dev, "ASoC: invalid BE DAI size\n");
+ return -EINVAL;
+ }
+
+ soc_tplg_be_dai_config(tplg, be);
+ tplg->pos += (sizeof(*be) + be->priv.size);
+ }
+
+ dev_dbg(tplg->dev, "ASoC: Configure %d BE DAIs\n", count);
+ return 0;
+}
+
+
static int soc_tplg_manifest_load(struct soc_tplg *tplg,
- struct snd_soc_tplg_hdr *hdr)
+ struct snd_soc_tplg_hdr *hdr)
{
struct snd_soc_tplg_manifest *manifest;
@@ -1793,6 +1902,8 @@ static int soc_tplg_load_header(struct soc_tplg *tplg,
return soc_tplg_dapm_widget_elems_load(tplg, hdr);
case SND_SOC_TPLG_TYPE_PCM:
return soc_tplg_pcm_elems_load(tplg, hdr);
+ case SND_SOC_TPLG_TYPE_BE_DAI:
+ return soc_tplg_be_dai_elems_load(tplg, hdr);
case SND_SOC_TPLG_TYPE_MANIFEST:
return soc_tplg_manifest_load(tplg, hdr);
default:
diff --git a/sound/soc/soc-utils.c b/sound/soc/soc-utils.c
index 53dd085d3ee2..393e8f0fe2cc 100644
--- a/sound/soc/soc-utils.c
+++ b/sound/soc/soc-utils.c
@@ -80,7 +80,7 @@ static int dummy_dma_open(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_pcm_ops dummy_dma_ops = {
+static const struct snd_pcm_ops dummy_dma_ops = {
.open = dummy_dma_open,
.ioctl = snd_pcm_lib_ioctl,
};
diff --git a/sound/soc/sti/sti_uniperif.c b/sound/soc/sti/sti_uniperif.c
index 488ef4ed8fba..549fac349fa0 100644
--- a/sound/soc/sti/sti_uniperif.c
+++ b/sound/soc/sti/sti_uniperif.c
@@ -19,6 +19,84 @@
#define UNIPERIF_MAX_FRAME_SZ 0x20
#define UNIPERIF_ALLOWED_FRAME_SZ (0x08 | 0x10 | 0x18 | UNIPERIF_MAX_FRAME_SZ)
+struct sti_uniperiph_dev_data {
+ unsigned int id; /* Nb available player instances */
+ unsigned int version; /* player IP version */
+ unsigned int stream;
+ const char *dai_names;
+ enum uniperif_type type;
+};
+
+static const struct sti_uniperiph_dev_data sti_uniplayer_hdmi = {
+ .id = 0,
+ .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
+ .stream = SNDRV_PCM_STREAM_PLAYBACK,
+ .dai_names = "Uni Player #0 (HDMI)",
+ .type = SND_ST_UNIPERIF_TYPE_HDMI
+};
+
+static const struct sti_uniperiph_dev_data sti_uniplayer_pcm_out = {
+ .id = 1,
+ .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
+ .stream = SNDRV_PCM_STREAM_PLAYBACK,
+ .dai_names = "Uni Player #1 (PCM OUT)",
+ .type = SND_ST_UNIPERIF_TYPE_PCM | SND_ST_UNIPERIF_TYPE_TDM,
+};
+
+static const struct sti_uniperiph_dev_data sti_uniplayer_dac = {
+ .id = 2,
+ .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
+ .stream = SNDRV_PCM_STREAM_PLAYBACK,
+ .dai_names = "Uni Player #2 (DAC)",
+ .type = SND_ST_UNIPERIF_TYPE_PCM,
+};
+
+static const struct sti_uniperiph_dev_data sti_uniplayer_spdif = {
+ .id = 3,
+ .version = SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0,
+ .stream = SNDRV_PCM_STREAM_PLAYBACK,
+ .dai_names = "Uni Player #3 (SPDIF)",
+ .type = SND_ST_UNIPERIF_TYPE_SPDIF
+};
+
+static const struct sti_uniperiph_dev_data sti_unireader_pcm_in = {
+ .id = 0,
+ .version = SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0,
+ .stream = SNDRV_PCM_STREAM_CAPTURE,
+ .dai_names = "Uni Reader #0 (PCM IN)",
+ .type = SND_ST_UNIPERIF_TYPE_PCM | SND_ST_UNIPERIF_TYPE_TDM,
+};
+
+static const struct sti_uniperiph_dev_data sti_unireader_hdmi_in = {
+ .id = 1,
+ .version = SND_ST_UNIPERIF_VERSION_UNI_RDR_1_0,
+ .stream = SNDRV_PCM_STREAM_CAPTURE,
+ .dai_names = "Uni Reader #1 (HDMI IN)",
+ .type = SND_ST_UNIPERIF_TYPE_PCM,
+};
+
+static const struct of_device_id snd_soc_sti_match[] = {
+ { .compatible = "st,stih407-uni-player-hdmi",
+ .data = &sti_uniplayer_hdmi
+ },
+ { .compatible = "st,stih407-uni-player-pcm-out",
+ .data = &sti_uniplayer_pcm_out
+ },
+ { .compatible = "st,stih407-uni-player-dac",
+ .data = &sti_uniplayer_dac
+ },
+ { .compatible = "st,stih407-uni-player-spdif",
+ .data = &sti_uniplayer_spdif
+ },
+ { .compatible = "st,stih407-uni-reader-pcm_in",
+ .data = &sti_unireader_pcm_in
+ },
+ { .compatible = "st,stih407-uni-reader-hdmi",
+ .data = &sti_unireader_hdmi_in
+ },
+ {},
+};
+
int sti_uniperiph_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
unsigned int rx_mask, int slots,
int slot_width)
@@ -167,8 +245,8 @@ static int sti_uniperiph_dai_create_ctrl(struct snd_soc_dai *dai)
* Uniperipheral instance ID
*/
ctrl = &uni->snd_ctrls[i];
- ctrl->index = uni->info->id;
- ctrl->device = uni->info->id;
+ ctrl->index = uni->id;
+ ctrl->device = uni->id;
}
return snd_soc_add_dai_controls(dai, uni->snd_ctrls, uni->num_ctrls);
@@ -186,7 +264,7 @@ int sti_uniperiph_dai_hw_params(struct snd_pcm_substream *substream,
struct snd_dmaengine_dai_dma_data *dma_data;
int transfer_size;
- if (uni->info->type == SND_ST_UNIPERIF_TYPE_TDM)
+ if (uni->type == SND_ST_UNIPERIF_TYPE_TDM)
/* transfer size = user frame size (in 32-bits FIFO cell) */
transfer_size = snd_soc_params_to_frame_size(params) / 32;
else
@@ -235,7 +313,7 @@ static int sti_uniperiph_dai_resume(struct snd_soc_dai *dai)
struct uniperif *uni = priv->dai_data.uni;
int ret;
- if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player")) {
+ if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK) {
ret = uni_player_resume(uni);
if (ret)
return ret;
@@ -256,7 +334,7 @@ static int sti_uniperiph_dai_probe(struct snd_soc_dai *dai)
struct sti_uniperiph_dai *dai_data = &priv->dai_data;
/* DMA settings*/
- if (of_device_is_compatible(dai->dev->of_node, "st,sti-uni-player"))
+ if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK)
snd_soc_dai_init_dma_data(dai, &dai_data->dma_data, NULL);
else
snd_soc_dai_init_dma_data(dai, NULL, &dai_data->dma_data);
@@ -280,25 +358,32 @@ static const struct snd_soc_component_driver sti_uniperiph_dai_component = {
static int sti_uniperiph_cpu_dai_of(struct device_node *node,
struct sti_uniperiph_data *priv)
{
- const char *str;
- int ret;
struct device *dev = &priv->pdev->dev;
struct sti_uniperiph_dai *dai_data = &priv->dai_data;
struct snd_soc_dai_driver *dai = priv->dai;
struct snd_soc_pcm_stream *stream;
struct uniperif *uni;
+ const struct of_device_id *of_id;
+ const struct sti_uniperiph_dev_data *dev_data;
+ const char *mode;
+
+ /* Populate data structure depending on compatibility */
+ of_id = of_match_node(snd_soc_sti_match, node);
+ if (!of_id->data) {
+ dev_err(dev, "data associated to device is missing");
+ return -EINVAL;
+ }
+ dev_data = (struct sti_uniperiph_dev_data *)of_id->data;
uni = devm_kzalloc(dev, sizeof(*uni), GFP_KERNEL);
if (!uni)
return -ENOMEM;
+ uni->id = dev_data->id;
+ uni->ver = dev_data->version;
+
*dai = sti_uniperiph_dai_template;
- ret = of_property_read_string(node, "dai-name", &str);
- if (ret < 0) {
- dev_err(dev, "%s: dai name missing.\n", __func__);
- return -EINVAL;
- }
- dai->name = str;
+ dai->name = dev_data->dai_names;
/* Get resources */
uni->mem_region = platform_get_resource(priv->pdev, IORESOURCE_MEM, 0);
@@ -322,9 +407,20 @@ static int sti_uniperiph_cpu_dai_of(struct device_node *node,
return -ENXIO;
}
+ uni->type = dev_data->type;
+
+ /* check if player should be configured for tdm */
+ if (dev_data->type & SND_ST_UNIPERIF_TYPE_TDM) {
+ if (!of_property_read_string(node, "st,tdm-mode", &mode))
+ uni->type = SND_ST_UNIPERIF_TYPE_TDM;
+ else
+ uni->type = SND_ST_UNIPERIF_TYPE_PCM;
+ }
+
dai_data->uni = uni;
+ dai_data->stream = dev_data->stream;
- if (of_device_is_compatible(node, "st,sti-uni-player")) {
+ if (priv->dai_data.stream == SNDRV_PCM_STREAM_PLAYBACK) {
uni_player_init(priv->pdev, uni);
stream = &dai->playback;
} else {
@@ -376,12 +472,6 @@ static int sti_uniperiph_probe(struct platform_device *pdev)
&dmaengine_pcm_config, 0);
}
-static const struct of_device_id snd_soc_sti_match[] = {
- { .compatible = "st,sti-uni-player", },
- { .compatible = "st,sti-uni-reader", },
- {},
-};
-
static struct platform_driver sti_uniperiph_driver = {
.driver = {
.name = "sti-uniperiph-dai",
diff --git a/sound/soc/sti/uniperif.h b/sound/soc/sti/uniperif.h
index eb9933c62ad6..1993c655fb79 100644
--- a/sound/soc/sti/uniperif.h
+++ b/sound/soc/sti/uniperif.h
@@ -1220,16 +1220,16 @@
#define UNIPERIF_FIFO_FRAMES 4 /* FDMA trigger limit in frames */
#define UNIPERIF_TYPE_IS_HDMI(p) \
- ((p)->info->type == SND_ST_UNIPERIF_TYPE_HDMI)
+ ((p)->type == SND_ST_UNIPERIF_TYPE_HDMI)
#define UNIPERIF_TYPE_IS_PCM(p) \
- ((p)->info->type == SND_ST_UNIPERIF_TYPE_PCM)
+ ((p)->type == SND_ST_UNIPERIF_TYPE_PCM)
#define UNIPERIF_TYPE_IS_SPDIF(p) \
- ((p)->info->type == SND_ST_UNIPERIF_TYPE_SPDIF)
+ ((p)->type == SND_ST_UNIPERIF_TYPE_SPDIF)
#define UNIPERIF_TYPE_IS_IEC958(p) \
(UNIPERIF_TYPE_IS_HDMI(p) || \
UNIPERIF_TYPE_IS_SPDIF(p))
#define UNIPERIF_TYPE_IS_TDM(p) \
- ((p)->info->type == SND_ST_UNIPERIF_TYPE_TDM)
+ ((p)->type == SND_ST_UNIPERIF_TYPE_TDM)
/*
* Uniperipheral IP revisions
@@ -1249,11 +1249,11 @@ enum uniperif_version {
};
enum uniperif_type {
- SND_ST_UNIPERIF_TYPE_NONE,
- SND_ST_UNIPERIF_TYPE_HDMI,
- SND_ST_UNIPERIF_TYPE_PCM,
- SND_ST_UNIPERIF_TYPE_SPDIF,
- SND_ST_UNIPERIF_TYPE_TDM
+ SND_ST_UNIPERIF_TYPE_NONE = 0x00,
+ SND_ST_UNIPERIF_TYPE_HDMI = 0x01,
+ SND_ST_UNIPERIF_TYPE_PCM = 0x02,
+ SND_ST_UNIPERIF_TYPE_SPDIF = 0x04,
+ SND_ST_UNIPERIF_TYPE_TDM = 0x08
};
enum uniperif_state {
@@ -1278,12 +1278,6 @@ enum uniperif_word_pos {
WORD_MAX
};
-struct uniperif_info {
- int id; /* instance value of the uniperipheral IP */
- enum uniperif_type type;
- int underflow_enabled; /* Underflow recovery mode */
-};
-
struct uniperif_iec958_settings {
enum uniperif_iec958_encoding_mode encoding_mode;
struct snd_aes_iec958 iec958;
@@ -1298,8 +1292,10 @@ struct dai_tdm_slot {
struct uniperif {
/* System information */
- struct uniperif_info *info;
+ enum uniperif_type type;
+ int underflow_enabled; /* Underflow recovery mode */
struct device *dev;
+ int id; /* instance value of the uniperipheral IP */
int ver; /* IP version, used by register access macros */
struct regmap_field *clk_sel;
struct regmap_field *valid_sel;
diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c
index ee1c7c245bc7..1bc8ebc2528e 100644
--- a/sound/soc/sti/uniperif_player.c
+++ b/sound/soc/sti/uniperif_player.c
@@ -100,7 +100,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
dev_err(player->dev, "FIFO underflow error detected");
/* Interrupt is just for information when underflow recovery */
- if (player->info->underflow_enabled) {
+ if (player->underflow_enabled) {
/* Update state to underflow */
player->state = UNIPERIF_STATE_UNDERFLOW;
@@ -134,7 +134,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
/* Check for underflow recovery done */
if (unlikely(status & UNIPERIF_ITM_UNDERFLOW_REC_DONE_MASK(player))) {
- if (!player->info->underflow_enabled) {
+ if (!player->underflow_enabled) {
dev_err(player->dev, "unexpected Underflow recovering");
return -EPERM;
}
@@ -764,7 +764,7 @@ static int uni_player_prepare(struct snd_pcm_substream *substream,
}
/* Calculate transfer size (in fifo cells and bytes) for frame count */
- if (player->info->type == SND_ST_UNIPERIF_TYPE_TDM) {
+ if (player->type == SND_ST_UNIPERIF_TYPE_TDM) {
/* transfer size = user frame size (in 32 bits FIFO cell) */
transfer_size =
sti_uniperiph_get_user_frame_size(runtime) / 4;
@@ -794,7 +794,7 @@ static int uni_player_prepare(struct snd_pcm_substream *substream,
SET_UNIPERIF_CONFIG_DMA_TRIG_LIMIT(player, trigger_limit);
/* Uniperipheral setup depends on player type */
- switch (player->info->type) {
+ switch (player->type) {
case SND_ST_UNIPERIF_TYPE_HDMI:
ret = uni_player_prepare_iec958(player, runtime);
break;
@@ -884,7 +884,7 @@ static int uni_player_start(struct uniperif *player)
SET_UNIPERIF_ITM_BSET_FIFO_ERROR(player);
/* Enable underflow recovery interrupts */
- if (player->info->underflow_enabled) {
+ if (player->underflow_enabled) {
SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(player);
SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(player);
}
@@ -893,8 +893,10 @@ static int uni_player_start(struct uniperif *player)
SET_UNIPERIF_SOFT_RST_SOFT_RST(player);
ret = reset_player(player);
- if (ret < 0)
+ if (ret < 0) {
+ clk_disable_unprepare(player->clk);
return ret;
+ }
/*
* Does not use IEC61937 features of the uniperipheral hardware.
@@ -1021,17 +1023,17 @@ static int uni_player_parse_dt_audio_glue(struct platform_device *pdev,
struct reg_field regfield[2] = {
/* PCM_CLK_SEL */
REG_FIELD(SYS_CFG_AUDIO_GLUE,
- 8 + player->info->id,
- 8 + player->info->id),
+ 8 + player->id,
+ 8 + player->id),
/* PCMP_VALID_SEL */
REG_FIELD(SYS_CFG_AUDIO_GLUE, 0, 1)
};
regmap = syscon_regmap_lookup_by_phandle(node, "st,syscfg");
- if (!regmap) {
+ if (IS_ERR(regmap)) {
dev_err(&pdev->dev, "sti-audio-clk-glue syscf not found\n");
- return -EINVAL;
+ return PTR_ERR(regmap);
}
player->clk_sel = regmap_field_alloc(regmap, regfield[0]);
@@ -1040,60 +1042,6 @@ static int uni_player_parse_dt_audio_glue(struct platform_device *pdev,
return 0;
}
-static int uni_player_parse_dt(struct platform_device *pdev,
- struct uniperif *player)
-{
- struct uniperif_info *info;
- struct device *dev = &pdev->dev;
- struct device_node *pnode = pdev->dev.of_node;
- const char *mode;
-
- /* Allocate memory for the info structure */
- info = devm_kzalloc(dev, sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- if (of_property_read_u32(pnode, "st,version", &player->ver) ||
- player->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) {
- dev_err(dev, "Unknown uniperipheral version ");
- return -EINVAL;
- }
- /* Underflow recovery is only supported on later ip revisions */
- if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
- info->underflow_enabled = 1;
-
- if (of_property_read_u32(pnode, "st,uniperiph-id", &info->id)) {
- dev_err(dev, "uniperipheral id not defined");
- return -EINVAL;
- }
-
- /* Read the device mode property */
- if (of_property_read_string(pnode, "st,mode", &mode)) {
- dev_err(dev, "uniperipheral mode not defined");
- return -EINVAL;
- }
-
- if (strcasecmp(mode, "hdmi") == 0)
- info->type = SND_ST_UNIPERIF_TYPE_HDMI;
- else if (strcasecmp(mode, "pcm") == 0)
- info->type = SND_ST_UNIPERIF_TYPE_PCM;
- else if (strcasecmp(mode, "spdif") == 0)
- info->type = SND_ST_UNIPERIF_TYPE_SPDIF;
- else if (strcasecmp(mode, "tdm") == 0)
- info->type = SND_ST_UNIPERIF_TYPE_TDM;
- else
- info->type = SND_ST_UNIPERIF_TYPE_NONE;
-
- /* Save the info structure */
- player->info = info;
-
- /* Get PCM_CLK_SEL & PCMP_VALID_SEL from audio-glue-ctrl SoC reg */
- if (uni_player_parse_dt_audio_glue(pdev, player))
- return -EINVAL;
-
- return 0;
-}
-
static const struct snd_soc_dai_ops uni_player_dai_ops = {
.startup = uni_player_startup,
.shutdown = uni_player_shutdown,
@@ -1114,13 +1062,18 @@ int uni_player_init(struct platform_device *pdev,
player->state = UNIPERIF_STATE_STOPPED;
player->dai_ops = &uni_player_dai_ops;
- ret = uni_player_parse_dt(pdev, player);
+ /* Get PCM_CLK_SEL & PCMP_VALID_SEL from audio-glue-ctrl SoC reg */
+ ret = uni_player_parse_dt_audio_glue(pdev, player);
if (ret < 0) {
dev_err(player->dev, "Failed to parse DeviceTree");
return ret;
}
+ /* Underflow recovery is only supported on later ip revisions */
+ if (player->ver >= SND_ST_UNIPERIF_VERSION_UNI_PLR_TOP_1_0)
+ player->underflow_enabled = 1;
+
if (UNIPERIF_TYPE_IS_TDM(player))
player->hw = &uni_tdm_hw;
else
@@ -1144,8 +1097,8 @@ int uni_player_init(struct platform_device *pdev,
/* connect to I2S/TDM TX bus */
if (player->valid_sel &&
- (player->info->id == UNIPERIF_PLAYER_I2S_OUT)) {
- ret = regmap_field_write(player->valid_sel, player->info->id);
+ (player->id == UNIPERIF_PLAYER_I2S_OUT)) {
+ ret = regmap_field_write(player->valid_sel, player->id);
if (ret) {
dev_err(player->dev,
"%s: unable to connect to tdm bus", __func__);
diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c
index eb74a328c928..0e1c3ee56675 100644
--- a/sound/soc/sti/uniperif_reader.c
+++ b/sound/soc/sti/uniperif_reader.c
@@ -13,6 +13,7 @@
#include "uniperif.h"
+#define UNIPERIF_READER_I2S_IN 0 /* reader id connected to I2S/TDM TX bus */
/*
* Note: snd_pcm_hardware is linked to DMA controller but is declared here to
* integrate unireader capability in term of rate and supported channels
@@ -195,7 +196,7 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream,
}
/* Calculate transfer size (in fifo cells and bytes) for frame count */
- if (reader->info->type == SND_ST_UNIPERIF_TYPE_TDM) {
+ if (reader->type == SND_ST_UNIPERIF_TYPE_TDM) {
/* transfer size = unip frame size (in 32 bits FIFO cell) */
transfer_size =
sti_uniperiph_get_user_frame_size(runtime) / 4;
@@ -280,7 +281,7 @@ static int uni_reader_prepare(struct snd_pcm_substream *substream,
SET_UNIPERIF_ITM_BSET_MEM_BLK_READ(reader);
/* Enable underflow recovery interrupts */
- if (reader->info->underflow_enabled) {
+ if (reader->underflow_enabled) {
SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_DONE(reader);
SET_UNIPERIF_ITM_BSET_UNDERFLOW_REC_FAILED(reader);
}
@@ -394,41 +395,6 @@ static void uni_reader_shutdown(struct snd_pcm_substream *substream,
}
}
-static int uni_reader_parse_dt(struct platform_device *pdev,
- struct uniperif *reader)
-{
- struct uniperif_info *info;
- struct device_node *node = pdev->dev.of_node;
- const char *mode;
-
- /* Allocate memory for the info structure */
- info = devm_kzalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
- if (!info)
- return -ENOMEM;
-
- if (of_property_read_u32(node, "st,version", &reader->ver) ||
- reader->ver == SND_ST_UNIPERIF_VERSION_UNKNOWN) {
- dev_err(&pdev->dev, "Unknown uniperipheral version ");
- return -EINVAL;
- }
-
- /* Read the device mode property */
- if (of_property_read_string(node, "st,mode", &mode)) {
- dev_err(&pdev->dev, "uniperipheral mode not defined");
- return -EINVAL;
- }
-
- if (strcasecmp(mode, "tdm") == 0)
- info->type = SND_ST_UNIPERIF_TYPE_TDM;
- else
- info->type = SND_ST_UNIPERIF_TYPE_PCM;
-
- /* Save the info structure */
- reader->info = info;
-
- return 0;
-}
-
static const struct snd_soc_dai_ops uni_reader_dai_ops = {
.startup = uni_reader_startup,
.shutdown = uni_reader_shutdown,
@@ -448,12 +414,6 @@ int uni_reader_init(struct platform_device *pdev,
reader->state = UNIPERIF_STATE_STOPPED;
reader->dai_ops = &uni_reader_dai_ops;
- ret = uni_reader_parse_dt(pdev, reader);
- if (ret < 0) {
- dev_err(reader->dev, "Failed to parse DeviceTree");
- return ret;
- }
-
if (UNIPERIF_TYPE_IS_TDM(reader))
reader->hw = &uni_tdm_hw;
else
diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
index ae42294ef688..dd2368297fd3 100644
--- a/sound/soc/sunxi/Kconfig
+++ b/sound/soc/sunxi/Kconfig
@@ -1,4 +1,5 @@
menu "Allwinner SoC Audio support"
+ depends on ARCH_SUNXI || COMPILE_TEST
config SND_SUN4I_CODEC
tristate "Allwinner A10 Codec Support"
@@ -8,6 +9,15 @@ config SND_SUN4I_CODEC
Select Y or M to add support for the Codec embedded in the Allwinner
A10 and affiliated SoCs.
+config SND_SUN4I_I2S
+ tristate "Allwinner A10 I2S Support"
+ 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 Allwinner A10 I2S. You will also need to select the
+ individual machine drivers to support below.
+
config SND_SUN4I_SPDIF
tristate "Allwinner A10 SPDIF Support"
depends on OF
diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
index 8f5e889667f1..604c7b842837 100644
--- a/sound/soc/sunxi/Makefile
+++ b/sound/soc/sunxi/Makefile
@@ -1,3 +1,3 @@
obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
-
+obj-$(CONFIG_SND_SUN4I_I2S) += sun4i-i2s.o
obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
index 44f170c73b06..e047ec06d538 100644
--- a/sound/soc/sunxi/sun4i-codec.c
+++ b/sound/soc/sunxi/sun4i-codec.c
@@ -96,8 +96,8 @@
/* Other various ADC registers */
#define SUN4I_CODEC_DAC_TXCNT (0x30)
#define SUN4I_CODEC_ADC_RXCNT (0x34)
-#define SUN4I_CODEC_AC_SYS_VERI (0x38)
-#define SUN4I_CODEC_AC_MIC_PHONE_CAL (0x3c)
+#define SUN7I_CODEC_AC_DAC_CAL (0x38)
+#define SUN7I_CODEC_AC_MIC_PHONE_CAL (0x3c)
struct sun4i_codec {
struct device *dev;
@@ -509,7 +509,7 @@ static const struct snd_kcontrol_new sun4i_codec_pa_mute =
static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);
-static const struct snd_kcontrol_new sun4i_codec_widgets[] = {
+static const struct snd_kcontrol_new sun4i_codec_controls[] = {
SOC_SINGLE_TLV("Power Amplifier Volume", SUN4I_CODEC_DAC_ACTL,
SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
sun4i_codec_pa_volume_scale),
@@ -628,12 +628,14 @@ static const struct snd_soc_dapm_route sun4i_codec_codec_dapm_routes[] = {
};
static struct snd_soc_codec_driver sun4i_codec_codec = {
- .controls = sun4i_codec_widgets,
- .num_controls = ARRAY_SIZE(sun4i_codec_widgets),
- .dapm_widgets = sun4i_codec_codec_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
- .dapm_routes = sun4i_codec_codec_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes),
+ .component_driver = {
+ .controls = sun4i_codec_controls,
+ .num_controls = ARRAY_SIZE(sun4i_codec_controls),
+ .dapm_widgets = sun4i_codec_codec_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(sun4i_codec_codec_dapm_widgets),
+ .dapm_routes = sun4i_codec_codec_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(sun4i_codec_codec_dapm_routes),
+ },
};
static const struct snd_soc_component_driver sun4i_codec_component = {
@@ -680,12 +682,37 @@ static const struct regmap_config sun4i_codec_regmap_config = {
.reg_bits = 32,
.reg_stride = 4,
.val_bits = 32,
- .max_register = SUN4I_CODEC_AC_MIC_PHONE_CAL,
+ .max_register = SUN4I_CODEC_ADC_RXCNT,
+};
+
+static const struct regmap_config sun7i_codec_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SUN7I_CODEC_AC_MIC_PHONE_CAL,
+};
+
+struct sun4i_codec_quirks {
+ const struct regmap_config *regmap_config;
+};
+
+static const struct sun4i_codec_quirks sun4i_codec_quirks = {
+ .regmap_config = &sun4i_codec_regmap_config,
+};
+
+static const struct sun4i_codec_quirks sun7i_codec_quirks = {
+ .regmap_config = &sun7i_codec_regmap_config,
};
static const struct of_device_id sun4i_codec_of_match[] = {
- { .compatible = "allwinner,sun4i-a10-codec" },
- { .compatible = "allwinner,sun7i-a20-codec" },
+ {
+ .compatible = "allwinner,sun4i-a10-codec",
+ .data = &sun4i_codec_quirks,
+ },
+ {
+ .compatible = "allwinner,sun7i-a20-codec",
+ .data = &sun7i_codec_quirks,
+ },
{}
};
MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
@@ -758,6 +785,7 @@ static int sun4i_codec_probe(struct platform_device *pdev)
{
struct snd_soc_card *card;
struct sun4i_codec *scodec;
+ const struct sun4i_codec_quirks *quirks;
struct resource *res;
void __iomem *base;
int ret;
@@ -775,8 +803,14 @@ static int sun4i_codec_probe(struct platform_device *pdev)
return PTR_ERR(base);
}
+ quirks = of_device_get_match_data(&pdev->dev);
+ if (quirks == NULL) {
+ dev_err(&pdev->dev, "Failed to determine the quirks to use\n");
+ return -ENODEV;
+ }
+
scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
- &sun4i_codec_regmap_config);
+ quirks->regmap_config);
if (IS_ERR(scodec->regmap)) {
dev_err(&pdev->dev, "Failed to create our regmap\n");
return PTR_ERR(scodec->regmap);
diff --git a/sound/soc/sunxi/sun4i-i2s.c b/sound/soc/sunxi/sun4i-i2s.c
new file mode 100644
index 000000000000..687a8f83dbe5
--- /dev/null
+++ b/sound/soc/sunxi/sun4i-i2s.c
@@ -0,0 +1,701 @@
+/*
+ * Copyright (C) 2015 Andrea Venturi
+ * Andrea Venturi <be17068@iperbole.bo.it>
+ *
+ * Copyright (C) 2016 Maxime Ripard
+ * Maxime Ripard <maxime.ripard@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/clk.h>
+#include <linux/dmaengine.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#define SUN4I_I2S_CTRL_REG 0x00
+#define SUN4I_I2S_CTRL_SDO_EN_MASK GENMASK(11, 8)
+#define SUN4I_I2S_CTRL_SDO_EN(sdo) BIT(8 + (sdo))
+#define SUN4I_I2S_CTRL_MODE_MASK BIT(5)
+#define SUN4I_I2S_CTRL_MODE_SLAVE (1 << 5)
+#define SUN4I_I2S_CTRL_MODE_MASTER (0 << 5)
+#define SUN4I_I2S_CTRL_TX_EN BIT(2)
+#define SUN4I_I2S_CTRL_RX_EN BIT(1)
+#define SUN4I_I2S_CTRL_GL_EN BIT(0)
+
+#define SUN4I_I2S_FMT0_REG 0x04
+#define SUN4I_I2S_FMT0_LRCLK_POLARITY_MASK BIT(7)
+#define SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED (1 << 7)
+#define SUN4I_I2S_FMT0_LRCLK_POLARITY_NORMAL (0 << 7)
+#define SUN4I_I2S_FMT0_BCLK_POLARITY_MASK BIT(6)
+#define SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED (1 << 6)
+#define SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL (0 << 6)
+#define SUN4I_I2S_FMT0_SR_MASK GENMASK(5, 4)
+#define SUN4I_I2S_FMT0_SR(sr) ((sr) << 4)
+#define SUN4I_I2S_FMT0_WSS_MASK GENMASK(3, 2)
+#define SUN4I_I2S_FMT0_WSS(wss) ((wss) << 2)
+#define SUN4I_I2S_FMT0_FMT_MASK GENMASK(1, 0)
+#define SUN4I_I2S_FMT0_FMT_RIGHT_J (2 << 0)
+#define SUN4I_I2S_FMT0_FMT_LEFT_J (1 << 0)
+#define SUN4I_I2S_FMT0_FMT_I2S (0 << 0)
+
+#define SUN4I_I2S_FMT1_REG 0x08
+#define SUN4I_I2S_FIFO_TX_REG 0x0c
+#define SUN4I_I2S_FIFO_RX_REG 0x10
+
+#define SUN4I_I2S_FIFO_CTRL_REG 0x14
+#define SUN4I_I2S_FIFO_CTRL_FLUSH_TX BIT(25)
+#define SUN4I_I2S_FIFO_CTRL_FLUSH_RX BIT(24)
+#define SUN4I_I2S_FIFO_CTRL_TX_MODE_MASK BIT(2)
+#define SUN4I_I2S_FIFO_CTRL_TX_MODE(mode) ((mode) << 2)
+#define SUN4I_I2S_FIFO_CTRL_RX_MODE_MASK GENMASK(1, 0)
+#define SUN4I_I2S_FIFO_CTRL_RX_MODE(mode) (mode)
+
+#define SUN4I_I2S_FIFO_STA_REG 0x18
+
+#define SUN4I_I2S_DMA_INT_CTRL_REG 0x1c
+#define SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN BIT(7)
+#define SUN4I_I2S_DMA_INT_CTRL_RX_DRQ_EN BIT(3)
+
+#define SUN4I_I2S_INT_STA_REG 0x20
+
+#define SUN4I_I2S_CLK_DIV_REG 0x24
+#define SUN4I_I2S_CLK_DIV_MCLK_EN BIT(7)
+#define SUN4I_I2S_CLK_DIV_BCLK_MASK GENMASK(6, 4)
+#define SUN4I_I2S_CLK_DIV_BCLK(bclk) ((bclk) << 4)
+#define SUN4I_I2S_CLK_DIV_MCLK_MASK GENMASK(3, 0)
+#define SUN4I_I2S_CLK_DIV_MCLK(mclk) ((mclk) << 0)
+
+#define SUN4I_I2S_RX_CNT_REG 0x28
+#define SUN4I_I2S_TX_CNT_REG 0x2c
+
+#define SUN4I_I2S_TX_CHAN_SEL_REG 0x30
+#define SUN4I_I2S_TX_CHAN_SEL(num_chan) (((num_chan) - 1) << 0)
+
+#define SUN4I_I2S_TX_CHAN_MAP_REG 0x34
+#define SUN4I_I2S_TX_CHAN_MAP(chan, sample) ((sample) << (chan << 2))
+
+#define SUN4I_I2S_RX_CHAN_SEL_REG 0x38
+#define SUN4I_I2S_RX_CHAN_MAP_REG 0x3c
+
+struct sun4i_i2s {
+ struct clk *bus_clk;
+ struct clk *mod_clk;
+ struct regmap *regmap;
+
+ struct snd_dmaengine_dai_dma_data playback_dma_data;
+};
+
+struct sun4i_i2s_clk_div {
+ u8 div;
+ u8 val;
+};
+
+static const struct sun4i_i2s_clk_div sun4i_i2s_bclk_div[] = {
+ { .div = 2, .val = 0 },
+ { .div = 4, .val = 1 },
+ { .div = 6, .val = 2 },
+ { .div = 8, .val = 3 },
+ { .div = 12, .val = 4 },
+ { .div = 16, .val = 5 },
+};
+
+static const struct sun4i_i2s_clk_div sun4i_i2s_mclk_div[] = {
+ { .div = 1, .val = 0 },
+ { .div = 2, .val = 1 },
+ { .div = 4, .val = 2 },
+ { .div = 6, .val = 3 },
+ { .div = 8, .val = 4 },
+ { .div = 12, .val = 5 },
+ { .div = 16, .val = 6 },
+ { .div = 24, .val = 7 },
+};
+
+static int sun4i_i2s_get_bclk_div(struct sun4i_i2s *i2s,
+ unsigned int oversample_rate,
+ unsigned int word_size)
+{
+ int div = oversample_rate / word_size / 2;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sun4i_i2s_bclk_div); i++) {
+ const struct sun4i_i2s_clk_div *bdiv = &sun4i_i2s_bclk_div[i];
+
+ if (bdiv->div == div)
+ return bdiv->val;
+ }
+
+ return -EINVAL;
+}
+
+static int sun4i_i2s_get_mclk_div(struct sun4i_i2s *i2s,
+ unsigned int oversample_rate,
+ unsigned int module_rate,
+ unsigned int sampling_rate)
+{
+ int div = module_rate / sampling_rate / oversample_rate;
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(sun4i_i2s_mclk_div); i++) {
+ const struct sun4i_i2s_clk_div *mdiv = &sun4i_i2s_mclk_div[i];
+
+ if (mdiv->div == div)
+ return mdiv->val;
+ }
+
+ return -EINVAL;
+}
+
+static int sun4i_i2s_oversample_rates[] = { 128, 192, 256, 384, 512, 768 };
+
+static int sun4i_i2s_set_clk_rate(struct sun4i_i2s *i2s,
+ unsigned int rate,
+ unsigned int word_size)
+{
+ unsigned int clk_rate;
+ int bclk_div, mclk_div;
+ int ret, i;
+
+ switch (rate) {
+ case 176400:
+ case 88200:
+ case 44100:
+ case 22050:
+ case 11025:
+ clk_rate = 22579200;
+ break;
+
+ case 192000:
+ case 128000:
+ case 96000:
+ case 64000:
+ case 48000:
+ case 32000:
+ case 24000:
+ case 16000:
+ case 12000:
+ case 8000:
+ clk_rate = 24576000;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(i2s->mod_clk, clk_rate);
+ if (ret)
+ return ret;
+
+ /* Always favor the highest oversampling rate */
+ for (i = (ARRAY_SIZE(sun4i_i2s_oversample_rates) - 1); i >= 0; i--) {
+ unsigned int oversample_rate = sun4i_i2s_oversample_rates[i];
+
+ bclk_div = sun4i_i2s_get_bclk_div(i2s, oversample_rate,
+ word_size);
+ mclk_div = sun4i_i2s_get_mclk_div(i2s, oversample_rate,
+ clk_rate,
+ rate);
+
+ if ((bclk_div >= 0) && (mclk_div >= 0))
+ break;
+ }
+
+ if ((bclk_div < 0) || (mclk_div < 0))
+ return -EINVAL;
+
+ regmap_write(i2s->regmap, SUN4I_I2S_CLK_DIV_REG,
+ SUN4I_I2S_CLK_DIV_BCLK(bclk_div) |
+ SUN4I_I2S_CLK_DIV_MCLK(mclk_div) |
+ SUN4I_I2S_CLK_DIV_MCLK_EN);
+
+ return 0;
+}
+
+static int sun4i_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ int sr, wss;
+ u32 width;
+
+ if (params_channels(params) != 2)
+ return -EINVAL;
+
+ switch (params_physical_width(params)) {
+ case 16:
+ width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+ break;
+ default:
+ return -EINVAL;
+ }
+ i2s->playback_dma_data.addr_width = width;
+
+ switch (params_width(params)) {
+ case 16:
+ sr = 0;
+ wss = 0;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
+ SUN4I_I2S_FMT0_WSS_MASK | SUN4I_I2S_FMT0_SR_MASK,
+ SUN4I_I2S_FMT0_WSS(wss) | SUN4I_I2S_FMT0_SR(sr));
+
+ return sun4i_i2s_set_clk_rate(i2s, params_rate(params),
+ params_width(params));
+}
+
+static int sun4i_i2s_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+ u32 val;
+
+ /* DAI Mode */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ val = SUN4I_I2S_FMT0_FMT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ val = SUN4I_I2S_FMT0_FMT_LEFT_J;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ val = SUN4I_I2S_FMT0_FMT_RIGHT_J;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
+ SUN4I_I2S_FMT0_FMT_MASK,
+ val);
+
+ /* DAI clock polarity */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_IB_IF:
+ /* Invert both clocks */
+ val = SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED |
+ SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED;
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ /* Invert bit clock */
+ val = SUN4I_I2S_FMT0_BCLK_POLARITY_INVERTED |
+ SUN4I_I2S_FMT0_LRCLK_POLARITY_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ /* Invert frame clock */
+ val = SUN4I_I2S_FMT0_LRCLK_POLARITY_INVERTED |
+ SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL;
+ break;
+ case SND_SOC_DAIFMT_NB_NF:
+ /* Nothing to do for both normal cases */
+ val = SUN4I_I2S_FMT0_BCLK_POLARITY_NORMAL |
+ SUN4I_I2S_FMT0_LRCLK_POLARITY_NORMAL;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_FMT0_REG,
+ SUN4I_I2S_FMT0_BCLK_POLARITY_MASK |
+ SUN4I_I2S_FMT0_LRCLK_POLARITY_MASK,
+ val);
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ /* BCLK and LRCLK master */
+ val = SUN4I_I2S_CTRL_MODE_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* BCLK and LRCLK slave */
+ val = SUN4I_I2S_CTRL_MODE_SLAVE;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+ SUN4I_I2S_CTRL_MODE_MASK,
+ val);
+
+ /* Set significant bits in our FIFOs */
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
+ SUN4I_I2S_FIFO_CTRL_TX_MODE_MASK |
+ SUN4I_I2S_FIFO_CTRL_RX_MODE_MASK,
+ SUN4I_I2S_FIFO_CTRL_TX_MODE(1) |
+ SUN4I_I2S_FIFO_CTRL_RX_MODE(1));
+ return 0;
+}
+
+static void sun4i_i2s_start_playback(struct sun4i_i2s *i2s)
+{
+ /* Flush TX FIFO */
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_FIFO_CTRL_REG,
+ SUN4I_I2S_FIFO_CTRL_FLUSH_TX,
+ SUN4I_I2S_FIFO_CTRL_FLUSH_TX);
+
+ /* Clear TX counter */
+ regmap_write(i2s->regmap, SUN4I_I2S_TX_CNT_REG, 0);
+
+ /* Enable TX Block */
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+ SUN4I_I2S_CTRL_TX_EN,
+ SUN4I_I2S_CTRL_TX_EN);
+
+ /* Enable TX DRQ */
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_DMA_INT_CTRL_REG,
+ SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN,
+ SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN);
+}
+
+
+static void sun4i_i2s_stop_playback(struct sun4i_i2s *i2s)
+{
+ /* Disable TX Block */
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+ SUN4I_I2S_CTRL_TX_EN,
+ 0);
+
+ /* Disable TX DRQ */
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_DMA_INT_CTRL_REG,
+ SUN4I_I2S_DMA_INT_CTRL_TX_DRQ_EN,
+ 0);
+}
+
+static int sun4i_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ 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)
+ sun4i_i2s_start_playback(i2s);
+ else
+ return -EINVAL;
+ break;
+
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ sun4i_i2s_stop_playback(i2s);
+ else
+ return -EINVAL;
+ break;
+
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int sun4i_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ /* Enable the whole hardware block */
+ regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG,
+ SUN4I_I2S_CTRL_GL_EN);
+
+ /* Enable the first output line */
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+ SUN4I_I2S_CTRL_SDO_EN_MASK,
+ SUN4I_I2S_CTRL_SDO_EN(0));
+
+ /* Enable the first two channels */
+ regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_SEL_REG,
+ SUN4I_I2S_TX_CHAN_SEL(2));
+
+ /* Map them to the two first samples coming in */
+ regmap_write(i2s->regmap, SUN4I_I2S_TX_CHAN_MAP_REG,
+ SUN4I_I2S_TX_CHAN_MAP(0, 0) | SUN4I_I2S_TX_CHAN_MAP(1, 1));
+
+ return clk_prepare_enable(i2s->mod_clk);
+}
+
+static void sun4i_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ clk_disable_unprepare(i2s->mod_clk);
+
+ /* Disable our output lines */
+ regmap_update_bits(i2s->regmap, SUN4I_I2S_CTRL_REG,
+ SUN4I_I2S_CTRL_SDO_EN_MASK, 0);
+
+ /* Disable the whole hardware block */
+ regmap_write(i2s->regmap, SUN4I_I2S_CTRL_REG, 0);
+}
+
+static const struct snd_soc_dai_ops sun4i_i2s_dai_ops = {
+ .hw_params = sun4i_i2s_hw_params,
+ .set_fmt = sun4i_i2s_set_fmt,
+ .shutdown = sun4i_i2s_shutdown,
+ .startup = sun4i_i2s_startup,
+ .trigger = sun4i_i2s_trigger,
+};
+
+static int sun4i_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct sun4i_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai, &i2s->playback_dma_data, NULL);
+
+ snd_soc_dai_set_drvdata(dai, i2s);
+
+ return 0;
+}
+
+static struct snd_soc_dai_driver sun4i_i2s_dai = {
+ .probe = sun4i_i2s_dai_probe,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE,
+ },
+ .ops = &sun4i_i2s_dai_ops,
+ .symmetric_rates = 1,
+};
+
+static const struct snd_soc_component_driver sun4i_i2s_component = {
+ .name = "sun4i-dai",
+};
+
+static bool sun4i_i2s_rd_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SUN4I_I2S_FIFO_TX_REG:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+static bool sun4i_i2s_wr_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SUN4I_I2S_FIFO_RX_REG:
+ case SUN4I_I2S_FIFO_STA_REG:
+ return false;
+
+ default:
+ return true;
+ }
+}
+
+static bool sun4i_i2s_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case SUN4I_I2S_FIFO_RX_REG:
+ case SUN4I_I2S_INT_STA_REG:
+ case SUN4I_I2S_RX_CNT_REG:
+ case SUN4I_I2S_TX_CNT_REG:
+ return true;
+
+ default:
+ return false;
+ }
+}
+
+static const struct reg_default sun4i_i2s_reg_defaults[] = {
+ { SUN4I_I2S_CTRL_REG, 0x00000000 },
+ { SUN4I_I2S_FMT0_REG, 0x0000000c },
+ { SUN4I_I2S_FMT1_REG, 0x00004020 },
+ { SUN4I_I2S_FIFO_CTRL_REG, 0x000400f0 },
+ { SUN4I_I2S_DMA_INT_CTRL_REG, 0x00000000 },
+ { SUN4I_I2S_CLK_DIV_REG, 0x00000000 },
+ { SUN4I_I2S_TX_CHAN_SEL_REG, 0x00000001 },
+ { SUN4I_I2S_TX_CHAN_MAP_REG, 0x76543210 },
+ { SUN4I_I2S_RX_CHAN_SEL_REG, 0x00000001 },
+ { SUN4I_I2S_RX_CHAN_MAP_REG, 0x00003210 },
+};
+
+static const struct regmap_config sun4i_i2s_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = SUN4I_I2S_RX_CHAN_MAP_REG,
+
+ .cache_type = REGCACHE_FLAT,
+ .reg_defaults = sun4i_i2s_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(sun4i_i2s_reg_defaults),
+ .writeable_reg = sun4i_i2s_wr_reg,
+ .readable_reg = sun4i_i2s_rd_reg,
+ .volatile_reg = sun4i_i2s_volatile_reg,
+};
+
+static int sun4i_i2s_runtime_resume(struct device *dev)
+{
+ struct sun4i_i2s *i2s = dev_get_drvdata(dev);
+ int ret;
+
+ ret = clk_prepare_enable(i2s->bus_clk);
+ if (ret) {
+ dev_err(dev, "Failed to enable bus clock\n");
+ return ret;
+ }
+
+ regcache_cache_only(i2s->regmap, false);
+ regcache_mark_dirty(i2s->regmap);
+
+ ret = regcache_sync(i2s->regmap);
+ if (ret) {
+ dev_err(dev, "Failed to sync regmap cache\n");
+ goto err_disable_clk;
+ }
+
+ return 0;
+
+err_disable_clk:
+ clk_disable_unprepare(i2s->bus_clk);
+ return ret;
+}
+
+static int sun4i_i2s_runtime_suspend(struct device *dev)
+{
+ struct sun4i_i2s *i2s = dev_get_drvdata(dev);
+
+ regcache_cache_only(i2s->regmap, true);
+
+ clk_disable_unprepare(i2s->bus_clk);
+
+ return 0;
+}
+
+static int sun4i_i2s_probe(struct platform_device *pdev)
+{
+ struct sun4i_i2s *i2s;
+ struct resource *res;
+ void __iomem *regs;
+ int irq, ret;
+
+ i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
+ if (!i2s)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, i2s);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ regs = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(regs))
+ return PTR_ERR(regs);
+
+ irq = platform_get_irq(pdev, 0);
+ if (irq < 0) {
+ dev_err(&pdev->dev, "Can't retrieve our interrupt\n");
+ return irq;
+ }
+
+ i2s->bus_clk = devm_clk_get(&pdev->dev, "apb");
+ if (IS_ERR(i2s->bus_clk)) {
+ dev_err(&pdev->dev, "Can't get our bus clock\n");
+ return PTR_ERR(i2s->bus_clk);
+ }
+
+ i2s->regmap = devm_regmap_init_mmio(&pdev->dev, regs,
+ &sun4i_i2s_regmap_config);
+ if (IS_ERR(i2s->regmap)) {
+ dev_err(&pdev->dev, "Regmap initialisation failed\n");
+ return PTR_ERR(i2s->regmap);
+ }
+
+ i2s->mod_clk = devm_clk_get(&pdev->dev, "mod");
+ if (IS_ERR(i2s->mod_clk)) {
+ dev_err(&pdev->dev, "Can't get our mod clock\n");
+ return PTR_ERR(i2s->mod_clk);
+ }
+
+ i2s->playback_dma_data.addr = res->start + SUN4I_I2S_FIFO_TX_REG;
+ i2s->playback_dma_data.maxburst = 4;
+
+ pm_runtime_enable(&pdev->dev);
+ if (!pm_runtime_enabled(&pdev->dev)) {
+ ret = sun4i_i2s_runtime_resume(&pdev->dev);
+ if (ret)
+ goto err_pm_disable;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev,
+ &sun4i_i2s_component,
+ &sun4i_i2s_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Could not register DAI\n");
+ goto err_suspend;
+ }
+
+ ret = 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))
+ sun4i_i2s_runtime_suspend(&pdev->dev);
+err_pm_disable:
+ pm_runtime_disable(&pdev->dev);
+
+ return ret;
+}
+
+static int sun4i_i2s_remove(struct platform_device *pdev)
+{
+ snd_dmaengine_pcm_unregister(&pdev->dev);
+
+ pm_runtime_disable(&pdev->dev);
+ if (!pm_runtime_status_suspended(&pdev->dev))
+ sun4i_i2s_runtime_suspend(&pdev->dev);
+
+ return 0;
+}
+
+static const struct of_device_id sun4i_i2s_match[] = {
+ { .compatible = "allwinner,sun4i-a10-i2s", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, sun4i_i2s_match);
+
+static const struct dev_pm_ops sun4i_i2s_pm_ops = {
+ .runtime_resume = sun4i_i2s_runtime_resume,
+ .runtime_suspend = sun4i_i2s_runtime_suspend,
+};
+
+static struct platform_driver sun4i_i2s_driver = {
+ .probe = sun4i_i2s_probe,
+ .remove = sun4i_i2s_remove,
+ .driver = {
+ .name = "sun4i-i2s",
+ .of_match_table = sun4i_i2s_match,
+ .pm = &sun4i_i2s_pm_ops,
+ },
+};
+module_platform_driver(sun4i_i2s_driver);
+
+MODULE_AUTHOR("Andrea Venturi <be17068@iperbole.bo.it>");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_DESCRIPTION("Allwinner A10 I2S driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
index 0b04fb02125c..88fbb3a1e660 100644
--- a/sound/soc/sunxi/sun4i-spdif.c
+++ b/sound/soc/sunxi/sun4i-spdif.c
@@ -29,6 +29,7 @@
#include <linux/module.h>
#include <linux/platform_device.h>
#include <linux/pm_runtime.h>
+#include <linux/reset.h>
#include <sound/dmaengine_pcm.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
@@ -162,6 +163,7 @@ struct sun4i_spdif_dev {
struct platform_device *pdev;
struct clk *spdif_clk;
struct clk *apb_clk;
+ struct reset_control *rst;
struct snd_soc_dai_driver cpu_dai_drv;
struct regmap *regmap;
struct snd_dmaengine_dai_dma_data dma_params_tx;
@@ -411,6 +413,7 @@ static const struct snd_soc_dapm_route dit_routes[] = {
static const struct of_device_id sun4i_spdif_of_match[] = {
{ .compatible = "allwinner,sun4i-a10-spdif", },
+ { .compatible = "allwinner,sun6i-a31-spdif", },
{ /* sentinel */ }
};
MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match);
@@ -482,11 +485,23 @@ static int sun4i_spdif_probe(struct platform_device *pdev)
}
host->dma_params_tx.addr = res->start + SUN4I_SPDIF_TXFIFO;
- host->dma_params_tx.maxburst = 4;
+ host->dma_params_tx.maxburst = 8;
host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
platform_set_drvdata(pdev, host);
+ if (of_device_is_compatible(pdev->dev.of_node,
+ "allwinner,sun6i-a31-spdif")) {
+ host->rst = devm_reset_control_get_optional(&pdev->dev, NULL);
+ if (IS_ERR(host->rst) && PTR_ERR(host->rst) == -EPROBE_DEFER) {
+ ret = -EPROBE_DEFER;
+ dev_err(&pdev->dev, "Failed to get reset: %d\n", ret);
+ goto err_disable_apb_clk;
+ }
+ if (!IS_ERR(host->rst))
+ reset_control_deassert(host->rst);
+ }
+
ret = devm_snd_soc_register_component(&pdev->dev,
&sun4i_spdif_component, &sun4i_spdif_dai, 1);
if (ret)
diff --git a/sound/soc/tegra/Kconfig b/sound/soc/tegra/Kconfig
index a6768f832c6f..efbe8d4c019e 100644
--- a/sound/soc/tegra/Kconfig
+++ b/sound/soc/tegra/Kconfig
@@ -138,3 +138,14 @@ config SND_SOC_TEGRA_RT5677
help
Say Y or M here if you want to add support for SoC audio on Tegra
boards using the RT5677 codec, such as Ryu.
+
+config SND_SOC_TEGRA_SGTL5000
+ tristate "SoC Audio support for Tegra boards using a SGTL5000 codec"
+ depends on SND_SOC_TEGRA && I2C && GPIOLIB
+ select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
+ select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
+ select SND_SOC_SGTL5000
+ help
+ Say Y or M here if you want to add support for SoC audio on Tegra
+ boards using the SGTL5000 codec, such as Apalis T30, Apalis TK1 or
+ Colibri T30.
diff --git a/sound/soc/tegra/Makefile b/sound/soc/tegra/Makefile
index 9171655ad843..f214a3fd0024 100644
--- a/sound/soc/tegra/Makefile
+++ b/sound/soc/tegra/Makefile
@@ -26,6 +26,7 @@ snd-soc-tegra-wm9712-objs := tegra_wm9712.o
snd-soc-tegra-trimslice-objs := trimslice.o
snd-soc-tegra-alc5632-objs := tegra_alc5632.o
snd-soc-tegra-max98090-objs := tegra_max98090.o
+snd-soc-tegra-sgtl5000-objs := tegra_sgtl5000.o
obj-$(CONFIG_SND_SOC_TEGRA_RT5640) += snd-soc-tegra-rt5640.o
obj-$(CONFIG_SND_SOC_TEGRA_RT5677) += snd-soc-tegra-rt5677.o
@@ -35,3 +36,4 @@ obj-$(CONFIG_SND_SOC_TEGRA_WM9712) += snd-soc-tegra-wm9712.o
obj-$(CONFIG_SND_SOC_TEGRA_TRIMSLICE) += snd-soc-tegra-trimslice.o
obj-$(CONFIG_SND_SOC_TEGRA_ALC5632) += snd-soc-tegra-alc5632.o
obj-$(CONFIG_SND_SOC_TEGRA_MAX98090) += snd-soc-tegra-max98090.o
+obj-$(CONFIG_SND_SOC_TEGRA_SGTL5000) += snd-soc-tegra-sgtl5000.o \ No newline at end of file
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c
index 773daecaa5e8..e5ef4e9c4ac5 100644
--- a/sound/soc/tegra/tegra_rt5640.c
+++ b/sound/soc/tegra/tegra_rt5640.c
@@ -1,5 +1,5 @@
/*
-* tegra_rt5640.c - Tegra machine ASoC driver for boards using WM8903 codec.
+* tegra_rt5640.c - Tegra machine ASoC driver for boards using RT5640 codec.
*
* Copyright (c) 2013, NVIDIA CORPORATION. All rights reserved.
*
diff --git a/sound/soc/tegra/tegra_sgtl5000.c b/sound/soc/tegra/tegra_sgtl5000.c
new file mode 100644
index 000000000000..1e76869dd488
--- /dev/null
+++ b/sound/soc/tegra/tegra_sgtl5000.c
@@ -0,0 +1,212 @@
+/*
+ * tegra_sgtl5000.c - Tegra machine ASoC driver for boards using SGTL5000 codec
+ *
+ * Author: Marcel Ziswiler <marcel@ziswiler.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.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Based on code copyright/by:
+ *
+ * Copyright (C) 2010-2012 - NVIDIA, Inc.
+ * (c) 2009, 2010 Nvidia Graphics Pvt. Ltd.
+ * Copyright 2007 Wolfson Microelectronics PLC.
+ */
+
+#include <linux/module.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/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#include "../codecs/sgtl5000.h"
+
+#include "tegra_asoc_utils.h"
+
+#define DRV_NAME "tegra-snd-sgtl5000"
+
+struct tegra_sgtl5000 {
+ struct tegra_asoc_utils_data util_data;
+};
+
+static int tegra_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_card *card = rtd->card;
+ struct tegra_sgtl5000 *machine = snd_soc_card_get_drvdata(card);
+ int srate, mclk;
+ int err;
+
+ srate = params_rate(params);
+ switch (srate) {
+ case 11025:
+ case 22050:
+ case 44100:
+ case 88200:
+ mclk = 11289600;
+ break;
+ default:
+ mclk = 12288000;
+ break;
+ }
+
+ err = tegra_asoc_utils_set_rate(&machine->util_data, srate, mclk);
+ if (err < 0) {
+ dev_err(card->dev, "Can't configure clocks\n");
+ return err;
+ }
+
+ err = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk,
+ SND_SOC_CLOCK_IN);
+ if (err < 0) {
+ dev_err(card->dev, "codec_dai clock not set\n");
+ return err;
+ }
+
+ return 0;
+}
+
+static struct snd_soc_ops tegra_sgtl5000_ops = {
+ .hw_params = tegra_sgtl5000_hw_params,
+};
+
+static const struct snd_soc_dapm_widget tegra_sgtl5000_dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_LINE("Line In Jack", NULL),
+ SND_SOC_DAPM_MIC("Mic Jack", NULL),
+};
+
+static struct snd_soc_dai_link tegra_sgtl5000_dai = {
+ .name = "sgtl5000",
+ .stream_name = "HiFi",
+ .codec_dai_name = "sgtl5000",
+ .ops = &tegra_sgtl5000_ops,
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS,
+};
+
+static struct snd_soc_card snd_soc_tegra_sgtl5000 = {
+ .name = "tegra-sgtl5000",
+ .owner = THIS_MODULE,
+ .dai_link = &tegra_sgtl5000_dai,
+ .num_links = 1,
+ .dapm_widgets = tegra_sgtl5000_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(tegra_sgtl5000_dapm_widgets),
+ .fully_routed = true,
+};
+
+static int tegra_sgtl5000_driver_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct snd_soc_card *card = &snd_soc_tegra_sgtl5000;
+ struct tegra_sgtl5000 *machine;
+ int ret;
+
+ machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_sgtl5000),
+ GFP_KERNEL);
+ if (!machine) {
+ dev_err(&pdev->dev, "Can't allocate tegra_sgtl5000 struct\n");
+ return -ENOMEM;
+ }
+
+ card->dev = &pdev->dev;
+ platform_set_drvdata(pdev, card);
+ snd_soc_card_set_drvdata(card, machine);
+
+ ret = snd_soc_of_parse_card_name(card, "nvidia,model");
+ if (ret)
+ goto err;
+
+ ret = snd_soc_of_parse_audio_routing(card, "nvidia,audio-routing");
+ if (ret)
+ goto err;
+
+ tegra_sgtl5000_dai.codec_of_node = of_parse_phandle(np,
+ "nvidia,audio-codec", 0);
+ if (!tegra_sgtl5000_dai.codec_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,audio-codec' missing or invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tegra_sgtl5000_dai.cpu_of_node = of_parse_phandle(np,
+ "nvidia,i2s-controller", 0);
+ if (!tegra_sgtl5000_dai.cpu_of_node) {
+ dev_err(&pdev->dev,
+ "Property 'nvidia,i2s-controller' missing/invalid\n");
+ ret = -EINVAL;
+ goto err;
+ }
+
+ tegra_sgtl5000_dai.platform_of_node = tegra_sgtl5000_dai.cpu_of_node;
+
+ ret = tegra_asoc_utils_init(&machine->util_data, &pdev->dev);
+ if (ret)
+ goto err;
+
+ ret = snd_soc_register_card(card);
+ if (ret) {
+ dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
+ ret);
+ goto err_fini_utils;
+ }
+
+ return 0;
+
+err_fini_utils:
+ tegra_asoc_utils_fini(&machine->util_data);
+err:
+ return ret;
+}
+
+static int tegra_sgtl5000_driver_remove(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = platform_get_drvdata(pdev);
+ struct tegra_sgtl5000 *machine = snd_soc_card_get_drvdata(card);
+ int ret;
+
+ ret = snd_soc_unregister_card(card);
+
+ tegra_asoc_utils_fini(&machine->util_data);
+
+ return ret;
+}
+
+static const struct of_device_id tegra_sgtl5000_of_match[] = {
+ { .compatible = "nvidia,tegra-audio-sgtl5000", },
+ { /* sentinel */ },
+};
+
+static struct platform_driver tegra_sgtl5000_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .pm = &snd_soc_pm_ops,
+ .of_match_table = tegra_sgtl5000_of_match,
+ },
+ .probe = tegra_sgtl5000_driver_probe,
+ .remove = tegra_sgtl5000_driver_remove,
+};
+module_platform_driver(tegra_sgtl5000_driver);
+
+MODULE_AUTHOR("Marcel Ziswiler <marcel@ziswiler.com>");
+MODULE_DESCRIPTION("Tegra SGTL5000 machine ASoC driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:" DRV_NAME);
+MODULE_DEVICE_TABLE(of, tegra_sgtl5000_of_match);
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index 6d5698b25bd4..b343efd9be5b 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -187,7 +187,7 @@ static int setup_clocking(struct snd_soc_dai *dai,
default:
dev_err(dai->dev,
- "%s: Error: Unsopported inversion (fmt = 0x%x)!\n",
+ "%s: Error: Unsupported inversion (fmt = 0x%x)!\n",
__func__, fmt);
return -EINVAL;
@@ -218,7 +218,7 @@ static int setup_clocking(struct snd_soc_dai *dai,
break;
default:
- dev_err(dai->dev, "%s: Error: Unsopported master (fmt = 0x%x)!\n",
+ dev_err(dai->dev, "%s: Error: Unsupported master (fmt = 0x%x)!\n",
__func__, fmt);
return -EINVAL;
@@ -374,7 +374,7 @@ static int setup_msp_config(struct snd_pcm_substream *substream,
break;
default:
- dev_err(dai->dev, "%s: Error: Unsopported format (%d)!\n",
+ dev_err(dai->dev, "%s: Error: Unsupported format (%d)!\n",
__func__, fmt);
return -EINVAL;
}
diff --git a/sound/sound_firmware.c b/sound/sound_firmware.c
deleted file mode 100644
index 026347643c81..000000000000
--- a/sound/sound_firmware.c
+++ /dev/null
@@ -1,77 +0,0 @@
-#include <linux/vmalloc.h>
-#include <linux/module.h>
-#include <linux/fs.h>
-#include <linux/file.h>
-#include <linux/mm.h>
-#include <linux/sched.h>
-#include <asm/uaccess.h>
-#include "oss/sound_firmware.h"
-
-static int do_mod_firmware_load(const char *fn, char **fp)
-{
- struct file* filp;
- long l;
- char *dp;
-
- filp = filp_open(fn, 0, 0);
- if (IS_ERR(filp))
- {
- printk(KERN_INFO "Unable to load '%s'.\n", fn);
- return 0;
- }
- l = i_size_read(file_inode(filp));
- if (l <= 0 || l > 131072)
- {
- printk(KERN_INFO "Invalid firmware '%s'\n", fn);
- fput(filp);
- return 0;
- }
- dp = vmalloc(l);
- if (dp == NULL)
- {
- printk(KERN_INFO "Out of memory loading '%s'.\n", fn);
- fput(filp);
- return 0;
- }
- if (kernel_read(filp, 0, dp, l) != l)
- {
- printk(KERN_INFO "Failed to read '%s'.\n", fn);
- vfree(dp);
- fput(filp);
- return 0;
- }
- fput(filp);
- *fp = dp;
- return (int) l;
-}
-
-/**
- * mod_firmware_load - load sound driver firmware
- * @fn: filename
- * @fp: return for the buffer.
- *
- * Load the firmware for a sound module (up to 128K) into a buffer.
- * The buffer is returned in *fp. It is allocated with vmalloc so is
- * virtually linear and not DMAable. The caller should free it with
- * vfree when finished.
- *
- * The length of the buffer is returned on a successful load, the
- * value zero on a failure.
- *
- * Caution: This API is not recommended. Firmware should be loaded via
- * request_firmware.
- */
-
-int mod_firmware_load(const char *fn, char **fp)
-{
- int r;
- mm_segment_t fs = get_fs();
-
- set_fs(get_ds());
- r = do_mod_firmware_load(fn, fp);
- set_fs(fs);
- return r;
-}
-EXPORT_SYMBOL(mod_firmware_load);
-
-MODULE_LICENSE("GPL");
diff --git a/sound/usb/caiaq/audio.c b/sound/usb/caiaq/audio.c
index 327f8642ca80..8f66ba730d69 100644
--- a/sound/usb/caiaq/audio.c
+++ b/sound/usb/caiaq/audio.c
@@ -739,7 +739,6 @@ static struct urb **alloc_urbs(struct snd_usb_caiaqdev *cdev, int dir, int *ret)
for (i = 0; i < N_URBS; i++) {
urbs[i] = usb_alloc_urb(FRAMES_PER_URB, GFP_KERNEL);
if (!urbs[i]) {
- dev_err(dev, "unable to usb_alloc_urb(), OOM!?\n");
*ret = -ENOMEM;
return urbs;
}
diff --git a/sound/usb/card.c b/sound/usb/card.c
index 69860da473ea..9e5276d6dda0 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -556,7 +556,6 @@ static int usb_audio_probe(struct usb_interface *intf,
goto __error;
}
chip = usb_chip[i];
- dev_set_drvdata(&dev->dev, chip);
atomic_inc(&chip->active); /* avoid autopm */
break;
}
@@ -582,6 +581,7 @@ static int usb_audio_probe(struct usb_interface *intf,
goto __error;
}
}
+ dev_set_drvdata(&dev->dev, chip);
/*
* For devices with more than one control interface, we assume the
diff --git a/sound/usb/card.h b/sound/usb/card.h
index 71778ca4b26a..111b0f009afa 100644
--- a/sound/usb/card.h
+++ b/sound/usb/card.h
@@ -92,7 +92,7 @@ struct snd_usb_endpoint {
unsigned int curframesize; /* current packet size in frames (for capture) */
unsigned int syncmaxsize; /* sync endpoint packet size */
unsigned int fill_max:1; /* fill max packet size always */
- unsigned int udh01_fb_quirk:1; /* corrupted feedback data */
+ unsigned int tenor_fb_quirk:1; /* corrupted feedback data */
unsigned int datainterval; /* log_2 of data packet interval */
unsigned int syncinterval; /* P for adaptive mode, 0 otherwise */
unsigned char silence_value;
diff --git a/sound/usb/endpoint.c b/sound/usb/endpoint.c
index c07a7eda42a2..c470251cea4b 100644
--- a/sound/usb/endpoint.c
+++ b/sound/usb/endpoint.c
@@ -502,10 +502,6 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
ep->syncinterval = 3;
ep->syncmaxsize = le16_to_cpu(get_endpoint(alts, 1)->wMaxPacketSize);
-
- if (chip->usb_id == USB_ID(0x0644, 0x8038) /* TEAC UD-H01 */ &&
- ep->syncmaxsize == 4)
- ep->udh01_fb_quirk = 1;
}
list_add_tail(&ep->list, &chip->ep_list);
@@ -1171,15 +1167,16 @@ void snd_usb_handle_sync_urb(struct snd_usb_endpoint *ep,
if (f == 0)
return;
- if (unlikely(sender->udh01_fb_quirk)) {
+ if (unlikely(sender->tenor_fb_quirk)) {
/*
- * The TEAC UD-H01 firmware sometimes changes the feedback value
+ * Devices based on Tenor 8802 chipsets (TEAC UD-H01
+ * and others) sometimes change the feedback value
* by +/- 0x1.0000.
*/
if (f < ep->freqn - 0x8000)
- f += 0x10000;
+ f += 0xf000;
else if (f > ep->freqn + 0x8000)
- f -= 0x10000;
+ f -= 0xf000;
} else if (unlikely(ep->freqshift == INT_MIN)) {
/*
* The first time we see a feedback value, determine its format
diff --git a/sound/usb/format.c b/sound/usb/format.c
index 789d19ec035d..2c44386e5569 100644
--- a/sound/usb/format.c
+++ b/sound/usb/format.c
@@ -176,10 +176,8 @@ static int parse_audio_format_rates_v1(struct snd_usb_audio *chip, struct audiof
int r, idx;
fp->rate_table = kmalloc(sizeof(int) * nr_rates, GFP_KERNEL);
- if (fp->rate_table == NULL) {
- usb_audio_err(chip, "cannot malloc\n");
+ if (fp->rate_table == NULL)
return -ENOMEM;
- }
fp->nr_rates = 0;
fp->rate_min = fp->rate_max = 0;
diff --git a/sound/usb/line6/Kconfig b/sound/usb/line6/Kconfig
index f4585d378ef3..39b400392d71 100644
--- a/sound/usb/line6/Kconfig
+++ b/sound/usb/line6/Kconfig
@@ -2,6 +2,7 @@ config SND_USB_LINE6
tristate
select SND_RAWMIDI
select SND_PCM
+ select SND_HWDEP
config SND_USB_POD
tristate "Line 6 POD USB support"
@@ -21,10 +22,10 @@ config SND_USB_POD
re-amping)
config SND_USB_PODHD
- tristate "Line 6 POD HD300/400/500 USB support"
+ tristate "Line 6 POD X3/HD300/400/500 USB support"
select SND_USB_LINE6
help
- This is a driver for POD HD300, 400 and 500 devices.
+ This is a driver for POD X3, HD300, 400 and 500 devices.
config SND_USB_TONEPORT
tristate "TonePort GX, UX1 and UX2 USB support"
diff --git a/sound/usb/line6/capture.c b/sound/usb/line6/capture.c
index f518fbbe88de..7c812565f90d 100644
--- a/sound/usb/line6/capture.c
+++ b/sound/usb/line6/capture.c
@@ -29,10 +29,10 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
int ret;
struct urb *urb_in;
- index =
- find_first_zero_bit(&line6pcm->in.active_urbs, LINE6_ISO_BUFFERS);
+ index = find_first_zero_bit(&line6pcm->in.active_urbs,
+ line6pcm->line6->iso_buffers);
- if (index < 0 || index >= LINE6_ISO_BUFFERS) {
+ if (index < 0 || index >= line6pcm->line6->iso_buffers) {
dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
return -EINVAL;
}
@@ -44,13 +44,13 @@ static int submit_audio_in_urb(struct snd_line6_pcm *line6pcm)
struct usb_iso_packet_descriptor *fin =
&urb_in->iso_frame_desc[i];
fin->offset = urb_size;
- fin->length = line6pcm->max_packet_size;
- urb_size += line6pcm->max_packet_size;
+ fin->length = line6pcm->max_packet_size_in;
+ urb_size += line6pcm->max_packet_size_in;
}
urb_in->transfer_buffer =
line6pcm->in.buffer +
- index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
+ index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_in;
urb_in->transfer_buffer_length = urb_size;
urb_in->context = line6pcm;
@@ -73,7 +73,7 @@ int line6_submit_audio_in_all_urbs(struct snd_line6_pcm *line6pcm)
{
int ret = 0, i;
- for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+ for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
ret = submit_audio_in_urb(line6pcm);
if (ret < 0)
break;
@@ -90,7 +90,9 @@ void line6_capture_copy(struct snd_line6_pcm *line6pcm, char *fbuf, int fsize)
struct snd_pcm_substream *substream =
get_substream(line6pcm, SNDRV_PCM_STREAM_CAPTURE);
struct snd_pcm_runtime *runtime = substream->runtime;
- const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
+ const int bytes_per_frame =
+ line6pcm->properties->bytes_per_channel *
+ line6pcm->properties->capture_hw.channels_max;
int frames = fsize / bytes_per_frame;
if (runtime == NULL)
@@ -154,7 +156,7 @@ static void audio_in_callback(struct urb *urb)
line6pcm->in.last_frame = urb->start_frame;
/* find index of URB */
- for (index = 0; index < LINE6_ISO_BUFFERS; ++index)
+ for (index = 0; index < line6pcm->line6->iso_buffers; ++index)
if (urb == line6pcm->in.urbs[index])
break;
@@ -173,17 +175,27 @@ static void audio_in_callback(struct urb *urb)
fbuf = urb->transfer_buffer + fin->offset;
fsize = fin->actual_length;
- if (fsize > line6pcm->max_packet_size) {
+ if (fsize > line6pcm->max_packet_size_in) {
dev_err(line6pcm->line6->ifcdev,
"driver and/or device bug: packet too large (%d > %d)\n",
- fsize, line6pcm->max_packet_size);
+ fsize, line6pcm->max_packet_size_in);
}
length += fsize;
- /* the following assumes LINE6_ISO_PACKETS == 1: */
+ BUILD_BUG_ON_MSG(LINE6_ISO_PACKETS != 1,
+ "The following code assumes LINE6_ISO_PACKETS == 1");
+ /* TODO:
+ * Also, if iso_buffers != 2, the prev frame is almost random at
+ * playback side.
+ * This needs to be redesigned. It should be "stable", but we may
+ * experience sync problems on such high-speed configs.
+ */
+
line6pcm->prev_fbuf = fbuf;
- line6pcm->prev_fsize = fsize;
+ line6pcm->prev_fsize = fsize /
+ (line6pcm->properties->bytes_per_channel *
+ line6pcm->properties->capture_hw.channels_max);
if (!test_bit(LINE6_STREAM_IMPULSE, &line6pcm->in.running) &&
test_bit(LINE6_STREAM_PCM, &line6pcm->in.running) &&
@@ -220,6 +232,8 @@ static int snd_line6_capture_open(struct snd_pcm_substream *substream)
if (err < 0)
return err;
+ line6_pcm_acquire(line6pcm, LINE6_STREAM_CAPTURE_HELPER, false);
+
runtime->hw = line6pcm->properties->capture_hw;
return 0;
}
@@ -227,6 +241,9 @@ static int snd_line6_capture_open(struct snd_pcm_substream *substream)
/* close capture callback */
static int snd_line6_capture_close(struct snd_pcm_substream *substream)
{
+ struct snd_line6_pcm *line6pcm = snd_pcm_substream_chip(substream);
+
+ line6_pcm_release(line6pcm, LINE6_STREAM_CAPTURE_HELPER);
return 0;
}
@@ -247,8 +264,13 @@ int line6_create_audio_in_urbs(struct snd_line6_pcm *line6pcm)
struct usb_line6 *line6 = line6pcm->line6;
int i;
+ line6pcm->in.urbs = kzalloc(
+ sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL);
+ if (line6pcm->in.urbs == NULL)
+ return -ENOMEM;
+
/* create audio URBs and fill in constant values: */
- for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+ for (i = 0; i < line6->iso_buffers; ++i) {
struct urb *urb;
/* URB for audio in: */
diff --git a/sound/usb/line6/driver.c b/sound/usb/line6/driver.c
index 81b7da8e56d3..14e587e70655 100644
--- a/sound/usb/line6/driver.c
+++ b/sound/usb/line6/driver.c
@@ -17,6 +17,7 @@
#include <sound/core.h>
#include <sound/initval.h>
+#include <sound/hwdep.h>
#include "capture.h"
#include "driver.h"
@@ -29,7 +30,7 @@
/*
This is Line 6's MIDI manufacturer ID.
*/
-const unsigned char line6_midi_id[] = {
+const unsigned char line6_midi_id[3] = {
0x00, 0x01, 0x0c
};
EXPORT_SYMBOL_GPL(line6_midi_id);
@@ -66,10 +67,17 @@ static int line6_start_listen(struct usb_line6 *line6)
{
int err;
- usb_fill_int_urb(line6->urb_listen, line6->usbdev,
- usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r),
- line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
- line6_data_received, line6, line6->interval);
+ if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+ usb_fill_int_urb(line6->urb_listen, line6->usbdev,
+ usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r),
+ line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
+ line6_data_received, line6, line6->interval);
+ } else {
+ usb_fill_bulk_urb(line6->urb_listen, line6->usbdev,
+ usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r),
+ line6->buffer_listen, LINE6_BUFSIZE_LISTEN,
+ line6_data_received, line6);
+ }
line6->urb_listen->actual_length = 0;
err = usb_submit_urb(line6->urb_listen, GFP_ATOMIC);
return err;
@@ -90,6 +98,7 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
int size)
{
int i, done = 0;
+ const struct line6_properties *properties = line6->properties;
for (i = 0; i < size; i += line6->max_packet_size) {
int partial;
@@ -97,15 +106,21 @@ static int line6_send_raw_message(struct usb_line6 *line6, const char *buffer,
int frag_size = min(line6->max_packet_size, size - i);
int retval;
- retval = usb_interrupt_msg(line6->usbdev,
- usb_sndintpipe(line6->usbdev,
- line6->properties->ep_ctrl_w),
- (char *)frag_buf, frag_size,
- &partial, LINE6_TIMEOUT * HZ);
+ if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+ retval = usb_interrupt_msg(line6->usbdev,
+ usb_sndintpipe(line6->usbdev, properties->ep_ctrl_w),
+ (char *)frag_buf, frag_size,
+ &partial, LINE6_TIMEOUT * HZ);
+ } else {
+ retval = usb_bulk_msg(line6->usbdev,
+ usb_sndbulkpipe(line6->usbdev, properties->ep_ctrl_w),
+ (char *)frag_buf, frag_size,
+ &partial, LINE6_TIMEOUT * HZ);
+ }
if (retval) {
dev_err(line6->ifcdev,
- "usb_interrupt_msg failed (%d)\n", retval);
+ "usb_bulk_msg failed (%d)\n", retval);
break;
}
@@ -140,10 +155,17 @@ static int line6_send_raw_message_async_part(struct message *msg,
int done = msg->done;
int bytes = min(msg->size - done, line6->max_packet_size);
- usb_fill_int_urb(urb, line6->usbdev,
- usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w),
- (char *)msg->buffer + done, bytes,
- line6_async_request_sent, msg, line6->interval);
+ if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+ usb_fill_int_urb(urb, line6->usbdev,
+ usb_sndintpipe(line6->usbdev, line6->properties->ep_ctrl_w),
+ (char *)msg->buffer + done, bytes,
+ line6_async_request_sent, msg, line6->interval);
+ } else {
+ usb_fill_bulk_urb(urb, line6->usbdev,
+ usb_sndbulkpipe(line6->usbdev, line6->properties->ep_ctrl_w),
+ (char *)msg->buffer + done, bytes,
+ line6_async_request_sent, msg);
+ }
msg->done += bytes;
retval = usb_submit_urb(urb, GFP_ATOMIC);
@@ -269,28 +291,36 @@ static void line6_data_received(struct urb *urb)
if (urb->status == -ESHUTDOWN)
return;
- done =
- line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
+ if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+ done =
+ line6_midibuf_write(mb, urb->transfer_buffer, urb->actual_length);
- if (done < urb->actual_length) {
- line6_midibuf_ignore(mb, done);
- dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
- done, urb->actual_length);
- }
+ if (done < urb->actual_length) {
+ line6_midibuf_ignore(mb, done);
+ dev_dbg(line6->ifcdev, "%d %d buffer overflow - message skipped\n",
+ done, urb->actual_length);
+ }
- for (;;) {
- done =
- line6_midibuf_read(mb, line6->buffer_message,
- LINE6_MESSAGE_MAXLEN);
+ for (;;) {
+ done =
+ line6_midibuf_read(mb, line6->buffer_message,
+ LINE6_MIDI_MESSAGE_MAXLEN);
- if (done == 0)
- break;
+ if (done == 0)
+ break;
- line6->message_length = done;
- line6_midi_receive(line6, line6->buffer_message, done);
+ line6->message_length = done;
+ line6_midi_receive(line6, line6->buffer_message, done);
+ if (line6->process_message)
+ line6->process_message(line6);
+ }
+ } else {
+ line6->buffer_message = urb->transfer_buffer;
+ line6->message_length = urb->actual_length;
if (line6->process_message)
line6->process_message(line6);
+ line6->buffer_message = NULL;
}
line6_start_listen(line6);
@@ -447,12 +477,16 @@ static void line6_destruct(struct snd_card *card)
struct usb_line6 *line6 = card->private_data;
struct usb_device *usbdev = line6->usbdev;
- /* free buffer memory first: */
+ /* Free buffer memory first. We cannot depend on the existence of private
+ * data from the (podhd) module, it may be gone already during this call
+ */
kfree(line6->buffer_message);
+
kfree(line6->buffer_listen);
/* then free URBs: */
usb_free_urb(line6->urb_listen);
+ line6->urb_listen = NULL;
/* decrement reference counters: */
usb_put_dev(usbdev);
@@ -462,13 +496,29 @@ static void line6_destruct(struct snd_card *card)
static void line6_get_interval(struct usb_line6 *line6)
{
struct usb_device *usbdev = line6->usbdev;
+ const struct line6_properties *properties = line6->properties;
+ int pipe;
struct usb_host_endpoint *ep;
- unsigned pipe = usb_rcvintpipe(usbdev, line6->properties->ep_ctrl_r);
- unsigned epnum = usb_pipeendpoint(pipe);
- ep = usbdev->ep_in[epnum];
+ if (properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+ pipe =
+ usb_rcvintpipe(line6->usbdev, line6->properties->ep_ctrl_r);
+ } else {
+ pipe =
+ usb_rcvbulkpipe(line6->usbdev, line6->properties->ep_ctrl_r);
+ }
+ ep = usbdev->ep_in[usb_pipeendpoint(pipe)];
+
if (ep) {
line6->interval = ep->desc.bInterval;
+ if (usbdev->speed == USB_SPEED_LOW) {
+ line6->intervals_per_second = USB_LOW_INTERVALS_PER_SECOND;
+ line6->iso_buffers = USB_LOW_ISO_BUFFERS;
+ } else {
+ line6->intervals_per_second = USB_HIGH_INTERVALS_PER_SECOND;
+ line6->iso_buffers = USB_HIGH_ISO_BUFFERS;
+ }
+
line6->max_packet_size = le16_to_cpu(ep->desc.wMaxPacketSize);
} else {
dev_err(line6->ifcdev,
@@ -478,6 +528,138 @@ static void line6_get_interval(struct usb_line6 *line6)
}
}
+
+/* Enable buffering of incoming messages, flush the buffer */
+static int line6_hwdep_open(struct snd_hwdep *hw, struct file *file)
+{
+ struct usb_line6 *line6 = hw->private_data;
+
+ /* NOTE: hwdep layer provides atomicity here */
+
+ line6->messages.active = 1;
+
+ return 0;
+}
+
+/* Stop buffering */
+static int line6_hwdep_release(struct snd_hwdep *hw, struct file *file)
+{
+ struct usb_line6 *line6 = hw->private_data;
+
+ line6->messages.active = 0;
+
+ return 0;
+}
+
+/* Read from circular buffer, return to user */
+static long
+line6_hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct usb_line6 *line6 = hwdep->private_data;
+ long rv = 0;
+ unsigned int out_count;
+
+ if (mutex_lock_interruptible(&line6->messages.read_lock))
+ return -ERESTARTSYS;
+
+ while (kfifo_len(&line6->messages.fifo) == 0) {
+ mutex_unlock(&line6->messages.read_lock);
+
+ rv = wait_event_interruptible(
+ line6->messages.wait_queue,
+ kfifo_len(&line6->messages.fifo) != 0);
+ if (rv < 0)
+ return rv;
+
+ if (mutex_lock_interruptible(&line6->messages.read_lock))
+ return -ERESTARTSYS;
+ }
+
+ if (kfifo_peek_len(&line6->messages.fifo) > count) {
+ /* Buffer too small; allow re-read of the current item... */
+ rv = -EINVAL;
+ } else {
+ rv = kfifo_to_user(&line6->messages.fifo, buf, count, &out_count);
+ if (rv == 0)
+ rv = out_count;
+ }
+
+ mutex_unlock(&line6->messages.read_lock);
+ return rv;
+}
+
+/* Write directly (no buffering) to device by user*/
+static long
+line6_hwdep_write(struct snd_hwdep *hwdep, const char __user *data, long count,
+ loff_t *offset)
+{
+ struct usb_line6 *line6 = hwdep->private_data;
+ int rv;
+ char *data_copy;
+
+ if (count > line6->max_packet_size * LINE6_RAW_MESSAGES_MAXCOUNT) {
+ /* This is an arbitrary limit - still better than nothing... */
+ return -EINVAL;
+ }
+
+ data_copy = memdup_user(data, count);
+ if (IS_ERR(ERR_PTR))
+ return -ENOMEM;
+
+ rv = line6_send_raw_message(line6, data_copy, count);
+
+ kfree(data_copy);
+ return rv;
+}
+
+static const struct snd_hwdep_ops hwdep_ops = {
+ .open = line6_hwdep_open,
+ .release = line6_hwdep_release,
+ .read = line6_hwdep_read,
+ .write = line6_hwdep_write,
+};
+
+/* Insert into circular buffer */
+static void line6_hwdep_push_message(struct usb_line6 *line6)
+{
+ if (!line6->messages.active)
+ return;
+
+ if (kfifo_avail(&line6->messages.fifo) >= line6->message_length) {
+ /* No race condition here, there's only one writer */
+ kfifo_in(&line6->messages.fifo,
+ line6->buffer_message, line6->message_length);
+ } /* else TODO: signal overflow */
+
+ wake_up_interruptible(&line6->messages.wait_queue);
+}
+
+static int line6_hwdep_init(struct usb_line6 *line6)
+{
+ int err;
+ struct snd_hwdep *hwdep;
+
+ /* TODO: usb_driver_claim_interface(); */
+ line6->process_message = line6_hwdep_push_message;
+ line6->messages.active = 0;
+ init_waitqueue_head(&line6->messages.wait_queue);
+ mutex_init(&line6->messages.read_lock);
+ INIT_KFIFO(line6->messages.fifo);
+
+ err = snd_hwdep_new(line6->card, "config", 0, &hwdep);
+ if (err < 0)
+ goto end;
+ strcpy(hwdep->name, "config");
+ hwdep->iface = SNDRV_HWDEP_IFACE_LINE6;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = line6;
+ hwdep->exclusive = true;
+
+end:
+ return err;
+}
+
static int line6_init_cap_control(struct usb_line6 *line6)
{
int ret;
@@ -487,14 +669,20 @@ static int line6_init_cap_control(struct usb_line6 *line6)
if (!line6->buffer_listen)
return -ENOMEM;
- line6->buffer_message = kmalloc(LINE6_MESSAGE_MAXLEN, GFP_KERNEL);
- if (!line6->buffer_message)
- return -ENOMEM;
-
line6->urb_listen = usb_alloc_urb(0, GFP_KERNEL);
if (!line6->urb_listen)
return -ENOMEM;
+ if (line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI) {
+ line6->buffer_message = kmalloc(LINE6_MIDI_MESSAGE_MAXLEN, GFP_KERNEL);
+ if (!line6->buffer_message)
+ return -ENOMEM;
+ } else {
+ ret = line6_hwdep_init(line6);
+ if (ret < 0)
+ return ret;
+ }
+
ret = line6_start_listen(line6);
if (ret < 0) {
dev_err(line6->ifcdev, "cannot start listening: %d\n", ret);
@@ -558,6 +746,7 @@ int line6_probe(struct usb_interface *interface,
/* query interface number */
interface_number = interface->cur_altsetting->desc.bInterfaceNumber;
+ /* TODO reserves the bus bandwidth even without actual transfer */
ret = usb_set_interface(usbdev, interface_number,
properties->altsetting);
if (ret < 0) {
@@ -565,9 +754,8 @@ int line6_probe(struct usb_interface *interface,
goto error;
}
- line6_get_interval(line6);
-
if (properties->capabilities & LINE6_CAP_CONTROL) {
+ line6_get_interval(line6);
ret = line6_init_cap_control(line6);
if (ret < 0)
goto error;
@@ -670,3 +858,4 @@ EXPORT_SYMBOL_GPL(line6_resume);
MODULE_AUTHOR(DRIVER_AUTHOR);
MODULE_DESCRIPTION(DRIVER_DESC);
MODULE_LICENSE("GPL");
+
diff --git a/sound/usb/line6/driver.h b/sound/usb/line6/driver.h
index 7da643e79e3b..7e3a3aada222 100644
--- a/sound/usb/line6/driver.h
+++ b/sound/usb/line6/driver.h
@@ -12,21 +12,37 @@
#ifndef DRIVER_H
#define DRIVER_H
-#include <linux/spinlock.h>
#include <linux/usb.h>
+#include <linux/mutex.h>
+#include <linux/kfifo.h>
#include <sound/core.h>
#include "midi.h"
-#define USB_INTERVALS_PER_SECOND 1000
+/* USB 1.1 speed configuration */
+#define USB_LOW_INTERVALS_PER_SECOND 1000
+#define USB_LOW_ISO_BUFFERS 2
+
+/* USB 2.0+ speed configuration */
+#define USB_HIGH_INTERVALS_PER_SECOND 8000
+#define USB_HIGH_ISO_BUFFERS 16
/* Fallback USB interval and max packet size values */
#define LINE6_FALLBACK_INTERVAL 10
#define LINE6_FALLBACK_MAXPACKETSIZE 16
#define LINE6_TIMEOUT 1
-#define LINE6_BUFSIZE_LISTEN 32
-#define LINE6_MESSAGE_MAXLEN 256
+#define LINE6_BUFSIZE_LISTEN 64
+#define LINE6_MIDI_MESSAGE_MAXLEN 256
+
+#define LINE6_RAW_MESSAGES_MAXCOUNT_ORDER 7
+/* 4k packets are common, BUFSIZE * MAXCOUNT should be bigger... */
+#define LINE6_RAW_MESSAGES_MAXCOUNT (1 << LINE6_RAW_MESSAGES_MAXCOUNT_ORDER)
+
+
+#if LINE6_BUFSIZE_LISTEN > 65535
+#error "Use dynamic fifo instead"
+#endif
/*
Line 6 MIDI control commands
@@ -94,8 +110,12 @@ enum {
LINE6_CAP_CONTROL = 1 << 0,
/* device supports PCM input/output via USB */
LINE6_CAP_PCM = 1 << 1,
- /* device support hardware monitoring */
+ /* device supports hardware monitoring */
LINE6_CAP_HWMON = 1 << 2,
+ /* device requires output data when input is read */
+ LINE6_CAP_IN_NEEDS_OUT = 1 << 3,
+ /* device uses raw MIDI via USB (data endpoints) */
+ LINE6_CAP_CONTROL_MIDI = 1 << 4,
};
/*
@@ -109,10 +129,15 @@ struct usb_line6 {
/* Properties */
const struct line6_properties *properties;
- /* Interval (ms) */
+ /* Interval for data USB packets */
int interval;
+ /* ...for isochronous transfers framing */
+ int intervals_per_second;
+
+ /* Number of isochronous URBs used for frame transfers */
+ int iso_buffers;
- /* Maximum size of USB packet */
+ /* Maximum size of data USB packet */
int max_packet_size;
/* Device representing the USB interface */
@@ -129,18 +154,30 @@ struct usb_line6 {
/* Line 6 MIDI device data structure */
struct snd_line6_midi *line6midi;
- /* URB for listening to PODxt Pro control endpoint */
+ /* URB for listening to POD data endpoint */
struct urb *urb_listen;
- /* Buffer for listening to PODxt Pro control endpoint */
+ /* Buffer for incoming data from POD data endpoint */
unsigned char *buffer_listen;
- /* Buffer for message to be processed */
+ /* Buffer for message to be processed, generated from MIDI layer */
unsigned char *buffer_message;
- /* Length of message to be processed */
+ /* Length of message to be processed, generated from MIDI layer */
int message_length;
+ /* Circular buffer for non-MIDI control messages */
+ struct {
+ struct mutex read_lock;
+ wait_queue_head_t wait_queue;
+ unsigned int active:1;
+ STRUCT_KFIFO_REC_2(LINE6_BUFSIZE_LISTEN * LINE6_RAW_MESSAGES_MAXCOUNT)
+ fifo;
+ } messages;
+
+ /* If MIDI is supported, buffer_message contains the pre-processed data;
+ * otherwise the data is only in urb_listen (buffer_incoming).
+ */
void (*process_message)(struct usb_line6 *);
void (*disconnect)(struct usb_line6 *line6);
};
diff --git a/sound/usb/line6/midi.c b/sound/usb/line6/midi.c
index cebea9b7f769..d0fb2f205bd9 100644
--- a/sound/usb/line6/midi.c
+++ b/sound/usb/line6/midi.c
@@ -258,7 +258,7 @@ int line6_init_midi(struct usb_line6 *line6)
struct snd_rawmidi *rmidi;
struct snd_line6_midi *line6midi;
- if (!(line6->properties->capabilities & LINE6_CAP_CONTROL)) {
+ if (!(line6->properties->capabilities & LINE6_CAP_CONTROL_MIDI)) {
/* skip MIDI initialization and report success */
return 0;
}
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index 204cc074adb9..fab53f58d447 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -52,10 +52,9 @@ static int snd_line6_impulse_volume_put(struct snd_kcontrol *kcontrol,
line6pcm->impulse_volume = value;
if (value > 0) {
- err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE);
+ err = line6_pcm_acquire(line6pcm, LINE6_STREAM_IMPULSE, true);
if (err < 0) {
line6pcm->impulse_volume = 0;
- line6_pcm_release(line6pcm, LINE6_STREAM_IMPULSE);
return err;
}
} else {
@@ -105,7 +104,7 @@ static void line6_unlink_audio_urbs(struct snd_line6_pcm *line6pcm,
{
int i;
- for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
+ for (i = 0; i < line6pcm->line6->iso_buffers; i++) {
if (test_bit(i, &pcms->active_urbs)) {
if (!test_and_set_bit(i, &pcms->unlink_urbs))
usb_unlink_urb(pcms->urbs[i]);
@@ -125,7 +124,7 @@ static void line6_wait_clear_audio_urbs(struct snd_line6_pcm *line6pcm,
do {
alive = 0;
- for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
+ for (i = 0; i < line6pcm->line6->iso_buffers; i++) {
if (test_bit(i, &pcms->active_urbs))
alive++;
}
@@ -147,15 +146,20 @@ get_stream(struct snd_line6_pcm *line6pcm, int direction)
}
/* allocate a buffer if not opened yet;
- * call this in line6pcm.state_change mutex
+ * call this in line6pcm.state_mutex
*/
static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
- struct line6_pcm_stream *pstr, int type)
+ struct line6_pcm_stream *pstr, int direction, int type)
{
+ const int pkt_size =
+ (direction == SNDRV_PCM_STREAM_PLAYBACK) ?
+ line6pcm->max_packet_size_out :
+ line6pcm->max_packet_size_in;
+
/* Invoked multiple times in a row so allocate once only */
if (!test_and_set_bit(type, &pstr->opened) && !pstr->buffer) {
- pstr->buffer = kmalloc(LINE6_ISO_BUFFERS * LINE6_ISO_PACKETS *
- line6pcm->max_packet_size, GFP_KERNEL);
+ pstr->buffer = kmalloc(line6pcm->line6->iso_buffers *
+ LINE6_ISO_PACKETS * pkt_size, GFP_KERNEL);
if (!pstr->buffer)
return -ENOMEM;
}
@@ -163,12 +167,11 @@ static int line6_buffer_acquire(struct snd_line6_pcm *line6pcm,
}
/* free a buffer if all streams are closed;
- * call this in line6pcm.state_change mutex
+ * call this in line6pcm.state_mutex
*/
static void line6_buffer_release(struct snd_line6_pcm *line6pcm,
struct line6_pcm_stream *pstr, int type)
{
-
clear_bit(type, &pstr->opened);
if (!pstr->opened) {
line6_wait_clear_audio_urbs(line6pcm, pstr);
@@ -195,6 +198,7 @@ static int line6_stream_start(struct snd_line6_pcm *line6pcm, int direction,
else
ret = line6_submit_audio_in_all_urbs(line6pcm);
}
+
if (ret < 0)
clear_bit(type, &pstr->running);
spin_unlock_irqrestore(&pstr->lock, flags);
@@ -211,7 +215,9 @@ static void line6_stream_stop(struct snd_line6_pcm *line6pcm, int direction,
spin_lock_irqsave(&pstr->lock, flags);
clear_bit(type, &pstr->running);
if (!pstr->running) {
+ spin_unlock_irqrestore(&pstr->lock, flags);
line6_unlink_audio_urbs(line6pcm, pstr);
+ spin_lock_irqsave(&pstr->lock, flags);
if (direction == SNDRV_PCM_STREAM_CAPTURE) {
line6pcm->prev_fbuf = NULL;
line6pcm->prev_fsize = 0;
@@ -236,6 +242,14 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
switch (cmd) {
case SNDRV_PCM_TRIGGER_START:
case SNDRV_PCM_TRIGGER_RESUME:
+ if (s->stream == SNDRV_PCM_STREAM_CAPTURE &&
+ (line6pcm->line6->properties->capabilities &
+ LINE6_CAP_IN_NEEDS_OUT)) {
+ err = line6_stream_start(line6pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ LINE6_STREAM_CAPTURE_HELPER);
+ if (err < 0)
+ return err;
+ }
err = line6_stream_start(line6pcm, s->stream,
LINE6_STREAM_PCM);
if (err < 0)
@@ -244,6 +258,12 @@ int snd_line6_trigger(struct snd_pcm_substream *substream, int cmd)
case SNDRV_PCM_TRIGGER_STOP:
case SNDRV_PCM_TRIGGER_SUSPEND:
+ if (s->stream == SNDRV_PCM_STREAM_CAPTURE &&
+ (line6pcm->line6->properties->capabilities &
+ LINE6_CAP_IN_NEEDS_OUT)) {
+ line6_stream_stop(line6pcm, SNDRV_PCM_STREAM_PLAYBACK,
+ LINE6_STREAM_CAPTURE_HELPER);
+ }
line6_stream_stop(line6pcm, s->stream,
LINE6_STREAM_PCM);
break;
@@ -277,27 +297,30 @@ snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream)
return pstr->pos_done;
}
-/* Acquire and start duplex streams:
+/* Acquire and optionally start duplex streams:
* type is either LINE6_STREAM_IMPULSE or LINE6_STREAM_MONITOR
*/
-int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type)
+int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type, bool start)
{
struct line6_pcm_stream *pstr;
int ret = 0, dir;
+ /* TODO: We should assert SNDRV_PCM_STREAM_PLAYBACK/CAPTURE == 0/1 */
mutex_lock(&line6pcm->state_mutex);
for (dir = 0; dir < 2; dir++) {
pstr = get_stream(line6pcm, dir);
- ret = line6_buffer_acquire(line6pcm, pstr, type);
+ ret = line6_buffer_acquire(line6pcm, pstr, dir, type);
if (ret < 0)
goto error;
if (!pstr->running)
line6_wait_clear_audio_urbs(line6pcm, pstr);
}
- for (dir = 0; dir < 2; dir++) {
- ret = line6_stream_start(line6pcm, dir, type);
- if (ret < 0)
- goto error;
+ if (start) {
+ for (dir = 0; dir < 2; dir++) {
+ ret = line6_stream_start(line6pcm, dir, type);
+ if (ret < 0)
+ goto error;
+ }
}
error:
mutex_unlock(&line6pcm->state_mutex);
@@ -333,7 +356,8 @@ int snd_line6_hw_params(struct snd_pcm_substream *substream,
struct line6_pcm_stream *pstr = get_stream(line6pcm, substream->stream);
mutex_lock(&line6pcm->state_mutex);
- ret = line6_buffer_acquire(line6pcm, pstr, LINE6_STREAM_PCM);
+ ret = line6_buffer_acquire(line6pcm, pstr, substream->stream,
+ LINE6_STREAM_PCM);
if (ret < 0)
goto error;
@@ -433,24 +457,30 @@ static struct snd_kcontrol_new line6_controls[] = {
/*
Cleanup the PCM device.
*/
-static void cleanup_urbs(struct line6_pcm_stream *pcms)
+static void cleanup_urbs(struct line6_pcm_stream *pcms, int iso_buffers)
{
int i;
- for (i = 0; i < LINE6_ISO_BUFFERS; i++) {
+ /* Most likely impossible in current code... */
+ if (pcms->urbs == NULL)
+ return;
+
+ for (i = 0; i < iso_buffers; i++) {
if (pcms->urbs[i]) {
usb_kill_urb(pcms->urbs[i]);
usb_free_urb(pcms->urbs[i]);
}
}
+ kfree(pcms->urbs);
+ pcms->urbs = NULL;
}
static void line6_cleanup_pcm(struct snd_pcm *pcm)
{
struct snd_line6_pcm *line6pcm = snd_pcm_chip(pcm);
- cleanup_urbs(&line6pcm->out);
- cleanup_urbs(&line6pcm->in);
+ cleanup_urbs(&line6pcm->out, line6pcm->line6->iso_buffers);
+ cleanup_urbs(&line6pcm->in, line6pcm->line6->iso_buffers);
kfree(line6pcm);
}
@@ -522,12 +552,12 @@ int line6_init_pcm(struct usb_line6 *line6,
line6pcm->volume_monitor = 255;
line6pcm->line6 = line6;
- /* Read and write buffers are sized identically, so choose minimum */
- line6pcm->max_packet_size = min(
- usb_maxpacket(line6->usbdev,
- usb_rcvisocpipe(line6->usbdev, ep_read), 0),
- usb_maxpacket(line6->usbdev,
- usb_sndisocpipe(line6->usbdev, ep_write), 1));
+ line6pcm->max_packet_size_in =
+ usb_maxpacket(line6->usbdev,
+ usb_rcvisocpipe(line6->usbdev, ep_read), 0);
+ line6pcm->max_packet_size_out =
+ usb_maxpacket(line6->usbdev,
+ usb_sndisocpipe(line6->usbdev, ep_write), 1);
spin_lock_init(&line6pcm->out.lock);
spin_lock_init(&line6pcm->in.lock);
diff --git a/sound/usb/line6/pcm.h b/sound/usb/line6/pcm.h
index 508410adbd51..bb0c9cbf2a78 100644
--- a/sound/usb/line6/pcm.h
+++ b/sound/usb/line6/pcm.h
@@ -20,9 +20,6 @@
#include "driver.h"
-/* number of URBs */
-#define LINE6_ISO_BUFFERS 2
-
/*
number of USB frames per URB
The Line 6 Windows driver always transmits two frames per packet, but
@@ -31,7 +28,9 @@
*/
#define LINE6_ISO_PACKETS 1
-/* in a "full speed" device (such as the PODxt Pro) this means 1ms */
+/* in a "full speed" device (such as the PODxt Pro) this means 1ms,
+ * for "high speed" it's 1/8ms
+ */
#define LINE6_ISO_INTERVAL 1
#define LINE6_IMPULSE_DEFAULT_PERIOD 100
@@ -74,6 +73,7 @@ enum {
LINE6_STREAM_PCM,
LINE6_STREAM_MONITOR,
LINE6_STREAM_IMPULSE,
+ LINE6_STREAM_CAPTURE_HELPER,
};
/* misc bit flags for PCM operation */
@@ -85,12 +85,12 @@ enum {
struct line6_pcm_properties {
struct snd_pcm_hardware playback_hw, capture_hw;
struct snd_pcm_hw_constraint_ratdens rates;
- int bytes_per_frame;
+ int bytes_per_channel;
};
struct line6_pcm_stream {
/* allocated URBs */
- struct urb *urbs[LINE6_ISO_BUFFERS];
+ struct urb **urbs;
/* Temporary buffer;
* Since the packet size is not known in advance, this buffer is
@@ -157,11 +157,12 @@ struct snd_line6_pcm {
/* Previously captured frame (for software monitoring) */
unsigned char *prev_fbuf;
- /* Size of previously captured frame (for software monitoring) */
+ /* Size of previously captured frame (for software monitoring/sync) */
int prev_fsize;
/* Maximum size of USB packet */
- int max_packet_size;
+ int max_packet_size_in;
+ int max_packet_size_out;
/* PCM playback volume (left and right) */
int volume_playback[2];
@@ -191,7 +192,8 @@ extern int snd_line6_hw_params(struct snd_pcm_substream *substream,
extern int snd_line6_hw_free(struct snd_pcm_substream *substream);
extern snd_pcm_uframes_t snd_line6_pointer(struct snd_pcm_substream *substream);
extern void line6_pcm_disconnect(struct snd_line6_pcm *line6pcm);
-extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type);
+extern int line6_pcm_acquire(struct snd_line6_pcm *line6pcm, int type,
+ bool start);
extern void line6_pcm_release(struct snd_line6_pcm *line6pcm, int type);
#endif
diff --git a/sound/usb/line6/playback.c b/sound/usb/line6/playback.c
index 97ed593f6010..812d18191e01 100644
--- a/sound/usb/line6/playback.c
+++ b/sound/usb/line6/playback.c
@@ -146,18 +146,20 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
int index;
int i, urb_size, urb_frames;
int ret;
- const int bytes_per_frame = line6pcm->properties->bytes_per_frame;
+ const int bytes_per_frame =
+ line6pcm->properties->bytes_per_channel *
+ line6pcm->properties->playback_hw.channels_max;
const int frame_increment =
line6pcm->properties->rates.rats[0].num_min;
const int frame_factor =
line6pcm->properties->rates.rats[0].den *
- (USB_INTERVALS_PER_SECOND / LINE6_ISO_INTERVAL);
+ (line6pcm->line6->intervals_per_second / LINE6_ISO_INTERVAL);
struct urb *urb_out;
- index =
- find_first_zero_bit(&line6pcm->out.active_urbs, LINE6_ISO_BUFFERS);
+ index = find_first_zero_bit(&line6pcm->out.active_urbs,
+ line6pcm->line6->iso_buffers);
- if (index < 0 || index >= LINE6_ISO_BUFFERS) {
+ if (index < 0 || index >= line6pcm->line6->iso_buffers) {
dev_err(line6pcm->line6->ifcdev, "no free URB found\n");
return -EINVAL;
}
@@ -165,6 +167,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
urb_out = line6pcm->out.urbs[index];
urb_size = 0;
+ /* TODO: this may not work for LINE6_ISO_PACKETS != 1 */
for (i = 0; i < LINE6_ISO_PACKETS; ++i) {
/* compute frame size for given sampling rate */
int fsize = 0;
@@ -178,9 +181,11 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
line6pcm->out.count += frame_increment;
n = line6pcm->out.count / frame_factor;
line6pcm->out.count -= n * frame_factor;
- fsize = n * bytes_per_frame;
+ fsize = n;
}
+ fsize *= bytes_per_frame;
+
fout->offset = urb_size;
fout->length = fsize;
urb_size += fsize;
@@ -195,7 +200,7 @@ static int submit_audio_out_urb(struct snd_line6_pcm *line6pcm)
urb_frames = urb_size / bytes_per_frame;
urb_out->transfer_buffer =
line6pcm->out.buffer +
- index * LINE6_ISO_PACKETS * line6pcm->max_packet_size;
+ index * LINE6_ISO_PACKETS * line6pcm->max_packet_size_out;
urb_out->transfer_buffer_length = urb_size;
urb_out->context = line6pcm;
@@ -286,7 +291,7 @@ int line6_submit_audio_out_all_urbs(struct snd_line6_pcm *line6pcm)
{
int ret = 0, i;
- for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+ for (i = 0; i < line6pcm->line6->iso_buffers; ++i) {
ret = submit_audio_out_urb(line6pcm);
if (ret < 0)
break;
@@ -305,6 +310,9 @@ static void audio_out_callback(struct urb *urb)
struct snd_line6_pcm *line6pcm = (struct snd_line6_pcm *)urb->context;
struct snd_pcm_substream *substream =
get_substream(line6pcm, SNDRV_PCM_STREAM_PLAYBACK);
+ const int bytes_per_frame =
+ line6pcm->properties->bytes_per_channel *
+ line6pcm->properties->playback_hw.channels_max;
#if USE_CLEAR_BUFFER_WORKAROUND
memset(urb->transfer_buffer, 0, urb->transfer_buffer_length);
@@ -313,11 +321,11 @@ static void audio_out_callback(struct urb *urb)
line6pcm->out.last_frame = urb->start_frame;
/* find index of URB */
- for (index = 0; index < LINE6_ISO_BUFFERS; index++)
+ for (index = 0; index < line6pcm->line6->iso_buffers; index++)
if (urb == line6pcm->out.urbs[index])
break;
- if (index >= LINE6_ISO_BUFFERS)
+ if (index >= line6pcm->line6->iso_buffers)
return; /* URB has been unlinked asynchronously */
for (i = 0; i < LINE6_ISO_PACKETS; i++)
@@ -329,7 +337,7 @@ static void audio_out_callback(struct urb *urb)
struct snd_pcm_runtime *runtime = substream->runtime;
line6pcm->out.pos_done +=
- length / line6pcm->properties->bytes_per_frame;
+ length / bytes_per_frame;
if (line6pcm->out.pos_done >= runtime->buffer_size)
line6pcm->out.pos_done -= runtime->buffer_size;
@@ -401,8 +409,13 @@ int line6_create_audio_out_urbs(struct snd_line6_pcm *line6pcm)
struct usb_line6 *line6 = line6pcm->line6;
int i;
+ line6pcm->out.urbs = kzalloc(
+ sizeof(struct urb *) * line6->iso_buffers, GFP_KERNEL);
+ if (line6pcm->out.urbs == NULL)
+ return -ENOMEM;
+
/* create audio URBs and fill in constant values: */
- for (i = 0; i < LINE6_ISO_BUFFERS; ++i) {
+ for (i = 0; i < line6->iso_buffers; ++i) {
struct urb *urb;
/* URB for audio out: */
diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c
index daf81d169a42..17aa616e61f5 100644
--- a/sound/usb/line6/pod.c
+++ b/sound/usb/line6/pod.c
@@ -83,7 +83,6 @@ struct usb_line6_pod {
};
#define POD_SYSEX_CODE 3
-#define POD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */
/* *INDENT-OFF* */
@@ -167,7 +166,7 @@ static struct line6_pcm_properties pod_pcm_properties = {
.rates = {
.nrats = 1,
.rats = &pod_ratden},
- .bytes_per_frame = POD_BYTES_PER_FRAME
+ .bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
};
static const char pod_version_header[] = {
@@ -244,8 +243,8 @@ static int pod_set_system_param_int(struct usb_line6_pod *pod, int value,
static ssize_t serial_number_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct usb_interface *interface = to_usb_interface(dev);
- struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ struct snd_card *card = dev_to_snd_card(dev);
+ struct usb_line6_pod *pod = card->private_data;
return sprintf(buf, "%u\n", pod->serial_number);
}
@@ -256,8 +255,8 @@ static ssize_t serial_number_show(struct device *dev,
static ssize_t firmware_version_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct usb_interface *interface = to_usb_interface(dev);
- struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ struct snd_card *card = dev_to_snd_card(dev);
+ struct usb_line6_pod *pod = card->private_data;
return sprintf(buf, "%d.%02d\n", pod->firmware_version / 100,
pod->firmware_version % 100);
@@ -269,8 +268,8 @@ static ssize_t firmware_version_show(struct device *dev,
static ssize_t device_id_show(struct device *dev,
struct device_attribute *attr, char *buf)
{
- struct usb_interface *interface = to_usb_interface(dev);
- struct usb_line6_pod *pod = usb_get_intfdata(interface);
+ struct snd_card *card = dev_to_snd_card(dev);
+ struct usb_line6_pod *pod = card->private_data;
return sprintf(buf, "%d\n", pod->device_id);
}
@@ -476,6 +475,7 @@ static const struct line6_properties pod_properties_table[] = {
.id = "BassPODxt",
.name = "BassPODxt",
.capabilities = LINE6_CAP_CONTROL
+ | LINE6_CAP_CONTROL_MIDI
| LINE6_CAP_PCM
| LINE6_CAP_HWMON,
.altsetting = 5,
@@ -488,6 +488,7 @@ static const struct line6_properties pod_properties_table[] = {
.id = "BassPODxtLive",
.name = "BassPODxt Live",
.capabilities = LINE6_CAP_CONTROL
+ | LINE6_CAP_CONTROL_MIDI
| LINE6_CAP_PCM
| LINE6_CAP_HWMON,
.altsetting = 1,
@@ -500,6 +501,7 @@ static const struct line6_properties pod_properties_table[] = {
.id = "BassPODxtPro",
.name = "BassPODxt Pro",
.capabilities = LINE6_CAP_CONTROL
+ | LINE6_CAP_CONTROL_MIDI
| LINE6_CAP_PCM
| LINE6_CAP_HWMON,
.altsetting = 5,
@@ -511,7 +513,8 @@ static const struct line6_properties pod_properties_table[] = {
[LINE6_POCKETPOD] = {
.id = "PocketPOD",
.name = "Pocket POD",
- .capabilities = LINE6_CAP_CONTROL,
+ .capabilities = LINE6_CAP_CONTROL
+ | LINE6_CAP_CONTROL_MIDI,
.altsetting = 0,
.ep_ctrl_r = 0x82,
.ep_ctrl_w = 0x02,
@@ -521,6 +524,7 @@ static const struct line6_properties pod_properties_table[] = {
.id = "PODxt",
.name = "PODxt",
.capabilities = LINE6_CAP_CONTROL
+ | LINE6_CAP_CONTROL_MIDI
| LINE6_CAP_PCM
| LINE6_CAP_HWMON,
.altsetting = 5,
@@ -533,6 +537,7 @@ static const struct line6_properties pod_properties_table[] = {
.id = "PODxtLive",
.name = "PODxt Live",
.capabilities = LINE6_CAP_CONTROL
+ | LINE6_CAP_CONTROL_MIDI
| LINE6_CAP_PCM
| LINE6_CAP_HWMON,
.altsetting = 1,
@@ -545,6 +550,7 @@ static const struct line6_properties pod_properties_table[] = {
.id = "PODxtPro",
.name = "PODxt Pro",
.capabilities = LINE6_CAP_CONTROL
+ | LINE6_CAP_CONTROL_MIDI
| LINE6_CAP_PCM
| LINE6_CAP_HWMON,
.altsetting = 5,
diff --git a/sound/usb/line6/podhd.c b/sound/usb/line6/podhd.c
index 63dcaef41ac3..9352a44ae6e4 100644
--- a/sound/usb/line6/podhd.c
+++ b/sound/usb/line6/podhd.c
@@ -2,6 +2,7 @@
* Line 6 Pod HD
*
* Copyright (C) 2011 Stefan Hajnoczi <stefanha@gmail.com>
+ * Copyright (C) 2015 Andrej Krutak <dev@andree.sk>
*
* This program is free software; you can redistribute it and/or
* modify it under the terms of the GNU General Public License as
@@ -18,14 +19,46 @@
#include "driver.h"
#include "pcm.h"
+#define PODHD_STARTUP_DELAY 500
+
+/*
+ * Stages of POD startup procedure
+ */
+enum {
+ PODHD_STARTUP_INIT = 1,
+ PODHD_STARTUP_SCHEDULE_WORKQUEUE,
+ PODHD_STARTUP_SETUP,
+ PODHD_STARTUP_LAST = PODHD_STARTUP_SETUP - 1
+};
+
enum {
LINE6_PODHD300,
LINE6_PODHD400,
LINE6_PODHD500_0,
LINE6_PODHD500_1,
+ LINE6_PODX3,
+ LINE6_PODX3LIVE
};
-#define PODHD_BYTES_PER_FRAME 6 /* 24bit audio (stereo) */
+struct usb_line6_podhd {
+ /* Generic Line 6 USB data */
+ struct usb_line6 line6;
+
+ /* Timer for device initialization */
+ struct timer_list startup_timer;
+
+ /* Work handler for device initialization */
+ struct work_struct startup_work;
+
+ /* Current progress in startup procedure */
+ int startup_progress;
+
+ /* Serial number of device */
+ u32 serial_number;
+
+ /* Firmware version */
+ int firmware_version;
+};
static struct snd_ratden podhd_ratden = {
.num_min = 48000,
@@ -73,29 +106,233 @@ static struct line6_pcm_properties podhd_pcm_properties = {
.rates = {
.nrats = 1,
.rats = &podhd_ratden},
- .bytes_per_frame = PODHD_BYTES_PER_FRAME
+ .bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
+};
+
+static struct line6_pcm_properties podx3_pcm_properties = {
+ .playback_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_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ .channels_min = 2,
+ .channels_max = 2,
+ .buffer_bytes_max = 60000,
+ .period_bytes_min = 64,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 1024},
+ .capture_hw = {
+ .info = (SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_SYNC_START),
+ .formats = SNDRV_PCM_FMTBIT_S24_3LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ .rate_min = 48000,
+ .rate_max = 48000,
+ /* 1+2: Main signal (out), 3+4: Tone 1,
+ * 5+6: Tone 2, 7+8: raw
+ */
+ .channels_min = 8,
+ .channels_max = 8,
+ .buffer_bytes_max = 60000,
+ .period_bytes_min = 64,
+ .period_bytes_max = 8192,
+ .periods_min = 1,
+ .periods_max = 1024},
+ .rates = {
+ .nrats = 1,
+ .rats = &podhd_ratden},
+ .bytes_per_channel = 3 /* SNDRV_PCM_FMTBIT_S24_3LE */
+};
+
+static void podhd_startup_start_workqueue(unsigned long data);
+static void podhd_startup_workqueue(struct work_struct *work);
+static int podhd_startup_finalize(struct usb_line6_podhd *pod);
+
+static ssize_t serial_number_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_card *card = dev_to_snd_card(dev);
+ struct usb_line6_podhd *pod = card->private_data;
+
+ return sprintf(buf, "%u\n", pod->serial_number);
+}
+
+static ssize_t firmware_version_show(struct device *dev,
+ struct device_attribute *attr, char *buf)
+{
+ struct snd_card *card = dev_to_snd_card(dev);
+ struct usb_line6_podhd *pod = card->private_data;
+
+ return sprintf(buf, "%06x\n", pod->firmware_version);
+}
+
+static DEVICE_ATTR_RO(firmware_version);
+static DEVICE_ATTR_RO(serial_number);
+
+static struct attribute *podhd_dev_attrs[] = {
+ &dev_attr_firmware_version.attr,
+ &dev_attr_serial_number.attr,
+ NULL
+};
+
+static const struct attribute_group podhd_dev_attr_group = {
+ .name = "podhd",
+ .attrs = podhd_dev_attrs,
};
/*
+ * POD X3 startup procedure.
+ *
+ * May be compatible with other POD HD's, since it's also similar to the
+ * previous POD setup. In any case, it doesn't seem to be required for the
+ * audio nor bulk interfaces to work.
+ */
+
+static void podhd_startup(struct usb_line6_podhd *pod)
+{
+ CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_INIT);
+
+ /* delay startup procedure: */
+ line6_start_timer(&pod->startup_timer, PODHD_STARTUP_DELAY,
+ podhd_startup_start_workqueue, (unsigned long)pod);
+}
+
+static void podhd_startup_start_workqueue(unsigned long data)
+{
+ struct usb_line6_podhd *pod = (struct usb_line6_podhd *)data;
+
+ CHECK_STARTUP_PROGRESS(pod->startup_progress,
+ PODHD_STARTUP_SCHEDULE_WORKQUEUE);
+
+ /* schedule work for global work queue: */
+ schedule_work(&pod->startup_work);
+}
+
+static int podhd_dev_start(struct usb_line6_podhd *pod)
+{
+ int ret;
+ u8 init_bytes[8];
+ int i;
+ struct usb_device *usbdev = pod->line6.usbdev;
+
+ ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+ 0x67, USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_OUT,
+ 0x11, 0,
+ NULL, 0, LINE6_TIMEOUT * HZ);
+ if (ret < 0) {
+ dev_err(pod->line6.ifcdev, "read request failed (error %d)\n", ret);
+ return ret;
+ }
+
+ /* NOTE: looks like some kind of ping message */
+ ret = usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0), 0x67,
+ USB_TYPE_VENDOR | USB_RECIP_DEVICE | USB_DIR_IN,
+ 0x11, 0x0,
+ &init_bytes, 3, LINE6_TIMEOUT * HZ);
+ if (ret < 0) {
+ dev_err(pod->line6.ifcdev,
+ "receive length failed (error %d)\n", ret);
+ return ret;
+ }
+
+ pod->firmware_version =
+ (init_bytes[0] << 16) | (init_bytes[1] << 8) | (init_bytes[2] << 0);
+
+ for (i = 0; i <= 16; i++) {
+ ret = line6_read_data(&pod->line6, 0xf000 + 0x08 * i, init_bytes, 8);
+ if (ret < 0)
+ return ret;
+ }
+
+ ret = usb_control_msg(usbdev, usb_sndctrlpipe(usbdev, 0),
+ USB_REQ_SET_FEATURE,
+ USB_TYPE_STANDARD | USB_RECIP_DEVICE | USB_DIR_OUT,
+ 1, 0,
+ NULL, 0, LINE6_TIMEOUT * HZ);
+ if (ret < 0)
+ return ret;
+
+ return 0;
+}
+
+static void podhd_startup_workqueue(struct work_struct *work)
+{
+ struct usb_line6_podhd *pod =
+ container_of(work, struct usb_line6_podhd, startup_work);
+
+ CHECK_STARTUP_PROGRESS(pod->startup_progress, PODHD_STARTUP_SETUP);
+
+ podhd_dev_start(pod);
+ line6_read_serial_number(&pod->line6, &pod->serial_number);
+
+ podhd_startup_finalize(pod);
+}
+
+static int podhd_startup_finalize(struct usb_line6_podhd *pod)
+{
+ struct usb_line6 *line6 = &pod->line6;
+
+ /* ALSA audio interface: */
+ return snd_card_register(line6->card);
+}
+
+static void podhd_disconnect(struct usb_line6 *line6)
+{
+ struct usb_line6_podhd *pod = (struct usb_line6_podhd *)line6;
+
+ if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+ del_timer_sync(&pod->startup_timer);
+ cancel_work_sync(&pod->startup_work);
+ }
+}
+
+/*
Try to init POD HD device.
*/
static int podhd_init(struct usb_line6 *line6,
const struct usb_device_id *id)
{
int err;
+ struct usb_line6_podhd *pod = (struct usb_line6_podhd *) line6;
- /* initialize MIDI subsystem: */
- err = line6_init_midi(line6);
- if (err < 0)
- return err;
+ line6->disconnect = podhd_disconnect;
- /* initialize PCM subsystem: */
- err = line6_init_pcm(line6, &podhd_pcm_properties);
- if (err < 0)
- return err;
+ if (pod->line6.properties->capabilities & LINE6_CAP_CONTROL) {
+ /* create sysfs entries: */
+ err = snd_card_add_dev_attr(line6->card, &podhd_dev_attr_group);
+ if (err < 0)
+ return err;
+ }
- /* register USB audio system: */
- return snd_card_register(line6->card);
+ if (pod->line6.properties->capabilities & LINE6_CAP_PCM) {
+ /* initialize PCM subsystem: */
+ err = line6_init_pcm(line6,
+ (id->driver_info == LINE6_PODX3) ? &podx3_pcm_properties :
+ &podhd_pcm_properties);
+ if (err < 0)
+ return err;
+ }
+
+ if (!(pod->line6.properties->capabilities & LINE6_CAP_CONTROL)) {
+ /* register USB audio system directly */
+ return podhd_startup_finalize(pod);
+ }
+
+ /* init device and delay registering */
+ init_timer(&pod->startup_timer);
+ INIT_WORK(&pod->startup_work, podhd_startup_workqueue);
+ podhd_startup(pod);
+ return 0;
}
#define LINE6_DEVICE(prod) USB_DEVICE(0x0e41, prod)
@@ -103,10 +340,13 @@ static int podhd_init(struct usb_line6 *line6,
/* table of devices that work with this driver */
static const struct usb_device_id podhd_id_table[] = {
+ /* TODO: no need to alloc data interfaces when only audio is used */
{ LINE6_DEVICE(0x5057), .driver_info = LINE6_PODHD300 },
{ LINE6_DEVICE(0x5058), .driver_info = LINE6_PODHD400 },
{ LINE6_IF_NUM(0x414D, 0), .driver_info = LINE6_PODHD500_0 },
{ LINE6_IF_NUM(0x414D, 1), .driver_info = LINE6_PODHD500_1 },
+ { LINE6_IF_NUM(0x414A, 0), .driver_info = LINE6_PODX3 },
+ { LINE6_IF_NUM(0x414B, 0), .driver_info = LINE6_PODX3LIVE },
{}
};
@@ -116,8 +356,7 @@ static const struct line6_properties podhd_properties_table[] = {
[LINE6_PODHD300] = {
.id = "PODHD300",
.name = "POD HD300",
- .capabilities = LINE6_CAP_CONTROL
- | LINE6_CAP_PCM
+ .capabilities = LINE6_CAP_PCM
| LINE6_CAP_HWMON,
.altsetting = 5,
.ep_ctrl_r = 0x84,
@@ -128,8 +367,7 @@ static const struct line6_properties podhd_properties_table[] = {
[LINE6_PODHD400] = {
.id = "PODHD400",
.name = "POD HD400",
- .capabilities = LINE6_CAP_CONTROL
- | LINE6_CAP_PCM
+ .capabilities = LINE6_CAP_PCM
| LINE6_CAP_HWMON,
.altsetting = 5,
.ep_ctrl_r = 0x84,
@@ -140,8 +378,7 @@ static const struct line6_properties podhd_properties_table[] = {
[LINE6_PODHD500_0] = {
.id = "PODHD500",
.name = "POD HD500",
- .capabilities = LINE6_CAP_CONTROL
- | LINE6_CAP_PCM
+ .capabilities = LINE6_CAP_PCM
| LINE6_CAP_HWMON,
.altsetting = 1,
.ep_ctrl_r = 0x81,
@@ -152,8 +389,7 @@ static const struct line6_properties podhd_properties_table[] = {
[LINE6_PODHD500_1] = {
.id = "PODHD500",
.name = "POD HD500",
- .capabilities = LINE6_CAP_CONTROL
- | LINE6_CAP_PCM
+ .capabilities = LINE6_CAP_PCM
| LINE6_CAP_HWMON,
.altsetting = 1,
.ep_ctrl_r = 0x81,
@@ -161,6 +397,28 @@ static const struct line6_properties podhd_properties_table[] = {
.ep_audio_r = 0x86,
.ep_audio_w = 0x02,
},
+ [LINE6_PODX3] = {
+ .id = "PODX3",
+ .name = "POD X3",
+ .capabilities = LINE6_CAP_CONTROL
+ | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
+ .altsetting = 1,
+ .ep_ctrl_r = 0x81,
+ .ep_ctrl_w = 0x01,
+ .ep_audio_r = 0x86,
+ .ep_audio_w = 0x02,
+ },
+ [LINE6_PODX3LIVE] = {
+ .id = "PODX3LIVE",
+ .name = "POD X3 LIVE",
+ .capabilities = LINE6_CAP_CONTROL
+ | LINE6_CAP_PCM | LINE6_CAP_HWMON | LINE6_CAP_IN_NEEDS_OUT,
+ .altsetting = 1,
+ .ep_ctrl_r = 0x81,
+ .ep_ctrl_w = 0x01,
+ .ep_audio_r = 0x86,
+ .ep_audio_w = 0x02,
+ },
};
/*
@@ -171,7 +429,7 @@ static int podhd_probe(struct usb_interface *interface,
{
return line6_probe(interface, id, "Line6-PODHD",
&podhd_properties_table[id->driver_info],
- podhd_init, sizeof(struct usb_line6));
+ podhd_init, sizeof(struct usb_line6_podhd));
}
static struct usb_driver podhd_driver = {
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c
index 6d4c50c9b17d..8e22f430d700 100644
--- a/sound/usb/line6/toneport.c
+++ b/sound/usb/line6/toneport.c
@@ -114,7 +114,7 @@ static struct line6_pcm_properties toneport_pcm_properties = {
.rates = {
.nrats = 1,
.rats = &toneport_ratden},
- .bytes_per_frame = 4
+ .bytes_per_channel = 2
};
static const struct {
@@ -177,7 +177,7 @@ static int snd_toneport_monitor_put(struct snd_kcontrol *kcontrol,
line6pcm->volume_monitor = ucontrol->value.integer.value[0];
if (line6pcm->volume_monitor > 0) {
- err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR);
+ err = line6_pcm_acquire(line6pcm, LINE6_STREAM_MONITOR, true);
if (err < 0) {
line6pcm->volume_monitor = 0;
line6_pcm_release(line6pcm, LINE6_STREAM_MONITOR);
@@ -246,7 +246,7 @@ static void toneport_start_pcm(unsigned long arg)
struct usb_line6_toneport *toneport = (struct usb_line6_toneport *)arg;
struct usb_line6 *line6 = &toneport->line6;
- line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR);
+ line6_pcm_acquire(line6->line6pcm, LINE6_STREAM_MONITOR, true);
}
/* control definition */
diff --git a/sound/usb/line6/variax.c b/sound/usb/line6/variax.c
index ddc23ddf0750..0c4512d0382e 100644
--- a/sound/usb/line6/variax.c
+++ b/sound/usb/line6/variax.c
@@ -259,7 +259,8 @@ static const struct line6_properties variax_properties_table[] = {
[LINE6_PODXTLIVE_VARIAX] = {
.id = "PODxtLive",
.name = "PODxt Live",
- .capabilities = LINE6_CAP_CONTROL,
+ .capabilities = LINE6_CAP_CONTROL
+ | LINE6_CAP_CONTROL_MIDI,
.altsetting = 1,
.ep_ctrl_r = 0x86,
.ep_ctrl_w = 0x05,
@@ -269,7 +270,8 @@ static const struct line6_properties variax_properties_table[] = {
[LINE6_VARIAX] = {
.id = "Variax",
.name = "Variax Workbench",
- .capabilities = LINE6_CAP_CONTROL,
+ .capabilities = LINE6_CAP_CONTROL
+ | LINE6_CAP_CONTROL_MIDI,
.altsetting = 1,
.ep_ctrl_r = 0x82,
.ep_ctrl_w = 0x01,
diff --git a/sound/usb/mixer_maps.c b/sound/usb/mixer_maps.c
index 1f8fb0d904e0..9038b2e7df73 100644
--- a/sound/usb/mixer_maps.c
+++ b/sound/usb/mixer_maps.c
@@ -107,8 +107,10 @@ static struct usbmix_name_map extigy_map[] = {
* e.g. no Master and fake PCM volume
* Pavel Mihaylov <bin@bash.info>
*/
-static struct usbmix_dB_map mp3plus_dB_1 = {-4781, 0}; /* just guess */
-static struct usbmix_dB_map mp3plus_dB_2 = {-1781, 618}; /* just guess */
+static struct usbmix_dB_map mp3plus_dB_1 = {.min = -4781, .max = 0};
+ /* just guess */
+static struct usbmix_dB_map mp3plus_dB_2 = {.min = -1781, .max = 618};
+ /* just guess */
static struct usbmix_name_map mp3plus_map[] = {
/* 1: IT pcm */
diff --git a/sound/usb/mixer_quirks.c b/sound/usb/mixer_quirks.c
index f6c3bf79af9a..04991b009132 100644
--- a/sound/usb/mixer_quirks.c
+++ b/sound/usb/mixer_quirks.c
@@ -1831,6 +1831,7 @@ void snd_usb_mixer_rc_memory_change(struct usb_mixer_interface *mixer,
}
static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
+ struct usb_mixer_elem_info *cval,
struct snd_kcontrol *kctl)
{
/* Approximation using 10 ranges based on output measurement on hw v1.2.
@@ -1848,10 +1849,19 @@ static void snd_dragonfly_quirk_db_scale(struct usb_mixer_interface *mixer,
41, 50, TLV_DB_MINMAX_ITEM(-441, 0),
);
- usb_audio_info(mixer->chip, "applying DragonFly dB scale quirk\n");
- kctl->tlv.p = scale;
- kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
- kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ if (cval->min == 0 && cval->max == 50) {
+ usb_audio_info(mixer->chip, "applying DragonFly dB scale quirk (0-50 variant)\n");
+ kctl->tlv.p = scale;
+ kctl->vd[0].access |= SNDRV_CTL_ELEM_ACCESS_TLV_READ;
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+
+ } else if (cval->min == 0 && cval->max <= 1000) {
+ /* Some other clearly broken DragonFly variant.
+ * At least a 0..53 variant (hw v1.0) exists.
+ */
+ usb_audio_info(mixer->chip, "ignoring too narrow dB range on a DragonFly device");
+ kctl->vd[0].access &= ~SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK;
+ }
}
void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
@@ -1860,8 +1870,8 @@ void snd_usb_mixer_fu_apply_quirk(struct usb_mixer_interface *mixer,
{
switch (mixer->chip->usb_id) {
case USB_ID(0x21b4, 0x0081): /* AudioQuest DragonFly */
- if (unitid == 7 && cval->min == 0 && cval->max == 50)
- snd_dragonfly_quirk_db_scale(mixer, kctl);
+ if (unitid == 7 && cval->control == UAC_FU_VOLUME)
+ snd_dragonfly_quirk_db_scale(mixer, cval, kctl);
break;
}
}
diff --git a/sound/usb/quirks.c b/sound/usb/quirks.c
index 6adde457b602..2782155ae3ce 100644
--- a/sound/usb/quirks.c
+++ b/sound/usb/quirks.c
@@ -1128,6 +1128,7 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
{
/* devices which do not support reading the sample rate. */
switch (chip->usb_id) {
+ case USB_ID(0x041E, 0x4080): /* Creative Live Cam VF0610 */
case USB_ID(0x045E, 0x075D): /* MS Lifecam Cinema */
case USB_ID(0x045E, 0x076D): /* MS Lifecam HD-5000 */
case USB_ID(0x045E, 0x076E): /* MS Lifecam HD-5001 */
@@ -1138,7 +1139,9 @@ bool snd_usb_get_sample_rate_quirk(struct snd_usb_audio *chip)
case USB_ID(0x047F, 0xAA05): /* Plantronics DA45 */
case USB_ID(0x04D8, 0xFEEA): /* Benchmark DAC1 Pre */
case USB_ID(0x0556, 0x0014): /* Phoenix Audio TMX320VC */
+ case USB_ID(0x05A3, 0x9420): /* ELP HD USB Camera */
case USB_ID(0x074D, 0x3553): /* Outlaw RR2150 (Micronas UAC3553B) */
+ case USB_ID(0x1901, 0x0191): /* GE B850V3 CP2114 audio interface */
case USB_ID(0x1de7, 0x0013): /* Phoenix Audio MT202exe */
case USB_ID(0x1de7, 0x0014): /* Phoenix Audio TMX320 */
case USB_ID(0x1de7, 0x0114): /* Phoenix Audio MT202pcs */
@@ -1214,6 +1217,12 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
ep->chip->usb_id == USB_ID(0x0763, 0x2031)) &&
ep->type == SND_USB_ENDPOINT_TYPE_DATA)
ep->skip_packets = 16;
+
+ /* Work around devices that report unreasonable feedback data */
+ if ((ep->chip->usb_id == USB_ID(0x0644, 0x8038) || /* TEAC UD-H01 */
+ ep->chip->usb_id == USB_ID(0x1852, 0x5034)) && /* T+A Dac8 */
+ ep->syncmaxsize == 4)
+ ep->tenor_fb_quirk = 1;
}
void snd_usb_set_interface_quirk(struct usb_device *dev)