summaryrefslogtreecommitdiffstats
path: root/sound
diff options
context:
space:
mode:
Diffstat (limited to 'sound')
-rw-r--r--sound/Kconfig1
-rw-r--r--sound/core/seq/seq_lock.c9
-rw-r--r--sound/core/timer.c19
-rw-r--r--sound/drivers/mpu401/mpu401.c4
-rw-r--r--sound/drivers/mtpav.c4
-rw-r--r--sound/drivers/serial-u16550.c4
-rw-r--r--sound/drivers/vx/vx_core.c4
-rw-r--r--sound/firewire/Kconfig20
-rw-r--r--sound/firewire/Makefile2
-rw-r--r--sound/firewire/amdtp-stream-trace.h94
-rw-r--r--sound/firewire/amdtp-stream.c158
-rw-r--r--sound/firewire/amdtp-stream.h16
-rw-r--r--sound/firewire/bebob/bebob_command.c30
-rw-r--r--sound/firewire/digi00x/amdtp-dot.c55
-rw-r--r--sound/firewire/digi00x/digi00x-midi.c208
-rw-r--r--sound/firewire/digi00x/digi00x-transaction.c88
-rw-r--r--sound/firewire/digi00x/digi00x.c13
-rw-r--r--sound/firewire/digi00x/digi00x.h7
-rw-r--r--sound/firewire/fcp.c12
-rw-r--r--sound/firewire/fireface/Makefile3
-rw-r--r--sound/firewire/fireface/amdtp-ff.c155
-rw-r--r--sound/firewire/fireface/ff-hwdep.c191
-rw-r--r--sound/firewire/fireface/ff-midi.c131
-rw-r--r--sound/firewire/fireface/ff-pcm.c409
-rw-r--r--sound/firewire/fireface/ff-proc.c63
-rw-r--r--sound/firewire/fireface/ff-protocol-ff400.c371
-rw-r--r--sound/firewire/fireface/ff-stream.c282
-rw-r--r--sound/firewire/fireface/ff-transaction.c295
-rw-r--r--sound/firewire/fireface/ff.c209
-rw-r--r--sound/firewire/fireface/ff.h146
-rw-r--r--sound/firewire/lib.c141
-rw-r--r--sound/firewire/lib.h54
-rw-r--r--sound/firewire/motu/Makefile6
-rw-r--r--sound/firewire/motu/amdtp-motu-trace.h123
-rw-r--r--sound/firewire/motu/amdtp-motu.c427
-rw-r--r--sound/firewire/motu/motu-hwdep.c198
-rw-r--r--sound/firewire/motu/motu-midi.c169
-rw-r--r--sound/firewire/motu/motu-pcm.c398
-rw-r--r--sound/firewire/motu/motu-proc.c118
-rw-r--r--sound/firewire/motu/motu-protocol-v2.c237
-rw-r--r--sound/firewire/motu/motu-protocol-v3.c311
-rw-r--r--sound/firewire/motu/motu-stream.c381
-rw-r--r--sound/firewire/motu/motu-transaction.c137
-rw-r--r--sound/firewire/motu/motu.c264
-rw-r--r--sound/firewire/motu/motu.h161
-rw-r--r--sound/firewire/oxfw/oxfw-command.c12
-rw-r--r--sound/firewire/oxfw/oxfw.c4
-rw-r--r--sound/firewire/tascam/tascam-midi.c13
-rw-r--r--sound/firewire/tascam/tascam-transaction.c142
-rw-r--r--sound/firewire/tascam/tascam.h39
-rw-r--r--sound/hda/ext/hdac_ext_controller.c6
-rw-r--r--sound/hda/hdac_controller.c8
-rw-r--r--sound/hda/hdac_stream.c4
-rw-r--r--sound/isa/ad1848/ad1848.c6
-rw-r--r--sound/isa/adlib.c2
-rw-r--r--sound/isa/cmi8328.c12
-rw-r--r--sound/isa/cmi8330.c20
-rw-r--r--sound/isa/cs423x/cs4231.c12
-rw-r--r--sound/isa/cs423x/cs4236.c18
-rw-r--r--sound/isa/es1688/es1688.c12
-rw-r--r--sound/isa/es1688/es1688_lib.c2
-rw-r--r--sound/isa/es18xx.c12
-rw-r--r--sound/isa/galaxy/galaxy.c16
-rw-r--r--sound/isa/gus/gusclassic.c8
-rw-r--r--sound/isa/gus/gusextreme.c16
-rw-r--r--sound/isa/gus/gusmax.c8
-rw-r--r--sound/isa/gus/interwave.c10
-rw-r--r--sound/isa/msnd/msnd_pinnacle.c20
-rw-r--r--sound/isa/opl3sa2.c16
-rw-r--r--sound/isa/opti9xx/miro.c14
-rw-r--r--sound/isa/opti9xx/opti92x-ad1848.c14
-rw-r--r--sound/isa/sb/jazz16.c12
-rw-r--r--sound/isa/sb/sb16.c14
-rw-r--r--sound/isa/sb/sb8.c6
-rw-r--r--sound/isa/sc6000.c12
-rw-r--r--sound/isa/sscape.c12
-rw-r--r--sound/isa/wavefront/wavefront.c18
-rw-r--r--sound/oss/ad1848.c8
-rw-r--r--sound/oss/aedsp16.c12
-rw-r--r--sound/oss/mpu401.c4
-rw-r--r--sound/oss/msnd_pinnacle.c20
-rw-r--r--sound/oss/opl3.c2
-rw-r--r--sound/oss/pas2_card.c18
-rw-r--r--sound/oss/pss.c14
-rw-r--r--sound/oss/sb_card.c10
-rw-r--r--sound/oss/trix.c18
-rw-r--r--sound/oss/uart401.c4
-rw-r--r--sound/oss/uart6850.c4
-rw-r--r--sound/oss/waveartist.c8
-rw-r--r--sound/pci/ali5451/ali5451.c2
-rw-r--r--sound/pci/als4000.c2
-rw-r--r--sound/pci/au88x0/au88x0_a3d.c2
-rw-r--r--sound/pci/au88x0/au88x0_core.c3
-rw-r--r--sound/pci/au88x0/au88x0_eq.c6
-rw-r--r--sound/pci/au88x0/au88x0_pcm.c2
-rw-r--r--sound/pci/aw2/aw2-alsa.c2
-rw-r--r--sound/pci/bt87x.c6
-rw-r--r--sound/pci/ca0106/ca0106_mixer.c4
-rw-r--r--sound/pci/cmipci.c12
-rw-r--r--sound/pci/cs4281.c4
-rw-r--r--sound/pci/echoaudio/echoaudio.c26
-rw-r--r--sound/pci/emu10k1/emu10k1x.c6
-rw-r--r--sound/pci/emu10k1/emumixer.c30
-rw-r--r--sound/pci/emu10k1/emupcm.c2
-rw-r--r--sound/pci/ens1370.c6
-rw-r--r--sound/pci/hda/dell_wmi_helper.c30
-rw-r--r--sound/pci/hda/hda_auto_parser.c1
-rw-r--r--sound/pci/hda/hda_codec.c4
-rw-r--r--sound/pci/hda/hda_codec.h1
-rw-r--r--sound/pci/hda/hda_generic.c9
-rw-r--r--sound/pci/hda/hda_generic.h1
-rw-r--r--sound/pci/hda/hda_intel.c141
-rw-r--r--sound/pci/hda/patch_ca0132.c10
-rw-r--r--sound/pci/hda/patch_conexant.c85
-rw-r--r--sound/pci/hda/patch_hdmi.c29
-rw-r--r--sound/pci/hda/patch_realtek.c221
-rw-r--r--sound/pci/ice1712/delta.c2
-rw-r--r--sound/pci/ice1712/ews.c4
-rw-r--r--sound/pci/ice1712/ice1712.c30
-rw-r--r--sound/pci/ice1712/ice1724.c20
-rw-r--r--sound/pci/intel8x0.c4
-rw-r--r--sound/pci/lola/lola_mixer.c2
-rw-r--r--sound/pci/lx6464es/lx6464es.c2
-rw-r--r--sound/pci/mixart/mixart_mixer.c6
-rw-r--r--sound/pci/oxygen/oxygen.c6
-rw-r--r--sound/pci/pcxhr/pcxhr_mix22.c6
-rw-r--r--sound/pci/pcxhr/pcxhr_mixer.c22
-rw-r--r--sound/pci/riptide/riptide.c6
-rw-r--r--sound/pci/sonicvibes.c2
-rw-r--r--sound/pci/trident/trident_main.c22
-rw-r--r--sound/pci/via82xx.c8
-rw-r--r--sound/pci/vx222/vx222_ops.c4
-rw-r--r--sound/pci/ymfpci/ymfpci.c6
-rw-r--r--sound/pci/ymfpci/ymfpci_main.c14
-rw-r--r--sound/soc/Kconfig2
-rw-r--r--sound/soc/Makefile2
-rw-r--r--sound/soc/blackfin/bfin-eval-adau1373.c2
-rw-r--r--sound/soc/blackfin/bfin-eval-adav80x.c2
-rw-r--r--sound/soc/codecs/Kconfig30
-rw-r--r--sound/soc/codecs/Makefile10
-rw-r--r--sound/soc/codecs/ak4613.c10
-rw-r--r--sound/soc/codecs/cs35l35.c1580
-rw-r--r--sound/soc/codecs/cs35l35.h294
-rw-r--r--sound/soc/codecs/cs4271.c2
-rw-r--r--sound/soc/codecs/cs53l30.c1
-rw-r--r--sound/soc/codecs/da7213.c13
-rw-r--r--sound/soc/codecs/dio2125.c120
-rw-r--r--sound/soc/codecs/es7134.c116
-rw-r--r--sound/soc/codecs/es8328.c51
-rw-r--r--sound/soc/codecs/hdac_hdmi.c4
-rw-r--r--sound/soc/codecs/max9867.c4
-rw-r--r--sound/soc/codecs/max98927.c841
-rw-r--r--sound/soc/codecs/max98927.h272
-rw-r--r--sound/soc/codecs/nau8540.c1224
-rw-r--r--sound/soc/codecs/nau8540.h310
-rw-r--r--sound/soc/codecs/nau8824.c1831
-rw-r--r--sound/soc/codecs/nau8824.h466
-rw-r--r--sound/soc/codecs/rt5514.c36
-rw-r--r--sound/soc/codecs/rt5645.c10
-rw-r--r--sound/soc/codecs/rt5665.c222
-rw-r--r--sound/soc/codecs/rt5665.h2
-rw-r--r--sound/soc/codecs/rt5670.c21
-rw-r--r--sound/soc/codecs/rt5677.c7
-rw-r--r--sound/soc/codecs/sgtl5000.c19
-rw-r--r--sound/soc/codecs/ssm4567.c9
-rw-r--r--sound/soc/codecs/sta529.c7
-rw-r--r--sound/soc/codecs/tas2552.c6
-rw-r--r--sound/soc/codecs/tlv320aic23.c7
-rw-r--r--sound/soc/codecs/twl6040.c8
-rw-r--r--sound/soc/codecs/uda1380.c7
-rw-r--r--sound/soc/codecs/wm5100.c2
-rw-r--r--sound/soc/codecs/wm8903.c31
-rw-r--r--sound/soc/codecs/wm8960.c195
-rw-r--r--sound/soc/codecs/wm8978.c7
-rw-r--r--sound/soc/codecs/wm_adsp.c324
-rw-r--r--sound/soc/codecs/wm_adsp.h24
-rw-r--r--sound/soc/dwc/Kconfig4
-rw-r--r--sound/soc/dwc/Makefile6
-rw-r--r--sound/soc/dwc/dwc-i2s.c (renamed from sound/soc/dwc/designware_i2s.c)0
-rw-r--r--sound/soc/dwc/dwc-pcm.c (renamed from sound/soc/dwc/designware_pcm.c)3
-rw-r--r--sound/soc/fsl/eukrea-tlv320.c2
-rw-r--r--sound/soc/fsl/fsl_asrc_dma.c2
-rw-r--r--sound/soc/fsl/fsl_esai.c5
-rw-r--r--sound/soc/fsl/fsl_ssi.c27
-rw-r--r--sound/soc/fsl/imx-mc13783.c2
-rw-r--r--sound/soc/fsl/imx-pcm-dma.c28
-rw-r--r--sound/soc/fsl/imx-pcm-fiq.c2
-rw-r--r--sound/soc/fsl/imx-wm8962.c72
-rw-r--r--sound/soc/fsl/mpc8610_hpcd.c2
-rw-r--r--sound/soc/fsl/mx27vis-aic32x4.c2
-rw-r--r--sound/soc/fsl/p1022_ds.c2
-rw-r--r--sound/soc/fsl/p1022_rdk.c2
-rw-r--r--sound/soc/fsl/phycore-ac97.c2
-rw-r--r--sound/soc/fsl/wm1133-ev1.c2
-rw-r--r--sound/soc/generic/simple-card.c43
-rw-r--r--sound/soc/generic/simple-scu-card.c37
-rw-r--r--sound/soc/hisilicon/Kconfig5
-rw-r--r--sound/soc/hisilicon/Makefile1
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.c618
-rw-r--r--sound/soc/hisilicon/hi6210-i2s.h276
-rw-r--r--sound/soc/intel/Kconfig24
-rw-r--r--sound/soc/intel/atom/sst/sst_acpi.c41
-rw-r--r--sound/soc/intel/atom/sst/sst_ipc.c4
-rw-r--r--sound/soc/intel/boards/Makefile4
-rw-r--r--sound/soc/intel/boards/bdw-rt5677.c5
-rw-r--r--sound/soc/intel/boards/broadwell.c3
-rw-r--r--sound/soc/intel/boards/bxt_da7219_max98357a.c97
-rw-r--r--sound/soc/intel/boards/bxt_rt298.c3
-rw-r--r--sound/soc/intel/boards/bytcht_da7213.c283
-rw-r--r--sound/soc/intel/boards/bytcht_nocodec.c208
-rw-r--r--sound/soc/intel/boards/bytcr_rt5640.c113
-rw-r--r--sound/soc/intel/boards/bytcr_rt5651.c2
-rw-r--r--sound/soc/intel/haswell/sst-haswell-ipc.c6
-rw-r--r--sound/soc/intel/skylake/bxt-sst.c118
-rw-r--r--sound/soc/intel/skylake/skl-messages.c16
-rw-r--r--sound/soc/intel/skylake/skl-nhlt.c7
-rw-r--r--sound/soc/intel/skylake/skl-pcm.c118
-rw-r--r--sound/soc/intel/skylake/skl-sst-cldma.c26
-rw-r--r--sound/soc/intel/skylake/skl-sst-cldma.h2
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.c6
-rw-r--r--sound/soc/intel/skylake/skl-sst-dsp.h40
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.c76
-rw-r--r--sound/soc/intel/skylake/skl-sst-ipc.h17
-rw-r--r--sound/soc/intel/skylake/skl-sst-utils.c140
-rw-r--r--sound/soc/intel/skylake/skl-sst.c175
-rw-r--r--sound/soc/intel/skylake/skl-topology.c247
-rw-r--r--sound/soc/intel/skylake/skl-topology.h17
-rw-r--r--sound/soc/intel/skylake/skl.c2
-rw-r--r--sound/soc/intel/skylake/skl.h22
-rw-r--r--sound/soc/mediatek/Kconfig10
-rw-r--r--sound/soc/mediatek/mt2701/Makefile1
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-afe-pcm.c16
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-cs42448.c2
-rw-r--r--sound/soc/mediatek/mt2701/mt2701-wm8960.c176
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-max98090.c2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c2
-rw-r--r--sound/soc/mediatek/mt8173/mt8173-rt5650.c2
-rw-r--r--sound/soc/omap/am3517evm.c2
-rw-r--r--sound/soc/omap/n810.c2
-rw-r--r--sound/soc/omap/omap-abe-twl6040.c2
-rw-r--r--sound/soc/omap/omap-twl4030.c2
-rw-r--r--sound/soc/omap/omap3pandora.c2
-rw-r--r--sound/soc/omap/osk5912.c2
-rw-r--r--sound/soc/omap/rx51.c7
-rw-r--r--sound/soc/pxa/brownstone.c2
-rw-r--r--sound/soc/pxa/corgi.c2
-rw-r--r--sound/soc/pxa/e750_wm9705.c2
-rw-r--r--sound/soc/pxa/e800_wm9712.c2
-rw-r--r--sound/soc/pxa/em-x270.c2
-rw-r--r--sound/soc/pxa/hx4700.c2
-rw-r--r--sound/soc/pxa/imote2.c2
-rw-r--r--sound/soc/pxa/magician.c4
-rw-r--r--sound/soc/pxa/mioa701_wm9713.c2
-rw-r--r--sound/soc/pxa/mmp-pcm.c1
-rw-r--r--sound/soc/pxa/mmp-sspa.c1
-rw-r--r--sound/soc/pxa/poodle.c2
-rw-r--r--sound/soc/pxa/pxa-ssp.c15
-rw-r--r--sound/soc/pxa/pxa2xx-ac97.c5
-rw-r--r--sound/soc/pxa/pxa2xx-i2s.c8
-rw-r--r--sound/soc/pxa/pxa2xx-pcm.c2
-rw-r--r--sound/soc/pxa/raumfeld.c8
-rw-r--r--sound/soc/pxa/spitz.c6
-rw-r--r--sound/soc/pxa/tosa.c4
-rw-r--r--sound/soc/pxa/z2.c6
-rw-r--r--sound/soc/pxa/zylonite.c2
-rw-r--r--sound/soc/qcom/lpass-apq8016.c12
-rw-r--r--sound/soc/qcom/lpass-cpu.c22
-rw-r--r--sound/soc/qcom/lpass-ipq806x.c6
-rw-r--r--sound/soc/qcom/lpass.h2
-rw-r--r--sound/soc/rockchip/rk3288_hdmi_analog.c3
-rw-r--r--sound/soc/samsung/Kconfig8
-rw-r--r--sound/soc/samsung/Makefile2
-rw-r--r--sound/soc/samsung/bells.c1
-rw-r--r--sound/soc/samsung/i2s-regs.h2
-rw-r--r--sound/soc/samsung/i2s.c1
-rw-r--r--sound/soc/samsung/odroid.c219
-rw-r--r--sound/soc/samsung/s3c-i2s-v2.c1
-rw-r--r--sound/soc/sh/rcar/adg.c75
-rw-r--r--sound/soc/sh/rcar/core.c111
-rw-r--r--sound/soc/sh/rcar/dvc.c24
-rw-r--r--sound/soc/sh/rcar/rsnd.h55
-rw-r--r--sound/soc/sh/rcar/src.c3
-rw-r--r--sound/soc/sh/rcar/ssi.c9
-rw-r--r--sound/soc/sirf/sirf-audio-port.c1
-rw-r--r--sound/soc/sirf/sirf-audio.c1
-rw-r--r--sound/soc/sirf/sirf-usp.c3
-rw-r--r--sound/soc/soc-core.c29
-rw-r--r--sound/soc/soc-jack.c48
-rw-r--r--sound/soc/soc-topology.c6
-rw-r--r--sound/soc/sti/uniperif.h1
-rw-r--r--sound/soc/sti/uniperif_player.c37
-rw-r--r--sound/soc/sti/uniperif_reader.c24
-rw-r--r--sound/soc/stm/Kconfig8
-rw-r--r--sound/soc/stm/Makefile6
-rw-r--r--sound/soc/stm/stm32_sai.c115
-rw-r--r--sound/soc/stm/stm32_sai.h200
-rw-r--r--sound/soc/stm/stm32_sai_sub.c884
-rw-r--r--sound/soc/sunxi/sun8i-codec-analog.c168
-rw-r--r--sound/soc/sunxi/sun8i-codec.c10
-rw-r--r--sound/soc/tegra/tegra20_ac97.c1
-rw-r--r--sound/soc/tegra/tegra20_das.c2
-rw-r--r--sound/soc/tegra/tegra20_i2s.c1
-rw-r--r--sound/soc/tegra/tegra20_spdif.c5
-rw-r--r--sound/soc/tegra/tegra30_ahub.c5
-rw-r--r--sound/soc/tegra/tegra30_i2s.c1
-rw-r--r--sound/soc/tegra/tegra_alc5632.c4
-rw-r--r--sound/soc/tegra/tegra_max98090.c4
-rw-r--r--sound/soc/tegra/tegra_rt5640.c4
-rw-r--r--sound/soc/tegra/tegra_sgtl5000.c4
-rw-r--r--sound/soc/tegra/tegra_wm8753.c4
-rw-r--r--sound/soc/tegra/tegra_wm8903.c4
-rw-r--r--sound/soc/tegra/tegra_wm9712.c4
-rw-r--r--sound/soc/tegra/trimslice.c4
-rw-r--r--sound/soc/txx9/txx9aclc.c5
-rw-r--r--sound/soc/ux500/mop500.c4
-rw-r--r--sound/soc/ux500/ux500_msp_dai.c4
-rw-r--r--sound/soc/ux500/ux500_msp_i2s.c1
-rw-r--r--sound/soc/zte/Kconfig8
-rw-r--r--sound/soc/zte/Makefile1
-rw-r--r--sound/soc/zte/zx-tdm.c461
-rw-r--r--sound/synth/emux/emux_oss.c4
-rw-r--r--sound/usb/card.c6
-rw-r--r--sound/usb/line6/pcm.c2
-rw-r--r--sound/usb/line6/pod.c2
-rw-r--r--sound/usb/line6/toneport.c4
-rw-r--r--sound/usb/midi.c2
-rw-r--r--sound/usb/mixer.c6
-rw-r--r--sound/usb/mixer_scarlett.c12
-rw-r--r--sound/usb/usx2y/us122l.c2
-rw-r--r--sound/usb/usx2y/usX2Yhwdep.c2
-rw-r--r--sound/usb/usx2y/usx2yhwdeppcm.c2
-rw-r--r--sound/x86/intel_hdmi_audio.c2
333 files changed, 18795 insertions, 2872 deletions
diff --git a/sound/Kconfig b/sound/Kconfig
index ee2e69a9ecd1..6a215a8c0490 100644
--- a/sound/Kconfig
+++ b/sound/Kconfig
@@ -115,6 +115,7 @@ endif # SND
menuconfig SOUND_PRIME
tristate "Open Sound System (DEPRECATED)"
select SOUND_OSS_CORE
+ depends on BROKEN
help
Say 'Y' or 'M' to enable Open Sound System drivers.
diff --git a/sound/core/seq/seq_lock.c b/sound/core/seq/seq_lock.c
index 3b693e924db7..12ba83367b1b 100644
--- a/sound/core/seq/seq_lock.c
+++ b/sound/core/seq/seq_lock.c
@@ -28,19 +28,16 @@
/* wait until all locks are released */
void snd_use_lock_sync_helper(snd_use_lock_t *lockp, const char *file, int line)
{
- int max_count = 5 * HZ;
+ int warn_count = 5 * HZ;
if (atomic_read(lockp) < 0) {
pr_warn("ALSA: seq_lock: lock trouble [counter = %d] in %s:%d\n", atomic_read(lockp), file, line);
return;
}
while (atomic_read(lockp) > 0) {
- if (max_count == 0) {
- pr_warn("ALSA: seq_lock: timeout [%d left] in %s:%d\n", atomic_read(lockp), file, line);
- break;
- }
+ if (warn_count-- == 0)
+ pr_warn("ALSA: seq_lock: waiting [%d left] in %s:%d\n", atomic_read(lockp), file, line);
schedule_timeout_uninterruptible(1);
- max_count--;
}
}
diff --git a/sound/core/timer.c b/sound/core/timer.c
index 6d4fbc439246..2f836ca09860 100644
--- a/sound/core/timer.c
+++ b/sound/core/timer.c
@@ -1277,6 +1277,7 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
struct timespec tstamp;
int prev, append = 0;
+ memset(&r1, 0, sizeof(r1));
memset(&tstamp, 0, sizeof(tstamp));
spin_lock(&tu->qlock);
if ((tu->filter & ((1 << SNDRV_TIMER_EVENT_RESOLUTION) |
@@ -1292,7 +1293,6 @@ static void snd_timer_user_tinterrupt(struct snd_timer_instance *timeri,
}
if ((tu->filter & (1 << SNDRV_TIMER_EVENT_RESOLUTION)) &&
tu->last_resolution != resolution) {
- memset(&r1, 0, sizeof(r1));
r1.event = SNDRV_TIMER_EVENT_RESOLUTION;
r1.tstamp = tstamp;
r1.val = resolution;
@@ -1430,18 +1430,13 @@ static int snd_timer_user_next_device(struct snd_timer_id __user *_tid)
if (id.card < 0) {
id.card = 0;
} else {
- if (id.card < 0) {
- id.card = 0;
+ if (id.device < 0) {
+ id.device = 0;
} else {
- if (id.device < 0) {
- id.device = 0;
- } else {
- if (id.subdevice < 0) {
- id.subdevice = 0;
- } else {
- id.subdevice++;
- }
- }
+ if (id.subdevice < 0)
+ id.subdevice = 0;
+ else
+ id.subdevice++;
}
}
list_for_each(p, &snd_timer_list) {
diff --git a/sound/drivers/mpu401/mpu401.c b/sound/drivers/mpu401/mpu401.c
index fed7e7e2177b..9b86e00d7d95 100644
--- a/sound/drivers/mpu401/mpu401.c
+++ b/sound/drivers/mpu401/mpu401.c
@@ -53,9 +53,9 @@ MODULE_PARM_DESC(enable, "Enable MPU-401 device.");
module_param_array(pnp, bool, NULL, 0444);
MODULE_PARM_DESC(pnp, "PnP detection for MPU-401 device.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for MPU-401 device.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for MPU-401 device.");
module_param_array(uart_enter, bool, NULL, 0444);
MODULE_PARM_DESC(uart_enter, "Issue UART_ENTER command at open.");
diff --git a/sound/drivers/mtpav.c b/sound/drivers/mtpav.c
index 00b31f92c504..0f6392001e30 100644
--- a/sound/drivers/mtpav.c
+++ b/sound/drivers/mtpav.c
@@ -86,9 +86,9 @@ module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for MotuMTPAV MIDI.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for MotuMTPAV MIDI.");
-module_param(port, long, 0444);
+module_param_hw(port, long, ioport, 0444);
MODULE_PARM_DESC(port, "Parallel port # for MotuMTPAV MIDI.");
-module_param(irq, int, 0444);
+module_param_hw(irq, int, irq, 0444);
MODULE_PARM_DESC(irq, "Parallel IRQ # for MotuMTPAV MIDI.");
module_param(hwports, int, 0444);
MODULE_PARM_DESC(hwports, "Hardware ports # for MotuMTPAV MIDI.");
diff --git a/sound/drivers/serial-u16550.c b/sound/drivers/serial-u16550.c
index 60d51ac4ccfe..88e66ea0306d 100644
--- a/sound/drivers/serial-u16550.c
+++ b/sound/drivers/serial-u16550.c
@@ -84,9 +84,9 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Serial MIDI.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable UART16550A chip.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for UART16550A chip.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for UART16550A chip.");
module_param_array(speed, int, NULL, 0444);
MODULE_PARM_DESC(speed, "Speed in bauds.");
diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c
index 289f041706cd..f684fffd1397 100644
--- a/sound/drivers/vx/vx_core.c
+++ b/sound/drivers/vx/vx_core.c
@@ -795,10 +795,8 @@ struct vx_core *snd_vx_create(struct snd_card *card, struct snd_vx_hardware *hw,
return NULL;
chip = kzalloc(sizeof(*chip) + extra_size, GFP_KERNEL);
- if (! chip) {
- snd_printk(KERN_ERR "vx_core: no memory\n");
+ if (! chip)
return NULL;
- }
mutex_init(&chip->lock);
chip->irq = -1;
chip->hw = hw;
diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig
index 9f00696c4e4a..529d9f405fa9 100644
--- a/sound/firewire/Kconfig
+++ b/sound/firewire/Kconfig
@@ -140,4 +140,24 @@ config SND_FIREWIRE_TASCAM
To compile this driver as a module, choose M here: the module
will be called snd-firewire-tascam.
+config SND_FIREWIRE_MOTU
+ tristate "Mark of the unicorn FireWire series support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to enable support for FireWire devices which MOTU produced:
+ * 828mk2
+ * 828mk3
+
+ To compile this driver as a module, choose M here: the module
+ will be called snd-firewire-motu.
+
+config SND_FIREFACE
+ tristate "RME Fireface series support"
+ select SND_FIREWIRE_LIB
+ select SND_HWDEP
+ help
+ Say Y here to include support for RME fireface series.
+ * Fireface 400
+
endif # SND_FIREWIRE
diff --git a/sound/firewire/Makefile b/sound/firewire/Makefile
index 0ee1fb115d88..1b98fa3fa3d4 100644
--- a/sound/firewire/Makefile
+++ b/sound/firewire/Makefile
@@ -13,3 +13,5 @@ obj-$(CONFIG_SND_FIREWORKS) += fireworks/
obj-$(CONFIG_SND_BEBOB) += bebob/
obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/
obj-$(CONFIG_SND_FIREWIRE_TASCAM) += tascam/
+obj-$(CONFIG_SND_FIREWIRE_MOTU) += motu/
+obj-$(CONFIG_SND_FIREFACE) += fireface/
diff --git a/sound/firewire/amdtp-stream-trace.h b/sound/firewire/amdtp-stream-trace.h
index 9c04faf206b2..ea0d486652c8 100644
--- a/sound/firewire/amdtp-stream-trace.h
+++ b/sound/firewire/amdtp-stream-trace.h
@@ -14,8 +14,8 @@
#include <linux/tracepoint.h>
TRACE_EVENT(in_packet,
- TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 cip_header[2], unsigned int payload_quadlets, unsigned int index),
- TP_ARGS(s, cycles, cip_header, payload_quadlets, index),
+ TP_PROTO(const struct amdtp_stream *s, u32 cycles, u32 cip_header[2], unsigned int payload_length, unsigned int index),
+ TP_ARGS(s, cycles, cip_header, payload_length, index),
TP_STRUCT__entry(
__field(unsigned int, second)
__field(unsigned int, cycle)
@@ -37,7 +37,7 @@ TRACE_EVENT(in_packet,
__entry->dest = fw_parent_device(s->unit)->card->node_id;
__entry->cip_header0 = cip_header[0];
__entry->cip_header1 = cip_header[1];
- __entry->payload_quadlets = payload_quadlets;
+ __entry->payload_quadlets = payload_length / 4;
__entry->packet_index = s->packet_index;
__entry->irq = !!in_interrupt();
__entry->index = index;
@@ -101,6 +101,94 @@ TRACE_EVENT(out_packet,
__entry->index)
);
+TRACE_EVENT(in_packet_without_header,
+ TP_PROTO(const struct amdtp_stream *s, u32 cycles, unsigned int payload_quadlets, unsigned int data_blocks, unsigned int index),
+ TP_ARGS(s, cycles, payload_quadlets, data_blocks, index),
+ TP_STRUCT__entry(
+ __field(unsigned int, second)
+ __field(unsigned int, cycle)
+ __field(int, channel)
+ __field(int, src)
+ __field(int, dest)
+ __field(unsigned int, payload_quadlets)
+ __field(unsigned int, data_blocks)
+ __field(unsigned int, data_block_counter)
+ __field(unsigned int, packet_index)
+ __field(unsigned int, irq)
+ __field(unsigned int, index)
+ ),
+ TP_fast_assign(
+ __entry->second = cycles / CYCLES_PER_SECOND;
+ __entry->cycle = cycles % CYCLES_PER_SECOND;
+ __entry->channel = s->context->channel;
+ __entry->src = fw_parent_device(s->unit)->node_id;
+ __entry->dest = fw_parent_device(s->unit)->card->node_id;
+ __entry->payload_quadlets = payload_quadlets;
+ __entry->data_blocks = data_blocks,
+ __entry->data_block_counter = s->data_block_counter,
+ __entry->packet_index = s->packet_index;
+ __entry->irq = !!in_interrupt();
+ __entry->index = index;
+ ),
+ TP_printk(
+ "%02u %04u %04x %04x %02d %03u %3u %3u %02u %01u %02u",
+ __entry->second,
+ __entry->cycle,
+ __entry->src,
+ __entry->dest,
+ __entry->channel,
+ __entry->payload_quadlets,
+ __entry->data_blocks,
+ __entry->data_block_counter,
+ __entry->packet_index,
+ __entry->irq,
+ __entry->index)
+);
+
+TRACE_EVENT(out_packet_without_header,
+ TP_PROTO(const struct amdtp_stream *s, u32 cycles, unsigned int payload_length, unsigned int data_blocks, unsigned int index),
+ TP_ARGS(s, cycles, payload_length, data_blocks, index),
+ TP_STRUCT__entry(
+ __field(unsigned int, second)
+ __field(unsigned int, cycle)
+ __field(int, channel)
+ __field(int, src)
+ __field(int, dest)
+ __field(unsigned int, payload_quadlets)
+ __field(unsigned int, data_blocks)
+ __field(unsigned int, data_block_counter)
+ __field(unsigned int, packet_index)
+ __field(unsigned int, irq)
+ __field(unsigned int, index)
+ ),
+ TP_fast_assign(
+ __entry->second = cycles / CYCLES_PER_SECOND;
+ __entry->cycle = cycles % CYCLES_PER_SECOND;
+ __entry->channel = s->context->channel;
+ __entry->src = fw_parent_device(s->unit)->card->node_id;
+ __entry->dest = fw_parent_device(s->unit)->node_id;
+ __entry->payload_quadlets = payload_length / 4;
+ __entry->data_blocks = data_blocks,
+ __entry->data_blocks = s->data_block_counter,
+ __entry->packet_index = s->packet_index;
+ __entry->irq = !!in_interrupt();
+ __entry->index = index;
+ ),
+ TP_printk(
+ "%02u %04u %04x %04x %02d %03u %02u %03u %02u %01u %02u",
+ __entry->second,
+ __entry->cycle,
+ __entry->src,
+ __entry->dest,
+ __entry->channel,
+ __entry->payload_quadlets,
+ __entry->data_blocks,
+ __entry->data_block_counter,
+ __entry->packet_index,
+ __entry->irq,
+ __entry->index)
+);
+
#endif
#undef TRACE_INCLUDE_PATH
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
index 00060c4a9deb..9e6f54f8c45d 100644
--- a/sound/firewire/amdtp-stream.c
+++ b/sound/firewire/amdtp-stream.c
@@ -27,6 +27,7 @@
/* isochronous header parameters */
#define ISO_DATA_LENGTH_SHIFT 16
+#define TAG_NO_CIP_HEADER 0
#define TAG_CIP 1
/* common isochronous packet header parameters */
@@ -37,6 +38,8 @@
#define CIP_SID_MASK 0x3f000000
#define CIP_DBS_MASK 0x00ff0000
#define CIP_DBS_SHIFT 16
+#define CIP_SPH_MASK 0x00000400
+#define CIP_SPH_SHIFT 10
#define CIP_DBC_MASK 0x000000ff
#define CIP_FMT_SHIFT 24
#define CIP_FMT_MASK 0x3f000000
@@ -232,11 +235,15 @@ EXPORT_SYMBOL(amdtp_stream_set_parameters);
unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
{
unsigned int multiplier = 1;
+ unsigned int header_size = 0;
if (s->flags & CIP_JUMBO_PAYLOAD)
multiplier = 5;
+ if (!(s->flags & CIP_NO_HEADER))
+ header_size = 8;
- return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier;
+ return header_size +
+ s->syt_interval * s->data_block_quadlets * 4 * multiplier;
}
EXPORT_SYMBOL(amdtp_stream_get_max_payload);
@@ -378,7 +385,7 @@ static int queue_packet(struct amdtp_stream *s, unsigned int header_length,
goto end;
p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
- p.tag = TAG_CIP;
+ p.tag = s->tag;
p.header_length = header_length;
if (payload_length > 0)
p.payload_length = payload_length;
@@ -405,17 +412,16 @@ static inline int queue_out_packet(struct amdtp_stream *s,
static inline int queue_in_packet(struct amdtp_stream *s)
{
- return queue_packet(s, IN_PACKET_HEADER_SIZE,
- amdtp_stream_get_max_payload(s));
+ return queue_packet(s, IN_PACKET_HEADER_SIZE, s->max_payload_length);
}
-static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
+static int handle_out_packet(struct amdtp_stream *s,
+ unsigned int payload_length, unsigned int cycle,
unsigned int index)
{
__be32 *buffer;
unsigned int syt;
unsigned int data_blocks;
- unsigned int payload_length;
unsigned int pcm_frames;
struct snd_pcm_substream *pcm;
@@ -424,15 +430,22 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
data_blocks = calculate_data_blocks(s, syt);
pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
+ if (s->flags & CIP_DBC_IS_END_EVENT)
+ s->data_block_counter =
+ (s->data_block_counter + data_blocks) & 0xff;
+
buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
(s->data_block_quadlets << CIP_DBS_SHIFT) |
+ ((s->sph << CIP_SPH_SHIFT) & CIP_SPH_MASK) |
s->data_block_counter);
buffer[1] = cpu_to_be32(CIP_EOH |
((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
(syt & CIP_SYT_MASK));
- s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+ if (!(s->flags & CIP_DBC_IS_END_EVENT))
+ s->data_block_counter =
+ (s->data_block_counter + data_blocks) & 0xff;
payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
trace_out_packet(s, cycle, buffer, payload_length, index);
@@ -448,13 +461,45 @@ static int handle_out_packet(struct amdtp_stream *s, unsigned int cycle,
return 0;
}
+static int handle_out_packet_without_header(struct amdtp_stream *s,
+ unsigned int payload_length, unsigned int cycle,
+ unsigned int index)
+{
+ __be32 *buffer;
+ unsigned int syt;
+ unsigned int data_blocks;
+ unsigned int pcm_frames;
+ struct snd_pcm_substream *pcm;
+
+ buffer = s->buffer.packets[s->packet_index].buffer;
+ syt = calculate_syt(s, cycle);
+ data_blocks = calculate_data_blocks(s, syt);
+ pcm_frames = s->process_data_blocks(s, buffer, data_blocks, &syt);
+ s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+
+ payload_length = data_blocks * 4 * s->data_block_quadlets;
+
+ trace_out_packet_without_header(s, cycle, payload_length, data_blocks,
+ index);
+
+ if (queue_out_packet(s, payload_length) < 0)
+ return -EIO;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm && pcm_frames > 0)
+ update_pcm_pointers(s, pcm, pcm_frames);
+
+ /* No need to return the number of handled data blocks. */
+ return 0;
+}
+
static int handle_in_packet(struct amdtp_stream *s,
- unsigned int payload_quadlets, unsigned int cycle,
+ unsigned int payload_length, unsigned int cycle,
unsigned int index)
{
__be32 *buffer;
u32 cip_header[2];
- unsigned int fmt, fdf, syt;
+ unsigned int sph, fmt, fdf, syt;
unsigned int data_block_quadlets, data_block_counter, dbc_interval;
unsigned int data_blocks;
struct snd_pcm_substream *pcm;
@@ -465,14 +510,15 @@ static int handle_in_packet(struct amdtp_stream *s,
cip_header[0] = be32_to_cpu(buffer[0]);
cip_header[1] = be32_to_cpu(buffer[1]);
- trace_in_packet(s, cycle, cip_header, payload_quadlets, index);
+ trace_in_packet(s, cycle, cip_header, payload_length, index);
/*
* This module supports 'Two-quadlet CIP header with SYT field'.
* For convenience, also check FMT field is AM824 or not.
*/
- if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
- ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) {
+ if ((((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
+ ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) &&
+ (!(s->flags & CIP_HEADER_WITHOUT_EOH))) {
dev_info_ratelimited(&s->unit->device,
"Invalid CIP header for AMDTP: %08X:%08X\n",
cip_header[0], cip_header[1]);
@@ -482,8 +528,9 @@ static int handle_in_packet(struct amdtp_stream *s,
}
/* Check valid protocol or not. */
+ sph = (cip_header[0] & CIP_SPH_MASK) >> CIP_SPH_SHIFT;
fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT;
- if (fmt != s->fmt) {
+ if (sph != s->sph || fmt != s->fmt) {
dev_info_ratelimited(&s->unit->device,
"Detect unexpected protocol: %08x %08x\n",
cip_header[0], cip_header[1]);
@@ -494,7 +541,7 @@ static int handle_in_packet(struct amdtp_stream *s,
/* Calculate data blocks */
fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
- if (payload_quadlets < 3 ||
+ if (payload_length < 12 ||
(fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
data_blocks = 0;
} else {
@@ -510,7 +557,8 @@ static int handle_in_packet(struct amdtp_stream *s,
if (s->flags & CIP_WRONG_DBS)
data_block_quadlets = s->data_block_quadlets;
- data_blocks = (payload_quadlets - 2) / data_block_quadlets;
+ data_blocks = (payload_length / 4 - 2) /
+ data_block_quadlets;
}
/* Check data block counter continuity */
@@ -561,6 +609,34 @@ end:
return 0;
}
+static int handle_in_packet_without_header(struct amdtp_stream *s,
+ unsigned int payload_quadlets, unsigned int cycle,
+ unsigned int index)
+{
+ __be32 *buffer;
+ unsigned int data_blocks;
+ struct snd_pcm_substream *pcm;
+ unsigned int pcm_frames;
+
+ buffer = s->buffer.packets[s->packet_index].buffer;
+ data_blocks = payload_quadlets / s->data_block_quadlets;
+
+ trace_in_packet_without_header(s, cycle, payload_quadlets, data_blocks,
+ index);
+
+ pcm_frames = s->process_data_blocks(s, buffer, data_blocks, NULL);
+ s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+
+ if (queue_in_packet(s) < 0)
+ return -EIO;
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm && pcm_frames > 0)
+ update_pcm_pointers(s, pcm, pcm_frames);
+
+ return 0;
+}
+
/*
* In CYCLE_TIMER register of IEEE 1394, 7 bits are used to represent second. On
* the other hand, in DMA descriptors of 1394 OHCI, 3 bits are used to represent
@@ -604,7 +680,7 @@ static void out_stream_callback(struct fw_iso_context *context, u32 tstamp,
for (i = 0; i < packets; ++i) {
cycle = increment_cycle_count(cycle, 1);
- if (handle_out_packet(s, cycle, i) < 0) {
+ if (s->handle_packet(s, 0, cycle, i) < 0) {
s->packet_index = -1;
amdtp_stream_pcm_abort(s);
return;
@@ -620,7 +696,7 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
{
struct amdtp_stream *s = private_data;
unsigned int i, packets;
- unsigned int payload_quadlets, max_payload_quadlets;
+ unsigned int payload_length, max_payload_length;
__be32 *headers = header;
u32 cycle;
@@ -636,22 +712,22 @@ static void in_stream_callback(struct fw_iso_context *context, u32 tstamp,
cycle = decrement_cycle_count(cycle, packets);
/* For buffer-over-run prevention. */
- max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
+ max_payload_length = s->max_payload_length;
for (i = 0; i < packets; i++) {
cycle = increment_cycle_count(cycle, 1);
- /* The number of quadlets in this packet */
- payload_quadlets =
- (be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT) / 4;
- if (payload_quadlets > max_payload_quadlets) {
+ /* The number of bytes in this packet */
+ payload_length =
+ (be32_to_cpu(headers[i]) >> ISO_DATA_LENGTH_SHIFT);
+ if (payload_length > max_payload_length) {
dev_err(&s->unit->device,
- "Detect jumbo payload: %02x %02x\n",
- payload_quadlets, max_payload_quadlets);
+ "Detect jumbo payload: %04x %04x\n",
+ payload_length, max_payload_length);
break;
}
- if (handle_in_packet(s, payload_quadlets, cycle, i) < 0)
+ if (s->handle_packet(s, payload_length, cycle, i) < 0)
break;
}
@@ -671,6 +747,10 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
void *header, void *private_data)
{
struct amdtp_stream *s = private_data;
+ u32 cycle;
+ unsigned int packets;
+
+ s->max_payload_length = amdtp_stream_get_max_payload(s);
/*
* For in-stream, first packet has come.
@@ -679,10 +759,27 @@ static void amdtp_stream_first_callback(struct fw_iso_context *context,
s->callbacked = true;
wake_up(&s->callback_wait);
- if (s->direction == AMDTP_IN_STREAM)
+ cycle = compute_cycle_count(tstamp);
+
+ if (s->direction == AMDTP_IN_STREAM) {
+ packets = header_length / IN_PACKET_HEADER_SIZE;
+ cycle = decrement_cycle_count(cycle, packets);
context->callback.sc = in_stream_callback;
- else
+ if (s->flags & CIP_NO_HEADER)
+ s->handle_packet = handle_in_packet_without_header;
+ else
+ s->handle_packet = handle_in_packet;
+ } else {
+ packets = header_length / 4;
+ cycle = increment_cycle_count(cycle, QUEUE_LENGTH - packets);
context->callback.sc = out_stream_callback;
+ if (s->flags & CIP_NO_HEADER)
+ s->handle_packet = handle_out_packet_without_header;
+ else
+ s->handle_packet = handle_out_packet;
+ }
+
+ s->start_cycle = cycle;
context->callback.sc(context, tstamp, header_length, header, s);
}
@@ -759,6 +856,11 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
amdtp_stream_update(s);
+ if (s->flags & CIP_NO_HEADER)
+ s->tag = TAG_NO_CIP_HEADER;
+ else
+ s->tag = TAG_CIP;
+
s->packet_index = 0;
do {
if (s->direction == AMDTP_IN_STREAM)
@@ -771,7 +873,7 @@ int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
/* NOTE: TAG1 matches CIP. This just affects in stream. */
tag = FW_ISO_CONTEXT_MATCH_TAG1;
- if (s->flags & CIP_EMPTY_WITH_TAG0)
+ if ((s->flags & CIP_EMPTY_WITH_TAG0) || (s->flags & CIP_NO_HEADER))
tag |= FW_ISO_CONTEXT_MATCH_TAG0;
s->callbacked = false;
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
index c1bc7fad056e..7e8831722821 100644
--- a/sound/firewire/amdtp-stream.h
+++ b/sound/firewire/amdtp-stream.h
@@ -18,8 +18,8 @@
* SYT_INTERVAL samples, with these two types alternating so that
* the overall sample rate comes out right.
* @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
- * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
- * corresponds to the end of event in the packet. Out of IEC 61883.
+ * @CIP_DBC_IS_END_EVENT: The value of dbc in an packet corresponds to the end
+ * of event in the packet. Out of IEC 61883.
* @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets.
* The value of data_block_quadlets is used instead of reported value.
* @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream. Packets with zero in dbc is
@@ -29,6 +29,9 @@
* @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
* packet is larger than IEC 61883-6 defines. Current implementation
* allows 5 times as large as IEC 61883-6 defines.
+ * @CIP_HEADER_WITHOUT_EOH: Only for in-stream. CIP Header doesn't include
+ * valid EOH.
+ * @CIP_NO_HEADERS: a lack of headers in packets
*/
enum cip_flags {
CIP_NONBLOCKING = 0x00,
@@ -39,6 +42,8 @@ enum cip_flags {
CIP_SKIP_DBC_ZERO_CHECK = 0x10,
CIP_EMPTY_HAS_WRONG_DBC = 0x20,
CIP_JUMBO_PAYLOAD = 0x40,
+ CIP_HEADER_WITHOUT_EOH = 0x80,
+ CIP_NO_HEADER = 0x100,
};
/**
@@ -101,11 +106,17 @@ struct amdtp_stream {
struct fw_iso_context *context;
struct iso_packets_buffer buffer;
int packet_index;
+ int tag;
+ int (*handle_packet)(struct amdtp_stream *s,
+ unsigned int payload_quadlets, unsigned int cycle,
+ unsigned int index);
+ unsigned int max_payload_length;
/* For CIP headers. */
unsigned int source_node_id_field;
unsigned int data_block_quadlets;
unsigned int data_block_counter;
+ unsigned int sph;
unsigned int fmt;
unsigned int fdf;
/* quirk: fixed interval of dbc between previos/current packets. */
@@ -130,6 +141,7 @@ struct amdtp_stream {
/* To wait for first packet. */
bool callbacked;
wait_queue_head_t callback_wait;
+ u32 start_cycle;
/* For backends to process data blocks. */
void *protocol;
diff --git a/sound/firewire/bebob/bebob_command.c b/sound/firewire/bebob/bebob_command.c
index 9402cc15dbc1..f9b4225dd86f 100644
--- a/sound/firewire/bebob/bebob_command.c
+++ b/sound/firewire/bebob/bebob_command.c
@@ -31,13 +31,15 @@ int avc_audio_set_selector(struct fw_unit *unit, unsigned int subunit_id,
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(8));
- if (err > 0 && err < 9)
+ if (err < 0)
+ ;
+ else if (err < 9)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
else if (buf[0] == 0x0a) /* REJECTED */
err = -EINVAL;
- else if (err > 0)
+ else
err = 0;
kfree(buf);
@@ -67,7 +69,9 @@ int avc_audio_get_selector(struct fw_unit *unit, unsigned int subunit_id,
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(8));
- if (err > 0 && err < 9)
+ if (err < 0)
+ ;
+ else if (err < 9)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@@ -120,7 +124,9 @@ int avc_bridgeco_get_plug_type(struct fw_unit *unit,
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(9));
- if ((err >= 0) && (err < 8))
+ if (err < 0)
+ ;
+ else if (err < 11)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@@ -150,7 +156,9 @@ int avc_bridgeco_get_plug_ch_pos(struct fw_unit *unit,
err = fcp_avc_transaction(unit, buf, 12, buf, 256,
BIT(1) | BIT(2) | BIT(3) | BIT(4) |
BIT(5) | BIT(6) | BIT(7) | BIT(9));
- if ((err >= 0) && (err < 8))
+ if (err < 0)
+ ;
+ else if (err < 11)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@@ -187,7 +195,9 @@ int avc_bridgeco_get_plug_section_type(struct fw_unit *unit,
err = fcp_avc_transaction(unit, buf, 12, buf, 12,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(9) | BIT(10));
- if ((err >= 0) && (err < 8))
+ if (err < 0)
+ ;
+ else if (err < 12)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@@ -221,7 +231,9 @@ int avc_bridgeco_get_plug_input(struct fw_unit *unit,
err = fcp_avc_transaction(unit, buf, 16, buf, 16,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7));
- if ((err >= 0) && (err < 8))
+ if (err < 0)
+ ;
+ else if (err < 16)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@@ -260,7 +272,9 @@ int avc_bridgeco_get_plug_strm_fmt(struct fw_unit *unit,
err = fcp_avc_transaction(unit, buf, 12, buf, *len,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(10));
- if ((err >= 0) && (err < 12))
+ if (err < 0)
+ ;
+ else if (err < 12)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
index b3cffd01a19f..a4688545339c 100644
--- a/sound/firewire/digi00x/amdtp-dot.c
+++ b/sound/firewire/digi00x/amdtp-dot.c
@@ -28,6 +28,9 @@
*/
#define MAX_MIDI_RX_BLOCKS 8
+/* 3 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) + 1. */
+#define MAX_MIDI_PORTS 3
+
/*
* The double-oh-three algorithm was discovered by Robin Gareus and Damien
* Zammit in 2012, with reverse-engineering for Digi 003 Rack.
@@ -42,10 +45,8 @@ struct amdtp_dot {
unsigned int pcm_channels;
struct dot_state state;
- unsigned int midi_ports;
- /* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */
- struct snd_rawmidi_substream *midi[2];
- int midi_fifo_used[2];
+ struct snd_rawmidi_substream *midi[MAX_MIDI_PORTS];
+ int midi_fifo_used[MAX_MIDI_PORTS];
int midi_fifo_limit;
void (*transfer_samples)(struct amdtp_stream *s,
@@ -124,8 +125,8 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
return -EBUSY;
/*
- * A first data channel is for MIDI conformant data channel, the rest is
- * Multi Bit Linear Audio data channel.
+ * A first data channel is for MIDI messages, the rest is Multi Bit
+ * Linear Audio data channel.
*/
err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1);
if (err < 0)
@@ -135,11 +136,6 @@ int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
p->pcm_channels = pcm_channels;
- if (s->direction == AMDTP_IN_STREAM)
- p->midi_ports = DOT_MIDI_IN_PORTS;
- else
- p->midi_ports = DOT_MIDI_OUT_PORTS;
-
/*
* We do not know the actual MIDI FIFO size of most devices. Just
* assume two bytes, i.e., one byte can be received over the bus while
@@ -281,13 +277,25 @@ static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
b = (u8 *)&buffer[0];
len = 0;
- if (port < p->midi_ports &&
+ if (port < MAX_MIDI_PORTS &&
midi_ratelimit_per_packet(s, port) &&
p->midi[port] != NULL)
len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
if (len > 0) {
- b[3] = (0x10 << port) | len;
+ /*
+ * Upper 4 bits of LSB represent port number.
+ * - 0000b: physical MIDI port 1.
+ * - 0010b: physical MIDI port 2.
+ * - 1110b: console MIDI port.
+ */
+ if (port == 2)
+ b[3] = 0xe0;
+ else if (port == 1)
+ b[3] = 0x20;
+ else
+ b[3] = 0x00;
+ b[3] |= len;
midi_use_bytes(s, port, len);
} else {
b[1] = 0;
@@ -309,11 +317,22 @@ static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
for (f = 0; f < data_blocks; f++) {
b = (u8 *)&buffer[0];
- port = b[3] >> 4;
- len = b[3] & 0x0f;
- if (port < p->midi_ports && p->midi[port] && len > 0)
- snd_rawmidi_receive(p->midi[port], b + 1, len);
+ len = b[3] & 0x0f;
+ if (len > 0) {
+ /*
+ * Upper 4 bits of LSB represent port number.
+ * - 0000b: physical MIDI port 1. Use port 0.
+ * - 1110b: console MIDI port. Use port 2.
+ */
+ if (b[3] >> 4 > 0)
+ port = 2;
+ else
+ port = 0;
+
+ if (port < MAX_MIDI_PORTS && p->midi[port])
+ snd_rawmidi_receive(p->midi[port], b + 1, len);
+ }
buffer += s->data_block_quadlets;
}
@@ -364,7 +383,7 @@ void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
{
struct amdtp_dot *p = s->protocol;
- if (port < p->midi_ports)
+ if (port < MAX_MIDI_PORTS)
ACCESS_ONCE(p->midi[port]) = midi;
}
diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c
index 915d2a21223e..7ab3d0810f6b 100644
--- a/sound/firewire/digi00x/digi00x-midi.c
+++ b/sound/firewire/digi00x/digi00x-midi.c
@@ -8,7 +8,7 @@
#include "digi00x.h"
-static int midi_phys_open(struct snd_rawmidi_substream *substream)
+static int midi_open(struct snd_rawmidi_substream *substream)
{
struct snd_dg00x *dg00x = substream->rmidi->private_data;
int err;
@@ -27,7 +27,7 @@ static int midi_phys_open(struct snd_rawmidi_substream *substream)
return err;
}
-static int midi_phys_close(struct snd_rawmidi_substream *substream)
+static int midi_close(struct snd_rawmidi_substream *substream)
{
struct snd_dg00x *dg00x = substream->rmidi->private_data;
@@ -40,180 +40,130 @@ static int midi_phys_close(struct snd_rawmidi_substream *substream)
return 0;
}
-static void midi_phys_capture_trigger(struct snd_rawmidi_substream *substream,
- int up)
+static void midi_capture_trigger(struct snd_rawmidi_substream *substream,
+ int up)
{
struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned int port;
unsigned long flags;
- spin_lock_irqsave(&dg00x->lock, flags);
-
- if (up)
- amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
- substream);
+ if (substream->rmidi->device == 0)
+ port = substream->number;
else
- amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
- NULL);
-
- spin_unlock_irqrestore(&dg00x->lock, flags);
-}
-
-static void midi_phys_playback_trigger(struct snd_rawmidi_substream *substream,
- int up)
-{
- struct snd_dg00x *dg00x = substream->rmidi->private_data;
- unsigned long flags;
+ port = 2;
spin_lock_irqsave(&dg00x->lock, flags);
if (up)
- amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
- substream);
+ amdtp_dot_midi_trigger(&dg00x->tx_stream, port, substream);
else
- amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
- NULL);
+ amdtp_dot_midi_trigger(&dg00x->tx_stream, port, NULL);
spin_unlock_irqrestore(&dg00x->lock, flags);
}
-static int midi_ctl_open(struct snd_rawmidi_substream *substream)
-{
- /* Do nothing. */
- return 0;
-}
-
-static int midi_ctl_capture_close(struct snd_rawmidi_substream *substream)
-{
- /* Do nothing. */
- return 0;
-}
-
-static int midi_ctl_playback_close(struct snd_rawmidi_substream *substream)
-{
- struct snd_dg00x *dg00x = substream->rmidi->private_data;
-
- snd_fw_async_midi_port_finish(&dg00x->out_control);
-
- return 0;
-}
-
-static void midi_ctl_capture_trigger(struct snd_rawmidi_substream *substream,
- int up)
+static void midi_playback_trigger(struct snd_rawmidi_substream *substream,
+ int up)
{
struct snd_dg00x *dg00x = substream->rmidi->private_data;
+ unsigned int port;
unsigned long flags;
- spin_lock_irqsave(&dg00x->lock, flags);
-
- if (up)
- dg00x->in_control = substream;
+ if (substream->rmidi->device == 0)
+ port = substream->number;
else
- dg00x->in_control = NULL;
-
- spin_unlock_irqrestore(&dg00x->lock, flags);
-}
-
-static void midi_ctl_playback_trigger(struct snd_rawmidi_substream *substream,
- int up)
-{
- struct snd_dg00x *dg00x = substream->rmidi->private_data;
- unsigned long flags;
+ port = 2;
spin_lock_irqsave(&dg00x->lock, flags);
if (up)
- snd_fw_async_midi_port_run(&dg00x->out_control, substream);
+ amdtp_dot_midi_trigger(&dg00x->rx_stream, port, substream);
+ else
+ amdtp_dot_midi_trigger(&dg00x->rx_stream, port, NULL);
spin_unlock_irqrestore(&dg00x->lock, flags);
}
-static void set_midi_substream_names(struct snd_dg00x *dg00x,
- struct snd_rawmidi_str *str,
- bool is_ctl)
+static void set_substream_names(struct snd_dg00x *dg00x,
+ struct snd_rawmidi *rmidi, bool is_console)
{
struct snd_rawmidi_substream *subs;
-
- list_for_each_entry(subs, &str->substreams, list) {
- if (!is_ctl)
- snprintf(subs->name, sizeof(subs->name),
- "%s MIDI %d",
- dg00x->card->shortname, subs->number + 1);
- else
- /* This port is for asynchronous transaction. */
- snprintf(subs->name, sizeof(subs->name),
- "%s control",
- dg00x->card->shortname);
+ struct snd_rawmidi_str *str;
+ int i;
+
+ for (i = 0; i < 2; ++i) {
+ str = &rmidi->streams[i];
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ if (!is_console) {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d",
+ dg00x->card->shortname,
+ subs->number + 1);
+ } else {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s control",
+ dg00x->card->shortname);
+ }
+ }
}
}
-int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
+static int add_substream_pair(struct snd_dg00x *dg00x, unsigned int out_ports,
+ unsigned int in_ports, bool is_console)
{
- static const struct snd_rawmidi_ops phys_capture_ops = {
- .open = midi_phys_open,
- .close = midi_phys_close,
- .trigger = midi_phys_capture_trigger,
- };
- static const struct snd_rawmidi_ops phys_playback_ops = {
- .open = midi_phys_open,
- .close = midi_phys_close,
- .trigger = midi_phys_playback_trigger,
+ static const struct snd_rawmidi_ops capture_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_capture_trigger,
};
- static const struct snd_rawmidi_ops ctl_capture_ops = {
- .open = midi_ctl_open,
- .close = midi_ctl_capture_close,
- .trigger = midi_ctl_capture_trigger,
+ static const struct snd_rawmidi_ops playback_ops = {
+ .open = midi_open,
+ .close = midi_close,
+ .trigger = midi_playback_trigger,
};
- static const struct snd_rawmidi_ops ctl_playback_ops = {
- .open = midi_ctl_open,
- .close = midi_ctl_playback_close,
- .trigger = midi_ctl_playback_trigger,
- };
- struct snd_rawmidi *rmidi[2];
- struct snd_rawmidi_str *str;
- unsigned int i;
+ const char *label;
+ struct snd_rawmidi *rmidi;
int err;
/* Add physical midi ports. */
- err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0,
- DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi[0]);
+ err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, is_console,
+ out_ports, in_ports, &rmidi);
if (err < 0)
return err;
+ rmidi->private_data = dg00x;
- snprintf(rmidi[0]->name, sizeof(rmidi[0]->name),
- "%s MIDI", dg00x->card->shortname);
-
- snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_INPUT,
- &phys_capture_ops);
- snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_OUTPUT,
- &phys_playback_ops);
+ if (!is_console)
+ label = "%s control";
+ else
+ label = "%s MIDI";
+ snprintf(rmidi->name, sizeof(rmidi->name), label,
+ dg00x->card->shortname);
- /* Add a pair of control midi ports. */
- err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 1,
- 1, 1, &rmidi[1]);
- if (err < 0)
- return err;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT, &playback_ops);
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT, &capture_ops);
- snprintf(rmidi[1]->name, sizeof(rmidi[1]->name),
- "%s control", dg00x->card->shortname);
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
- snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_INPUT,
- &ctl_capture_ops);
- snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_OUTPUT,
- &ctl_playback_ops);
+ set_substream_names(dg00x, rmidi, is_console);
- for (i = 0; i < ARRAY_SIZE(rmidi); i++) {
- rmidi[i]->private_data = dg00x;
+ return 0;
+}
- rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
- str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_INPUT];
- set_midi_substream_names(dg00x, str, i);
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
+{
+ int err;
- rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
- str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
- set_midi_substream_names(dg00x, str, i);
+ /* Add physical midi ports. */
+ err = add_substream_pair(dg00x, DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS,
+ false);
+ if (err < 0)
+ return err;
- rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
- }
+ if (dg00x->is_console)
+ err = add_substream_pair(dg00x, 1, 1, true);
- return 0;
+ return err;
}
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c
index 735d35640807..af9bc8504781 100644
--- a/sound/firewire/digi00x/digi00x-transaction.c
+++ b/sound/firewire/digi00x/digi00x-transaction.c
@@ -9,40 +9,6 @@
#include <sound/asound.h>
#include "digi00x.h"
-static int fill_midi_message(struct snd_rawmidi_substream *substream, u8 *buf)
-{
- int bytes;
-
- buf[0] = 0x80;
- bytes = snd_rawmidi_transmit_peek(substream, buf + 1, 2);
- if (bytes >= 0)
- buf[3] = 0xc0 | bytes;
-
- return bytes;
-}
-
-static void handle_midi_control(struct snd_dg00x *dg00x, __be32 *buf,
- unsigned int length)
-{
- struct snd_rawmidi_substream *substream;
- unsigned int i;
- unsigned int len;
- u8 *b;
-
- substream = ACCESS_ONCE(dg00x->in_control);
- if (substream == NULL)
- return;
-
- length /= 4;
-
- for (i = 0; i < length; i++) {
- b = (u8 *)&buf[i];
- len = b[3] & 0xf;
- if (len > 0)
- snd_rawmidi_receive(dg00x->in_control, b + 1, len);
- }
-}
-
static void handle_unknown_message(struct snd_dg00x *dg00x,
unsigned long long offset, __be32 *buf)
{
@@ -63,39 +29,36 @@ static void handle_message(struct fw_card *card, struct fw_request *request,
struct snd_dg00x *dg00x = callback_data;
__be32 *buf = (__be32 *)data;
+ fw_send_response(card, request, RCODE_COMPLETE);
+
if (offset == dg00x->async_handler.offset)
handle_unknown_message(dg00x, offset, buf);
- else if (offset == dg00x->async_handler.offset + 4)
- handle_midi_control(dg00x, buf, length);
-
- fw_send_response(card, request, RCODE_COMPLETE);
}
int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x)
{
struct fw_device *device = fw_parent_device(dg00x->unit);
__be32 data[2];
- int err;
/* Unknown. 4bytes. */
data[0] = cpu_to_be32((device->card->node_id << 16) |
(dg00x->async_handler.offset >> 32));
data[1] = cpu_to_be32(dg00x->async_handler.offset);
- err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
- DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
- &data, sizeof(data), 0);
- if (err < 0)
- return err;
-
- /* Asynchronous transactions for MIDI control message. */
- data[0] = cpu_to_be32((device->card->node_id << 16) |
- (dg00x->async_handler.offset >> 32));
- data[1] = cpu_to_be32(dg00x->async_handler.offset + 4);
return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
- DG00X_ADDR_BASE + DG00X_OFFSET_MIDI_CTL_ADDR,
+ DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
&data, sizeof(data), 0);
}
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
+{
+ if (dg00x->async_handler.callback_data == NULL)
+ return;
+
+ fw_core_remove_address_handler(&dg00x->async_handler);
+
+ dg00x->async_handler.callback_data = NULL;
+}
+
int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
{
static const struct fw_address_region resp_register_region = {
@@ -104,7 +67,7 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
};
int err;
- dg00x->async_handler.length = 12;
+ dg00x->async_handler.length = 4;
dg00x->async_handler.address_callback = handle_message;
dg00x->async_handler.callback_data = dg00x;
@@ -115,28 +78,7 @@ int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
err = snd_dg00x_transaction_reregister(dg00x);
if (err < 0)
- goto error;
-
- err = snd_fw_async_midi_port_init(&dg00x->out_control, dg00x->unit,
- DG00X_ADDR_BASE + DG00X_OFFSET_MMC,
- 4, fill_midi_message);
- if (err < 0)
- goto error;
+ snd_dg00x_transaction_unregister(dg00x);
return err;
-error:
- fw_core_remove_address_handler(&dg00x->async_handler);
- dg00x->async_handler.callback_data = NULL;
- return err;
-}
-
-void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
-{
- if (dg00x->async_handler.callback_data == NULL)
- return;
-
- snd_fw_async_midi_port_destroy(&dg00x->out_control);
- fw_core_remove_address_handler(&dg00x->async_handler);
-
- dg00x->async_handler.callback_data = NULL;
}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
index cc4776c6ded3..1f5e1d23f31a 100644
--- a/sound/firewire/digi00x/digi00x.c
+++ b/sound/firewire/digi00x/digi00x.c
@@ -13,7 +13,8 @@ MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
MODULE_LICENSE("GPL v2");
#define VENDOR_DIGIDESIGN 0x00a07e
-#define MODEL_DIGI00X 0x000002
+#define MODEL_CONSOLE 0x000001
+#define MODEL_RACK 0x000002
static int name_card(struct snd_dg00x *dg00x)
{
@@ -129,6 +130,8 @@ static int snd_dg00x_probe(struct fw_unit *unit,
spin_lock_init(&dg00x->lock);
init_waitqueue_head(&dg00x->hwdep_wait);
+ dg00x->is_console = entry->model_id == MODEL_CONSOLE;
+
/* Allocate and register this sound card later. */
INIT_DEFERRABLE_WORK(&dg00x->dwork, do_registration);
snd_fw_schedule_registration(unit, &dg00x->dwork);
@@ -183,7 +186,13 @@ static const struct ieee1394_device_id snd_dg00x_id_table[] = {
.match_flags = IEEE1394_MATCH_VENDOR_ID |
IEEE1394_MATCH_MODEL_ID,
.vendor_id = VENDOR_DIGIDESIGN,
- .model_id = MODEL_DIGI00X,
+ .model_id = MODEL_CONSOLE,
+ },
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = VENDOR_DIGIDESIGN,
+ .model_id = MODEL_RACK,
},
{}
};
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
index 9dc761bdacca..1275a50956c0 100644
--- a/sound/firewire/digi00x/digi00x.h
+++ b/sound/firewire/digi00x/digi00x.h
@@ -58,16 +58,15 @@ struct snd_dg00x {
struct fw_address_handler async_handler;
u32 msg;
- /* For asynchronous MIDI controls. */
- struct snd_rawmidi_substream *in_control;
- struct snd_fw_async_midi_port out_control;
+ /* Console models have additional MIDI ports for control surface. */
+ bool is_console;
};
#define DG00X_ADDR_BASE 0xffffe0000000ull
#define DG00X_OFFSET_STREAMING_STATE 0x0000
#define DG00X_OFFSET_STREAMING_SET 0x0004
-#define DG00X_OFFSET_MIDI_CTL_ADDR 0x0008
+/* unknown but address in host space 0x0008 */
/* For LSB of the address 0x000c */
/* unknown 0x0010 */
#define DG00X_OFFSET_MESSAGE_ADDR 0x0014
diff --git a/sound/firewire/fcp.c b/sound/firewire/fcp.c
index cce19768f43d..61dda828f767 100644
--- a/sound/firewire/fcp.c
+++ b/sound/firewire/fcp.c
@@ -63,7 +63,9 @@ int avc_general_set_sig_fmt(struct fw_unit *unit, unsigned int rate,
/* do transaction and check buf[1-5] are the same against command */
err = fcp_avc_transaction(unit, buf, 8, buf, 8,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
- if (err >= 0 && err < 8)
+ if (err < 0)
+ ;
+ else if (err < 8)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@@ -106,7 +108,9 @@ int avc_general_get_sig_fmt(struct fw_unit *unit, unsigned int *rate,
/* do transaction and check buf[1-4] are the same against command */
err = fcp_avc_transaction(unit, buf, 8, buf, 8,
BIT(1) | BIT(2) | BIT(3) | BIT(4));
- if (err >= 0 && err < 8)
+ if (err < 0)
+ ;
+ else if (err < 8)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@@ -154,7 +158,9 @@ int avc_general_get_plug_info(struct fw_unit *unit, unsigned int subunit_type,
buf[3] = 0xff & subfunction;
err = fcp_avc_transaction(unit, buf, 8, buf, 8, BIT(1) | BIT(2));
- if (err >= 0 && err < 8)
+ if (err < 0)
+ ;
+ else if (err < 8)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile
new file mode 100644
index 000000000000..8f807284ba54
--- /dev/null
+++ b/sound/firewire/fireface/Makefile
@@ -0,0 +1,3 @@
+snd-fireface-objs := ff.o ff-transaction.o ff-midi.o ff-proc.o amdtp-ff.o \
+ ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-ff400.o
+obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o
diff --git a/sound/firewire/fireface/amdtp-ff.c b/sound/firewire/fireface/amdtp-ff.c
new file mode 100644
index 000000000000..780da9deb2f0
--- /dev/null
+++ b/sound/firewire/fireface/amdtp-ff.c
@@ -0,0 +1,155 @@
+/*
+ * amdtp-ff.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/pcm.h>
+#include "ff.h"
+
+struct amdtp_ff {
+ unsigned int pcm_channels;
+};
+
+int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels)
+{
+ struct amdtp_ff *p = s->protocol;
+ unsigned int data_channels;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ p->pcm_channels = pcm_channels;
+ data_channels = pcm_channels;
+
+ return amdtp_stream_set_parameters(s, rate, data_channels);
+}
+
+static void write_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __le32 *buffer, unsigned int frames)
+{
+ struct amdtp_ff *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ const u32 *src;
+
+ channels = p->pcm_channels;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ buffer[c] = cpu_to_le32(*src);
+ src++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_substream *pcm,
+ __le32 *buffer, unsigned int frames)
+{
+ struct amdtp_ff *p = s->protocol;
+ struct snd_pcm_runtime *runtime = pcm->runtime;
+ unsigned int channels, remaining_frames, i, c;
+ u32 *dst;
+
+ channels = p->pcm_channels;
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c) {
+ *dst = le32_to_cpu(buffer[c]) & 0xffffff00;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s,
+ __le32 *buffer, unsigned int frames)
+{
+ struct amdtp_ff *p = s->protocol;
+ unsigned int i, c, channels = p->pcm_channels;
+
+ for (i = 0; i < frames; ++i) {
+ for (c = 0; c < channels; ++c)
+ buffer[c] = cpu_to_le32(0x00000000);
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+ unsigned int pcm_frames;
+
+ if (pcm) {
+ write_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
+ pcm_frames = data_blocks;
+ } else {
+ write_pcm_silence(s, (__le32 *)buffer, data_blocks);
+ pcm_frames = 0;
+ }
+
+ return pcm_frames;
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+ unsigned int pcm_frames;
+
+ if (pcm) {
+ read_pcm_s32(s, pcm, (__le32 *)buffer, data_blocks);
+ pcm_frames = data_blocks;
+ } else {
+ pcm_frames = 0;
+ }
+
+ return pcm_frames;
+}
+
+int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir)
+{
+ amdtp_stream_process_data_blocks_t process_data_blocks;
+
+ if (dir == AMDTP_IN_STREAM)
+ process_data_blocks = process_tx_data_blocks;
+ else
+ process_data_blocks = process_rx_data_blocks;
+
+ return amdtp_stream_init(s, unit, dir, CIP_NO_HEADER, 0,
+ process_data_blocks, sizeof(struct amdtp_ff));
+}
diff --git a/sound/firewire/fireface/ff-hwdep.c b/sound/firewire/fireface/ff-hwdep.c
new file mode 100644
index 000000000000..3ee04b054585
--- /dev/null
+++ b/sound/firewire/fireface/ff-hwdep.c
@@ -0,0 +1,191 @@
+/*
+ * ff-hwdep.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "ff.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_ff *ff = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&ff->lock);
+
+ while (!ff->dev_lock_changed) {
+ prepare_to_wait(&ff->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&ff->lock);
+ schedule();
+ finish_wait(&ff->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&ff->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (ff->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (ff->dev_lock_count > 0);
+ ff->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+ }
+
+ spin_unlock_irq(&ff->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_ff *ff = hwdep->private_data;
+ unsigned int events;
+
+ poll_wait(file, &ff->hwdep_wait, wait);
+
+ spin_lock_irq(&ff->lock);
+ if (ff->dev_lock_changed)
+ events = POLLIN | POLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&ff->lock);
+
+ return events;
+}
+
+static int hwdep_get_info(struct snd_ff *ff, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(ff->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_FIREFACE;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strlcpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_ff *ff)
+{
+ int err;
+
+ spin_lock_irq(&ff->lock);
+
+ if (ff->dev_lock_count == 0) {
+ ff->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&ff->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_ff *ff)
+{
+ int err;
+
+ spin_lock_irq(&ff->lock);
+
+ if (ff->dev_lock_count == -1) {
+ ff->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&ff->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_ff *ff = hwdep->private_data;
+
+ spin_lock_irq(&ff->lock);
+ if (ff->dev_lock_count == -1)
+ ff->dev_lock_count = 0;
+ spin_unlock_irq(&ff->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_ff *ff = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(ff, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(ff);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(ff);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_ff_create_hwdep_devices(struct snd_ff *ff)
+{
+ static const struct snd_hwdep_ops hwdep_ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(ff->card, ff->card->driver, 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strcpy(hwdep->name, ff->card->driver);
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_FIREFACE;
+ hwdep->ops = hwdep_ops;
+ hwdep->private_data = ff;
+ hwdep->exclusive = true;
+
+ return 0;
+}
diff --git a/sound/firewire/fireface/ff-midi.c b/sound/firewire/fireface/ff-midi.c
new file mode 100644
index 000000000000..29ee0a7365c3
--- /dev/null
+++ b/sound/firewire/fireface/ff-midi.c
@@ -0,0 +1,131 @@
+/*
+ * ff-midi.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "ff.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ff *ff = substream->rmidi->private_data;
+
+ /* Initialize internal status. */
+ ff->running_status[substream->number] = 0;
+ ff->rx_midi_error[substream->number] = false;
+
+ ACCESS_ONCE(ff->rx_midi_substreams[substream->number]) = substream;
+
+ return 0;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ /* Do nothing. */
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_ff *ff = substream->rmidi->private_data;
+
+ cancel_work_sync(&ff->rx_midi_work[substream->number]);
+ ACCESS_ONCE(ff->rx_midi_substreams[substream->number]) = NULL;
+
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_ff *ff = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ff->lock, flags);
+
+ if (up)
+ ACCESS_ONCE(ff->tx_midi_substreams[substream->number]) =
+ substream;
+ else
+ ACCESS_ONCE(ff->tx_midi_substreams[substream->number]) = NULL;
+
+ spin_unlock_irqrestore(&ff->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substream,
+ int up)
+{
+ struct snd_ff *ff = substream->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&ff->lock, flags);
+
+ if (up || !ff->rx_midi_error[substream->number])
+ schedule_work(&ff->rx_midi_work[substream->number]);
+
+ spin_unlock_irqrestore(&ff->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_rawmidi_str *stream,
+ const char *const name)
+{
+ struct snd_rawmidi_substream *substream;
+
+ list_for_each_entry(substream, &stream->substreams, list) {
+ snprintf(substream->name, sizeof(substream->name),
+ "%s MIDI %d", name, substream->number + 1);
+ }
+}
+
+int snd_ff_create_midi_devices(struct snd_ff *ff)
+{
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *stream;
+ int err;
+
+ err = snd_rawmidi_new(ff->card, ff->card->driver, 0,
+ ff->spec->midi_out_ports, ff->spec->midi_in_ports,
+ &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", ff->card->shortname);
+ rmidi->private_data = ff;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &midi_capture_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+ set_midi_substream_names(stream, ff->card->shortname);
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &midi_playback_ops);
+ stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+ set_midi_substream_names(stream, ff->card->shortname);
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ return 0;
+}
diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c
new file mode 100644
index 000000000000..93cee1978e8e
--- /dev/null
+++ b/sound/firewire/fireface/ff-pcm.c
@@ -0,0 +1,409 @@
+/*
+ * ff-pcm.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "ff.h"
+
+static inline unsigned int get_multiplier_mode_with_index(unsigned int index)
+{
+ return ((int)index - 1) / 2;
+}
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ const unsigned int *pcm_channels = rule->private;
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, mode;
+
+ for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
+ mode = get_multiplier_mode_with_index(i);
+ if (!snd_interval_test(c, pcm_channels[mode]))
+ continue;
+
+ t.min = min(t.min, amdtp_rate_table[i]);
+ t.max = max(t.max, amdtp_rate_table[i]);
+ }
+
+ return snd_interval_refine(r, &t);
+}
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ const unsigned int *pcm_channels = rule->private;
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval t = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, mode;
+
+ for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
+ mode = get_multiplier_mode_with_index(i);
+ if (!snd_interval_test(r, amdtp_rate_table[i]))
+ continue;
+
+ t.min = min(t.min, pcm_channels[mode]);
+ t.max = max(t.max, pcm_channels[mode]);
+ }
+
+ return snd_interval_refine(c, &t);
+}
+
+static void limit_channels_and_rates(struct snd_pcm_hardware *hw,
+ const unsigned int *pcm_channels)
+{
+ unsigned int mode;
+ unsigned int rate, channels;
+ int i;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+ hw->rate_min = UINT_MAX;
+ hw->rate_max = 0;
+
+ for (i = 0; i < ARRAY_SIZE(amdtp_rate_table); i++) {
+ mode = get_multiplier_mode_with_index(i);
+
+ channels = pcm_channels[mode];
+ if (pcm_channels[mode] == 0)
+ continue;
+ hw->channels_min = min(hw->channels_min, channels);
+ hw->channels_max = max(hw->channels_max, channels);
+
+ rate = amdtp_rate_table[i];
+ hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+ hw->rate_min = min(hw->rate_min, rate);
+ hw->rate_max = max(hw->rate_max, rate);
+ }
+}
+
+static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
+{
+ hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */
+ hw->periods_max = UINT_MAX;
+
+ hw->period_bytes_min = 4 * hw->channels_max; /* bytes for a frame */
+
+ /* Just to prevent from allocating much pages. */
+ hw->period_bytes_max = hw->period_bytes_min * 2048;
+ hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
+}
+
+static int pcm_init_hw_params(struct snd_ff *ff,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct amdtp_stream *s;
+ const unsigned int *pcm_channels;
+ int err;
+
+ runtime->hw.info = SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ s = &ff->tx_stream;
+ pcm_channels = ff->spec->pcm_capture_channels;
+ } else {
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+ s = &ff->rx_stream;
+ pcm_channels = ff->spec->pcm_playback_channels;
+ }
+
+ /* limit rates */
+ limit_channels_and_rates(&runtime->hw, pcm_channels);
+ limit_period_and_buffer(&runtime->hw);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ hw_rule_channels, (void *)pcm_channels,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ hw_rule_rate, (void *)pcm_channels,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+
+ return amdtp_ff_add_pcm_hw_constraints(s, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+ unsigned int rate;
+ enum snd_ff_clock_src src;
+ int i, err;
+
+ err = snd_ff_stream_lock_try(ff);
+ if (err < 0)
+ return err;
+
+ err = pcm_init_hw_params(ff, substream);
+ if (err < 0) {
+ snd_ff_stream_lock_release(ff);
+ return err;
+ }
+
+ err = ff->spec->protocol->get_clock(ff, &rate, &src);
+ if (err < 0) {
+ snd_ff_stream_lock_release(ff);
+ return err;
+ }
+
+ if (src != SND_FF_CLOCK_SRC_INTERNAL) {
+ for (i = 0; i < CIP_SFC_COUNT; ++i) {
+ if (amdtp_rate_table[i] == rate)
+ break;
+ }
+ /*
+ * The unit is configured at sampling frequency which packet
+ * streaming engine can't support.
+ */
+ if (i >= CIP_SFC_COUNT) {
+ snd_ff_stream_lock_release(ff);
+ return -EIO;
+ }
+
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+ } else {
+ if (amdtp_stream_pcm_running(&ff->rx_stream) ||
+ amdtp_stream_pcm_running(&ff->tx_stream)) {
+ rate = amdtp_rate_table[ff->rx_stream.sfc];
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+ }
+ }
+
+ snd_pcm_set_sync(substream);
+
+ return 0;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ snd_ff_stream_lock_release(ff);
+
+ return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_ff *ff = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&ff->mutex);
+ ff->substreams_counter++;
+ mutex_unlock(&ff->mutex);
+ }
+
+ return 0;
+}
+
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_ff *ff = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&ff->mutex);
+ ff->substreams_counter++;
+ mutex_unlock(&ff->mutex);
+ }
+
+ return 0;
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ mutex_lock(&ff->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ ff->substreams_counter--;
+
+ snd_ff_stream_stop_duplex(ff);
+
+ mutex_unlock(&ff->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ mutex_lock(&ff->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ ff->substreams_counter--;
+
+ snd_ff_stream_stop_duplex(ff);
+
+ mutex_unlock(&ff->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&ff->mutex);
+
+ err = snd_ff_stream_start_duplex(ff, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&ff->tx_stream);
+
+ mutex_unlock(&ff->mutex);
+
+ return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_ff *ff = substream->private_data;
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ int err;
+
+ mutex_lock(&ff->mutex);
+
+ err = snd_ff_stream_start_duplex(ff, runtime->rate);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&ff->rx_stream);
+
+ mutex_unlock(&ff->mutex);
+
+ return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&ff->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&ff->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_ff *ff = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&ff->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&ff->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_ff *ff = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&ff->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+ struct snd_ff *ff = sbstrm->private_data;
+
+ return amdtp_stream_pcm_pointer(&ff->rx_stream);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_capture_hw_params,
+ .hw_free = pcm_capture_hw_free,
+ .prepare = pcm_capture_prepare,
+ .trigger = pcm_capture_trigger,
+ .pointer = pcm_capture_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+};
+
+static struct snd_pcm_ops pcm_playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = pcm_playback_hw_params,
+ .hw_free = pcm_playback_hw_free,
+ .prepare = pcm_playback_prepare,
+ .trigger = pcm_playback_trigger,
+ .pointer = pcm_playback_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_ff_create_pcm_devices(struct snd_ff *ff)
+{
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(ff->card, ff->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+
+ pcm->private_data = ff;
+ snprintf(pcm->name, sizeof(pcm->name),
+ "%s PCM", ff->card->shortname);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+
+ return 0;
+}
diff --git a/sound/firewire/fireface/ff-proc.c b/sound/firewire/fireface/ff-proc.c
new file mode 100644
index 000000000000..69441d121f71
--- /dev/null
+++ b/sound/firewire/fireface/ff-proc.c
@@ -0,0 +1,63 @@
+/*
+ * ff-proc.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./ff.h"
+
+static void proc_dump_clock_config(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_ff *ff = entry->private_data;
+
+ ff->spec->protocol->dump_clock_config(ff, buffer);
+}
+
+static void proc_dump_sync_status(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_ff *ff = entry->private_data;
+
+ ff->spec->protocol->dump_sync_status(ff, buffer);
+}
+
+static void add_node(struct snd_ff *ff, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(ff->card, name, root);
+ if (entry == NULL)
+ return;
+
+ snd_info_set_text_ops(entry, ff, op);
+ if (snd_info_register(entry) < 0)
+ snd_info_free_entry(entry);
+}
+
+void snd_ff_proc_init(struct snd_ff *ff)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(ff->card, "firewire",
+ ff->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(root) < 0) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ add_node(ff, root, "clock-config", proc_dump_clock_config);
+ add_node(ff, root, "sync-status", proc_dump_sync_status);
+}
diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c
new file mode 100644
index 000000000000..fcec6de80eeb
--- /dev/null
+++ b/sound/firewire/fireface/ff-protocol-ff400.c
@@ -0,0 +1,371 @@
+/*
+ * ff-protocol-ff400.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/delay.h>
+#include "ff.h"
+
+#define FF400_STF 0x000080100500ull
+#define FF400_RX_PACKET_FORMAT 0x000080100504ull
+#define FF400_ISOC_COMM_START 0x000080100508ull
+#define FF400_TX_PACKET_FORMAT 0x00008010050cull
+#define FF400_ISOC_COMM_STOP 0x000080100510ull
+#define FF400_SYNC_STATUS 0x0000801c0000ull
+#define FF400_FETCH_PCM_FRAMES 0x0000801c0000ull /* For block request. */
+#define FF400_CLOCK_CONFIG 0x0000801c0004ull
+
+#define FF400_MIDI_HIGH_ADDR 0x0000801003f4ull
+#define FF400_MIDI_RX_PORT_0 0x000080180000ull
+#define FF400_MIDI_RX_PORT_1 0x000080190000ull
+
+static int ff400_get_clock(struct snd_ff *ff, unsigned int *rate,
+ enum snd_ff_clock_src *src)
+{
+ __le32 reg;
+ u32 data;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+ FF400_SYNC_STATUS, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+ data = le32_to_cpu(reg);
+
+ /* Calculate sampling rate. */
+ switch ((data >> 1) & 0x03) {
+ case 0x01:
+ *rate = 32000;
+ break;
+ case 0x00:
+ *rate = 44100;
+ break;
+ case 0x03:
+ *rate = 48000;
+ break;
+ case 0x02:
+ default:
+ return -EIO;
+ }
+
+ if (data & 0x08)
+ *rate *= 2;
+ else if (data & 0x10)
+ *rate *= 4;
+
+ /* Calculate source of clock. */
+ if (data & 0x01) {
+ *src = SND_FF_CLOCK_SRC_INTERNAL;
+ } else {
+ /* TODO: 0x00, 0x01, 0x02, 0x06, 0x07? */
+ switch ((data >> 10) & 0x07) {
+ case 0x03:
+ *src = SND_FF_CLOCK_SRC_SPDIF;
+ break;
+ case 0x04:
+ *src = SND_FF_CLOCK_SRC_WORD;
+ break;
+ case 0x05:
+ *src = SND_FF_CLOCK_SRC_LTC;
+ break;
+ case 0x00:
+ default:
+ *src = SND_FF_CLOCK_SRC_ADAT;
+ break;
+ }
+ }
+
+ return 0;
+}
+
+static int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
+{
+ __le32 reg;
+ int i, err;
+
+ /* Check whether the given value is supported or not. */
+ for (i = 0; i < CIP_SFC_COUNT; i++) {
+ if (amdtp_rate_table[i] == rate)
+ break;
+ }
+ if (i == CIP_SFC_COUNT)
+ return -EINVAL;
+
+ /* Set the number of data blocks transferred in a second. */
+ reg = cpu_to_le32(rate);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_STF, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ msleep(100);
+
+ /*
+ * Set isochronous channel and the number of quadlets of received
+ * packets.
+ */
+ reg = cpu_to_le32(((ff->rx_stream.data_block_quadlets << 3) << 8) |
+ ff->rx_resources.channel);
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /*
+ * Set isochronous channel and the number of quadlets of transmitted
+ * packet.
+ */
+ /* TODO: investigate the purpose of this 0x80. */
+ reg = cpu_to_le32((0x80 << 24) |
+ (ff->tx_resources.channel << 5) |
+ (ff->tx_stream.data_block_quadlets));
+ err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_TX_PACKET_FORMAT, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return err;
+
+ /* Allow to transmit packets. */
+ reg = cpu_to_le32(0x00000001);
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_ISOC_COMM_START, &reg, sizeof(reg), 0);
+}
+
+static void ff400_finish_session(struct snd_ff *ff)
+{
+ __le32 reg;
+
+ reg = cpu_to_le32(0x80000000);
+ snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ FF400_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
+}
+
+static int ff400_switch_fetching_mode(struct snd_ff *ff, bool enable)
+{
+ __le32 *reg;
+ int i;
+
+ reg = kzalloc(sizeof(__le32) * 18, GFP_KERNEL);
+ if (reg == NULL)
+ return -ENOMEM;
+
+ if (enable) {
+ /*
+ * Each quadlet is corresponding to data channels in a data
+ * blocks in reverse order. Precisely, quadlets for available
+ * data channels should be enabled. Here, I take second best
+ * to fetch PCM frames from all of data channels regardless of
+ * stf.
+ */
+ for (i = 0; i < 18; ++i)
+ reg[i] = cpu_to_le32(0x00000001);
+ }
+
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST,
+ FF400_FETCH_PCM_FRAMES, reg,
+ sizeof(__le32) * 18, 0);
+}
+
+static void ff400_dump_sync_status(struct snd_ff *ff,
+ struct snd_info_buffer *buffer)
+{
+ __le32 reg;
+ u32 data;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+ FF400_SYNC_STATUS, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return;
+
+ data = le32_to_cpu(reg);
+
+ snd_iprintf(buffer, "External source detection:\n");
+
+ snd_iprintf(buffer, "Word Clock:");
+ if ((data >> 24) & 0x20) {
+ if ((data >> 24) & 0x40)
+ snd_iprintf(buffer, "sync\n");
+ else
+ snd_iprintf(buffer, "lock\n");
+ } else {
+ snd_iprintf(buffer, "none\n");
+ }
+
+ snd_iprintf(buffer, "S/PDIF:");
+ if ((data >> 16) & 0x10) {
+ if ((data >> 16) & 0x04)
+ snd_iprintf(buffer, "sync\n");
+ else
+ snd_iprintf(buffer, "lock\n");
+ } else {
+ snd_iprintf(buffer, "none\n");
+ }
+
+ snd_iprintf(buffer, "ADAT:");
+ if ((data >> 8) & 0x04) {
+ if ((data >> 8) & 0x10)
+ snd_iprintf(buffer, "sync\n");
+ else
+ snd_iprintf(buffer, "lock\n");
+ } else {
+ snd_iprintf(buffer, "none\n");
+ }
+
+ snd_iprintf(buffer, "\nUsed external source:\n");
+
+ if (((data >> 22) & 0x07) == 0x07) {
+ snd_iprintf(buffer, "None\n");
+ } else {
+ switch ((data >> 22) & 0x07) {
+ case 0x00:
+ snd_iprintf(buffer, "ADAT:");
+ break;
+ case 0x03:
+ snd_iprintf(buffer, "S/PDIF:");
+ break;
+ case 0x04:
+ snd_iprintf(buffer, "Word:");
+ break;
+ case 0x07:
+ snd_iprintf(buffer, "Nothing:");
+ break;
+ case 0x01:
+ case 0x02:
+ case 0x05:
+ case 0x06:
+ default:
+ snd_iprintf(buffer, "unknown:");
+ break;
+ }
+
+ if ((data >> 25) & 0x07) {
+ switch ((data >> 25) & 0x07) {
+ case 0x01:
+ snd_iprintf(buffer, "32000\n");
+ break;
+ case 0x02:
+ snd_iprintf(buffer, "44100\n");
+ break;
+ case 0x03:
+ snd_iprintf(buffer, "48000\n");
+ break;
+ case 0x04:
+ snd_iprintf(buffer, "64000\n");
+ break;
+ case 0x05:
+ snd_iprintf(buffer, "88200\n");
+ break;
+ case 0x06:
+ snd_iprintf(buffer, "96000\n");
+ break;
+ case 0x07:
+ snd_iprintf(buffer, "128000\n");
+ break;
+ case 0x08:
+ snd_iprintf(buffer, "176400\n");
+ break;
+ case 0x09:
+ snd_iprintf(buffer, "192000\n");
+ break;
+ case 0x00:
+ snd_iprintf(buffer, "unknown\n");
+ break;
+ }
+ }
+ }
+
+ snd_iprintf(buffer, "Multiplied:");
+ snd_iprintf(buffer, "%d\n", (data & 0x3ff) * 250);
+}
+
+static void ff400_dump_clock_config(struct snd_ff *ff,
+ struct snd_info_buffer *buffer)
+{
+ __le32 reg;
+ u32 data;
+ unsigned int rate;
+ const char *src;
+ int err;
+
+ err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
+ FF400_CLOCK_CONFIG, &reg, sizeof(reg), 0);
+ if (err < 0)
+ return;
+
+ data = le32_to_cpu(reg);
+
+ snd_iprintf(buffer, "Output S/PDIF format: %s (Emphasis: %s)\n",
+ (data & 0x20) ? "Professional" : "Consumer",
+ (data & 0x40) ? "on" : "off");
+
+ snd_iprintf(buffer, "Optical output interface format: %s\n",
+ ((data >> 8) & 0x01) ? "S/PDIF" : "ADAT");
+
+ snd_iprintf(buffer, "Word output single speed: %s\n",
+ ((data >> 8) & 0x20) ? "on" : "off");
+
+ snd_iprintf(buffer, "S/PDIF input interface: %s\n",
+ ((data >> 8) & 0x02) ? "Optical" : "Coaxial");
+
+ switch ((data >> 1) & 0x03) {
+ case 0x01:
+ rate = 32000;
+ break;
+ case 0x00:
+ rate = 44100;
+ break;
+ case 0x03:
+ rate = 48000;
+ break;
+ case 0x02:
+ default:
+ return;
+ }
+
+ if (data & 0x08)
+ rate *= 2;
+ else if (data & 0x10)
+ rate *= 4;
+
+ snd_iprintf(buffer, "Sampling rate: %d\n", rate);
+
+ if (data & 0x01) {
+ src = "Internal";
+ } else {
+ switch ((data >> 10) & 0x07) {
+ case 0x00:
+ src = "ADAT";
+ break;
+ case 0x03:
+ src = "S/PDIF";
+ break;
+ case 0x04:
+ src = "Word";
+ break;
+ case 0x05:
+ src = "LTC";
+ break;
+ default:
+ return;
+ }
+ }
+
+ snd_iprintf(buffer, "Sync to clock source: %s\n", src);
+}
+
+struct snd_ff_protocol snd_ff_protocol_ff400 = {
+ .get_clock = ff400_get_clock,
+ .begin_session = ff400_begin_session,
+ .finish_session = ff400_finish_session,
+ .switch_fetching_mode = ff400_switch_fetching_mode,
+
+ .dump_sync_status = ff400_dump_sync_status,
+ .dump_clock_config = ff400_dump_clock_config,
+
+ .midi_high_addr_reg = FF400_MIDI_HIGH_ADDR,
+ .midi_rx_port_0_reg = FF400_MIDI_RX_PORT_0,
+ .midi_rx_port_1_reg = FF400_MIDI_RX_PORT_1,
+};
diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c
new file mode 100644
index 000000000000..78880922120e
--- /dev/null
+++ b/sound/firewire/fireface/ff-stream.c
@@ -0,0 +1,282 @@
+/*
+ * ff-stream.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "ff.h"
+
+#define CALLBACK_TIMEOUT_MS 200
+
+static int get_rate_mode(unsigned int rate, unsigned int *mode)
+{
+ int i;
+
+ for (i = 0; i < CIP_SFC_COUNT; i++) {
+ if (amdtp_rate_table[i] == rate)
+ break;
+ }
+
+ if (i == CIP_SFC_COUNT)
+ return -EINVAL;
+
+ *mode = ((int)i - 1) / 2;
+
+ return 0;
+}
+
+/*
+ * Fireface 400 manages isochronous channel number in 3 bit field. Therefore,
+ * we can allocate between 0 and 7 channel.
+ */
+static int keep_resources(struct snd_ff *ff, unsigned int rate)
+{
+ int mode;
+ int err;
+
+ err = get_rate_mode(rate, &mode);
+ if (err < 0)
+ return err;
+
+ /* Keep resources for in-stream. */
+ err = amdtp_ff_set_parameters(&ff->tx_stream, rate,
+ ff->spec->pcm_capture_channels[mode]);
+ if (err < 0)
+ return err;
+ ff->tx_resources.channels_mask = 0x00000000000000ffuLL;
+ err = fw_iso_resources_allocate(&ff->tx_resources,
+ amdtp_stream_get_max_payload(&ff->tx_stream),
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ /* Keep resources for out-stream. */
+ err = amdtp_ff_set_parameters(&ff->rx_stream, rate,
+ ff->spec->pcm_playback_channels[mode]);
+ if (err < 0)
+ return err;
+ ff->rx_resources.channels_mask = 0x00000000000000ffuLL;
+ err = fw_iso_resources_allocate(&ff->rx_resources,
+ amdtp_stream_get_max_payload(&ff->rx_stream),
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ fw_iso_resources_free(&ff->tx_resources);
+
+ return err;
+}
+
+static void release_resources(struct snd_ff *ff)
+{
+ fw_iso_resources_free(&ff->tx_resources);
+ fw_iso_resources_free(&ff->rx_resources);
+}
+
+static inline void finish_session(struct snd_ff *ff)
+{
+ ff->spec->protocol->finish_session(ff);
+ ff->spec->protocol->switch_fetching_mode(ff, false);
+}
+
+static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
+{
+ int err;
+ struct fw_iso_resources *resources;
+ struct amdtp_stream *stream;
+
+ if (dir == AMDTP_IN_STREAM) {
+ resources = &ff->tx_resources;
+ stream = &ff->tx_stream;
+ } else {
+ resources = &ff->rx_resources;
+ stream = &ff->rx_stream;
+ }
+
+ err = fw_iso_resources_init(resources, ff->unit);
+ if (err < 0)
+ return err;
+
+ err = amdtp_ff_init(stream, ff->unit, dir);
+ if (err < 0)
+ fw_iso_resources_destroy(resources);
+
+ return err;
+}
+
+static void destroy_stream(struct snd_ff *ff, enum amdtp_stream_direction dir)
+{
+ if (dir == AMDTP_IN_STREAM) {
+ amdtp_stream_destroy(&ff->tx_stream);
+ fw_iso_resources_destroy(&ff->tx_resources);
+ } else {
+ amdtp_stream_destroy(&ff->rx_stream);
+ fw_iso_resources_destroy(&ff->rx_resources);
+ }
+}
+
+int snd_ff_stream_init_duplex(struct snd_ff *ff)
+{
+ int err;
+
+ err = init_stream(ff, AMDTP_OUT_STREAM);
+ if (err < 0)
+ goto end;
+
+ err = init_stream(ff, AMDTP_IN_STREAM);
+ if (err < 0)
+ destroy_stream(ff, AMDTP_OUT_STREAM);
+end:
+ return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_ff_stream_destroy_duplex(struct snd_ff *ff)
+{
+ destroy_stream(ff, AMDTP_IN_STREAM);
+ destroy_stream(ff, AMDTP_OUT_STREAM);
+}
+
+int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate)
+{
+ unsigned int curr_rate;
+ enum snd_ff_clock_src src;
+ int err;
+
+ if (ff->substreams_counter == 0)
+ return 0;
+
+ err = ff->spec->protocol->get_clock(ff, &curr_rate, &src);
+ if (err < 0)
+ return err;
+ if (curr_rate != rate ||
+ amdtp_streaming_error(&ff->tx_stream) ||
+ amdtp_streaming_error(&ff->rx_stream)) {
+ finish_session(ff);
+
+ amdtp_stream_stop(&ff->tx_stream);
+ amdtp_stream_stop(&ff->rx_stream);
+
+ release_resources(ff);
+ }
+
+ /*
+ * Regardless of current source of clock signal, drivers transfer some
+ * packets. Then, the device transfers packets.
+ */
+ if (!amdtp_stream_running(&ff->rx_stream)) {
+ err = keep_resources(ff, rate);
+ if (err < 0)
+ goto error;
+
+ err = ff->spec->protocol->begin_session(ff, rate);
+ if (err < 0)
+ goto error;
+
+ err = amdtp_stream_start(&ff->rx_stream,
+ ff->rx_resources.channel,
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&ff->rx_stream,
+ CALLBACK_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+
+ err = ff->spec->protocol->switch_fetching_mode(ff, true);
+ if (err < 0)
+ goto error;
+ }
+
+ if (!amdtp_stream_running(&ff->tx_stream)) {
+ err = amdtp_stream_start(&ff->tx_stream,
+ ff->tx_resources.channel,
+ fw_parent_device(ff->unit)->max_speed);
+ if (err < 0)
+ goto error;
+
+ if (!amdtp_stream_wait_callback(&ff->tx_stream,
+ CALLBACK_TIMEOUT_MS)) {
+ err = -ETIMEDOUT;
+ goto error;
+ }
+ }
+
+ return 0;
+error:
+ amdtp_stream_stop(&ff->tx_stream);
+ amdtp_stream_stop(&ff->rx_stream);
+
+ finish_session(ff);
+ release_resources(ff);
+
+ return err;
+}
+
+void snd_ff_stream_stop_duplex(struct snd_ff *ff)
+{
+ if (ff->substreams_counter > 0)
+ return;
+
+ amdtp_stream_stop(&ff->tx_stream);
+ amdtp_stream_stop(&ff->rx_stream);
+ finish_session(ff);
+ release_resources(ff);
+}
+
+void snd_ff_stream_update_duplex(struct snd_ff *ff)
+{
+ /* The device discontinue to transfer packets. */
+ amdtp_stream_pcm_abort(&ff->tx_stream);
+ amdtp_stream_stop(&ff->tx_stream);
+
+ amdtp_stream_pcm_abort(&ff->rx_stream);
+ amdtp_stream_stop(&ff->rx_stream);
+
+ fw_iso_resources_update(&ff->tx_resources);
+ fw_iso_resources_update(&ff->rx_resources);
+}
+
+void snd_ff_stream_lock_changed(struct snd_ff *ff)
+{
+ ff->dev_lock_changed = true;
+ wake_up(&ff->hwdep_wait);
+}
+
+int snd_ff_stream_lock_try(struct snd_ff *ff)
+{
+ int err;
+
+ spin_lock_irq(&ff->lock);
+
+ /* user land lock this */
+ if (ff->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto end;
+ }
+
+ /* this is the first time */
+ if (ff->dev_lock_count++ == 0)
+ snd_ff_stream_lock_changed(ff);
+ err = 0;
+end:
+ spin_unlock_irq(&ff->lock);
+ return err;
+}
+
+void snd_ff_stream_lock_release(struct snd_ff *ff)
+{
+ spin_lock_irq(&ff->lock);
+
+ if (WARN_ON(ff->dev_lock_count <= 0))
+ goto end;
+ if (--ff->dev_lock_count == 0)
+ snd_ff_stream_lock_changed(ff);
+end:
+ spin_unlock_irq(&ff->lock);
+}
diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c
new file mode 100644
index 000000000000..dd6c8e839647
--- /dev/null
+++ b/sound/firewire/fireface/ff-transaction.c
@@ -0,0 +1,295 @@
+/*
+ * ff-transaction.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "ff.h"
+
+static void finish_transmit_midi_msg(struct snd_ff *ff, unsigned int port,
+ int rcode)
+{
+ struct snd_rawmidi_substream *substream =
+ ACCESS_ONCE(ff->rx_midi_substreams[port]);
+
+ if (rcode_is_permanent_error(rcode)) {
+ ff->rx_midi_error[port] = true;
+ return;
+ }
+
+ if (rcode != RCODE_COMPLETE) {
+ /* Transfer the message again, immediately. */
+ ff->next_ktime[port] = 0;
+ schedule_work(&ff->rx_midi_work[port]);
+ return;
+ }
+
+ snd_rawmidi_transmit_ack(substream, ff->rx_bytes[port]);
+ ff->rx_bytes[port] = 0;
+
+ if (!snd_rawmidi_transmit_empty(substream))
+ schedule_work(&ff->rx_midi_work[port]);
+}
+
+static void finish_transmit_midi0_msg(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ void *callback_data)
+{
+ struct snd_ff *ff =
+ container_of(callback_data, struct snd_ff, transactions[0]);
+ finish_transmit_midi_msg(ff, 0, rcode);
+}
+
+static void finish_transmit_midi1_msg(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ void *callback_data)
+{
+ struct snd_ff *ff =
+ container_of(callback_data, struct snd_ff, transactions[1]);
+ finish_transmit_midi_msg(ff, 1, rcode);
+}
+
+static inline void fill_midi_buf(struct snd_ff *ff, unsigned int port,
+ unsigned int index, u8 byte)
+{
+ ff->msg_buf[port][index] = cpu_to_le32(byte);
+}
+
+static void transmit_midi_msg(struct snd_ff *ff, unsigned int port)
+{
+ struct snd_rawmidi_substream *substream =
+ ACCESS_ONCE(ff->rx_midi_substreams[port]);
+ u8 *buf = (u8 *)ff->msg_buf[port];
+ int i, len;
+
+ struct fw_device *fw_dev = fw_parent_device(ff->unit);
+ unsigned long long addr;
+ int generation;
+ fw_transaction_callback_t callback;
+
+ if (substream == NULL || snd_rawmidi_transmit_empty(substream))
+ return;
+
+ if (ff->rx_bytes[port] > 0 || ff->rx_midi_error[port])
+ return;
+
+ /* Do it in next chance. */
+ if (ktime_after(ff->next_ktime[port], ktime_get())) {
+ schedule_work(&ff->rx_midi_work[port]);
+ return;
+ }
+
+ len = snd_rawmidi_transmit_peek(substream, buf,
+ SND_FF_MAXIMIM_MIDI_QUADS);
+ if (len <= 0)
+ return;
+
+ for (i = len - 1; i >= 0; i--)
+ fill_midi_buf(ff, port, i, buf[i]);
+
+ if (port == 0) {
+ addr = ff->spec->protocol->midi_rx_port_0_reg;
+ callback = finish_transmit_midi0_msg;
+ } else {
+ addr = ff->spec->protocol->midi_rx_port_1_reg;
+ callback = finish_transmit_midi1_msg;
+ }
+
+ /* Set interval to next transaction. */
+ ff->next_ktime[port] = ktime_add_ns(ktime_get(),
+ len * 8 * NSEC_PER_SEC / 31250);
+ ff->rx_bytes[port] = len;
+
+ /*
+ * In Linux FireWire core, when generation is updated with memory
+ * barrier, node id has already been updated. In this module, After
+ * this smp_rmb(), load/store instructions to memory are completed.
+ * Thus, both of generation and node id are available with recent
+ * values. This is a light-serialization solution to handle bus reset
+ * events on IEEE 1394 bus.
+ */
+ generation = fw_dev->generation;
+ smp_rmb();
+ fw_send_request(fw_dev->card, &ff->transactions[port],
+ TCODE_WRITE_BLOCK_REQUEST,
+ fw_dev->node_id, generation, fw_dev->max_speed,
+ addr, &ff->msg_buf[port], len * 4,
+ callback, &ff->transactions[port]);
+}
+
+static void transmit_midi0_msg(struct work_struct *work)
+{
+ struct snd_ff *ff = container_of(work, struct snd_ff, rx_midi_work[0]);
+
+ transmit_midi_msg(ff, 0);
+}
+
+static void transmit_midi1_msg(struct work_struct *work)
+{
+ struct snd_ff *ff = container_of(work, struct snd_ff, rx_midi_work[1]);
+
+ transmit_midi_msg(ff, 1);
+}
+
+static void handle_midi_msg(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_ff *ff = callback_data;
+ __le32 *buf = data;
+ u32 quad;
+ u8 byte;
+ unsigned int index;
+ struct snd_rawmidi_substream *substream;
+ int i;
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+
+ for (i = 0; i < length / 4; i++) {
+ quad = le32_to_cpu(buf[i]);
+
+ /* Message in first port. */
+ /*
+ * This value may represent the index of this unit when the same
+ * units are on the same IEEE 1394 bus. This driver doesn't use
+ * it.
+ */
+ index = (quad >> 8) & 0xff;
+ if (index > 0) {
+ substream = ACCESS_ONCE(ff->tx_midi_substreams[0]);
+ if (substream != NULL) {
+ byte = quad & 0xff;
+ snd_rawmidi_receive(substream, &byte, 1);
+ }
+ }
+
+ /* Message in second port. */
+ index = (quad >> 24) & 0xff;
+ if (index > 0) {
+ substream = ACCESS_ONCE(ff->tx_midi_substreams[1]);
+ if (substream != NULL) {
+ byte = (quad >> 16) & 0xff;
+ snd_rawmidi_receive(substream, &byte, 1);
+ }
+ }
+ }
+}
+
+static int allocate_own_address(struct snd_ff *ff, int i)
+{
+ struct fw_address_region midi_msg_region;
+ int err;
+
+ ff->async_handler.length = SND_FF_MAXIMIM_MIDI_QUADS * 4;
+ ff->async_handler.address_callback = handle_midi_msg;
+ ff->async_handler.callback_data = ff;
+
+ midi_msg_region.start = 0x000100000000ull * i;
+ midi_msg_region.end = midi_msg_region.start + ff->async_handler.length;
+
+ err = fw_core_add_address_handler(&ff->async_handler, &midi_msg_region);
+ if (err >= 0) {
+ /* Controllers are allowed to register this region. */
+ if (ff->async_handler.offset & 0x0000ffffffff) {
+ fw_core_remove_address_handler(&ff->async_handler);
+ err = -EAGAIN;
+ }
+ }
+
+ return err;
+}
+
+/*
+ * The configuration to start asynchronous transactions for MIDI messages is in
+ * 0x'0000'8010'051c. This register includes the other options, thus this driver
+ * doesn't touch it and leaves the decision to userspace. The userspace MUST add
+ * 0x04000000 to write transactions to the register to receive any MIDI
+ * messages.
+ *
+ * Here, I just describe MIDI-related offsets of the register, in little-endian
+ * order.
+ *
+ * Controllers are allowed to register higher 4 bytes of address to receive
+ * the transactions. The register is 0x'0000'8010'03f4. On the other hand, the
+ * controllers are not allowed to register lower 4 bytes of the address. They
+ * are forced to select from 4 options by writing corresponding bits to
+ * 0x'0000'8010'051c.
+ *
+ * The 3rd-6th bits in MSB of this register are used to indicate lower 4 bytes
+ * of address to which the device transferrs the transactions.
+ * - 6th: 0x'....'....'0000'0180
+ * - 5th: 0x'....'....'0000'0100
+ * - 4th: 0x'....'....'0000'0080
+ * - 3rd: 0x'....'....'0000'0000
+ *
+ * This driver configure 0x'....'....'0000'0000 for units to receive MIDI
+ * messages. 3rd bit of the register should be configured, however this driver
+ * deligates this task to user space applications due to a restriction that
+ * this register is write-only and the other bits have own effects.
+ *
+ * The 1st and 2nd bits in LSB of this register are used to cancel transferring
+ * asynchronous transactions. These two bits have the same effect.
+ * - 1st/2nd: cancel transferring
+ */
+int snd_ff_transaction_reregister(struct snd_ff *ff)
+{
+ struct fw_card *fw_card = fw_parent_device(ff->unit)->card;
+ u32 addr;
+ __le32 reg;
+
+ /*
+ * Controllers are allowed to register its node ID and upper 2 byte of
+ * local address to listen asynchronous transactions.
+ */
+ addr = (fw_card->node_id << 16) | (ff->async_handler.offset >> 32);
+ reg = cpu_to_le32(addr);
+ return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ ff->spec->protocol->midi_high_addr_reg,
+ &reg, sizeof(reg), 0);
+}
+
+int snd_ff_transaction_register(struct snd_ff *ff)
+{
+ int i, err;
+
+ /*
+ * Allocate in Memory Space of IEC 13213, but lower 4 byte in LSB should
+ * be zero due to device specification.
+ */
+ for (i = 0; i < 0xffff; i++) {
+ err = allocate_own_address(ff, i);
+ if (err != -EBUSY && err != -EAGAIN)
+ break;
+ }
+ if (err < 0)
+ return err;
+
+ err = snd_ff_transaction_reregister(ff);
+ if (err < 0)
+ return err;
+
+ INIT_WORK(&ff->rx_midi_work[0], transmit_midi0_msg);
+ INIT_WORK(&ff->rx_midi_work[1], transmit_midi1_msg);
+
+ return 0;
+}
+
+void snd_ff_transaction_unregister(struct snd_ff *ff)
+{
+ __le32 reg;
+
+ if (ff->async_handler.callback_data == NULL)
+ return;
+ ff->async_handler.callback_data = NULL;
+
+ /* Release higher 4 bytes of address. */
+ reg = cpu_to_le32(0x00000000);
+ snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+ ff->spec->protocol->midi_high_addr_reg,
+ &reg, sizeof(reg), 0);
+
+ fw_core_remove_address_handler(&ff->async_handler);
+}
diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c
new file mode 100644
index 000000000000..eee7c8eac7a6
--- /dev/null
+++ b/sound/firewire/fireface/ff.c
@@ -0,0 +1,209 @@
+/*
+ * ff.c - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "ff.h"
+
+#define OUI_RME 0x000a35
+
+MODULE_DESCRIPTION("RME Fireface series Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static void name_card(struct snd_ff *ff)
+{
+ struct fw_device *fw_dev = fw_parent_device(ff->unit);
+
+ strcpy(ff->card->driver, "Fireface");
+ strcpy(ff->card->shortname, ff->spec->name);
+ strcpy(ff->card->mixername, ff->spec->name);
+ snprintf(ff->card->longname, sizeof(ff->card->longname),
+ "RME %s, GUID %08x%08x at %s, S%d", ff->spec->name,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&ff->unit->device), 100 << fw_dev->max_speed);
+}
+
+static void ff_free(struct snd_ff *ff)
+{
+ snd_ff_stream_destroy_duplex(ff);
+ snd_ff_transaction_unregister(ff);
+
+ fw_unit_put(ff->unit);
+
+ mutex_destroy(&ff->mutex);
+ kfree(ff);
+}
+
+static void ff_card_free(struct snd_card *card)
+{
+ ff_free(card->private_data);
+}
+
+static void do_registration(struct work_struct *work)
+{
+ struct snd_ff *ff = container_of(work, struct snd_ff, dwork.work);
+ int err;
+
+ if (ff->registered)
+ return;
+
+ err = snd_card_new(&ff->unit->device, -1, NULL, THIS_MODULE, 0,
+ &ff->card);
+ if (err < 0)
+ return;
+
+ err = snd_ff_transaction_register(ff);
+ if (err < 0)
+ goto error;
+
+ name_card(ff);
+
+ err = snd_ff_stream_init_duplex(ff);
+ if (err < 0)
+ goto error;
+
+ snd_ff_proc_init(ff);
+
+ err = snd_ff_create_midi_devices(ff);
+ if (err < 0)
+ goto error;
+
+ err = snd_ff_create_pcm_devices(ff);
+ if (err < 0)
+ goto error;
+
+ err = snd_ff_create_hwdep_devices(ff);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(ff->card);
+ if (err < 0)
+ goto error;
+
+ ff->card->private_free = ff_card_free;
+ ff->card->private_data = ff;
+ ff->registered = true;
+
+ return;
+error:
+ snd_ff_transaction_unregister(ff);
+ snd_ff_stream_destroy_duplex(ff);
+ snd_card_free(ff->card);
+ dev_info(&ff->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static int snd_ff_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_ff *ff;
+
+ ff = kzalloc(sizeof(struct snd_ff), GFP_KERNEL);
+ if (ff == NULL)
+ return -ENOMEM;
+
+ /* initialize myself */
+ ff->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, ff);
+
+ mutex_init(&ff->mutex);
+ spin_lock_init(&ff->lock);
+ init_waitqueue_head(&ff->hwdep_wait);
+
+ ff->spec = (const struct snd_ff_spec *)entry->driver_data;
+
+ /* Register this sound card later. */
+ INIT_DEFERRABLE_WORK(&ff->dwork, do_registration);
+ snd_fw_schedule_registration(unit, &ff->dwork);
+
+ return 0;
+}
+
+static void snd_ff_update(struct fw_unit *unit)
+{
+ struct snd_ff *ff = dev_get_drvdata(&unit->device);
+
+ /* Postpone a workqueue for deferred registration. */
+ if (!ff->registered)
+ snd_fw_schedule_registration(unit, &ff->dwork);
+
+ snd_ff_transaction_reregister(ff);
+
+ if (ff->registered)
+ snd_ff_stream_update_duplex(ff);
+}
+
+static void snd_ff_remove(struct fw_unit *unit)
+{
+ struct snd_ff *ff = dev_get_drvdata(&unit->device);
+
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_work_sync(&ff->dwork.work);
+
+ if (ff->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(ff->card);
+ } else {
+ /* Don't forget this case. */
+ ff_free(ff);
+ }
+}
+
+static struct snd_ff_spec spec_ff400 = {
+ .name = "Fireface400",
+ .pcm_capture_channels = {18, 14, 10},
+ .pcm_playback_channels = {18, 14, 10},
+ .midi_in_ports = 2,
+ .midi_out_ports = 2,
+ .protocol = &snd_ff_protocol_ff400,
+};
+
+static const struct ieee1394_device_id snd_ff_id_table[] = {
+ /* Fireface 400 */
+ {
+ .match_flags = IEEE1394_MATCH_VENDOR_ID |
+ IEEE1394_MATCH_SPECIFIER_ID |
+ IEEE1394_MATCH_VERSION |
+ IEEE1394_MATCH_MODEL_ID,
+ .vendor_id = OUI_RME,
+ .specifier_id = 0x000a35,
+ .version = 0x000002,
+ .model_id = 0x101800,
+ .driver_data = (kernel_ulong_t)&spec_ff400,
+ },
+ {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table);
+
+static struct fw_driver ff_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = "snd-fireface",
+ .bus = &fw_bus_type,
+ },
+ .probe = snd_ff_probe,
+ .update = snd_ff_update,
+ .remove = snd_ff_remove,
+ .id_table = snd_ff_id_table,
+};
+
+static int __init snd_ff_init(void)
+{
+ return driver_register(&ff_driver.driver);
+}
+
+static void __exit snd_ff_exit(void)
+{
+ driver_unregister(&ff_driver.driver);
+}
+
+module_init(snd_ff_init);
+module_exit(snd_ff_exit);
diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h
new file mode 100644
index 000000000000..3cb812a50030
--- /dev/null
+++ b/sound/firewire/fireface/ff.h
@@ -0,0 +1,146 @@
+/*
+ * ff.h - a part of driver for RME Fireface series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_FIREFACE_H_INCLUDED
+#define SOUND_FIREFACE_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/sched/signal.h>
+
+#include <sound/core.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/hwdep.h>
+#include <sound/firewire.h>
+
+#include "../lib.h"
+#include "../amdtp-stream.h"
+#include "../iso-resources.h"
+
+#define SND_FF_STREAM_MODES 3
+
+#define SND_FF_MAXIMIM_MIDI_QUADS 9
+#define SND_FF_IN_MIDI_PORTS 2
+#define SND_FF_OUT_MIDI_PORTS 2
+
+struct snd_ff_protocol;
+struct snd_ff_spec {
+ const char *const name;
+
+ const unsigned int pcm_capture_channels[SND_FF_STREAM_MODES];
+ const unsigned int pcm_playback_channels[SND_FF_STREAM_MODES];
+
+ unsigned int midi_in_ports;
+ unsigned int midi_out_ports;
+
+ struct snd_ff_protocol *protocol;
+};
+
+struct snd_ff {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ struct mutex mutex;
+ spinlock_t lock;
+
+ bool registered;
+ struct delayed_work dwork;
+
+ const struct snd_ff_spec *spec;
+
+ /* To handle MIDI tx. */
+ struct snd_rawmidi_substream *tx_midi_substreams[SND_FF_IN_MIDI_PORTS];
+ struct fw_address_handler async_handler;
+
+ /* TO handle MIDI rx. */
+ struct snd_rawmidi_substream *rx_midi_substreams[SND_FF_OUT_MIDI_PORTS];
+ u8 running_status[SND_FF_OUT_MIDI_PORTS];
+ __le32 msg_buf[SND_FF_OUT_MIDI_PORTS][SND_FF_MAXIMIM_MIDI_QUADS];
+ struct work_struct rx_midi_work[SND_FF_OUT_MIDI_PORTS];
+ struct fw_transaction transactions[SND_FF_OUT_MIDI_PORTS];
+ ktime_t next_ktime[SND_FF_OUT_MIDI_PORTS];
+ bool rx_midi_error[SND_FF_OUT_MIDI_PORTS];
+ unsigned int rx_bytes[SND_FF_OUT_MIDI_PORTS];
+
+ unsigned int substreams_counter;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ struct fw_iso_resources tx_resources;
+ struct fw_iso_resources rx_resources;
+
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+};
+
+enum snd_ff_clock_src {
+ SND_FF_CLOCK_SRC_INTERNAL,
+ SND_FF_CLOCK_SRC_SPDIF,
+ SND_FF_CLOCK_SRC_ADAT,
+ SND_FF_CLOCK_SRC_WORD,
+ SND_FF_CLOCK_SRC_LTC,
+ /* TODO: perhaps ADAT2 and TCO exists. */
+};
+
+struct snd_ff_protocol {
+ int (*get_clock)(struct snd_ff *ff, unsigned int *rate,
+ enum snd_ff_clock_src *src);
+ int (*begin_session)(struct snd_ff *ff, unsigned int rate);
+ void (*finish_session)(struct snd_ff *ff);
+ int (*switch_fetching_mode)(struct snd_ff *ff, bool enable);
+
+ void (*dump_sync_status)(struct snd_ff *ff,
+ struct snd_info_buffer *buffer);
+ void (*dump_clock_config)(struct snd_ff *ff,
+ struct snd_info_buffer *buffer);
+
+ u64 midi_high_addr_reg;
+ u64 midi_rx_port_0_reg;
+ u64 midi_rx_port_1_reg;
+};
+
+extern struct snd_ff_protocol snd_ff_protocol_ff400;
+
+int snd_ff_transaction_register(struct snd_ff *ff);
+int snd_ff_transaction_reregister(struct snd_ff *ff);
+void snd_ff_transaction_unregister(struct snd_ff *ff);
+
+int amdtp_ff_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int pcm_channels);
+int amdtp_ff_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+int amdtp_ff_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir);
+
+int snd_ff_stream_init_duplex(struct snd_ff *ff);
+void snd_ff_stream_destroy_duplex(struct snd_ff *ff);
+int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate);
+void snd_ff_stream_stop_duplex(struct snd_ff *ff);
+void snd_ff_stream_update_duplex(struct snd_ff *ff);
+
+void snd_ff_stream_lock_changed(struct snd_ff *ff);
+int snd_ff_stream_lock_try(struct snd_ff *ff);
+void snd_ff_stream_lock_release(struct snd_ff *ff);
+
+void snd_ff_proc_init(struct snd_ff *ff);
+
+int snd_ff_create_midi_devices(struct snd_ff *ff);
+
+int snd_ff_create_pcm_devices(struct snd_ff *ff);
+
+int snd_ff_create_hwdep_devices(struct snd_ff *ff);
+
+#endif
diff --git a/sound/firewire/lib.c b/sound/firewire/lib.c
index 7683238283b6..39dfa74906ef 100644
--- a/sound/firewire/lib.c
+++ b/sound/firewire/lib.c
@@ -99,147 +99,6 @@ void snd_fw_schedule_registration(struct fw_unit *unit,
}
EXPORT_SYMBOL(snd_fw_schedule_registration);
-static void async_midi_port_callback(struct fw_card *card, int rcode,
- void *data, size_t length,
- void *callback_data)
-{
- struct snd_fw_async_midi_port *port = callback_data;
- struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
-
- /* This port is closed. */
- if (substream == NULL)
- return;
-
- if (rcode == RCODE_COMPLETE)
- snd_rawmidi_transmit_ack(substream, port->consume_bytes);
- else if (!rcode_is_permanent_error(rcode))
- /* To start next transaction immediately for recovery. */
- port->next_ktime = 0;
- else
- /* Don't continue processing. */
- port->error = true;
-
- port->idling = true;
-
- if (!snd_rawmidi_transmit_empty(substream))
- schedule_work(&port->work);
-}
-
-static void midi_port_work(struct work_struct *work)
-{
- struct snd_fw_async_midi_port *port =
- container_of(work, struct snd_fw_async_midi_port, work);
- struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
- int generation;
- int type;
-
- /* Under transacting or error state. */
- if (!port->idling || port->error)
- return;
-
- /* Nothing to do. */
- if (substream == NULL || snd_rawmidi_transmit_empty(substream))
- return;
-
- /* Do it in next chance. */
- if (ktime_after(port->next_ktime, ktime_get())) {
- schedule_work(&port->work);
- return;
- }
-
- /*
- * Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
- * Later, snd_rawmidi_transmit_ack() is called.
- */
- memset(port->buf, 0, port->len);
- port->consume_bytes = port->fill(substream, port->buf);
- if (port->consume_bytes <= 0) {
- /* Do it in next chance, immediately. */
- if (port->consume_bytes == 0) {
- port->next_ktime = 0;
- schedule_work(&port->work);
- } else {
- /* Fatal error. */
- port->error = true;
- }
- return;
- }
-
- /* Calculate type of transaction. */
- if (port->len == 4)
- type = TCODE_WRITE_QUADLET_REQUEST;
- else
- type = TCODE_WRITE_BLOCK_REQUEST;
-
- /* Set interval to next transaction. */
- port->next_ktime = ktime_add_ns(ktime_get(),
- port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
-
- /* Start this transaction. */
- port->idling = false;
-
- /*
- * In Linux FireWire core, when generation is updated with memory
- * barrier, node id has already been updated. In this module, After
- * this smp_rmb(), load/store instructions to memory are completed.
- * Thus, both of generation and node id are available with recent
- * values. This is a light-serialization solution to handle bus reset
- * events on IEEE 1394 bus.
- */
- generation = port->parent->generation;
- smp_rmb();
-
- fw_send_request(port->parent->card, &port->transaction, type,
- port->parent->node_id, generation,
- port->parent->max_speed, port->addr,
- port->buf, port->len, async_midi_port_callback,
- port);
-}
-
-/**
- * snd_fw_async_midi_port_init - initialize asynchronous MIDI port structure
- * @port: the asynchronous MIDI port to initialize
- * @unit: the target of the asynchronous transaction
- * @addr: the address to which transactions are transferred
- * @len: the length of transaction
- * @fill: the callback function to fill given buffer, and returns the
- * number of consumed bytes for MIDI message.
- *
- */
-int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
- struct fw_unit *unit, u64 addr, unsigned int len,
- snd_fw_async_midi_port_fill fill)
-{
- port->len = DIV_ROUND_UP(len, 4) * 4;
- port->buf = kzalloc(port->len, GFP_KERNEL);
- if (port->buf == NULL)
- return -ENOMEM;
-
- port->parent = fw_parent_device(unit);
- port->addr = addr;
- port->fill = fill;
- port->idling = true;
- port->next_ktime = 0;
- port->error = false;
-
- INIT_WORK(&port->work, midi_port_work);
-
- return 0;
-}
-EXPORT_SYMBOL(snd_fw_async_midi_port_init);
-
-/**
- * snd_fw_async_midi_port_destroy - free asynchronous MIDI port structure
- * @port: the asynchronous MIDI port structure
- */
-void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port)
-{
- snd_fw_async_midi_port_finish(port);
- cancel_work_sync(&port->work);
- kfree(port->buf);
-}
-EXPORT_SYMBOL(snd_fw_async_midi_port_destroy);
-
MODULE_DESCRIPTION("FireWire audio helper functions");
MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
MODULE_LICENSE("GPL v2");
diff --git a/sound/firewire/lib.h b/sound/firewire/lib.h
index f6769312ebfc..eef70922ed89 100644
--- a/sound/firewire/lib.h
+++ b/sound/firewire/lib.h
@@ -25,58 +25,4 @@ static inline bool rcode_is_permanent_error(int rcode)
void snd_fw_schedule_registration(struct fw_unit *unit,
struct delayed_work *dwork);
-struct snd_fw_async_midi_port;
-typedef int (*snd_fw_async_midi_port_fill)(
- struct snd_rawmidi_substream *substream,
- u8 *buf);
-
-struct snd_fw_async_midi_port {
- struct fw_device *parent;
- struct work_struct work;
- bool idling;
- ktime_t next_ktime;
- bool error;
-
- u64 addr;
- struct fw_transaction transaction;
-
- u8 *buf;
- unsigned int len;
-
- struct snd_rawmidi_substream *substream;
- snd_fw_async_midi_port_fill fill;
- unsigned int consume_bytes;
-};
-
-int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
- struct fw_unit *unit, u64 addr, unsigned int len,
- snd_fw_async_midi_port_fill fill);
-void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port);
-
-/**
- * snd_fw_async_midi_port_run - run transactions for the async MIDI port
- * @port: the asynchronous MIDI port
- * @substream: the MIDI substream
- */
-static inline void
-snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
- struct snd_rawmidi_substream *substream)
-{
- if (!port->error) {
- port->substream = substream;
- schedule_work(&port->work);
- }
-}
-
-/**
- * snd_fw_async_midi_port_finish - finish the asynchronous MIDI port
- * @port: the asynchronous MIDI port
- */
-static inline void
-snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
-{
- port->substream = NULL;
- port->error = false;
-}
-
#endif
diff --git a/sound/firewire/motu/Makefile b/sound/firewire/motu/Makefile
new file mode 100644
index 000000000000..728f586e754b
--- /dev/null
+++ b/sound/firewire/motu/Makefile
@@ -0,0 +1,6 @@
+CFLAGS_amdtp-motu.o := -I$(src)
+
+snd-firewire-motu-objs := motu.o amdtp-motu.o motu-transaction.o motu-stream.o \
+ motu-proc.o motu-pcm.o motu-midi.o motu-hwdep.o \
+ motu-protocol-v2.o motu-protocol-v3.o
+obj-$(CONFIG_SND_FIREWIRE_MOTU) += snd-firewire-motu.o
diff --git a/sound/firewire/motu/amdtp-motu-trace.h b/sound/firewire/motu/amdtp-motu-trace.h
new file mode 100644
index 000000000000..cd0cbfa9f96f
--- /dev/null
+++ b/sound/firewire/motu/amdtp-motu-trace.h
@@ -0,0 +1,123 @@
+/*
+ * amdtp-motu-trace.h - tracepoint definitions to dump a part of packet data
+ *
+ * Copyright (c) 2017 Takashi Sakamoto
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#undef TRACE_SYSTEM
+#define TRACE_SYSTEM snd_firewire_motu
+
+#if !defined(_SND_FIREWIRE_MOTU_TRACE_H) || defined(TRACE_HEADER_MULTI_READ)
+#define _SND_FIREWIRE_MOTU_TRACE_H
+
+#include <linux/tracepoint.h>
+
+static void copy_sph(u32 *frame, __be32 *buffer, unsigned int data_blocks,
+ unsigned int data_block_quadlets);
+static void copy_message(u64 *frames, __be32 *buffer, unsigned int data_blocks,
+ unsigned int data_block_quadlets);
+
+TRACE_EVENT(in_data_block_sph,
+ TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
+ TP_ARGS(s, data_blocks, buffer),
+ TP_STRUCT__entry(
+ __field(int, src)
+ __field(int, dst)
+ __field(unsigned int, data_blocks)
+ __dynamic_array(u32, tstamps, data_blocks)
+ ),
+ TP_fast_assign(
+ __entry->src = fw_parent_device(s->unit)->node_id;
+ __entry->dst = fw_parent_device(s->unit)->card->node_id;
+ __entry->data_blocks = data_blocks;
+ copy_sph(__get_dynamic_array(tstamps), buffer, data_blocks, s->data_block_quadlets);
+ ),
+ TP_printk(
+ "%04x %04x %u %s",
+ __entry->src,
+ __entry->dst,
+ __entry->data_blocks,
+ __print_array(__get_dynamic_array(tstamps), __entry->data_blocks, 4)
+ )
+);
+
+TRACE_EVENT(out_data_block_sph,
+ TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
+ TP_ARGS(s, data_blocks, buffer),
+ TP_STRUCT__entry(
+ __field(int, src)
+ __field(int, dst)
+ __field(unsigned int, data_blocks)
+ __dynamic_array(u32, tstamps, data_blocks)
+ ),
+ TP_fast_assign(
+ __entry->src = fw_parent_device(s->unit)->card->node_id;
+ __entry->dst = fw_parent_device(s->unit)->node_id;
+ __entry->data_blocks = data_blocks;
+ copy_sph(__get_dynamic_array(tstamps), buffer, data_blocks, s->data_block_quadlets);
+ ),
+ TP_printk(
+ "%04x %04x %u %s",
+ __entry->src,
+ __entry->dst,
+ __entry->data_blocks,
+ __print_array(__get_dynamic_array(tstamps), __entry->data_blocks, 4)
+ )
+);
+
+TRACE_EVENT(in_data_block_message,
+ TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
+ TP_ARGS(s, data_blocks, buffer),
+ TP_STRUCT__entry(
+ __field(int, src)
+ __field(int, dst)
+ __field(unsigned int, data_blocks)
+ __dynamic_array(u64, messages, data_blocks)
+ ),
+ TP_fast_assign(
+ __entry->src = fw_parent_device(s->unit)->node_id;
+ __entry->dst = fw_parent_device(s->unit)->card->node_id;
+ __entry->data_blocks = data_blocks;
+ copy_message(__get_dynamic_array(messages), buffer, data_blocks, s->data_block_quadlets);
+ ),
+ TP_printk(
+ "%04x %04x %u %s",
+ __entry->src,
+ __entry->dst,
+ __entry->data_blocks,
+ __print_array(__get_dynamic_array(messages), __entry->data_blocks, 8)
+ )
+);
+
+TRACE_EVENT(out_data_block_message,
+ TP_PROTO(struct amdtp_stream *s, unsigned int data_blocks, __be32 *buffer),
+ TP_ARGS(s, data_blocks, buffer),
+ TP_STRUCT__entry(
+ __field(int, src)
+ __field(int, dst)
+ __field(unsigned int, data_blocks)
+ __dynamic_array(u64, messages, data_blocks)
+ ),
+ TP_fast_assign(
+ __entry->src = fw_parent_device(s->unit)->card->node_id;
+ __entry->dst = fw_parent_device(s->unit)->node_id;
+ __entry->data_blocks = data_blocks;
+ copy_message(__get_dynamic_array(messages), buffer, data_blocks, s->data_block_quadlets);
+ ),
+ TP_printk(
+ "%04x %04x %u %s",
+ __entry->src,
+ __entry->dst,
+ __entry->data_blocks,
+ __print_array(__get_dynamic_array(messages), __entry->data_blocks, 8)
+ )
+);
+
+#endif
+
+#undef TRACE_INCLUDE_PATH
+#define TRACE_INCLUDE_PATH .
+#undef TRACE_INCLUDE_FILE
+#define TRACE_INCLUDE_FILE amdtp-motu-trace
+#include <trace/define_trace.h>
diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c
new file mode 100644
index 000000000000..96f0091144bb
--- /dev/null
+++ b/sound/firewire/motu/amdtp-motu.c
@@ -0,0 +1,427 @@
+/*
+ * amdtp-motu.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include "motu.h"
+
+#define CREATE_TRACE_POINTS
+#include "amdtp-motu-trace.h"
+
+#define CIP_FMT_MOTU 0x02
+#define CIP_FMT_MOTU_TX_V3 0x22
+#define MOTU_FDF_AM824 0x22
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND 3093
+
+struct amdtp_motu {
+ /* For timestamp processing. */
+ unsigned int quotient_ticks_per_event;
+ unsigned int remainder_ticks_per_event;
+ unsigned int next_ticks;
+ unsigned int next_accumulated;
+ unsigned int next_cycles;
+ unsigned int next_seconds;
+
+ unsigned int pcm_chunks;
+ unsigned int pcm_byte_offset;
+
+ struct snd_rawmidi_substream *midi;
+ unsigned int midi_ports;
+ unsigned int midi_flag_offset;
+ unsigned int midi_byte_offset;
+
+ int midi_db_count;
+ unsigned int midi_db_interval;
+};
+
+int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int midi_ports,
+ struct snd_motu_packet_format *formats)
+{
+ static const struct {
+ unsigned int quotient_ticks_per_event;
+ unsigned int remainder_ticks_per_event;
+ } params[] = {
+ [CIP_SFC_44100] = { 557, 123 },
+ [CIP_SFC_48000] = { 512, 0 },
+ [CIP_SFC_88200] = { 278, 282 },
+ [CIP_SFC_96000] = { 256, 0 },
+ [CIP_SFC_176400] = { 139, 141 },
+ [CIP_SFC_192000] = { 128, 0 },
+ };
+ struct amdtp_motu *p = s->protocol;
+ unsigned int pcm_chunks, data_chunks, data_block_quadlets;
+ unsigned int delay;
+ unsigned int mode;
+ int i, err;
+
+ if (amdtp_stream_running(s))
+ return -EBUSY;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ if (snd_motu_clock_rates[i] == rate) {
+ mode = i >> 1;
+ break;
+ }
+ }
+ if (i == ARRAY_SIZE(snd_motu_clock_rates))
+ return -EINVAL;
+
+ pcm_chunks = formats->fixed_part_pcm_chunks[mode] +
+ formats->differed_part_pcm_chunks[mode];
+ data_chunks = formats->msg_chunks + pcm_chunks;
+
+ /*
+ * Each data block includes SPH in its head. Data chunks follow with
+ * 3 byte alignment. Padding follows with zero to conform to quadlet
+ * alignment.
+ */
+ data_block_quadlets = 1 + DIV_ROUND_UP(data_chunks * 3, 4);
+
+ err = amdtp_stream_set_parameters(s, rate, data_block_quadlets);
+ if (err < 0)
+ return err;
+
+ p->pcm_chunks = pcm_chunks;
+ p->pcm_byte_offset = formats->pcm_byte_offset;
+
+ p->midi_ports = midi_ports;
+ p->midi_flag_offset = formats->midi_flag_offset;
+ p->midi_byte_offset = formats->midi_byte_offset;
+
+ p->midi_db_count = 0;
+ p->midi_db_interval = rate / MIDI_BYTES_PER_SECOND;
+
+ /* IEEE 1394 bus requires. */
+ delay = 0x2e00;
+
+ /* For no-data or empty packets to adjust PCM sampling frequency. */
+ delay += 8000 * 3072 * s->syt_interval / rate;
+
+ p->next_seconds = 0;
+ p->next_cycles = delay / 3072;
+ p->quotient_ticks_per_event = params[s->sfc].quotient_ticks_per_event;
+ p->remainder_ticks_per_event = params[s->sfc].remainder_ticks_per_event;
+ p->next_ticks = delay % 3072;
+ p->next_accumulated = 0;
+
+ return 0;
+}
+
+static void read_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime,
+ __be32 *buffer, unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ unsigned int channels, remaining_frames, i, c;
+ u8 *byte;
+ u32 *dst;
+
+ channels = p->pcm_chunks;
+ dst = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < data_blocks; ++i) {
+ byte = (u8 *)buffer + p->pcm_byte_offset;
+
+ for (c = 0; c < channels; ++c) {
+ *dst = (byte[0] << 24) | (byte[1] << 16) | byte[2];
+ byte += 3;
+ dst++;
+ }
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ dst = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_s32(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime,
+ __be32 *buffer, unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ unsigned int channels, remaining_frames, i, c;
+ u8 *byte;
+ const u32 *src;
+
+ channels = p->pcm_chunks;
+ src = (void *)runtime->dma_area +
+ frames_to_bytes(runtime, s->pcm_buffer_pointer);
+ remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+ for (i = 0; i < data_blocks; ++i) {
+ byte = (u8 *)buffer + p->pcm_byte_offset;
+
+ for (c = 0; c < channels; ++c) {
+ byte[0] = (*src >> 24) & 0xff;
+ byte[1] = (*src >> 16) & 0xff;
+ byte[2] = (*src >> 8) & 0xff;
+ byte += 3;
+ src++;
+ }
+
+ buffer += s->data_block_quadlets;
+ if (--remaining_frames == 0)
+ src = (void *)runtime->dma_area;
+ }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ unsigned int channels, i, c;
+ u8 *byte;
+
+ channels = p->pcm_chunks;
+
+ for (i = 0; i < data_blocks; ++i) {
+ byte = (u8 *)buffer + p->pcm_byte_offset;
+
+ for (c = 0; c < channels; ++c) {
+ byte[0] = 0;
+ byte[1] = 0;
+ byte[2] = 0;
+ byte += 3;
+ }
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime)
+{
+ int err;
+
+ /* TODO: how to set an constraint for exactly 24bit PCM sample? */
+ err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+ if (err < 0)
+ return err;
+
+ return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi)
+{
+ struct amdtp_motu *p = s->protocol;
+
+ if (port < p->midi_ports)
+ WRITE_ONCE(p->midi, midi);
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ struct snd_rawmidi_substream *midi = READ_ONCE(p->midi);
+ u8 *b;
+ int i;
+
+ for (i = 0; i < data_blocks; i++) {
+ b = (u8 *)buffer;
+
+ if (midi && p->midi_db_count == 0 &&
+ snd_rawmidi_transmit(midi, b + p->midi_byte_offset, 1) == 1) {
+ b[p->midi_flag_offset] = 0x01;
+ } else {
+ b[p->midi_byte_offset] = 0x00;
+ b[p->midi_flag_offset] = 0x00;
+ }
+
+ buffer += s->data_block_quadlets;
+
+ if (--p->midi_db_count < 0)
+ p->midi_db_count = p->midi_db_interval;
+ }
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ struct snd_rawmidi_substream *midi;
+ u8 *b;
+ int i;
+
+ for (i = 0; i < data_blocks; i++) {
+ b = (u8 *)buffer;
+ midi = READ_ONCE(p->midi);
+
+ if (midi && (b[p->midi_flag_offset] & 0x01))
+ snd_rawmidi_receive(midi, b + p->midi_byte_offset, 1);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+/* For tracepoints. */
+static void __maybe_unused copy_sph(u32 *frames, __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int data_block_quadlets)
+{
+ unsigned int i;
+
+ for (i = 0; i < data_blocks; ++i) {
+ *frames = be32_to_cpu(*buffer);
+ buffer += data_block_quadlets;
+ frames++;
+ }
+}
+
+/* For tracepoints. */
+static void __maybe_unused copy_message(u64 *frames, __be32 *buffer,
+ unsigned int data_blocks,
+ unsigned int data_block_quadlets)
+{
+ unsigned int i;
+
+ /* This is just for v2/v3 protocol. */
+ for (i = 0; i < data_blocks; ++i) {
+ *frames = (be32_to_cpu(buffer[1]) << 16) |
+ (be32_to_cpu(buffer[2]) >> 16);
+ buffer += data_block_quadlets;
+ frames++;
+ }
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_motu *p = s->protocol;
+ struct snd_pcm_substream *pcm;
+
+ trace_in_data_block_sph(s, data_blocks, buffer);
+ trace_in_data_block_message(s, data_blocks, buffer);
+
+ if (p->midi_ports)
+ read_midi_messages(s, buffer, data_blocks);
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (data_blocks > 0 && pcm)
+ read_pcm_s32(s, pcm->runtime, buffer, data_blocks);
+
+ return data_blocks;
+}
+
+static inline void compute_next_elapse_from_start(struct amdtp_motu *p)
+{
+ p->next_accumulated += p->remainder_ticks_per_event;
+ if (p->next_accumulated >= 441) {
+ p->next_accumulated -= 441;
+ p->next_ticks++;
+ }
+
+ p->next_ticks += p->quotient_ticks_per_event;
+ if (p->next_ticks >= 3072) {
+ p->next_ticks -= 3072;
+ p->next_cycles++;
+ }
+
+ if (p->next_cycles >= 8000) {
+ p->next_cycles -= 8000;
+ p->next_seconds++;
+ }
+
+ if (p->next_seconds >= 128)
+ p->next_seconds -= 128;
+}
+
+static void write_sph(struct amdtp_stream *s, __be32 *buffer,
+ unsigned int data_blocks)
+{
+ struct amdtp_motu *p = s->protocol;
+ unsigned int next_cycles;
+ unsigned int i;
+ u32 sph;
+
+ for (i = 0; i < data_blocks; i++) {
+ next_cycles = (s->start_cycle + p->next_cycles) % 8000;
+ sph = ((next_cycles << 12) | p->next_ticks) & 0x01ffffff;
+ *buffer = cpu_to_be32(sph);
+
+ compute_next_elapse_from_start(p);
+
+ buffer += s->data_block_quadlets;
+ }
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
+ __be32 *buffer, unsigned int data_blocks,
+ unsigned int *syt)
+{
+ struct amdtp_motu *p = (struct amdtp_motu *)s->protocol;
+ struct snd_pcm_substream *pcm;
+
+ /* Not used. */
+ *syt = 0xffff;
+
+ /* TODO: how to interact control messages between userspace? */
+
+ if (p->midi_ports)
+ write_midi_messages(s, buffer, data_blocks);
+
+ pcm = ACCESS_ONCE(s->pcm);
+ if (pcm)
+ write_pcm_s32(s, pcm->runtime, buffer, data_blocks);
+ else
+ write_pcm_silence(s, buffer, data_blocks);
+
+ write_sph(s, buffer, data_blocks);
+
+ trace_out_data_block_sph(s, data_blocks, buffer);
+ trace_out_data_block_message(s, data_blocks, buffer);
+
+ return data_blocks;
+}
+
+int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir,
+ const struct snd_motu_protocol *const protocol)
+{
+ amdtp_stream_process_data_blocks_t process_data_blocks;
+ int fmt = CIP_FMT_MOTU;
+ int flags = CIP_BLOCKING;
+ int err;
+
+ if (dir == AMDTP_IN_STREAM) {
+ process_data_blocks = process_tx_data_blocks;
+
+ /*
+ * Units of version 3 transmits packets with invalid CIP header
+ * against IEC 61883-1.
+ */
+ if (protocol == &snd_motu_protocol_v3) {
+ flags |= CIP_WRONG_DBS |
+ CIP_SKIP_DBC_ZERO_CHECK |
+ CIP_HEADER_WITHOUT_EOH;
+ fmt = CIP_FMT_MOTU_TX_V3;
+ }
+ } else {
+ process_data_blocks = process_rx_data_blocks;
+ flags |= CIP_DBC_IS_END_EVENT;
+ }
+
+ err = amdtp_stream_init(s, unit, dir, flags, fmt, process_data_blocks,
+ sizeof(struct amdtp_motu));
+ if (err < 0)
+ return err;
+
+ s->sph = 1;
+ s->fdf = MOTU_FDF_AM824;
+
+ return 0;
+}
diff --git a/sound/firewire/motu/motu-hwdep.c b/sound/firewire/motu/motu-hwdep.c
new file mode 100644
index 000000000000..b87ccb69d597
--- /dev/null
+++ b/sound/firewire/motu/motu-hwdep.c
@@ -0,0 +1,198 @@
+/*
+ * motu-hwdep.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes have five functionalities.
+ *
+ * 1.get information about firewire node
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock streaming
+ *
+ */
+
+#include "motu.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+ loff_t *offset)
+{
+ struct snd_motu *motu = hwdep->private_data;
+ DEFINE_WAIT(wait);
+ union snd_firewire_event event;
+
+ spin_lock_irq(&motu->lock);
+
+ while (!motu->dev_lock_changed && motu->msg == 0) {
+ prepare_to_wait(&motu->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+ spin_unlock_irq(&motu->lock);
+ schedule();
+ finish_wait(&motu->hwdep_wait, &wait);
+ if (signal_pending(current))
+ return -ERESTARTSYS;
+ spin_lock_irq(&motu->lock);
+ }
+
+ memset(&event, 0, sizeof(event));
+ if (motu->dev_lock_changed) {
+ event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+ event.lock_status.status = (motu->dev_lock_count > 0);
+ motu->dev_lock_changed = false;
+
+ count = min_t(long, count, sizeof(event.lock_status));
+ } else {
+ event.motu_notification.type = SNDRV_FIREWIRE_EVENT_MOTU_NOTIFICATION;
+ event.motu_notification.message = motu->msg;
+ motu->msg = 0;
+
+ count = min_t(long, count, sizeof(event.motu_notification));
+ }
+
+ spin_unlock_irq(&motu->lock);
+
+ if (copy_to_user(buf, &event, count))
+ return -EFAULT;
+
+ return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+ poll_table *wait)
+{
+ struct snd_motu *motu = hwdep->private_data;
+ unsigned int events;
+
+ poll_wait(file, &motu->hwdep_wait, wait);
+
+ spin_lock_irq(&motu->lock);
+ if (motu->dev_lock_changed || motu->msg)
+ events = POLLIN | POLLRDNORM;
+ else
+ events = 0;
+ spin_unlock_irq(&motu->lock);
+
+ return events | POLLOUT;
+}
+
+static int hwdep_get_info(struct snd_motu *motu, void __user *arg)
+{
+ struct fw_device *dev = fw_parent_device(motu->unit);
+ struct snd_firewire_get_info info;
+
+ memset(&info, 0, sizeof(info));
+ info.type = SNDRV_FIREWIRE_TYPE_MOTU;
+ info.card = dev->card->index;
+ *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+ *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+ strlcpy(info.device_name, dev_name(&dev->device),
+ sizeof(info.device_name));
+
+ if (copy_to_user(arg, &info, sizeof(info)))
+ return -EFAULT;
+
+ return 0;
+}
+
+static int hwdep_lock(struct snd_motu *motu)
+{
+ int err;
+
+ spin_lock_irq(&motu->lock);
+
+ if (motu->dev_lock_count == 0) {
+ motu->dev_lock_count = -1;
+ err = 0;
+ } else {
+ err = -EBUSY;
+ }
+
+ spin_unlock_irq(&motu->lock);
+
+ return err;
+}
+
+static int hwdep_unlock(struct snd_motu *motu)
+{
+ int err;
+
+ spin_lock_irq(&motu->lock);
+
+ if (motu->dev_lock_count == -1) {
+ motu->dev_lock_count = 0;
+ err = 0;
+ } else {
+ err = -EBADFD;
+ }
+
+ spin_unlock_irq(&motu->lock);
+
+ return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+ struct snd_motu *motu = hwdep->private_data;
+
+ spin_lock_irq(&motu->lock);
+ if (motu->dev_lock_count == -1)
+ motu->dev_lock_count = 0;
+ spin_unlock_irq(&motu->lock);
+
+ return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ struct snd_motu *motu = hwdep->private_data;
+
+ switch (cmd) {
+ case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+ return hwdep_get_info(motu, (void __user *)arg);
+ case SNDRV_FIREWIRE_IOCTL_LOCK:
+ return hwdep_lock(motu);
+ case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+ return hwdep_unlock(motu);
+ default:
+ return -ENOIOCTLCMD;
+ }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+ unsigned int cmd, unsigned long arg)
+{
+ return hwdep_ioctl(hwdep, file, cmd,
+ (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+int snd_motu_create_hwdep_device(struct snd_motu *motu)
+{
+ static const struct snd_hwdep_ops ops = {
+ .read = hwdep_read,
+ .release = hwdep_release,
+ .poll = hwdep_poll,
+ .ioctl = hwdep_ioctl,
+ .ioctl_compat = hwdep_compat_ioctl,
+ };
+ struct snd_hwdep *hwdep;
+ int err;
+
+ err = snd_hwdep_new(motu->card, motu->card->driver, 0, &hwdep);
+ if (err < 0)
+ return err;
+
+ strcpy(hwdep->name, "MOTU");
+ hwdep->iface = SNDRV_HWDEP_IFACE_FW_MOTU;
+ hwdep->ops = ops;
+ hwdep->private_data = motu;
+ hwdep->exclusive = true;
+
+ return 0;
+}
diff --git a/sound/firewire/motu/motu-midi.c b/sound/firewire/motu/motu-midi.c
new file mode 100644
index 000000000000..e3acfcc53f4e
--- /dev/null
+++ b/sound/firewire/motu/motu-midi.c
@@ -0,0 +1,169 @@
+/*
+ * motu-midi.h - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+#include "motu.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_motu *motu = substream->rmidi->private_data;
+ int err;
+
+ err = snd_motu_stream_lock_try(motu);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&motu->mutex);
+
+ motu->capture_substreams++;
+ err = snd_motu_stream_start_duplex(motu, 0);
+
+ mutex_unlock(&motu->mutex);
+
+ if (err < 0)
+ snd_motu_stream_lock_release(motu);
+
+ return err;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+ struct snd_motu *motu = substream->rmidi->private_data;
+ int err;
+
+ err = snd_motu_stream_lock_try(motu);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&motu->mutex);
+
+ motu->playback_substreams++;
+ err = snd_motu_stream_start_duplex(motu, 0);
+
+ mutex_unlock(&motu->mutex);
+
+ if (err < 0)
+ snd_motu_stream_lock_release(motu);
+
+ return err;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_motu *motu = substream->rmidi->private_data;
+
+ mutex_lock(&motu->mutex);
+
+ motu->capture_substreams--;
+ snd_motu_stream_stop_duplex(motu);
+
+ mutex_unlock(&motu->mutex);
+
+ snd_motu_stream_lock_release(motu);
+ return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+ struct snd_motu *motu = substream->rmidi->private_data;
+
+ mutex_lock(&motu->mutex);
+
+ motu->playback_substreams--;
+ snd_motu_stream_stop_duplex(motu);
+
+ mutex_unlock(&motu->mutex);
+
+ snd_motu_stream_lock_release(motu);
+ return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_motu *motu = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&motu->lock, flags);
+
+ if (up)
+ amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
+ substrm);
+ else
+ amdtp_motu_midi_trigger(&motu->tx_stream, substrm->number,
+ NULL);
+
+ spin_unlock_irqrestore(&motu->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+ struct snd_motu *motu = substrm->rmidi->private_data;
+ unsigned long flags;
+
+ spin_lock_irqsave(&motu->lock, flags);
+
+ if (up)
+ amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
+ substrm);
+ else
+ amdtp_motu_midi_trigger(&motu->rx_stream, substrm->number,
+ NULL);
+
+ spin_unlock_irqrestore(&motu->lock, flags);
+}
+
+static void set_midi_substream_names(struct snd_motu *motu,
+ struct snd_rawmidi_str *str)
+{
+ struct snd_rawmidi_substream *subs;
+
+ list_for_each_entry(subs, &str->substreams, list) {
+ snprintf(subs->name, sizeof(subs->name),
+ "%s MIDI %d", motu->card->shortname, subs->number + 1);
+ }
+}
+
+int snd_motu_create_midi_devices(struct snd_motu *motu)
+{
+ static struct snd_rawmidi_ops capture_ops = {
+ .open = midi_capture_open,
+ .close = midi_capture_close,
+ .trigger = midi_capture_trigger,
+ };
+ static struct snd_rawmidi_ops playback_ops = {
+ .open = midi_playback_open,
+ .close = midi_playback_close,
+ .trigger = midi_playback_trigger,
+ };
+ struct snd_rawmidi *rmidi;
+ struct snd_rawmidi_str *str;
+ int err;
+
+ /* create midi ports */
+ err = snd_rawmidi_new(motu->card, motu->card->driver, 0, 1, 1, &rmidi);
+ if (err < 0)
+ return err;
+
+ snprintf(rmidi->name, sizeof(rmidi->name),
+ "%s MIDI", motu->card->shortname);
+ rmidi->private_data = motu;
+
+ rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT |
+ SNDRV_RAWMIDI_INFO_OUTPUT |
+ SNDRV_RAWMIDI_INFO_DUPLEX;
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+ &capture_ops);
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+ set_midi_substream_names(motu, str);
+
+ snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+ &playback_ops);
+ str = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+ set_midi_substream_names(motu, str);
+
+ return 0;
+}
diff --git a/sound/firewire/motu/motu-pcm.c b/sound/firewire/motu/motu-pcm.c
new file mode 100644
index 000000000000..94558f3d218b
--- /dev/null
+++ b/sound/firewire/motu/motu-pcm.c
@@ -0,0 +1,398 @@
+/*
+ * motu-pcm.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/pcm_params.h>
+#include "motu.h"
+
+static int motu_rate_constraint(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_motu_packet_format *formats = rule->private;
+
+ const struct snd_interval *c =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval *r =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval rates = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, pcm_channels, rate, mode;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ rate = snd_motu_clock_rates[i];
+ mode = i / 2;
+
+ pcm_channels = formats->fixed_part_pcm_chunks[mode] +
+ formats->differed_part_pcm_chunks[mode];
+ if (!snd_interval_test(c, pcm_channels))
+ continue;
+
+ rates.min = min(rates.min, rate);
+ rates.max = max(rates.max, rate);
+ }
+
+ return snd_interval_refine(r, &rates);
+}
+
+static int motu_channels_constraint(struct snd_pcm_hw_params *params,
+ struct snd_pcm_hw_rule *rule)
+{
+ struct snd_motu_packet_format *formats = rule->private;
+
+ const struct snd_interval *r =
+ hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *c =
+ hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+ struct snd_interval channels = {
+ .min = UINT_MAX, .max = 0, .integer = 1
+ };
+ unsigned int i, pcm_channels, rate, mode;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ rate = snd_motu_clock_rates[i];
+ mode = i / 2;
+
+ if (!snd_interval_test(r, rate))
+ continue;
+
+ pcm_channels = formats->fixed_part_pcm_chunks[mode] +
+ formats->differed_part_pcm_chunks[mode];
+ channels.min = min(channels.min, pcm_channels);
+ channels.max = max(channels.max, pcm_channels);
+ }
+
+ return snd_interval_refine(c, &channels);
+}
+
+static void limit_channels_and_rates(struct snd_motu *motu,
+ struct snd_pcm_runtime *runtime,
+ struct snd_motu_packet_format *formats)
+{
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ unsigned int i, pcm_channels, rate, mode;
+
+ hw->channels_min = UINT_MAX;
+ hw->channels_max = 0;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ rate = snd_motu_clock_rates[i];
+ mode = i / 2;
+
+ pcm_channels = formats->fixed_part_pcm_chunks[mode] +
+ formats->differed_part_pcm_chunks[mode];
+ if (pcm_channels == 0)
+ continue;
+
+ hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+ hw->channels_min = min(hw->channels_min, pcm_channels);
+ hw->channels_max = max(hw->channels_max, pcm_channels);
+ }
+
+ snd_pcm_limit_hw_rates(runtime);
+}
+
+static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
+{
+ hw->periods_min = 2; /* SNDRV_PCM_INFO_BATCH */
+ hw->periods_max = UINT_MAX;
+
+ hw->period_bytes_min = 4 * hw->channels_max; /* byte for a frame */
+
+ /* Just to prevent from allocating much pages. */
+ hw->period_bytes_max = hw->period_bytes_min * 2048;
+ hw->buffer_bytes_max = hw->period_bytes_max * hw->periods_min;
+}
+
+static int init_hw_info(struct snd_motu *motu,
+ struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+ struct snd_pcm_hardware *hw = &runtime->hw;
+ struct amdtp_stream *stream;
+ struct snd_motu_packet_format *formats;
+ int err;
+
+ hw->info = SNDRV_PCM_INFO_MMAP |
+ SNDRV_PCM_INFO_MMAP_VALID |
+ SNDRV_PCM_INFO_BATCH |
+ SNDRV_PCM_INFO_INTERLEAVED |
+ SNDRV_PCM_INFO_JOINT_DUPLEX |
+ SNDRV_PCM_INFO_BLOCK_TRANSFER;
+
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+ hw->formats = SNDRV_PCM_FMTBIT_S32;
+ stream = &motu->tx_stream;
+ formats = &motu->tx_packet_formats;
+ } else {
+ hw->formats = SNDRV_PCM_FMTBIT_S32;
+ stream = &motu->rx_stream;
+ formats = &motu->rx_packet_formats;
+ }
+
+ limit_channels_and_rates(motu, runtime, formats);
+ limit_period_and_buffer(hw);
+
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
+ motu_rate_constraint, formats,
+ SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+ if (err < 0)
+ return err;
+ err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
+ motu_channels_constraint, formats,
+ SNDRV_PCM_HW_PARAM_RATE, -1);
+ if (err < 0)
+ return err;
+
+ return amdtp_motu_add_pcm_hw_constraints(stream, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+ const struct snd_motu_protocol *const protocol = motu->spec->protocol;
+ enum snd_motu_clock_source src;
+ unsigned int rate;
+ int err;
+
+ err = snd_motu_stream_lock_try(motu);
+ if (err < 0)
+ return err;
+
+ mutex_lock(&motu->mutex);
+
+ err = protocol->cache_packet_formats(motu);
+ if (err < 0)
+ goto err_locked;
+
+ err = init_hw_info(motu, substream);
+ if (err < 0)
+ goto err_locked;
+
+ /*
+ * When source of clock is not internal or any PCM streams are running,
+ * available sampling rate is limited at current sampling rate.
+ */
+ err = protocol->get_clock_source(motu, &src);
+ if (err < 0)
+ goto err_locked;
+ if (src != SND_MOTU_CLOCK_SOURCE_INTERNAL ||
+ amdtp_stream_pcm_running(&motu->tx_stream) ||
+ amdtp_stream_pcm_running(&motu->rx_stream)) {
+ err = protocol->get_clock_rate(motu, &rate);
+ if (err < 0)
+ goto err_locked;
+ substream->runtime->hw.rate_min = rate;
+ substream->runtime->hw.rate_max = rate;
+ }
+
+ snd_pcm_set_sync(substream);
+
+ mutex_unlock(&motu->mutex);
+
+ return err;
+err_locked:
+ mutex_unlock(&motu->mutex);
+ snd_motu_stream_lock_release(motu);
+ return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ snd_motu_stream_lock_release(motu);
+
+ return 0;
+}
+
+static int capture_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_motu *motu = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&motu->mutex);
+ motu->capture_substreams++;
+ mutex_unlock(&motu->mutex);
+ }
+
+ return 0;
+}
+static int playback_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *hw_params)
+{
+ struct snd_motu *motu = substream->private_data;
+ int err;
+
+ err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+ params_buffer_bytes(hw_params));
+ if (err < 0)
+ return err;
+
+ if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+ mutex_lock(&motu->mutex);
+ motu->playback_substreams++;
+ mutex_unlock(&motu->mutex);
+ }
+
+ return 0;
+}
+
+static int capture_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ mutex_lock(&motu->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ motu->capture_substreams--;
+
+ snd_motu_stream_stop_duplex(motu);
+
+ mutex_unlock(&motu->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int playback_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ mutex_lock(&motu->mutex);
+
+ if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+ motu->playback_substreams--;
+
+ snd_motu_stream_stop_duplex(motu);
+
+ mutex_unlock(&motu->mutex);
+
+ return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int capture_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+ int err;
+
+ mutex_lock(&motu->mutex);
+ err = snd_motu_stream_start_duplex(motu, substream->runtime->rate);
+ mutex_unlock(&motu->mutex);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&motu->tx_stream);
+
+ return 0;
+}
+static int playback_prepare(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+ int err;
+
+ mutex_lock(&motu->mutex);
+ err = snd_motu_stream_start_duplex(motu, substream->runtime->rate);
+ mutex_unlock(&motu->mutex);
+ if (err >= 0)
+ amdtp_stream_pcm_prepare(&motu->rx_stream);
+
+ return err;
+}
+
+static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&motu->tx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&motu->tx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ amdtp_stream_pcm_trigger(&motu->rx_stream, substream);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ amdtp_stream_pcm_trigger(&motu->rx_stream, NULL);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ return amdtp_stream_pcm_pointer(&motu->tx_stream);
+}
+static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
+{
+ struct snd_motu *motu = substream->private_data;
+
+ return amdtp_stream_pcm_pointer(&motu->rx_stream);
+}
+
+int snd_motu_create_pcm_devices(struct snd_motu *motu)
+{
+ static struct snd_pcm_ops capture_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = capture_hw_params,
+ .hw_free = capture_hw_free,
+ .prepare = capture_prepare,
+ .trigger = capture_trigger,
+ .pointer = capture_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+ };
+ static struct snd_pcm_ops playback_ops = {
+ .open = pcm_open,
+ .close = pcm_close,
+ .ioctl = snd_pcm_lib_ioctl,
+ .hw_params = playback_hw_params,
+ .hw_free = playback_hw_free,
+ .prepare = playback_prepare,
+ .trigger = playback_trigger,
+ .pointer = playback_pointer,
+ .page = snd_pcm_lib_get_vmalloc_page,
+ .mmap = snd_pcm_lib_mmap_vmalloc,
+ };
+ struct snd_pcm *pcm;
+ int err;
+
+ err = snd_pcm_new(motu->card, motu->card->driver, 0, 1, 1, &pcm);
+ if (err < 0)
+ return err;
+ pcm->private_data = motu;
+ strcpy(pcm->name, motu->card->shortname);
+
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+ snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+
+ return 0;
+}
diff --git a/sound/firewire/motu/motu-proc.c b/sound/firewire/motu/motu-proc.c
new file mode 100644
index 000000000000..4edc064999ed
--- /dev/null
+++ b/sound/firewire/motu/motu-proc.c
@@ -0,0 +1,118 @@
+/*
+ * motu-proc.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./motu.h"
+
+static const char *const clock_names[] = {
+ [SND_MOTU_CLOCK_SOURCE_INTERNAL] = "Internal",
+ [SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB] = "ADAT on Dsub-9pin interface",
+ [SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT] = "ADAT on optical interface",
+ [SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A] = "ADAT on optical interface A",
+ [SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B] = "ADAT on optical interface B",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT] = "S/PDIF on optical interface",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A] = "S/PDIF on optical interface A",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B] = "S/PDIF on optical interface B",
+ [SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX] = "S/PCIF on coaxial interface",
+ [SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR] = "AESEBU on XLR interface",
+ [SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC] = "Word clock on BNC interface",
+};
+
+static void proc_read_clock(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+
+ struct snd_motu *motu = entry->private_data;
+ const struct snd_motu_protocol *const protocol = motu->spec->protocol;
+ unsigned int rate;
+ enum snd_motu_clock_source source;
+
+ if (protocol->get_clock_rate(motu, &rate) < 0)
+ return;
+ if (protocol->get_clock_source(motu, &source) < 0)
+ return;
+
+ snd_iprintf(buffer, "Rate:\t%d\n", rate);
+ snd_iprintf(buffer, "Source:\t%s\n", clock_names[source]);
+}
+
+static void proc_read_format(struct snd_info_entry *entry,
+ struct snd_info_buffer *buffer)
+{
+ struct snd_motu *motu = entry->private_data;
+ const struct snd_motu_protocol *const protocol = motu->spec->protocol;
+ unsigned int mode;
+ struct snd_motu_packet_format *formats;
+ int i;
+
+ if (protocol->cache_packet_formats(motu) < 0)
+ return;
+
+ snd_iprintf(buffer, "tx:\tmsg\tfixed\tdiffered\n");
+ for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
+ mode = i >> 1;
+
+ formats = &motu->tx_packet_formats;
+ snd_iprintf(buffer,
+ "%u:\t%u\t%u\t%u\n",
+ snd_motu_clock_rates[i],
+ formats->msg_chunks,
+ formats->fixed_part_pcm_chunks[mode],
+ formats->differed_part_pcm_chunks[mode]);
+ }
+
+ snd_iprintf(buffer, "rx:\tmsg\tfixed\tdiffered\n");
+ for (i = 0; i < SND_MOTU_CLOCK_RATE_COUNT; ++i) {
+ mode = i >> 1;
+
+ formats = &motu->rx_packet_formats;
+ snd_iprintf(buffer,
+ "%u:\t%u\t%u\t%u\n",
+ snd_motu_clock_rates[i],
+ formats->msg_chunks,
+ formats->fixed_part_pcm_chunks[mode],
+ formats->differed_part_pcm_chunks[mode]);
+ }
+}
+
+static void add_node(struct snd_motu *motu, struct snd_info_entry *root,
+ const char *name,
+ void (*op)(struct snd_info_entry *e,
+ struct snd_info_buffer *b))
+{
+ struct snd_info_entry *entry;
+
+ entry = snd_info_create_card_entry(motu->card, name, root);
+ if (entry == NULL)
+ return;
+
+ snd_info_set_text_ops(entry, motu, op);
+ if (snd_info_register(entry) < 0)
+ snd_info_free_entry(entry);
+}
+
+void snd_motu_proc_init(struct snd_motu *motu)
+{
+ struct snd_info_entry *root;
+
+ /*
+ * All nodes are automatically removed at snd_card_disconnect(),
+ * by following to link list.
+ */
+ root = snd_info_create_card_entry(motu->card, "firewire",
+ motu->card->proc_root);
+ if (root == NULL)
+ return;
+ root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+ if (snd_info_register(root) < 0) {
+ snd_info_free_entry(root);
+ return;
+ }
+
+ add_node(motu, root, "clock", proc_read_clock);
+ add_node(motu, root, "format", proc_read_format);
+}
diff --git a/sound/firewire/motu/motu-protocol-v2.c b/sound/firewire/motu/motu-protocol-v2.c
new file mode 100644
index 000000000000..05b5d287c2f3
--- /dev/null
+++ b/sound/firewire/motu/motu-protocol-v2.c
@@ -0,0 +1,237 @@
+/*
+ * motu-protocol-v2.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "motu.h"
+
+#define V2_CLOCK_STATUS_OFFSET 0x0b14
+#define V2_CLOCK_RATE_MASK 0x00000038
+#define V2_CLOCK_RATE_SHIFT 3
+#define V2_CLOCK_SRC_MASK 0x00000007
+#define V2_CLOCK_SRC_SHIFT 0
+
+#define V2_IN_OUT_CONF_OFFSET 0x0c04
+#define V2_OPT_OUT_IFACE_MASK 0x00000c00
+#define V2_OPT_OUT_IFACE_SHIFT 10
+#define V2_OPT_IN_IFACE_MASK 0x00000300
+#define V2_OPT_IN_IFACE_SHIFT 8
+#define V2_OPT_IFACE_MODE_NONE 0
+#define V2_OPT_IFACE_MODE_ADAT 1
+#define V2_OPT_IFACE_MODE_SPDIF 2
+
+static int v2_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
+{
+ __be32 reg;
+ unsigned int index;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ index = (be32_to_cpu(reg) & V2_CLOCK_RATE_MASK) >> V2_CLOCK_RATE_SHIFT;
+ if (index >= ARRAY_SIZE(snd_motu_clock_rates))
+ return -EIO;
+
+ *rate = snd_motu_clock_rates[index];
+
+ return 0;
+}
+
+static int v2_set_clock_rate(struct snd_motu *motu, unsigned int rate)
+{
+ __be32 reg;
+ u32 data;
+ int i;
+ int err;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ if (snd_motu_clock_rates[i] == rate)
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_motu_clock_rates))
+ return -EINVAL;
+
+ err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~V2_CLOCK_RATE_MASK;
+ data |= i << V2_CLOCK_RATE_SHIFT;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+}
+
+static int v2_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ unsigned int index;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V2_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ index = be32_to_cpu(reg) & V2_CLOCK_SRC_MASK;
+ if (index > 5)
+ return -EIO;
+
+ /* To check the configuration of optical interface. */
+ err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ switch (index) {
+ case 0:
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ break;
+ case 1:
+ if (be32_to_cpu(reg) & 0x00000200)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT;
+ break;
+ case 2:
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ break;
+ case 4:
+ *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+ break;
+ case 5:
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB;
+ break;
+ default:
+ return -EIO;
+ }
+
+ return 0;
+}
+
+static int v2_switch_fetching_mode(struct snd_motu *motu, bool enable)
+{
+ /* V2 protocol doesn't have this feature. */
+ return 0;
+}
+
+static void calculate_fixed_part(struct snd_motu_packet_format *formats,
+ enum amdtp_stream_direction dir,
+ enum snd_motu_spec_flags flags,
+ unsigned char analog_ports)
+{
+ unsigned char pcm_chunks[3] = {0, 0, 0};
+
+ formats->msg_chunks = 2;
+
+ pcm_chunks[0] = analog_ports;
+ pcm_chunks[1] = analog_ports;
+ if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+ pcm_chunks[2] = analog_ports;
+
+ if (dir == AMDTP_IN_STREAM) {
+ if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
+ pcm_chunks[0] += 2;
+ pcm_chunks[1] += 2;
+ }
+ if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
+ pcm_chunks[0] += 2;
+ pcm_chunks[1] += 2;
+ }
+ } else {
+ /*
+ * Packets to v2 units transfer main-out-1/2 and phone-out-1/2.
+ */
+ pcm_chunks[0] += 4;
+ pcm_chunks[1] += 4;
+ }
+
+ /*
+ * All of v2 models have a pair of coaxial interfaces for digital in/out
+ * port. At 44.1/48.0/88.2/96.0 kHz, packets includes PCM from these
+ * ports.
+ */
+ pcm_chunks[0] += 2;
+ pcm_chunks[1] += 2;
+
+ /* This part should be multiples of 4. */
+ formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2;
+ formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2;
+ if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+ formats->fixed_part_pcm_chunks[2] =
+ round_up(2 + pcm_chunks[2], 4) - 2;
+}
+
+static void calculate_differed_part(struct snd_motu_packet_format *formats,
+ enum snd_motu_spec_flags flags,
+ u32 data, u32 mask, u32 shift)
+{
+ unsigned char pcm_chunks[3] = {0, 0};
+
+ /*
+ * When optical interfaces are configured for S/PDIF (TOSLINK),
+ * the above PCM frames come from them, instead of coaxial
+ * interfaces.
+ */
+ data = (data & mask) >> shift;
+ if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) &&
+ data == V2_OPT_IFACE_MODE_ADAT) {
+ pcm_chunks[0] += 8;
+ pcm_chunks[1] += 4;
+ }
+
+ /* At mode x4, no data chunks are supported in this part. */
+ formats->differed_part_pcm_chunks[0] = pcm_chunks[0];
+ formats->differed_part_pcm_chunks[1] = pcm_chunks[1];
+}
+
+static int v2_cache_packet_formats(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V2_IN_OUT_CONF_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
+ motu->spec->flags, motu->spec->analog_in_ports);
+ calculate_differed_part(&motu->tx_packet_formats, motu->spec->flags,
+ data, V2_OPT_IN_IFACE_MASK, V2_OPT_IN_IFACE_SHIFT);
+
+ calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
+ motu->spec->flags, motu->spec->analog_out_ports);
+ calculate_differed_part(&motu->rx_packet_formats, motu->spec->flags,
+ data, V2_OPT_OUT_IFACE_MASK, V2_OPT_OUT_IFACE_SHIFT);
+
+ motu->tx_packet_formats.midi_flag_offset = 4;
+ motu->tx_packet_formats.midi_byte_offset = 6;
+ motu->tx_packet_formats.pcm_byte_offset = 10;
+
+ motu->rx_packet_formats.midi_flag_offset = 4;
+ motu->rx_packet_formats.midi_byte_offset = 6;
+ motu->rx_packet_formats.pcm_byte_offset = 10;
+
+ return 0;
+}
+
+const struct snd_motu_protocol snd_motu_protocol_v2 = {
+ .get_clock_rate = v2_get_clock_rate,
+ .set_clock_rate = v2_set_clock_rate,
+ .get_clock_source = v2_get_clock_source,
+ .switch_fetching_mode = v2_switch_fetching_mode,
+ .cache_packet_formats = v2_cache_packet_formats,
+};
diff --git a/sound/firewire/motu/motu-protocol-v3.c b/sound/firewire/motu/motu-protocol-v3.c
new file mode 100644
index 000000000000..ddb647254ed2
--- /dev/null
+++ b/sound/firewire/motu/motu-protocol-v3.c
@@ -0,0 +1,311 @@
+/*
+ * motu-protocol-v3.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/delay.h>
+#include "motu.h"
+
+#define V3_CLOCK_STATUS_OFFSET 0x0b14
+#define V3_FETCH_PCM_FRAMES 0x02000000
+#define V3_CLOCK_RATE_MASK 0x0000ff00
+#define V3_CLOCK_RATE_SHIFT 8
+#define V3_CLOCK_SOURCE_MASK 0x000000ff
+
+#define V3_OPT_IFACE_MODE_OFFSET 0x0c94
+#define V3_ENABLE_OPT_IN_IFACE_A 0x00000001
+#define V3_ENABLE_OPT_IN_IFACE_B 0x00000002
+#define V3_ENABLE_OPT_OUT_IFACE_A 0x00000100
+#define V3_ENABLE_OPT_OUT_IFACE_B 0x00000200
+#define V3_NO_ADAT_OPT_IN_IFACE_A 0x00010000
+#define V3_NO_ADAT_OPT_IN_IFACE_B 0x00100000
+#define V3_NO_ADAT_OPT_OUT_IFACE_A 0x00040000
+#define V3_NO_ADAT_OPT_OUT_IFACE_B 0x00400000
+
+static int v3_get_clock_rate(struct snd_motu *motu, unsigned int *rate)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data = (data & V3_CLOCK_RATE_MASK) >> V3_CLOCK_RATE_SHIFT;
+ if (data >= ARRAY_SIZE(snd_motu_clock_rates))
+ return -EIO;
+
+ *rate = snd_motu_clock_rates[data];
+
+ return 0;
+}
+
+static int v3_set_clock_rate(struct snd_motu *motu, unsigned int rate)
+{
+ __be32 reg;
+ u32 data;
+ bool need_to_wait;
+ int i, err;
+
+ for (i = 0; i < ARRAY_SIZE(snd_motu_clock_rates); ++i) {
+ if (snd_motu_clock_rates[i] == rate)
+ break;
+ }
+ if (i == ARRAY_SIZE(snd_motu_clock_rates))
+ return -EINVAL;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~(V3_CLOCK_RATE_MASK | V3_FETCH_PCM_FRAMES);
+ data |= i << V3_CLOCK_RATE_SHIFT;
+
+ need_to_wait = data != be32_to_cpu(reg);
+
+ reg = cpu_to_be32(data);
+ err = snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+
+ if (need_to_wait) {
+ /* Cost expensive. */
+ if (msleep_interruptible(4000) > 0)
+ return -EINTR;
+ }
+
+ return 0;
+}
+
+static int v3_get_clock_source(struct snd_motu *motu,
+ enum snd_motu_clock_source *src)
+{
+ __be32 reg;
+ u32 data;
+ unsigned int val;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ val = data & V3_CLOCK_SOURCE_MASK;
+ if (val == 0x00) {
+ *src = SND_MOTU_CLOCK_SOURCE_INTERNAL;
+ } else if (val == 0x01) {
+ *src = SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC;
+ } else if (val == 0x10) {
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX;
+ } else if (val == 0x18 || val == 0x19) {
+ err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET,
+ &reg, sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ if (val == 0x18) {
+ if (data & V3_NO_ADAT_OPT_IN_IFACE_A)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A;
+ } else {
+ if (data & V3_NO_ADAT_OPT_IN_IFACE_B)
+ *src = SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B;
+ else
+ *src = SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B;
+ }
+ } else {
+ *src = SND_MOTU_CLOCK_SOURCE_UNKNOWN;
+ }
+
+ return 0;
+}
+
+static int v3_switch_fetching_mode(struct snd_motu *motu, bool enable)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return 0;
+ data = be32_to_cpu(reg);
+
+ if (enable)
+ data |= V3_FETCH_PCM_FRAMES;
+ else
+ data &= ~V3_FETCH_PCM_FRAMES;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, V3_CLOCK_STATUS_OFFSET, &reg,
+ sizeof(reg));
+}
+
+static void calculate_fixed_part(struct snd_motu_packet_format *formats,
+ enum amdtp_stream_direction dir,
+ enum snd_motu_spec_flags flags,
+ unsigned char analog_ports)
+{
+ unsigned char pcm_chunks[3] = {0, 0, 0};
+
+ formats->msg_chunks = 2;
+
+ pcm_chunks[0] = analog_ports;
+ pcm_chunks[1] = analog_ports;
+ if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+ pcm_chunks[2] = analog_ports;
+
+ if (dir == AMDTP_IN_STREAM) {
+ if (flags & SND_MOTU_SPEC_TX_MICINST_CHUNK) {
+ pcm_chunks[0] += 2;
+ pcm_chunks[1] += 2;
+ if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+ pcm_chunks[2] += 2;
+ }
+
+ if (flags & SND_MOTU_SPEC_TX_RETURN_CHUNK) {
+ pcm_chunks[0] += 2;
+ pcm_chunks[1] += 2;
+ if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+ pcm_chunks[2] += 2;
+ }
+
+ if (flags & SND_MOTU_SPEC_TX_REVERB_CHUNK) {
+ pcm_chunks[0] += 2;
+ pcm_chunks[1] += 2;
+ }
+ } else {
+ /*
+ * Packets to v2 units transfer main-out-1/2 and phone-out-1/2.
+ */
+ pcm_chunks[0] += 4;
+ pcm_chunks[1] += 4;
+ }
+
+ /*
+ * At least, packets have two data chunks for S/PDIF on coaxial
+ * interface.
+ */
+ pcm_chunks[0] += 2;
+ pcm_chunks[1] += 2;
+
+ /*
+ * Fixed part consists of PCM chunks multiple of 4, with msg chunks. As
+ * a result, this part can includes empty data chunks.
+ */
+ formats->fixed_part_pcm_chunks[0] = round_up(2 + pcm_chunks[0], 4) - 2;
+ formats->fixed_part_pcm_chunks[1] = round_up(2 + pcm_chunks[1], 4) - 2;
+ if (flags & SND_MOTU_SPEC_SUPPORT_CLOCK_X4)
+ formats->fixed_part_pcm_chunks[2] =
+ round_up(2 + pcm_chunks[2], 4) - 2;
+}
+
+static void calculate_differed_part(struct snd_motu_packet_format *formats,
+ enum snd_motu_spec_flags flags, u32 data,
+ u32 a_enable_mask, u32 a_no_adat_mask,
+ u32 b_enable_mask, u32 b_no_adat_mask)
+{
+ unsigned char pcm_chunks[3] = {0, 0, 0};
+ int i;
+
+ if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_A) && (data & a_enable_mask)) {
+ if (data & a_no_adat_mask) {
+ /*
+ * Additional two data chunks for S/PDIF on optical
+ * interface A. This includes empty data chunks.
+ */
+ pcm_chunks[0] += 4;
+ pcm_chunks[1] += 4;
+ } else {
+ /*
+ * Additional data chunks for ADAT on optical interface
+ * A.
+ */
+ pcm_chunks[0] += 8;
+ pcm_chunks[1] += 4;
+ }
+ }
+
+ if ((flags & SND_MOTU_SPEC_HAS_OPT_IFACE_B) && (data & b_enable_mask)) {
+ if (data & b_no_adat_mask) {
+ /*
+ * Additional two data chunks for S/PDIF on optical
+ * interface B. This includes empty data chunks.
+ */
+ pcm_chunks[0] += 4;
+ pcm_chunks[1] += 4;
+ } else {
+ /*
+ * Additional data chunks for ADAT on optical interface
+ * B.
+ */
+ pcm_chunks[0] += 8;
+ pcm_chunks[1] += 4;
+ }
+ }
+
+ for (i = 0; i < 3; ++i) {
+ if (pcm_chunks[i] > 0)
+ pcm_chunks[i] = round_up(pcm_chunks[i], 4);
+
+ formats->differed_part_pcm_chunks[i] = pcm_chunks[i];
+ }
+}
+
+static int v3_cache_packet_formats(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, V3_OPT_IFACE_MODE_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ calculate_fixed_part(&motu->tx_packet_formats, AMDTP_IN_STREAM,
+ motu->spec->flags, motu->spec->analog_in_ports);
+ calculate_differed_part(&motu->tx_packet_formats,
+ motu->spec->flags, data,
+ V3_ENABLE_OPT_IN_IFACE_A, V3_NO_ADAT_OPT_IN_IFACE_A,
+ V3_ENABLE_OPT_IN_IFACE_B, V3_NO_ADAT_OPT_IN_IFACE_B);
+
+ calculate_fixed_part(&motu->rx_packet_formats, AMDTP_OUT_STREAM,
+ motu->spec->flags, motu->spec->analog_out_ports);
+ calculate_differed_part(&motu->rx_packet_formats,
+ motu->spec->flags, data,
+ V3_ENABLE_OPT_OUT_IFACE_A, V3_NO_ADAT_OPT_OUT_IFACE_A,
+ V3_ENABLE_OPT_OUT_IFACE_B, V3_NO_ADAT_OPT_OUT_IFACE_B);
+
+ motu->tx_packet_formats.midi_flag_offset = 8;
+ motu->tx_packet_formats.midi_byte_offset = 7;
+ motu->tx_packet_formats.pcm_byte_offset = 10;
+
+ motu->rx_packet_formats.midi_flag_offset = 8;
+ motu->rx_packet_formats.midi_byte_offset = 7;
+ motu->rx_packet_formats.pcm_byte_offset = 10;
+
+ return 0;
+}
+
+const struct snd_motu_protocol snd_motu_protocol_v3 = {
+ .get_clock_rate = v3_get_clock_rate,
+ .set_clock_rate = v3_set_clock_rate,
+ .get_clock_source = v3_get_clock_source,
+ .switch_fetching_mode = v3_switch_fetching_mode,
+ .cache_packet_formats = v3_cache_packet_formats,
+};
diff --git a/sound/firewire/motu/motu-stream.c b/sound/firewire/motu/motu-stream.c
new file mode 100644
index 000000000000..bd458029099e
--- /dev/null
+++ b/sound/firewire/motu/motu-stream.c
@@ -0,0 +1,381 @@
+/*
+ * motu-stream.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "motu.h"
+
+#define CALLBACK_TIMEOUT 200
+
+#define ISOC_COMM_CONTROL_OFFSET 0x0b00
+#define ISOC_COMM_CONTROL_MASK 0xffff0000
+#define CHANGE_RX_ISOC_COMM_STATE 0x80000000
+#define RX_ISOC_COMM_IS_ACTIVATED 0x40000000
+#define RX_ISOC_COMM_CHANNEL_MASK 0x3f000000
+#define RX_ISOC_COMM_CHANNEL_SHIFT 24
+#define CHANGE_TX_ISOC_COMM_STATE 0x00800000
+#define TX_ISOC_COMM_IS_ACTIVATED 0x00400000
+#define TX_ISOC_COMM_CHANNEL_MASK 0x003f0000
+#define TX_ISOC_COMM_CHANNEL_SHIFT 16
+
+#define PACKET_FORMAT_OFFSET 0x0b10
+#define TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000080
+#define RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS 0x00000040
+#define TX_PACKET_TRANSMISSION_SPEED_MASK 0x0000000f
+
+static int start_both_streams(struct snd_motu *motu, unsigned int rate)
+{
+ unsigned int midi_ports = 0;
+ __be32 reg;
+ u32 data;
+ int err;
+
+ if (motu->spec->flags & SND_MOTU_SPEC_HAS_MIDI)
+ midi_ports = 1;
+
+ /* Set packet formation to our packet streaming engine. */
+ err = amdtp_motu_set_parameters(&motu->rx_stream, rate, midi_ports,
+ &motu->rx_packet_formats);
+ if (err < 0)
+ return err;
+
+ err = amdtp_motu_set_parameters(&motu->tx_stream, rate, midi_ports,
+ &motu->tx_packet_formats);
+ if (err < 0)
+ return err;
+
+ /* Get isochronous resources on the bus. */
+ err = fw_iso_resources_allocate(&motu->rx_resources,
+ amdtp_stream_get_max_payload(&motu->rx_stream),
+ fw_parent_device(motu->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ err = fw_iso_resources_allocate(&motu->tx_resources,
+ amdtp_stream_get_max_payload(&motu->tx_stream),
+ fw_parent_device(motu->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ /* Configure the unit to start isochronous communication. */
+ err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg) & ~ISOC_COMM_CONTROL_MASK;
+
+ data |= CHANGE_RX_ISOC_COMM_STATE | RX_ISOC_COMM_IS_ACTIVATED |
+ (motu->rx_resources.channel << RX_ISOC_COMM_CHANNEL_SHIFT) |
+ CHANGE_TX_ISOC_COMM_STATE | TX_ISOC_COMM_IS_ACTIVATED |
+ (motu->tx_resources.channel << TX_ISOC_COMM_CHANNEL_SHIFT);
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+ sizeof(reg));
+}
+
+static void stop_both_streams(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = motu->spec->protocol->switch_fetching_mode(motu, false);
+ if (err < 0)
+ return;
+
+ err = snd_motu_transaction_read(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return;
+ data = be32_to_cpu(reg);
+
+ data &= ~(RX_ISOC_COMM_IS_ACTIVATED | TX_ISOC_COMM_IS_ACTIVATED);
+ data |= CHANGE_RX_ISOC_COMM_STATE | CHANGE_TX_ISOC_COMM_STATE;
+
+ reg = cpu_to_be32(data);
+ snd_motu_transaction_write(motu, ISOC_COMM_CONTROL_OFFSET, &reg,
+ sizeof(reg));
+
+ fw_iso_resources_free(&motu->tx_resources);
+ fw_iso_resources_free(&motu->rx_resources);
+}
+
+static int start_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
+{
+ struct fw_iso_resources *resources;
+ int err;
+
+ if (stream == &motu->rx_stream)
+ resources = &motu->rx_resources;
+ else
+ resources = &motu->tx_resources;
+
+ err = amdtp_stream_start(stream, resources->channel,
+ fw_parent_device(motu->unit)->max_speed);
+ if (err < 0)
+ return err;
+
+ if (!amdtp_stream_wait_callback(stream, CALLBACK_TIMEOUT)) {
+ amdtp_stream_stop(stream);
+ fw_iso_resources_free(resources);
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static void stop_isoc_ctx(struct snd_motu *motu, struct amdtp_stream *stream)
+{
+ struct fw_iso_resources *resources;
+
+ if (stream == &motu->rx_stream)
+ resources = &motu->rx_resources;
+ else
+ resources = &motu->tx_resources;
+
+ amdtp_stream_stop(stream);
+ fw_iso_resources_free(resources);
+}
+
+static int ensure_packet_formats(struct snd_motu *motu)
+{
+ __be32 reg;
+ u32 data;
+ int err;
+
+ err = snd_motu_transaction_read(motu, PACKET_FORMAT_OFFSET, &reg,
+ sizeof(reg));
+ if (err < 0)
+ return err;
+ data = be32_to_cpu(reg);
+
+ data &= ~(TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS |
+ RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS|
+ TX_PACKET_TRANSMISSION_SPEED_MASK);
+ if (motu->tx_packet_formats.differed_part_pcm_chunks[0] == 0)
+ data |= TX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
+ if (motu->rx_packet_formats.differed_part_pcm_chunks[0] == 0)
+ data |= RX_PACKET_EXCLUDE_DIFFERED_DATA_CHUNKS;
+ data |= fw_parent_device(motu->unit)->max_speed;
+
+ reg = cpu_to_be32(data);
+ return snd_motu_transaction_write(motu, PACKET_FORMAT_OFFSET, &reg,
+ sizeof(reg));
+}
+
+int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate)
+{
+ const struct snd_motu_protocol *protocol = motu->spec->protocol;
+ unsigned int curr_rate;
+ int err = 0;
+
+ if (motu->capture_substreams == 0 && motu->playback_substreams == 0)
+ return 0;
+
+ /* Some packet queueing errors. */
+ if (amdtp_streaming_error(&motu->rx_stream) ||
+ amdtp_streaming_error(&motu->tx_stream)) {
+ amdtp_stream_stop(&motu->rx_stream);
+ amdtp_stream_stop(&motu->tx_stream);
+ stop_both_streams(motu);
+ }
+
+ err = protocol->cache_packet_formats(motu);
+ if (err < 0)
+ return err;
+
+ /* Stop stream if rate is different. */
+ err = protocol->get_clock_rate(motu, &curr_rate);
+ if (err < 0) {
+ dev_err(&motu->unit->device,
+ "fail to get sampling rate: %d\n", err);
+ return err;
+ }
+ if (rate == 0)
+ rate = curr_rate;
+ if (rate != curr_rate) {
+ amdtp_stream_stop(&motu->rx_stream);
+ amdtp_stream_stop(&motu->tx_stream);
+ stop_both_streams(motu);
+ }
+
+ if (!amdtp_stream_running(&motu->rx_stream)) {
+ err = protocol->set_clock_rate(motu, rate);
+ if (err < 0) {
+ dev_err(&motu->unit->device,
+ "fail to set sampling rate: %d\n", err);
+ return err;
+ }
+
+ err = ensure_packet_formats(motu);
+ if (err < 0)
+ return err;
+
+ err = start_both_streams(motu, rate);
+ if (err < 0) {
+ dev_err(&motu->unit->device,
+ "fail to start isochronous comm: %d\n", err);
+ stop_both_streams(motu);
+ return err;
+ }
+
+ err = start_isoc_ctx(motu, &motu->rx_stream);
+ if (err < 0) {
+ dev_err(&motu->unit->device,
+ "fail to start IT context: %d\n", err);
+ stop_both_streams(motu);
+ return err;
+ }
+
+ err = protocol->switch_fetching_mode(motu, true);
+ if (err < 0) {
+ dev_err(&motu->unit->device,
+ "fail to enable frame fetching: %d\n", err);
+ stop_both_streams(motu);
+ return err;
+ }
+ }
+
+ if (!amdtp_stream_running(&motu->tx_stream) &&
+ motu->capture_substreams > 0) {
+ err = start_isoc_ctx(motu, &motu->tx_stream);
+ if (err < 0) {
+ dev_err(&motu->unit->device,
+ "fail to start IR context: %d", err);
+ amdtp_stream_stop(&motu->rx_stream);
+ stop_both_streams(motu);
+ return err;
+ }
+ }
+
+ return 0;
+}
+
+void snd_motu_stream_stop_duplex(struct snd_motu *motu)
+{
+ if (motu->capture_substreams == 0) {
+ if (amdtp_stream_running(&motu->tx_stream))
+ stop_isoc_ctx(motu, &motu->tx_stream);
+
+ if (motu->playback_substreams == 0) {
+ if (amdtp_stream_running(&motu->rx_stream))
+ stop_isoc_ctx(motu, &motu->rx_stream);
+ stop_both_streams(motu);
+ }
+ }
+}
+
+static int init_stream(struct snd_motu *motu, enum amdtp_stream_direction dir)
+{
+ int err;
+ struct amdtp_stream *stream;
+ struct fw_iso_resources *resources;
+
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &motu->tx_stream;
+ resources = &motu->tx_resources;
+ } else {
+ stream = &motu->rx_stream;
+ resources = &motu->rx_resources;
+ }
+
+ err = fw_iso_resources_init(resources, motu->unit);
+ if (err < 0)
+ return err;
+
+ err = amdtp_motu_init(stream, motu->unit, dir, motu->spec->protocol);
+ if (err < 0) {
+ amdtp_stream_destroy(stream);
+ fw_iso_resources_destroy(resources);
+ }
+
+ return err;
+}
+
+static void destroy_stream(struct snd_motu *motu,
+ enum amdtp_stream_direction dir)
+{
+ struct amdtp_stream *stream;
+ struct fw_iso_resources *resources;
+
+ if (dir == AMDTP_IN_STREAM) {
+ stream = &motu->tx_stream;
+ resources = &motu->tx_resources;
+ } else {
+ stream = &motu->rx_stream;
+ resources = &motu->rx_resources;
+ }
+
+ amdtp_stream_destroy(stream);
+ fw_iso_resources_free(resources);
+}
+
+int snd_motu_stream_init_duplex(struct snd_motu *motu)
+{
+ int err;
+
+ err = init_stream(motu, AMDTP_IN_STREAM);
+ if (err < 0)
+ return err;
+
+ err = init_stream(motu, AMDTP_OUT_STREAM);
+ if (err < 0)
+ destroy_stream(motu, AMDTP_IN_STREAM);
+
+ return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_motu_stream_destroy_duplex(struct snd_motu *motu)
+{
+ destroy_stream(motu, AMDTP_IN_STREAM);
+ destroy_stream(motu, AMDTP_OUT_STREAM);
+
+ motu->playback_substreams = 0;
+ motu->capture_substreams = 0;
+}
+
+static void motu_lock_changed(struct snd_motu *motu)
+{
+ motu->dev_lock_changed = true;
+ wake_up(&motu->hwdep_wait);
+}
+
+int snd_motu_stream_lock_try(struct snd_motu *motu)
+{
+ int err;
+
+ spin_lock_irq(&motu->lock);
+
+ if (motu->dev_lock_count < 0) {
+ err = -EBUSY;
+ goto out;
+ }
+
+ if (motu->dev_lock_count++ == 0)
+ motu_lock_changed(motu);
+ err = 0;
+out:
+ spin_unlock_irq(&motu->lock);
+ return err;
+}
+
+void snd_motu_stream_lock_release(struct snd_motu *motu)
+{
+ spin_lock_irq(&motu->lock);
+
+ if (WARN_ON(motu->dev_lock_count <= 0))
+ goto out;
+
+ if (--motu->dev_lock_count == 0)
+ motu_lock_changed(motu);
+out:
+ spin_unlock_irq(&motu->lock);
+}
diff --git a/sound/firewire/motu/motu-transaction.c b/sound/firewire/motu/motu-transaction.c
new file mode 100644
index 000000000000..7fc30091e0de
--- /dev/null
+++ b/sound/firewire/motu/motu-transaction.c
@@ -0,0 +1,137 @@
+/*
+ * motu-transaction.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+
+#include "motu.h"
+
+#define SND_MOTU_ADDR_BASE 0xfffff0000000ULL
+#define ASYNC_ADDR_HI 0x0b04
+#define ASYNC_ADDR_LO 0x0b08
+
+int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
+ size_t size)
+{
+ int tcode;
+
+ if (size % sizeof(__be32) > 0 || size <= 0)
+ return -EINVAL;
+ if (size == sizeof(__be32))
+ tcode = TCODE_READ_QUADLET_REQUEST;
+ else
+ tcode = TCODE_READ_BLOCK_REQUEST;
+
+ return snd_fw_transaction(motu->unit, tcode,
+ SND_MOTU_ADDR_BASE + offset, reg, size, 0);
+}
+
+int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
+ size_t size)
+{
+ int tcode;
+
+ if (size % sizeof(__be32) > 0 || size <= 0)
+ return -EINVAL;
+ if (size == sizeof(__be32))
+ tcode = TCODE_WRITE_QUADLET_REQUEST;
+ else
+ tcode = TCODE_WRITE_BLOCK_REQUEST;
+
+ return snd_fw_transaction(motu->unit, tcode,
+ SND_MOTU_ADDR_BASE + offset, reg, size, 0);
+}
+
+static void handle_message(struct fw_card *card, struct fw_request *request,
+ int tcode, int destination, int source,
+ int generation, unsigned long long offset,
+ void *data, size_t length, void *callback_data)
+{
+ struct snd_motu *motu = callback_data;
+ __be32 *buf = (__be32 *)data;
+ unsigned long flags;
+
+ if (tcode != TCODE_WRITE_QUADLET_REQUEST) {
+ fw_send_response(card, request, RCODE_COMPLETE);
+ return;
+ }
+
+ if (offset != motu->async_handler.offset || length != 4) {
+ fw_send_response(card, request, RCODE_ADDRESS_ERROR);
+ return;
+ }
+
+ spin_lock_irqsave(&motu->lock, flags);
+ motu->msg = be32_to_cpu(*buf);
+ spin_unlock_irqrestore(&motu->lock, flags);
+
+ fw_send_response(card, request, RCODE_COMPLETE);
+
+ wake_up(&motu->hwdep_wait);
+}
+
+int snd_motu_transaction_reregister(struct snd_motu *motu)
+{
+ struct fw_device *device = fw_parent_device(motu->unit);
+ __be32 data;
+ int err;
+
+ if (motu->async_handler.callback_data == NULL)
+ return -EINVAL;
+
+ /* Register messaging address. Block transaction is not allowed. */
+ data = cpu_to_be32((device->card->node_id << 16) |
+ (motu->async_handler.offset >> 32));
+ err = snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data,
+ sizeof(data));
+ if (err < 0)
+ return err;
+
+ data = cpu_to_be32(motu->async_handler.offset);
+ return snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data,
+ sizeof(data));
+}
+
+int snd_motu_transaction_register(struct snd_motu *motu)
+{
+ static const struct fw_address_region resp_register_region = {
+ .start = 0xffffe0000000ull,
+ .end = 0xffffe000ffffull,
+ };
+ int err;
+
+ /* Perhaps, 4 byte messages are transferred. */
+ motu->async_handler.length = 4;
+ motu->async_handler.address_callback = handle_message;
+ motu->async_handler.callback_data = motu;
+
+ err = fw_core_add_address_handler(&motu->async_handler,
+ &resp_register_region);
+ if (err < 0)
+ return err;
+
+ err = snd_motu_transaction_reregister(motu);
+ if (err < 0) {
+ fw_core_remove_address_handler(&motu->async_handler);
+ motu->async_handler.address_callback = NULL;
+ }
+
+ return err;
+}
+
+void snd_motu_transaction_unregister(struct snd_motu *motu)
+{
+ __be32 data;
+
+ if (motu->async_handler.address_callback != NULL)
+ fw_core_remove_address_handler(&motu->async_handler);
+ motu->async_handler.address_callback = NULL;
+
+ /* Unregister the address. */
+ data = cpu_to_be32(0x00000000);
+ snd_motu_transaction_write(motu, ASYNC_ADDR_HI, &data, sizeof(data));
+ snd_motu_transaction_write(motu, ASYNC_ADDR_LO, &data, sizeof(data));
+}
diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c
new file mode 100644
index 000000000000..bf779cfeef0d
--- /dev/null
+++ b/sound/firewire/motu/motu.c
@@ -0,0 +1,264 @@
+/*
+ * motu.c - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "motu.h"
+
+#define OUI_MOTU 0x0001f2
+
+MODULE_DESCRIPTION("MOTU FireWire driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+const unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT] = {
+ /* mode 0 */
+ [0] = 44100,
+ [1] = 48000,
+ /* mode 1 */
+ [2] = 88200,
+ [3] = 96000,
+ /* mode 2 */
+ [4] = 176400,
+ [5] = 192000,
+};
+
+static void name_card(struct snd_motu *motu)
+{
+ struct fw_device *fw_dev = fw_parent_device(motu->unit);
+ struct fw_csr_iterator it;
+ int key, val;
+ u32 version = 0;
+
+ fw_csr_iterator_init(&it, motu->unit->directory);
+ while (fw_csr_iterator_next(&it, &key, &val)) {
+ switch (key) {
+ case CSR_VERSION:
+ version = val;
+ break;
+ }
+ }
+
+ strcpy(motu->card->driver, "FW-MOTU");
+ strcpy(motu->card->shortname, motu->spec->name);
+ strcpy(motu->card->mixername, motu->spec->name);
+ snprintf(motu->card->longname, sizeof(motu->card->longname),
+ "MOTU %s (version:%d), GUID %08x%08x at %s, S%d",
+ motu->spec->name, version,
+ fw_dev->config_rom[3], fw_dev->config_rom[4],
+ dev_name(&motu->unit->device), 100 << fw_dev->max_speed);
+}
+
+static void motu_free(struct snd_motu *motu)
+{
+ snd_motu_transaction_unregister(motu);
+
+ snd_motu_stream_destroy_duplex(motu);
+ fw_unit_put(motu->unit);
+
+ mutex_destroy(&motu->mutex);
+ kfree(motu);
+}
+
+/*
+ * This module releases the FireWire unit data after all ALSA character devices
+ * are released by applications. This is for releasing stream data or finishing
+ * transactions safely. Thus at returning from .remove(), this module still keep
+ * references for the unit.
+ */
+static void motu_card_free(struct snd_card *card)
+{
+ motu_free(card->private_data);
+}
+
+static void do_registration(struct work_struct *work)
+{
+ struct snd_motu *motu = container_of(work, struct snd_motu, dwork.work);
+ int err;
+
+ if (motu->registered)
+ return;
+
+ err = snd_card_new(&motu->unit->device, -1, NULL, THIS_MODULE, 0,
+ &motu->card);
+ if (err < 0)
+ return;
+
+ name_card(motu);
+
+ err = snd_motu_transaction_register(motu);
+ if (err < 0)
+ goto error;
+
+ err = snd_motu_stream_init_duplex(motu);
+ if (err < 0)
+ goto error;
+
+ snd_motu_proc_init(motu);
+
+ err = snd_motu_create_pcm_devices(motu);
+ if (err < 0)
+ goto error;
+
+ if (motu->spec->flags & SND_MOTU_SPEC_HAS_MIDI) {
+ err = snd_motu_create_midi_devices(motu);
+ if (err < 0)
+ goto error;
+ }
+
+ err = snd_motu_create_hwdep_device(motu);
+ if (err < 0)
+ goto error;
+
+ err = snd_card_register(motu->card);
+ if (err < 0)
+ goto error;
+
+ /*
+ * After registered, motu instance can be released corresponding to
+ * releasing the sound card instance.
+ */
+ motu->card->private_free = motu_card_free;
+ motu->card->private_data = motu;
+ motu->registered = true;
+
+ return;
+error:
+ snd_motu_transaction_unregister(motu);
+ snd_card_free(motu->card);
+ dev_info(&motu->unit->device,
+ "Sound card registration failed: %d\n", err);
+}
+
+static int motu_probe(struct fw_unit *unit,
+ const struct ieee1394_device_id *entry)
+{
+ struct snd_motu *motu;
+
+ /* Allocate this independently of sound card instance. */
+ motu = kzalloc(sizeof(struct snd_motu), GFP_KERNEL);
+ if (motu == NULL)
+ return -ENOMEM;
+
+ motu->spec = (const struct snd_motu_spec *)entry->driver_data;
+ motu->unit = fw_unit_get(unit);
+ dev_set_drvdata(&unit->device, motu);
+
+ mutex_init(&motu->mutex);
+ spin_lock_init(&motu->lock);
+ init_waitqueue_head(&motu->hwdep_wait);
+
+ /* Allocate and register this sound card later. */
+ INIT_DEFERRABLE_WORK(&motu->dwork, do_registration);
+ snd_fw_schedule_registration(unit, &motu->dwork);
+
+ return 0;
+}
+
+static void motu_remove(struct fw_unit *unit)
+{
+ struct snd_motu *motu = dev_get_drvdata(&unit->device);
+
+ /*
+ * Confirm to stop the work for registration before the sound card is
+ * going to be released. The work is not scheduled again because bus
+ * reset handler is not called anymore.
+ */
+ cancel_delayed_work_sync(&motu->dwork);
+
+ if (motu->registered) {
+ /* No need to wait for releasing card object in this context. */
+ snd_card_free_when_closed(motu->card);
+ } else {
+ /* Don't forget this case. */
+ motu_free(motu);
+ }
+}
+
+static void motu_bus_update(struct fw_unit *unit)
+{
+ struct snd_motu *motu = dev_get_drvdata(&unit->device);
+
+ /* Postpone a workqueue for deferred registration. */
+ if (!motu->registered)
+ snd_fw_schedule_registration(unit, &motu->dwork);
+
+ /* The handler address register becomes initialized. */
+ snd_motu_transaction_reregister(motu);
+}
+
+static struct snd_motu_spec motu_828mk2 = {
+ .name = "828mk2",
+ .protocol = &snd_motu_protocol_v2,
+ .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
+ SND_MOTU_SPEC_TX_MICINST_CHUNK |
+ SND_MOTU_SPEC_TX_RETURN_CHUNK |
+ SND_MOTU_SPEC_HAS_OPT_IFACE_A |
+ SND_MOTU_SPEC_HAS_MIDI,
+
+ .analog_in_ports = 8,
+ .analog_out_ports = 8,
+};
+
+static struct snd_motu_spec motu_828mk3 = {
+ .name = "828mk3",
+ .protocol = &snd_motu_protocol_v3,
+ .flags = SND_MOTU_SPEC_SUPPORT_CLOCK_X2 |
+ SND_MOTU_SPEC_SUPPORT_CLOCK_X4 |
+ SND_MOTU_SPEC_TX_MICINST_CHUNK |
+ SND_MOTU_SPEC_TX_RETURN_CHUNK |
+ SND_MOTU_SPEC_TX_REVERB_CHUNK |
+ SND_MOTU_SPEC_HAS_OPT_IFACE_A |
+ SND_MOTU_SPEC_HAS_OPT_IFACE_B |
+ SND_MOTU_SPEC_HAS_MIDI,
+
+ .analog_in_ports = 8,
+ .analog_out_ports = 8,
+};
+
+#define SND_MOTU_DEV_ENTRY(model, data) \
+{ \
+ .match_flags = IEEE1394_MATCH_VENDOR_ID | \
+ IEEE1394_MATCH_MODEL_ID | \
+ IEEE1394_MATCH_SPECIFIER_ID, \
+ .vendor_id = OUI_MOTU, \
+ .model_id = model, \
+ .specifier_id = OUI_MOTU, \
+ .driver_data = (kernel_ulong_t)data, \
+}
+
+static const struct ieee1394_device_id motu_id_table[] = {
+ SND_MOTU_DEV_ENTRY(0x101800, &motu_828mk2),
+ SND_MOTU_DEV_ENTRY(0x106800, &motu_828mk3), /* FireWire only. */
+ SND_MOTU_DEV_ENTRY(0x100800, &motu_828mk3), /* Hybrid. */
+ { }
+};
+MODULE_DEVICE_TABLE(ieee1394, motu_id_table);
+
+static struct fw_driver motu_driver = {
+ .driver = {
+ .owner = THIS_MODULE,
+ .name = KBUILD_MODNAME,
+ .bus = &fw_bus_type,
+ },
+ .probe = motu_probe,
+ .update = motu_bus_update,
+ .remove = motu_remove,
+ .id_table = motu_id_table,
+};
+
+static int __init alsa_motu_init(void)
+{
+ return driver_register(&motu_driver.driver);
+}
+
+static void __exit alsa_motu_exit(void)
+{
+ driver_unregister(&motu_driver.driver);
+}
+
+module_init(alsa_motu_init);
+module_exit(alsa_motu_exit);
diff --git a/sound/firewire/motu/motu.h b/sound/firewire/motu/motu.h
new file mode 100644
index 000000000000..8d6a4a3af9cc
--- /dev/null
+++ b/sound/firewire/motu/motu.h
@@ -0,0 +1,161 @@
+/*
+ * motu.h - a part of driver for MOTU FireWire series
+ *
+ * Copyright (c) 2015-2017 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_FIREWIRE_MOTU_H_INCLUDED
+#define SOUND_FIREWIRE_MOTU_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+#include <linux/sched/signal.h>
+
+#include <sound/control.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/info.h>
+#include <sound/rawmidi.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+
+#include "../lib.h"
+#include "../amdtp-stream.h"
+#include "../iso-resources.h"
+
+struct snd_motu_packet_format {
+ unsigned char midi_flag_offset;
+ unsigned char midi_byte_offset;
+ unsigned char pcm_byte_offset;
+
+ unsigned char msg_chunks;
+ unsigned char fixed_part_pcm_chunks[3];
+ unsigned char differed_part_pcm_chunks[3];
+};
+
+struct snd_motu {
+ struct snd_card *card;
+ struct fw_unit *unit;
+ struct mutex mutex;
+ spinlock_t lock;
+
+ bool registered;
+ struct delayed_work dwork;
+
+ /* Model dependent information. */
+ const struct snd_motu_spec *spec;
+
+ /* For packet streaming */
+ struct snd_motu_packet_format tx_packet_formats;
+ struct snd_motu_packet_format rx_packet_formats;
+ struct amdtp_stream tx_stream;
+ struct amdtp_stream rx_stream;
+ struct fw_iso_resources tx_resources;
+ struct fw_iso_resources rx_resources;
+ unsigned int capture_substreams;
+ unsigned int playback_substreams;
+
+ /* For notification. */
+ struct fw_address_handler async_handler;
+ u32 msg;
+
+ /* For uapi */
+ int dev_lock_count;
+ bool dev_lock_changed;
+ wait_queue_head_t hwdep_wait;
+};
+
+enum snd_motu_spec_flags {
+ SND_MOTU_SPEC_SUPPORT_CLOCK_X2 = 0x0001,
+ SND_MOTU_SPEC_SUPPORT_CLOCK_X4 = 0x0002,
+ SND_MOTU_SPEC_TX_MICINST_CHUNK = 0x0004,
+ SND_MOTU_SPEC_TX_RETURN_CHUNK = 0x0008,
+ SND_MOTU_SPEC_TX_REVERB_CHUNK = 0x0010,
+ SND_MOTU_SPEC_TX_AESEBU_CHUNK = 0x0020,
+ SND_MOTU_SPEC_HAS_OPT_IFACE_A = 0x0040,
+ SND_MOTU_SPEC_HAS_OPT_IFACE_B = 0x0080,
+ SND_MOTU_SPEC_HAS_MIDI = 0x0100,
+};
+
+#define SND_MOTU_CLOCK_RATE_COUNT 6
+extern const unsigned int snd_motu_clock_rates[SND_MOTU_CLOCK_RATE_COUNT];
+
+enum snd_motu_clock_source {
+ SND_MOTU_CLOCK_SOURCE_INTERNAL,
+ SND_MOTU_CLOCK_SOURCE_ADAT_ON_DSUB,
+ SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT,
+ SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_A,
+ SND_MOTU_CLOCK_SOURCE_ADAT_ON_OPT_B,
+ SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT,
+ SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_A,
+ SND_MOTU_CLOCK_SOURCE_SPDIF_ON_OPT_B,
+ SND_MOTU_CLOCK_SOURCE_SPDIF_ON_COAX,
+ SND_MOTU_CLOCK_SOURCE_AESEBU_ON_XLR,
+ SND_MOTU_CLOCK_SOURCE_WORD_ON_BNC,
+ SND_MOTU_CLOCK_SOURCE_UNKNOWN,
+};
+
+struct snd_motu_protocol {
+ int (*get_clock_rate)(struct snd_motu *motu, unsigned int *rate);
+ int (*set_clock_rate)(struct snd_motu *motu, unsigned int rate);
+ int (*get_clock_source)(struct snd_motu *motu,
+ enum snd_motu_clock_source *source);
+ int (*switch_fetching_mode)(struct snd_motu *motu, bool enable);
+ int (*cache_packet_formats)(struct snd_motu *motu);
+};
+
+struct snd_motu_spec {
+ const char *const name;
+ enum snd_motu_spec_flags flags;
+
+ unsigned char analog_in_ports;
+ unsigned char analog_out_ports;
+
+ const struct snd_motu_protocol *const protocol;
+};
+
+extern const struct snd_motu_protocol snd_motu_protocol_v2;
+extern const struct snd_motu_protocol snd_motu_protocol_v3;
+
+int amdtp_motu_init(struct amdtp_stream *s, struct fw_unit *unit,
+ enum amdtp_stream_direction dir,
+ const struct snd_motu_protocol *const protocol);
+int amdtp_motu_set_parameters(struct amdtp_stream *s, unsigned int rate,
+ unsigned int midi_ports,
+ struct snd_motu_packet_format *formats);
+int amdtp_motu_add_pcm_hw_constraints(struct amdtp_stream *s,
+ struct snd_pcm_runtime *runtime);
+void amdtp_motu_midi_trigger(struct amdtp_stream *s, unsigned int port,
+ struct snd_rawmidi_substream *midi);
+
+int snd_motu_transaction_read(struct snd_motu *motu, u32 offset, __be32 *reg,
+ size_t size);
+int snd_motu_transaction_write(struct snd_motu *motu, u32 offset, __be32 *reg,
+ size_t size);
+int snd_motu_transaction_register(struct snd_motu *motu);
+int snd_motu_transaction_reregister(struct snd_motu *motu);
+void snd_motu_transaction_unregister(struct snd_motu *motu);
+
+int snd_motu_stream_init_duplex(struct snd_motu *motu);
+void snd_motu_stream_destroy_duplex(struct snd_motu *motu);
+int snd_motu_stream_start_duplex(struct snd_motu *motu, unsigned int rate);
+void snd_motu_stream_stop_duplex(struct snd_motu *motu);
+int snd_motu_stream_lock_try(struct snd_motu *motu);
+void snd_motu_stream_lock_release(struct snd_motu *motu);
+
+void snd_motu_proc_init(struct snd_motu *motu);
+
+int snd_motu_create_pcm_devices(struct snd_motu *motu);
+
+int snd_motu_create_midi_devices(struct snd_motu *motu);
+
+int snd_motu_create_hwdep_device(struct snd_motu *motu);
+#endif
diff --git a/sound/firewire/oxfw/oxfw-command.c b/sound/firewire/oxfw/oxfw-command.c
index 12ef3253bc89..ac3e2e301666 100644
--- a/sound/firewire/oxfw/oxfw-command.c
+++ b/sound/firewire/oxfw/oxfw-command.c
@@ -34,7 +34,9 @@ int avc_stream_set_format(struct fw_unit *unit, enum avc_general_plug_dir dir,
err = fcp_avc_transaction(unit, buf, len + 10, buf, len + 10,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7) | BIT(8));
- if ((err > 0) && (err < len + 10))
+ if (err < 0)
+ ;
+ else if (err < len + 10)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@@ -77,7 +79,9 @@ int avc_stream_get_format(struct fw_unit *unit,
err = fcp_avc_transaction(unit, buf, 12, buf, *len,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5) |
BIT(6) | BIT(7));
- if ((err > 0) && (err < 10))
+ if (err < 0)
+ ;
+ else if (err < 12)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
@@ -139,7 +143,9 @@ int avc_general_inquiry_sig_fmt(struct fw_unit *unit, unsigned int rate,
/* do transaction and check buf[1-5] are the same against command */
err = fcp_avc_transaction(unit, buf, 8, buf, 8,
BIT(1) | BIT(2) | BIT(3) | BIT(4) | BIT(5));
- if ((err > 0) && (err < 8))
+ if (err < 0)
+ ;
+ else if (err < 8)
err = -EIO;
else if (buf[0] == 0x08) /* NOT IMPLEMENTED */
err = -ENOSYS;
diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c
index 74d7fb6efce6..413ab6313bb6 100644
--- a/sound/firewire/oxfw/oxfw.c
+++ b/sound/firewire/oxfw/oxfw.c
@@ -227,11 +227,11 @@ static void do_registration(struct work_struct *work)
if (err < 0)
goto error;
- err = detect_quirks(oxfw);
+ err = snd_oxfw_stream_discover(oxfw);
if (err < 0)
goto error;
- err = snd_oxfw_stream_discover(oxfw);
+ err = detect_quirks(oxfw);
if (err < 0)
goto error;
diff --git a/sound/firewire/tascam/tascam-midi.c b/sound/firewire/tascam/tascam-midi.c
index df4f95d65925..4a741570d536 100644
--- a/sound/firewire/tascam/tascam-midi.c
+++ b/sound/firewire/tascam/tascam-midi.c
@@ -18,9 +18,8 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
{
struct snd_tscm *tscm = substream->rmidi->private_data;
- /* Initialize internal status. */
- tscm->running_status[substream->number] = 0;
- tscm->on_sysex[substream->number] = 0;
+ snd_fw_async_midi_port_init(&tscm->out_ports[substream->number]);
+
return 0;
}
@@ -32,11 +31,14 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
static int midi_playback_close(struct snd_rawmidi_substream *substream)
{
+ return 0;
+}
+
+static void midi_playback_drain(struct snd_rawmidi_substream *substream)
+{
struct snd_tscm *tscm = substream->rmidi->private_data;
snd_fw_async_midi_port_finish(&tscm->out_ports[substream->number]);
-
- return 0;
}
static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
@@ -78,6 +80,7 @@ int snd_tscm_create_midi_devices(struct snd_tscm *tscm)
static const struct snd_rawmidi_ops playback_ops = {
.open = midi_playback_open,
.close = midi_playback_close,
+ .drain = midi_playback_drain,
.trigger = midi_playback_trigger,
};
struct snd_rawmidi *rmidi;
diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c
index 040a96d1ba8e..8967c52f5032 100644
--- a/sound/firewire/tascam/tascam-transaction.c
+++ b/sound/firewire/tascam/tascam-transaction.c
@@ -58,39 +58,38 @@ static inline int calculate_message_bytes(u8 status)
return -EINVAL;
}
-static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
+static int fill_message(struct snd_fw_async_midi_port *port,
+ struct snd_rawmidi_substream *substream)
{
- struct snd_tscm *tscm = substream->rmidi->private_data;
- unsigned int port = substream->number;
int i, len, consume;
u8 *label, *msg;
u8 status;
/* The first byte is used for label, the rest for MIDI bytes. */
- label = buf;
- msg = buf + 1;
+ label = port->buf;
+ msg = port->buf + 1;
consume = snd_rawmidi_transmit_peek(substream, msg, 3);
if (consume == 0)
return 0;
/* On exclusive message. */
- if (tscm->on_sysex[port]) {
+ if (port->on_sysex) {
/* Seek the end of exclusives. */
for (i = 0; i < consume; ++i) {
if (msg[i] == 0xf7) {
- tscm->on_sysex[port] = false;
+ port->on_sysex = false;
break;
}
}
/* At the end of exclusive message, use label 0x07. */
- if (!tscm->on_sysex[port]) {
+ if (!port->on_sysex) {
consume = i + 1;
- *label = (port << 4) | 0x07;
+ *label = (substream->number << 4) | 0x07;
/* During exclusive message, use label 0x04. */
} else if (consume == 3) {
- *label = (port << 4) | 0x04;
+ *label = (substream->number << 4) | 0x04;
/* We need to fill whole 3 bytes. Go to next change. */
} else {
return 0;
@@ -101,12 +100,12 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
/* The beginning of exclusives. */
if (msg[0] == 0xf0) {
/* Transfer it in next chance in another condition. */
- tscm->on_sysex[port] = true;
+ port->on_sysex = true;
return 0;
} else {
/* On running-status. */
if ((msg[0] & 0x80) != 0x80)
- status = tscm->running_status[port];
+ status = port->running_status;
else
status = msg[0];
@@ -124,18 +123,18 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
msg[2] = msg[1];
msg[1] = msg[0];
- msg[0] = tscm->running_status[port];
+ msg[0] = port->running_status;
} else {
/* Enough MIDI bytes were not retrieved. */
if (consume < len)
return 0;
consume = len;
- tscm->running_status[port] = msg[0];
+ port->running_status = msg[0];
}
}
- *label = (port << 4) | (msg[0] >> 4);
+ *label = (substream->number << 4) | (msg[0] >> 4);
}
if (len > 0 && len < 3)
@@ -144,6 +143,106 @@ static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
return consume;
}
+static void async_midi_port_callback(struct fw_card *card, int rcode,
+ void *data, size_t length,
+ void *callback_data)
+{
+ struct snd_fw_async_midi_port *port = callback_data;
+ struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
+
+ /* This port is closed. */
+ if (substream == NULL)
+ return;
+
+ if (rcode == RCODE_COMPLETE)
+ snd_rawmidi_transmit_ack(substream, port->consume_bytes);
+ else if (!rcode_is_permanent_error(rcode))
+ /* To start next transaction immediately for recovery. */
+ port->next_ktime = 0;
+ else
+ /* Don't continue processing. */
+ port->error = true;
+
+ port->idling = true;
+
+ if (!snd_rawmidi_transmit_empty(substream))
+ schedule_work(&port->work);
+}
+
+static void midi_port_work(struct work_struct *work)
+{
+ struct snd_fw_async_midi_port *port =
+ container_of(work, struct snd_fw_async_midi_port, work);
+ struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
+ int generation;
+
+ /* Under transacting or error state. */
+ if (!port->idling || port->error)
+ return;
+
+ /* Nothing to do. */
+ if (substream == NULL || snd_rawmidi_transmit_empty(substream))
+ return;
+
+ /* Do it in next chance. */
+ if (ktime_after(port->next_ktime, ktime_get())) {
+ schedule_work(&port->work);
+ return;
+ }
+
+ /*
+ * Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
+ * Later, snd_rawmidi_transmit_ack() is called.
+ */
+ memset(port->buf, 0, 4);
+ port->consume_bytes = fill_message(port, substream);
+ if (port->consume_bytes <= 0) {
+ /* Do it in next chance, immediately. */
+ if (port->consume_bytes == 0) {
+ port->next_ktime = 0;
+ schedule_work(&port->work);
+ } else {
+ /* Fatal error. */
+ port->error = true;
+ }
+ return;
+ }
+
+ /* Set interval to next transaction. */
+ port->next_ktime = ktime_add_ns(ktime_get(),
+ port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
+
+ /* Start this transaction. */
+ port->idling = false;
+
+ /*
+ * In Linux FireWire core, when generation is updated with memory
+ * barrier, node id has already been updated. In this module, After
+ * this smp_rmb(), load/store instructions to memory are completed.
+ * Thus, both of generation and node id are available with recent
+ * values. This is a light-serialization solution to handle bus reset
+ * events on IEEE 1394 bus.
+ */
+ generation = port->parent->generation;
+ smp_rmb();
+
+ fw_send_request(port->parent->card, &port->transaction,
+ TCODE_WRITE_QUADLET_REQUEST,
+ port->parent->node_id, generation,
+ port->parent->max_speed,
+ TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
+ port->buf, 4, async_midi_port_callback,
+ port);
+}
+
+void snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port)
+{
+ port->idling = true;
+ port->error = false;
+ port->running_status = 0;
+ port->on_sysex = false;
+}
+
static void handle_midi_tx(struct fw_card *card, struct fw_request *request,
int tcode, int destination, int source,
int generation, unsigned long long offset,
@@ -219,12 +318,9 @@ int snd_tscm_transaction_register(struct snd_tscm *tscm)
goto error;
for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) {
- err = snd_fw_async_midi_port_init(
- &tscm->out_ports[i], tscm->unit,
- TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
- 4, fill_message);
- if (err < 0)
- goto error;
+ tscm->out_ports[i].parent = fw_parent_device(tscm->unit);
+ tscm->out_ports[i].next_ktime = 0;
+ INIT_WORK(&tscm->out_ports[i].work, midi_port_work);
}
return err;
@@ -275,7 +371,6 @@ int snd_tscm_transaction_reregister(struct snd_tscm *tscm)
void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
{
__be32 reg;
- unsigned int i;
if (tscm->async_handler.callback_data == NULL)
return;
@@ -302,7 +397,4 @@ void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
fw_core_remove_address_handler(&tscm->async_handler);
tscm->async_handler.callback_data = NULL;
-
- for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++)
- snd_fw_async_midi_port_destroy(&tscm->out_ports[i]);
}
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
index d3cd4065722b..08ecfae5c584 100644
--- a/sound/firewire/tascam/tascam.h
+++ b/sound/firewire/tascam/tascam.h
@@ -45,6 +45,23 @@ struct snd_tscm_spec {
#define TSCM_MIDI_IN_PORT_MAX 4
#define TSCM_MIDI_OUT_PORT_MAX 4
+struct snd_fw_async_midi_port {
+ struct fw_device *parent;
+ struct work_struct work;
+ bool idling;
+ ktime_t next_ktime;
+ bool error;
+
+ struct fw_transaction transaction;
+
+ u8 buf[4];
+ u8 running_status;
+ bool on_sysex;
+
+ struct snd_rawmidi_substream *substream;
+ int consume_bytes;
+};
+
struct snd_tscm {
struct snd_card *card;
struct fw_unit *unit;
@@ -72,8 +89,6 @@ struct snd_tscm {
/* For MIDI message outgoing transactions. */
struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX];
- u8 running_status[TSCM_MIDI_OUT_PORT_MAX];
- bool on_sysex[TSCM_MIDI_OUT_PORT_MAX];
};
#define TSCM_ADDR_BASE 0xffff00000000ull
@@ -131,6 +146,26 @@ void snd_tscm_stream_lock_changed(struct snd_tscm *tscm);
int snd_tscm_stream_lock_try(struct snd_tscm *tscm);
void snd_tscm_stream_lock_release(struct snd_tscm *tscm);
+void snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port);
+
+static inline void
+snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
+ struct snd_rawmidi_substream *substream)
+{
+ if (!port->error) {
+ port->substream = substream;
+ schedule_work(&port->work);
+ }
+}
+
+static inline void
+snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
+{
+ port->substream = NULL;
+ cancel_work_sync(&port->work);
+ port->error = false;
+}
+
int snd_tscm_transaction_register(struct snd_tscm *tscm);
int snd_tscm_transaction_reregister(struct snd_tscm *tscm);
void snd_tscm_transaction_unregister(struct snd_tscm *tscm);
diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c
index 261469188566..84f3b8168716 100644
--- a/sound/hda/ext/hdac_ext_controller.c
+++ b/sound/hda/ext/hdac_ext_controller.c
@@ -171,7 +171,7 @@ static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable)
{
int timeout;
u32 val;
- int mask = (1 << AZX_MLCTL_CPA);
+ int mask = (1 << AZX_MLCTL_CPA_SHIFT);
udelay(3);
timeout = 150;
@@ -179,10 +179,10 @@ static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable)
do {
val = readl(link->ml_addr + AZX_REG_ML_LCTL);
if (enable) {
- if (((val & mask) >> AZX_MLCTL_CPA))
+ if (((val & mask) >> AZX_MLCTL_CPA_SHIFT))
return 0;
} else {
- if (!((val & mask) >> AZX_MLCTL_CPA))
+ if (!((val & mask) >> AZX_MLCTL_CPA_SHIFT))
return 0;
}
udelay(3);
diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c
index 043065867656..978dc1801b3a 100644
--- a/sound/hda/hdac_controller.c
+++ b/sound/hda/hdac_controller.c
@@ -106,7 +106,11 @@ void snd_hdac_bus_stop_cmd_io(struct hdac_bus *bus)
/* disable ringbuffer DMAs */
snd_hdac_chip_writeb(bus, RIRBCTL, 0);
snd_hdac_chip_writeb(bus, CORBCTL, 0);
+ spin_unlock_irq(&bus->reg_lock);
+
hdac_wait_for_cmd_dmas(bus);
+
+ spin_lock_irq(&bus->reg_lock);
/* disable unsolicited responses */
snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, 0);
spin_unlock_irq(&bus->reg_lock);
@@ -268,11 +272,11 @@ int snd_hdac_bus_parse_capabilities(struct hdac_bus *bus)
unsigned int offset;
unsigned int counter = 0;
- offset = snd_hdac_chip_readl(bus, LLCH);
+ offset = snd_hdac_chip_readw(bus, LLCH);
/* Lets walk the linked capabilities list */
do {
- cur_cap = _snd_hdac_chip_read(l, bus, offset);
+ cur_cap = _snd_hdac_chip_readl(bus, offset);
dev_dbg(bus->dev, "Capability version: 0x%x\n",
(cur_cap & AZX_CAP_HDR_VER_MASK) >> AZX_CAP_HDR_VER_OFF);
diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c
index c6994ebb4567..e1472c7ab6c1 100644
--- a/sound/hda/hdac_stream.c
+++ b/sound/hda/hdac_stream.c
@@ -555,12 +555,12 @@ void snd_hdac_stream_sync_trigger(struct hdac_stream *azx_dev, bool set,
if (!reg)
reg = AZX_REG_SSYNC;
- val = _snd_hdac_chip_read(l, bus, reg);
+ val = _snd_hdac_chip_readl(bus, reg);
if (set)
val |= streams;
else
val &= ~streams;
- _snd_hdac_chip_write(l, bus, reg, val);
+ _snd_hdac_chip_writel(bus, reg, val);
}
EXPORT_SYMBOL_GPL(snd_hdac_stream_sync_trigger);
diff --git a/sound/isa/ad1848/ad1848.c b/sound/isa/ad1848/ad1848.c
index a302d1f8d14f..e739b1c85c25 100644
--- a/sound/isa/ad1848/ad1848.c
+++ b/sound/isa/ad1848/ad1848.c
@@ -55,11 +55,11 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " CRD_NAME " driver.");
module_param_array(thinkpad, bool, NULL, 0444);
MODULE_PARM_DESC(thinkpad, "Enable only for the onboard CS4248 of IBM Thinkpad 360/750/755 series.");
diff --git a/sound/isa/adlib.c b/sound/isa/adlib.c
index 8d3060fd7ad7..5fb619eca5c8 100644
--- a/sound/isa/adlib.c
+++ b/sound/isa/adlib.c
@@ -27,7 +27,7 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
static int snd_adlib_match(struct device *dev, unsigned int n)
diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c
index 787475084f46..8e1756c3b9bb 100644
--- a/sound/isa/cmi8328.c
+++ b/sound/isa/cmi8328.c
@@ -51,18 +51,18 @@ MODULE_PARM_DESC(index, "Index value for CMI8328 soundcard.");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for CMI8328 soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for CMI8328 driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for CMI8328 driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 for CMI8328 driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 for CMI8328 driver.");
-module_param_array(mpuport, long, NULL, 0444);
+module_param_hw_array(mpuport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8328 driver.");
-module_param_array(mpuirq, int, NULL, 0444);
+module_param_hw_array(mpuirq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8328 MPU-401 port.");
#ifdef SUPPORT_JOYSTICK
module_param_array(gameport, bool, NULL, 0444);
diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c
index dfedfd85f205..f64b29ab5cc7 100644
--- a/sound/isa/cmi8330.c
+++ b/sound/isa/cmi8330.c
@@ -95,27 +95,27 @@ module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
-module_param_array(sbport, long, NULL, 0444);
+module_param_hw_array(sbport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(sbport, "Port # for CMI8330/CMI8329 SB driver.");
-module_param_array(sbirq, int, NULL, 0444);
+module_param_hw_array(sbirq, int, irq, NULL, 0444);
MODULE_PARM_DESC(sbirq, "IRQ # for CMI8330/CMI8329 SB driver.");
-module_param_array(sbdma8, int, NULL, 0444);
+module_param_hw_array(sbdma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(sbdma8, "DMA8 for CMI8330/CMI8329 SB driver.");
-module_param_array(sbdma16, int, NULL, 0444);
+module_param_hw_array(sbdma16, int, dma, NULL, 0444);
MODULE_PARM_DESC(sbdma16, "DMA16 for CMI8330/CMI8329 SB driver.");
-module_param_array(wssport, long, NULL, 0444);
+module_param_hw_array(wssport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(wssport, "Port # for CMI8330/CMI8329 WSS driver.");
-module_param_array(wssirq, int, NULL, 0444);
+module_param_hw_array(wssirq, int, irq, NULL, 0444);
MODULE_PARM_DESC(wssirq, "IRQ # for CMI8330/CMI8329 WSS driver.");
-module_param_array(wssdma, int, NULL, 0444);
+module_param_hw_array(wssdma, int, dma, NULL, 0444);
MODULE_PARM_DESC(wssdma, "DMA for CMI8330/CMI8329 WSS driver.");
-module_param_array(fmport, long, NULL, 0444);
+module_param_hw_array(fmport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fmport, "FM port # for CMI8330/CMI8329 driver.");
-module_param_array(mpuport, long, NULL, 0444);
+module_param_hw_array(mpuport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpuport, "MPU-401 port # for CMI8330/CMI8329 driver.");
-module_param_array(mpuirq, int, NULL, 0444);
+module_param_hw_array(mpuirq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpuirq, "IRQ # for CMI8330/CMI8329 MPU-401 port.");
#ifdef CONFIG_PNP
static int isa_registered;
diff --git a/sound/isa/cs423x/cs4231.c b/sound/isa/cs423x/cs4231.c
index ef7448e9f813..e8edd9017a2f 100644
--- a/sound/isa/cs423x/cs4231.c
+++ b/sound/isa/cs423x/cs4231.c
@@ -55,17 +55,17 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " CRD_NAME " driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for " CRD_NAME " driver.");
static int snd_cs4231_match(struct device *dev, unsigned int n)
diff --git a/sound/isa/cs423x/cs4236.c b/sound/isa/cs423x/cs4236.c
index 9d7582c90a95..1f9a3b2be7a1 100644
--- a/sound/isa/cs423x/cs4236.c
+++ b/sound/isa/cs423x/cs4236.c
@@ -98,23 +98,23 @@ MODULE_PARM_DESC(enable, "Enable " IDENT " soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " IDENT " driver.");
-module_param_array(cport, long, NULL, 0444);
+module_param_hw_array(cport, long, ioport, NULL, 0444);
MODULE_PARM_DESC(cport, "Control port # for " IDENT " driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " IDENT " driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for " IDENT " driver.");
-module_param_array(sb_port, long, NULL, 0444);
+module_param_hw_array(sb_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(sb_port, "SB port # for " IDENT " driver (optional).");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " IDENT " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " IDENT " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " IDENT " driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for " IDENT " driver.");
#ifdef CONFIG_PNP
diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c
index 1901c2bb6c3b..36320e7f2789 100644
--- a/sound/isa/es1688/es1688.c
+++ b/sound/isa/es1688/es1688.c
@@ -71,17 +71,17 @@ module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for ES1688 driver.");
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver.");
#ifdef CONFIG_PNP
diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c
index e2cf508841b1..81cf26fa28d6 100644
--- a/sound/isa/es1688/es1688_lib.c
+++ b/sound/isa/es1688/es1688_lib.c
@@ -742,7 +742,7 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device)
pcm->private_data = chip;
pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX;
- sprintf(pcm->name, snd_es1688_chip_id(chip));
+ strcpy(pcm->name, snd_es1688_chip_id(chip));
chip->pcm = pcm;
snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c
index 5094b62d8f77..0cabe2b8974f 100644
--- a/sound/isa/es18xx.c
+++ b/sound/isa/es18xx.c
@@ -1999,17 +1999,17 @@ MODULE_PARM_DESC(enable, "Enable ES18xx soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for ES18xx driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for ES18xx driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for ES18xx driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for ES18xx driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA 1 # for ES18xx driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA 2 # for ES18xx driver.");
#ifdef CONFIG_PNP
diff --git a/sound/isa/galaxy/galaxy.c b/sound/isa/galaxy/galaxy.c
index 379abe2cbeb2..b9994cc9f5fb 100644
--- a/sound/isa/galaxy/galaxy.c
+++ b/sound/isa/galaxy/galaxy.c
@@ -53,21 +53,21 @@ static int mpu_irq[SNDRV_CARDS] = SNDRV_DEFAULT_IRQ;
static int dma1[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
static int dma2[SNDRV_CARDS] = SNDRV_DEFAULT_DMA;
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(wss_port, long, NULL, 0444);
+module_param_hw_array(wss_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(wss_port, "WSS port # for " CRD_NAME " driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "Playback DMA # for " CRD_NAME " driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "Capture DMA # for " CRD_NAME " driver.");
/*
diff --git a/sound/isa/gus/gusclassic.c b/sound/isa/gus/gusclassic.c
index c169be49ed71..92a997ab1229 100644
--- a/sound/isa/gus/gusclassic.c
+++ b/sound/isa/gus/gusclassic.c
@@ -58,13 +58,13 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for " CRD_NAME " driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for " CRD_NAME " driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for " CRD_NAME " driver.");
diff --git a/sound/isa/gus/gusextreme.c b/sound/isa/gus/gusextreme.c
index 77ac2fd723b4..beb52c0f70ea 100644
--- a/sound/isa/gus/gusextreme.c
+++ b/sound/isa/gus/gusextreme.c
@@ -66,21 +66,21 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for " CRD_NAME " soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable " CRD_NAME " soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for " CRD_NAME " driver.");
-module_param_array(gf1_port, long, NULL, 0444);
+module_param_hw_array(gf1_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(gf1_port, "GF1 port # for " CRD_NAME " driver (optional).");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for " CRD_NAME " driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for " CRD_NAME " driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for " CRD_NAME " driver.");
-module_param_array(gf1_irq, int, NULL, 0444);
+module_param_hw_array(gf1_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(gf1_irq, "GF1 IRQ # for " CRD_NAME " driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for " CRD_NAME " driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "GF1 DMA # for " CRD_NAME " driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for " CRD_NAME " driver.");
diff --git a/sound/isa/gus/gusmax.c b/sound/isa/gus/gusmax.c
index dd88c9d33492..63309a453140 100644
--- a/sound/isa/gus/gusmax.c
+++ b/sound/isa/gus/gusmax.c
@@ -56,13 +56,13 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for GUS MAX soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable GUS MAX soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for GUS MAX driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for GUS MAX driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for GUS MAX driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for GUS MAX driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for GUS MAX driver.");
diff --git a/sound/isa/gus/interwave.c b/sound/isa/gus/interwave.c
index 70d0040484c8..0687b7ef3e53 100644
--- a/sound/isa/gus/interwave.c
+++ b/sound/isa/gus/interwave.c
@@ -92,17 +92,17 @@ MODULE_PARM_DESC(enable, "Enable InterWave soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "ISA PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for InterWave driver.");
#ifdef SNDRV_STB
-module_param_array(port_tc, long, NULL, 0444);
+module_param_hw_array(port_tc, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port_tc, "Tone control (TEA6330T - i2c bus) port # for InterWave driver.");
#endif
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for InterWave driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for InterWave driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for InterWave driver.");
module_param_array(joystick_dac, int, NULL, 0444);
MODULE_PARM_DESC(joystick_dac, "Joystick DAC level 0.59V-4.52V or 0.389V-2.98V for InterWave driver.");
diff --git a/sound/isa/msnd/msnd_pinnacle.c b/sound/isa/msnd/msnd_pinnacle.c
index 4c072666115d..ad4897337df5 100644
--- a/sound/isa/msnd/msnd_pinnacle.c
+++ b/sound/isa/msnd/msnd_pinnacle.c
@@ -800,22 +800,22 @@ MODULE_LICENSE("GPL");
MODULE_FIRMWARE(INITCODEFILE);
MODULE_FIRMWARE(PERMCODEFILE);
-module_param_array(io, long, NULL, S_IRUGO);
+module_param_hw_array(io, long, ioport, NULL, S_IRUGO);
MODULE_PARM_DESC(io, "IO port #");
-module_param_array(irq, int, NULL, S_IRUGO);
-module_param_array(mem, long, NULL, S_IRUGO);
+module_param_hw_array(irq, int, irq, NULL, S_IRUGO);
+module_param_hw_array(mem, long, iomem, NULL, S_IRUGO);
module_param_array(write_ndelay, int, NULL, S_IRUGO);
module_param(calibrate_signal, int, S_IRUGO);
#ifndef MSND_CLASSIC
module_param_array(digital, int, NULL, S_IRUGO);
-module_param_array(cfg, long, NULL, S_IRUGO);
+module_param_hw_array(cfg, long, ioport, NULL, S_IRUGO);
module_param_array(reset, int, 0, S_IRUGO);
-module_param_array(mpu_io, long, NULL, S_IRUGO);
-module_param_array(mpu_irq, int, NULL, S_IRUGO);
-module_param_array(ide_io0, long, NULL, S_IRUGO);
-module_param_array(ide_io1, long, NULL, S_IRUGO);
-module_param_array(ide_irq, int, NULL, S_IRUGO);
-module_param_array(joystick_io, long, NULL, S_IRUGO);
+module_param_hw_array(mpu_io, long, ioport, NULL, S_IRUGO);
+module_param_hw_array(mpu_irq, int, irq, NULL, S_IRUGO);
+module_param_hw_array(ide_io0, long, ioport, NULL, S_IRUGO);
+module_param_hw_array(ide_io1, long, ioport, NULL, S_IRUGO);
+module_param_hw_array(ide_irq, int, irq, NULL, S_IRUGO);
+module_param_hw_array(joystick_io, long, ioport, NULL, S_IRUGO);
#endif
diff --git a/sound/isa/opl3sa2.c b/sound/isa/opl3sa2.c
index ae133633a420..4098e3e0353d 100644
--- a/sound/isa/opl3sa2.c
+++ b/sound/isa/opl3sa2.c
@@ -69,21 +69,21 @@ MODULE_PARM_DESC(enable, "Enable OPL3-SA soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for OPL3-SA driver.");
-module_param_array(sb_port, long, NULL, 0444);
+module_param_hw_array(sb_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(sb_port, "SB port # for OPL3-SA driver.");
-module_param_array(wss_port, long, NULL, 0444);
+module_param_hw_array(wss_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(wss_port, "WSS port # for OPL3-SA driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for OPL3-SA driver.");
-module_param_array(midi_port, long, NULL, 0444);
+module_param_hw_array(midi_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(midi_port, "MIDI port # for OPL3-SA driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for OPL3-SA driver.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for OPL3-SA driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for OPL3-SA driver.");
module_param_array(opl3sa3_ymode, int, NULL, 0444);
MODULE_PARM_DESC(opl3sa3_ymode, "Speaker size selection for 3D Enhancement mode: Desktop/Large Notebook/Small Notebook/HiFi.");
diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c
index 3a9067db1a84..bcbff56f060d 100644
--- a/sound/isa/opti9xx/miro.c
+++ b/sound/isa/opti9xx/miro.c
@@ -69,19 +69,19 @@ module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for miro soundcard.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for miro soundcard.");
-module_param(port, long, 0444);
+module_param_hw(port, long, ioport, 0444);
MODULE_PARM_DESC(port, "WSS port # for miro driver.");
-module_param(mpu_port, long, 0444);
+module_param_hw(mpu_port, long, ioport, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for miro driver.");
-module_param(fm_port, long, 0444);
+module_param_hw(fm_port, long, ioport, 0444);
MODULE_PARM_DESC(fm_port, "FM Port # for miro driver.");
-module_param(irq, int, 0444);
+module_param_hw(irq, int, irq, 0444);
MODULE_PARM_DESC(irq, "WSS irq # for miro driver.");
-module_param(mpu_irq, int, 0444);
+module_param_hw(mpu_irq, int, irq, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for miro driver.");
-module_param(dma1, int, 0444);
+module_param_hw(dma1, int, dma, 0444);
MODULE_PARM_DESC(dma1, "1st dma # for miro driver.");
-module_param(dma2, int, 0444);
+module_param_hw(dma2, int, dma, 0444);
MODULE_PARM_DESC(dma2, "2nd dma # for miro driver.");
module_param(wss, int, 0444);
MODULE_PARM_DESC(wss, "wss mode");
diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c
index 0a5266003786..ceddb392b1e3 100644
--- a/sound/isa/opti9xx/opti92x-ad1848.c
+++ b/sound/isa/opti9xx/opti92x-ad1848.c
@@ -88,20 +88,20 @@ MODULE_PARM_DESC(id, "ID string for opti9xx based soundcard.");
module_param(isapnp, bool, 0444);
MODULE_PARM_DESC(isapnp, "Enable ISA PnP detection for specified soundcard.");
#endif
-module_param(port, long, 0444);
+module_param_hw(port, long, ioport, 0444);
MODULE_PARM_DESC(port, "WSS port # for opti9xx driver.");
-module_param(mpu_port, long, 0444);
+module_param_hw(mpu_port, long, ioport, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for opti9xx driver.");
-module_param(fm_port, long, 0444);
+module_param_hw(fm_port, long, ioport, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for opti9xx driver.");
-module_param(irq, int, 0444);
+module_param_hw(irq, int, irq, 0444);
MODULE_PARM_DESC(irq, "WSS irq # for opti9xx driver.");
-module_param(mpu_irq, int, 0444);
+module_param_hw(mpu_irq, int, irq, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 irq # for opti9xx driver.");
-module_param(dma1, int, 0444);
+module_param_hw(dma1, int, dma, 0444);
MODULE_PARM_DESC(dma1, "1st dma # for opti9xx driver.");
#if defined(CS4231) || defined(OPTi93X)
-module_param(dma2, int, 0444);
+module_param_hw(dma2, int, dma, 0444);
MODULE_PARM_DESC(dma2, "2nd dma # for opti9xx driver.");
#endif /* CS4231 || OPTi93X */
diff --git a/sound/isa/sb/jazz16.c b/sound/isa/sb/jazz16.c
index 4d909971eedb..bfa0055e1fd6 100644
--- a/sound/isa/sb/jazz16.c
+++ b/sound/isa/sb/jazz16.c
@@ -50,17 +50,17 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Media Vision Jazz16 based soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Media Vision Jazz16 based soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for jazz16 driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for jazz16 driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for jazz16 driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for jazz16 driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "DMA8 # for jazz16 driver.");
-module_param_array(dma16, int, NULL, 0444);
+module_param_hw_array(dma16, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma16, "DMA16 # for jazz16 driver.");
#define SB_JAZZ16_WAKEUP 0xaf
diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c
index 4a7d7c89808f..3b2e4f405ff2 100644
--- a/sound/isa/sb/sb16.c
+++ b/sound/isa/sb/sb16.c
@@ -99,21 +99,21 @@ MODULE_PARM_DESC(enable, "Enable SoundBlaster 16 soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "PnP detection for specified soundcard.");
#endif
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for SB16 driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for SB16 driver.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port # for SB16 PnP driver.");
#ifdef SNDRV_SBAWE_EMU8000
-module_param_array(awe_port, long, NULL, 0444);
+module_param_hw_array(awe_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(awe_port, "AWE port # for SB16 PnP driver.");
#endif
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for SB16 driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for SB16 driver.");
-module_param_array(dma16, int, NULL, 0444);
+module_param_hw_array(dma16, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma16, "16-bit DMA # for SB16 driver.");
module_param_array(mic_agc, int, NULL, 0444);
MODULE_PARM_DESC(mic_agc, "Mic Auto-Gain-Control switch.");
diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c
index ad42d2364199..d77dcba276b5 100644
--- a/sound/isa/sb/sb8.c
+++ b/sound/isa/sb/sb8.c
@@ -47,11 +47,11 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for Sound Blaster soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Sound Blaster soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for SB8 driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for SB8 driver.");
-module_param_array(dma8, int, NULL, 0444);
+module_param_hw_array(dma8, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma8, "8-bit DMA # for SB8 driver.");
struct snd_sb8 {
diff --git a/sound/isa/sc6000.c b/sound/isa/sc6000.c
index b61a6633d8f2..c09d9b914efe 100644
--- a/sound/isa/sc6000.c
+++ b/sound/isa/sc6000.c
@@ -64,17 +64,17 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for sc-6000 based soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable sc-6000 based soundcard.");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for sc-6000 driver.");
-module_param_array(mss_port, long, NULL, 0444);
+module_param_hw_array(mss_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mss_port, "MSS Port # for sc-6000 driver.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port # for sc-6000 driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for sc-6000 driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ # for sc-6000 driver.");
-module_param_array(dma, int, NULL, 0444);
+module_param_hw_array(dma, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma, "DMA # for sc-6000 driver.");
module_param_array(joystick, bool, NULL, 0444);
MODULE_PARM_DESC(joystick, "Enable gameport.");
diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c
index fdcfa29e2205..54f5758a1bb3 100644
--- a/sound/isa/sscape.c
+++ b/sound/isa/sscape.c
@@ -63,22 +63,22 @@ MODULE_PARM_DESC(index, "Index number for SoundScape soundcard");
module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "Description for SoundScape card");
-module_param_array(port, long, NULL, 0444);
+module_param_hw_array(port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(port, "Port # for SoundScape driver.");
-module_param_array(wss_port, long, NULL, 0444);
+module_param_hw_array(wss_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(wss_port, "WSS Port # for SoundScape driver.");
-module_param_array(irq, int, NULL, 0444);
+module_param_hw_array(irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(irq, "IRQ # for SoundScape driver.");
-module_param_array(mpu_irq, int, NULL, 0444);
+module_param_hw_array(mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(mpu_irq, "MPU401 IRQ # for SoundScape driver.");
-module_param_array(dma, int, NULL, 0444);
+module_param_hw_array(dma, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma, "DMA # for SoundScape driver.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for SoundScape driver.");
module_param_array(joystick, bool, NULL, 0444);
diff --git a/sound/isa/wavefront/wavefront.c b/sound/isa/wavefront/wavefront.c
index a0987a57c8a9..da4e9a85f0af 100644
--- a/sound/isa/wavefront/wavefront.c
+++ b/sound/isa/wavefront/wavefront.c
@@ -63,23 +63,23 @@ MODULE_PARM_DESC(enable, "Enable WaveFront soundcard.");
module_param_array(isapnp, bool, NULL, 0444);
MODULE_PARM_DESC(isapnp, "ISA PnP detection for WaveFront soundcards.");
#endif
-module_param_array(cs4232_pcm_port, long, NULL, 0444);
+module_param_hw_array(cs4232_pcm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(cs4232_pcm_port, "Port # for CS4232 PCM interface.");
-module_param_array(cs4232_pcm_irq, int, NULL, 0444);
+module_param_hw_array(cs4232_pcm_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(cs4232_pcm_irq, "IRQ # for CS4232 PCM interface.");
-module_param_array(dma1, int, NULL, 0444);
+module_param_hw_array(dma1, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma1, "DMA1 # for CS4232 PCM interface.");
-module_param_array(dma2, int, NULL, 0444);
+module_param_hw_array(dma2, int, dma, NULL, 0444);
MODULE_PARM_DESC(dma2, "DMA2 # for CS4232 PCM interface.");
-module_param_array(cs4232_mpu_port, long, NULL, 0444);
+module_param_hw_array(cs4232_mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(cs4232_mpu_port, "port # for CS4232 MPU-401 interface.");
-module_param_array(cs4232_mpu_irq, int, NULL, 0444);
+module_param_hw_array(cs4232_mpu_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(cs4232_mpu_irq, "IRQ # for CS4232 MPU-401 interface.");
-module_param_array(ics2115_irq, int, NULL, 0444);
+module_param_hw_array(ics2115_irq, int, irq, NULL, 0444);
MODULE_PARM_DESC(ics2115_irq, "IRQ # for ICS2115.");
-module_param_array(ics2115_port, long, NULL, 0444);
+module_param_hw_array(ics2115_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(ics2115_port, "Port # for ICS2115.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port #.");
module_param_array(use_cs4232_midi, bool, NULL, 0444);
MODULE_PARM_DESC(use_cs4232_midi, "Use CS4232 MPU-401 interface (inaccessibly located inside your computer)");
diff --git a/sound/oss/ad1848.c b/sound/oss/ad1848.c
index f6156d8169d0..2421f59cf279 100644
--- a/sound/oss/ad1848.c
+++ b/sound/oss/ad1848.c
@@ -2805,10 +2805,10 @@ static int __initdata dma = -1;
static int __initdata dma2 = -1;
static int __initdata type = 0;
-module_param(io, int, 0); /* I/O for a raw AD1848 card */
-module_param(irq, int, 0); /* IRQ to use */
-module_param(dma, int, 0); /* First DMA channel */
-module_param(dma2, int, 0); /* Second DMA channel */
+module_param_hw(io, int, ioport, 0); /* I/O for a raw AD1848 card */
+module_param_hw(irq, int, irq, 0); /* IRQ to use */
+module_param_hw(dma, int, dma, 0); /* First DMA channel */
+module_param_hw(dma2, int, dma, 0); /* Second DMA channel */
module_param(type, int, 0); /* Card type */
module_param(deskpro_xl, bool, 0); /* Special magic for Deskpro XL boxen */
module_param(deskpro_m, bool, 0); /* Special magic for Deskpro M box */
diff --git a/sound/oss/aedsp16.c b/sound/oss/aedsp16.c
index bb477d5c8528..f058ed6bdb69 100644
--- a/sound/oss/aedsp16.c
+++ b/sound/oss/aedsp16.c
@@ -1303,17 +1303,17 @@ static int __initdata mpu_irq = -1;
static int __initdata mss_base = -1;
static int __initdata mpu_base = -1;
-module_param(io, int, 0);
+module_param_hw(io, int, ioport, 0);
MODULE_PARM_DESC(io, "I/O base address (0x220 0x240)");
-module_param(irq, int, 0);
+module_param_hw(irq, int, irq, 0);
MODULE_PARM_DESC(irq, "IRQ line (5 7 9 10 11)");
-module_param(dma, int, 0);
+module_param_hw(dma, int, dma, 0);
MODULE_PARM_DESC(dma, "dma line (0 1 3)");
-module_param(mpu_irq, int, 0);
+module_param_hw(mpu_irq, int, irq, 0);
MODULE_PARM_DESC(mpu_irq, "MPU-401 IRQ line (5 7 9 10 0)");
-module_param(mss_base, int, 0);
+module_param_hw(mss_base, int, ioport, 0);
MODULE_PARM_DESC(mss_base, "MSS emulation I/O base address (0x530 0xE80)");
-module_param(mpu_base, int, 0);
+module_param_hw(mpu_base, int, ioport, 0);
MODULE_PARM_DESC(mpu_base,"MPU-401 I/O base address (0x300 0x310 0x320 0x330)");
MODULE_AUTHOR("Riccardo Facchetti <fizban@tin.it>");
MODULE_DESCRIPTION("Audio Excel DSP 16 Driver Version " VERSION);
diff --git a/sound/oss/mpu401.c b/sound/oss/mpu401.c
index 862735005b43..20e8fa46f647 100644
--- a/sound/oss/mpu401.c
+++ b/sound/oss/mpu401.c
@@ -1748,8 +1748,8 @@ static struct address_info cfg;
static int io = -1;
static int irq = -1;
-module_param(irq, int, 0);
-module_param(io, int, 0);
+module_param_hw(irq, int, irq, 0);
+module_param_hw(io, int, ioport, 0);
static int __init init_mpu401(void)
{
diff --git a/sound/oss/msnd_pinnacle.c b/sound/oss/msnd_pinnacle.c
index f34ec01d2239..d2abc2cf3213 100644
--- a/sound/oss/msnd_pinnacle.c
+++ b/sound/oss/msnd_pinnacle.c
@@ -1727,22 +1727,22 @@ static int
calibrate_signal __initdata = CONFIG_MSND_CALSIGNAL;
#endif /* MODULE */
-module_param (io, int, 0);
-module_param (irq, int, 0);
-module_param (mem, int, 0);
+module_param_hw (io, int, ioport, 0);
+module_param_hw (irq, int, irq, 0);
+module_param_hw (mem, int, iomem, 0);
module_param (write_ndelay, int, 0);
module_param (fifosize, int, 0);
module_param (calibrate_signal, int, 0);
#ifndef MSND_CLASSIC
module_param (digital, bool, 0);
-module_param (cfg, int, 0);
+module_param_hw (cfg, int, ioport, 0);
module_param (reset, int, 0);
-module_param (mpu_io, int, 0);
-module_param (mpu_irq, int, 0);
-module_param (ide_io0, int, 0);
-module_param (ide_io1, int, 0);
-module_param (ide_irq, int, 0);
-module_param (joystick_io, int, 0);
+module_param_hw (mpu_io, int, ioport, 0);
+module_param_hw (mpu_irq, int, irq, 0);
+module_param_hw (ide_io0, int, ioport, 0);
+module_param_hw (ide_io1, int, ioport, 0);
+module_param_hw (ide_irq, int, irq, 0);
+module_param_hw (joystick_io, int, ioport, 0);
#endif
static int __init msnd_init(void)
diff --git a/sound/oss/opl3.c b/sound/oss/opl3.c
index b6d19adf8f41..f0f5b5be6314 100644
--- a/sound/oss/opl3.c
+++ b/sound/oss/opl3.c
@@ -1200,7 +1200,7 @@ static int me;
static int io = -1;
-module_param(io, int, 0);
+module_param_hw(io, int, ioport, 0);
static int __init init_opl3 (void)
{
diff --git a/sound/oss/pas2_card.c b/sound/oss/pas2_card.c
index b07954a79536..769fca692d2a 100644
--- a/sound/oss/pas2_card.c
+++ b/sound/oss/pas2_card.c
@@ -383,15 +383,15 @@ static int __initdata sb_irq = -1;
static int __initdata sb_dma = -1;
static int __initdata sb_dma16 = -1;
-module_param(io, int, 0);
-module_param(irq, int, 0);
-module_param(dma, int, 0);
-module_param(dma16, int, 0);
-
-module_param(sb_io, int, 0);
-module_param(sb_irq, int, 0);
-module_param(sb_dma, int, 0);
-module_param(sb_dma16, int, 0);
+module_param_hw(io, int, ioport, 0);
+module_param_hw(irq, int, irq, 0);
+module_param_hw(dma, int, dma, 0);
+module_param_hw(dma16, int, dma, 0);
+
+module_param_hw(sb_io, int, ioport, 0);
+module_param_hw(sb_irq, int, irq, 0);
+module_param_hw(sb_dma, int, dma, 0);
+module_param_hw(sb_dma16, int, dma, 0);
module_param(joystick, bool, 0);
module_param(symphony, bool, 0);
diff --git a/sound/oss/pss.c b/sound/oss/pss.c
index 81314f9e2ccb..33c3a442e162 100644
--- a/sound/oss/pss.c
+++ b/sound/oss/pss.c
@@ -1139,19 +1139,19 @@ static bool pss_no_sound = 0; /* Just configure non-sound components */
static bool pss_keep_settings = 1; /* Keep hardware settings at module exit */
static char *pss_firmware = "/etc/sound/pss_synth";
-module_param(pss_io, int, 0);
+module_param_hw(pss_io, int, ioport, 0);
MODULE_PARM_DESC(pss_io, "Set i/o base of PSS card (probably 0x220 or 0x240)");
-module_param(mss_io, int, 0);
+module_param_hw(mss_io, int, ioport, 0);
MODULE_PARM_DESC(mss_io, "Set WSS (audio) i/o base (0x530, 0x604, 0xE80, 0xF40, or other. Address must end in 0 or 4 and must be from 0x100 to 0xFF4)");
-module_param(mss_irq, int, 0);
+module_param_hw(mss_irq, int, irq, 0);
MODULE_PARM_DESC(mss_irq, "Set WSS (audio) IRQ (3, 5, 7, 9, 10, 11, 12)");
-module_param(mss_dma, int, 0);
+module_param_hw(mss_dma, int, dma, 0);
MODULE_PARM_DESC(mss_dma, "Set WSS (audio) DMA (0, 1, 3)");
-module_param(mpu_io, int, 0);
+module_param_hw(mpu_io, int, ioport, 0);
MODULE_PARM_DESC(mpu_io, "Set MIDI i/o base (0x330 or other. Address must be on 4 location boundaries and must be from 0x100 to 0xFFC)");
-module_param(mpu_irq, int, 0);
+module_param_hw(mpu_irq, int, irq, 0);
MODULE_PARM_DESC(mpu_irq, "Set MIDI IRQ (3, 5, 7, 9, 10, 11, 12)");
-module_param(pss_cdrom_port, int, 0);
+module_param_hw(pss_cdrom_port, int, ioport, 0);
MODULE_PARM_DESC(pss_cdrom_port, "Set the PSS CDROM port i/o base (0x340 or other)");
module_param(pss_enable_joystick, bool, 0);
MODULE_PARM_DESC(pss_enable_joystick, "Enables the PSS joystick port (1 to enable, 0 to disable)");
diff --git a/sound/oss/sb_card.c b/sound/oss/sb_card.c
index fb5d7250de38..2a92cfe6cfe9 100644
--- a/sound/oss/sb_card.c
+++ b/sound/oss/sb_card.c
@@ -61,15 +61,15 @@ static int __initdata uart401 = 0;
static int __initdata pnp = 0;
#endif
-module_param(io, int, 000);
+module_param_hw(io, int, ioport, 000);
MODULE_PARM_DESC(io, "Soundblaster i/o base address (0x220,0x240,0x260,0x280)");
-module_param(irq, int, 000);
+module_param_hw(irq, int, irq, 000);
MODULE_PARM_DESC(irq, "IRQ (5,7,9,10)");
-module_param(dma, int, 000);
+module_param_hw(dma, int, dma, 000);
MODULE_PARM_DESC(dma, "8-bit DMA channel (0,1,3)");
-module_param(dma16, int, 000);
+module_param_hw(dma16, int, dma, 000);
MODULE_PARM_DESC(dma16, "16-bit DMA channel (5,6,7)");
-module_param(mpu_io, int, 000);
+module_param_hw(mpu_io, int, ioport, 000);
MODULE_PARM_DESC(mpu_io, "MPU base address");
module_param(type, int, 000);
MODULE_PARM_DESC(type, "You can set this to specific card type (doesn't " \
diff --git a/sound/oss/trix.c b/sound/oss/trix.c
index 3c494dc93b93..a57bc635d758 100644
--- a/sound/oss/trix.c
+++ b/sound/oss/trix.c
@@ -413,15 +413,15 @@ static int __initdata sb_irq = -1;
static int __initdata mpu_io = -1;
static int __initdata mpu_irq = -1;
-module_param(io, int, 0);
-module_param(irq, int, 0);
-module_param(dma, int, 0);
-module_param(dma2, int, 0);
-module_param(sb_io, int, 0);
-module_param(sb_dma, int, 0);
-module_param(sb_irq, int, 0);
-module_param(mpu_io, int, 0);
-module_param(mpu_irq, int, 0);
+module_param_hw(io, int, ioport, 0);
+module_param_hw(irq, int, irq, 0);
+module_param_hw(dma, int, dma, 0);
+module_param_hw(dma2, int, dma, 0);
+module_param_hw(sb_io, int, ioport, 0);
+module_param_hw(sb_dma, int, dma, 0);
+module_param_hw(sb_irq, int, irq, 0);
+module_param_hw(mpu_io, int, ioport, 0);
+module_param_hw(mpu_irq, int, irq, 0);
module_param(joystick, bool, 0);
static int __init init_trix(void)
diff --git a/sound/oss/uart401.c b/sound/oss/uart401.c
index dae4d4344407..83dcc85b8688 100644
--- a/sound/oss/uart401.c
+++ b/sound/oss/uart401.c
@@ -429,8 +429,8 @@ static struct address_info cfg_mpu;
static int io = -1;
static int irq = -1;
-module_param(io, int, 0444);
-module_param(irq, int, 0444);
+module_param_hw(io, int, ioport, 0444);
+module_param_hw(irq, int, irq, 0444);
static int __init init_uart401(void)
diff --git a/sound/oss/uart6850.c b/sound/oss/uart6850.c
index 1079133dd6ab..eda32d7eddbd 100644
--- a/sound/oss/uart6850.c
+++ b/sound/oss/uart6850.c
@@ -315,8 +315,8 @@ static struct address_info cfg_mpu;
static int __initdata io = -1;
static int __initdata irq = -1;
-module_param(io, int, 0);
-module_param(irq, int, 0);
+module_param_hw(io, int, ioport, 0);
+module_param_hw(irq, int, irq, 0);
static int __init init_uart6850(void)
{
diff --git a/sound/oss/waveartist.c b/sound/oss/waveartist.c
index 0b8d0de87273..4f0c3a232e41 100644
--- a/sound/oss/waveartist.c
+++ b/sound/oss/waveartist.c
@@ -2036,8 +2036,8 @@ __setup("waveartist=", setup_waveartist);
#endif
MODULE_DESCRIPTION("Rockwell WaveArtist RWA-010 sound driver");
-module_param(io, int, 0); /* IO base */
-module_param(irq, int, 0); /* IRQ */
-module_param(dma, int, 0); /* DMA */
-module_param(dma2, int, 0); /* DMA2 */
+module_param_hw(io, int, ioport, 0); /* IO base */
+module_param_hw(irq, int, irq, 0); /* IRQ */
+module_param_hw(dma, int, dma, 0); /* DMA */
+module_param_hw(dma2, int, dma, 0); /* DMA2 */
MODULE_LICENSE("GPL");
diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c
index 92b819e4f729..34bbc2e730a6 100644
--- a/sound/pci/ali5451/ali5451.c
+++ b/sound/pci/ali5451/ali5451.c
@@ -1339,7 +1339,7 @@ static int snd_ali_prepare(struct snd_pcm_substream *substream)
rate = snd_ali_get_spdif_in_rate(codec);
if (rate == 0) {
dev_warn(codec->card->dev,
- "ali_capture_preapre: spdif rate detect err!\n");
+ "ali_capture_prepare: spdif rate detect err!\n");
rate = 48000;
}
spin_lock_irq(&codec->reg_lock);
diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c
index 92bc06d01288..7844a75d8ed9 100644
--- a/sound/pci/als4000.c
+++ b/sound/pci/als4000.c
@@ -102,7 +102,7 @@ MODULE_PARM_DESC(id, "ID string for ALS4000 soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable ALS4000 soundcard.");
#ifdef SUPPORT_JOYSTICK
-module_param_array(joystick_port, int, NULL, 0444);
+module_param_hw_array(joystick_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(joystick_port, "Joystick port address for ALS4000 soundcard. (0 = disabled)");
#endif
diff --git a/sound/pci/au88x0/au88x0_a3d.c b/sound/pci/au88x0/au88x0_a3d.c
index ab0f87312911..7a4558a70fb9 100644
--- a/sound/pci/au88x0/au88x0_a3d.c
+++ b/sound/pci/au88x0/au88x0_a3d.c
@@ -846,7 +846,7 @@ snd_vortex_a3d_filter_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new vortex_a3d_kcontrol = {
+static const struct snd_kcontrol_new vortex_a3d_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Playback PCM advanced processing",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c
index e1af24f87566..c308a4f70550 100644
--- a/sound/pci/au88x0/au88x0_core.c
+++ b/sound/pci/au88x0/au88x0_core.c
@@ -2279,6 +2279,9 @@ vortex_adb_allocroute(vortex_t *vortex, int dma, int nr_ch, int dir,
} else {
int src[2], mix[2];
+ if (nr_ch < 1)
+ return -EINVAL;
+
/* Get SRC and MIXER hardware resources. */
for (i = 0; i < nr_ch; i++) {
if ((mix[i] =
diff --git a/sound/pci/au88x0/au88x0_eq.c b/sound/pci/au88x0/au88x0_eq.c
index 9585c5c63b96..b566b44e4da7 100644
--- a/sound/pci/au88x0/au88x0_eq.c
+++ b/sound/pci/au88x0/au88x0_eq.c
@@ -757,7 +757,7 @@ snd_vortex_eqtoggle_put(struct snd_kcontrol *kcontrol,
return 1; /* Allways changes */
}
-static struct snd_kcontrol_new vortex_eqtoggle_kcontrol = {
+static const struct snd_kcontrol_new vortex_eqtoggle_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "EQ Enable",
.index = 0,
@@ -815,7 +815,7 @@ snd_vortex_eq_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucon
return changed;
}
-static struct snd_kcontrol_new vortex_eq_kcontrol = {
+static const struct snd_kcontrol_new vortex_eq_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = " .",
.index = 0,
@@ -855,7 +855,7 @@ snd_vortex_peaks_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *u
return 0;
}
-static struct snd_kcontrol_new vortex_levels_kcontrol = {
+static const struct snd_kcontrol_new vortex_levels_kcontrol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "EQ Peaks",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
diff --git a/sound/pci/au88x0/au88x0_pcm.c b/sound/pci/au88x0/au88x0_pcm.c
index df5741a78fd2..335979a753fe 100644
--- a/sound/pci/au88x0/au88x0_pcm.c
+++ b/sound/pci/au88x0/au88x0_pcm.c
@@ -601,7 +601,7 @@ static int snd_vortex_pcm_vol_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_MINMAX(vortex_pcm_vol_db_scale, -9600, 2400);
-static struct snd_kcontrol_new snd_vortex_pcm_vol = {
+static const struct snd_kcontrol_new snd_vortex_pcm_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PCM Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c
index 57bbb87d0c62..8356180bfe0e 100644
--- a/sound/pci/aw2/aw2-alsa.c
+++ b/sound/pci/aw2/aw2-alsa.c
@@ -202,7 +202,7 @@ static const struct snd_pcm_ops snd_aw2_capture_ops = {
.pointer = snd_aw2_pcm_pointer_capture,
};
-static struct snd_kcontrol_new aw2_control = {
+static const struct snd_kcontrol_new aw2_control = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Route",
.index = 0,
diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c
index f2c0709d7441..099efb046b1c 100644
--- a/sound/pci/bt87x.c
+++ b/sound/pci/bt87x.c
@@ -598,7 +598,7 @@ static int snd_bt87x_capture_volume_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_bt87x_capture_volume = {
+static const struct snd_kcontrol_new snd_bt87x_capture_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Volume",
.info = snd_bt87x_capture_volume_info,
@@ -634,7 +634,7 @@ static int snd_bt87x_capture_boost_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_bt87x_capture_boost = {
+static const struct snd_kcontrol_new snd_bt87x_capture_boost = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Boost",
.info = snd_bt87x_capture_boost_info,
@@ -676,7 +676,7 @@ static int snd_bt87x_capture_source_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_bt87x_capture_source = {
+static const struct snd_kcontrol_new snd_bt87x_capture_source = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = snd_bt87x_capture_source_info,
diff --git a/sound/pci/ca0106/ca0106_mixer.c b/sound/pci/ca0106/ca0106_mixer.c
index 025805cba779..b4d3415331f6 100644
--- a/sound/pci/ca0106/ca0106_mixer.c
+++ b/sound/pci/ca0106/ca0106_mixer.c
@@ -301,7 +301,7 @@ static int snd_ca0106_capture_mic_line_in_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in =
+static const struct snd_kcontrol_new snd_ca0106_capture_mic_line_in =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Shared Mic/Line in Capture Switch",
@@ -310,7 +310,7 @@ static struct snd_kcontrol_new snd_ca0106_capture_mic_line_in =
.put = snd_ca0106_capture_mic_line_in_put
};
-static struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out =
+static const struct snd_kcontrol_new snd_ca0106_capture_line_in_side_out =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Shared Line in/Side out Capture Switch",
diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c
index aeedc270ed9b..745a0a3743b4 100644
--- a/sound/pci/cmipci.c
+++ b/sound/pci/cmipci.c
@@ -68,14 +68,14 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for C-Media PCI soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable C-Media PCI soundcard.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM port.");
module_param_array(soft_ac3, bool, NULL, 0444);
MODULE_PARM_DESC(soft_ac3, "Software-conversion of raw SPDIF packets (model 033 only).");
#ifdef SUPPORT_JOYSTICK
-module_param_array(joystick_port, int, NULL, 0444);
+module_param_hw_array(joystick_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(joystick_port, "Joystick port address.");
#endif
@@ -1045,7 +1045,7 @@ static int snd_cmipci_spdif_default_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_cmipci_spdif_default =
+static const struct snd_kcontrol_new snd_cmipci_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -1072,7 +1072,7 @@ static int snd_cmipci_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_cmipci_spdif_mask =
+static const struct snd_kcontrol_new snd_cmipci_spdif_mask =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1119,7 +1119,7 @@ static int snd_cmipci_spdif_stream_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_cmipci_spdif_stream =
+static const struct snd_kcontrol_new snd_cmipci_spdif_stream =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c
index fa7c51684dd2..f870697aca67 100644
--- a/sound/pci/cs4281.c
+++ b/sound/pci/cs4281.c
@@ -1055,7 +1055,7 @@ static int snd_cs4281_put_volume(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_dsp, -4650, 150, 0);
-static struct snd_kcontrol_new snd_cs4281_fm_vol =
+static const struct snd_kcontrol_new snd_cs4281_fm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Synth Playback Volume",
@@ -1066,7 +1066,7 @@ static struct snd_kcontrol_new snd_cs4281_fm_vol =
.tlv = { .p = db_scale_dsp },
};
-static struct snd_kcontrol_new snd_cs4281_pcm_vol =
+static const struct snd_kcontrol_new snd_cs4281_pcm_vol =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Stream Playback Volume",
diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c
index 937071760bc4..d15ecf9febbf 100644
--- a/sound/pci/echoaudio/echoaudio.c
+++ b/sound/pci/echoaudio/echoaudio.c
@@ -1039,7 +1039,7 @@ static int snd_echo_output_gain_put(struct snd_kcontrol *kcontrol,
#ifdef ECHOCARD_HAS_LINE_OUT_GAIN
/* On the Mia this one controls the line-out volume */
-static struct snd_kcontrol_new snd_echo_line_output_gain = {
+static const struct snd_kcontrol_new snd_echo_line_output_gain = {
.name = "Line Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -1050,7 +1050,7 @@ static struct snd_kcontrol_new snd_echo_line_output_gain = {
.tlv = {.p = db_scale_output_gain},
};
#else
-static struct snd_kcontrol_new snd_echo_pcm_output_gain = {
+static const struct snd_kcontrol_new snd_echo_pcm_output_gain = {
.name = "PCM Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -1120,7 +1120,7 @@ static int snd_echo_input_gain_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_input_gain, -2500, 50, 0);
-static struct snd_kcontrol_new snd_echo_line_input_gain = {
+static const struct snd_kcontrol_new snd_echo_line_input_gain = {
.name = "Line Capture Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -1184,7 +1184,7 @@ static int snd_echo_output_nominal_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_echo_output_nominal_level = {
+static const struct snd_kcontrol_new snd_echo_output_nominal_level = {
.name = "Line Playback Switch (-10dBV)",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_echo_output_nominal_info,
@@ -1250,7 +1250,7 @@ static int snd_echo_input_nominal_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_echo_intput_nominal_level = {
+static const struct snd_kcontrol_new snd_echo_intput_nominal_level = {
.name = "Line Capture Switch (-10dBV)",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_echo_input_nominal_info,
@@ -1477,7 +1477,7 @@ static int snd_echo_digital_mode_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_echo_digital_mode_switch = {
+static const struct snd_kcontrol_new snd_echo_digital_mode_switch = {
.name = "Digital mode Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_digital_mode_info,
@@ -1527,7 +1527,7 @@ static int snd_echo_spdif_mode_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_echo_spdif_mode_switch = {
+static const struct snd_kcontrol_new snd_echo_spdif_mode_switch = {
.name = "S/PDIF mode Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_spdif_mode_info,
@@ -1600,7 +1600,7 @@ static int snd_echo_clock_source_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_echo_clock_source_switch = {
+static const struct snd_kcontrol_new snd_echo_clock_source_switch = {
.name = "Sample Clock Source",
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.info = snd_echo_clock_source_info,
@@ -1643,7 +1643,7 @@ static int snd_echo_phantom_power_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_echo_phantom_power_switch = {
+static const struct snd_kcontrol_new snd_echo_phantom_power_switch = {
.name = "Phantom power Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_phantom_power_info,
@@ -1686,7 +1686,7 @@ static int snd_echo_automute_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_echo_automute_switch = {
+static const struct snd_kcontrol_new snd_echo_automute_switch = {
.name = "Digital Capture Switch (automute)",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.info = snd_echo_automute_info,
@@ -1713,7 +1713,7 @@ static int snd_echo_vumeters_switch_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new snd_echo_vumeters_switch = {
+static const struct snd_kcontrol_new snd_echo_vumeters_switch = {
.name = "VU-meters Switch",
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.access = SNDRV_CTL_ELEM_ACCESS_WRITE,
@@ -1751,7 +1751,7 @@ static int snd_echo_vumeters_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_echo_vumeters = {
+static const struct snd_kcontrol_new snd_echo_vumeters = {
.name = "VU-meters",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READ |
@@ -1804,7 +1804,7 @@ static int snd_echo_channels_info_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_echo_channels_info = {
+static const struct snd_kcontrol_new snd_echo_channels_info = {
.name = "Channels info",
.iface = SNDRV_CTL_ELEM_IFACE_HWDEP,
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c
index 32842734ada6..77a4413f4564 100644
--- a/sound/pci/emu10k1/emu10k1x.c
+++ b/sound/pci/emu10k1/emu10k1x.c
@@ -1112,7 +1112,7 @@ static int snd_emu10k1x_shared_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1x_shared_spdif =
+static const struct snd_kcontrol_new snd_emu10k1x_shared_spdif =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Analog/Digital Output Jack",
@@ -1171,7 +1171,7 @@ static int snd_emu10k1x_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
+static const struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1181,7 +1181,7 @@ static struct snd_kcontrol_new snd_emu10k1x_spdif_mask_control =
.get = snd_emu10k1x_spdif_get_mask
};
-static struct snd_kcontrol_new snd_emu10k1x_spdif_control =
+static const struct snd_kcontrol_new snd_emu10k1x_spdif_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
diff --git a/sound/pci/emu10k1/emumixer.c b/sound/pci/emu10k1/emumixer.c
index 076b117009c5..b2219a73c17c 100644
--- a/sound/pci/emu10k1/emumixer.c
+++ b/sound/pci/emu10k1/emumixer.c
@@ -795,7 +795,7 @@ static int snd_emu1010_internal_clock_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu1010_internal_clock =
+static const struct snd_kcontrol_new snd_emu1010_internal_clock =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
@@ -847,7 +847,7 @@ static int snd_emu1010_optical_out_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu1010_optical_out = {
+static const struct snd_kcontrol_new snd_emu1010_optical_out = {
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Optical Output Mode",
@@ -898,7 +898,7 @@ static int snd_emu1010_optical_in_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu1010_optical_in = {
+static const struct snd_kcontrol_new snd_emu1010_optical_in = {
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Optical Input Mode",
@@ -978,7 +978,7 @@ static int snd_audigy_i2c_capture_source_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_audigy_i2c_capture_source =
+static const struct snd_kcontrol_new snd_audigy_i2c_capture_source =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
@@ -1177,7 +1177,7 @@ static int snd_emu10k1_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
+static const struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1187,7 +1187,7 @@ static struct snd_kcontrol_new snd_emu10k1_spdif_mask_control =
.get = snd_emu10k1_spdif_get_mask
};
-static struct snd_kcontrol_new snd_emu10k1_spdif_control =
+static const struct snd_kcontrol_new snd_emu10k1_spdif_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -1293,7 +1293,7 @@ static int snd_emu10k1_send_routing_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_send_routing_control =
+static const struct snd_kcontrol_new snd_emu10k1_send_routing_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1364,7 +1364,7 @@ static int snd_emu10k1_send_volume_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_send_volume_control =
+static const struct snd_kcontrol_new snd_emu10k1_send_volume_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1429,7 +1429,7 @@ static int snd_emu10k1_attn_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_attn_control =
+static const struct snd_kcontrol_new snd_emu10k1_attn_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1501,7 +1501,7 @@ static int snd_emu10k1_efx_send_routing_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_efx_send_routing_control =
+static const struct snd_kcontrol_new snd_emu10k1_efx_send_routing_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1568,7 +1568,7 @@ static int snd_emu10k1_efx_send_volume_put(struct snd_kcontrol *kcontrol,
}
-static struct snd_kcontrol_new snd_emu10k1_efx_send_volume_control =
+static const struct snd_kcontrol_new snd_emu10k1_efx_send_volume_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1626,7 +1626,7 @@ static int snd_emu10k1_efx_attn_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_efx_attn_control =
+static const struct snd_kcontrol_new snd_emu10k1_efx_attn_control =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1691,7 +1691,7 @@ static int snd_emu10k1_shared_spdif_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_shared_spdif =
+static const struct snd_kcontrol_new snd_emu10k1_shared_spdif =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "SB Live Analog/Digital Output Jack",
@@ -1700,7 +1700,7 @@ static struct snd_kcontrol_new snd_emu10k1_shared_spdif =
.put = snd_emu10k1_shared_spdif_put
};
-static struct snd_kcontrol_new snd_audigy_shared_spdif =
+static const struct snd_kcontrol_new snd_audigy_shared_spdif =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Audigy Analog/Digital Output Jack",
@@ -1738,7 +1738,7 @@ static int snd_audigy_capture_boost_put(struct snd_kcontrol *kcontrol,
return snd_ac97_update(emu->ac97, AC97_REC_GAIN, val);
}
-static struct snd_kcontrol_new snd_audigy_capture_boost =
+static const struct snd_kcontrol_new snd_audigy_capture_boost =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mic Extra Boost",
diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c
index 37be1e14d756..ef1cf530c929 100644
--- a/sound/pci/emu10k1/emupcm.c
+++ b/sound/pci/emu10k1/emupcm.c
@@ -1542,7 +1542,7 @@ static int snd_emu10k1_pcm_efx_voices_mask_put(struct snd_kcontrol *kcontrol, st
return change;
}
-static struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
+static const struct snd_kcontrol_new snd_emu10k1_pcm_efx_voices_mask = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Captured FX8010 Outputs",
.info = snd_emu10k1_pcm_efx_voices_mask_info,
diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c
index 164adad91650..09a63ef41ef2 100644
--- a/sound/pci/ens1370.c
+++ b/sound/pci/ens1370.c
@@ -106,7 +106,7 @@ module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Ensoniq AudioPCI soundcard.");
#ifdef SUPPORT_JOYSTICK
#ifdef CHIP1371
-module_param_array(joystick_port, int, NULL, 0444);
+module_param_hw_array(joystick_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(joystick_port, "Joystick port address.");
#else
module_param_array(joystick, bool, NULL, 0444);
@@ -1530,7 +1530,7 @@ static int snd_es1373_rear_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ens1373_rear =
+static const struct snd_kcontrol_new snd_ens1373_rear =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "AC97 2ch->4ch Copy Switch",
@@ -1575,7 +1575,7 @@ static int snd_es1373_line_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new snd_ens1373_line =
+static const struct snd_kcontrol_new snd_ens1373_line =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Line In->Rear Out Switch",
diff --git a/sound/pci/hda/dell_wmi_helper.c b/sound/pci/hda/dell_wmi_helper.c
index 19d41da79f93..7efa7bd7acb2 100644
--- a/sound/pci/hda/dell_wmi_helper.c
+++ b/sound/pci/hda/dell_wmi_helper.c
@@ -2,11 +2,11 @@
* to be included from codec driver
*/
-#if IS_ENABLED(CONFIG_LEDS_DELL_NETBOOKS)
+#if IS_ENABLED(CONFIG_DELL_LAPTOP)
#include <linux/dell-led.h>
static int dell_led_value;
-static int (*dell_led_set_func)(int, int);
+static int (*dell_micmute_led_set_func)(int);
static void (*dell_old_cap_hook)(struct hda_codec *,
struct snd_kcontrol *,
struct snd_ctl_elem_value *);
@@ -18,7 +18,7 @@ static void update_dell_wmi_micmute_led(struct hda_codec *codec,
if (dell_old_cap_hook)
dell_old_cap_hook(codec, kcontrol, ucontrol);
- if (!ucontrol || !dell_led_set_func)
+ if (!ucontrol || !dell_micmute_led_set_func)
return;
if (strcmp("Capture Switch", ucontrol->id.name) == 0 && ucontrol->id.index == 0) {
/* TODO: How do I verify if it's a mono or stereo here? */
@@ -26,8 +26,8 @@ static void update_dell_wmi_micmute_led(struct hda_codec *codec,
if (val == dell_led_value)
return;
dell_led_value = val;
- if (dell_led_set_func)
- dell_led_set_func(DELL_LED_MICMUTE, dell_led_value);
+ if (dell_micmute_led_set_func)
+ dell_micmute_led_set_func(dell_led_value);
}
}
@@ -39,15 +39,15 @@ static void alc_fixup_dell_wmi(struct hda_codec *codec,
bool removefunc = false;
if (action == HDA_FIXUP_ACT_PROBE) {
- if (!dell_led_set_func)
- dell_led_set_func = symbol_request(dell_app_wmi_led_set);
- if (!dell_led_set_func) {
- codec_warn(codec, "Failed to find dell wmi symbol dell_app_wmi_led_set\n");
+ if (!dell_micmute_led_set_func)
+ dell_micmute_led_set_func = symbol_request(dell_micmute_led_set);
+ if (!dell_micmute_led_set_func) {
+ codec_warn(codec, "Failed to find dell wmi symbol dell_micmute_led_set\n");
return;
}
removefunc = true;
- if (dell_led_set_func(DELL_LED_MICMUTE, false) >= 0) {
+ if (dell_micmute_led_set_func(false) >= 0) {
dell_led_value = 0;
if (spec->gen.num_adc_nids > 1 && !spec->gen.dyn_adc_switch)
codec_dbg(codec, "Skipping micmute LED control due to several ADCs");
@@ -60,17 +60,17 @@ static void alc_fixup_dell_wmi(struct hda_codec *codec,
}
- if (dell_led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
- symbol_put(dell_app_wmi_led_set);
- dell_led_set_func = NULL;
+ if (dell_micmute_led_set_func && (action == HDA_FIXUP_ACT_FREE || removefunc)) {
+ symbol_put(dell_micmute_led_set);
+ dell_micmute_led_set_func = NULL;
dell_old_cap_hook = NULL;
}
}
-#else /* CONFIG_LEDS_DELL_NETBOOKS */
+#else /* CONFIG_DELL_LAPTOP */
static void alc_fixup_dell_wmi(struct hda_codec *codec,
const struct hda_fixup *fix, int action)
{
}
-#endif /* CONFIG_LEDS_DELL_NETBOOKS */
+#endif /* CONFIG_DELL_LAPTOP */
diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c
index a03cf68d0bcd..d3ea73171a3d 100644
--- a/sound/pci/hda/hda_auto_parser.c
+++ b/sound/pci/hda/hda_auto_parser.c
@@ -580,6 +580,7 @@ const char *hda_get_autocfg_input_label(struct hda_codec *codec,
has_multiple_pins = 1;
if (has_multiple_pins && type == AUTO_PIN_MIC)
has_multiple_pins &= check_mic_location_need(codec, cfg, input);
+ has_multiple_pins |= codec->force_pin_prefix;
return hda_get_input_pin_label(codec, &cfg->inputs[input],
cfg->inputs[input].pin,
has_multiple_pins);
diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c
index 8fd745cb3f36..70bb365a08d2 100644
--- a/sound/pci/hda/hda_codec.c
+++ b/sound/pci/hda/hda_codec.c
@@ -1965,7 +1965,7 @@ static int vmaster_mute_mode_put(struct snd_kcontrol *kcontrol,
return 1;
}
-static struct snd_kcontrol_new vmaster_mute_mode = {
+static const struct snd_kcontrol_new vmaster_mute_mode = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Mute-LED Mode",
.info = vmaster_mute_mode_info,
@@ -2705,7 +2705,7 @@ static int spdif_share_sw_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new spdif_share_sw = {
+static const struct snd_kcontrol_new spdif_share_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "IEC958 Default PCM Playback Switch",
.info = snd_ctl_boolean_mono_info,
diff --git a/sound/pci/hda/hda_codec.h b/sound/pci/hda/hda_codec.h
index f17f25245e52..d6fb2d5d01a7 100644
--- a/sound/pci/hda/hda_codec.h
+++ b/sound/pci/hda/hda_codec.h
@@ -256,6 +256,7 @@ struct hda_codec {
unsigned int dump_coef:1; /* dump processing coefs in codec proc file */
unsigned int power_save_node:1; /* advanced PM for each widget */
unsigned int auto_runtime_pm:1; /* enable automatic codec runtime pm */
+ unsigned int force_pin_prefix:1; /* Add location prefix */
#ifdef CONFIG_PM
unsigned long power_on_acct;
unsigned long power_off_acct;
diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c
index e7c8f4f076d5..2842c82363c0 100644
--- a/sound/pci/hda/hda_generic.c
+++ b/sound/pci/hda/hda_generic.c
@@ -196,6 +196,9 @@ static void parse_user_hints(struct hda_codec *codec)
val = snd_hda_get_bool_hint(codec, "hp_mic_detect");
if (val >= 0)
spec->suppress_hp_mic_detect = !val;
+ val = snd_hda_get_bool_hint(codec, "vmaster");
+ if (val >= 0)
+ spec->suppress_vmaster = !val;
if (!snd_hda_get_int_hint(codec, "mixer_nid", &val))
spec->mixer_nid = val;
@@ -1125,6 +1128,7 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
*index = 0;
if (cfg->line_outs == 1 && !spec->multi_ios &&
+ !codec->force_pin_prefix &&
!cfg->hp_outs && !cfg->speaker_outs)
return spec->vmaster_mute.hook ? "PCM" : "Master";
@@ -1132,6 +1136,7 @@ static const char *get_line_out_pfx(struct hda_codec *codec, int ch,
* use it master (or "PCM" if a vmaster hook is present)
*/
if (spec->multiout.num_dacs == 1 && !spec->mixer_nid &&
+ !codec->force_pin_prefix &&
!spec->multiout.hp_out_nid[0] && !spec->multiout.extra_out_nid[0])
return spec->vmaster_mute.hook ? "PCM" : "Master";
@@ -5031,7 +5036,7 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
}
/* if we have no master control, let's create it */
- if (!spec->no_analog &&
+ if (!spec->no_analog && !spec->suppress_vmaster &&
!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
err = snd_hda_add_vmaster(codec, "Master Playback Volume",
spec->vmaster_tlv, slave_pfxs,
@@ -5039,7 +5044,7 @@ int snd_hda_gen_build_controls(struct hda_codec *codec)
if (err < 0)
return err;
}
- if (!spec->no_analog &&
+ if (!spec->no_analog && !spec->suppress_vmaster &&
!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
err = __snd_hda_add_vmaster(codec, "Master Playback Switch",
NULL, slave_pfxs,
diff --git a/sound/pci/hda/hda_generic.h b/sound/pci/hda/hda_generic.h
index f66fc7e25e07..61772317de46 100644
--- a/sound/pci/hda/hda_generic.h
+++ b/sound/pci/hda/hda_generic.h
@@ -229,6 +229,7 @@ struct hda_gen_spec {
unsigned int add_jack_modes:1; /* add i/o jack mode enum ctls */
unsigned int power_down_unused:1; /* power down unused widgets */
unsigned int dac_min_mute:1; /* minimal = mute for DACs */
+ unsigned int suppress_vmaster:1; /* don't create vmaster kctls */
/* other internal flags */
unsigned int no_analog:1; /* digital I/O only */
diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c
index c8256a89375a..1770f085c2a6 100644
--- a/sound/pci/hda/hda_intel.c
+++ b/sound/pci/hda/hda_intel.c
@@ -53,7 +53,7 @@
#ifdef CONFIG_X86
/* for snoop control */
#include <asm/pgtable.h>
-#include <asm/cacheflush.h>
+#include <asm/set_memory.h>
#include <asm/cpufeature.h>
#endif
#include <sound/core.h>
@@ -77,6 +77,7 @@ enum {
POS_FIX_POSBUF,
POS_FIX_VIACOMBO,
POS_FIX_COMBO,
+ POS_FIX_SKL,
};
/* Defines for ATI HD Audio support in SB450 south bridge */
@@ -148,7 +149,7 @@ module_param_array(model, charp, NULL, 0444);
MODULE_PARM_DESC(model, "Use the given board model.");
module_param_array(position_fix, int, NULL, 0444);
MODULE_PARM_DESC(position_fix, "DMA pointer read method."
- "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO).");
+ "(-1 = system default, 0 = auto, 1 = LPIB, 2 = POSBUF, 3 = VIACOMBO, 4 = COMBO, 5 = SKL+).");
module_param_array(bdl_pos_adj, int, NULL, 0644);
MODULE_PARM_DESC(bdl_pos_adj, "BDL position adjustment offset.");
module_param_array(probe_mask, int, NULL, 0444);
@@ -369,8 +370,10 @@ enum {
#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_GLK(pci) ((pci)->vendor == 0x8086 && (pci)->device == 0x3198)
#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)
+ IS_KBL(pci) || IS_KBL_LP(pci) || IS_KBL_H(pci) || \
+ IS_GLK(pci)
static char *driver_short_names[] = {
[AZX_DRIVER_ICH] = "HDA Intel",
@@ -534,9 +537,101 @@ static void bxt_reduce_dma_latency(struct azx *chip)
{
u32 val;
- val = azx_readl(chip, SKL_EM4L);
+ val = azx_readl(chip, VS_EM4L);
val &= (0x3 << 20);
- azx_writel(chip, SKL_EM4L, val);
+ azx_writel(chip, VS_EM4L, val);
+}
+
+/*
+ * ML_LCAP bits:
+ * bit 0: 6 MHz Supported
+ * bit 1: 12 MHz Supported
+ * bit 2: 24 MHz Supported
+ * bit 3: 48 MHz Supported
+ * bit 4: 96 MHz Supported
+ * bit 5: 192 MHz Supported
+ */
+static int intel_get_lctl_scf(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ static int preferred_bits[] = { 2, 3, 1, 4, 5 };
+ u32 val, t;
+ int i;
+
+ val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCAP);
+
+ for (i = 0; i < ARRAY_SIZE(preferred_bits); i++) {
+ t = preferred_bits[i];
+ if (val & (1 << t))
+ return t;
+ }
+
+ dev_warn(chip->card->dev, "set audio clock frequency to 6MHz");
+ return 0;
+}
+
+static int intel_ml_lctl_set_power(struct azx *chip, int state)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ u32 val;
+ int timeout;
+
+ /*
+ * the codecs are sharing the first link setting by default
+ * If other links are enabled for stream, they need similar fix
+ */
+ val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+ val &= ~AZX_MLCTL_SPA;
+ val |= state << AZX_MLCTL_SPA_SHIFT;
+ writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+ /* wait for CPA */
+ timeout = 50;
+ while (timeout) {
+ if (((readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL)) &
+ AZX_MLCTL_CPA) == (state << AZX_MLCTL_CPA_SHIFT))
+ return 0;
+ timeout--;
+ udelay(10);
+ }
+
+ return -1;
+}
+
+static void intel_init_lctl(struct azx *chip)
+{
+ struct hdac_bus *bus = azx_bus(chip);
+ u32 val;
+ int ret;
+
+ /* 0. check lctl register value is correct or not */
+ val = readl(bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+ /* if SCF is already set, let's use it */
+ if ((val & ML_LCTL_SCF_MASK) != 0)
+ return;
+
+ /*
+ * Before operating on SPA, CPA must match SPA.
+ * Any deviation may result in undefined behavior.
+ */
+ if (((val & AZX_MLCTL_SPA) >> AZX_MLCTL_SPA_SHIFT) !=
+ ((val & AZX_MLCTL_CPA) >> AZX_MLCTL_CPA_SHIFT))
+ return;
+
+ /* 1. turn link down: set SPA to 0 and wait CPA to 0 */
+ ret = intel_ml_lctl_set_power(chip, 0);
+ udelay(100);
+ if (ret)
+ goto set_spa;
+
+ /* 2. update SCF to select a properly audio clock*/
+ val &= ~ML_LCTL_SCF_MASK;
+ val |= intel_get_lctl_scf(chip);
+ writel(val, bus->mlcap + AZX_ML_BASE + AZX_REG_ML_LCTL);
+
+set_spa:
+ /* 4. turn link up: set SPA to 1 and wait CPA to 1 */
+ intel_ml_lctl_set_power(chip, 1);
+ udelay(100);
}
static void hda_intel_init_chip(struct azx *chip, bool full_reset)
@@ -564,6 +659,9 @@ static void hda_intel_init_chip(struct azx *chip, bool full_reset)
/* reduce dma latency to avoid noise */
if (IS_BXT(pci))
bxt_reduce_dma_latency(chip);
+
+ if (bus->mlcap != NULL)
+ intel_init_lctl(chip);
}
/* calculate runtime delay from LPIB */
@@ -815,6 +913,31 @@ static unsigned int azx_via_get_position(struct azx *chip,
return bound_pos + mod_dma_pos;
}
+static unsigned int azx_skl_get_dpib_pos(struct azx *chip,
+ struct azx_dev *azx_dev)
+{
+ return _snd_hdac_chip_readl(azx_bus(chip),
+ AZX_REG_VS_SDXDPIB_XBASE +
+ (AZX_REG_VS_SDXDPIB_XINTERVAL *
+ azx_dev->core.index));
+}
+
+/* get the current DMA position with correction on SKL+ chips */
+static unsigned int azx_get_pos_skl(struct azx *chip, struct azx_dev *azx_dev)
+{
+ /* DPIB register gives a more accurate position for playback */
+ if (azx_dev->core.substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ return azx_skl_get_dpib_pos(chip, azx_dev);
+
+ /* For capture, we need to read posbuf, but it requires a delay
+ * for the possible boundary overlap; the read of DPIB fetches the
+ * actual posbuf
+ */
+ udelay(20);
+ azx_skl_get_dpib_pos(chip, azx_dev);
+ return azx_get_pos_posbuf(chip, azx_dev);
+}
+
#ifdef CONFIG_PM
static DEFINE_MUTEX(card_list_lock);
static LIST_HEAD(card_list);
@@ -1351,6 +1474,7 @@ static int check_position_fix(struct azx *chip, int fix)
case POS_FIX_POSBUF:
case POS_FIX_VIACOMBO:
case POS_FIX_COMBO:
+ case POS_FIX_SKL:
return fix;
}
@@ -1371,6 +1495,10 @@ static int check_position_fix(struct azx *chip, int fix)
dev_dbg(chip->card->dev, "Using LPIB position fix\n");
return POS_FIX_LPIB;
}
+ if (IS_SKL_PLUS(chip->pci)) {
+ dev_dbg(chip->card->dev, "Using SKL position fix\n");
+ return POS_FIX_SKL;
+ }
return POS_FIX_AUTO;
}
@@ -1382,6 +1510,7 @@ static void assign_position_fix(struct azx *chip, int fix)
[POS_FIX_POSBUF] = azx_get_pos_posbuf,
[POS_FIX_VIACOMBO] = azx_via_get_position,
[POS_FIX_COMBO] = azx_get_pos_lpib,
+ [POS_FIX_SKL] = azx_get_pos_skl,
};
chip->get_position[0] = chip->get_position[1] = callbacks[fix];
@@ -1390,7 +1519,7 @@ static void assign_position_fix(struct azx *chip, int fix)
if (fix == POS_FIX_COMBO)
chip->get_position[1] = NULL;
- if (fix == POS_FIX_POSBUF &&
+ if ((fix == POS_FIX_POSBUF || fix == POS_FIX_SKL) &&
(chip->driver_caps & AZX_DCAPS_COUNT_LPIB_DELAY)) {
chip->get_delay[0] = chip->get_delay[1] =
azx_get_delay_from_lpib;
diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c
index 07a9deb17477..a148176c16a9 100644
--- a/sound/pci/hda/patch_ca0132.c
+++ b/sound/pci/hda/patch_ca0132.c
@@ -857,7 +857,7 @@ static int chipio_write_address(struct hda_codec *codec,
chip_addx >> 16);
}
- spec->curr_chip_addx = (res < 0) ? ~0UL : chip_addx;
+ spec->curr_chip_addx = (res < 0) ? ~0U : chip_addx;
return res;
}
@@ -882,7 +882,7 @@ static int chipio_write_data(struct hda_codec *codec, unsigned int data)
/*If no error encountered, automatically increment the address
as per chip behaviour*/
spec->curr_chip_addx = (res != -EIO) ?
- (spec->curr_chip_addx + 4) : ~0UL;
+ (spec->curr_chip_addx + 4) : ~0U;
return res;
}
@@ -933,7 +933,7 @@ static int chipio_read_data(struct hda_codec *codec, unsigned int *data)
/*If no error encountered, automatically increment the address
as per chip behaviour*/
spec->curr_chip_addx = (res != -EIO) ?
- (spec->curr_chip_addx + 4) : ~0UL;
+ (spec->curr_chip_addx + 4) : ~0U;
return res;
}
@@ -1168,7 +1168,7 @@ static int dspio_write_multiple(struct hda_codec *codec,
int status = 0;
unsigned int count;
- if ((buffer == NULL))
+ if (buffer == NULL)
return -EINVAL;
count = 0;
@@ -1210,7 +1210,7 @@ static int dspio_read_multiple(struct hda_codec *codec, unsigned int *buffer,
unsigned int skip_count;
unsigned int dummy;
- if ((buffer == NULL))
+ if (buffer == NULL)
return -1;
count = 0;
diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c
index 69266b8ea2ad..63bc894ddf5e 100644
--- a/sound/pci/hda/patch_conexant.c
+++ b/sound/pci/hda/patch_conexant.c
@@ -52,6 +52,12 @@ struct conexant_spec {
bool dc_enable;
unsigned int dc_input_bias; /* offset into olpc_xo_dc_bias */
struct nid_path *dc_mode_path;
+
+ int mute_led_polarity;
+ unsigned int gpio_led;
+ unsigned int gpio_mute_led_mask;
+ unsigned int gpio_mic_led_mask;
+
};
@@ -264,6 +270,7 @@ enum {
CXT_FIXUP_HP_DOCK,
CXT_FIXUP_HP_SPECTRE,
CXT_FIXUP_HP_GATE_MIC,
+ CXT_FIXUP_MUTE_LED_GPIO,
};
/* for hda_fixup_thinkpad_acpi() */
@@ -646,6 +653,74 @@ static void cxt_fixup_hp_gate_mic_jack(struct hda_codec *codec,
snd_hda_jack_set_gating_jack(codec, 0x19, 0x16);
}
+/* update LED status via GPIO */
+static void cxt_update_gpio_led(struct hda_codec *codec, unsigned int mask,
+ bool enabled)
+{
+ struct conexant_spec *spec = codec->spec;
+ unsigned int oldval = spec->gpio_led;
+
+ if (spec->mute_led_polarity)
+ enabled = !enabled;
+
+ if (enabled)
+ spec->gpio_led &= ~mask;
+ else
+ spec->gpio_led |= mask;
+ if (spec->gpio_led != oldval)
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
+}
+
+/* turn on/off mute LED via GPIO per vmaster hook */
+static void cxt_fixup_gpio_mute_hook(void *private_data, int enabled)
+{
+ struct hda_codec *codec = private_data;
+ struct conexant_spec *spec = codec->spec;
+
+ cxt_update_gpio_led(codec, spec->gpio_mute_led_mask, enabled);
+}
+
+/* turn on/off mic-mute LED via GPIO per capture hook */
+static void cxt_fixup_gpio_mic_mute_hook(struct hda_codec *codec,
+ struct snd_kcontrol *kcontrol,
+ struct snd_ctl_elem_value *ucontrol)
+{
+ struct conexant_spec *spec = codec->spec;
+
+ if (ucontrol)
+ cxt_update_gpio_led(codec, spec->gpio_mic_led_mask,
+ ucontrol->value.integer.value[0] ||
+ ucontrol->value.integer.value[1]);
+}
+
+
+static void cxt_fixup_mute_led_gpio(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct conexant_spec *spec = codec->spec;
+ static const struct hda_verb gpio_init[] = {
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x03 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x03 },
+ {}
+ };
+ codec_info(codec, "action: %d gpio_led: %d\n", action, spec->gpio_led);
+
+ if (action == HDA_FIXUP_ACT_PRE_PROBE) {
+ spec->gen.vmaster_mute.hook = cxt_fixup_gpio_mute_hook;
+ spec->gen.cap_sync_hook = cxt_fixup_gpio_mic_mute_hook;
+ spec->gpio_led = 0;
+ spec->mute_led_polarity = 0;
+ spec->gpio_mute_led_mask = 0x01;
+ spec->gpio_mic_led_mask = 0x02;
+ }
+ snd_hda_add_verbs(codec, gpio_init);
+ if (spec->gpio_led)
+ snd_hda_codec_write(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
+ spec->gpio_led);
+}
+
+
/* ThinkPad X200 & co with cxt5051 */
static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
{ 0x16, 0x042140ff }, /* HP (seq# overridden) */
@@ -785,7 +860,9 @@ static const struct hda_fixup cxt_fixups[] = {
{ 0x16, 0x21011020 }, /* line-out */
{ 0x18, 0x2181103f }, /* line-in */
{ }
- }
+ },
+ .chained = true,
+ .chain_id = CXT_FIXUP_MUTE_LED_GPIO,
},
[CXT_FIXUP_HP_SPECTRE] = {
.type = HDA_FIXUP_PINS,
@@ -799,6 +876,10 @@ static const struct hda_fixup cxt_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = cxt_fixup_hp_gate_mic_jack,
},
+ [CXT_FIXUP_MUTE_LED_GPIO] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = cxt_fixup_mute_led_gpio,
+ },
};
static const struct snd_pci_quirk cxt5045_fixups[] = {
@@ -851,6 +932,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = {
SND_PCI_QUIRK(0x103c, 0x8079, "HP EliteBook 840 G3", CXT_FIXUP_HP_DOCK),
SND_PCI_QUIRK(0x103c, 0x8174, "HP Spectre x360", CXT_FIXUP_HP_SPECTRE),
SND_PCI_QUIRK(0x103c, 0x8115, "HP Z1 Gen3", CXT_FIXUP_HP_GATE_MIC),
+ SND_PCI_QUIRK(0x103c, 0x814f, "HP ZBook 15u G3", CXT_FIXUP_MUTE_LED_GPIO),
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),
@@ -882,6 +964,7 @@ static const struct hda_model_fixup cxt5066_fixup_models[] = {
{ .id = CXT_FIXUP_OLPC_XO, .name = "olpc-xo" },
{ .id = CXT_FIXUP_MUTE_LED_EAPD, .name = "mute-led-eapd" },
{ .id = CXT_FIXUP_HP_DOCK, .name = "hp-dock" },
+ { .id = CXT_FIXUP_MUTE_LED_GPIO, .name = "mute-led-gpio" },
{}
};
diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c
index 1461ef8eb749..90e4ff87445e 100644
--- a/sound/pci/hda/patch_hdmi.c
+++ b/sound/pci/hda/patch_hdmi.c
@@ -177,6 +177,7 @@ struct hdmi_spec {
bool i915_bound; /* was i915 bound in this driver? */
struct hdac_chmap chmap;
+ hda_nid_t vendor_nid;
};
#ifdef CONFIG_SND_HDA_I915
@@ -383,7 +384,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new eld_bytes_ctl = {
+static const struct snd_kcontrol_new eld_bytes_ctl = {
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "ELD",
@@ -2372,6 +2373,7 @@ static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
}
#define INTEL_VENDOR_NID 0x08
+#define INTEL_GLK_VENDOR_NID 0x0B
#define INTEL_GET_VENDOR_VERB 0xf81
#define INTEL_SET_VENDOR_VERB 0x781
#define INTEL_EN_DP12 0x02 /* enable DP 1.2 features */
@@ -2381,14 +2383,15 @@ static void intel_haswell_enable_all_pins(struct hda_codec *codec,
bool update_tree)
{
unsigned int vendor_param;
+ struct hdmi_spec *spec = codec->spec;
- vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
+ vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0,
INTEL_GET_VENDOR_VERB, 0);
if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
return;
vendor_param |= INTEL_EN_ALL_PIN_CVTS;
- vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
+ vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0,
INTEL_SET_VENDOR_VERB, vendor_param);
if (vendor_param == -1)
return;
@@ -2400,8 +2403,9 @@ static void intel_haswell_enable_all_pins(struct hda_codec *codec,
static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
{
unsigned int vendor_param;
+ struct hdmi_spec *spec = codec->spec;
- vendor_param = snd_hda_codec_read(codec, INTEL_VENDOR_NID, 0,
+ vendor_param = snd_hda_codec_read(codec, spec->vendor_nid, 0,
INTEL_GET_VENDOR_VERB, 0);
if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
return;
@@ -2409,7 +2413,7 @@ static void intel_haswell_fixup_enable_dp12(struct hda_codec *codec)
/* enable DP1.2 mode */
vendor_param |= INTEL_EN_DP12;
snd_hdac_regmap_add_vendor_verb(&codec->core, INTEL_SET_VENDOR_VERB);
- snd_hda_codec_write_cache(codec, INTEL_VENDOR_NID, 0,
+ snd_hda_codec_write_cache(codec, spec->vendor_nid, 0,
INTEL_SET_VENDOR_VERB, vendor_param);
}
@@ -2503,7 +2507,7 @@ static void i915_pin_cvt_fixup(struct hda_codec *codec,
}
/* Intel Haswell and onwards; audio component with eld notifier */
-static int patch_i915_hsw_hdmi(struct hda_codec *codec)
+static int intel_hsw_common_init(struct hda_codec *codec, hda_nid_t vendor_nid)
{
struct hdmi_spec *spec;
int err;
@@ -2520,6 +2524,7 @@ static int patch_i915_hsw_hdmi(struct hda_codec *codec)
spec = codec->spec;
codec->dp_mst = true;
spec->dyn_pcm_assign = true;
+ spec->vendor_nid = vendor_nid;
intel_haswell_enable_all_pins(codec, true);
intel_haswell_fixup_enable_dp12(codec);
@@ -2548,6 +2553,16 @@ static int patch_i915_hsw_hdmi(struct hda_codec *codec)
return 0;
}
+static int patch_i915_hsw_hdmi(struct hda_codec *codec)
+{
+ return intel_hsw_common_init(codec, INTEL_VENDOR_NID);
+}
+
+static int patch_i915_glk_hdmi(struct hda_codec *codec)
+{
+ return intel_hsw_common_init(codec, INTEL_GLK_VENDOR_NID);
+}
+
/* Intel Baytrail and Braswell; with eld notifier */
static int patch_i915_byt_hdmi(struct hda_codec *codec)
{
@@ -3800,7 +3815,7 @@ HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI", patch_i915_hsw_hdmi),
HDA_CODEC_ENTRY(0x8086280b, "Kabylake HDMI", patch_i915_hsw_hdmi),
-HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_hsw_hdmi),
+HDA_CODEC_ENTRY(0x8086280d, "Geminilake HDMI", patch_i915_glk_hdmi),
HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI", patch_i915_byt_hdmi),
HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI", patch_i915_byt_hdmi),
diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c
index 299835d1fbaa..58df440013c5 100644
--- a/sound/pci/hda/patch_realtek.c
+++ b/sound/pci/hda/patch_realtek.c
@@ -1800,6 +1800,7 @@ enum {
ALC882_FIXUP_NO_PRIMARY_HP,
ALC887_FIXUP_ASUS_BASS,
ALC887_FIXUP_BASS_CHMAP,
+ ALC1220_FIXUP_GB_DUAL_CODECS,
};
static void alc889_fixup_coef(struct hda_codec *codec,
@@ -1962,6 +1963,61 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
static void alc_fixup_bass_chmap(struct hda_codec *codec,
const struct hda_fixup *fix, int action);
+/* For dual-codec configuration, we need to disable some features to avoid
+ * conflicts of kctls and PCM streams
+ */
+static void alc_fixup_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix, int action)
+{
+ struct alc_spec *spec = codec->spec;
+
+ if (action != HDA_FIXUP_ACT_PRE_PROBE)
+ return;
+ /* disable vmaster */
+ spec->gen.suppress_vmaster = 1;
+ /* auto-mute and auto-mic switch don't work with multiple codecs */
+ spec->gen.suppress_auto_mute = 1;
+ spec->gen.suppress_auto_mic = 1;
+ /* disable aamix as well */
+ spec->gen.mixer_nid = 0;
+ /* add location prefix to avoid conflicts */
+ codec->force_pin_prefix = 1;
+}
+
+static void rename_ctl(struct hda_codec *codec, const char *oldname,
+ const char *newname)
+{
+ struct snd_kcontrol *kctl;
+
+ kctl = snd_hda_find_mixer_ctl(codec, oldname);
+ if (kctl)
+ strcpy(kctl->id.name, newname);
+}
+
+static void alc1220_fixup_gb_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ alc_fixup_dual_codecs(codec, fix, action);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* override card longname to provide a unique UCM profile */
+ strcpy(codec->card->longname, "HDAudio-Gigabyte-ALC1220DualCodecs");
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ /* rename Capture controls depending on the codec */
+ rename_ctl(codec, "Capture Volume",
+ codec->addr == 0 ?
+ "Rear-Panel Capture Volume" :
+ "Front-Panel Capture Volume");
+ rename_ctl(codec, "Capture Switch",
+ codec->addr == 0 ?
+ "Rear-Panel Capture Switch" :
+ "Front-Panel Capture Switch");
+ break;
+ }
+}
+
static const struct hda_fixup alc882_fixups[] = {
[ALC882_FIXUP_ABIT_AW9D_MAX] = {
.type = HDA_FIXUP_PINS,
@@ -2198,6 +2254,10 @@ static const struct hda_fixup alc882_fixups[] = {
.type = HDA_FIXUP_FUNC,
.v.func = alc_fixup_bass_chmap,
},
+ [ALC1220_FIXUP_GB_DUAL_CODECS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc1220_fixup_gb_dual_codecs,
+ },
};
static const struct snd_pci_quirk alc882_fixup_tbl[] = {
@@ -2267,6 +2327,7 @@ static const struct snd_pci_quirk alc882_fixup_tbl[] = {
SND_PCI_QUIRK(0x1462, 0x7350, "MSI-7350", ALC889_FIXUP_CD),
SND_PCI_QUIRK_VENDOR(0x1462, "MSI", ALC882_FIXUP_GPIO3),
SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte EP45-DS3/Z87X-UD3H", ALC889_FIXUP_FRONT_HP_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1458, 0xa0b8, "Gigabyte AZ370-Gaming", ALC1220_FIXUP_GB_DUAL_CODECS),
SND_PCI_QUIRK(0x147b, 0x107a, "Abit AW9D-MAX", ALC882_FIXUP_ABIT_AW9D_MAX),
SND_PCI_QUIRK_VENDOR(0x1558, "Clevo laptop", ALC882_FIXUP_EAPD),
SND_PCI_QUIRK(0x161f, 0x2054, "Medion laptop", ALC883_FIXUP_EAPD),
@@ -4663,7 +4724,6 @@ static void alc282_fixup_asus_tx300(struct hda_codec *codec,
{ 0x1b, 0x21114000 }, /* dock speaker pin */
{}
};
- struct snd_kcontrol *kctl;
switch (action) {
case HDA_FIXUP_ACT_PRE_PROBE:
@@ -4678,12 +4738,10 @@ static void alc282_fixup_asus_tx300(struct hda_codec *codec,
/* this is a bit tricky; give more sane names for the main
* (tablet) speaker and the dock speaker, respectively
*/
- kctl = snd_hda_find_mixer_ctl(codec, "Speaker Playback Switch");
- if (kctl)
- strcpy(kctl->id.name, "Dock Speaker Playback Switch");
- kctl = snd_hda_find_mixer_ctl(codec, "Bass Speaker Playback Switch");
- if (kctl)
- strcpy(kctl->id.name, "Speaker Playback Switch");
+ rename_ctl(codec, "Speaker Playback Switch",
+ "Dock Speaker Playback Switch");
+ rename_ctl(codec, "Bass Speaker Playback Switch",
+ "Speaker Playback Switch");
break;
}
}
@@ -4766,6 +4824,30 @@ static void alc280_fixup_hp_9480m(struct hda_codec *codec,
}
}
+static void alc233_alc662_fixup_lenovo_dual_codecs(struct hda_codec *codec,
+ const struct hda_fixup *fix,
+ int action)
+{
+ alc_fixup_dual_codecs(codec, fix, action);
+ switch (action) {
+ case HDA_FIXUP_ACT_PRE_PROBE:
+ /* override card longname to provide a unique UCM profile */
+ strcpy(codec->card->longname, "HDAudio-Lenovo-DualCodecs");
+ break;
+ case HDA_FIXUP_ACT_BUILD:
+ /* rename Capture controls depending on the codec */
+ rename_ctl(codec, "Capture Volume",
+ codec->addr == 0 ?
+ "Rear-Panel Capture Volume" :
+ "Front-Panel Capture Volume");
+ rename_ctl(codec, "Capture Switch",
+ codec->addr == 0 ?
+ "Rear-Panel Capture Switch" :
+ "Front-Panel Capture Switch");
+ break;
+ }
+}
+
/* for hda_fixup_thinkpad_acpi() */
#include "thinkpad_helper.c"
@@ -4833,6 +4915,8 @@ enum {
ALC290_FIXUP_SUBWOOFER_HSJACK,
ALC269_FIXUP_THINKPAD_ACPI,
ALC269_FIXUP_DMIC_THINKPAD_ACPI,
+ ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
+ ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC255_FIXUP_DELL2_MIC_NO_PRESENCE,
ALC255_FIXUP_HEADSET_MODE,
@@ -4872,6 +4956,12 @@ enum {
ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER,
ALC269_FIXUP_ATIV_BOOK_8,
ALC221_FIXUP_HP_MIC_NO_PRESENCE,
+ ALC256_FIXUP_ASUS_HEADSET_MODE,
+ ALC256_FIXUP_ASUS_MIC,
+ ALC256_FIXUP_ASUS_AIO_GPIO2,
+ ALC233_FIXUP_ASUS_MIC_NO_PRESENCE,
+ ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE,
+ ALC233_FIXUP_LENOVO_MULTI_CODECS,
};
static const struct hda_fixup alc269_fixups[] = {
@@ -5289,6 +5379,24 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_THINKPAD_ACPI,
},
+ [ALC255_FIXUP_ACER_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE
+ },
+ [ALC255_FIXUP_ASUS_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC255_FIXUP_HEADSET_MODE
+ },
[ALC255_FIXUP_DELL1_MIC_NO_PRESENCE] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
@@ -5579,6 +5687,54 @@ static const struct hda_fixup alc269_fixups[] = {
.chained = true,
.chain_id = ALC269_FIXUP_HEADSET_MODE
},
+ [ALC256_FIXUP_ASUS_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode,
+ },
+ [ALC256_FIXUP_ASUS_MIC] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x13, 0x90a60160 }, /* use as internal mic */
+ { 0x19, 0x04a11120 }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC256_FIXUP_ASUS_HEADSET_MODE
+ },
+ [ALC256_FIXUP_ASUS_AIO_GPIO2] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Set up GPIO2 for the speaker amp */
+ { 0x01, AC_VERB_SET_GPIO_MASK, 0x04 },
+ { 0x01, AC_VERB_SET_GPIO_DIRECTION, 0x04 },
+ { 0x01, AC_VERB_SET_GPIO_DATA, 0x04 },
+ {}
+ },
+ },
+ [ALC233_FIXUP_ASUS_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_PINS,
+ .v.pins = (const struct hda_pintbl[]) {
+ { 0x19, 0x01a1913c }, /* use as headset mic, without its own jack detect */
+ { }
+ },
+ .chained = true,
+ .chain_id = ALC269_FIXUP_HEADSET_MIC
+ },
+ [ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE] = {
+ .type = HDA_FIXUP_VERBS,
+ .v.verbs = (const struct hda_verb[]) {
+ /* Enables internal speaker */
+ {0x20, AC_VERB_SET_COEF_INDEX, 0x40},
+ {0x20, AC_VERB_SET_PROC_COEF, 0x8800},
+ {}
+ },
+ .chained = true,
+ .chain_id = ALC233_FIXUP_ASUS_MIC_NO_PRESENCE
+ },
+ [ALC233_FIXUP_LENOVO_MULTI_CODECS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_alc662_fixup_lenovo_dual_codecs,
+ },
};
static const struct snd_pci_quirk alc269_fixup_tbl[] = {
@@ -5692,15 +5848,27 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x103c, 0x8256, "HP", ALC221_FIXUP_HP_FRONT_MIC),
SND_PCI_QUIRK(0x103c, 0x82bf, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
SND_PCI_QUIRK(0x103c, 0x82c0, "HP", ALC221_FIXUP_HP_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x103e, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x103f, "ASUS TX300", ALC282_FIXUP_ASUS_TX300),
SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x10c0, "ASUS X540SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x12f0, "ASUS X541UV", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC),
SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK),
SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A),
SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW),
SND_PCI_QUIRK(0x1043, 0x1b13, "Asus U41SV", ALC269_FIXUP_INV_DMIC),
SND_PCI_QUIRK(0x1043, 0x1c23, "Asus X55U", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+ SND_PCI_QUIRK(0x1043, 0x1bbd, "ASUS Z550MA", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x10d0, "ASUS X540LA/X540LJ", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x11c0, "ASUS X556UR", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1290, "ASUS X441SA", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x12a0, "ASUS X441UV", ALC233_FIXUP_EAPD_COEF_AND_MIC_NO_PRESENCE),
+ SND_PCI_QUIRK(0x1043, 0x1ccd, "ASUS X555UB", ALC256_FIXUP_ASUS_MIC),
+ SND_PCI_QUIRK(0x1043, 0x3030, "ASUS ZN270IE", ALC256_FIXUP_ASUS_AIO_GPIO2),
SND_PCI_QUIRK(0x1043, 0x831a, "ASUS P901", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x834a, "ASUS S101", ALC269_FIXUP_STEREO_DMIC),
SND_PCI_QUIRK(0x1043, 0x8398, "ASUS P1005", ALC269_FIXUP_STEREO_DMIC),
@@ -5721,6 +5889,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
SND_PCI_QUIRK(0x144d, 0xc740, "Samsung Ativ book 8 (NP870Z5G)", ALC269_FIXUP_ATIV_BOOK_8),
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, 0x1036, "Lenovo P520", ALC233_FIXUP_LENOVO_MULTI_CODECS),
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),
@@ -5875,6 +6044,18 @@ static const struct hda_model_fixup alc269_fixup_models[] = {
{0x21, 0x03211020}
static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1025, "Acer", ALC255_FIXUP_ACER_MIC_NO_PRESENCE,
+ {0x12, 0x90a601c0},
+ {0x14, 0x90171120},
+ {0x21, 0x02211030}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x14, 0x90170110},
+ {0x1b, 0x90a70130},
+ {0x21, 0x03211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0255, 0x1043, "ASUS", ALC255_FIXUP_ASUS_MIC_NO_PRESENCE,
+ {0x1a, 0x90a70130},
+ {0x1b, 0x90170110},
+ {0x21, 0x03211020}),
SND_HDA_PIN_QUIRK(0x10ec0225, 0x1028, "Dell", ALC225_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC225_STANDARD_PINS,
{0x12, 0xb7a60130},
@@ -5995,6 +6176,14 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = {
{0x21, 0x02211020}),
SND_HDA_PIN_QUIRK(0x10ec0256, 0x1028, "Dell", ALC255_FIXUP_DELL1_MIC_NO_PRESENCE,
ALC256_STANDARD_PINS),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC,
+ {0x14, 0x90170110},
+ {0x1b, 0x90a70130},
+ {0x21, 0x04211020}),
+ SND_HDA_PIN_QUIRK(0x10ec0256, 0x1043, "ASUS", ALC256_FIXUP_ASUS_MIC,
+ {0x14, 0x90170110},
+ {0x1b, 0x90a70130},
+ {0x21, 0x03211020}),
SND_HDA_PIN_QUIRK(0x10ec0280, 0x103c, "HP", ALC280_FIXUP_HP_GPIO4,
{0x12, 0x90a60130},
{0x14, 0x90170110},
@@ -6714,6 +6903,7 @@ enum {
ALC668_FIXUP_DELL_DISABLE_AAMIX,
ALC668_FIXUP_DELL_XPS13,
ALC662_FIXUP_ASUS_Nx50,
+ ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE,
ALC668_FIXUP_ASUS_Nx51,
ALC891_FIXUP_HEADSET_MODE,
ALC891_FIXUP_DELL_MIC_NO_PRESENCE,
@@ -6721,6 +6911,7 @@ enum {
ALC892_FIXUP_ASROCK_MOBO,
ALC662_FIXUP_USI_FUNC,
ALC662_FIXUP_USI_HEADSET_MODE,
+ ALC662_FIXUP_LENOVO_MULTI_CODECS,
};
static const struct hda_fixup alc662_fixups[] = {
@@ -6967,14 +7158,21 @@ static const struct hda_fixup alc662_fixups[] = {
.chained = true,
.chain_id = ALC662_FIXUP_BASS_1A
},
+ [ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc_fixup_headset_mode_alc668,
+ .chain_id = ALC662_FIXUP_BASS_CHMAP
+ },
[ALC668_FIXUP_ASUS_Nx51] = {
.type = HDA_FIXUP_PINS,
.v.pins = (const struct hda_pintbl[]) {
- {0x1a, 0x90170151}, /* bass speaker */
+ { 0x19, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+ { 0x1a, 0x90170151 }, /* bass speaker */
+ { 0x1b, 0x03a1113c }, /* use as headset mic, without its own jack detect */
{}
},
.chained = true,
- .chain_id = ALC662_FIXUP_BASS_CHMAP,
+ .chain_id = ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE,
},
[ALC891_FIXUP_HEADSET_MODE] = {
.type = HDA_FIXUP_FUNC,
@@ -7019,6 +7217,10 @@ static const struct hda_fixup alc662_fixups[] = {
.chained = true,
.chain_id = ALC662_FIXUP_USI_FUNC
},
+ [ALC662_FIXUP_LENOVO_MULTI_CODECS] = {
+ .type = HDA_FIXUP_FUNC,
+ .v.func = alc233_alc662_fixup_lenovo_dual_codecs,
+ },
};
static const struct snd_pci_quirk alc662_fixup_tbl[] = {
@@ -7056,6 +7258,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = {
SND_PCI_QUIRK(0x105b, 0x0cd6, "Foxconn", ALC662_FIXUP_ASUS_MODE2),
SND_PCI_QUIRK(0x144d, 0xc051, "Samsung R720", ALC662_FIXUP_IDEAPAD),
SND_PCI_QUIRK(0x14cd, 0x5003, "USI", ALC662_FIXUP_USI_HEADSET_MODE),
+ SND_PCI_QUIRK(0x17aa, 0x1036, "Lenovo P520", ALC662_FIXUP_LENOVO_MULTI_CODECS),
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(0x1849, 0x5892, "ASRock B150M", ALC892_FIXUP_ASROCK_MOBO),
diff --git a/sound/pci/ice1712/delta.c b/sound/pci/ice1712/delta.c
index 3bfdc78cbc5f..da5f37b7fdd0 100644
--- a/sound/pci/ice1712/delta.c
+++ b/sound/pci/ice1712/delta.c
@@ -432,7 +432,7 @@ static int snd_ice1712_delta1010lt_wordclock_status_get(struct snd_kcontrol *kco
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status =
+static const struct snd_kcontrol_new snd_ice1712_delta1010lt_wordclock_status =
{
.access = (SNDRV_CTL_ELEM_ACCESS_READ),
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c
index 5cb587cf360e..ec07136fc288 100644
--- a/sound/pci/ice1712/ews.c
+++ b/sound/pci/ice1712/ews.c
@@ -719,7 +719,7 @@ static int snd_ice1712_ews88mt_input_sense_put(struct snd_kcontrol *kcontrol, st
return ndata != data;
}
-static struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense = {
+static const struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Input Sensitivity Switch",
.info = snd_ice1712_ewx_io_sense_info,
@@ -728,7 +728,7 @@ static struct snd_kcontrol_new snd_ice1712_ews88mt_input_sense = {
.count = 8,
};
-static struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense = {
+static const struct snd_kcontrol_new snd_ice1712_ews88mt_output_sense = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Output Sensitivity Switch",
.info = snd_ice1712_ewx_io_sense_info,
diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c
index b4aa4c1370a8..1d8612cabb9e 100644
--- a/sound/pci/ice1712/ice1712.c
+++ b/sound/pci/ice1712/ice1712.c
@@ -279,7 +279,7 @@ static int snd_ice1712_digmix_route_ac97_put(struct snd_kcontrol *kcontrol, stru
return val != nval;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_digmix_route_ac97 = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_digmix_route_ac97 = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Digital Mixer To AC97",
.info = snd_ice1712_digmix_route_ac97_info,
@@ -1410,7 +1410,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_switch = {
.private_value = 10,
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, SWITCH),
.info = snd_ice1712_pro_mixer_switch_info,
@@ -1432,7 +1432,7 @@ static struct snd_kcontrol_new snd_ice1712_multi_capture_analog_volume = {
.tlv = { .p = db_scale_playback }
};
-static struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume = {
+static const struct snd_kcontrol_new snd_ice1712_multi_capture_spdif_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("Multi ", CAPTURE, VOLUME),
.info = snd_ice1712_pro_mixer_volume_info,
@@ -1630,7 +1630,7 @@ static int snd_ice1712_eeprom_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_eeprom = {
+static const struct snd_kcontrol_new snd_ice1712_eeprom = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "ICE1712 EEPROM",
.access = SNDRV_CTL_ELEM_ACCESS_READ,
@@ -1666,7 +1666,7 @@ static int snd_ice1712_spdif_default_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_spdif_default =
+static const struct snd_kcontrol_new snd_ice1712_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
@@ -1717,7 +1717,7 @@ static int snd_ice1712_spdif_maskp_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_spdif_maskc =
+static const struct snd_kcontrol_new snd_ice1712_spdif_maskc =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1726,7 +1726,7 @@ static struct snd_kcontrol_new snd_ice1712_spdif_maskc =
.get = snd_ice1712_spdif_maskc_get,
};
-static struct snd_kcontrol_new snd_ice1712_spdif_maskp =
+static const struct snd_kcontrol_new snd_ice1712_spdif_maskp =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1753,7 +1753,7 @@ static int snd_ice1712_spdif_stream_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_spdif_stream =
+static const struct snd_kcontrol_new snd_ice1712_spdif_stream =
{
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_INACTIVE),
@@ -1878,7 +1878,7 @@ static int snd_ice1712_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_internal_clock = {
+static const struct snd_kcontrol_new snd_ice1712_pro_internal_clock = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock",
.info = snd_ice1712_pro_internal_clock_info,
@@ -1943,7 +1943,7 @@ static int snd_ice1712_pro_internal_clock_default_put(struct snd_kcontrol *kcont
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_internal_clock_default = {
+static const struct snd_kcontrol_new snd_ice1712_pro_internal_clock_default = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock Default",
.info = snd_ice1712_pro_internal_clock_default_info,
@@ -1974,7 +1974,7 @@ static int snd_ice1712_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_rate_locking = {
+static const struct snd_kcontrol_new snd_ice1712_pro_rate_locking = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Locking",
.info = snd_ice1712_pro_rate_locking_info,
@@ -2005,7 +2005,7 @@ static int snd_ice1712_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_pro_rate_reset = {
+static const struct snd_kcontrol_new snd_ice1712_pro_rate_reset = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Reset",
.info = snd_ice1712_pro_rate_reset_info,
@@ -2173,7 +2173,7 @@ static struct snd_kcontrol_new snd_ice1712_mixer_pro_analog_route = {
.put = snd_ice1712_pro_route_analog_put,
};
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_spdif_route = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",
.info = snd_ice1712_pro_route_info,
@@ -2215,7 +2215,7 @@ static int snd_ice1712_pro_volume_rate_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_volume_rate = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_volume_rate = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Volume Rate",
.info = snd_ice1712_pro_volume_rate_info,
@@ -2248,7 +2248,7 @@ static int snd_ice1712_pro_peak_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ice1712_mixer_pro_peak = {
+static const struct snd_kcontrol_new snd_ice1712_mixer_pro_peak = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Multi Track Peak",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c
index 842744e7a139..9cd6e55c0642 100644
--- a/sound/pci/ice1712/ice1724.c
+++ b/sound/pci/ice1712/ice1724.c
@@ -1598,7 +1598,7 @@ static int snd_vt1724_eeprom_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_vt1724_eeprom = {
+static const struct snd_kcontrol_new snd_vt1724_eeprom = {
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "ICE1724 EEPROM",
.access = SNDRV_CTL_ELEM_ACCESS_READ,
@@ -1711,7 +1711,7 @@ static int snd_vt1724_spdif_default_put(struct snd_kcontrol *kcontrol,
return val != old;
}
-static struct snd_kcontrol_new snd_vt1724_spdif_default =
+static const struct snd_kcontrol_new snd_vt1724_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
@@ -1743,7 +1743,7 @@ static int snd_vt1724_spdif_maskp_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_vt1724_spdif_maskc =
+static const struct snd_kcontrol_new snd_vt1724_spdif_maskc =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1752,7 +1752,7 @@ static struct snd_kcontrol_new snd_vt1724_spdif_maskc =
.get = snd_vt1724_spdif_maskc_get,
};
-static struct snd_kcontrol_new snd_vt1724_spdif_maskp =
+static const struct snd_kcontrol_new snd_vt1724_spdif_maskp =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1789,7 +1789,7 @@ static int snd_vt1724_spdif_sw_put(struct snd_kcontrol *kcontrol,
return old != val;
}
-static struct snd_kcontrol_new snd_vt1724_spdif_switch =
+static const struct snd_kcontrol_new snd_vt1724_spdif_switch =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
/* FIXME: the following conflict with IEC958 Playback Route */
@@ -1964,7 +1964,7 @@ static int snd_vt1724_pro_internal_clock_put(struct snd_kcontrol *kcontrol,
return old_rate != new_rate;
}
-static struct snd_kcontrol_new snd_vt1724_pro_internal_clock = {
+static const struct snd_kcontrol_new snd_vt1724_pro_internal_clock = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Internal Clock",
.info = snd_vt1724_pro_internal_clock_info,
@@ -1995,7 +1995,7 @@ static int snd_vt1724_pro_rate_locking_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_vt1724_pro_rate_locking = {
+static const struct snd_kcontrol_new snd_vt1724_pro_rate_locking = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Locking",
.info = snd_vt1724_pro_rate_locking_info,
@@ -2026,7 +2026,7 @@ static int snd_vt1724_pro_rate_reset_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_vt1724_pro_rate_reset = {
+static const struct snd_kcontrol_new snd_vt1724_pro_rate_reset = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Multi Track Rate Reset",
.info = snd_vt1724_pro_rate_reset_info,
@@ -2151,7 +2151,7 @@ static struct snd_kcontrol_new snd_vt1724_mixer_pro_analog_route =
.put = snd_vt1724_pro_route_analog_put,
};
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route = {
+static const struct snd_kcontrol_new snd_vt1724_mixer_pro_spdif_route = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, NONE) "Route",
.info = snd_vt1724_pro_route_info,
@@ -2187,7 +2187,7 @@ static int snd_vt1724_pro_peak_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_vt1724_mixer_pro_peak = {
+static const struct snd_kcontrol_new snd_vt1724_mixer_pro_peak = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "Multi Track Peak",
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c
index 9720a30dbfff..6d17b171c17b 100644
--- a/sound/pci/intel8x0.c
+++ b/sound/pci/intel8x0.c
@@ -40,7 +40,9 @@
#include <sound/initval.h>
/* for 440MX workaround */
#include <asm/pgtable.h>
-#include <asm/cacheflush.h>
+#ifdef CONFIG_X86
+#include <asm/set_memory.h>
+#endif
MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>");
MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455");
diff --git a/sound/pci/lola/lola_mixer.c b/sound/pci/lola/lola_mixer.c
index e7fe15dd5a90..cb25acf7bc49 100644
--- a/sound/pci/lola/lola_mixer.c
+++ b/sound/pci/lola/lola_mixer.c
@@ -645,7 +645,7 @@ static int lola_input_src_put(struct snd_kcontrol *kcontrol,
return lola_set_src_config(chip, mask, true);
}
-static struct snd_kcontrol_new lola_input_src_mixer = {
+static const struct snd_kcontrol_new lola_input_src_mixer = {
.name = "Digital SRC Capture Switch",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = lola_input_src_info,
diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c
index c0f0c349c3ec..f9c3e86d55d5 100644
--- a/sound/pci/lx6464es/lx6464es.c
+++ b/sound/pci/lx6464es/lx6464es.c
@@ -899,7 +899,7 @@ static int lx_control_playback_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new lx_control_playback_switch = {
+static const struct snd_kcontrol_new lx_control_playback_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.index = 0,
diff --git a/sound/pci/mixart/mixart_mixer.c b/sound/pci/mixart/mixart_mixer.c
index 51e53497f0ad..4a4616aac787 100644
--- a/sound/pci/mixart/mixart_mixer.c
+++ b/sound/pci/mixart/mixart_mixer.c
@@ -448,7 +448,7 @@ static int mixart_audio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_ele
return changed;
}
-static struct snd_kcontrol_new mixart_control_output_switch = {
+static const struct snd_kcontrol_new mixart_control_output_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.info = mixart_sw_info, /* shared */
@@ -1024,7 +1024,7 @@ static int mixart_monitor_vol_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
return changed;
}
-static struct snd_kcontrol_new mixart_control_monitor_vol = {
+static const struct snd_kcontrol_new mixart_control_monitor_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -1091,7 +1091,7 @@ static int mixart_monitor_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return (changed != 0);
}
-static struct snd_kcontrol_new mixart_control_monitor_sw = {
+static const struct snd_kcontrol_new mixart_control_monitor_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Switch",
.info = mixart_sw_info, /* shared */
diff --git a/sound/pci/oxygen/oxygen.c b/sound/pci/oxygen/oxygen.c
index 74afb6b75976..e36ed8af55ad 100644
--- a/sound/pci/oxygen/oxygen.c
+++ b/sound/pci/oxygen/oxygen.c
@@ -767,6 +767,8 @@ static int get_oxygen_model(struct oxygen *chip,
[MODEL_FANTASIA] = "TempoTec HiFier Fantasia",
[MODEL_SERENADE] = "TempoTec HiFier Serenade",
[MODEL_HG2PCI] = "CMI8787-HG2PCI",
+ [MODEL_XONAR_DG] = "Xonar DG",
+ [MODEL_XONAR_DGX] = "Xonar DGX",
};
chip->model = model_generic;
@@ -829,12 +831,8 @@ static int get_oxygen_model(struct oxygen *chip,
chip->model.dac_channels_mixer = 2;
break;
case MODEL_XONAR_DG:
- chip->model = model_xonar_dg;
- chip->model.shortname = "Xonar DG";
- break;
case MODEL_XONAR_DGX:
chip->model = model_xonar_dg;
- chip->model.shortname = "Xonar DGX";
break;
}
if (id->driver_data == MODEL_MERIDIAN ||
diff --git a/sound/pci/pcxhr/pcxhr_mix22.c b/sound/pci/pcxhr/pcxhr_mix22.c
index 6a56e5306a65..8b4d0282efb8 100644
--- a/sound/pci/pcxhr/pcxhr_mix22.c
+++ b/sound/pci/pcxhr/pcxhr_mix22.c
@@ -744,7 +744,7 @@ static int hr222_mic_vol_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new hr222_control_mic_level = {
+static const struct snd_kcontrol_new hr222_control_mic_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -794,7 +794,7 @@ static int hr222_mic_boost_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new hr222_control_mic_boost = {
+static const struct snd_kcontrol_new hr222_control_mic_boost = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -836,7 +836,7 @@ static int hr222_phantom_power_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new hr222_phantom_power_switch = {
+static const struct snd_kcontrol_new hr222_phantom_power_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Phantom Power Switch",
.info = hr222_phantom_power_info,
diff --git a/sound/pci/pcxhr/pcxhr_mixer.c b/sound/pci/pcxhr/pcxhr_mixer.c
index 63136c4f3f3d..36875df30dbf 100644
--- a/sound/pci/pcxhr/pcxhr_mixer.c
+++ b/sound/pci/pcxhr/pcxhr_mixer.c
@@ -235,7 +235,7 @@ static int pcxhr_audio_sw_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new pcxhr_control_output_switch = {
+static const struct snd_kcontrol_new pcxhr_control_output_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Master Playback Switch",
.info = pcxhr_sw_info, /* shared */
@@ -460,7 +460,7 @@ static int pcxhr_pcm_sw_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new pcxhr_control_pcm_switch = {
+static const struct snd_kcontrol_new pcxhr_control_pcm_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Switch",
.count = PCXHR_PLAYBACK_STREAMS,
@@ -509,7 +509,7 @@ static int pcxhr_monitor_vol_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new pcxhr_control_monitor_vol = {
+static const struct snd_kcontrol_new pcxhr_control_monitor_vol = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -562,7 +562,7 @@ static int pcxhr_monitor_sw_put(struct snd_kcontrol *kcontrol,
return (changed != 0);
}
-static struct snd_kcontrol_new pcxhr_control_monitor_sw = {
+static const struct snd_kcontrol_new pcxhr_control_monitor_sw = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitoring Playback Switch",
.info = pcxhr_sw_info, /* shared */
@@ -697,7 +697,7 @@ static int pcxhr_audio_src_put(struct snd_kcontrol *kcontrol,
return ret;
}
-static struct snd_kcontrol_new pcxhr_control_audio_src = {
+static const struct snd_kcontrol_new pcxhr_control_audio_src = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Capture Source",
.info = pcxhr_audio_src_info,
@@ -798,7 +798,7 @@ static int pcxhr_clock_type_put(struct snd_kcontrol *kcontrol,
return ret;
}
-static struct snd_kcontrol_new pcxhr_control_clock_type = {
+static const struct snd_kcontrol_new pcxhr_control_clock_type = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Clock Mode",
.info = pcxhr_clock_type_info,
@@ -842,7 +842,7 @@ static int pcxhr_clock_rate_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new pcxhr_control_clock_rate = {
+static const struct snd_kcontrol_new pcxhr_control_clock_rate = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_CARD,
.name = "Clock Rates",
@@ -1017,14 +1017,14 @@ static int pcxhr_iec958_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new pcxhr_control_playback_iec958_mask = {
+static const struct snd_kcontrol_new pcxhr_control_playback_iec958_mask = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,MASK),
.info = pcxhr_iec958_info,
.get = pcxhr_iec958_mask_get
};
-static struct snd_kcontrol_new pcxhr_control_playback_iec958 = {
+static const struct snd_kcontrol_new pcxhr_control_playback_iec958 = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
.info = pcxhr_iec958_info,
@@ -1033,14 +1033,14 @@ static struct snd_kcontrol_new pcxhr_control_playback_iec958 = {
.private_value = 0 /* playback */
};
-static struct snd_kcontrol_new pcxhr_control_capture_iec958_mask = {
+static const struct snd_kcontrol_new pcxhr_control_capture_iec958_mask = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,MASK),
.info = pcxhr_iec958_info,
.get = pcxhr_iec958_mask_get
};
-static struct snd_kcontrol_new pcxhr_control_capture_iec958 = {
+static const struct snd_kcontrol_new pcxhr_control_capture_iec958 = {
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",CAPTURE,DEFAULT),
diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c
index 19c9df6b0f3d..f067c76d77f8 100644
--- a/sound/pci/riptide/riptide.c
+++ b/sound/pci/riptide/riptide.c
@@ -137,12 +137,12 @@ MODULE_PARM_DESC(id, "ID string for Riptide soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Riptide soundcard.");
#ifdef SUPPORT_JOYSTICK
-module_param_array(joystick_port, int, NULL, 0444);
+module_param_hw_array(joystick_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(joystick_port, "Joystick port # for Riptide soundcard.");
#endif
-module_param_array(mpu_port, int, NULL, 0444);
+module_param_hw_array(mpu_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU401 port # for Riptide driver.");
-module_param_array(opl3_port, int, NULL, 0444);
+module_param_hw_array(opl3_port, int, ioport, NULL, 0444);
MODULE_PARM_DESC(opl3_port, "OPL3 port # for Riptide driver.");
/*
diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c
index a6aa48c5b969..8e3d4ec39c35 100644
--- a/sound/pci/sonicvibes.c
+++ b/sound/pci/sonicvibes.c
@@ -66,7 +66,7 @@ module_param_array(reverb, bool, NULL, 0444);
MODULE_PARM_DESC(reverb, "Enable reverb (SRAM is present) for S3 SonicVibes soundcard.");
module_param_array(mge, bool, NULL, 0444);
MODULE_PARM_DESC(mge, "MIC Gain Enable for S3 SonicVibes soundcard.");
-module_param(dmaio, uint, 0444);
+module_param_hw(dmaio, uint, ioport, 0444);
MODULE_PARM_DESC(dmaio, "DDMA i/o base address for S3 SonicVibes soundcard.");
/*
diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c
index 92ad2d7a6bf8..64d3b8eba4bb 100644
--- a/sound/pci/trident/trident_main.c
+++ b/sound/pci/trident/trident_main.c
@@ -2356,7 +2356,7 @@ static int snd_trident_spdif_control_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_spdif_control =
+static const struct snd_kcontrol_new snd_trident_spdif_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,SWITCH),
@@ -2419,7 +2419,7 @@ static int snd_trident_spdif_default_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_spdif_default =
+static const struct snd_kcontrol_new snd_trident_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -2452,7 +2452,7 @@ static int snd_trident_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_trident_spdif_mask =
+static const struct snd_kcontrol_new snd_trident_spdif_mask =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -2514,7 +2514,7 @@ static int snd_trident_spdif_stream_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_spdif_stream =
+static const struct snd_kcontrol_new snd_trident_spdif_stream =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -2564,7 +2564,7 @@ static int snd_trident_ac97_control_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_ac97_rear_control =
+static const struct snd_kcontrol_new snd_trident_ac97_rear_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Rear Path",
@@ -2622,7 +2622,7 @@ static int snd_trident_vol_control_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_vol_music_control =
+static const struct snd_kcontrol_new snd_trident_vol_music_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Music Playback Volume",
@@ -2633,7 +2633,7 @@ static struct snd_kcontrol_new snd_trident_vol_music_control =
.tlv = { .p = db_scale_gvol },
};
-static struct snd_kcontrol_new snd_trident_vol_wave_control =
+static const struct snd_kcontrol_new snd_trident_vol_wave_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Wave Playback Volume",
@@ -2700,7 +2700,7 @@ static int snd_trident_pcm_vol_control_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_pcm_vol_control =
+static const struct snd_kcontrol_new snd_trident_pcm_vol_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Front Playback Volume",
@@ -2764,7 +2764,7 @@ static int snd_trident_pcm_pan_control_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_pcm_pan_control =
+static const struct snd_kcontrol_new snd_trident_pcm_pan_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Pan Playback Control",
@@ -2821,7 +2821,7 @@ static int snd_trident_pcm_rvol_control_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_crvol, -3175, 25, 1);
-static struct snd_kcontrol_new snd_trident_pcm_rvol_control =
+static const struct snd_kcontrol_new snd_trident_pcm_rvol_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Reverb Playback Volume",
@@ -2877,7 +2877,7 @@ static int snd_trident_pcm_cvol_control_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_trident_pcm_cvol_control =
+static const struct snd_kcontrol_new snd_trident_pcm_cvol_control =
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Chorus Playback Volume",
diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c
index 2d8c14e3f8d2..b6c84d15b10b 100644
--- a/sound/pci/via82xx.c
+++ b/sound/pci/via82xx.c
@@ -92,7 +92,7 @@ module_param(index, int, 0444);
MODULE_PARM_DESC(index, "Index value for VIA 82xx bridge.");
module_param(id, charp, 0444);
MODULE_PARM_DESC(id, "ID string for VIA 82xx bridge.");
-module_param(mpu_port, long, 0444);
+module_param_hw(mpu_port, long, ioport, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 port. (VT82C686x only)");
#ifdef SUPPORT_JOYSTICK
module_param(joystick, bool, 0444);
@@ -1683,7 +1683,7 @@ static int snd_via8233_dxs3_spdif_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_via8233_dxs3_spdif_control = {
+static const struct snd_kcontrol_new snd_via8233_dxs3_spdif_control = {
.name = SNDRV_CTL_NAME_IEC958("Output ",NONE,SWITCH),
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_via8233_dxs3_spdif_info,
@@ -1772,7 +1772,7 @@ static int snd_via8233_pcmdxs_volume_put(struct snd_kcontrol *kcontrol,
static const DECLARE_TLV_DB_SCALE(db_scale_dxs, -4650, 150, 1);
-static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control = {
+static const struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control = {
.name = "PCM Playback Volume",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
@@ -1783,7 +1783,7 @@ static struct snd_kcontrol_new snd_via8233_pcmdxs_volume_control = {
.tlv = { .p = db_scale_dxs }
};
-static struct snd_kcontrol_new snd_via8233_dxs_volume_control = {
+static const struct snd_kcontrol_new snd_via8233_dxs_volume_control = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.device = 0,
/* .subdevice set later */
diff --git a/sound/pci/vx222/vx222_ops.c b/sound/pci/vx222/vx222_ops.c
index 8e457ea27f89..7df1663ea510 100644
--- a/sound/pci/vx222/vx222_ops.c
+++ b/sound/pci/vx222/vx222_ops.c
@@ -945,7 +945,7 @@ static int vx_mic_level_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_v
return 0;
}
-static struct snd_kcontrol_new vx_control_input_level = {
+static const struct snd_kcontrol_new vx_control_input_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
@@ -956,7 +956,7 @@ static struct snd_kcontrol_new vx_control_input_level = {
.tlv = { .p = db_scale_mic },
};
-static struct snd_kcontrol_new vx_control_mic_level = {
+static const struct snd_kcontrol_new vx_control_mic_level = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = (SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ),
diff --git a/sound/pci/ymfpci/ymfpci.c b/sound/pci/ymfpci/ymfpci.c
index 812e27a1bcbc..4faf3e1ed06a 100644
--- a/sound/pci/ymfpci/ymfpci.c
+++ b/sound/pci/ymfpci/ymfpci.c
@@ -55,12 +55,12 @@ module_param_array(id, charp, NULL, 0444);
MODULE_PARM_DESC(id, "ID string for the Yamaha DS-1 PCI soundcard.");
module_param_array(enable, bool, NULL, 0444);
MODULE_PARM_DESC(enable, "Enable Yamaha DS-1 soundcard.");
-module_param_array(mpu_port, long, NULL, 0444);
+module_param_hw_array(mpu_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(mpu_port, "MPU-401 Port.");
-module_param_array(fm_port, long, NULL, 0444);
+module_param_hw_array(fm_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(fm_port, "FM OPL-3 Port.");
#ifdef SUPPORT_JOYSTICK
-module_param_array(joystick_port, long, NULL, 0444);
+module_param_hw_array(joystick_port, long, ioport, NULL, 0444);
MODULE_PARM_DESC(joystick_port, "Joystick port address");
#endif
module_param_array(rear_switch, bool, NULL, 0444);
diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c
index ffee284898b3..fe4ba463b57c 100644
--- a/sound/pci/ymfpci/ymfpci_main.c
+++ b/sound/pci/ymfpci/ymfpci_main.c
@@ -1316,7 +1316,7 @@ static int snd_ymfpci_spdif_default_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ymfpci_spdif_default =
+static const struct snd_kcontrol_new snd_ymfpci_spdif_default =
{
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,DEFAULT),
@@ -1344,7 +1344,7 @@ static int snd_ymfpci_spdif_mask_get(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ymfpci_spdif_mask =
+static const struct snd_kcontrol_new snd_ymfpci_spdif_mask =
{
.access = SNDRV_CTL_ELEM_ACCESS_READ,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1391,7 +1391,7 @@ static int snd_ymfpci_spdif_stream_put(struct snd_kcontrol *kcontrol,
return change;
}
-static struct snd_kcontrol_new snd_ymfpci_spdif_stream =
+static const struct snd_kcontrol_new snd_ymfpci_spdif_stream =
{
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE | SNDRV_CTL_ELEM_ACCESS_INACTIVE,
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
@@ -1439,7 +1439,7 @@ static int snd_ymfpci_drec_source_put(struct snd_kcontrol *kcontrol, struct snd_
return reg != old_reg;
}
-static struct snd_kcontrol_new snd_ymfpci_drec_source = {
+static const struct snd_kcontrol_new snd_ymfpci_drec_source = {
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Direct Recording Source",
@@ -1609,7 +1609,7 @@ static int snd_ymfpci_put_dup4ch(struct snd_kcontrol *kcontrol, struct snd_ctl_e
return change;
}
-static struct snd_kcontrol_new snd_ymfpci_dup4ch = {
+static const struct snd_kcontrol_new snd_ymfpci_dup4ch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "4ch Duplication",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE,
@@ -1712,7 +1712,7 @@ static int snd_ymfpci_gpio_sw_put(struct snd_kcontrol *kcontrol, struct snd_ctl_
return 0;
}
-static struct snd_kcontrol_new snd_ymfpci_rear_shared = {
+static const struct snd_kcontrol_new snd_ymfpci_rear_shared = {
.name = "Shared Rear/Line-In Switch",
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.info = snd_ymfpci_gpio_sw_info,
@@ -1776,7 +1776,7 @@ static int snd_ymfpci_pcm_vol_put(struct snd_kcontrol *kcontrol,
return 0;
}
-static struct snd_kcontrol_new snd_ymfpci_pcm_volume = {
+static const struct snd_kcontrol_new snd_ymfpci_pcm_volume = {
.iface = SNDRV_CTL_ELEM_IFACE_PCM,
.name = "PCM Playback Volume",
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig
index 182d92efc7c8..c0abad2067e1 100644
--- a/sound/soc/Kconfig
+++ b/sound/soc/Kconfig
@@ -47,6 +47,7 @@ source "sound/soc/cirrus/Kconfig"
source "sound/soc/davinci/Kconfig"
source "sound/soc/dwc/Kconfig"
source "sound/soc/fsl/Kconfig"
+source "sound/soc/hisilicon/Kconfig"
source "sound/soc/jz4740/Kconfig"
source "sound/soc/nuc900/Kconfig"
source "sound/soc/omap/Kconfig"
@@ -63,6 +64,7 @@ source "sound/soc/sh/Kconfig"
source "sound/soc/sirf/Kconfig"
source "sound/soc/spear/Kconfig"
source "sound/soc/sti/Kconfig"
+source "sound/soc/stm/Kconfig"
source "sound/soc/sunxi/Kconfig"
source "sound/soc/tegra/Kconfig"
source "sound/soc/txx9/Kconfig"
diff --git a/sound/soc/Makefile b/sound/soc/Makefile
index 9a30f21d16ee..39c27a58158d 100644
--- a/sound/soc/Makefile
+++ b/sound/soc/Makefile
@@ -27,6 +27,7 @@ obj-$(CONFIG_SND_SOC) += cirrus/
obj-$(CONFIG_SND_SOC) += davinci/
obj-$(CONFIG_SND_SOC) += dwc/
obj-$(CONFIG_SND_SOC) += fsl/
+obj-$(CONFIG_SND_SOC) += hisilicon/
obj-$(CONFIG_SND_SOC) += jz4740/
obj-$(CONFIG_SND_SOC) += img/
obj-$(CONFIG_SND_SOC) += intel/
@@ -43,6 +44,7 @@ obj-$(CONFIG_SND_SOC) += sh/
obj-$(CONFIG_SND_SOC) += sirf/
obj-$(CONFIG_SND_SOC) += spear/
obj-$(CONFIG_SND_SOC) += sti/
+obj-$(CONFIG_SND_SOC) += stm/
obj-$(CONFIG_SND_SOC) += sunxi/
obj-$(CONFIG_SND_SOC) += tegra/
obj-$(CONFIG_SND_SOC) += txx9/
diff --git a/sound/soc/blackfin/bfin-eval-adau1373.c b/sound/soc/blackfin/bfin-eval-adau1373.c
index 72ac78988426..64b88fdc1f6c 100644
--- a/sound/soc/blackfin/bfin-eval-adau1373.c
+++ b/sound/soc/blackfin/bfin-eval-adau1373.c
@@ -119,7 +119,7 @@ static int bfin_eval_adau1373_codec_init(struct snd_soc_pcm_runtime *rtd)
return ret;
}
-static struct snd_soc_ops bfin_eval_adau1373_ops = {
+static const struct snd_soc_ops bfin_eval_adau1373_ops = {
.hw_params = bfin_eval_adau1373_hw_params,
};
diff --git a/sound/soc/blackfin/bfin-eval-adav80x.c b/sound/soc/blackfin/bfin-eval-adav80x.c
index 1037477d10b2..99e5ecabdcda 100644
--- a/sound/soc/blackfin/bfin-eval-adav80x.c
+++ b/sound/soc/blackfin/bfin-eval-adav80x.c
@@ -64,7 +64,7 @@ static int bfin_eval_adav80x_codec_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static struct snd_soc_ops bfin_eval_adav80x_ops = {
+static const struct snd_soc_ops bfin_eval_adav80x_ops = {
.hw_params = bfin_eval_adav80x_hw_params,
};
diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig
index e49e9da7f1f6..883ed4c8a551 100644
--- a/sound/soc/codecs/Kconfig
+++ b/sound/soc/codecs/Kconfig
@@ -49,6 +49,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_CS35L32 if I2C
select SND_SOC_CS35L33 if I2C
select SND_SOC_CS35L34 if I2C
+ select SND_SOC_CS35L35 if I2C
select SND_SOC_CS42L42 if I2C
select SND_SOC_CS42L51_I2C if I2C
select SND_SOC_CS42L52 if I2C && INPUT
@@ -69,9 +70,11 @@ config SND_SOC_ALL_CODECS
select SND_SOC_DA7219 if I2C
select SND_SOC_DA732X if I2C
select SND_SOC_DA9055 if I2C
+ select SND_SOC_DIO2125
select SND_SOC_DMIC
select SND_SOC_ES8328_SPI if SPI_MASTER
select SND_SOC_ES8328_I2C if I2C
+ select SND_SOC_ES7134
select SND_SOC_GTM601
select SND_SOC_HDAC_HDMI
select SND_SOC_ICS43432
@@ -89,6 +92,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_MAX9867 if I2C
select SND_SOC_MAX98925 if I2C
select SND_SOC_MAX98926 if I2C
+ select SND_SOC_MAX98927 if I2C
select SND_SOC_MAX9850 if I2C
select SND_SOC_MAX9860 if I2C
select SND_SOC_MAX9768 if I2C
@@ -97,6 +101,7 @@ config SND_SOC_ALL_CODECS
select SND_SOC_ML26124 if I2C
select SND_SOC_NAU8540 if I2C
select SND_SOC_NAU8810 if I2C
+ select SND_SOC_NAU8824 if I2C
select SND_SOC_NAU8825 if I2C
select SND_SOC_HDMI_CODEC
select SND_SOC_PCM1681 if I2C
@@ -303,12 +308,14 @@ config SND_SOC_ADAU1761
select SND_SOC_ADAU17X1
config SND_SOC_ADAU1761_I2C
- tristate
+ tristate "Analog Devices AU1761 CODEC - I2C"
+ depends on I2C
select SND_SOC_ADAU1761
select REGMAP_I2C
config SND_SOC_ADAU1761_SPI
- tristate
+ tristate "Analog Devices AU1761 CODEC - SPI"
+ depends on SPI
select SND_SOC_ADAU1761
select REGMAP_SPI
@@ -408,6 +415,10 @@ config SND_SOC_CS35L34
tristate "Cirrus Logic CS35L34 CODEC"
depends on I2C
+config SND_SOC_CS35L35
+ tristate "Cirrus Logic CS35L35 CODEC"
+ depends on I2C
+
config SND_SOC_CS42L42
tristate "Cirrus Logic CS42L42 CODEC"
depends on I2C
@@ -516,6 +527,10 @@ config SND_SOC_DA732X
config SND_SOC_DA9055
tristate
+config SND_SOC_DIO2125
+ tristate "Dioo DIO2125 Amplifier"
+ select GPIOLIB
+
config SND_SOC_DMIC
tristate
@@ -525,6 +540,9 @@ config SND_SOC_HDMI_CODEC
select SND_PCM_IEC958
select HDMI
+config SND_SOC_ES7134
+ tristate "Everest Semi ES7134 CODEC"
+
config SND_SOC_ES8328
tristate
@@ -588,6 +606,10 @@ config SND_SOC_MAX98925
config SND_SOC_MAX98926
tristate
+config SND_SOC_MAX98927
+ tristate "Maxim Integrated MAX98927 Speaker Amplifier"
+ depends on I2C
+
config SND_SOC_MAX9850
tristate
@@ -1116,6 +1138,10 @@ config SND_SOC_NAU8810
tristate "Nuvoton Technology Corporation NAU88C10 CODEC"
depends on I2C
+config SND_SOC_NAU8824
+ tristate "Nuvoton Technology Corporation NAU88L24 CODEC"
+ depends on I2C
+
config SND_SOC_NAU8825
tristate
diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile
index 1796cb987e71..28a63fdaf982 100644
--- a/sound/soc/codecs/Makefile
+++ b/sound/soc/codecs/Makefile
@@ -39,6 +39,7 @@ snd-soc-cq93vc-objs := cq93vc.o
snd-soc-cs35l32-objs := cs35l32.o
snd-soc-cs35l33-objs := cs35l33.o
snd-soc-cs35l34-objs := cs35l34.o
+snd-soc-cs35l35-objs := cs35l35.o
snd-soc-cs42l42-objs := cs42l42.o
snd-soc-cs42l51-objs := cs42l51.o
snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o
@@ -63,6 +64,7 @@ snd-soc-da7219-objs := da7219.o da7219-aad.o
snd-soc-da732x-objs := da732x.o
snd-soc-da9055-objs := da9055.o
snd-soc-dmic-objs := dmic.o
+snd-soc-es7134-objs := es7134.o
snd-soc-es8328-objs := es8328.o
snd-soc-es8328-i2c-objs := es8328-i2c.o
snd-soc-es8328-spi-objs := es8328-spi.o
@@ -84,6 +86,7 @@ snd-soc-max98371-objs := max98371.o
snd-soc-max9867-objs := max9867.o
snd-soc-max98925-objs := max98925.o
snd-soc-max98926-objs := max98926.o
+snd-soc-max98927-objs := max98927.o
snd-soc-max9850-objs := max9850.o
snd-soc-max9860-objs := max9860.o
snd-soc-mc13783-objs := mc13783.o
@@ -92,6 +95,7 @@ snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o
snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o
snd-soc-nau8540-objs := nau8540.o
snd-soc-nau8810-objs := nau8810.o
+snd-soc-nau8824-objs := nau8824.o
snd-soc-nau8825-objs := nau8825.o
snd-soc-hdmi-codec-objs := hdmi-codec.o
snd-soc-pcm1681-objs := pcm1681.o
@@ -221,6 +225,7 @@ snd-soc-wm9712-objs := wm9712.o
snd-soc-wm9713-objs := wm9713.o
snd-soc-wm-hubs-objs := wm_hubs.o
# Amp
+snd-soc-dio2125-objs := dio2125.o
snd-soc-max9877-objs := max9877.o
snd-soc-max98504-objs := max98504.o
snd-soc-tpa6130a2-objs := tpa6130a2.o
@@ -269,6 +274,7 @@ 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_CS35L34) += snd-soc-cs35l34.o
+obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o
obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o
obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o
obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o
@@ -293,6 +299,7 @@ obj-$(CONFIG_SND_SOC_DA7219) += snd-soc-da7219.o
obj-$(CONFIG_SND_SOC_DA732X) += snd-soc-da732x.o
obj-$(CONFIG_SND_SOC_DA9055) += snd-soc-da9055.o
obj-$(CONFIG_SND_SOC_DMIC) += snd-soc-dmic.o
+obj-$(CONFIG_SND_SOC_ES7134) += snd-soc-es7134.o
obj-$(CONFIG_SND_SOC_ES8328) += snd-soc-es8328.o
obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o
obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o
@@ -313,6 +320,7 @@ obj-$(CONFIG_SND_SOC_MAX98357A) += snd-soc-max98357a.o
obj-$(CONFIG_SND_SOC_MAX9867) += snd-soc-max9867.o
obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o
+obj-$(CONFIG_SND_SOC_MAX98927) += snd-soc-max98927.o
obj-$(CONFIG_SND_SOC_MAX9850) += snd-soc-max9850.o
obj-$(CONFIG_SND_SOC_MAX9860) += snd-soc-max9860.o
obj-$(CONFIG_SND_SOC_MC13783) += snd-soc-mc13783.o
@@ -321,6 +329,7 @@ obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o
obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o
obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o
obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o
+obj-$(CONFIG_SND_SOC_NAU8824) += snd-soc-nau8824.o
obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o
obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
obj-$(CONFIG_SND_SOC_PCM1681) += snd-soc-pcm1681.o
@@ -448,6 +457,7 @@ obj-$(CONFIG_SND_SOC_WM_ADSP) += snd-soc-wm-adsp.o
obj-$(CONFIG_SND_SOC_WM_HUBS) += snd-soc-wm-hubs.o
# Amp
+obj-$(CONFIG_SND_SOC_DIO2125) += snd-soc-dio2125.o
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/ak4613.c b/sound/soc/codecs/ak4613.c
index e819dd8c82fd..b2dfddead227 100644
--- a/sound/soc/codecs/ak4613.c
+++ b/sound/soc/codecs/ak4613.c
@@ -75,6 +75,12 @@
#define DFS_DOUBLE_SPEED (1 << 2)
#define DFS_QUAD_SPEED (2 << 2)
+/* ICTRL */
+#define ICTRL_MASK (0x3)
+
+/* OCTRL */
+#define OCTRL_MASK (0x3F)
+
struct ak4613_formats {
unsigned int width;
unsigned int fmt;
@@ -365,8 +371,8 @@ static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
snd_soc_update_bits(codec, CTRL2, DFS_MASK, ctrl2);
- snd_soc_write(codec, ICTRL, priv->ic);
- snd_soc_write(codec, OCTRL, priv->oc);
+ snd_soc_update_bits(codec, ICTRL, ICTRL_MASK, priv->ic);
+ snd_soc_update_bits(codec, OCTRL, OCTRL_MASK, priv->oc);
hw_params_end:
if (ret < 0)
diff --git a/sound/soc/codecs/cs35l35.c b/sound/soc/codecs/cs35l35.c
new file mode 100644
index 000000000000..f8aef5869b03
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.c
@@ -0,0 +1,1580 @@
+/*
+ * cs35l35.c -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2017 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/version.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/platform_device.h>
+#include <linux/regulator/consumer.h>
+#include <linux/gpio/consumer.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <linux/gpio.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/cs35l35.h>
+#include <linux/of_irq.h>
+#include <linux/completion.h>
+
+#include "cs35l35.h"
+
+/*
+ * Some fields take zero as a valid value so use a high bit flag that won't
+ * get written to the device to mark those.
+ */
+#define CS35L35_VALID_PDATA 0x80000000
+
+static const struct reg_default cs35l35_reg[] = {
+ {CS35L35_PWRCTL1, 0x01},
+ {CS35L35_PWRCTL2, 0x11},
+ {CS35L35_PWRCTL3, 0x00},
+ {CS35L35_CLK_CTL1, 0x04},
+ {CS35L35_CLK_CTL2, 0x12},
+ {CS35L35_CLK_CTL3, 0xCF},
+ {CS35L35_SP_FMT_CTL1, 0x20},
+ {CS35L35_SP_FMT_CTL2, 0x00},
+ {CS35L35_SP_FMT_CTL3, 0x02},
+ {CS35L35_MAG_COMP_CTL, 0x00},
+ {CS35L35_AMP_INP_DRV_CTL, 0x01},
+ {CS35L35_AMP_DIG_VOL_CTL, 0x12},
+ {CS35L35_AMP_DIG_VOL, 0x00},
+ {CS35L35_ADV_DIG_VOL, 0x00},
+ {CS35L35_PROTECT_CTL, 0x06},
+ {CS35L35_AMP_GAIN_AUD_CTL, 0x13},
+ {CS35L35_AMP_GAIN_PDM_CTL, 0x00},
+ {CS35L35_AMP_GAIN_ADV_CTL, 0x00},
+ {CS35L35_GPI_CTL, 0x00},
+ {CS35L35_BST_CVTR_V_CTL, 0x00},
+ {CS35L35_BST_PEAK_I, 0x07},
+ {CS35L35_BST_RAMP_CTL, 0x85},
+ {CS35L35_BST_CONV_COEF_1, 0x24},
+ {CS35L35_BST_CONV_COEF_2, 0x24},
+ {CS35L35_BST_CONV_SLOPE_COMP, 0x4E},
+ {CS35L35_BST_CONV_SW_FREQ, 0x04},
+ {CS35L35_CLASS_H_CTL, 0x0B},
+ {CS35L35_CLASS_H_HEADRM_CTL, 0x0B},
+ {CS35L35_CLASS_H_RELEASE_RATE, 0x08},
+ {CS35L35_CLASS_H_FET_DRIVE_CTL, 0x41},
+ {CS35L35_CLASS_H_VP_CTL, 0xC5},
+ {CS35L35_VPBR_CTL, 0x0A},
+ {CS35L35_VPBR_VOL_CTL, 0x90},
+ {CS35L35_VPBR_TIMING_CTL, 0x6A},
+ {CS35L35_VPBR_MODE_VOL_CTL, 0x00},
+ {CS35L35_SPKR_MON_CTL, 0xC0},
+ {CS35L35_IMON_SCALE_CTL, 0x30},
+ {CS35L35_AUDIN_RXLOC_CTL, 0x00},
+ {CS35L35_ADVIN_RXLOC_CTL, 0x80},
+ {CS35L35_VMON_TXLOC_CTL, 0x00},
+ {CS35L35_IMON_TXLOC_CTL, 0x80},
+ {CS35L35_VPMON_TXLOC_CTL, 0x04},
+ {CS35L35_VBSTMON_TXLOC_CTL, 0x84},
+ {CS35L35_VPBR_STATUS_TXLOC_CTL, 0x04},
+ {CS35L35_ZERO_FILL_LOC_CTL, 0x00},
+ {CS35L35_AUDIN_DEPTH_CTL, 0x0F},
+ {CS35L35_SPKMON_DEPTH_CTL, 0x0F},
+ {CS35L35_SUPMON_DEPTH_CTL, 0x0F},
+ {CS35L35_ZEROFILL_DEPTH_CTL, 0x00},
+ {CS35L35_MULT_DEV_SYNCH1, 0x02},
+ {CS35L35_MULT_DEV_SYNCH2, 0x80},
+ {CS35L35_PROT_RELEASE_CTL, 0x00},
+ {CS35L35_DIAG_MODE_REG_LOCK, 0x00},
+ {CS35L35_DIAG_MODE_CTL_1, 0x40},
+ {CS35L35_DIAG_MODE_CTL_2, 0x00},
+ {CS35L35_INT_MASK_1, 0xFF},
+ {CS35L35_INT_MASK_2, 0xFF},
+ {CS35L35_INT_MASK_3, 0xFF},
+ {CS35L35_INT_MASK_4, 0xFF},
+
+};
+
+static bool cs35l35_volatile_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_INT_STATUS_1:
+ case CS35L35_INT_STATUS_2:
+ case CS35L35_INT_STATUS_3:
+ case CS35L35_INT_STATUS_4:
+ case CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l35_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_DEVID_AB ... CS35L35_PWRCTL3:
+ case CS35L35_CLK_CTL1 ... CS35L35_SP_FMT_CTL3:
+ case CS35L35_MAG_COMP_CTL ... CS35L35_AMP_GAIN_AUD_CTL:
+ case CS35L35_AMP_GAIN_PDM_CTL ... CS35L35_BST_PEAK_I:
+ case CS35L35_BST_RAMP_CTL ... CS35L35_BST_CONV_SW_FREQ:
+ case CS35L35_CLASS_H_CTL ... CS35L35_CLASS_H_VP_CTL:
+ case CS35L35_CLASS_H_STATUS:
+ case CS35L35_VPBR_CTL ... CS35L35_VPBR_MODE_VOL_CTL:
+ case CS35L35_VPBR_ATTEN_STATUS:
+ case CS35L35_SPKR_MON_CTL:
+ case CS35L35_IMON_SCALE_CTL ... CS35L35_ZEROFILL_DEPTH_CTL:
+ case CS35L35_MULT_DEV_SYNCH1 ... CS35L35_PROT_RELEASE_CTL:
+ case CS35L35_DIAG_MODE_REG_LOCK ... CS35L35_DIAG_MODE_CTL_2:
+ case CS35L35_INT_MASK_1 ... CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool cs35l35_precious_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case CS35L35_INT_STATUS_1:
+ case CS35L35_INT_STATUS_2:
+ case CS35L35_INT_STATUS_3:
+ case CS35L35_INT_STATUS_4:
+ case CS35L35_PLL_STATUS:
+ case CS35L35_OTP_TRIM_STATUS:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static int cs35l35_wait_for_pdn(struct cs35l35_private *cs35l35)
+{
+ int ret;
+
+ if (cs35l35->pdata.ext_bst) {
+ usleep_range(5000, 5500);
+ return 0;
+ }
+
+ reinit_completion(&cs35l35->pdn_done);
+
+ ret = wait_for_completion_timeout(&cs35l35->pdn_done,
+ msecs_to_jiffies(100));
+ if (ret == 0) {
+ dev_err(cs35l35->dev, "PDN_DONE did not complete\n");
+ return -ETIMEDOUT;
+ }
+
+ return 0;
+}
+
+static int cs35l35_sdin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ int ret = 0;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MCLK_DIS_MASK,
+ 0 << CS35L35_MCLK_DIS_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_DISCHG_FILT_MASK,
+ 0 << CS35L35_DISCHG_FILT_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL_MASK, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_DISCHG_FILT_MASK,
+ 1 << CS35L35_DISCHG_FILT_SHIFT);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL_MASK, 1);
+
+ /* Already muted, so disable volume ramp for faster shutdown */
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_DIG_VOL_CTL,
+ CS35L35_AMP_DIGSFT_MASK, 0);
+
+ ret = cs35l35_wait_for_pdn(cs35l35);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MCLK_DIS_MASK,
+ 1 << CS35L35_MCLK_DIS_SHIFT);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_DIG_VOL_CTL,
+ CS35L35_AMP_DIGSFT_MASK,
+ 1 << CS35L35_AMP_DIGSFT_SHIFT);
+ break;
+ default:
+ dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+ ret = -EINVAL;
+ }
+ return ret;
+}
+
+static int cs35l35_main_amp_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ unsigned int reg[4];
+ int i;
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 0 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 0 << CS35L35_PDN_BST_FETOFF_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMU:
+ usleep_range(5000, 5100);
+ /* If in PDM mode we must use VP for Voltage control */
+ if (cs35l35->pdm_mode)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_BST_CVTR_V_CTL,
+ CS35L35_BST_CTL_MASK,
+ 0 << CS35L35_BST_CTL_SHIFT);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK, 0);
+
+ for (i = 0; i < 2; i++)
+ regmap_bulk_read(cs35l35->regmap, CS35L35_INT_STATUS_1,
+ &reg, ARRAY_SIZE(reg));
+
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK,
+ 1 << CS35L35_AMP_MUTE_SHIFT);
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 1 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ usleep_range(5000, 5100);
+ /*
+ * If PDM mode we should switch back to pdata value
+ * for Voltage control when we go down
+ */
+ if (cs35l35->pdm_mode)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_BST_CVTR_V_CTL,
+ CS35L35_BST_CTL_MASK,
+ cs35l35->pdata.bst_vctl
+ << CS35L35_BST_CTL_SHIFT);
+
+ break;
+ default:
+ dev_err(codec->dev, "Invalid event = 0x%x\n", event);
+ }
+ return 0;
+}
+
+static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1);
+static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 50, 0);
+
+static const struct snd_kcontrol_new cs35l35_aud_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital Audio Volume", CS35L35_AMP_DIG_VOL,
+ 0, 0x34, 0xE4, dig_vol_tlv),
+ SOC_SINGLE_TLV("Analog Audio Volume", CS35L35_AMP_GAIN_AUD_CTL, 0, 19, 0,
+ amp_gain_tlv),
+ SOC_SINGLE_TLV("PDM Volume", CS35L35_AMP_GAIN_PDM_CTL, 0, 19, 0,
+ amp_gain_tlv),
+};
+
+static const struct snd_kcontrol_new cs35l35_adv_controls[] = {
+ SOC_SINGLE_SX_TLV("Digital Advisory Volume", CS35L35_ADV_DIG_VOL,
+ 0, 0x34, 0xE4, dig_vol_tlv),
+ SOC_SINGLE_TLV("Analog Advisory Volume", CS35L35_AMP_GAIN_ADV_CTL, 0, 19, 0,
+ amp_gain_tlv),
+};
+
+static const struct snd_soc_dapm_widget cs35l35_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN_E("SDIN", NULL, 0, CS35L35_PWRCTL3, 1, 1,
+ cs35l35_sdin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_AIF_OUT("SDOUT", NULL, 0, CS35L35_PWRCTL3, 2, 1),
+
+ SND_SOC_DAPM_OUTPUT("SPK"),
+
+ SND_SOC_DAPM_INPUT("VP"),
+ SND_SOC_DAPM_INPUT("VBST"),
+ SND_SOC_DAPM_INPUT("ISENSE"),
+ SND_SOC_DAPM_INPUT("VSENSE"),
+
+ SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L35_PWRCTL2, 7, 1),
+ SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L35_PWRCTL2, 6, 1),
+ SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L35_PWRCTL3, 3, 1),
+ SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L35_PWRCTL3, 4, 1),
+ SND_SOC_DAPM_ADC("CLASS H", NULL, CS35L35_PWRCTL2, 5, 1),
+
+ SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L35_PWRCTL2, 0, 1, NULL, 0,
+ cs35l35_main_amp_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU |
+ SND_SOC_DAPM_PRE_PMD),
+};
+
+static const struct snd_soc_dapm_route cs35l35_audio_map[] = {
+ {"VPMON ADC", NULL, "VP"},
+ {"VBSTMON ADC", NULL, "VBST"},
+ {"IMON ADC", NULL, "ISENSE"},
+ {"VMON ADC", NULL, "VSENSE"},
+ {"SDOUT", NULL, "IMON ADC"},
+ {"SDOUT", NULL, "VMON ADC"},
+ {"SDOUT", NULL, "VBSTMON ADC"},
+ {"SDOUT", NULL, "VPMON ADC"},
+ {"AMP Capture", NULL, "SDOUT"},
+
+ {"SDIN", NULL, "AMP Playback"},
+ {"CLASS H", NULL, "SDIN"},
+ {"Main AMP", NULL, "CLASS H"},
+ {"SPK", NULL, "Main AMP"},
+};
+
+static int cs35l35_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MS_MASK, 1 << CS35L35_MS_SHIFT);
+ cs35l35->slave_mode = false;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_MS_MASK, 0 << CS35L35_MS_SHIFT);
+ cs35l35->slave_mode = true;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ cs35l35->i2s_mode = true;
+ cs35l35->pdm_mode = false;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ cs35l35->pdm_mode = true;
+ cs35l35->i2s_mode = false;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+struct cs35l35_sysclk_config {
+ int sysclk;
+ int srate;
+ u8 clk_cfg;
+};
+
+static struct cs35l35_sysclk_config cs35l35_clk_ctl[] = {
+
+ /* SYSCLK, Sample Rate, Serial Port Cfg */
+ {5644800, 44100, 0x00},
+ {5644800, 88200, 0x40},
+ {6144000, 48000, 0x10},
+ {6144000, 96000, 0x50},
+ {11289600, 44100, 0x01},
+ {11289600, 88200, 0x41},
+ {11289600, 176400, 0x81},
+ {12000000, 44100, 0x03},
+ {12000000, 48000, 0x13},
+ {12000000, 88200, 0x43},
+ {12000000, 96000, 0x53},
+ {12000000, 176400, 0x83},
+ {12000000, 192000, 0x93},
+ {12288000, 48000, 0x11},
+ {12288000, 96000, 0x51},
+ {12288000, 192000, 0x91},
+ {13000000, 44100, 0x07},
+ {13000000, 48000, 0x17},
+ {13000000, 88200, 0x47},
+ {13000000, 96000, 0x57},
+ {13000000, 176400, 0x87},
+ {13000000, 192000, 0x97},
+ {22579200, 44100, 0x02},
+ {22579200, 88200, 0x42},
+ {22579200, 176400, 0x82},
+ {24000000, 44100, 0x0B},
+ {24000000, 48000, 0x1B},
+ {24000000, 88200, 0x4B},
+ {24000000, 96000, 0x5B},
+ {24000000, 176400, 0x8B},
+ {24000000, 192000, 0x9B},
+ {24576000, 48000, 0x12},
+ {24576000, 96000, 0x52},
+ {24576000, 192000, 0x92},
+ {26000000, 44100, 0x0F},
+ {26000000, 48000, 0x1F},
+ {26000000, 88200, 0x4F},
+ {26000000, 96000, 0x5F},
+ {26000000, 176400, 0x8F},
+ {26000000, 192000, 0x9F},
+};
+
+static int cs35l35_get_clk_config(int sysclk, int srate)
+{
+ int i;
+
+ for (i = 0; i < ARRAY_SIZE(cs35l35_clk_ctl); i++) {
+ if (cs35l35_clk_ctl[i].sysclk == sysclk &&
+ cs35l35_clk_ctl[i].srate == srate)
+ return cs35l35_clk_ctl[i].clk_cfg;
+ }
+ return -EINVAL;
+}
+
+static int cs35l35_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+ int srate = params_rate(params);
+ int ret = 0;
+ u8 sp_sclks;
+ int audin_format;
+ int errata_chk;
+
+ int clk_ctl = cs35l35_get_clk_config(cs35l35->sysclk, srate);
+
+ if (clk_ctl < 0) {
+ dev_err(codec->dev, "Invalid CLK:Rate %d:%d\n",
+ cs35l35->sysclk, srate);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL2,
+ CS35L35_CLK_CTL2_MASK, clk_ctl);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set port config %d\n", ret);
+ return ret;
+ }
+
+ /*
+ * Rev A0 Errata
+ * When configured for the weak-drive detection path (CH_WKFET_DIS = 0)
+ * the Class H algorithm does not enable weak-drive operation for
+ * nonzero values of CH_WKFET_DELAY if SP_RATE = 01 or 10
+ */
+ errata_chk = clk_ctl & CS35L35_SP_RATE_MASK;
+
+ if (classh->classh_wk_fet_disable == 0x00 &&
+ (errata_chk == 0x01 || errata_chk == 0x03)) {
+ ret = regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL,
+ CS35L35_CH_WKFET_DEL_MASK,
+ 0 << CS35L35_CH_WKFET_DEL_SHIFT);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set fet config %d\n",
+ ret);
+ return ret;
+ }
+ }
+
+ /*
+ * You can pull more Monitor data from the SDOUT pin than going to SDIN
+ * Just make sure your SCLK is fast enough to fill the frame
+ */
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ switch (params_width(params)) {
+ case 8:
+ audin_format = CS35L35_SDIN_DEPTH_8;
+ break;
+ case 16:
+ audin_format = CS35L35_SDIN_DEPTH_16;
+ break;
+ case 24:
+ audin_format = CS35L35_SDIN_DEPTH_24;
+ break;
+ default:
+ dev_err(codec->dev, "Unsupported Width %d\n",
+ params_width(params));
+ return -EINVAL;
+ }
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_DEPTH_CTL,
+ CS35L35_AUDIN_DEPTH_MASK,
+ audin_format <<
+ CS35L35_AUDIN_DEPTH_SHIFT);
+ if (cs35l35->pdata.stereo) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_DEPTH_CTL,
+ CS35L35_ADVIN_DEPTH_MASK,
+ audin_format <<
+ CS35L35_ADVIN_DEPTH_SHIFT);
+ }
+ }
+
+ if (cs35l35->i2s_mode) {
+ /* We have to take the SCLK to derive num sclks
+ * to configure the CLOCK_CTL3 register correctly
+ */
+ if ((cs35l35->sclk / srate) % 4) {
+ dev_err(codec->dev, "Unsupported sclk/fs ratio %d:%d\n",
+ cs35l35->sclk, srate);
+ return -EINVAL;
+ }
+ sp_sclks = ((cs35l35->sclk / srate) / 4) - 1;
+
+ /* Only certain ratios are supported in I2S Slave Mode */
+ if (cs35l35->slave_mode) {
+ switch (sp_sclks) {
+ case CS35L35_SP_SCLKS_32FS:
+ case CS35L35_SP_SCLKS_48FS:
+ case CS35L35_SP_SCLKS_64FS:
+ break;
+ default:
+ dev_err(codec->dev, "ratio not supported\n");
+ return -EINVAL;
+ }
+ } else {
+ /* Only certain ratios supported in I2S MASTER Mode */
+ switch (sp_sclks) {
+ case CS35L35_SP_SCLKS_32FS:
+ case CS35L35_SP_SCLKS_64FS:
+ break;
+ default:
+ dev_err(codec->dev, "ratio not supported\n");
+ return -EINVAL;
+ }
+ }
+ ret = regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLK_CTL3,
+ CS35L35_SP_SCLKS_MASK, sp_sclks <<
+ CS35L35_SP_SCLKS_SHIFT);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set fsclk %d\n", ret);
+ return ret;
+ }
+ }
+
+ return ret;
+}
+
+static const unsigned int cs35l35_src_rates[] = {
+ 44100, 48000, 88200, 96000, 176400, 192000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_constraints = {
+ .count = ARRAY_SIZE(cs35l35_src_rates),
+ .list = cs35l35_src_rates,
+};
+
+static int cs35l35_pcm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+ if (!substream->runtime)
+ return 0;
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE, &cs35l35_constraints);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+ CS35L35_PDM_MODE_MASK,
+ 0 << CS35L35_PDM_MODE_SHIFT);
+
+ return 0;
+}
+
+static const unsigned int cs35l35_pdm_rates[] = {
+ 44100, 48000, 88200, 96000
+};
+
+static const struct snd_pcm_hw_constraint_list cs35l35_pdm_constraints = {
+ .count = ARRAY_SIZE(cs35l35_pdm_rates),
+ .list = cs35l35_pdm_rates,
+};
+
+static int cs35l35_pdm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+ if (!substream->runtime)
+ return 0;
+
+ snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &cs35l35_pdm_constraints);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_AMP_INP_DRV_CTL,
+ CS35L35_PDM_MODE_MASK,
+ 1 << CS35L35_PDM_MODE_SHIFT);
+
+ return 0;
+}
+
+static int cs35l35_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+
+ /* Need the SCLK Frequency regardless of sysclk source for I2S */
+ cs35l35->sclk = freq;
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops cs35l35_ops = {
+ .startup = cs35l35_pcm_startup,
+ .set_fmt = cs35l35_set_dai_fmt,
+ .hw_params = cs35l35_hw_params,
+ .set_sysclk = cs35l35_dai_set_sysclk,
+};
+
+static const struct snd_soc_dai_ops cs35l35_pdm_ops = {
+ .startup = cs35l35_pdm_startup,
+ .set_fmt = cs35l35_set_dai_fmt,
+ .hw_params = cs35l35_hw_params,
+};
+
+static struct snd_soc_dai_driver cs35l35_dai[] = {
+ {
+ .name = "cs35l35-pcm",
+ .id = 0,
+ .playback = {
+ .stream_name = "AMP Playback",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .capture = {
+ .stream_name = "AMP Capture",
+ .channels_min = 1,
+ .channels_max = 8,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .ops = &cs35l35_ops,
+ .symmetric_rates = 1,
+ },
+ {
+ .name = "cs35l35-pdm",
+ .id = 1,
+ .playback = {
+ .stream_name = "PDM Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_KNOT,
+ .formats = CS35L35_FORMATS,
+ },
+ .ops = &cs35l35_pdm_ops,
+ },
+};
+
+static int cs35l35_codec_set_sysclk(struct snd_soc_codec *codec,
+ int clk_id, int source, unsigned int freq,
+ int dir)
+{
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ int clksrc;
+ int ret = 0;
+
+ switch (clk_id) {
+ case 0:
+ clksrc = CS35L35_CLK_SOURCE_MCLK;
+ break;
+ case 1:
+ clksrc = CS35L35_CLK_SOURCE_SCLK;
+ break;
+ case 2:
+ clksrc = CS35L35_CLK_SOURCE_PDM;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid CLK Source\n");
+ return -EINVAL;
+ }
+
+ switch (freq) {
+ case 5644800:
+ case 6144000:
+ case 11289600:
+ case 12000000:
+ case 12288000:
+ case 13000000:
+ case 22579200:
+ case 24000000:
+ case 24576000:
+ case 26000000:
+ cs35l35->sysclk = freq;
+ break;
+ default:
+ dev_err(codec->dev, "Invalid CLK Frequency Input : %d\n", freq);
+ return -EINVAL;
+ }
+
+ ret = regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_CLK_SOURCE_MASK,
+ clksrc << CS35L35_CLK_SOURCE_SHIFT);
+ if (ret != 0) {
+ dev_err(codec->dev, "Failed to set sysclk %d\n", ret);
+ return ret;
+ }
+
+ return ret;
+}
+
+static int cs35l35_codec_probe(struct snd_soc_codec *codec)
+{
+ struct cs35l35_private *cs35l35 = snd_soc_codec_get_drvdata(codec);
+ struct classh_cfg *classh = &cs35l35->pdata.classh_algo;
+ struct monitor_cfg *monitor_config = &cs35l35->pdata.mon_cfg;
+ int ret;
+
+ /* Set Platform Data */
+ if (cs35l35->pdata.bst_vctl)
+ regmap_update_bits(cs35l35->regmap, CS35L35_BST_CVTR_V_CTL,
+ CS35L35_BST_CTL_MASK,
+ cs35l35->pdata.bst_vctl);
+
+ if (cs35l35->pdata.bst_ipk)
+ regmap_update_bits(cs35l35->regmap, CS35L35_BST_PEAK_I,
+ CS35L35_BST_IPK_MASK,
+ cs35l35->pdata.bst_ipk <<
+ CS35L35_BST_IPK_SHIFT);
+
+ if (cs35l35->pdata.gain_zc)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_GAIN_ZC_MASK,
+ cs35l35->pdata.gain_zc <<
+ CS35L35_AMP_GAIN_ZC_SHIFT);
+
+ if (cs35l35->pdata.aud_channel)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_AUDIN_RXLOC_CTL,
+ CS35L35_AUD_IN_LR_MASK,
+ cs35l35->pdata.aud_channel <<
+ CS35L35_AUD_IN_LR_SHIFT);
+
+ if (cs35l35->pdata.stereo) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ADVIN_RXLOC_CTL,
+ CS35L35_ADV_IN_LR_MASK,
+ cs35l35->pdata.adv_channel <<
+ CS35L35_ADV_IN_LR_SHIFT);
+ if (cs35l35->pdata.shared_bst)
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLASS_H_CTL,
+ CS35L35_CH_STEREO_MASK,
+ 1 << CS35L35_CH_STEREO_SHIFT);
+ ret = snd_soc_add_codec_controls(codec, cs35l35_adv_controls,
+ ARRAY_SIZE(cs35l35_adv_controls));
+ if (ret)
+ return ret;
+ }
+
+ if (cs35l35->pdata.sp_drv_str)
+ regmap_update_bits(cs35l35->regmap, CS35L35_CLK_CTL1,
+ CS35L35_SP_DRV_MASK,
+ cs35l35->pdata.sp_drv_str <<
+ CS35L35_SP_DRV_SHIFT);
+ if (cs35l35->pdata.sp_drv_unused)
+ regmap_update_bits(cs35l35->regmap, CS35L35_SP_FMT_CTL3,
+ CS35L35_SP_I2S_DRV_MASK,
+ cs35l35->pdata.sp_drv_unused <<
+ CS35L35_SP_I2S_DRV_SHIFT);
+
+ if (classh->classh_algo_enable) {
+ if (classh->classh_bst_override)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL,
+ CS35L35_CH_BST_OVR_MASK,
+ classh->classh_bst_override <<
+ CS35L35_CH_BST_OVR_SHIFT);
+ if (classh->classh_bst_max_limit)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL,
+ CS35L35_CH_BST_LIM_MASK,
+ classh->classh_bst_max_limit <<
+ CS35L35_CH_BST_LIM_SHIFT);
+ if (classh->classh_mem_depth)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_CTL,
+ CS35L35_CH_MEM_DEPTH_MASK,
+ classh->classh_mem_depth <<
+ CS35L35_CH_MEM_DEPTH_SHIFT);
+ if (classh->classh_headroom)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_HEADRM_CTL,
+ CS35L35_CH_HDRM_CTL_MASK,
+ classh->classh_headroom <<
+ CS35L35_CH_HDRM_CTL_SHIFT);
+ if (classh->classh_release_rate)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_RELEASE_RATE,
+ CS35L35_CH_REL_RATE_MASK,
+ classh->classh_release_rate <<
+ CS35L35_CH_REL_RATE_SHIFT);
+ if (classh->classh_wk_fet_disable)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL,
+ CS35L35_CH_WKFET_DIS_MASK,
+ classh->classh_wk_fet_disable <<
+ CS35L35_CH_WKFET_DIS_SHIFT);
+ if (classh->classh_wk_fet_delay)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL,
+ CS35L35_CH_WKFET_DEL_MASK,
+ classh->classh_wk_fet_delay <<
+ CS35L35_CH_WKFET_DEL_SHIFT);
+ if (classh->classh_wk_fet_thld)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_FET_DRIVE_CTL,
+ CS35L35_CH_WKFET_THLD_MASK,
+ classh->classh_wk_fet_thld <<
+ CS35L35_CH_WKFET_THLD_SHIFT);
+ if (classh->classh_vpch_auto)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL,
+ CS35L35_CH_VP_AUTO_MASK,
+ classh->classh_vpch_auto <<
+ CS35L35_CH_VP_AUTO_SHIFT);
+ if (classh->classh_vpch_rate)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL,
+ CS35L35_CH_VP_RATE_MASK,
+ classh->classh_vpch_rate <<
+ CS35L35_CH_VP_RATE_SHIFT);
+ if (classh->classh_vpch_man)
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_CLASS_H_VP_CTL,
+ CS35L35_CH_VP_MAN_MASK,
+ classh->classh_vpch_man <<
+ CS35L35_CH_VP_MAN_SHIFT);
+ }
+
+ if (monitor_config->is_present) {
+ if (monitor_config->vmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SPKMON_DEPTH_CTL,
+ CS35L35_VMON_DEPTH_MASK,
+ monitor_config->vmon_dpth <<
+ CS35L35_VMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VMON_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->vmon_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VMON_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->vmon_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->imon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SPKMON_DEPTH_CTL,
+ CS35L35_IMON_DEPTH_MASK,
+ monitor_config->imon_dpth <<
+ CS35L35_IMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->imon_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->imon_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_IMON_SCALE_CTL,
+ CS35L35_IMON_SCALE_MASK,
+ monitor_config->imon_scale <<
+ CS35L35_IMON_SCALE_SHIFT);
+ }
+ if (monitor_config->vpmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL,
+ CS35L35_VPMON_DEPTH_MASK,
+ monitor_config->vpmon_dpth <<
+ CS35L35_VPMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPMON_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->vpmon_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPMON_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->vpmon_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->vbstmon_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL,
+ CS35L35_VBSTMON_DEPTH_MASK,
+ monitor_config->vpmon_dpth <<
+ CS35L35_VBSTMON_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VBSTMON_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->vbstmon_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VBSTMON_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->vbstmon_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->vpbrstat_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL,
+ CS35L35_VPBRSTAT_DEPTH_MASK,
+ monitor_config->vpbrstat_dpth <<
+ CS35L35_VPBRSTAT_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPBR_STATUS_TXLOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->vpbrstat_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_VPBR_STATUS_TXLOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->vpbrstat_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ if (monitor_config->zerofill_specs) {
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_SUPMON_DEPTH_CTL,
+ CS35L35_ZEROFILL_DEPTH_MASK,
+ monitor_config->zerofill_dpth <<
+ CS35L35_ZEROFILL_DEPTH_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ZERO_FILL_LOC_CTL,
+ CS35L35_MON_TXLOC_MASK,
+ monitor_config->zerofill_loc <<
+ CS35L35_MON_TXLOC_SHIFT);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_ZERO_FILL_LOC_CTL,
+ CS35L35_MON_FRM_MASK,
+ monitor_config->zerofill_frm <<
+ CS35L35_MON_FRM_SHIFT);
+ }
+ }
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_cs35l35 = {
+ .probe = cs35l35_codec_probe,
+ .set_sysclk = cs35l35_codec_set_sysclk,
+ .component_driver = {
+ .dapm_widgets = cs35l35_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(cs35l35_dapm_widgets),
+
+ .dapm_routes = cs35l35_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(cs35l35_audio_map),
+
+ .controls = cs35l35_aud_controls,
+ .num_controls = ARRAY_SIZE(cs35l35_aud_controls),
+ },
+
+};
+
+static struct regmap_config cs35l35_regmap = {
+ .reg_bits = 8,
+ .val_bits = 8,
+
+ .max_register = CS35L35_MAX_REGISTER,
+ .reg_defaults = cs35l35_reg,
+ .num_reg_defaults = ARRAY_SIZE(cs35l35_reg),
+ .volatile_reg = cs35l35_volatile_register,
+ .readable_reg = cs35l35_readable_register,
+ .precious_reg = cs35l35_precious_register,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static irqreturn_t cs35l35_irq(int irq, void *data)
+{
+ struct cs35l35_private *cs35l35 = data;
+ unsigned int sticky1, sticky2, sticky3, sticky4;
+ unsigned int mask1, mask2, mask3, mask4, current1;
+
+ /* ack the irq by reading all status registers */
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_4, &sticky4);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_3, &sticky3);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_2, &sticky2);
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &sticky1);
+
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_4, &mask4);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_3, &mask3);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_2, &mask2);
+ regmap_read(cs35l35->regmap, CS35L35_INT_MASK_1, &mask1);
+
+ /* Check to see if unmasked bits are active */
+ if (!(sticky1 & ~mask1) && !(sticky2 & ~mask2) && !(sticky3 & ~mask3)
+ && !(sticky4 & ~mask4))
+ return IRQ_NONE;
+
+ if (sticky2 & CS35L35_PDN_DONE)
+ complete(&cs35l35->pdn_done);
+
+ /* read the current values */
+ regmap_read(cs35l35->regmap, CS35L35_INT_STATUS_1, &current1);
+
+ /* handle the interrupts */
+ if (sticky1 & CS35L35_CAL_ERR) {
+ dev_crit(cs35l35->dev, "Calibration Error\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_CAL_ERR)) {
+ pr_debug("%s : Cal error release\n", __func__);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_CAL_ERR_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_CAL_ERR_RLS,
+ CS35L35_CAL_ERR_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_CAL_ERR_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_AMP_SHORT) {
+ dev_crit(cs35l35->dev, "AMP Short Error\n");
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_AMP_SHORT)) {
+ dev_dbg(cs35l35->dev, "Amp short error release\n");
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_SHORT_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_SHORT_RLS,
+ CS35L35_SHORT_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_SHORT_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_OTW) {
+ dev_warn(cs35l35->dev, "Over temperature warning\n");
+
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_OTW)) {
+ dev_dbg(cs35l35->dev, "Over temperature warn release\n");
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTW_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTW_RLS,
+ CS35L35_OTW_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTW_RLS, 0);
+ }
+ }
+
+ if (sticky1 & CS35L35_OTE) {
+ dev_crit(cs35l35->dev, "Over temperature error\n");
+ /* error is no longer asserted; safe to reset */
+ if (!(current1 & CS35L35_OTE)) {
+ dev_dbg(cs35l35->dev, "Over temperature error release\n");
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTE_RLS, 0);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTE_RLS,
+ CS35L35_OTE_RLS);
+ regmap_update_bits(cs35l35->regmap,
+ CS35L35_PROT_RELEASE_CTL,
+ CS35L35_OTE_RLS, 0);
+ }
+ }
+
+ if (sticky3 & CS35L35_BST_HIGH) {
+ dev_crit(cs35l35->dev, "VBST error: powering off!\n");
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+ }
+
+ if (sticky3 & CS35L35_LBST_SHORT) {
+ dev_crit(cs35l35->dev, "LBST error: powering off!\n");
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_AMP, CS35L35_PDN_AMP);
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL1,
+ CS35L35_PDN_ALL, CS35L35_PDN_ALL);
+ }
+
+ if (sticky2 & CS35L35_VPBR_ERR)
+ dev_dbg(cs35l35->dev, "Error: Reactive Brownout\n");
+
+ if (sticky4 & CS35L35_VMON_OVFL)
+ dev_dbg(cs35l35->dev, "Error: VMON overflow\n");
+
+ if (sticky4 & CS35L35_IMON_OVFL)
+ dev_dbg(cs35l35->dev, "Error: IMON overflow\n");
+
+ return IRQ_HANDLED;
+}
+
+
+static int cs35l35_handle_of_data(struct i2c_client *i2c_client,
+ struct cs35l35_platform_data *pdata)
+{
+ struct device_node *np = i2c_client->dev.of_node;
+ struct device_node *classh, *signal_format;
+ struct classh_cfg *classh_config = &pdata->classh_algo;
+ struct monitor_cfg *monitor_config = &pdata->mon_cfg;
+ unsigned int val32 = 0;
+ u8 monitor_array[4];
+ const int imon_array_size = ARRAY_SIZE(monitor_array);
+ const int mon_array_size = imon_array_size - 1;
+ int ret = 0;
+
+ if (!np)
+ return 0;
+
+ pdata->bst_pdn_fet_on = of_property_read_bool(np,
+ "cirrus,boost-pdn-fet-on");
+
+ ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val32);
+ if (ret >= 0) {
+ if (val32 < 2600 || val32 > 9000) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Voltage %d mV\n", val32);
+ return -EINVAL;
+ }
+ pdata->bst_vctl = ((val32 - 2600) / 100) + 1;
+ }
+
+ ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val32);
+ if (ret >= 0) {
+ if (val32 < 1680 || val32 > 4480) {
+ dev_err(&i2c_client->dev,
+ "Invalid Boost Peak Current %u mA\n", val32);
+ return -EINVAL;
+ }
+
+ pdata->bst_ipk = (val32 - 1680) / 110;
+ }
+
+ if (of_property_read_u32(np, "cirrus,sp-drv-strength", &val32) >= 0)
+ pdata->sp_drv_str = val32;
+ if (of_property_read_u32(np, "cirrus,sp-drv-unused", &val32) >= 0)
+ pdata->sp_drv_unused = val32 | CS35L35_VALID_PDATA;
+
+ pdata->stereo = of_property_read_bool(np, "cirrus,stereo-config");
+
+ if (pdata->stereo) {
+ ret = of_property_read_u32(np, "cirrus,audio-channel", &val32);
+ if (ret >= 0)
+ pdata->aud_channel = val32;
+
+ ret = of_property_read_u32(np, "cirrus,advisory-channel",
+ &val32);
+ if (ret >= 0)
+ pdata->adv_channel = val32;
+
+ pdata->shared_bst = of_property_read_bool(np,
+ "cirrus,shared-boost");
+ }
+
+ pdata->ext_bst = of_property_read_bool(np, "cirrus,external-boost");
+
+ pdata->gain_zc = of_property_read_bool(np, "cirrus,amp-gain-zc");
+
+ classh = of_get_child_by_name(np, "cirrus,classh-internal-algo");
+ classh_config->classh_algo_enable = classh ? true : false;
+
+ if (classh_config->classh_algo_enable) {
+ classh_config->classh_bst_override =
+ of_property_read_bool(np, "cirrus,classh-bst-overide");
+
+ ret = of_property_read_u32(classh,
+ "cirrus,classh-bst-max-limit",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_bst_max_limit = val32;
+ }
+
+ ret = of_property_read_u32(classh,
+ "cirrus,classh-bst-max-limit",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_bst_max_limit = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-mem-depth",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_mem_depth = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-release-rate",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_release_rate = val32;
+
+ ret = of_property_read_u32(classh, "cirrus,classh-headroom",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_headroom = val32;
+ }
+
+ ret = of_property_read_u32(classh,
+ "cirrus,classh-wk-fet-disable",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_wk_fet_disable = val32;
+
+ ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-delay",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_wk_fet_delay = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-wk-fet-thld",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_wk_fet_thld = val32;
+
+ ret = of_property_read_u32(classh, "cirrus,classh-vpch-auto",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_vpch_auto = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-vpch-rate",
+ &val32);
+ if (ret >= 0) {
+ val32 |= CS35L35_VALID_PDATA;
+ classh_config->classh_vpch_rate = val32;
+ }
+
+ ret = of_property_read_u32(classh, "cirrus,classh-vpch-man",
+ &val32);
+ if (ret >= 0)
+ classh_config->classh_vpch_man = val32;
+ }
+ of_node_put(classh);
+
+ /* frame depth location */
+ signal_format = of_get_child_by_name(np, "cirrus,monitor-signal-format");
+ monitor_config->is_present = signal_format ? true : false;
+ if (monitor_config->is_present) {
+ ret = of_property_read_u8_array(signal_format, "cirrus,imon",
+ monitor_array, imon_array_size);
+ if (!ret) {
+ monitor_config->imon_specs = true;
+ monitor_config->imon_dpth = monitor_array[0];
+ monitor_config->imon_loc = monitor_array[1];
+ monitor_config->imon_frm = monitor_array[2];
+ monitor_config->imon_scale = monitor_array[3];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vmon",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->vmon_specs = true;
+ monitor_config->vmon_dpth = monitor_array[0];
+ monitor_config->vmon_loc = monitor_array[1];
+ monitor_config->vmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vpmon",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->vpmon_specs = true;
+ monitor_config->vpmon_dpth = monitor_array[0];
+ monitor_config->vpmon_loc = monitor_array[1];
+ monitor_config->vpmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vbstmon",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->vbstmon_specs = true;
+ monitor_config->vbstmon_dpth = monitor_array[0];
+ monitor_config->vbstmon_loc = monitor_array[1];
+ monitor_config->vbstmon_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,vpbrstat",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->vpbrstat_specs = true;
+ monitor_config->vpbrstat_dpth = monitor_array[0];
+ monitor_config->vpbrstat_loc = monitor_array[1];
+ monitor_config->vpbrstat_frm = monitor_array[2];
+ }
+ ret = of_property_read_u8_array(signal_format, "cirrus,zerofill",
+ monitor_array, mon_array_size);
+ if (!ret) {
+ monitor_config->zerofill_specs = true;
+ monitor_config->zerofill_dpth = monitor_array[0];
+ monitor_config->zerofill_loc = monitor_array[1];
+ monitor_config->zerofill_frm = monitor_array[2];
+ }
+ }
+ of_node_put(signal_format);
+
+ return 0;
+}
+
+/* Errata Rev A0 */
+static const struct reg_sequence cs35l35_errata_patch[] = {
+
+ { 0x7F, 0x99 },
+ { 0x00, 0x99 },
+ { 0x52, 0x22 },
+ { 0x04, 0x14 },
+ { 0x6D, 0x44 },
+ { 0x24, 0x10 },
+ { 0x58, 0xC4 },
+ { 0x00, 0x98 },
+ { 0x18, 0x08 },
+ { 0x00, 0x00 },
+ { 0x7F, 0x00 },
+};
+
+static int cs35l35_i2c_probe(struct i2c_client *i2c_client,
+ const struct i2c_device_id *id)
+{
+ struct cs35l35_private *cs35l35;
+ struct device *dev = &i2c_client->dev;
+ struct cs35l35_platform_data *pdata = dev_get_platdata(dev);
+ int i;
+ int ret;
+ unsigned int devid = 0;
+ unsigned int reg;
+
+ cs35l35 = devm_kzalloc(dev, sizeof(struct cs35l35_private), GFP_KERNEL);
+ if (!cs35l35)
+ return -ENOMEM;
+
+ cs35l35->dev = dev;
+
+ i2c_set_clientdata(i2c_client, cs35l35);
+ cs35l35->regmap = devm_regmap_init_i2c(i2c_client, &cs35l35_regmap);
+ if (IS_ERR(cs35l35->regmap)) {
+ ret = PTR_ERR(cs35l35->regmap);
+ dev_err(dev, "regmap_init() failed: %d\n", ret);
+ goto err;
+ }
+
+ for (i = 0; i < ARRAY_SIZE(cs35l35_supplies); i++)
+ cs35l35->supplies[i].supply = cs35l35_supplies[i];
+
+ cs35l35->num_supplies = ARRAY_SIZE(cs35l35_supplies);
+
+ ret = devm_regulator_bulk_get(dev, cs35l35->num_supplies,
+ cs35l35->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request core supplies: %d\n", ret);
+ return ret;
+ }
+
+ if (pdata) {
+ cs35l35->pdata = *pdata;
+ } else {
+ pdata = devm_kzalloc(dev, sizeof(struct cs35l35_platform_data),
+ GFP_KERNEL);
+ if (!pdata)
+ return -ENOMEM;
+ if (i2c_client->dev.of_node) {
+ ret = cs35l35_handle_of_data(i2c_client, pdata);
+ if (ret != 0)
+ return ret;
+
+ }
+ cs35l35->pdata = *pdata;
+ }
+
+ ret = regulator_bulk_enable(cs35l35->num_supplies,
+ cs35l35->supplies);
+ if (ret != 0) {
+ dev_err(dev, "Failed to enable core supplies: %d\n", ret);
+ return ret;
+ }
+
+ /* returning NULL can be valid if in stereo mode */
+ cs35l35->reset_gpio = devm_gpiod_get_optional(dev, "reset",
+ GPIOD_OUT_LOW);
+ if (IS_ERR(cs35l35->reset_gpio)) {
+ ret = PTR_ERR(cs35l35->reset_gpio);
+ cs35l35->reset_gpio = NULL;
+ if (ret == -EBUSY) {
+ dev_info(dev,
+ "Reset line busy, assuming shared reset\n");
+ } else {
+ dev_err(dev, "Failed to get reset GPIO: %d\n", ret);
+ goto err;
+ }
+ }
+
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 1);
+
+ init_completion(&cs35l35->pdn_done);
+
+ ret = devm_request_threaded_irq(dev, i2c_client->irq, NULL, cs35l35_irq,
+ IRQF_ONESHOT | IRQF_TRIGGER_LOW |
+ IRQF_SHARED, "cs35l35", cs35l35);
+ if (ret != 0) {
+ dev_err(dev, "Failed to request IRQ: %d\n", ret);
+ goto err;
+ }
+ /* initialize codec */
+ ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_AB, &reg);
+
+ devid = (reg & 0xFF) << 12;
+ ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_CD, &reg);
+ devid |= (reg & 0xFF) << 4;
+ ret = regmap_read(cs35l35->regmap, CS35L35_DEVID_E, &reg);
+ devid |= (reg & 0xF0) >> 4;
+
+ if (devid != CS35L35_CHIP_ID) {
+ dev_err(dev, "CS35L35 Device ID (%X). Expected ID %X\n",
+ devid, CS35L35_CHIP_ID);
+ ret = -ENODEV;
+ goto err;
+ }
+
+ ret = regmap_read(cs35l35->regmap, CS35L35_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(dev, "Get Revision ID failed: %d\n", ret);
+ goto err;
+ }
+
+ ret = regmap_register_patch(cs35l35->regmap, cs35l35_errata_patch,
+ ARRAY_SIZE(cs35l35_errata_patch));
+ if (ret < 0) {
+ dev_err(dev, "Failed to apply errata patch: %d\n", ret);
+ goto err;
+ }
+
+ dev_info(dev, "Cirrus Logic CS35L35 (%x), Revision: %02X\n",
+ devid, reg & 0xFF);
+
+ /* Set the INT Masks for critical errors */
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_1,
+ CS35L35_INT1_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_2,
+ CS35L35_INT2_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_3,
+ CS35L35_INT3_CRIT_MASK);
+ regmap_write(cs35l35->regmap, CS35L35_INT_MASK_4,
+ CS35L35_INT4_CRIT_MASK);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PWR2_PDN_MASK,
+ CS35L35_PWR2_PDN_MASK);
+
+ if (cs35l35->pdata.bst_pdn_fet_on)
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 1 << CS35L35_PDN_BST_FETON_SHIFT);
+ else
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL2,
+ CS35L35_PDN_BST_MASK,
+ 1 << CS35L35_PDN_BST_FETOFF_SHIFT);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PWRCTL3,
+ CS35L35_PWR3_PDN_MASK,
+ CS35L35_PWR3_PDN_MASK);
+
+ regmap_update_bits(cs35l35->regmap, CS35L35_PROTECT_CTL,
+ CS35L35_AMP_MUTE_MASK, 1 << CS35L35_AMP_MUTE_SHIFT);
+
+ ret = snd_soc_register_codec(dev, &soc_codec_dev_cs35l35, cs35l35_dai,
+ ARRAY_SIZE(cs35l35_dai));
+ if (ret < 0) {
+ dev_err(dev, "Failed to register codec: %d\n", ret);
+ goto err;
+ }
+
+ return 0;
+
+err:
+ regulator_bulk_disable(cs35l35->num_supplies,
+ cs35l35->supplies);
+ gpiod_set_value_cansleep(cs35l35->reset_gpio, 0);
+
+ return ret;
+}
+
+static int cs35l35_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct of_device_id cs35l35_of_match[] = {
+ {.compatible = "cirrus,cs35l35"},
+ {},
+};
+MODULE_DEVICE_TABLE(of, cs35l35_of_match);
+
+static const struct i2c_device_id cs35l35_id[] = {
+ {"cs35l35", 0},
+ {}
+};
+
+MODULE_DEVICE_TABLE(i2c, cs35l35_id);
+
+static struct i2c_driver cs35l35_i2c_driver = {
+ .driver = {
+ .name = "cs35l35",
+ .of_match_table = cs35l35_of_match,
+ },
+ .id_table = cs35l35_id,
+ .probe = cs35l35_i2c_probe,
+ .remove = cs35l35_i2c_remove,
+};
+
+module_i2c_driver(cs35l35_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS35L35 driver");
+MODULE_AUTHOR("Brian Austin, Cirrus Logic Inc, <brian.austin@cirrus.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs35l35.h b/sound/soc/codecs/cs35l35.h
new file mode 100644
index 000000000000..5a6e43a87c4d
--- /dev/null
+++ b/sound/soc/codecs/cs35l35.h
@@ -0,0 +1,294 @@
+/*
+ * cs35l35.h -- CS35L35 ALSA SoC audio driver
+ *
+ * Copyright 2016 Cirrus Logic, Inc.
+ *
+ * Author: Brian Austin <brian.austin@cirrus.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ *
+ */
+
+#ifndef __CS35L35_H__
+#define __CS35L35_H__
+
+#define CS35L35_FIRSTREG 0x01
+#define CS35L35_LASTREG 0x7E
+#define CS35L35_CHIP_ID 0x00035A35
+#define CS35L35_DEVID_AB 0x01 /* Device ID A & B [RO] */
+#define CS35L35_DEVID_CD 0x02 /* Device ID C & D [RO] */
+#define CS35L35_DEVID_E 0x03 /* Device ID E [RO] */
+#define CS35L35_FAB_ID 0x04 /* Fab ID [RO] */
+#define CS35L35_REV_ID 0x05 /* Revision ID [RO] */
+#define CS35L35_PWRCTL1 0x06 /* Power Ctl 1 */
+#define CS35L35_PWRCTL2 0x07 /* Power Ctl 2 */
+#define CS35L35_PWRCTL3 0x08 /* Power Ctl 3 */
+#define CS35L35_CLK_CTL1 0x0A /* Clocking Ctl 1 */
+#define CS35L35_CLK_CTL2 0x0B /* Clocking Ctl 2 */
+#define CS35L35_CLK_CTL3 0x0C /* Clocking Ctl 3 */
+#define CS35L35_SP_FMT_CTL1 0x0D /* Serial Port Format CTL1 */
+#define CS35L35_SP_FMT_CTL2 0x0E /* Serial Port Format CTL2 */
+#define CS35L35_SP_FMT_CTL3 0x0F /* Serial Port Format CTL3 */
+#define CS35L35_MAG_COMP_CTL 0x13 /* Magnitude Comp CTL */
+#define CS35L35_AMP_INP_DRV_CTL 0x14 /* Amp Input Drive Ctl */
+#define CS35L35_AMP_DIG_VOL_CTL 0x15 /* Amplifier Dig Volume Ctl */
+#define CS35L35_AMP_DIG_VOL 0x16 /* Amplifier Dig Volume */
+#define CS35L35_ADV_DIG_VOL 0x17 /* Advisory Digital Volume */
+#define CS35L35_PROTECT_CTL 0x18 /* Amp Gain - Prot Ctl Param */
+#define CS35L35_AMP_GAIN_AUD_CTL 0x19 /* Amp Serial Port Gain Ctl */
+#define CS35L35_AMP_GAIN_PDM_CTL 0x1A /* Amplifier Gain PDM Ctl */
+#define CS35L35_AMP_GAIN_ADV_CTL 0x1B /* Amplifier Gain Ctl */
+#define CS35L35_GPI_CTL 0x1C /* GPI Ctl */
+#define CS35L35_BST_CVTR_V_CTL 0x1D /* Boost Conv Voltage Ctl */
+#define CS35L35_BST_PEAK_I 0x1E /* Boost Conv Peak Current */
+#define CS35L35_BST_RAMP_CTL 0x20 /* Boost Conv Soft Ramp Ctl */
+#define CS35L35_BST_CONV_COEF_1 0x21 /* Boost Conv Coefficients 1 */
+#define CS35L35_BST_CONV_COEF_2 0x22 /* Boost Conv Coefficients 2 */
+#define CS35L35_BST_CONV_SLOPE_COMP 0x23 /* Boost Conv Slope Comp */
+#define CS35L35_BST_CONV_SW_FREQ 0x24 /* Boost Conv L BST SW Freq */
+#define CS35L35_CLASS_H_CTL 0x30 /* CLS H Control */
+#define CS35L35_CLASS_H_HEADRM_CTL 0x31 /* CLS H Headroom Ctl */
+#define CS35L35_CLASS_H_RELEASE_RATE 0x32 /* CLS H Release Rate */
+#define CS35L35_CLASS_H_FET_DRIVE_CTL 0x33 /* CLS H Weak FET Drive Ctl */
+#define CS35L35_CLASS_H_VP_CTL 0x34 /* CLS H VP Ctl */
+#define CS35L35_CLASS_H_STATUS 0x38 /* CLS H Status */
+#define CS35L35_VPBR_CTL 0x3A /* VPBR Ctl */
+#define CS35L35_VPBR_VOL_CTL 0x3B /* VPBR Volume Ctl */
+#define CS35L35_VPBR_TIMING_CTL 0x3C /* VPBR Timing Ctl */
+#define CS35L35_VPBR_MODE_VOL_CTL 0x3D /* VPBR Mode/Attack Vol Ctl */
+#define CS35L35_VPBR_ATTEN_STATUS 0x4B /* VPBR Attenuation Status */
+#define CS35L35_SPKR_MON_CTL 0x4E /* Speaker Monitoring Ctl */
+#define CS35L35_IMON_SCALE_CTL 0x51 /* IMON Scale Ctl */
+#define CS35L35_AUDIN_RXLOC_CTL 0x52 /* Audio Input RX Loc Ctl */
+#define CS35L35_ADVIN_RXLOC_CTL 0x53 /* Advisory Input RX Loc Ctl */
+#define CS35L35_VMON_TXLOC_CTL 0x54 /* VMON TX Loc Ctl */
+#define CS35L35_IMON_TXLOC_CTL 0x55 /* IMON TX Loc Ctl */
+#define CS35L35_VPMON_TXLOC_CTL 0x56 /* VPMON TX Loc Ctl */
+#define CS35L35_VBSTMON_TXLOC_CTL 0x57 /* VBSTMON TX Loc Ctl */
+#define CS35L35_VPBR_STATUS_TXLOC_CTL 0x58 /* VPBR Status TX Loc Ctl */
+#define CS35L35_ZERO_FILL_LOC_CTL 0x59 /* Zero Fill Loc Ctl */
+#define CS35L35_AUDIN_DEPTH_CTL 0x5A /* Audio Input Depth Ctl */
+#define CS35L35_SPKMON_DEPTH_CTL 0x5B /* SPK Mon Output Depth Ctl */
+#define CS35L35_SUPMON_DEPTH_CTL 0x5C /* Supply Mon Out Depth Ctl */
+#define CS35L35_ZEROFILL_DEPTH_CTL 0x5D /* Zero Fill Mon Output Ctl */
+#define CS35L35_MULT_DEV_SYNCH1 0x62 /* Multidevice Synch */
+#define CS35L35_MULT_DEV_SYNCH2 0x63 /* Multidevice Synch 2 */
+#define CS35L35_PROT_RELEASE_CTL 0x64 /* Protection Release Ctl */
+#define CS35L35_DIAG_MODE_REG_LOCK 0x68 /* Diagnostic Mode Reg Lock */
+#define CS35L35_DIAG_MODE_CTL_1 0x69 /* Diagnostic Mode Ctl 1 */
+#define CS35L35_DIAG_MODE_CTL_2 0x6A /* Diagnostic Mode Ctl 2 */
+#define CS35L35_INT_MASK_1 0x70 /* Interrupt Mask 1 */
+#define CS35L35_INT_MASK_2 0x71 /* Interrupt Mask 2 */
+#define CS35L35_INT_MASK_3 0x72 /* Interrupt Mask 3 */
+#define CS35L35_INT_MASK_4 0x73 /* Interrupt Mask 4 */
+#define CS35L35_INT_STATUS_1 0x74 /* Interrupt Status 1 */
+#define CS35L35_INT_STATUS_2 0x75 /* Interrupt Status 2 */
+#define CS35L35_INT_STATUS_3 0x76 /* Interrupt Status 3 */
+#define CS35L35_INT_STATUS_4 0x77 /* Interrupt Status 4 */
+#define CS35L35_PLL_STATUS 0x78 /* PLL Status */
+#define CS35L35_OTP_TRIM_STATUS 0x7E /* OTP Trim Status */
+
+#define CS35L35_MAX_REGISTER 0x7F
+
+/* CS35L35_PWRCTL1 */
+#define CS35L35_SFT_RST 0x80
+#define CS35L35_DISCHG_FLT 0x02
+#define CS35L35_PDN_ALL 0x01
+
+/* CS35L35_PWRCTL2 */
+#define CS35L35_PDN_VMON 0x80
+#define CS35L35_PDN_IMON 0x40
+#define CS35L35_PDN_CLASSH 0x20
+#define CS35L35_PDN_VPBR 0x10
+#define CS35L35_PDN_BST 0x04
+#define CS35L35_PDN_AMP 0x01
+
+/* CS35L35_PWRCTL3 */
+#define CS35L35_PDN_VBSTMON_OUT 0x10
+#define CS35L35_PDN_VMON_OUT 0x08
+
+#define CS35L35_AUDIN_DEPTH_MASK 0x03
+#define CS35L35_AUDIN_DEPTH_SHIFT 0
+#define CS35L35_ADVIN_DEPTH_MASK 0x0C
+#define CS35L35_ADVIN_DEPTH_SHIFT 2
+#define CS35L35_SDIN_DEPTH_8 0x01
+#define CS35L35_SDIN_DEPTH_16 0x02
+#define CS35L35_SDIN_DEPTH_24 0x03
+
+#define CS35L35_SDOUT_DEPTH_8 0x01
+#define CS35L35_SDOUT_DEPTH_12 0x02
+#define CS35L35_SDOUT_DEPTH_16 0x03
+
+#define CS35L35_AUD_IN_LR_MASK 0x80
+#define CS35L35_AUD_IN_LR_SHIFT 7
+#define CS35L35_ADV_IN_LR_MASK 0x80
+#define CS35L35_ADV_IN_LR_SHIFT 7
+#define CS35L35_AUD_IN_LOC_MASK 0x0F
+#define CS35L35_AUD_IN_LOC_SHIFT 0
+#define CS35L35_ADV_IN_LOC_MASK 0x0F
+#define CS35L35_ADV_IN_LOC_SHIFT 0
+
+#define CS35L35_IMON_DEPTH_MASK 0x03
+#define CS35L35_IMON_DEPTH_SHIFT 0
+#define CS35L35_VMON_DEPTH_MASK 0x0C
+#define CS35L35_VMON_DEPTH_SHIFT 2
+#define CS35L35_VBSTMON_DEPTH_MASK 0x03
+#define CS35L35_VBSTMON_DEPTH_SHIFT 0
+#define CS35L35_VPMON_DEPTH_MASK 0x0C
+#define CS35L35_VPMON_DEPTH_SHIFT 2
+#define CS35L35_VPBRSTAT_DEPTH_MASK 0x30
+#define CS35L35_VPBRSTAT_DEPTH_SHIFT 4
+#define CS35L35_ZEROFILL_DEPTH_MASK 0x03
+#define CS35L35_ZEROFILL_DEPTH_SHIFT 0x00
+
+#define CS35L35_MON_TXLOC_MASK 0x3F
+#define CS35L35_MON_TXLOC_SHIFT 0
+#define CS35L35_MON_FRM_MASK 0x80
+#define CS35L35_MON_FRM_SHIFT 7
+
+#define CS35L35_IMON_SCALE_MASK 0xF8
+#define CS35L35_IMON_SCALE_SHIFT 3
+
+#define CS35L35_MS_MASK 0x80
+#define CS35L35_MS_SHIFT 7
+#define CS35L35_SPMODE_MASK 0x40
+#define CS35L35_SP_DRV_MASK 0x10
+#define CS35L35_SP_DRV_SHIFT 4
+#define CS35L35_CLK_CTL2_MASK 0xFF
+#define CS35L35_PDM_MODE_MASK 0x40
+#define CS35L35_PDM_MODE_SHIFT 6
+#define CS35L35_CLK_SOURCE_MASK 0x03
+#define CS35L35_CLK_SOURCE_SHIFT 0
+#define CS35L35_CLK_SOURCE_MCLK 0
+#define CS35L35_CLK_SOURCE_SCLK 1
+#define CS35L35_CLK_SOURCE_PDM 2
+
+#define CS35L35_SP_SCLKS_MASK 0x0F
+#define CS35L35_SP_SCLKS_SHIFT 0x00
+#define CS35L35_SP_SCLKS_16FS 0x03
+#define CS35L35_SP_SCLKS_32FS 0x07
+#define CS35L35_SP_SCLKS_48FS 0x0B
+#define CS35L35_SP_SCLKS_64FS 0x0F
+#define CS35L35_SP_RATE_MASK 0xC0
+
+#define CS35L35_PDN_BST_MASK 0x06
+#define CS35L35_PDN_BST_FETON_SHIFT 1
+#define CS35L35_PDN_BST_FETOFF_SHIFT 2
+#define CS35L35_PWR2_PDN_MASK 0xE0
+#define CS35L35_PWR3_PDN_MASK 0x1E
+#define CS35L35_PDN_ALL_MASK 0x01
+#define CS35L35_DISCHG_FILT_MASK 0x02
+#define CS35L35_DISCHG_FILT_SHIFT 1
+#define CS35L35_MCLK_DIS_MASK 0x04
+#define CS35L35_MCLK_DIS_SHIFT 2
+
+#define CS35L35_BST_CTL_MASK 0x7F
+#define CS35L35_BST_CTL_SHIFT 0
+#define CS35L35_BST_IPK_MASK 0x1F
+#define CS35L35_BST_IPK_SHIFT 0
+#define CS35L35_AMP_MUTE_MASK 0x20
+#define CS35L35_AMP_MUTE_SHIFT 5
+#define CS35L35_AMP_GAIN_ZC_MASK 0x10
+#define CS35L35_AMP_GAIN_ZC_SHIFT 4
+
+#define CS35L35_AMP_DIGSFT_MASK 0x02
+#define CS35L35_AMP_DIGSFT_SHIFT 1
+
+/* CS35L35_SP_FMT_CTL3 */
+#define CS35L35_SP_I2S_DRV_MASK 0x03
+#define CS35L35_SP_I2S_DRV_SHIFT 0
+
+/* Class H Algorithm Control */
+#define CS35L35_CH_STEREO_MASK 0x40
+#define CS35L35_CH_STEREO_SHIFT 6
+#define CS35L35_CH_BST_OVR_MASK 0x04
+#define CS35L35_CH_BST_OVR_SHIFT 2
+#define CS35L35_CH_BST_LIM_MASK 0x08
+#define CS35L35_CH_BST_LIM_SHIFT 3
+#define CS35L35_CH_MEM_DEPTH_MASK 0x01
+#define CS35L35_CH_MEM_DEPTH_SHIFT 0
+#define CS35L35_CH_HDRM_CTL_MASK 0x3F
+#define CS35L35_CH_HDRM_CTL_SHIFT 0
+#define CS35L35_CH_REL_RATE_MASK 0xFF
+#define CS35L35_CH_REL_RATE_SHIFT 0
+#define CS35L35_CH_WKFET_DIS_MASK 0x80
+#define CS35L35_CH_WKFET_DIS_SHIFT 7
+#define CS35L35_CH_WKFET_DEL_MASK 0x70
+#define CS35L35_CH_WKFET_DEL_SHIFT 4
+#define CS35L35_CH_WKFET_THLD_MASK 0x0F
+#define CS35L35_CH_WKFET_THLD_SHIFT 0
+#define CS35L35_CH_VP_AUTO_MASK 0x80
+#define CS35L35_CH_VP_AUTO_SHIFT 7
+#define CS35L35_CH_VP_RATE_MASK 0x60
+#define CS35L35_CH_VP_RATE_SHIFT 5
+#define CS35L35_CH_VP_MAN_MASK 0x1F
+#define CS35L35_CH_VP_MAN_SHIFT 0
+
+/* CS35L35_PROT_RELEASE_CTL */
+#define CS35L35_CAL_ERR_RLS 0x80
+#define CS35L35_SHORT_RLS 0x04
+#define CS35L35_OTW_RLS 0x02
+#define CS35L35_OTE_RLS 0x01
+
+/* INT Mask Registers */
+#define CS35L35_INT1_CRIT_MASK 0x38
+#define CS35L35_INT2_CRIT_MASK 0xEF
+#define CS35L35_INT3_CRIT_MASK 0xEE
+#define CS35L35_INT4_CRIT_MASK 0xFF
+
+/* PDN DONE Masks */
+#define CS35L35_M_PDN_DONE_SHIFT 4
+#define CS35L35_M_PDN_DONE_MASK 0x10
+
+/* CS35L35_INT_1 */
+#define CS35L35_CAL_ERR 0x80
+#define CS35L35_OTP_ERR 0x40
+#define CS35L35_LRCLK_ERR 0x20
+#define CS35L35_SPCLK_ERR 0x10
+#define CS35L35_MCLK_ERR 0x08
+#define CS35L35_AMP_SHORT 0x04
+#define CS35L35_OTW 0x02
+#define CS35L35_OTE 0x01
+
+/* CS35L35_INT_2 */
+#define CS35L35_PDN_DONE 0x10
+#define CS35L35_VPBR_ERR 0x02
+#define CS35L35_VPBR_CLR 0x01
+
+/* CS35L35_INT_3 */
+#define CS35L35_BST_HIGH 0x10
+#define CS35L35_BST_HIGH_FLAG 0x08
+#define CS35L35_BST_IPK_FLAG 0x04
+#define CS35L35_LBST_SHORT 0x01
+
+/* CS35L35_INT_4 */
+#define CS35L35_VMON_OVFL 0x08
+#define CS35L35_IMON_OVFL 0x04
+
+#define CS35L35_FORMATS (SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+struct cs35l35_private {
+ struct device *dev;
+ struct cs35l35_platform_data pdata;
+ struct regmap *regmap;
+ struct regulator_bulk_data supplies[2];
+ int num_supplies;
+ int sysclk;
+ int sclk;
+ bool pdm_mode;
+ bool i2s_mode;
+ bool slave_mode;
+ /* GPIO for /RST */
+ struct gpio_desc *reset_gpio;
+ struct completion pdn_done;
+};
+
+static const char * const cs35l35_supplies[] = {
+ "VA",
+ "VP",
+};
+
+#endif
diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c
index 8c0f3b89b5bc..e78b5f055f25 100644
--- a/sound/soc/codecs/cs4271.c
+++ b/sound/soc/codecs/cs4271.c
@@ -498,7 +498,7 @@ static int cs4271_reset(struct snd_soc_codec *codec)
struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
if (gpio_is_valid(cs4271->gpio_nreset)) {
- gpio_set_value(cs4271->gpio_nreset, 0);
+ gpio_direction_output(cs4271->gpio_nreset, 0);
mdelay(1);
gpio_set_value(cs4271->gpio_nreset, 1);
mdelay(1);
diff --git a/sound/soc/codecs/cs53l30.c b/sound/soc/codecs/cs53l30.c
index cb47fb595ff4..1e0d5973b758 100644
--- a/sound/soc/codecs/cs53l30.c
+++ b/sound/soc/codecs/cs53l30.c
@@ -1130,6 +1130,7 @@ MODULE_DEVICE_TABLE(i2c, cs53l30_id);
static struct i2c_driver cs53l30_i2c_driver = {
.driver = {
.name = "cs53l30",
+ .of_match_table = cs53l30_of_match,
.pm = &cs53l30_runtime_pm,
},
.id_table = cs53l30_id,
diff --git a/sound/soc/codecs/da7213.c b/sound/soc/codecs/da7213.c
index 12da55882c06..6dd7578f0bb8 100644
--- a/sound/soc/codecs/da7213.c
+++ b/sound/soc/codecs/da7213.c
@@ -12,6 +12,7 @@
* option) any later version.
*/
+#include <linux/acpi.h>
#include <linux/clk.h>
#include <linux/delay.h>
#include <linux/i2c.h>
@@ -1528,12 +1529,23 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec,
return 0;
}
+#if defined(CONFIG_OF)
/* DT */
static const struct of_device_id da7213_of_match[] = {
{ .compatible = "dlg,da7213", },
{ }
};
MODULE_DEVICE_TABLE(of, da7213_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id da7213_acpi_match[] = {
+ { "DLGS7212", 0},
+ { "DLGS7213", 0},
+ { },
+};
+MODULE_DEVICE_TABLE(acpi, da7213_acpi_match);
+#endif
static enum da7213_micbias_voltage
da7213_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
@@ -1844,6 +1856,7 @@ static struct i2c_driver da7213_i2c_driver = {
.driver = {
.name = "da7213",
.of_match_table = of_match_ptr(da7213_of_match),
+ .acpi_match_table = ACPI_PTR(da7213_acpi_match),
},
.probe = da7213_i2c_probe,
.remove = da7213_remove,
diff --git a/sound/soc/codecs/dio2125.c b/sound/soc/codecs/dio2125.c
new file mode 100644
index 000000000000..09451cd44f9b
--- /dev/null
+++ b/sound/soc/codecs/dio2125.c
@@ -0,0 +1,120 @@
+/*
+ * Copyright (c) 2017 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ */
+
+#include <linux/gpio/consumer.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+
+#define DRV_NAME "dio2125"
+
+struct dio2125 {
+ struct gpio_desc *gpiod_enable;
+};
+
+static int drv_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *control, int event)
+{
+ struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+ struct dio2125 *priv = snd_soc_component_get_drvdata(c);
+ int val;
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ val = 1;
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ val = 0;
+ break;
+ default:
+ WARN(1, "Unexpected event");
+ return -EINVAL;
+ }
+
+ gpiod_set_value_cansleep(priv->gpiod_enable, val);
+
+ return 0;
+}
+
+static const struct snd_soc_dapm_widget dio2125_dapm_widgets[] = {
+ SND_SOC_DAPM_INPUT("INL"),
+ SND_SOC_DAPM_INPUT("INR"),
+ SND_SOC_DAPM_OUT_DRV_E("DRV", SND_SOC_NOPM, 0, 0, NULL, 0, drv_event,
+ (SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)),
+ SND_SOC_DAPM_OUTPUT("OUTL"),
+ SND_SOC_DAPM_OUTPUT("OUTR"),
+};
+
+static const struct snd_soc_dapm_route dio2125_dapm_routes[] = {
+ { "DRV", NULL, "INL" },
+ { "DRV", NULL, "INR" },
+ { "OUTL", NULL, "DRV" },
+ { "OUTR", NULL, "DRV" },
+};
+
+static const struct snd_soc_component_driver dio2125_component_driver = {
+ .dapm_widgets = dio2125_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dio2125_dapm_widgets),
+ .dapm_routes = dio2125_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(dio2125_dapm_routes),
+};
+
+static int dio2125_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct dio2125 *priv;
+ int err;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (priv == NULL)
+ return -ENOMEM;
+ platform_set_drvdata(pdev, priv);
+
+ priv->gpiod_enable = devm_gpiod_get(dev, "enable", GPIOD_OUT_LOW);
+ if (IS_ERR(priv->gpiod_enable)) {
+ err = PTR_ERR(priv->gpiod_enable);
+ if (err != -EPROBE_DEFER)
+ dev_err(dev, "Failed to get 'enable' gpio: %d", err);
+ return err;
+ }
+
+ return devm_snd_soc_register_component(dev, &dio2125_component_driver,
+ NULL, 0);
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id dio2125_ids[] = {
+ { .compatible = "dioo,dio2125", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, dio2125_ids);
+#endif
+
+static struct platform_driver dio2125_driver = {
+ .driver = {
+ .name = DRV_NAME,
+ .of_match_table = of_match_ptr(dio2125_ids),
+ },
+ .probe = dio2125_probe,
+};
+
+module_platform_driver(dio2125_driver);
+
+MODULE_DESCRIPTION("ASoC DIO2125 output driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es7134.c b/sound/soc/codecs/es7134.c
new file mode 100644
index 000000000000..25ede825d349
--- /dev/null
+++ b/sound/soc/codecs/es7134.c
@@ -0,0 +1,116 @@
+/*
+ * Copyright (c) 2017 BayLibre, SAS.
+ * Author: Jerome Brunet <jbrunet@baylibre.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of version 2 of the GNU General Public License as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program; if not, see <http://www.gnu.org/licenses/>.
+ * The full GNU General Public License is included in this distribution
+ * in the file called COPYING.
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+/*
+ * The everest 7134 is a very simple DA converter with no register
+ */
+
+static int es7134_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ fmt &= (SND_SOC_DAIFMT_FORMAT_MASK | SND_SOC_DAIFMT_INV_MASK |
+ SND_SOC_DAIFMT_MASTER_MASK);
+
+ if (fmt != (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS)) {
+ dev_err(codec_dai->dev, "Invalid DAI format\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops es7134_dai_ops = {
+ .set_fmt = es7134_set_fmt,
+};
+
+static struct snd_soc_dai_driver es7134_dai = {
+ .name = "es7134-hifi",
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 2,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S18_3LE |
+ SNDRV_PCM_FMTBIT_S20_3LE |
+ SNDRV_PCM_FMTBIT_S24_3LE |
+ SNDRV_PCM_FMTBIT_S24_LE),
+ },
+ .ops = &es7134_dai_ops,
+};
+
+static const struct snd_soc_dapm_widget es7134_dapm_widgets[] = {
+ SND_SOC_DAPM_OUTPUT("AOUTL"),
+ SND_SOC_DAPM_OUTPUT("AOUTR"),
+ SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route es7134_dapm_routes[] = {
+ { "AOUTL", NULL, "DAC" },
+ { "AOUTR", NULL, "DAC" },
+};
+
+static struct snd_soc_codec_driver es7134_codec_driver = {
+ .component_driver = {
+ .dapm_widgets = es7134_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(es7134_dapm_widgets),
+ .dapm_routes = es7134_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(es7134_dapm_routes),
+ },
+};
+
+static int es7134_probe(struct platform_device *pdev)
+{
+ return snd_soc_register_codec(&pdev->dev,
+ &es7134_codec_driver,
+ &es7134_dai, 1);
+}
+
+static int es7134_remove(struct platform_device *pdev)
+{
+ snd_soc_unregister_codec(&pdev->dev);
+ return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id es7134_ids[] = {
+ { .compatible = "everest,es7134", },
+ { .compatible = "everest,es7144", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, es7134_ids);
+#endif
+
+static struct platform_driver es7134_driver = {
+ .driver = {
+ .name = "es7134",
+ .of_match_table = of_match_ptr(es7134_ids),
+ },
+ .probe = es7134_probe,
+ .remove = es7134_remove,
+};
+
+module_platform_driver(es7134_driver);
+
+MODULE_DESCRIPTION("ASoC ES7134 audio codec driver");
+MODULE_AUTHOR("Jerome Brunet <jbrunet@baylibre.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c
index 3f84fbd071e2..ed7cc42d1ee2 100644
--- a/sound/soc/codecs/es8328.c
+++ b/sound/soc/codecs/es8328.c
@@ -69,14 +69,10 @@ static const char * const supply_names[ES8328_SUPPLY_NUM] = {
"HPVDD",
};
-#define ES8328_RATES (SNDRV_PCM_RATE_96000 | \
- SNDRV_PCM_RATE_48000 | \
- SNDRV_PCM_RATE_44100 | \
- SNDRV_PCM_RATE_32000 | \
- SNDRV_PCM_RATE_22050 | \
- SNDRV_PCM_RATE_16000 | \
- SNDRV_PCM_RATE_11025 | \
- SNDRV_PCM_RATE_8000)
+#define ES8328_RATES (SNDRV_PCM_RATE_192000 | \
+ SNDRV_PCM_RATE_96000 | \
+ SNDRV_PCM_RATE_88200 | \
+ SNDRV_PCM_RATE_8000_48000)
#define ES8328_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S18_3LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
@@ -91,6 +87,7 @@ struct es8328_priv {
int mclkdiv2;
const struct snd_pcm_hw_constraint_list *sysclk_constraints;
const int *mclk_ratios;
+ bool master;
struct regulator_bulk_data supplies[ES8328_SUPPLY_NUM];
};
@@ -469,7 +466,7 @@ static int es8328_startup(struct snd_pcm_substream *substream,
struct snd_soc_codec *codec = dai->codec;
struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
- if (es8328->sysclk_constraints)
+ if (es8328->master && es8328->sysclk_constraints)
snd_pcm_hw_constraint_list(substream->runtime, 0,
SNDRV_PCM_HW_PARAM_RATE,
es8328->sysclk_constraints);
@@ -488,27 +485,34 @@ static int es8328_hw_params(struct snd_pcm_substream *substream,
int wl;
int ratio;
- if (!es8328->sysclk_constraints) {
- dev_err(codec->dev, "No MCLK configured\n");
- return -EINVAL;
- }
-
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
reg = ES8328_DACCONTROL2;
else
reg = ES8328_ADCCONTROL5;
- for (i = 0; i < es8328->sysclk_constraints->count; i++)
- if (es8328->sysclk_constraints->list[i] == params_rate(params))
- break;
+ if (es8328->master) {
+ if (!es8328->sysclk_constraints) {
+ dev_err(codec->dev, "No MCLK configured\n");
+ return -EINVAL;
+ }
- if (i == es8328->sysclk_constraints->count) {
- dev_err(codec->dev, "LRCLK %d unsupported with current clock\n",
- params_rate(params));
- return -EINVAL;
+ for (i = 0; i < es8328->sysclk_constraints->count; i++)
+ if (es8328->sysclk_constraints->list[i] ==
+ params_rate(params))
+ break;
+
+ if (i == es8328->sysclk_constraints->count) {
+ dev_err(codec->dev,
+ "LRCLK %d unsupported with current clock\n",
+ params_rate(params));
+ return -EINVAL;
+ }
+ ratio = es8328->mclk_ratios[i];
+ } else {
+ ratio = 0;
+ es8328->mclkdiv2 = 0;
}
- ratio = es8328->mclk_ratios[i];
snd_soc_update_bits(codec, ES8328_MASTERMODE,
ES8328_MASTERMODE_MCLKDIV2,
es8328->mclkdiv2 ? ES8328_MASTERMODE_MCLKDIV2 : 0);
@@ -586,6 +590,7 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
unsigned int fmt)
{
struct snd_soc_codec *codec = codec_dai->codec;
+ struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
u8 dac_mode = 0;
u8 adc_mode = 0;
@@ -595,11 +600,13 @@ static int es8328_set_dai_fmt(struct snd_soc_dai *codec_dai,
snd_soc_update_bits(codec, ES8328_MASTERMODE,
ES8328_MASTERMODE_MSC,
ES8328_MASTERMODE_MSC);
+ es8328->master = true;
break;
case SND_SOC_DAIFMT_CBS_CFS:
/* Slave serial port mode */
snd_soc_update_bits(codec, ES8328_MASTERMODE,
ES8328_MASTERMODE_MSC, 0);
+ es8328->master = false;
break;
default:
return -EINVAL;
diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c
index fd272a40485b..bc2e74ff3b2d 100644
--- a/sound/soc/codecs/hdac_hdmi.c
+++ b/sound/soc/codecs/hdac_hdmi.c
@@ -469,7 +469,7 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
format = snd_hdac_calc_stream_format(params_rate(hparams),
params_channels(hparams), params_format(hparams),
- 24, 0);
+ dai->driver->playback.sig_bits, 0);
pcm = hdac_hdmi_get_pcm_from_cvt(hdmi, dai_map->cvt);
if (!pcm)
@@ -1419,8 +1419,8 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdac,
hdmi_dais[i].playback.rate_min = rate_min;
hdmi_dais[i].playback.channels_min = 2;
hdmi_dais[i].playback.channels_max = 2;
+ hdmi_dais[i].playback.sig_bits = bps;
hdmi_dais[i].ops = &hdmi_dai_ops;
-
i++;
}
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
index 6cdf15ab46de..0247edc9c84e 100644
--- a/sound/soc/codecs/max9867.c
+++ b/sound/soc/codecs/max9867.c
@@ -516,13 +516,13 @@ static const struct i2c_device_id max9867_i2c_id[] = {
{ "max9867", 0 },
{ }
};
+MODULE_DEVICE_TABLE(i2c, max9867_i2c_id);
static const struct of_device_id max9867_of_match[] = {
{ .compatible = "maxim,max9867", },
{ }
};
-
-MODULE_DEVICE_TABLE(i2c, max9867_i2c_id);
+MODULE_DEVICE_TABLE(of, max9867_of_match);
static const struct dev_pm_ops max9867_pm_ops = {
SET_SYSTEM_SLEEP_PM_OPS(max9867_suspend, max9867_resume)
diff --git a/sound/soc/codecs/max98927.c b/sound/soc/codecs/max98927.c
new file mode 100644
index 000000000000..b5ee29499e16
--- /dev/null
+++ b/sound/soc/codecs/max98927.c
@@ -0,0 +1,841 @@
+/*
+ * max98927.c -- MAX98927 ALSA Soc Audio driver
+ *
+ * Copyright (C) 2016 Maxim Integrated Products
+ * Author: Ryan Lee <ryans.lee@maximintegrated.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ */
+
+#include <linux/acpi.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <sound/tlv.h>
+#include "max98927.h"
+
+static struct reg_default max98927_reg[] = {
+ {MAX98927_R0001_INT_RAW1, 0x00},
+ {MAX98927_R0002_INT_RAW2, 0x00},
+ {MAX98927_R0003_INT_RAW3, 0x00},
+ {MAX98927_R0004_INT_STATE1, 0x00},
+ {MAX98927_R0005_INT_STATE2, 0x00},
+ {MAX98927_R0006_INT_STATE3, 0x00},
+ {MAX98927_R0007_INT_FLAG1, 0x00},
+ {MAX98927_R0008_INT_FLAG2, 0x00},
+ {MAX98927_R0009_INT_FLAG3, 0x00},
+ {MAX98927_R000A_INT_EN1, 0x00},
+ {MAX98927_R000B_INT_EN2, 0x00},
+ {MAX98927_R000C_INT_EN3, 0x00},
+ {MAX98927_R000D_INT_FLAG_CLR1, 0x00},
+ {MAX98927_R000E_INT_FLAG_CLR2, 0x00},
+ {MAX98927_R000F_INT_FLAG_CLR3, 0x00},
+ {MAX98927_R0010_IRQ_CTRL, 0x00},
+ {MAX98927_R0011_CLK_MON, 0x00},
+ {MAX98927_R0012_WDOG_CTRL, 0x00},
+ {MAX98927_R0013_WDOG_RST, 0x00},
+ {MAX98927_R0014_MEAS_ADC_THERM_WARN_THRESH, 0x00},
+ {MAX98927_R0015_MEAS_ADC_THERM_SHDN_THRESH, 0x00},
+ {MAX98927_R0016_MEAS_ADC_THERM_HYSTERESIS, 0x00},
+ {MAX98927_R0017_PIN_CFG, 0x55},
+ {MAX98927_R0018_PCM_RX_EN_A, 0x00},
+ {MAX98927_R0019_PCM_RX_EN_B, 0x00},
+ {MAX98927_R001A_PCM_TX_EN_A, 0x00},
+ {MAX98927_R001B_PCM_TX_EN_B, 0x00},
+ {MAX98927_R001C_PCM_TX_HIZ_CTRL_A, 0x00},
+ {MAX98927_R001D_PCM_TX_HIZ_CTRL_B, 0x00},
+ {MAX98927_R001E_PCM_TX_CH_SRC_A, 0x00},
+ {MAX98927_R001F_PCM_TX_CH_SRC_B, 0x00},
+ {MAX98927_R0020_PCM_MODE_CFG, 0x40},
+ {MAX98927_R0021_PCM_MASTER_MODE, 0x00},
+ {MAX98927_R0022_PCM_CLK_SETUP, 0x22},
+ {MAX98927_R0023_PCM_SR_SETUP1, 0x00},
+ {MAX98927_R0024_PCM_SR_SETUP2, 0x00},
+ {MAX98927_R0025_PCM_TO_SPK_MONOMIX_A, 0x00},
+ {MAX98927_R0026_PCM_TO_SPK_MONOMIX_B, 0x00},
+ {MAX98927_R0027_ICC_RX_EN_A, 0x00},
+ {MAX98927_R0028_ICC_RX_EN_B, 0x00},
+ {MAX98927_R002B_ICC_TX_EN_A, 0x00},
+ {MAX98927_R002C_ICC_TX_EN_B, 0x00},
+ {MAX98927_R002E_ICC_HIZ_MANUAL_MODE, 0x00},
+ {MAX98927_R002F_ICC_TX_HIZ_EN_A, 0x00},
+ {MAX98927_R0030_ICC_TX_HIZ_EN_B, 0x00},
+ {MAX98927_R0031_ICC_LNK_EN, 0x00},
+ {MAX98927_R0032_PDM_TX_EN, 0x00},
+ {MAX98927_R0033_PDM_TX_HIZ_CTRL, 0x00},
+ {MAX98927_R0034_PDM_TX_CTRL, 0x00},
+ {MAX98927_R0035_PDM_RX_CTRL, 0x00},
+ {MAX98927_R0036_AMP_VOL_CTRL, 0x00},
+ {MAX98927_R0037_AMP_DSP_CFG, 0x02},
+ {MAX98927_R0038_TONE_GEN_DC_CFG, 0x00},
+ {MAX98927_R0039_DRE_CTRL, 0x01},
+ {MAX98927_R003A_AMP_EN, 0x00},
+ {MAX98927_R003B_SPK_SRC_SEL, 0x00},
+ {MAX98927_R003C_SPK_GAIN, 0x00},
+ {MAX98927_R003D_SSM_CFG, 0x01},
+ {MAX98927_R003E_MEAS_EN, 0x00},
+ {MAX98927_R003F_MEAS_DSP_CFG, 0x04},
+ {MAX98927_R0040_BOOST_CTRL0, 0x00},
+ {MAX98927_R0041_BOOST_CTRL3, 0x00},
+ {MAX98927_R0042_BOOST_CTRL1, 0x00},
+ {MAX98927_R0043_MEAS_ADC_CFG, 0x00},
+ {MAX98927_R0044_MEAS_ADC_BASE_MSB, 0x00},
+ {MAX98927_R0045_MEAS_ADC_BASE_LSB, 0x00},
+ {MAX98927_R0046_ADC_CH0_DIVIDE, 0x00},
+ {MAX98927_R0047_ADC_CH1_DIVIDE, 0x00},
+ {MAX98927_R0048_ADC_CH2_DIVIDE, 0x00},
+ {MAX98927_R0049_ADC_CH0_FILT_CFG, 0x00},
+ {MAX98927_R004A_ADC_CH1_FILT_CFG, 0x00},
+ {MAX98927_R004B_ADC_CH2_FILT_CFG, 0x00},
+ {MAX98927_R004C_MEAS_ADC_CH0_READ, 0x00},
+ {MAX98927_R004D_MEAS_ADC_CH1_READ, 0x00},
+ {MAX98927_R004E_MEAS_ADC_CH2_READ, 0x00},
+ {MAX98927_R0051_BROWNOUT_STATUS, 0x00},
+ {MAX98927_R0052_BROWNOUT_EN, 0x00},
+ {MAX98927_R0053_BROWNOUT_INFINITE_HOLD, 0x00},
+ {MAX98927_R0054_BROWNOUT_INFINITE_HOLD_CLR, 0x00},
+ {MAX98927_R0055_BROWNOUT_LVL_HOLD, 0x00},
+ {MAX98927_R005A_BROWNOUT_LVL1_THRESH, 0x00},
+ {MAX98927_R005B_BROWNOUT_LVL2_THRESH, 0x00},
+ {MAX98927_R005C_BROWNOUT_LVL3_THRESH, 0x00},
+ {MAX98927_R005D_BROWNOUT_LVL4_THRESH, 0x00},
+ {MAX98927_R005E_BROWNOUT_THRESH_HYSTERYSIS, 0x00},
+ {MAX98927_R005F_BROWNOUT_AMP_LIMITER_ATK_REL, 0x00},
+ {MAX98927_R0060_BROWNOUT_AMP_GAIN_ATK_REL, 0x00},
+ {MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE, 0x00},
+ {MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT, 0x00},
+ {MAX98927_R0073_BROWNOUT_LVL1_AMP1_CTRL1, 0x00},
+ {MAX98927_R0074_BROWNOUT_LVL1_AMP1_CTRL2, 0x00},
+ {MAX98927_R0075_BROWNOUT_LVL1_AMP1_CTRL3, 0x00},
+ {MAX98927_R0076_BROWNOUT_LVL2_CUR_LIMIT, 0x00},
+ {MAX98927_R0077_BROWNOUT_LVL2_AMP1_CTRL1, 0x00},
+ {MAX98927_R0078_BROWNOUT_LVL2_AMP1_CTRL2, 0x00},
+ {MAX98927_R0079_BROWNOUT_LVL2_AMP1_CTRL3, 0x00},
+ {MAX98927_R007A_BROWNOUT_LVL3_CUR_LIMIT, 0x00},
+ {MAX98927_R007B_BROWNOUT_LVL3_AMP1_CTRL1, 0x00},
+ {MAX98927_R007C_BROWNOUT_LVL3_AMP1_CTRL2, 0x00},
+ {MAX98927_R007D_BROWNOUT_LVL3_AMP1_CTRL3, 0x00},
+ {MAX98927_R007E_BROWNOUT_LVL4_CUR_LIMIT, 0x00},
+ {MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1, 0x00},
+ {MAX98927_R0080_BROWNOUT_LVL4_AMP1_CTRL2, 0x00},
+ {MAX98927_R0081_BROWNOUT_LVL4_AMP1_CTRL3, 0x00},
+ {MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM, 0x00},
+ {MAX98927_R0083_ENV_TRACK_BOOST_VOUT_DELAY, 0x00},
+ {MAX98927_R0084_ENV_TRACK_REL_RATE, 0x00},
+ {MAX98927_R0085_ENV_TRACK_HOLD_RATE, 0x00},
+ {MAX98927_R0086_ENV_TRACK_CTRL, 0x00},
+ {MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ, 0x00},
+ {MAX98927_R00FF_GLOBAL_SHDN, 0x00},
+ {MAX98927_R0100_SOFT_RESET, 0x00},
+ {MAX98927_R01FF_REV_ID, 0x40},
+};
+
+static int max98927_dai_set_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = codec_dai->codec;
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ unsigned int mode = 0;
+ unsigned int format = 0;
+ unsigned int invert = 0;
+
+ dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBS_CFS:
+ mode = MAX98927_PCM_MASTER_MODE_SLAVE;
+ break;
+ case SND_SOC_DAIFMT_CBM_CFM:
+ max98927->master = true;
+ mode = MAX98927_PCM_MASTER_MODE_MASTER;
+ break;
+ default:
+ dev_err(codec->dev, "DAI clock mode unsupported");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0021_PCM_MASTER_MODE,
+ MAX98927_PCM_MASTER_MODE_MASK,
+ mode);
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ invert = MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE;
+ break;
+ default:
+ dev_err(codec->dev, "DAI invert mode unsupported");
+ return -EINVAL;
+ }
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE,
+ invert);
+
+ /* interface format */
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ max98927->iface |= SND_SOC_DAIFMT_I2S;
+ format = MAX98927_PCM_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ max98927->iface |= SND_SOC_DAIFMT_LEFT_J;
+ format = MAX98927_PCM_FORMAT_LJ;
+ break;
+ case SND_SOC_DAIFMT_PDM:
+ max98927->iface |= SND_SOC_DAIFMT_PDM;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ /* pcm channel configuration */
+ if (max98927->iface & (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_LEFT_J)) {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0018_PCM_RX_EN_A,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN);
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_FORMAT_MASK,
+ format << MAX98927_PCM_MODE_CFG_FORMAT_SHIFT);
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R003B_SPK_SRC_SEL,
+ MAX98927_SPK_SRC_MASK, 0);
+
+ } else
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0018_PCM_RX_EN_A,
+ MAX98927_PCM_RX_CH0_EN | MAX98927_PCM_RX_CH1_EN, 0);
+
+ /* pdm channel configuration */
+ if (max98927->iface & SND_SOC_DAIFMT_PDM) {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0035_PDM_RX_CTRL,
+ MAX98927_PDM_RX_EN_MASK, 1);
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R003B_SPK_SRC_SEL,
+ MAX98927_SPK_SRC_MASK, 3);
+ } else
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0035_PDM_RX_CTRL,
+ MAX98927_PDM_RX_EN_MASK, 0);
+ return 0;
+}
+
+/* codec MCLK rate in master mode */
+static const int rate_table[] = {
+ 5644800, 6000000, 6144000, 6500000,
+ 9600000, 11289600, 12000000, 12288000,
+ 13000000, 19200000,
+};
+
+static int max98927_set_clock(struct max98927_priv *max98927,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_codec *codec = max98927->codec;
+ /* BCLK/LRCLK ratio calculation */
+ int blr_clk_ratio = params_channels(params) * max98927->ch_size;
+ int value;
+
+ if (max98927->master) {
+ int i;
+ /* match rate to closest value */
+ for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+ if (rate_table[i] >= max98927->sysclk)
+ break;
+ }
+ if (i == ARRAY_SIZE(rate_table)) {
+ dev_err(codec->dev, "failed to find proper clock rate.\n");
+ return -EINVAL;
+ }
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0021_PCM_MASTER_MODE,
+ MAX98927_PCM_MASTER_MODE_MCLK_MASK,
+ i << MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT);
+ }
+
+ switch (blr_clk_ratio) {
+ case 32:
+ value = 2;
+ break;
+ case 48:
+ value = 3;
+ break;
+ case 64:
+ value = 4;
+ break;
+ default:
+ return -EINVAL;
+ }
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0022_PCM_CLK_SETUP,
+ MAX98927_PCM_CLK_SETUP_BSEL_MASK,
+ value);
+ return 0;
+}
+
+static int max98927_dai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *dai)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+ unsigned int sampling_rate = 0;
+ unsigned int chan_sz = 0;
+
+ /* pcm mode configuration */
+ switch (snd_pcm_format_width(params_format(params))) {
+ case 16:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_16;
+ break;
+ case 24:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_24;
+ break;
+ case 32:
+ chan_sz = MAX98927_PCM_MODE_CFG_CHANSZ_32;
+ break;
+ default:
+ dev_err(codec->dev, "format unsupported %d",
+ params_format(params));
+ goto err;
+ }
+
+ max98927->ch_size = snd_pcm_format_width(params_format(params));
+
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0020_PCM_MODE_CFG,
+ MAX98927_PCM_MODE_CFG_CHANSZ_MASK, chan_sz);
+
+ dev_dbg(codec->dev, "format supported %d",
+ params_format(params));
+
+ /* sampling rate configuration */
+ switch (params_rate(params)) {
+ case 8000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_8000;
+ break;
+ case 11025:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_11025;
+ break;
+ case 12000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_12000;
+ break;
+ case 16000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_16000;
+ break;
+ case 22050:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_22050;
+ break;
+ case 24000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_24000;
+ break;
+ case 32000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_32000;
+ break;
+ case 44100:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_44100;
+ break;
+ case 48000:
+ sampling_rate = MAX98927_PCM_SR_SET1_SR_48000;
+ break;
+ default:
+ dev_err(codec->dev, "rate %d not supported\n",
+ params_rate(params));
+ goto err;
+ }
+ /* set DAI_SR to correct LRCLK frequency */
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0023_PCM_SR_SETUP1,
+ MAX98927_PCM_SR_SET1_SR_MASK,
+ sampling_rate);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0024_PCM_SR_SETUP2,
+ MAX98927_PCM_SR_SET2_SR_MASK,
+ sampling_rate << MAX98927_PCM_SR_SET2_SR_SHIFT);
+
+ /* set sampling rate of IV */
+ if (max98927->interleave_mode &&
+ sampling_rate > MAX98927_PCM_SR_SET1_SR_16000)
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0024_PCM_SR_SETUP2,
+ MAX98927_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate - 3);
+ else
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R0024_PCM_SR_SETUP2,
+ MAX98927_PCM_SR_SET2_IVADC_SR_MASK,
+ sampling_rate);
+ return max98927_set_clock(max98927, params);
+err:
+ return -EINVAL;
+}
+
+#define MAX98927_RATES SNDRV_PCM_RATE_8000_48000
+
+#define MAX98927_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+ SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static int max98927_dai_set_sysclk(struct snd_soc_dai *dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+
+ max98927->sysclk = freq;
+ return 0;
+}
+
+static const struct snd_soc_dai_ops max98927_dai_ops = {
+ .set_sysclk = max98927_dai_set_sysclk,
+ .set_fmt = max98927_dai_set_fmt,
+ .hw_params = max98927_dai_hw_params,
+};
+
+static int max98927_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R003A_AMP_EN,
+ MAX98927_AMP_EN_MASK, 1);
+ /* enable VMON and IMON */
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R003E_MEAS_EN,
+ MAX98927_MEAS_V_EN | MAX98927_MEAS_I_EN,
+ MAX98927_MEAS_V_EN | MAX98927_MEAS_I_EN);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R00FF_GLOBAL_SHDN,
+ MAX98927_GLOBAL_EN_MASK, 1);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R00FF_GLOBAL_SHDN,
+ MAX98927_GLOBAL_EN_MASK, 0);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R003A_AMP_EN,
+ MAX98927_AMP_EN_MASK, 0);
+ /* disable VMON and IMON */
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R003E_MEAS_EN,
+ MAX98927_MEAS_V_EN | MAX98927_MEAS_I_EN, 0);
+ break;
+ default:
+ return 0;
+ }
+ return 0;
+}
+
+static const char * const max98927_switch_text[] = {
+ "Left", "Right", "LeftRight"};
+
+static const struct soc_enum dai_sel_enum =
+ SOC_ENUM_SINGLE(MAX98927_R0025_PCM_TO_SPK_MONOMIX_A,
+ MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT,
+ 3, max98927_switch_text);
+
+static const struct snd_kcontrol_new max98927_dai_controls =
+ SOC_DAPM_ENUM("DAI Sel", dai_sel_enum);
+
+static const struct snd_soc_dapm_widget max98927_dapm_widgets[] = {
+ SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_DAC_E("Amp Enable", "HiFi Playback", MAX98927_R003A_AMP_EN,
+ 0, 0, max98927_dac_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_MUX("DAI Sel Mux", SND_SOC_NOPM, 0, 0,
+ &max98927_dai_controls),
+ SND_SOC_DAPM_OUTPUT("BE_OUT"),
+};
+
+static DECLARE_TLV_DB_SCALE(max98927_spk_tlv, 300, 300, 0);
+static DECLARE_TLV_DB_SCALE(max98927_digital_tlv, -1600, 25, 0);
+
+static bool max98927_readable_register(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98927_R0001_INT_RAW1 ... MAX98927_R0028_ICC_RX_EN_B:
+ case MAX98927_R002B_ICC_TX_EN_A ... MAX98927_R002C_ICC_TX_EN_B:
+ case MAX98927_R002E_ICC_HIZ_MANUAL_MODE
+ ... MAX98927_R004E_MEAS_ADC_CH2_READ:
+ case MAX98927_R0051_BROWNOUT_STATUS
+ ... MAX98927_R0055_BROWNOUT_LVL_HOLD:
+ case MAX98927_R005A_BROWNOUT_LVL1_THRESH
+ ... MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE:
+ case MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT
+ ... MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ:
+ case MAX98927_R00FF_GLOBAL_SHDN:
+ case MAX98927_R0100_SOFT_RESET:
+ case MAX98927_R01FF_REV_ID:
+ return true;
+ default:
+ return false;
+ }
+};
+
+static bool max98927_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case MAX98927_R0001_INT_RAW1 ... MAX98927_R0009_INT_FLAG3:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const max98927_boost_voltage_text[] = {
+ "6.5V", "6.625V", "6.75V", "6.875V", "7V", "7.125V", "7.25V", "7.375V",
+ "7.5V", "7.625V", "7.75V", "7.875V", "8V", "8.125V", "8.25V", "8.375V",
+ "8.5V", "8.625V", "8.75V", "8.875V", "9V", "9.125V", "9.25V", "9.375V",
+ "9.5V", "9.625V", "9.75V", "9.875V", "10V"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98927_boost_voltage,
+ MAX98927_R0040_BOOST_CTRL0, 0,
+ max98927_boost_voltage_text);
+
+static const char * const max98927_current_limit_text[] = {
+ "1.00A", "1.10A", "1.20A", "1.30A", "1.40A", "1.50A", "1.60A", "1.70A",
+ "1.80A", "1.90A", "2.00A", "2.10A", "2.20A", "2.30A", "2.40A", "2.50A",
+ "2.60A", "2.70A", "2.80A", "2.90A", "3.00A", "3.10A", "3.20A", "3.30A",
+ "3.40A", "3.50A", "3.60A", "3.70A", "3.80A", "3.90A", "4.00A", "4.10A"
+};
+
+static SOC_ENUM_SINGLE_DECL(max98927_current_limit,
+ MAX98927_R0042_BOOST_CTRL1, 1,
+ max98927_current_limit_text);
+
+static const struct snd_kcontrol_new max98927_snd_controls[] = {
+ SOC_SINGLE_TLV("Speaker Volume", MAX98927_R003C_SPK_GAIN,
+ 0, 6, 0,
+ max98927_spk_tlv),
+ SOC_SINGLE_TLV("Digital Volume", MAX98927_R0036_AMP_VOL_CTRL,
+ 0, (1<<MAX98927_AMP_VOL_WIDTH)-1, 0,
+ max98927_digital_tlv),
+ SOC_SINGLE("Amp DSP Switch", MAX98927_R0052_BROWNOUT_EN,
+ MAX98927_BROWNOUT_DSP_SHIFT, 1, 0),
+ SOC_SINGLE("Ramp Switch", MAX98927_R0037_AMP_DSP_CFG,
+ MAX98927_AMP_DSP_CFG_RMP_SHIFT, 1, 0),
+ SOC_SINGLE("DRE Switch", MAX98927_R0039_DRE_CTRL,
+ MAX98927_DRE_EN_SHIFT, 1, 0),
+ SOC_SINGLE("Volume Location Switch", MAX98927_R0036_AMP_VOL_CTRL,
+ MAX98927_AMP_VOL_SEL_SHIFT, 1, 0),
+ SOC_ENUM("Boost Output Voltage", max98927_boost_voltage),
+ SOC_ENUM("Current Limit", max98927_current_limit),
+};
+
+static const struct snd_soc_dapm_route max98927_audio_map[] = {
+ {"Amp Enable", NULL, "DAI_OUT"},
+ {"DAI Sel Mux", "Left", "Amp Enable"},
+ {"DAI Sel Mux", "Right", "Amp Enable"},
+ {"DAI Sel Mux", "LeftRight", "Amp Enable"},
+ {"BE_OUT", NULL, "DAI Sel Mux"},
+};
+
+static struct snd_soc_dai_driver max98927_dai[] = {
+ {
+ .name = "max98927-aif1",
+ .playback = {
+ .stream_name = "HiFi Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98927_RATES,
+ .formats = MAX98927_FORMATS,
+ },
+ .capture = {
+ .stream_name = "HiFi Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = MAX98927_RATES,
+ .formats = MAX98927_FORMATS,
+ },
+ .ops = &max98927_dai_ops,
+ }
+};
+
+static int max98927_probe(struct snd_soc_codec *codec)
+{
+ struct max98927_priv *max98927 = snd_soc_codec_get_drvdata(codec);
+
+ max98927->codec = codec;
+ codec->control_data = max98927->regmap;
+ codec->cache_bypass = 1;
+
+ /* Software Reset */
+ regmap_write(max98927->regmap,
+ MAX98927_R0100_SOFT_RESET, MAX98927_SOFT_RESET);
+
+ /* IV default slot configuration */
+ regmap_write(max98927->regmap,
+ MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ 0xFF);
+ regmap_write(max98927->regmap,
+ MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ 0xFF);
+ regmap_write(max98927->regmap,
+ MAX98927_R0025_PCM_TO_SPK_MONOMIX_A,
+ 0x80);
+ regmap_write(max98927->regmap,
+ MAX98927_R0026_PCM_TO_SPK_MONOMIX_B,
+ 0x1);
+ /* Set inital volume (+13dB) */
+ regmap_write(max98927->regmap,
+ MAX98927_R0036_AMP_VOL_CTRL,
+ 0x38);
+ regmap_write(max98927->regmap,
+ MAX98927_R003C_SPK_GAIN,
+ 0x05);
+ /* Enable DC blocker */
+ regmap_write(max98927->regmap,
+ MAX98927_R0037_AMP_DSP_CFG,
+ 0x03);
+ /* Enable IMON VMON DC blocker */
+ regmap_write(max98927->regmap,
+ MAX98927_R003F_MEAS_DSP_CFG,
+ 0xF7);
+ /* Boost Output Voltage & Current limit */
+ regmap_write(max98927->regmap,
+ MAX98927_R0040_BOOST_CTRL0,
+ 0x1C);
+ regmap_write(max98927->regmap,
+ MAX98927_R0042_BOOST_CTRL1,
+ 0x3E);
+ /* Measurement ADC config */
+ regmap_write(max98927->regmap,
+ MAX98927_R0043_MEAS_ADC_CFG,
+ 0x04);
+ regmap_write(max98927->regmap,
+ MAX98927_R0044_MEAS_ADC_BASE_MSB,
+ 0x00);
+ regmap_write(max98927->regmap,
+ MAX98927_R0045_MEAS_ADC_BASE_LSB,
+ 0x24);
+ /* Brownout Level */
+ regmap_write(max98927->regmap,
+ MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1,
+ 0x06);
+ /* Envelope Tracking configuration */
+ regmap_write(max98927->regmap,
+ MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM,
+ 0x08);
+ regmap_write(max98927->regmap,
+ MAX98927_R0086_ENV_TRACK_CTRL,
+ 0x01);
+ regmap_write(max98927->regmap,
+ MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ,
+ 0x10);
+
+ /* voltage, current slot configuration */
+ regmap_write(max98927->regmap,
+ MAX98927_R001E_PCM_TX_CH_SRC_A,
+ (max98927->i_l_slot<<MAX98927_PCM_TX_CH_SRC_A_I_SHIFT|
+ max98927->v_l_slot)&0xFF);
+
+ if (max98927->v_l_slot < 8) {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ 1 << max98927->v_l_slot, 0);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001A_PCM_TX_EN_A,
+ 1 << max98927->v_l_slot,
+ 1 << max98927->v_l_slot);
+ } else {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ 1 << (max98927->v_l_slot - 8), 0);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001B_PCM_TX_EN_B,
+ 1 << (max98927->v_l_slot - 8),
+ 1 << (max98927->v_l_slot - 8));
+ }
+
+ if (max98927->i_l_slot < 8) {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001C_PCM_TX_HIZ_CTRL_A,
+ 1 << max98927->i_l_slot, 0);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001A_PCM_TX_EN_A,
+ 1 << max98927->i_l_slot,
+ 1 << max98927->i_l_slot);
+ } else {
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001D_PCM_TX_HIZ_CTRL_B,
+ 1 << (max98927->i_l_slot - 8), 0);
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001B_PCM_TX_EN_B,
+ 1 << (max98927->i_l_slot - 8),
+ 1 << (max98927->i_l_slot - 8));
+ }
+
+ /* Set interleave mode */
+ if (max98927->interleave_mode)
+ regmap_update_bits(max98927->regmap,
+ MAX98927_R001F_PCM_TX_CH_SRC_B,
+ MAX98927_PCM_TX_CH_INTERLEAVE_MASK,
+ MAX98927_PCM_TX_CH_INTERLEAVE_MASK);
+ return 0;
+}
+
+static const struct snd_soc_codec_driver soc_codec_dev_max98927 = {
+ .probe = max98927_probe,
+ .component_driver = {
+ .controls = max98927_snd_controls,
+ .num_controls = ARRAY_SIZE(max98927_snd_controls),
+ .dapm_widgets = max98927_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(max98927_dapm_widgets),
+ .dapm_routes = max98927_audio_map,
+ .num_dapm_routes = ARRAY_SIZE(max98927_audio_map),
+ },
+};
+
+static const struct regmap_config max98927_regmap = {
+ .reg_bits = 16,
+ .val_bits = 8,
+ .max_register = MAX98927_R01FF_REV_ID,
+ .reg_defaults = max98927_reg,
+ .num_reg_defaults = ARRAY_SIZE(max98927_reg),
+ .readable_reg = max98927_readable_register,
+ .volatile_reg = max98927_volatile_reg,
+ .cache_type = REGCACHE_RBTREE,
+};
+
+static void max98927_slot_config(struct i2c_client *i2c,
+ struct max98927_priv *max98927)
+{
+ int value;
+
+ if (!of_property_read_u32(i2c->dev.of_node,
+ "vmon-slot-no", &value))
+ max98927->v_l_slot = value & 0xF;
+ else
+ max98927->v_l_slot = 0;
+ if (!of_property_read_u32(i2c->dev.of_node,
+ "imon-slot-no", &value))
+ max98927->i_l_slot = value & 0xF;
+ else
+ max98927->i_l_slot = 1;
+}
+
+static int max98927_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+
+ int ret = 0, value;
+ int reg = 0;
+ struct max98927_priv *max98927 = NULL;
+
+ max98927 = devm_kzalloc(&i2c->dev,
+ sizeof(*max98927), GFP_KERNEL);
+
+ if (!max98927) {
+ ret = -ENOMEM;
+ return ret;
+ }
+ i2c_set_clientdata(i2c, max98927);
+
+ /* update interleave mode info */
+ if (!of_property_read_u32(i2c->dev.of_node,
+ "interleave_mode", &value)) {
+ if (value > 0)
+ max98927->interleave_mode = 1;
+ else
+ max98927->interleave_mode = 0;
+ } else
+ max98927->interleave_mode = 0;
+
+ /* regmap initialization */
+ max98927->regmap
+ = devm_regmap_init_i2c(i2c, &max98927_regmap);
+ if (IS_ERR(max98927->regmap)) {
+ ret = PTR_ERR(max98927->regmap);
+ dev_err(&i2c->dev,
+ "Failed to allocate regmap: %d\n", ret);
+ return ret;
+ }
+
+ /* Check Revision ID */
+ ret = regmap_read(max98927->regmap,
+ MAX98927_R01FF_REV_ID, &reg);
+ if (ret < 0) {
+ dev_err(&i2c->dev,
+ "Failed to read: 0x%02X\n", MAX98927_R01FF_REV_ID);
+ return ret;
+ }
+ dev_info(&i2c->dev, "MAX98927 revisionID: 0x%02X\n", reg);
+
+ /* voltage/current slot configuration */
+ max98927_slot_config(i2c, max98927);
+
+ /* codec registeration */
+ ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98927,
+ max98927_dai, ARRAY_SIZE(max98927_dai));
+ if (ret < 0)
+ dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+
+ return ret;
+}
+
+static int max98927_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id max98927_i2c_id[] = {
+ { "max98927", 0},
+ { },
+};
+
+MODULE_DEVICE_TABLE(i2c, max98927_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id max98927_of_match[] = {
+ { .compatible = "maxim,max98927", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, max98927_of_match);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id max98927_acpi_match[] = {
+ { "MX98927", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, max98927_acpi_match);
+#endif
+
+static struct i2c_driver max98927_i2c_driver = {
+ .driver = {
+ .name = "max98927",
+ .of_match_table = of_match_ptr(max98927_of_match),
+ .acpi_match_table = ACPI_PTR(max98927_acpi_match),
+ .pm = NULL,
+ },
+ .probe = max98927_i2c_probe,
+ .remove = max98927_i2c_remove,
+ .id_table = max98927_i2c_id,
+};
+
+module_i2c_driver(max98927_i2c_driver)
+
+MODULE_DESCRIPTION("ALSA SoC MAX98927 driver");
+MODULE_AUTHOR("Ryan Lee <ryans.lee@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98927.h b/sound/soc/codecs/max98927.h
new file mode 100644
index 000000000000..ece6a608cbe1
--- /dev/null
+++ b/sound/soc/codecs/max98927.h
@@ -0,0 +1,272 @@
+/*
+ * max98927.h -- MAX98927 ALSA Soc Audio driver
+ *
+ * Copyright 2013-15 Maxim Integrated Products
+ * Author: Ryan Lee <ryans.lee@maximintegrated.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License as published by the
+ * Free Software Foundation; either version 2 of the License, or (at your
+ * option) any later version.
+ *
+ */
+#ifndef _MAX98927_H
+#define _MAX98927_H
+
+/* Register Values */
+#define MAX98927_R0001_INT_RAW1 0x0001
+#define MAX98927_R0002_INT_RAW2 0x0002
+#define MAX98927_R0003_INT_RAW3 0x0003
+#define MAX98927_R0004_INT_STATE1 0x0004
+#define MAX98927_R0005_INT_STATE2 0x0005
+#define MAX98927_R0006_INT_STATE3 0x0006
+#define MAX98927_R0007_INT_FLAG1 0x0007
+#define MAX98927_R0008_INT_FLAG2 0x0008
+#define MAX98927_R0009_INT_FLAG3 0x0009
+#define MAX98927_R000A_INT_EN1 0x000A
+#define MAX98927_R000B_INT_EN2 0x000B
+#define MAX98927_R000C_INT_EN3 0x000C
+#define MAX98927_R000D_INT_FLAG_CLR1 0x000D
+#define MAX98927_R000E_INT_FLAG_CLR2 0x000E
+#define MAX98927_R000F_INT_FLAG_CLR3 0x000F
+#define MAX98927_R0010_IRQ_CTRL 0x0010
+#define MAX98927_R0011_CLK_MON 0x0011
+#define MAX98927_R0012_WDOG_CTRL 0x0012
+#define MAX98927_R0013_WDOG_RST 0x0013
+#define MAX98927_R0014_MEAS_ADC_THERM_WARN_THRESH 0x0014
+#define MAX98927_R0015_MEAS_ADC_THERM_SHDN_THRESH 0x0015
+#define MAX98927_R0016_MEAS_ADC_THERM_HYSTERESIS 0x0016
+#define MAX98927_R0017_PIN_CFG 0x0017
+#define MAX98927_R0018_PCM_RX_EN_A 0x0018
+#define MAX98927_R0019_PCM_RX_EN_B 0x0019
+#define MAX98927_R001A_PCM_TX_EN_A 0x001A
+#define MAX98927_R001B_PCM_TX_EN_B 0x001B
+#define MAX98927_R001C_PCM_TX_HIZ_CTRL_A 0x001C
+#define MAX98927_R001D_PCM_TX_HIZ_CTRL_B 0x001D
+#define MAX98927_R001E_PCM_TX_CH_SRC_A 0x001E
+#define MAX98927_R001F_PCM_TX_CH_SRC_B 0x001F
+#define MAX98927_R0020_PCM_MODE_CFG 0x0020
+#define MAX98927_R0021_PCM_MASTER_MODE 0x0021
+#define MAX98927_R0022_PCM_CLK_SETUP 0x0022
+#define MAX98927_R0023_PCM_SR_SETUP1 0x0023
+#define MAX98927_R0024_PCM_SR_SETUP2 0x0024
+#define MAX98927_R0025_PCM_TO_SPK_MONOMIX_A 0x0025
+#define MAX98927_R0026_PCM_TO_SPK_MONOMIX_B 0x0026
+#define MAX98927_R0027_ICC_RX_EN_A 0x0027
+#define MAX98927_R0028_ICC_RX_EN_B 0x0028
+#define MAX98927_R002B_ICC_TX_EN_A 0x002B
+#define MAX98927_R002C_ICC_TX_EN_B 0x002C
+#define MAX98927_R002E_ICC_HIZ_MANUAL_MODE 0x002E
+#define MAX98927_R002F_ICC_TX_HIZ_EN_A 0x002F
+#define MAX98927_R0030_ICC_TX_HIZ_EN_B 0x0030
+#define MAX98927_R0031_ICC_LNK_EN 0x0031
+#define MAX98927_R0032_PDM_TX_EN 0x0032
+#define MAX98927_R0033_PDM_TX_HIZ_CTRL 0x0033
+#define MAX98927_R0034_PDM_TX_CTRL 0x0034
+#define MAX98927_R0035_PDM_RX_CTRL 0x0035
+#define MAX98927_R0036_AMP_VOL_CTRL 0x0036
+#define MAX98927_R0037_AMP_DSP_CFG 0x0037
+#define MAX98927_R0038_TONE_GEN_DC_CFG 0x0038
+#define MAX98927_R0039_DRE_CTRL 0x0039
+#define MAX98927_R003A_AMP_EN 0x003A
+#define MAX98927_R003B_SPK_SRC_SEL 0x003B
+#define MAX98927_R003C_SPK_GAIN 0x003C
+#define MAX98927_R003D_SSM_CFG 0x003D
+#define MAX98927_R003E_MEAS_EN 0x003E
+#define MAX98927_R003F_MEAS_DSP_CFG 0x003F
+#define MAX98927_R0040_BOOST_CTRL0 0x0040
+#define MAX98927_R0041_BOOST_CTRL3 0x0041
+#define MAX98927_R0042_BOOST_CTRL1 0x0042
+#define MAX98927_R0043_MEAS_ADC_CFG 0x0043
+#define MAX98927_R0044_MEAS_ADC_BASE_MSB 0x0044
+#define MAX98927_R0045_MEAS_ADC_BASE_LSB 0x0045
+#define MAX98927_R0046_ADC_CH0_DIVIDE 0x0046
+#define MAX98927_R0047_ADC_CH1_DIVIDE 0x0047
+#define MAX98927_R0048_ADC_CH2_DIVIDE 0x0048
+#define MAX98927_R0049_ADC_CH0_FILT_CFG 0x0049
+#define MAX98927_R004A_ADC_CH1_FILT_CFG 0x004A
+#define MAX98927_R004B_ADC_CH2_FILT_CFG 0x004B
+#define MAX98927_R004C_MEAS_ADC_CH0_READ 0x004C
+#define MAX98927_R004D_MEAS_ADC_CH1_READ 0x004D
+#define MAX98927_R004E_MEAS_ADC_CH2_READ 0x004E
+#define MAX98927_R0051_BROWNOUT_STATUS 0x0051
+#define MAX98927_R0052_BROWNOUT_EN 0x0052
+#define MAX98927_R0053_BROWNOUT_INFINITE_HOLD 0x0053
+#define MAX98927_R0054_BROWNOUT_INFINITE_HOLD_CLR 0x0054
+#define MAX98927_R0055_BROWNOUT_LVL_HOLD 0x0055
+#define MAX98927_R005A_BROWNOUT_LVL1_THRESH 0x005A
+#define MAX98927_R005B_BROWNOUT_LVL2_THRESH 0x005B
+#define MAX98927_R005C_BROWNOUT_LVL3_THRESH 0x005C
+#define MAX98927_R005D_BROWNOUT_LVL4_THRESH 0x005D
+#define MAX98927_R005E_BROWNOUT_THRESH_HYSTERYSIS 0x005E
+#define MAX98927_R005F_BROWNOUT_AMP_LIMITER_ATK_REL 0x005F
+#define MAX98927_R0060_BROWNOUT_AMP_GAIN_ATK_REL 0x0060
+#define MAX98927_R0061_BROWNOUT_AMP1_CLIP_MODE 0x0061
+#define MAX98927_R0072_BROWNOUT_LVL1_CUR_LIMIT 0x0072
+#define MAX98927_R0073_BROWNOUT_LVL1_AMP1_CTRL1 0x0073
+#define MAX98927_R0074_BROWNOUT_LVL1_AMP1_CTRL2 0x0074
+#define MAX98927_R0075_BROWNOUT_LVL1_AMP1_CTRL3 0x0075
+#define MAX98927_R0076_BROWNOUT_LVL2_CUR_LIMIT 0x0076
+#define MAX98927_R0077_BROWNOUT_LVL2_AMP1_CTRL1 0x0077
+#define MAX98927_R0078_BROWNOUT_LVL2_AMP1_CTRL2 0x0078
+#define MAX98927_R0079_BROWNOUT_LVL2_AMP1_CTRL3 0x0079
+#define MAX98927_R007A_BROWNOUT_LVL3_CUR_LIMIT 0x007A
+#define MAX98927_R007B_BROWNOUT_LVL3_AMP1_CTRL1 0x007B
+#define MAX98927_R007C_BROWNOUT_LVL3_AMP1_CTRL2 0x007C
+#define MAX98927_R007D_BROWNOUT_LVL3_AMP1_CTRL3 0x007D
+#define MAX98927_R007E_BROWNOUT_LVL4_CUR_LIMIT 0x007E
+#define MAX98927_R007F_BROWNOUT_LVL4_AMP1_CTRL1 0x007F
+#define MAX98927_R0080_BROWNOUT_LVL4_AMP1_CTRL2 0x0080
+#define MAX98927_R0081_BROWNOUT_LVL4_AMP1_CTRL3 0x0081
+#define MAX98927_R0082_ENV_TRACK_VOUT_HEADROOM 0x0082
+#define MAX98927_R0083_ENV_TRACK_BOOST_VOUT_DELAY 0x0083
+#define MAX98927_R0084_ENV_TRACK_REL_RATE 0x0084
+#define MAX98927_R0085_ENV_TRACK_HOLD_RATE 0x0085
+#define MAX98927_R0086_ENV_TRACK_CTRL 0x0086
+#define MAX98927_R0087_ENV_TRACK_BOOST_VOUT_READ 0x0087
+#define MAX98927_R00FF_GLOBAL_SHDN 0x00FF
+#define MAX98927_R0100_SOFT_RESET 0x0100
+#define MAX98927_R01FF_REV_ID 0x01FF
+
+/* MAX98927_R0018_PCM_RX_EN_A */
+#define MAX98927_PCM_RX_CH0_EN (0x1 << 0)
+#define MAX98927_PCM_RX_CH1_EN (0x1 << 1)
+#define MAX98927_PCM_RX_CH2_EN (0x1 << 2)
+#define MAX98927_PCM_RX_CH3_EN (0x1 << 3)
+#define MAX98927_PCM_RX_CH4_EN (0x1 << 4)
+#define MAX98927_PCM_RX_CH5_EN (0x1 << 5)
+#define MAX98927_PCM_RX_CH6_EN (0x1 << 6)
+#define MAX98927_PCM_RX_CH7_EN (0x1 << 7)
+
+/* MAX98927_R001A_PCM_TX_EN_A */
+#define MAX98927_PCM_TX_CH0_EN (0x1 << 0)
+#define MAX98927_PCM_TX_CH1_EN (0x1 << 1)
+#define MAX98927_PCM_TX_CH2_EN (0x1 << 2)
+#define MAX98927_PCM_TX_CH3_EN (0x1 << 3)
+#define MAX98927_PCM_TX_CH4_EN (0x1 << 4)
+#define MAX98927_PCM_TX_CH5_EN (0x1 << 5)
+#define MAX98927_PCM_TX_CH6_EN (0x1 << 6)
+#define MAX98927_PCM_TX_CH7_EN (0x1 << 7)
+
+/* MAX98927_R001E_PCM_TX_CH_SRC_A */
+#define MAX98927_PCM_TX_CH_SRC_A_V_SHIFT (0)
+#define MAX98927_PCM_TX_CH_SRC_A_I_SHIFT (4)
+
+/* MAX98927_R001F_PCM_TX_CH_SRC_B */
+#define MAX98927_PCM_TX_CH_INTERLEAVE_MASK (0x1 << 5)
+
+/* MAX98927_R0020_PCM_MODE_CFG */
+#define MAX98927_PCM_MODE_CFG_PCM_BCLKEDGE (0x1 << 2)
+#define MAX98927_PCM_MODE_CFG_FORMAT_MASK (0x7 << 3)
+#define MAX98927_PCM_MODE_CFG_FORMAT_SHIFT (3)
+#define MAX98927_PCM_FORMAT_I2S (0x0 << 0)
+#define MAX98927_PCM_FORMAT_LJ (0x1 << 0)
+
+#define MAX98927_PCM_MODE_CFG_CHANSZ_MASK (0x3 << 6)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_16 (0x1 << 6)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_24 (0x2 << 6)
+#define MAX98927_PCM_MODE_CFG_CHANSZ_32 (0x3 << 6)
+
+/* MAX98927_R0021_PCM_MASTER_MODE */
+#define MAX98927_PCM_MASTER_MODE_MASK (0x3 << 0)
+#define MAX98927_PCM_MASTER_MODE_SLAVE (0x0 << 0)
+#define MAX98927_PCM_MASTER_MODE_MASTER (0x3 << 0)
+
+#define MAX98927_PCM_MASTER_MODE_MCLK_MASK (0xF << 2)
+#define MAX98927_PCM_MASTER_MODE_MCLK_RATE_SHIFT (2)
+
+/* MAX98927_R0022_PCM_CLK_SETUP */
+#define MAX98927_PCM_CLK_SETUP_BSEL_MASK (0xF << 0)
+
+/* MAX98927_R0023_PCM_SR_SETUP1 */
+#define MAX98927_PCM_SR_SET1_SR_MASK (0xF << 0)
+
+#define MAX98927_PCM_SR_SET1_SR_8000 (0x0 << 0)
+#define MAX98927_PCM_SR_SET1_SR_11025 (0x1 << 0)
+#define MAX98927_PCM_SR_SET1_SR_12000 (0x2 << 0)
+#define MAX98927_PCM_SR_SET1_SR_16000 (0x3 << 0)
+#define MAX98927_PCM_SR_SET1_SR_22050 (0x4 << 0)
+#define MAX98927_PCM_SR_SET1_SR_24000 (0x5 << 0)
+#define MAX98927_PCM_SR_SET1_SR_32000 (0x6 << 0)
+#define MAX98927_PCM_SR_SET1_SR_44100 (0x7 << 0)
+#define MAX98927_PCM_SR_SET1_SR_48000 (0x8 << 0)
+
+/* MAX98927_R0024_PCM_SR_SETUP2 */
+#define MAX98927_PCM_SR_SET2_SR_MASK (0xF << 4)
+#define MAX98927_PCM_SR_SET2_SR_SHIFT (4)
+#define MAX98927_PCM_SR_SET2_IVADC_SR_MASK (0xf << 0)
+
+/* MAX98927_R0025_PCM_TO_SPK_MONOMIX_A */
+#define MAX98927_PCM_TO_SPK_MONOMIX_CFG_MASK (0x3 << 6)
+#define MAX98927_PCM_TO_SPK_MONOMIX_CFG_SHIFT (6)
+
+/* MAX98927_R0035_PDM_RX_CTRL */
+#define MAX98927_PDM_RX_EN_MASK (0x1 << 0)
+
+/* MAX98927_R0036_AMP_VOL_CTRL */
+#define MAX98927_AMP_VOL_SEL (0x1 << 7)
+#define MAX98927_AMP_VOL_SEL_WIDTH (1)
+#define MAX98927_AMP_VOL_SEL_SHIFT (7)
+#define MAX98927_AMP_VOL_MASK (0x7f << 0)
+#define MAX98927_AMP_VOL_WIDTH (7)
+#define MAX98927_AMP_VOL_SHIFT (0)
+
+/* MAX98927_R0037_AMP_DSP_CFG */
+#define MAX98927_AMP_DSP_CFG_DCBLK_EN (0x1 << 0)
+#define MAX98927_AMP_DSP_CFG_DITH_EN (0x1 << 1)
+#define MAX98927_AMP_DSP_CFG_RMP_BYPASS (0x1 << 4)
+#define MAX98927_AMP_DSP_CFG_DAC_INV (0x1 << 5)
+#define MAX98927_AMP_DSP_CFG_RMP_SHIFT (4)
+
+/* MAX98927_R0039_DRE_CTRL */
+#define MAX98927_DRE_CTRL_DRE_EN (0x1 << 0)
+#define MAX98927_DRE_EN_SHIFT 0x1
+
+/* MAX98927_R003A_AMP_EN */
+#define MAX98927_AMP_EN_MASK (0x1 << 0)
+
+/* MAX98927_R003B_SPK_SRC_SEL */
+#define MAX98927_SPK_SRC_MASK (0x3 << 0)
+
+/* MAX98927_R003C_SPK_GAIN */
+#define MAX98927_SPK_PCM_GAIN_MASK (0x7 << 0)
+#define MAX98927_SPK_PDM_GAIN_MASK (0x7 << 4)
+#define MAX98927_SPK_GAIN_WIDTH (3)
+
+/* MAX98927_R003E_MEAS_EN */
+#define MAX98927_MEAS_V_EN (0x1 << 0)
+#define MAX98927_MEAS_I_EN (0x1 << 1)
+
+/* MAX98927_R0040_BOOST_CTRL0 */
+#define MAX98927_BOOST_CTRL0_VOUT_MASK (0x1f << 0)
+#define MAX98927_BOOST_CTRL0_PVDD_MASK (0x1 << 7)
+#define MAX98927_BOOST_CTRL0_PVDD_EN_SHIFT (7)
+
+/* MAX98927_R0052_BROWNOUT_EN */
+#define MAX98927_BROWNOUT_BDE_EN (0x1 << 0)
+#define MAX98927_BROWNOUT_AMP_EN (0x1 << 1)
+#define MAX98927_BROWNOUT_DSP_EN (0x1 << 2)
+#define MAX98927_BROWNOUT_DSP_SHIFT (2)
+
+/* MAX98927_R0100_SOFT_RESET */
+#define MAX98927_SOFT_RESET (0x1 << 0)
+
+/* MAX98927_R00FF_GLOBAL_SHDN */
+#define MAX98927_GLOBAL_EN_MASK (0x1 << 0)
+
+struct max98927_priv {
+ struct regmap *regmap;
+ struct snd_soc_codec *codec;
+ struct max98927_pdata *pdata;
+ unsigned int spk_gain;
+ unsigned int sysclk;
+ unsigned int v_l_slot;
+ unsigned int i_l_slot;
+ bool interleave_mode;
+ unsigned int ch_size;
+ unsigned int rate;
+ unsigned int iface;
+ unsigned int master;
+ unsigned int digital_gain;
+};
+#endif
diff --git a/sound/soc/codecs/nau8540.c b/sound/soc/codecs/nau8540.c
index 9e8f0f4aa51a..c8bcb1db966d 100644
--- a/sound/soc/codecs/nau8540.c
+++ b/sound/soc/codecs/nau8540.c
@@ -39,147 +39,147 @@
/* scaling for mclk from sysclk_src output */
static const struct nau8540_fll_attr mclk_src_scaling[] = {
- { 1, 0x0 },
- { 2, 0x2 },
- { 4, 0x3 },
- { 8, 0x4 },
- { 16, 0x5 },
- { 32, 0x6 },
- { 3, 0x7 },
- { 6, 0xa },
- { 12, 0xb },
- { 24, 0xc },
+ { 1, 0x0 },
+ { 2, 0x2 },
+ { 4, 0x3 },
+ { 8, 0x4 },
+ { 16, 0x5 },
+ { 32, 0x6 },
+ { 3, 0x7 },
+ { 6, 0xa },
+ { 12, 0xb },
+ { 24, 0xc },
};
/* ratio for input clk freq */
static const struct nau8540_fll_attr fll_ratio[] = {
- { 512000, 0x01 },
- { 256000, 0x02 },
- { 128000, 0x04 },
- { 64000, 0x08 },
- { 32000, 0x10 },
- { 8000, 0x20 },
- { 4000, 0x40 },
+ { 512000, 0x01 },
+ { 256000, 0x02 },
+ { 128000, 0x04 },
+ { 64000, 0x08 },
+ { 32000, 0x10 },
+ { 8000, 0x20 },
+ { 4000, 0x40 },
};
static const struct nau8540_fll_attr fll_pre_scalar[] = {
- { 1, 0x0 },
- { 2, 0x1 },
- { 4, 0x2 },
- { 8, 0x3 },
+ { 1, 0x0 },
+ { 2, 0x1 },
+ { 4, 0x2 },
+ { 8, 0x3 },
};
/* over sampling rate */
static const struct nau8540_osr_attr osr_adc_sel[] = {
- { 32, 3 }, /* OSR 32, SRC 1/8 */
- { 64, 2 }, /* OSR 64, SRC 1/4 */
- { 128, 1 }, /* OSR 128, SRC 1/2 */
- { 256, 0 }, /* OSR 256, SRC 1 */
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
};
static const struct reg_default nau8540_reg_defaults[] = {
- {NAU8540_REG_POWER_MANAGEMENT, 0x0000},
- {NAU8540_REG_CLOCK_CTRL, 0x0000},
- {NAU8540_REG_CLOCK_SRC, 0x0000},
- {NAU8540_REG_FLL1, 0x0001},
- {NAU8540_REG_FLL2, 0x3126},
- {NAU8540_REG_FLL3, 0x0008},
- {NAU8540_REG_FLL4, 0x0010},
- {NAU8540_REG_FLL5, 0xC000},
- {NAU8540_REG_FLL6, 0x6000},
- {NAU8540_REG_FLL_VCO_RSV, 0xF13C},
- {NAU8540_REG_PCM_CTRL0, 0x000B},
- {NAU8540_REG_PCM_CTRL1, 0x3010},
- {NAU8540_REG_PCM_CTRL2, 0x0800},
- {NAU8540_REG_PCM_CTRL3, 0x0000},
- {NAU8540_REG_PCM_CTRL4, 0x000F},
- {NAU8540_REG_ALC_CONTROL_1, 0x0000},
- {NAU8540_REG_ALC_CONTROL_2, 0x700B},
- {NAU8540_REG_ALC_CONTROL_3, 0x0022},
- {NAU8540_REG_ALC_CONTROL_4, 0x1010},
- {NAU8540_REG_ALC_CONTROL_5, 0x1010},
- {NAU8540_REG_NOTCH_FIL1_CH1, 0x0000},
- {NAU8540_REG_NOTCH_FIL2_CH1, 0x0000},
- {NAU8540_REG_NOTCH_FIL1_CH2, 0x0000},
- {NAU8540_REG_NOTCH_FIL2_CH2, 0x0000},
- {NAU8540_REG_NOTCH_FIL1_CH3, 0x0000},
- {NAU8540_REG_NOTCH_FIL2_CH3, 0x0000},
- {NAU8540_REG_NOTCH_FIL1_CH4, 0x0000},
- {NAU8540_REG_NOTCH_FIL2_CH4, 0x0000},
- {NAU8540_REG_HPF_FILTER_CH12, 0x0000},
- {NAU8540_REG_HPF_FILTER_CH34, 0x0000},
- {NAU8540_REG_ADC_SAMPLE_RATE, 0x0002},
- {NAU8540_REG_DIGITAL_GAIN_CH1, 0x0400},
- {NAU8540_REG_DIGITAL_GAIN_CH2, 0x0400},
- {NAU8540_REG_DIGITAL_GAIN_CH3, 0x0400},
- {NAU8540_REG_DIGITAL_GAIN_CH4, 0x0400},
- {NAU8540_REG_DIGITAL_MUX, 0x00E4},
- {NAU8540_REG_GPIO_CTRL, 0x0000},
- {NAU8540_REG_MISC_CTRL, 0x0000},
- {NAU8540_REG_I2C_CTRL, 0xEFFF},
- {NAU8540_REG_VMID_CTRL, 0x0000},
- {NAU8540_REG_MUTE, 0x0000},
- {NAU8540_REG_ANALOG_ADC1, 0x0011},
- {NAU8540_REG_ANALOG_ADC2, 0x0020},
- {NAU8540_REG_ANALOG_PWR, 0x0000},
- {NAU8540_REG_MIC_BIAS, 0x0004},
- {NAU8540_REG_REFERENCE, 0x0000},
- {NAU8540_REG_FEPGA1, 0x0000},
- {NAU8540_REG_FEPGA2, 0x0000},
- {NAU8540_REG_FEPGA3, 0x0101},
- {NAU8540_REG_FEPGA4, 0x0101},
- {NAU8540_REG_PWR, 0x0000},
+ {NAU8540_REG_POWER_MANAGEMENT, 0x0000},
+ {NAU8540_REG_CLOCK_CTRL, 0x0000},
+ {NAU8540_REG_CLOCK_SRC, 0x0000},
+ {NAU8540_REG_FLL1, 0x0001},
+ {NAU8540_REG_FLL2, 0x3126},
+ {NAU8540_REG_FLL3, 0x0008},
+ {NAU8540_REG_FLL4, 0x0010},
+ {NAU8540_REG_FLL5, 0xC000},
+ {NAU8540_REG_FLL6, 0x6000},
+ {NAU8540_REG_FLL_VCO_RSV, 0xF13C},
+ {NAU8540_REG_PCM_CTRL0, 0x000B},
+ {NAU8540_REG_PCM_CTRL1, 0x3010},
+ {NAU8540_REG_PCM_CTRL2, 0x0800},
+ {NAU8540_REG_PCM_CTRL3, 0x0000},
+ {NAU8540_REG_PCM_CTRL4, 0x000F},
+ {NAU8540_REG_ALC_CONTROL_1, 0x0000},
+ {NAU8540_REG_ALC_CONTROL_2, 0x700B},
+ {NAU8540_REG_ALC_CONTROL_3, 0x0022},
+ {NAU8540_REG_ALC_CONTROL_4, 0x1010},
+ {NAU8540_REG_ALC_CONTROL_5, 0x1010},
+ {NAU8540_REG_NOTCH_FIL1_CH1, 0x0000},
+ {NAU8540_REG_NOTCH_FIL2_CH1, 0x0000},
+ {NAU8540_REG_NOTCH_FIL1_CH2, 0x0000},
+ {NAU8540_REG_NOTCH_FIL2_CH2, 0x0000},
+ {NAU8540_REG_NOTCH_FIL1_CH3, 0x0000},
+ {NAU8540_REG_NOTCH_FIL2_CH3, 0x0000},
+ {NAU8540_REG_NOTCH_FIL1_CH4, 0x0000},
+ {NAU8540_REG_NOTCH_FIL2_CH4, 0x0000},
+ {NAU8540_REG_HPF_FILTER_CH12, 0x0000},
+ {NAU8540_REG_HPF_FILTER_CH34, 0x0000},
+ {NAU8540_REG_ADC_SAMPLE_RATE, 0x0002},
+ {NAU8540_REG_DIGITAL_GAIN_CH1, 0x0400},
+ {NAU8540_REG_DIGITAL_GAIN_CH2, 0x0400},
+ {NAU8540_REG_DIGITAL_GAIN_CH3, 0x0400},
+ {NAU8540_REG_DIGITAL_GAIN_CH4, 0x0400},
+ {NAU8540_REG_DIGITAL_MUX, 0x00E4},
+ {NAU8540_REG_GPIO_CTRL, 0x0000},
+ {NAU8540_REG_MISC_CTRL, 0x0000},
+ {NAU8540_REG_I2C_CTRL, 0xEFFF},
+ {NAU8540_REG_VMID_CTRL, 0x0000},
+ {NAU8540_REG_MUTE, 0x0000},
+ {NAU8540_REG_ANALOG_ADC1, 0x0011},
+ {NAU8540_REG_ANALOG_ADC2, 0x0020},
+ {NAU8540_REG_ANALOG_PWR, 0x0000},
+ {NAU8540_REG_MIC_BIAS, 0x0004},
+ {NAU8540_REG_REFERENCE, 0x0000},
+ {NAU8540_REG_FEPGA1, 0x0000},
+ {NAU8540_REG_FEPGA2, 0x0000},
+ {NAU8540_REG_FEPGA3, 0x0101},
+ {NAU8540_REG_FEPGA4, 0x0101},
+ {NAU8540_REG_PWR, 0x0000},
};
static bool nau8540_readable_reg(struct device *dev, unsigned int reg)
{
- switch (reg) {
- case NAU8540_REG_POWER_MANAGEMENT ... NAU8540_REG_FLL_VCO_RSV:
- case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
- case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
- case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ADC_SAMPLE_RATE:
- case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
- case NAU8540_REG_P2P_CH1 ... NAU8540_REG_I2C_CTRL:
- case NAU8540_REG_I2C_DEVICE_ID:
- case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
- case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
- return true;
- default:
- return false;
- }
+ switch (reg) {
+ case NAU8540_REG_POWER_MANAGEMENT ... NAU8540_REG_FLL_VCO_RSV:
+ case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
+ case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
+ case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ADC_SAMPLE_RATE:
+ case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
+ case NAU8540_REG_P2P_CH1 ... NAU8540_REG_I2C_CTRL:
+ case NAU8540_REG_I2C_DEVICE_ID:
+ case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
+ case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
+ return true;
+ default:
+ return false;
+ }
}
static bool nau8540_writeable_reg(struct device *dev, unsigned int reg)
{
- switch (reg) {
- case NAU8540_REG_SW_RESET ... NAU8540_REG_FLL_VCO_RSV:
- case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
- case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
- case NAU8540_REG_NOTCH_FIL1_CH1 ... NAU8540_REG_ADC_SAMPLE_RATE:
- case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
- case NAU8540_REG_GPIO_CTRL ... NAU8540_REG_I2C_CTRL:
- case NAU8540_REG_RST:
- case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
- case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
- return true;
- default:
- return false;
- }
+ switch (reg) {
+ case NAU8540_REG_SW_RESET ... NAU8540_REG_FLL_VCO_RSV:
+ case NAU8540_REG_PCM_CTRL0 ... NAU8540_REG_PCM_CTRL4:
+ case NAU8540_REG_ALC_CONTROL_1 ... NAU8540_REG_ALC_CONTROL_5:
+ case NAU8540_REG_NOTCH_FIL1_CH1 ... NAU8540_REG_ADC_SAMPLE_RATE:
+ case NAU8540_REG_DIGITAL_GAIN_CH1 ... NAU8540_REG_DIGITAL_MUX:
+ case NAU8540_REG_GPIO_CTRL ... NAU8540_REG_I2C_CTRL:
+ case NAU8540_REG_RST:
+ case NAU8540_REG_VMID_CTRL ... NAU8540_REG_MUTE:
+ case NAU8540_REG_ANALOG_ADC1 ... NAU8540_REG_PWR:
+ return true;
+ default:
+ return false;
+ }
}
static bool nau8540_volatile_reg(struct device *dev, unsigned int reg)
{
- switch (reg) {
- case NAU8540_REG_SW_RESET:
- case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ALC_STATUS:
- case NAU8540_REG_P2P_CH1 ... NAU8540_REG_PEAK_CH4:
- case NAU8540_REG_I2C_DEVICE_ID:
- case NAU8540_REG_RST:
- return true;
- default:
- return false;
- }
+ switch (reg) {
+ case NAU8540_REG_SW_RESET:
+ case NAU8540_REG_ALC_GAIN_CH12 ... NAU8540_REG_ALC_STATUS:
+ case NAU8540_REG_P2P_CH1 ... NAU8540_REG_PEAK_CH4:
+ case NAU8540_REG_I2C_DEVICE_ID:
+ case NAU8540_REG_RST:
+ return true;
+ default:
+ return false;
+ }
}
@@ -187,255 +187,255 @@ static const DECLARE_TLV_DB_MINMAX(adc_vol_tlv, -12800, 3600);
static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
static const struct snd_kcontrol_new nau8540_snd_controls[] = {
- SOC_SINGLE_TLV("Mic1 Volume", NAU8540_REG_DIGITAL_GAIN_CH1,
- 0, 0x520, 0, adc_vol_tlv),
- SOC_SINGLE_TLV("Mic2 Volume", NAU8540_REG_DIGITAL_GAIN_CH2,
- 0, 0x520, 0, adc_vol_tlv),
- SOC_SINGLE_TLV("Mic3 Volume", NAU8540_REG_DIGITAL_GAIN_CH3,
- 0, 0x520, 0, adc_vol_tlv),
- SOC_SINGLE_TLV("Mic4 Volume", NAU8540_REG_DIGITAL_GAIN_CH4,
- 0, 0x520, 0, adc_vol_tlv),
-
- SOC_SINGLE_TLV("Frontend PGA1 Volume", NAU8540_REG_FEPGA3,
- 0, 0x25, 0, fepga_gain_tlv),
- SOC_SINGLE_TLV("Frontend PGA2 Volume", NAU8540_REG_FEPGA3,
- 8, 0x25, 0, fepga_gain_tlv),
- SOC_SINGLE_TLV("Frontend PGA3 Volume", NAU8540_REG_FEPGA4,
- 0, 0x25, 0, fepga_gain_tlv),
- SOC_SINGLE_TLV("Frontend PGA4 Volume", NAU8540_REG_FEPGA4,
- 8, 0x25, 0, fepga_gain_tlv),
+ SOC_SINGLE_TLV("Mic1 Volume", NAU8540_REG_DIGITAL_GAIN_CH1,
+ 0, 0x520, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("Mic2 Volume", NAU8540_REG_DIGITAL_GAIN_CH2,
+ 0, 0x520, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("Mic3 Volume", NAU8540_REG_DIGITAL_GAIN_CH3,
+ 0, 0x520, 0, adc_vol_tlv),
+ SOC_SINGLE_TLV("Mic4 Volume", NAU8540_REG_DIGITAL_GAIN_CH4,
+ 0, 0x520, 0, adc_vol_tlv),
+
+ SOC_SINGLE_TLV("Frontend PGA1 Volume", NAU8540_REG_FEPGA3,
+ 0, 0x25, 0, fepga_gain_tlv),
+ SOC_SINGLE_TLV("Frontend PGA2 Volume", NAU8540_REG_FEPGA3,
+ 8, 0x25, 0, fepga_gain_tlv),
+ SOC_SINGLE_TLV("Frontend PGA3 Volume", NAU8540_REG_FEPGA4,
+ 0, 0x25, 0, fepga_gain_tlv),
+ SOC_SINGLE_TLV("Frontend PGA4 Volume", NAU8540_REG_FEPGA4,
+ 8, 0x25, 0, fepga_gain_tlv),
};
static const char * const adc_channel[] = {
- "ADC channel 1", "ADC channel 2", "ADC channel 3", "ADC channel 4"
+ "ADC channel 1", "ADC channel 2", "ADC channel 3", "ADC channel 4"
};
static SOC_ENUM_SINGLE_DECL(
- digital_ch4_enum, NAU8540_REG_DIGITAL_MUX, 6, adc_channel);
+ digital_ch4_enum, NAU8540_REG_DIGITAL_MUX, 6, adc_channel);
static const struct snd_kcontrol_new digital_ch4_mux =
- SOC_DAPM_ENUM("Digital CH4 Select", digital_ch4_enum);
+ SOC_DAPM_ENUM("Digital CH4 Select", digital_ch4_enum);
static SOC_ENUM_SINGLE_DECL(
- digital_ch3_enum, NAU8540_REG_DIGITAL_MUX, 4, adc_channel);
+ digital_ch3_enum, NAU8540_REG_DIGITAL_MUX, 4, adc_channel);
static const struct snd_kcontrol_new digital_ch3_mux =
- SOC_DAPM_ENUM("Digital CH3 Select", digital_ch3_enum);
+ SOC_DAPM_ENUM("Digital CH3 Select", digital_ch3_enum);
static SOC_ENUM_SINGLE_DECL(
- digital_ch2_enum, NAU8540_REG_DIGITAL_MUX, 2, adc_channel);
+ digital_ch2_enum, NAU8540_REG_DIGITAL_MUX, 2, adc_channel);
static const struct snd_kcontrol_new digital_ch2_mux =
- SOC_DAPM_ENUM("Digital CH2 Select", digital_ch2_enum);
+ SOC_DAPM_ENUM("Digital CH2 Select", digital_ch2_enum);
static SOC_ENUM_SINGLE_DECL(
- digital_ch1_enum, NAU8540_REG_DIGITAL_MUX, 0, adc_channel);
+ digital_ch1_enum, NAU8540_REG_DIGITAL_MUX, 0, adc_channel);
static const struct snd_kcontrol_new digital_ch1_mux =
- SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum);
+ SOC_DAPM_ENUM("Digital CH1 Select", digital_ch1_enum);
static const struct snd_soc_dapm_widget nau8540_dapm_widgets[] = {
- SND_SOC_DAPM_SUPPLY("MICBIAS2", NAU8540_REG_MIC_BIAS, 11, 0, NULL, 0),
- SND_SOC_DAPM_SUPPLY("MICBIAS1", NAU8540_REG_MIC_BIAS, 10, 0, NULL, 0),
-
- SND_SOC_DAPM_INPUT("MIC1"),
- SND_SOC_DAPM_INPUT("MIC2"),
- SND_SOC_DAPM_INPUT("MIC3"),
- SND_SOC_DAPM_INPUT("MIC4"),
-
- SND_SOC_DAPM_PGA("Frontend PGA1", NAU8540_REG_PWR, 12, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Frontend PGA2", NAU8540_REG_PWR, 13, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Frontend PGA3", NAU8540_REG_PWR, 14, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Frontend PGA4", NAU8540_REG_PWR, 15, 0, NULL, 0),
-
- SND_SOC_DAPM_ADC("ADC1", NULL,
- NAU8540_REG_POWER_MANAGEMENT, 0, 0),
- SND_SOC_DAPM_ADC("ADC2", NULL,
- NAU8540_REG_POWER_MANAGEMENT, 1, 0),
- SND_SOC_DAPM_ADC("ADC3", NULL,
- NAU8540_REG_POWER_MANAGEMENT, 2, 0),
- SND_SOC_DAPM_ADC("ADC4", NULL,
- NAU8540_REG_POWER_MANAGEMENT, 3, 0),
-
- SND_SOC_DAPM_PGA("ADC CH1", NAU8540_REG_ANALOG_PWR, 0, 0, NULL, 0),
- SND_SOC_DAPM_PGA("ADC CH2", NAU8540_REG_ANALOG_PWR, 1, 0, NULL, 0),
- SND_SOC_DAPM_PGA("ADC CH3", NAU8540_REG_ANALOG_PWR, 2, 0, NULL, 0),
- SND_SOC_DAPM_PGA("ADC CH4", NAU8540_REG_ANALOG_PWR, 3, 0, NULL, 0),
-
- SND_SOC_DAPM_MUX("Digital CH4 Mux",
- SND_SOC_NOPM, 0, 0, &digital_ch4_mux),
- SND_SOC_DAPM_MUX("Digital CH3 Mux",
- SND_SOC_NOPM, 0, 0, &digital_ch3_mux),
- SND_SOC_DAPM_MUX("Digital CH2 Mux",
- SND_SOC_NOPM, 0, 0, &digital_ch2_mux),
- SND_SOC_DAPM_MUX("Digital CH1 Mux",
- SND_SOC_NOPM, 0, 0, &digital_ch1_mux),
-
- SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS2", NAU8540_REG_MIC_BIAS, 11, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS1", NAU8540_REG_MIC_BIAS, 10, 0, NULL, 0),
+
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("MIC3"),
+ SND_SOC_DAPM_INPUT("MIC4"),
+
+ SND_SOC_DAPM_PGA("Frontend PGA1", NAU8540_REG_PWR, 12, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Frontend PGA2", NAU8540_REG_PWR, 13, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Frontend PGA3", NAU8540_REG_PWR, 14, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Frontend PGA4", NAU8540_REG_PWR, 15, 0, NULL, 0),
+
+ SND_SOC_DAPM_ADC("ADC1", NULL,
+ NAU8540_REG_POWER_MANAGEMENT, 0, 0),
+ SND_SOC_DAPM_ADC("ADC2", NULL,
+ NAU8540_REG_POWER_MANAGEMENT, 1, 0),
+ SND_SOC_DAPM_ADC("ADC3", NULL,
+ NAU8540_REG_POWER_MANAGEMENT, 2, 0),
+ SND_SOC_DAPM_ADC("ADC4", NULL,
+ NAU8540_REG_POWER_MANAGEMENT, 3, 0),
+
+ SND_SOC_DAPM_PGA("ADC CH1", NAU8540_REG_ANALOG_PWR, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC CH2", NAU8540_REG_ANALOG_PWR, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC CH3", NAU8540_REG_ANALOG_PWR, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("ADC CH4", NAU8540_REG_ANALOG_PWR, 3, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("Digital CH4 Mux",
+ SND_SOC_NOPM, 0, 0, &digital_ch4_mux),
+ SND_SOC_DAPM_MUX("Digital CH3 Mux",
+ SND_SOC_NOPM, 0, 0, &digital_ch3_mux),
+ SND_SOC_DAPM_MUX("Digital CH2 Mux",
+ SND_SOC_NOPM, 0, 0, &digital_ch2_mux),
+ SND_SOC_DAPM_MUX("Digital CH1 Mux",
+ SND_SOC_NOPM, 0, 0, &digital_ch1_mux),
+
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, SND_SOC_NOPM, 0, 0),
};
static const struct snd_soc_dapm_route nau8540_dapm_routes[] = {
- {"Frontend PGA1", NULL, "MIC1"},
- {"Frontend PGA2", NULL, "MIC2"},
- {"Frontend PGA3", NULL, "MIC3"},
- {"Frontend PGA4", NULL, "MIC4"},
-
- {"ADC1", NULL, "Frontend PGA1"},
- {"ADC2", NULL, "Frontend PGA2"},
- {"ADC3", NULL, "Frontend PGA3"},
- {"ADC4", NULL, "Frontend PGA4"},
-
- {"ADC CH1", NULL, "ADC1"},
- {"ADC CH2", NULL, "ADC2"},
- {"ADC CH3", NULL, "ADC3"},
- {"ADC CH4", NULL, "ADC4"},
-
- {"ADC1", NULL, "MICBIAS1"},
- {"ADC2", NULL, "MICBIAS1"},
- {"ADC3", NULL, "MICBIAS2"},
- {"ADC4", NULL, "MICBIAS2"},
-
- {"Digital CH1 Mux", "ADC channel 1", "ADC CH1"},
- {"Digital CH1 Mux", "ADC channel 2", "ADC CH2"},
- {"Digital CH1 Mux", "ADC channel 3", "ADC CH3"},
- {"Digital CH1 Mux", "ADC channel 4", "ADC CH4"},
-
- {"Digital CH2 Mux", "ADC channel 1", "ADC CH1"},
- {"Digital CH2 Mux", "ADC channel 2", "ADC CH2"},
- {"Digital CH2 Mux", "ADC channel 3", "ADC CH3"},
- {"Digital CH2 Mux", "ADC channel 4", "ADC CH4"},
-
- {"Digital CH3 Mux", "ADC channel 1", "ADC CH1"},
- {"Digital CH3 Mux", "ADC channel 2", "ADC CH2"},
- {"Digital CH3 Mux", "ADC channel 3", "ADC CH3"},
- {"Digital CH3 Mux", "ADC channel 4", "ADC CH4"},
-
- {"Digital CH4 Mux", "ADC channel 1", "ADC CH1"},
- {"Digital CH4 Mux", "ADC channel 2", "ADC CH2"},
- {"Digital CH4 Mux", "ADC channel 3", "ADC CH3"},
- {"Digital CH4 Mux", "ADC channel 4", "ADC CH4"},
-
- {"AIFTX", NULL, "Digital CH1 Mux"},
- {"AIFTX", NULL, "Digital CH2 Mux"},
- {"AIFTX", NULL, "Digital CH3 Mux"},
- {"AIFTX", NULL, "Digital CH4 Mux"},
+ {"Frontend PGA1", NULL, "MIC1"},
+ {"Frontend PGA2", NULL, "MIC2"},
+ {"Frontend PGA3", NULL, "MIC3"},
+ {"Frontend PGA4", NULL, "MIC4"},
+
+ {"ADC1", NULL, "Frontend PGA1"},
+ {"ADC2", NULL, "Frontend PGA2"},
+ {"ADC3", NULL, "Frontend PGA3"},
+ {"ADC4", NULL, "Frontend PGA4"},
+
+ {"ADC CH1", NULL, "ADC1"},
+ {"ADC CH2", NULL, "ADC2"},
+ {"ADC CH3", NULL, "ADC3"},
+ {"ADC CH4", NULL, "ADC4"},
+
+ {"ADC1", NULL, "MICBIAS1"},
+ {"ADC2", NULL, "MICBIAS1"},
+ {"ADC3", NULL, "MICBIAS2"},
+ {"ADC4", NULL, "MICBIAS2"},
+
+ {"Digital CH1 Mux", "ADC channel 1", "ADC CH1"},
+ {"Digital CH1 Mux", "ADC channel 2", "ADC CH2"},
+ {"Digital CH1 Mux", "ADC channel 3", "ADC CH3"},
+ {"Digital CH1 Mux", "ADC channel 4", "ADC CH4"},
+
+ {"Digital CH2 Mux", "ADC channel 1", "ADC CH1"},
+ {"Digital CH2 Mux", "ADC channel 2", "ADC CH2"},
+ {"Digital CH2 Mux", "ADC channel 3", "ADC CH3"},
+ {"Digital CH2 Mux", "ADC channel 4", "ADC CH4"},
+
+ {"Digital CH3 Mux", "ADC channel 1", "ADC CH1"},
+ {"Digital CH3 Mux", "ADC channel 2", "ADC CH2"},
+ {"Digital CH3 Mux", "ADC channel 3", "ADC CH3"},
+ {"Digital CH3 Mux", "ADC channel 4", "ADC CH4"},
+
+ {"Digital CH4 Mux", "ADC channel 1", "ADC CH1"},
+ {"Digital CH4 Mux", "ADC channel 2", "ADC CH2"},
+ {"Digital CH4 Mux", "ADC channel 3", "ADC CH3"},
+ {"Digital CH4 Mux", "ADC channel 4", "ADC CH4"},
+
+ {"AIFTX", NULL, "Digital CH1 Mux"},
+ {"AIFTX", NULL, "Digital CH2 Mux"},
+ {"AIFTX", NULL, "Digital CH3 Mux"},
+ {"AIFTX", NULL, "Digital CH4 Mux"},
};
static int nau8540_clock_check(struct nau8540 *nau8540, int rate, int osr)
{
- int osrate;
+ int osrate;
- if (osr >= ARRAY_SIZE(osr_adc_sel))
- return -EINVAL;
- osrate = osr_adc_sel[osr].osr;
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
+ return -EINVAL;
+ osrate = osr_adc_sel[osr].osr;
- if (rate * osr > CLK_ADC_MAX) {
- dev_err(nau8540->dev, "exceed the maximum frequency of CLK_ADC\n");
- return -EINVAL;
- }
+ if (rate * osr > CLK_ADC_MAX) {
+ dev_err(nau8540->dev, "exceed the maximum frequency of CLK_ADC\n");
+ return -EINVAL;
+ }
- return 0;
+ return 0;
}
static int nau8540_hw_params(struct snd_pcm_substream *substream,
- struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+ struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
- struct snd_soc_codec *codec = dai->codec;
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
- unsigned int val_len = 0, osr;
-
- /* CLK_ADC = OSR * FS
- * ADC clock frequency is defined as Over Sampling Rate (OSR)
- * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
- * values must be selected such that the maximum frequency is less
- * than 6.144 MHz.
- */
- regmap_read(nau8540->regmap, NAU8540_REG_ADC_SAMPLE_RATE, &osr);
- osr &= NAU8540_ADC_OSR_MASK;
- if (nau8540_clock_check(nau8540, params_rate(params), osr))
- return -EINVAL;
- regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
- NAU8540_CLK_ADC_SRC_MASK,
- osr_adc_sel[osr].clk_src << NAU8540_CLK_ADC_SRC_SFT);
-
- switch (params_width(params)) {
- case 16:
- val_len |= NAU8540_I2S_DL_16;
- break;
- case 20:
- val_len |= NAU8540_I2S_DL_20;
- break;
- case 24:
- val_len |= NAU8540_I2S_DL_24;
- break;
- case 32:
- val_len |= NAU8540_I2S_DL_32;
- break;
- default:
- return -EINVAL;
- }
-
- regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
- NAU8540_I2S_DL_MASK, val_len);
-
- return 0;
+ struct snd_soc_codec *codec = dai->codec;
+ struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val_len = 0, osr;
+
+ /* CLK_ADC = OSR * FS
+ * ADC clock frequency is defined as Over Sampling Rate (OSR)
+ * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+ * values must be selected such that the maximum frequency is less
+ * than 6.144 MHz.
+ */
+ regmap_read(nau8540->regmap, NAU8540_REG_ADC_SAMPLE_RATE, &osr);
+ osr &= NAU8540_ADC_OSR_MASK;
+ if (nau8540_clock_check(nau8540, params_rate(params), osr))
+ return -EINVAL;
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_ADC_SRC_MASK,
+ osr_adc_sel[osr].clk_src << NAU8540_CLK_ADC_SRC_SFT);
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8540_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8540_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8540_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8540_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
+ NAU8540_I2S_DL_MASK, val_len);
+
+ return 0;
}
static int nau8540_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
{
- struct snd_soc_codec *codec = dai->codec;
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
- unsigned int ctrl1_val = 0, ctrl2_val = 0;
-
- switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
- case SND_SOC_DAIFMT_CBM_CFM:
- ctrl2_val |= NAU8540_I2S_MS_MASTER;
- break;
- case SND_SOC_DAIFMT_CBS_CFS:
- break;
- default:
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
- case SND_SOC_DAIFMT_NB_NF:
- break;
- case SND_SOC_DAIFMT_IB_NF:
- ctrl1_val |= NAU8540_I2S_BP_INV;
- break;
- default:
- return -EINVAL;
- }
-
- switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
- case SND_SOC_DAIFMT_I2S:
- ctrl1_val |= NAU8540_I2S_DF_I2S;
- break;
- case SND_SOC_DAIFMT_LEFT_J:
- ctrl1_val |= NAU8540_I2S_DF_LEFT;
- break;
- case SND_SOC_DAIFMT_RIGHT_J:
- ctrl1_val |= NAU8540_I2S_DF_RIGTH;
- break;
- case SND_SOC_DAIFMT_DSP_A:
- ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
- break;
- case SND_SOC_DAIFMT_DSP_B:
- ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
- ctrl1_val |= NAU8540_I2S_PCMB_EN;
- break;
- default:
- return -EINVAL;
- }
-
- regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
- NAU8540_I2S_DL_MASK | NAU8540_I2S_DF_MASK |
- NAU8540_I2S_BP_INV | NAU8540_I2S_PCMB_EN, ctrl1_val);
- regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
- NAU8540_I2S_MS_MASK | NAU8540_I2S_DO12_OE, ctrl2_val);
- regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
- NAU8540_I2S_DO34_OE, 0);
-
- return 0;
+ struct snd_soc_codec *codec = dai->codec;
+ struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+ unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl2_val |= NAU8540_I2S_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8540_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8540_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8540_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8540_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8540_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8540_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL0,
+ NAU8540_I2S_DL_MASK | NAU8540_I2S_DF_MASK |
+ NAU8540_I2S_BP_INV | NAU8540_I2S_PCMB_EN, ctrl1_val);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+ NAU8540_I2S_MS_MASK | NAU8540_I2S_DO12_OE, ctrl2_val);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+ NAU8540_I2S_DO34_OE, 0);
+
+ return 0;
}
/**
@@ -451,55 +451,55 @@ static int nau8540_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
* Configures a DAI for TDM operation. Only support 4 slots TDM.
*/
static int nau8540_set_tdm_slot(struct snd_soc_dai *dai,
- unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
+ unsigned int tx_mask, unsigned int rx_mask, int slots, int slot_width)
{
- struct snd_soc_codec *codec = dai->codec;
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
- unsigned int ctrl2_val = 0, ctrl4_val = 0;
-
- if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf)))
- return -EINVAL;
-
- ctrl4_val |= (NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN);
- if (tx_mask & 0xf0) {
- ctrl2_val = 4 * slot_width;
- ctrl4_val |= (tx_mask >> 4);
- } else {
- ctrl4_val |= tx_mask;
- }
- regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL4,
- NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN |
- NAU8540_TDM_TX_MASK, ctrl4_val);
- regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
- NAU8540_I2S_DO12_OE, NAU8540_I2S_DO12_OE);
- regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
- NAU8540_I2S_DO34_OE | NAU8540_I2S_TSLOT_L_MASK,
- NAU8540_I2S_DO34_OE | ctrl2_val);
-
- return 0;
+ struct snd_soc_codec *codec = dai->codec;
+ struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+ unsigned int ctrl2_val = 0, ctrl4_val = 0;
+
+ if (slots > 4 || ((tx_mask & 0xf0) && (tx_mask & 0xf)))
+ return -EINVAL;
+
+ ctrl4_val |= (NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN);
+ if (tx_mask & 0xf0) {
+ ctrl2_val = 4 * slot_width;
+ ctrl4_val |= (tx_mask >> 4);
+ } else {
+ ctrl4_val |= tx_mask;
+ }
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL4,
+ NAU8540_TDM_MODE | NAU8540_TDM_OFFSET_EN |
+ NAU8540_TDM_TX_MASK, ctrl4_val);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL1,
+ NAU8540_I2S_DO12_OE, NAU8540_I2S_DO12_OE);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_PCM_CTRL2,
+ NAU8540_I2S_DO34_OE | NAU8540_I2S_TSLOT_L_MASK,
+ NAU8540_I2S_DO34_OE | ctrl2_val);
+
+ return 0;
}
static const struct snd_soc_dai_ops nau8540_dai_ops = {
- .hw_params = nau8540_hw_params,
- .set_fmt = nau8540_set_fmt,
- .set_tdm_slot = nau8540_set_tdm_slot,
+ .hw_params = nau8540_hw_params,
+ .set_fmt = nau8540_set_fmt,
+ .set_tdm_slot = nau8540_set_tdm_slot,
};
#define NAU8540_RATES SNDRV_PCM_RATE_8000_48000
#define NAU8540_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
- | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
static struct snd_soc_dai_driver nau8540_dai = {
- .name = "nau8540-hifi",
- .capture = {
- .stream_name = "Capture",
- .channels_min = 1,
- .channels_max = 4,
- .rates = NAU8540_RATES,
- .formats = NAU8540_FORMATS,
- },
- .ops = &nau8540_dai_ops,
+ .name = "nau8540-hifi",
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = NAU8540_RATES,
+ .formats = NAU8540_FORMATS,
+ },
+ .ops = &nau8540_dai_ops,
};
/**
@@ -513,320 +513,320 @@ static struct snd_soc_dai_driver nau8540_dai = {
* Returns 0 for success or negative error code.
*/
static int nau8540_calc_fll_param(unsigned int fll_in,
- unsigned int fs, struct nau8540_fll *fll_param)
+ unsigned int fs, struct nau8540_fll *fll_param)
{
- u64 fvco, fvco_max;
- unsigned int fref, i, fvco_sel;
-
- /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
- * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
- * FREF = freq_in / NAU8540_FLL_REF_DIV_MASK
- */
- for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
- fref = fll_in / fll_pre_scalar[i].param;
- if (fref <= NAU_FREF_MAX)
- break;
- }
- if (i == ARRAY_SIZE(fll_pre_scalar))
- return -EINVAL;
- fll_param->clk_ref_div = fll_pre_scalar[i].val;
-
- /* Choose the FLL ratio based on FREF */
- for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
- if (fref >= fll_ratio[i].param)
- break;
- }
- if (i == ARRAY_SIZE(fll_ratio))
- return -EINVAL;
- fll_param->ratio = fll_ratio[i].val;
-
- /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
- * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
- * guaranteed across the full range of operation.
- * FDCO = freq_out * 2 * mclk_src_scaling
- */
- fvco_max = 0;
- fvco_sel = ARRAY_SIZE(mclk_src_scaling);
- for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
- fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
- if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
- fvco_max < fvco) {
- fvco_max = fvco;
- fvco_sel = i;
- }
- }
- if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
- return -EINVAL;
- fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
-
- /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
- * input based on FDCO, FREF and FLL ratio.
- */
- fvco = div_u64(fvco_max << 16, fref * fll_param->ratio);
- fll_param->fll_int = (fvco >> 16) & 0x3FF;
- fll_param->fll_frac = fvco & 0xFFFF;
- return 0;
+ u64 fvco, fvco_max;
+ unsigned int fref, i, fvco_sel;
+
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+ * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+ * FREF = freq_in / NAU8540_FLL_REF_DIV_MASK
+ */
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+ fref = fll_in / fll_pre_scalar[i].param;
+ if (fref <= NAU_FREF_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_pre_scalar))
+ return -EINVAL;
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+ /* Choose the FLL ratio based on FREF */
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+ if (fref >= fll_ratio[i].param)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_ratio))
+ return -EINVAL;
+ fll_param->ratio = fll_ratio[i].val;
+
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+ * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
+ * guaranteed across the full range of operation.
+ * FDCO = freq_out * 2 * mclk_src_scaling
+ */
+ fvco_max = 0;
+ fvco_sel = ARRAY_SIZE(mclk_src_scaling);
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+ fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
+ if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+ fvco_max < fvco) {
+ fvco_max = fvco;
+ fvco_sel = i;
+ }
+ }
+ if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
+ return -EINVAL;
+ fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
+
+ /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+ * input based on FDCO, FREF and FLL ratio.
+ */
+ fvco = div_u64(fvco_max << 16, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> 16) & 0x3FF;
+ fll_param->fll_frac = fvco & 0xFFFF;
+ return 0;
}
static void nau8540_fll_apply(struct regmap *regmap,
- struct nau8540_fll *fll_param)
+ struct nau8540_fll *fll_param)
{
- regmap_update_bits(regmap, NAU8540_REG_CLOCK_SRC,
- NAU8540_CLK_SRC_MASK | NAU8540_CLK_MCLK_SRC_MASK,
- NAU8540_CLK_SRC_MCLK | fll_param->mclk_src);
- regmap_update_bits(regmap, NAU8540_REG_FLL1,
- NAU8540_FLL_RATIO_MASK, fll_param->ratio);
- /* FLL 16-bit fractional input */
- regmap_write(regmap, NAU8540_REG_FLL2, fll_param->fll_frac);
- /* FLL 10-bit integer input */
- regmap_update_bits(regmap, NAU8540_REG_FLL3,
- NAU8540_FLL_INTEGER_MASK, fll_param->fll_int);
- /* FLL pre-scaler */
- regmap_update_bits(regmap, NAU8540_REG_FLL4,
- NAU8540_FLL_REF_DIV_MASK,
- fll_param->clk_ref_div << NAU8540_FLL_REF_DIV_SFT);
- regmap_update_bits(regmap, NAU8540_REG_FLL5,
- NAU8540_FLL_CLK_SW_MASK, NAU8540_FLL_CLK_SW_REF);
- regmap_update_bits(regmap,
- NAU8540_REG_FLL6, NAU8540_DCO_EN, 0);
- if (fll_param->fll_frac) {
- regmap_update_bits(regmap, NAU8540_REG_FLL5,
- NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
- NAU8540_FLL_FTR_SW_MASK,
- NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
- NAU8540_FLL_FTR_SW_FILTER);
- regmap_update_bits(regmap, NAU8540_REG_FLL6,
- NAU8540_SDM_EN, NAU8540_SDM_EN);
- } else {
- regmap_update_bits(regmap, NAU8540_REG_FLL5,
- NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
- NAU8540_FLL_FTR_SW_MASK, NAU8540_FLL_FTR_SW_ACCU);
- regmap_update_bits(regmap,
- NAU8540_REG_FLL6, NAU8540_SDM_EN, 0);
- }
+ regmap_update_bits(regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_SRC_MASK | NAU8540_CLK_MCLK_SRC_MASK,
+ NAU8540_CLK_SRC_MCLK | fll_param->mclk_src);
+ regmap_update_bits(regmap, NAU8540_REG_FLL1,
+ NAU8540_FLL_RATIO_MASK, fll_param->ratio);
+ /* FLL 16-bit fractional input */
+ regmap_write(regmap, NAU8540_REG_FLL2, fll_param->fll_frac);
+ /* FLL 10-bit integer input */
+ regmap_update_bits(regmap, NAU8540_REG_FLL3,
+ NAU8540_FLL_INTEGER_MASK, fll_param->fll_int);
+ /* FLL pre-scaler */
+ regmap_update_bits(regmap, NAU8540_REG_FLL4,
+ NAU8540_FLL_REF_DIV_MASK,
+ fll_param->clk_ref_div << NAU8540_FLL_REF_DIV_SFT);
+ regmap_update_bits(regmap, NAU8540_REG_FLL5,
+ NAU8540_FLL_CLK_SW_MASK, NAU8540_FLL_CLK_SW_REF);
+ regmap_update_bits(regmap,
+ NAU8540_REG_FLL6, NAU8540_DCO_EN, 0);
+ if (fll_param->fll_frac) {
+ regmap_update_bits(regmap, NAU8540_REG_FLL5,
+ NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+ NAU8540_FLL_FTR_SW_MASK,
+ NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+ NAU8540_FLL_FTR_SW_FILTER);
+ regmap_update_bits(regmap, NAU8540_REG_FLL6,
+ NAU8540_SDM_EN, NAU8540_SDM_EN);
+ } else {
+ regmap_update_bits(regmap, NAU8540_REG_FLL5,
+ NAU8540_FLL_PDB_DAC_EN | NAU8540_FLL_LOOP_FTR_EN |
+ NAU8540_FLL_FTR_SW_MASK, NAU8540_FLL_FTR_SW_ACCU);
+ regmap_update_bits(regmap,
+ NAU8540_REG_FLL6, NAU8540_SDM_EN, 0);
+ }
}
/* freq_out must be 256*Fs in order to achieve the best performance */
static int nau8540_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
- unsigned int freq_in, unsigned int freq_out)
+ unsigned int freq_in, unsigned int freq_out)
{
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
- struct nau8540_fll fll_param;
- int ret, fs;
-
- switch (pll_id) {
- case NAU8540_CLK_FLL_MCLK:
- regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
- NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_MCLK);
- break;
-
- case NAU8540_CLK_FLL_BLK:
- regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
- NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_BLK);
- break;
-
- case NAU8540_CLK_FLL_FS:
- regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
- NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_FS);
- break;
-
- default:
- dev_err(nau8540->dev, "Invalid clock id (%d)\n", pll_id);
- return -EINVAL;
- }
- dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
- freq_out, pll_id);
-
- fs = freq_out / 256;
- ret = nau8540_calc_fll_param(freq_in, fs, &fll_param);
- if (ret < 0) {
- dev_err(nau8540->dev, "Unsupported input clock %d\n", freq_in);
- return ret;
- }
- dev_dbg(nau8540->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
- fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
- fll_param.fll_int, fll_param.clk_ref_div);
-
- nau8540_fll_apply(nau8540->regmap, &fll_param);
- mdelay(2);
- regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
- NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
-
- return 0;
+ struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+ struct nau8540_fll fll_param;
+ int ret, fs;
+
+ switch (pll_id) {
+ case NAU8540_CLK_FLL_MCLK:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+ NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_MCLK);
+ break;
+
+ case NAU8540_CLK_FLL_BLK:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+ NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_BLK);
+ break;
+
+ case NAU8540_CLK_FLL_FS:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL3,
+ NAU8540_FLL_CLK_SRC_MASK, NAU8540_FLL_CLK_SRC_FS);
+ break;
+
+ default:
+ dev_err(nau8540->dev, "Invalid clock id (%d)\n", pll_id);
+ return -EINVAL;
+ }
+ dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq_out, pll_id);
+
+ fs = freq_out / 256;
+ ret = nau8540_calc_fll_param(freq_in, fs, &fll_param);
+ if (ret < 0) {
+ dev_err(nau8540->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+ dev_dbg(nau8540->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+ fll_param.fll_int, fll_param.clk_ref_div);
+
+ nau8540_fll_apply(nau8540->regmap, &fll_param);
+ mdelay(2);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
+
+ return 0;
}
static int nau8540_set_sysclk(struct snd_soc_codec *codec,
- int clk_id, int source, unsigned int freq, int dir)
+ int clk_id, int source, unsigned int freq, int dir)
{
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
-
- switch (clk_id) {
- case NAU8540_CLK_DIS:
- case NAU8540_CLK_MCLK:
- regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
- NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_MCLK);
- regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
- NAU8540_DCO_EN, 0);
- break;
-
- case NAU8540_CLK_INTERNAL:
- regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
- NAU8540_DCO_EN, NAU8540_DCO_EN);
- regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
- NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
- break;
-
- default:
- dev_err(nau8540->dev, "Invalid clock id (%d)\n", clk_id);
- return -EINVAL;
- }
-
- dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
- freq, clk_id);
-
- return 0;
+ struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+
+ switch (clk_id) {
+ case NAU8540_CLK_DIS:
+ case NAU8540_CLK_MCLK:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_MCLK);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
+ NAU8540_DCO_EN, 0);
+ break;
+
+ case NAU8540_CLK_INTERNAL:
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_FLL6,
+ NAU8540_DCO_EN, NAU8540_DCO_EN);
+ regmap_update_bits(nau8540->regmap, NAU8540_REG_CLOCK_SRC,
+ NAU8540_CLK_SRC_MASK, NAU8540_CLK_SRC_VCO);
+ break;
+
+ default:
+ dev_err(nau8540->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ dev_dbg(nau8540->dev, "Sysclk is %dHz and clock id is %d\n",
+ freq, clk_id);
+
+ return 0;
}
static void nau8540_reset_chip(struct regmap *regmap)
{
- regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
- regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
+ regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
+ regmap_write(regmap, NAU8540_REG_SW_RESET, 0x00);
}
static void nau8540_init_regs(struct nau8540 *nau8540)
{
- struct regmap *regmap = nau8540->regmap;
-
- /* Enable Bias/VMID/VMID Tieoff */
- regmap_update_bits(regmap, NAU8540_REG_VMID_CTRL,
- NAU8540_VMID_EN | NAU8540_VMID_SEL_MASK,
- NAU8540_VMID_EN | (0x2 << NAU8540_VMID_SEL_SFT));
- regmap_update_bits(regmap, NAU8540_REG_REFERENCE,
- NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN,
- NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN);
- mdelay(2);
- regmap_update_bits(regmap, NAU8540_REG_MIC_BIAS,
- NAU8540_PU_PRE, NAU8540_PU_PRE);
- regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL,
- NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN,
- NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN);
- /* ADC OSR selection, CLK_ADC = Fs * OSR */
- regmap_update_bits(regmap, NAU8540_REG_ADC_SAMPLE_RATE,
- NAU8540_ADC_OSR_MASK, NAU8540_ADC_OSR_64);
+ struct regmap *regmap = nau8540->regmap;
+
+ /* Enable Bias/VMID/VMID Tieoff */
+ regmap_update_bits(regmap, NAU8540_REG_VMID_CTRL,
+ NAU8540_VMID_EN | NAU8540_VMID_SEL_MASK,
+ NAU8540_VMID_EN | (0x2 << NAU8540_VMID_SEL_SFT));
+ regmap_update_bits(regmap, NAU8540_REG_REFERENCE,
+ NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN,
+ NAU8540_PRECHARGE_DIS | NAU8540_GLOBAL_BIAS_EN);
+ mdelay(2);
+ regmap_update_bits(regmap, NAU8540_REG_MIC_BIAS,
+ NAU8540_PU_PRE, NAU8540_PU_PRE);
+ regmap_update_bits(regmap, NAU8540_REG_CLOCK_CTRL,
+ NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN,
+ NAU8540_CLK_ADC_EN | NAU8540_CLK_I2S_EN);
+ /* ADC OSR selection, CLK_ADC = Fs * OSR */
+ regmap_update_bits(regmap, NAU8540_REG_ADC_SAMPLE_RATE,
+ NAU8540_ADC_OSR_MASK, NAU8540_ADC_OSR_64);
}
static int __maybe_unused nau8540_suspend(struct snd_soc_codec *codec)
{
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+ struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
- regcache_cache_only(nau8540->regmap, true);
- regcache_mark_dirty(nau8540->regmap);
+ regcache_cache_only(nau8540->regmap, true);
+ regcache_mark_dirty(nau8540->regmap);
- return 0;
+ return 0;
}
static int __maybe_unused nau8540_resume(struct snd_soc_codec *codec)
{
- struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
+ struct nau8540 *nau8540 = snd_soc_codec_get_drvdata(codec);
- regcache_cache_only(nau8540->regmap, false);
- regcache_sync(nau8540->regmap);
+ regcache_cache_only(nau8540->regmap, false);
+ regcache_sync(nau8540->regmap);
- return 0;
+ return 0;
}
static struct snd_soc_codec_driver nau8540_codec_driver = {
- .set_sysclk = nau8540_set_sysclk,
- .set_pll = nau8540_set_pll,
- .suspend = nau8540_suspend,
- .resume = nau8540_resume,
- .suspend_bias_off = true,
-
- .component_driver = {
- .controls = nau8540_snd_controls,
- .num_controls = ARRAY_SIZE(nau8540_snd_controls),
- .dapm_widgets = nau8540_dapm_widgets,
- .num_dapm_widgets = ARRAY_SIZE(nau8540_dapm_widgets),
- .dapm_routes = nau8540_dapm_routes,
- .num_dapm_routes = ARRAY_SIZE(nau8540_dapm_routes),
- },
+ .set_sysclk = nau8540_set_sysclk,
+ .set_pll = nau8540_set_pll,
+ .suspend = nau8540_suspend,
+ .resume = nau8540_resume,
+ .suspend_bias_off = true,
+
+ .component_driver = {
+ .controls = nau8540_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8540_snd_controls),
+ .dapm_widgets = nau8540_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8540_dapm_widgets),
+ .dapm_routes = nau8540_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8540_dapm_routes),
+ },
};
static const struct regmap_config nau8540_regmap_config = {
- .val_bits = 16,
- .reg_bits = 16,
+ .val_bits = 16,
+ .reg_bits = 16,
- .max_register = NAU8540_REG_MAX,
- .readable_reg = nau8540_readable_reg,
- .writeable_reg = nau8540_writeable_reg,
- .volatile_reg = nau8540_volatile_reg,
+ .max_register = NAU8540_REG_MAX,
+ .readable_reg = nau8540_readable_reg,
+ .writeable_reg = nau8540_writeable_reg,
+ .volatile_reg = nau8540_volatile_reg,
- .cache_type = REGCACHE_RBTREE,
- .reg_defaults = nau8540_reg_defaults,
- .num_reg_defaults = ARRAY_SIZE(nau8540_reg_defaults),
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8540_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8540_reg_defaults),
};
static int nau8540_i2c_probe(struct i2c_client *i2c,
- const struct i2c_device_id *id)
+ const struct i2c_device_id *id)
{
- struct device *dev = &i2c->dev;
- struct nau8540 *nau8540 = dev_get_platdata(dev);
- int ret, value;
-
- if (!nau8540) {
- nau8540 = devm_kzalloc(dev, sizeof(*nau8540), GFP_KERNEL);
- if (!nau8540)
- return -ENOMEM;
- }
- i2c_set_clientdata(i2c, nau8540);
-
- nau8540->regmap = devm_regmap_init_i2c(i2c, &nau8540_regmap_config);
- if (IS_ERR(nau8540->regmap))
- return PTR_ERR(nau8540->regmap);
- ret = regmap_read(nau8540->regmap, NAU8540_REG_I2C_DEVICE_ID, &value);
- if (ret < 0) {
- dev_err(dev, "Failed to read device id from the NAU85L40: %d\n",
- ret);
- return ret;
- }
-
- nau8540->dev = dev;
- nau8540_reset_chip(nau8540->regmap);
- nau8540_init_regs(nau8540);
-
- return snd_soc_register_codec(dev,
- &nau8540_codec_driver, &nau8540_dai, 1);
+ struct device *dev = &i2c->dev;
+ struct nau8540 *nau8540 = dev_get_platdata(dev);
+ int ret, value;
+
+ if (!nau8540) {
+ nau8540 = devm_kzalloc(dev, sizeof(*nau8540), GFP_KERNEL);
+ if (!nau8540)
+ return -ENOMEM;
+ }
+ i2c_set_clientdata(i2c, nau8540);
+
+ nau8540->regmap = devm_regmap_init_i2c(i2c, &nau8540_regmap_config);
+ if (IS_ERR(nau8540->regmap))
+ return PTR_ERR(nau8540->regmap);
+ ret = regmap_read(nau8540->regmap, NAU8540_REG_I2C_DEVICE_ID, &value);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device id from the NAU85L40: %d\n",
+ ret);
+ return ret;
+ }
+
+ nau8540->dev = dev;
+ nau8540_reset_chip(nau8540->regmap);
+ nau8540_init_regs(nau8540);
+
+ return snd_soc_register_codec(dev,
+ &nau8540_codec_driver, &nau8540_dai, 1);
}
static int nau8540_i2c_remove(struct i2c_client *client)
{
- snd_soc_unregister_codec(&client->dev);
- return 0;
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
}
static const struct i2c_device_id nau8540_i2c_ids[] = {
- { "nau8540", 0 },
- { }
+ { "nau8540", 0 },
+ { }
};
MODULE_DEVICE_TABLE(i2c, nau8540_i2c_ids);
#ifdef CONFIG_OF
static const struct of_device_id nau8540_of_ids[] = {
- { .compatible = "nuvoton,nau8540", },
- {}
+ { .compatible = "nuvoton,nau8540", },
+ {}
};
MODULE_DEVICE_TABLE(of, nau8540_of_ids);
#endif
static struct i2c_driver nau8540_i2c_driver = {
- .driver = {
- .name = "nau8540",
- .of_match_table = of_match_ptr(nau8540_of_ids),
- },
- .probe = nau8540_i2c_probe,
- .remove = nau8540_i2c_remove,
- .id_table = nau8540_i2c_ids,
+ .driver = {
+ .name = "nau8540",
+ .of_match_table = of_match_ptr(nau8540_of_ids),
+ },
+ .probe = nau8540_i2c_probe,
+ .remove = nau8540_i2c_remove,
+ .id_table = nau8540_i2c_ids,
};
module_i2c_driver(nau8540_i2c_driver);
diff --git a/sound/soc/codecs/nau8540.h b/sound/soc/codecs/nau8540.h
index d06e65188cd5..5db5b224944d 100644
--- a/sound/soc/codecs/nau8540.h
+++ b/sound/soc/codecs/nau8540.h
@@ -12,211 +12,211 @@
#ifndef __NAU8540_H__
#define __NAU8540_H__
-#define NAU8540_REG_SW_RESET 0x00
-#define NAU8540_REG_POWER_MANAGEMENT 0x01
-#define NAU8540_REG_CLOCK_CTRL 0x02
-#define NAU8540_REG_CLOCK_SRC 0x03
-#define NAU8540_REG_FLL1 0x04
-#define NAU8540_REG_FLL2 0x05
-#define NAU8540_REG_FLL3 0x06
-#define NAU8540_REG_FLL4 0x07
-#define NAU8540_REG_FLL5 0x08
-#define NAU8540_REG_FLL6 0x09
-#define NAU8540_REG_FLL_VCO_RSV 0x0A
-#define NAU8540_REG_PCM_CTRL0 0x10
-#define NAU8540_REG_PCM_CTRL1 0x11
-#define NAU8540_REG_PCM_CTRL2 0x12
-#define NAU8540_REG_PCM_CTRL3 0x13
-#define NAU8540_REG_PCM_CTRL4 0x14
-#define NAU8540_REG_ALC_CONTROL_1 0x20
-#define NAU8540_REG_ALC_CONTROL_2 0x21
-#define NAU8540_REG_ALC_CONTROL_3 0x22
-#define NAU8540_REG_ALC_CONTROL_4 0x23
-#define NAU8540_REG_ALC_CONTROL_5 0x24
-#define NAU8540_REG_ALC_GAIN_CH12 0x2D
-#define NAU8540_REG_ALC_GAIN_CH34 0x2E
-#define NAU8540_REG_ALC_STATUS 0x2F
-#define NAU8540_REG_NOTCH_FIL1_CH1 0x30
-#define NAU8540_REG_NOTCH_FIL2_CH1 0x31
-#define NAU8540_REG_NOTCH_FIL1_CH2 0x32
-#define NAU8540_REG_NOTCH_FIL2_CH2 0x33
-#define NAU8540_REG_NOTCH_FIL1_CH3 0x34
-#define NAU8540_REG_NOTCH_FIL2_CH3 0x35
-#define NAU8540_REG_NOTCH_FIL1_CH4 0x36
-#define NAU8540_REG_NOTCH_FIL2_CH4 0x37
-#define NAU8540_REG_HPF_FILTER_CH12 0x38
-#define NAU8540_REG_HPF_FILTER_CH34 0x39
-#define NAU8540_REG_ADC_SAMPLE_RATE 0x3A
-#define NAU8540_REG_DIGITAL_GAIN_CH1 0x40
-#define NAU8540_REG_DIGITAL_GAIN_CH2 0x41
-#define NAU8540_REG_DIGITAL_GAIN_CH3 0x42
-#define NAU8540_REG_DIGITAL_GAIN_CH4 0x43
-#define NAU8540_REG_DIGITAL_MUX 0x44
-#define NAU8540_REG_P2P_CH1 0x48
-#define NAU8540_REG_P2P_CH2 0x49
-#define NAU8540_REG_P2P_CH3 0x4A
-#define NAU8540_REG_P2P_CH4 0x4B
-#define NAU8540_REG_PEAK_CH1 0x4C
-#define NAU8540_REG_PEAK_CH2 0x4D
-#define NAU8540_REG_PEAK_CH3 0x4E
-#define NAU8540_REG_PEAK_CH4 0x4F
-#define NAU8540_REG_GPIO_CTRL 0x50
-#define NAU8540_REG_MISC_CTRL 0x51
-#define NAU8540_REG_I2C_CTRL 0x52
-#define NAU8540_REG_I2C_DEVICE_ID 0x58
-#define NAU8540_REG_RST 0x5A
-#define NAU8540_REG_VMID_CTRL 0x60
-#define NAU8540_REG_MUTE 0x61
-#define NAU8540_REG_ANALOG_ADC1 0x64
-#define NAU8540_REG_ANALOG_ADC2 0x65
-#define NAU8540_REG_ANALOG_PWR 0x66
-#define NAU8540_REG_MIC_BIAS 0x67
-#define NAU8540_REG_REFERENCE 0x68
-#define NAU8540_REG_FEPGA1 0x69
-#define NAU8540_REG_FEPGA2 0x6A
-#define NAU8540_REG_FEPGA3 0x6B
-#define NAU8540_REG_FEPGA4 0x6C
-#define NAU8540_REG_PWR 0x6D
-#define NAU8540_REG_MAX NAU8540_REG_PWR
+#define NAU8540_REG_SW_RESET 0x00
+#define NAU8540_REG_POWER_MANAGEMENT 0x01
+#define NAU8540_REG_CLOCK_CTRL 0x02
+#define NAU8540_REG_CLOCK_SRC 0x03
+#define NAU8540_REG_FLL1 0x04
+#define NAU8540_REG_FLL2 0x05
+#define NAU8540_REG_FLL3 0x06
+#define NAU8540_REG_FLL4 0x07
+#define NAU8540_REG_FLL5 0x08
+#define NAU8540_REG_FLL6 0x09
+#define NAU8540_REG_FLL_VCO_RSV 0x0A
+#define NAU8540_REG_PCM_CTRL0 0x10
+#define NAU8540_REG_PCM_CTRL1 0x11
+#define NAU8540_REG_PCM_CTRL2 0x12
+#define NAU8540_REG_PCM_CTRL3 0x13
+#define NAU8540_REG_PCM_CTRL4 0x14
+#define NAU8540_REG_ALC_CONTROL_1 0x20
+#define NAU8540_REG_ALC_CONTROL_2 0x21
+#define NAU8540_REG_ALC_CONTROL_3 0x22
+#define NAU8540_REG_ALC_CONTROL_4 0x23
+#define NAU8540_REG_ALC_CONTROL_5 0x24
+#define NAU8540_REG_ALC_GAIN_CH12 0x2D
+#define NAU8540_REG_ALC_GAIN_CH34 0x2E
+#define NAU8540_REG_ALC_STATUS 0x2F
+#define NAU8540_REG_NOTCH_FIL1_CH1 0x30
+#define NAU8540_REG_NOTCH_FIL2_CH1 0x31
+#define NAU8540_REG_NOTCH_FIL1_CH2 0x32
+#define NAU8540_REG_NOTCH_FIL2_CH2 0x33
+#define NAU8540_REG_NOTCH_FIL1_CH3 0x34
+#define NAU8540_REG_NOTCH_FIL2_CH3 0x35
+#define NAU8540_REG_NOTCH_FIL1_CH4 0x36
+#define NAU8540_REG_NOTCH_FIL2_CH4 0x37
+#define NAU8540_REG_HPF_FILTER_CH12 0x38
+#define NAU8540_REG_HPF_FILTER_CH34 0x39
+#define NAU8540_REG_ADC_SAMPLE_RATE 0x3A
+#define NAU8540_REG_DIGITAL_GAIN_CH1 0x40
+#define NAU8540_REG_DIGITAL_GAIN_CH2 0x41
+#define NAU8540_REG_DIGITAL_GAIN_CH3 0x42
+#define NAU8540_REG_DIGITAL_GAIN_CH4 0x43
+#define NAU8540_REG_DIGITAL_MUX 0x44
+#define NAU8540_REG_P2P_CH1 0x48
+#define NAU8540_REG_P2P_CH2 0x49
+#define NAU8540_REG_P2P_CH3 0x4A
+#define NAU8540_REG_P2P_CH4 0x4B
+#define NAU8540_REG_PEAK_CH1 0x4C
+#define NAU8540_REG_PEAK_CH2 0x4D
+#define NAU8540_REG_PEAK_CH3 0x4E
+#define NAU8540_REG_PEAK_CH4 0x4F
+#define NAU8540_REG_GPIO_CTRL 0x50
+#define NAU8540_REG_MISC_CTRL 0x51
+#define NAU8540_REG_I2C_CTRL 0x52
+#define NAU8540_REG_I2C_DEVICE_ID 0x58
+#define NAU8540_REG_RST 0x5A
+#define NAU8540_REG_VMID_CTRL 0x60
+#define NAU8540_REG_MUTE 0x61
+#define NAU8540_REG_ANALOG_ADC1 0x64
+#define NAU8540_REG_ANALOG_ADC2 0x65
+#define NAU8540_REG_ANALOG_PWR 0x66
+#define NAU8540_REG_MIC_BIAS 0x67
+#define NAU8540_REG_REFERENCE 0x68
+#define NAU8540_REG_FEPGA1 0x69
+#define NAU8540_REG_FEPGA2 0x6A
+#define NAU8540_REG_FEPGA3 0x6B
+#define NAU8540_REG_FEPGA4 0x6C
+#define NAU8540_REG_PWR 0x6D
+#define NAU8540_REG_MAX NAU8540_REG_PWR
/* POWER_MANAGEMENT (0x01) */
-#define NAU8540_ADC4_EN (0x1 << 3)
-#define NAU8540_ADC3_EN (0x1 << 2)
-#define NAU8540_ADC2_EN (0x1 << 1)
-#define NAU8540_ADC1_EN 0x1
+#define NAU8540_ADC4_EN (0x1 << 3)
+#define NAU8540_ADC3_EN (0x1 << 2)
+#define NAU8540_ADC2_EN (0x1 << 1)
+#define NAU8540_ADC1_EN 0x1
/* CLOCK_CTRL (0x02) */
-#define NAU8540_CLK_ADC_EN (0x1 << 15)
-#define NAU8540_CLK_I2S_EN (0x1 << 1)
+#define NAU8540_CLK_ADC_EN (0x1 << 15)
+#define NAU8540_CLK_I2S_EN (0x1 << 1)
/* CLOCK_SRC (0x03) */
-#define NAU8540_CLK_SRC_SFT 15
-#define NAU8540_CLK_SRC_MASK (1 << NAU8540_CLK_SRC_SFT)
-#define NAU8540_CLK_SRC_VCO (1 << NAU8540_CLK_SRC_SFT)
-#define NAU8540_CLK_SRC_MCLK (0 << NAU8540_CLK_SRC_SFT)
-#define NAU8540_CLK_ADC_SRC_SFT 6
-#define NAU8540_CLK_ADC_SRC_MASK (0x3 << NAU8540_CLK_ADC_SRC_SFT)
-#define NAU8540_CLK_MCLK_SRC_MASK 0xf
+#define NAU8540_CLK_SRC_SFT 15
+#define NAU8540_CLK_SRC_MASK (1 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_SRC_VCO (1 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_SRC_MCLK (0 << NAU8540_CLK_SRC_SFT)
+#define NAU8540_CLK_ADC_SRC_SFT 6
+#define NAU8540_CLK_ADC_SRC_MASK (0x3 << NAU8540_CLK_ADC_SRC_SFT)
+#define NAU8540_CLK_MCLK_SRC_MASK 0xf
/* FLL1 (0x04) */
-#define NAU8540_FLL_RATIO_MASK 0x7f
+#define NAU8540_FLL_RATIO_MASK 0x7f
/* FLL3 (0x06) */
-#define NAU8540_FLL_CLK_SRC_SFT 10
-#define NAU8540_FLL_CLK_SRC_MASK (0x3 << NAU8540_FLL_CLK_SRC_SFT)
-#define NAU8540_FLL_CLK_SRC_MCLK (0 << NAU8540_FLL_CLK_SRC_SFT)
-#define NAU8540_FLL_CLK_SRC_BLK (0x2 << NAU8540_FLL_CLK_SRC_SFT)
-#define NAU8540_FLL_CLK_SRC_FS (0x3 << NAU8540_FLL_CLK_SRC_SFT)
-#define NAU8540_FLL_INTEGER_MASK 0x3ff
+#define NAU8540_FLL_CLK_SRC_SFT 10
+#define NAU8540_FLL_CLK_SRC_MASK (0x3 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_MCLK (0 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_BLK (0x2 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_CLK_SRC_FS (0x3 << NAU8540_FLL_CLK_SRC_SFT)
+#define NAU8540_FLL_INTEGER_MASK 0x3ff
/* FLL4 (0x07) */
-#define NAU8540_FLL_REF_DIV_SFT 10
-#define NAU8540_FLL_REF_DIV_MASK (0x3 << NAU8540_FLL_REF_DIV_SFT)
+#define NAU8540_FLL_REF_DIV_SFT 10
+#define NAU8540_FLL_REF_DIV_MASK (0x3 << NAU8540_FLL_REF_DIV_SFT)
/* FLL5 (0x08) */
-#define NAU8540_FLL_PDB_DAC_EN (0x1 << 15)
-#define NAU8540_FLL_LOOP_FTR_EN (0x1 << 14)
-#define NAU8540_FLL_CLK_SW_MASK (0x1 << 13)
-#define NAU8540_FLL_CLK_SW_N2 (0x1 << 13)
-#define NAU8540_FLL_CLK_SW_REF (0x0 << 13)
-#define NAU8540_FLL_FTR_SW_MASK (0x1 << 12)
-#define NAU8540_FLL_FTR_SW_ACCU (0x1 << 12)
-#define NAU8540_FLL_FTR_SW_FILTER (0x0 << 12)
+#define NAU8540_FLL_PDB_DAC_EN (0x1 << 15)
+#define NAU8540_FLL_LOOP_FTR_EN (0x1 << 14)
+#define NAU8540_FLL_CLK_SW_MASK (0x1 << 13)
+#define NAU8540_FLL_CLK_SW_N2 (0x1 << 13)
+#define NAU8540_FLL_CLK_SW_REF (0x0 << 13)
+#define NAU8540_FLL_FTR_SW_MASK (0x1 << 12)
+#define NAU8540_FLL_FTR_SW_ACCU (0x1 << 12)
+#define NAU8540_FLL_FTR_SW_FILTER (0x0 << 12)
/* FLL6 (0x9) */
-#define NAU8540_DCO_EN (0x1 << 15)
-#define NAU8540_SDM_EN (0x1 << 14)
+#define NAU8540_DCO_EN (0x1 << 15)
+#define NAU8540_SDM_EN (0x1 << 14)
/* PCM_CTRL0 (0x10) */
-#define NAU8540_I2S_BP_SFT 7
-#define NAU8540_I2S_BP_INV (0x1 << NAU8540_I2S_BP_SFT)
-#define NAU8540_I2S_PCMB_SFT 6
-#define NAU8540_I2S_PCMB_EN (0x1 << NAU8540_I2S_PCMB_SFT)
-#define NAU8540_I2S_DL_SFT 2
-#define NAU8540_I2S_DL_MASK (0x3 << NAU8540_I2S_DL_SFT)
-#define NAU8540_I2S_DL_16 (0 << NAU8540_I2S_DL_SFT)
-#define NAU8540_I2S_DL_20 (0x1 << NAU8540_I2S_DL_SFT)
-#define NAU8540_I2S_DL_24 (0x2 << NAU8540_I2S_DL_SFT)
-#define NAU8540_I2S_DL_32 (0x3 << NAU8540_I2S_DL_SFT)
-#define NAU8540_I2S_DF_MASK 0x3
-#define NAU8540_I2S_DF_RIGTH 0
-#define NAU8540_I2S_DF_LEFT 0x1
-#define NAU8540_I2S_DF_I2S 0x2
-#define NAU8540_I2S_DF_PCM_AB 0x3
+#define NAU8540_I2S_BP_SFT 7
+#define NAU8540_I2S_BP_INV (0x1 << NAU8540_I2S_BP_SFT)
+#define NAU8540_I2S_PCMB_SFT 6
+#define NAU8540_I2S_PCMB_EN (0x1 << NAU8540_I2S_PCMB_SFT)
+#define NAU8540_I2S_DL_SFT 2
+#define NAU8540_I2S_DL_MASK (0x3 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_16 (0 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_20 (0x1 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_24 (0x2 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DL_32 (0x3 << NAU8540_I2S_DL_SFT)
+#define NAU8540_I2S_DF_MASK 0x3
+#define NAU8540_I2S_DF_RIGTH 0
+#define NAU8540_I2S_DF_LEFT 0x1
+#define NAU8540_I2S_DF_I2S 0x2
+#define NAU8540_I2S_DF_PCM_AB 0x3
/* PCM_CTRL1 (0x11) */
-#define NAU8540_I2S_LRC_DIV_SFT 12
-#define NAU8540_I2S_LRC_DIV_MASK (0x3 << NAU8540_I2S_LRC_DIV_SFT)
-#define NAU8540_I2S_DO12_OE (0x1 << 4)
-#define NAU8540_I2S_MS_SFT 3
-#define NAU8540_I2S_MS_MASK (0x1 << NAU8540_I2S_MS_SFT)
-#define NAU8540_I2S_MS_MASTER (0x1 << NAU8540_I2S_MS_SFT)
-#define NAU8540_I2S_MS_SLAVE (0x0 << NAU8540_I2S_MS_SFT)
-#define NAU8540_I2S_BLK_DIV_MASK 0x7
+#define NAU8540_I2S_LRC_DIV_SFT 12
+#define NAU8540_I2S_LRC_DIV_MASK (0x3 << NAU8540_I2S_LRC_DIV_SFT)
+#define NAU8540_I2S_DO12_OE (0x1 << 4)
+#define NAU8540_I2S_MS_SFT 3
+#define NAU8540_I2S_MS_MASK (0x1 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_MS_MASTER (0x1 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_MS_SLAVE (0x0 << NAU8540_I2S_MS_SFT)
+#define NAU8540_I2S_BLK_DIV_MASK 0x7
/* PCM_CTRL1 (0x12) */
-#define NAU8540_I2S_DO34_OE (0x1 << 11)
-#define NAU8540_I2S_TSLOT_L_MASK 0x3ff
+#define NAU8540_I2S_DO34_OE (0x1 << 11)
+#define NAU8540_I2S_TSLOT_L_MASK 0x3ff
/* PCM_CTRL4 (0x14) */
-#define NAU8540_TDM_MODE (0x1 << 15)
-#define NAU8540_TDM_OFFSET_EN (0x1 << 14)
-#define NAU8540_TDM_TX_MASK 0xf
+#define NAU8540_TDM_MODE (0x1 << 15)
+#define NAU8540_TDM_OFFSET_EN (0x1 << 14)
+#define NAU8540_TDM_TX_MASK 0xf
/* ADC_SAMPLE_RATE (0x3A) */
-#define NAU8540_ADC_OSR_MASK 0x3
-#define NAU8540_ADC_OSR_256 0x3
-#define NAU8540_ADC_OSR_128 0x2
-#define NAU8540_ADC_OSR_64 0x1
-#define NAU8540_ADC_OSR_32 0x0
+#define NAU8540_ADC_OSR_MASK 0x3
+#define NAU8540_ADC_OSR_256 0x3
+#define NAU8540_ADC_OSR_128 0x2
+#define NAU8540_ADC_OSR_64 0x1
+#define NAU8540_ADC_OSR_32 0x0
/* VMID_CTRL (0x60) */
-#define NAU8540_VMID_EN (1 << 6)
-#define NAU8540_VMID_SEL_SFT 4
-#define NAU8540_VMID_SEL_MASK (0x3 << NAU8540_VMID_SEL_SFT)
+#define NAU8540_VMID_EN (1 << 6)
+#define NAU8540_VMID_SEL_SFT 4
+#define NAU8540_VMID_SEL_MASK (0x3 << NAU8540_VMID_SEL_SFT)
/* MIC_BIAS (0x67) */
-#define NAU8540_PU_PRE (0x1 << 8)
+#define NAU8540_PU_PRE (0x1 << 8)
/* REFERENCE (0x68) */
-#define NAU8540_PRECHARGE_DIS (0x1 << 13)
-#define NAU8540_GLOBAL_BIAS_EN (0x1 << 12)
+#define NAU8540_PRECHARGE_DIS (0x1 << 13)
+#define NAU8540_GLOBAL_BIAS_EN (0x1 << 12)
/* System Clock Source */
enum {
- NAU8540_CLK_DIS,
- NAU8540_CLK_MCLK,
- NAU8540_CLK_INTERNAL,
- NAU8540_CLK_FLL_MCLK,
- NAU8540_CLK_FLL_BLK,
- NAU8540_CLK_FLL_FS,
+ NAU8540_CLK_DIS,
+ NAU8540_CLK_MCLK,
+ NAU8540_CLK_INTERNAL,
+ NAU8540_CLK_FLL_MCLK,
+ NAU8540_CLK_FLL_BLK,
+ NAU8540_CLK_FLL_FS,
};
struct nau8540 {
- struct device *dev;
- struct regmap *regmap;
+ struct device *dev;
+ struct regmap *regmap;
};
struct nau8540_fll {
- int mclk_src;
- int ratio;
- int fll_frac;
- int fll_int;
- int clk_ref_div;
+ int mclk_src;
+ int ratio;
+ int fll_frac;
+ int fll_int;
+ int clk_ref_div;
};
struct nau8540_fll_attr {
- unsigned int param;
- unsigned int val;
+ unsigned int param;
+ unsigned int val;
};
/* over sampling rate */
struct nau8540_osr_attr {
- unsigned int osr;
- unsigned int clk_src;
+ unsigned int osr;
+ unsigned int clk_src;
};
-#endif /* __NAU8540_H__ */
+#endif /* __NAU8540_H__ */
diff --git a/sound/soc/codecs/nau8824.c b/sound/soc/codecs/nau8824.c
new file mode 100644
index 000000000000..cca974d26136
--- /dev/null
+++ b/sound/soc/codecs/nau8824.c
@@ -0,0 +1,1831 @@
+/*
+ * NAU88L24 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@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.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/acpi.h>
+#include <linux/math64.h>
+#include <linux/semaphore.h>
+
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+#include "nau8824.h"
+
+
+static int nau8824_config_sysclk(struct nau8824 *nau8824,
+ int clk_id, unsigned int freq);
+static bool nau8824_is_jack_inserted(struct nau8824 *nau8824);
+
+/* the ADC threshold of headset */
+#define DMIC_CLK 3072000
+
+/* the ADC threshold of headset */
+#define HEADSET_SARADC_THD 0x80
+
+/* the parameter threshold of FLL */
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 124000000
+#define NAU_FVCO_MIN 90000000
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8824_fll_attr mclk_src_scaling[] = {
+ { 1, 0x0 },
+ { 2, 0x2 },
+ { 4, 0x3 },
+ { 8, 0x4 },
+ { 16, 0x5 },
+ { 32, 0x6 },
+ { 3, 0x7 },
+ { 6, 0xa },
+ { 12, 0xb },
+ { 24, 0xc },
+};
+
+/* ratio for input clk freq */
+static const struct nau8824_fll_attr fll_ratio[] = {
+ { 512000, 0x01 },
+ { 256000, 0x02 },
+ { 128000, 0x04 },
+ { 64000, 0x08 },
+ { 32000, 0x10 },
+ { 8000, 0x20 },
+ { 4000, 0x40 },
+};
+
+static const struct nau8824_fll_attr fll_pre_scalar[] = {
+ { 1, 0x0 },
+ { 2, 0x1 },
+ { 4, 0x2 },
+ { 8, 0x3 },
+};
+
+/* the maximum frequency of CLK_ADC and CLK_DAC */
+#define CLK_DA_AD_MAX 6144000
+
+/* over sampling rate */
+static const struct nau8824_osr_attr osr_dac_sel[] = {
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 0, 0 },
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+};
+
+static const struct nau8824_osr_attr osr_adc_sel[] = {
+ { 32, 3 }, /* OSR 32, SRC 1/8 */
+ { 64, 2 }, /* OSR 64, SRC 1/4 */
+ { 128, 1 }, /* OSR 128, SRC 1/2 */
+ { 256, 0 }, /* OSR 256, SRC 1 */
+};
+
+static const struct reg_default nau8824_reg_defaults[] = {
+ { NAU8824_REG_ENA_CTRL, 0x0000 },
+ { NAU8824_REG_CLK_GATING_ENA, 0x0000 },
+ { NAU8824_REG_CLK_DIVIDER, 0x0000 },
+ { NAU8824_REG_FLL1, 0x0000 },
+ { NAU8824_REG_FLL2, 0x3126 },
+ { NAU8824_REG_FLL3, 0x0008 },
+ { NAU8824_REG_FLL4, 0x0010 },
+ { NAU8824_REG_FLL5, 0xC000 },
+ { NAU8824_REG_FLL6, 0x6000 },
+ { NAU8824_REG_FLL_VCO_RSV, 0xF13C },
+ { NAU8824_REG_JACK_DET_CTRL, 0x0000 },
+ { NAU8824_REG_INTERRUPT_SETTING_1, 0x0000 },
+ { NAU8824_REG_IRQ, 0x0000 },
+ { NAU8824_REG_CLEAR_INT_REG, 0x0000 },
+ { NAU8824_REG_INTERRUPT_SETTING, 0x1000 },
+ { NAU8824_REG_SAR_ADC, 0x0015 },
+ { NAU8824_REG_VDET_COEFFICIENT, 0x0110 },
+ { NAU8824_REG_VDET_THRESHOLD_1, 0x0000 },
+ { NAU8824_REG_VDET_THRESHOLD_2, 0x0000 },
+ { NAU8824_REG_VDET_THRESHOLD_3, 0x0000 },
+ { NAU8824_REG_VDET_THRESHOLD_4, 0x0000 },
+ { NAU8824_REG_GPIO_SEL, 0x0000 },
+ { NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 0x000B },
+ { NAU8824_REG_PORT0_I2S_PCM_CTRL_2, 0x0010 },
+ { NAU8824_REG_PORT0_LEFT_TIME_SLOT, 0x0000 },
+ { NAU8824_REG_PORT0_RIGHT_TIME_SLOT, 0x0000 },
+ { NAU8824_REG_TDM_CTRL, 0x0000 },
+ { NAU8824_REG_ADC_HPF_FILTER, 0x0000 },
+ { NAU8824_REG_ADC_FILTER_CTRL, 0x0002 },
+ { NAU8824_REG_DAC_FILTER_CTRL_1, 0x0000 },
+ { NAU8824_REG_DAC_FILTER_CTRL_2, 0x0000 },
+ { NAU8824_REG_NOTCH_FILTER_1, 0x0000 },
+ { NAU8824_REG_NOTCH_FILTER_2, 0x0000 },
+ { NAU8824_REG_EQ1_LOW, 0x112C },
+ { NAU8824_REG_EQ2_EQ3, 0x2C2C },
+ { NAU8824_REG_EQ4_EQ5, 0x2C2C },
+ { NAU8824_REG_ADC_CH0_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_ADC_CH1_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_ADC_CH2_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_ADC_CH3_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_DAC_MUTE_CTRL, 0x0000 },
+ { NAU8824_REG_DAC_CH0_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_DAC_CH1_DGAIN_CTRL, 0x0100 },
+ { NAU8824_REG_ADC_TO_DAC_ST, 0x0000 },
+ { NAU8824_REG_DRC_KNEE_IP12_ADC_CH01, 0x1486 },
+ { NAU8824_REG_DRC_KNEE_IP34_ADC_CH01, 0x0F12 },
+ { NAU8824_REG_DRC_SLOPE_ADC_CH01, 0x25FF },
+ { NAU8824_REG_DRC_ATKDCY_ADC_CH01, 0x3457 },
+ { NAU8824_REG_DRC_KNEE_IP12_ADC_CH23, 0x1486 },
+ { NAU8824_REG_DRC_KNEE_IP34_ADC_CH23, 0x0F12 },
+ { NAU8824_REG_DRC_SLOPE_ADC_CH23, 0x25FF },
+ { NAU8824_REG_DRC_ATKDCY_ADC_CH23, 0x3457 },
+ { NAU8824_REG_DRC_GAINL_ADC0, 0x0200 },
+ { NAU8824_REG_DRC_GAINL_ADC1, 0x0200 },
+ { NAU8824_REG_DRC_GAINL_ADC2, 0x0200 },
+ { NAU8824_REG_DRC_GAINL_ADC3, 0x0200 },
+ { NAU8824_REG_DRC_KNEE_IP12_DAC, 0x1486 },
+ { NAU8824_REG_DRC_KNEE_IP34_DAC, 0x0F12 },
+ { NAU8824_REG_DRC_SLOPE_DAC, 0x25F9 },
+ { NAU8824_REG_DRC_ATKDCY_DAC, 0x3457 },
+ { NAU8824_REG_DRC_GAIN_DAC_CH0, 0x0200 },
+ { NAU8824_REG_DRC_GAIN_DAC_CH1, 0x0200 },
+ { NAU8824_REG_MODE, 0x0000 },
+ { NAU8824_REG_MODE1, 0x0000 },
+ { NAU8824_REG_MODE2, 0x0000 },
+ { NAU8824_REG_CLASSG, 0x0000 },
+ { NAU8824_REG_OTP_EFUSE, 0x0000 },
+ { NAU8824_REG_OTPDOUT_1, 0x0000 },
+ { NAU8824_REG_OTPDOUT_2, 0x0000 },
+ { NAU8824_REG_MISC_CTRL, 0x0000 },
+ { NAU8824_REG_I2C_TIMEOUT, 0xEFFF },
+ { NAU8824_REG_TEST_MODE, 0x0000 },
+ { NAU8824_REG_I2C_DEVICE_ID, 0x1AF1 },
+ { NAU8824_REG_SAR_ADC_DATA_OUT, 0x00FF },
+ { NAU8824_REG_BIAS_ADJ, 0x0000 },
+ { NAU8824_REG_PGA_GAIN, 0x0000 },
+ { NAU8824_REG_TRIM_SETTINGS, 0x0000 },
+ { NAU8824_REG_ANALOG_CONTROL_1, 0x0000 },
+ { NAU8824_REG_ANALOG_CONTROL_2, 0x0000 },
+ { NAU8824_REG_ENABLE_LO, 0x0000 },
+ { NAU8824_REG_GAIN_LO, 0x0000 },
+ { NAU8824_REG_CLASSD_GAIN_1, 0x0000 },
+ { NAU8824_REG_CLASSD_GAIN_2, 0x0000 },
+ { NAU8824_REG_ANALOG_ADC_1, 0x0011 },
+ { NAU8824_REG_ANALOG_ADC_2, 0x0020 },
+ { NAU8824_REG_RDAC, 0x0008 },
+ { NAU8824_REG_MIC_BIAS, 0x0006 },
+ { NAU8824_REG_HS_VOLUME_CONTROL, 0x0000 },
+ { NAU8824_REG_BOOST, 0x0000 },
+ { NAU8824_REG_FEPGA, 0x0000 },
+ { NAU8824_REG_FEPGA_II, 0x0000 },
+ { NAU8824_REG_FEPGA_SE, 0x0000 },
+ { NAU8824_REG_FEPGA_ATTENUATION, 0x0000 },
+ { NAU8824_REG_ATT_PORT0, 0x0000 },
+ { NAU8824_REG_ATT_PORT1, 0x0000 },
+ { NAU8824_REG_POWER_UP_CONTROL, 0x0000 },
+ { NAU8824_REG_CHARGE_PUMP_CONTROL, 0x0300 },
+ { NAU8824_REG_CHARGE_PUMP_INPUT, 0x0013 },
+};
+
+static int nau8824_sema_acquire(struct nau8824 *nau8824, long timeout)
+{
+ int ret;
+
+ if (timeout) {
+ ret = down_timeout(&nau8824->jd_sem, timeout);
+ if (ret < 0)
+ dev_warn(nau8824->dev, "Acquire semaphone timeout\n");
+ } else {
+ ret = down_interruptible(&nau8824->jd_sem);
+ if (ret < 0)
+ dev_warn(nau8824->dev, "Acquire semaphone fail\n");
+ }
+
+ return ret;
+}
+
+static inline void nau8824_sema_release(struct nau8824 *nau8824)
+{
+ up(&nau8824->jd_sem);
+}
+
+static bool nau8824_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8824_REG_ENA_CTRL ... NAU8824_REG_FLL_VCO_RSV:
+ case NAU8824_REG_JACK_DET_CTRL:
+ case NAU8824_REG_INTERRUPT_SETTING_1:
+ case NAU8824_REG_IRQ:
+ case NAU8824_REG_CLEAR_INT_REG ... NAU8824_REG_VDET_THRESHOLD_4:
+ case NAU8824_REG_GPIO_SEL:
+ case NAU8824_REG_PORT0_I2S_PCM_CTRL_1 ... NAU8824_REG_TDM_CTRL:
+ case NAU8824_REG_ADC_HPF_FILTER ... NAU8824_REG_EQ4_EQ5:
+ case NAU8824_REG_ADC_CH0_DGAIN_CTRL ... NAU8824_REG_ADC_TO_DAC_ST:
+ case NAU8824_REG_DRC_KNEE_IP12_ADC_CH01 ... NAU8824_REG_DRC_GAINL_ADC3:
+ case NAU8824_REG_DRC_KNEE_IP12_DAC ... NAU8824_REG_DRC_GAIN_DAC_CH1:
+ case NAU8824_REG_CLASSG ... NAU8824_REG_OTP_EFUSE:
+ case NAU8824_REG_OTPDOUT_1 ... NAU8824_REG_OTPDOUT_2:
+ case NAU8824_REG_I2C_TIMEOUT:
+ case NAU8824_REG_I2C_DEVICE_ID ... NAU8824_REG_SAR_ADC_DATA_OUT:
+ case NAU8824_REG_BIAS_ADJ ... NAU8824_REG_CLASSD_GAIN_2:
+ case NAU8824_REG_ANALOG_ADC_1 ... NAU8824_REG_ATT_PORT1:
+ case NAU8824_REG_POWER_UP_CONTROL ... NAU8824_REG_CHARGE_PUMP_INPUT:
+ return true;
+ default:
+ return false;
+ }
+
+}
+
+static bool nau8824_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8824_REG_RESET ... NAU8824_REG_FLL_VCO_RSV:
+ case NAU8824_REG_JACK_DET_CTRL:
+ case NAU8824_REG_INTERRUPT_SETTING_1:
+ case NAU8824_REG_CLEAR_INT_REG ... NAU8824_REG_VDET_THRESHOLD_4:
+ case NAU8824_REG_GPIO_SEL:
+ case NAU8824_REG_PORT0_I2S_PCM_CTRL_1 ... NAU8824_REG_TDM_CTRL:
+ case NAU8824_REG_ADC_HPF_FILTER ... NAU8824_REG_EQ4_EQ5:
+ case NAU8824_REG_ADC_CH0_DGAIN_CTRL ... NAU8824_REG_ADC_TO_DAC_ST:
+ case NAU8824_REG_DRC_KNEE_IP12_ADC_CH01:
+ case NAU8824_REG_DRC_KNEE_IP34_ADC_CH01:
+ case NAU8824_REG_DRC_SLOPE_ADC_CH01:
+ case NAU8824_REG_DRC_ATKDCY_ADC_CH01:
+ case NAU8824_REG_DRC_KNEE_IP12_ADC_CH23:
+ case NAU8824_REG_DRC_KNEE_IP34_ADC_CH23:
+ case NAU8824_REG_DRC_SLOPE_ADC_CH23:
+ case NAU8824_REG_DRC_ATKDCY_ADC_CH23:
+ case NAU8824_REG_DRC_KNEE_IP12_DAC ... NAU8824_REG_DRC_ATKDCY_DAC:
+ case NAU8824_REG_CLASSG ... NAU8824_REG_OTP_EFUSE:
+ case NAU8824_REG_I2C_TIMEOUT:
+ case NAU8824_REG_BIAS_ADJ ... NAU8824_REG_CLASSD_GAIN_2:
+ case NAU8824_REG_ANALOG_ADC_1 ... NAU8824_REG_ATT_PORT1:
+ case NAU8824_REG_POWER_UP_CONTROL ... NAU8824_REG_CHARGE_PUMP_CONTROL:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool nau8824_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case NAU8824_REG_RESET:
+ case NAU8824_REG_IRQ ... NAU8824_REG_CLEAR_INT_REG:
+ case NAU8824_REG_DRC_GAINL_ADC0 ... NAU8824_REG_DRC_GAINL_ADC3:
+ case NAU8824_REG_DRC_GAIN_DAC_CH0 ... NAU8824_REG_DRC_GAIN_DAC_CH1:
+ case NAU8824_REG_OTPDOUT_1 ... NAU8824_REG_OTPDOUT_2:
+ case NAU8824_REG_I2C_DEVICE_ID ... NAU8824_REG_SAR_ADC_DATA_OUT:
+ case NAU8824_REG_CHARGE_PUMP_INPUT:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const char * const nau8824_companding[] = {
+ "Off", "NC", "u-law", "A-law" };
+
+static const struct soc_enum nau8824_companding_adc_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 12,
+ ARRAY_SIZE(nau8824_companding), nau8824_companding);
+
+static const struct soc_enum nau8824_companding_dac_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_PORT0_I2S_PCM_CTRL_1, 14,
+ ARRAY_SIZE(nau8824_companding), nau8824_companding);
+
+static const char * const nau8824_adc_decimation[] = {
+ "32", "64", "128", "256" };
+
+static const struct soc_enum nau8824_adc_decimation_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_FILTER_CTRL, 0,
+ ARRAY_SIZE(nau8824_adc_decimation), nau8824_adc_decimation);
+
+static const char * const nau8824_dac_oversampl[] = {
+ "64", "256", "128", "", "32" };
+
+static const struct soc_enum nau8824_dac_oversampl_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_DAC_FILTER_CTRL_1, 0,
+ ARRAY_SIZE(nau8824_dac_oversampl), nau8824_dac_oversampl);
+
+static const char * const nau8824_input_channel[] = {
+ "Input CH0", "Input CH1", "Input CH2", "Input CH3" };
+
+static const struct soc_enum nau8824_adc_ch0_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH0_DGAIN_CTRL, 9,
+ ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const struct soc_enum nau8824_adc_ch1_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH1_DGAIN_CTRL, 9,
+ ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const struct soc_enum nau8824_adc_ch2_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH2_DGAIN_CTRL, 9,
+ ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const struct soc_enum nau8824_adc_ch3_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_ADC_CH3_DGAIN_CTRL, 9,
+ ARRAY_SIZE(nau8824_input_channel), nau8824_input_channel);
+
+static const char * const nau8824_tdm_slot[] = {
+ "Slot 0", "Slot 1", "Slot 2", "Slot 3" };
+
+static const struct soc_enum nau8824_dac_left_sel_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_TDM_CTRL, 6,
+ ARRAY_SIZE(nau8824_tdm_slot), nau8824_tdm_slot);
+
+static const struct soc_enum nau8824_dac_right_sel_enum =
+ SOC_ENUM_SINGLE(NAU8824_REG_TDM_CTRL, 4,
+ ARRAY_SIZE(nau8824_tdm_slot), nau8824_tdm_slot);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(spk_vol_tlv, 0, 2400);
+static const DECLARE_TLV_DB_MINMAX(hp_vol_tlv, -3000, 0);
+static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 200, 0);
+static const DECLARE_TLV_DB_SCALE(dmic_vol_tlv, -12800, 50, 0);
+
+static const struct snd_kcontrol_new nau8824_snd_controls[] = {
+ SOC_ENUM("ADC Companding", nau8824_companding_adc_enum),
+ SOC_ENUM("DAC Companding", nau8824_companding_dac_enum),
+
+ SOC_ENUM("ADC Decimation Rate", nau8824_adc_decimation_enum),
+ SOC_ENUM("DAC Oversampling Rate", nau8824_dac_oversampl_enum),
+
+ SOC_SINGLE_TLV("Speaker Right DACR Volume",
+ NAU8824_REG_CLASSD_GAIN_1, 8, 0x1f, 0, spk_vol_tlv),
+ SOC_SINGLE_TLV("Speaker Left DACL Volume",
+ NAU8824_REG_CLASSD_GAIN_2, 0, 0x1f, 0, spk_vol_tlv),
+ SOC_SINGLE_TLV("Speaker Left DACR Volume",
+ NAU8824_REG_CLASSD_GAIN_1, 0, 0x1f, 0, spk_vol_tlv),
+ SOC_SINGLE_TLV("Speaker Right DACL Volume",
+ NAU8824_REG_CLASSD_GAIN_2, 8, 0x1f, 0, spk_vol_tlv),
+
+ SOC_SINGLE_TLV("Headphone Right DACR Volume",
+ NAU8824_REG_ATT_PORT0, 8, 0x1f, 0, hp_vol_tlv),
+ SOC_SINGLE_TLV("Headphone Left DACL Volume",
+ NAU8824_REG_ATT_PORT0, 0, 0x1f, 0, hp_vol_tlv),
+ SOC_SINGLE_TLV("Headphone Right DACL Volume",
+ NAU8824_REG_ATT_PORT1, 8, 0x1f, 0, hp_vol_tlv),
+ SOC_SINGLE_TLV("Headphone Left DACR Volume",
+ NAU8824_REG_ATT_PORT1, 0, 0x1f, 0, hp_vol_tlv),
+
+ SOC_SINGLE_TLV("MIC1 Volume", NAU8824_REG_FEPGA_II,
+ NAU8824_FEPGA_GAINL_SFT, 0x12, 0, mic_vol_tlv),
+ SOC_SINGLE_TLV("MIC2 Volume", NAU8824_REG_FEPGA_II,
+ NAU8824_FEPGA_GAINR_SFT, 0x12, 0, mic_vol_tlv),
+
+ SOC_SINGLE_TLV("DMIC1 Volume", NAU8824_REG_ADC_CH0_DGAIN_CTRL,
+ 0, 0x164, 0, dmic_vol_tlv),
+ SOC_SINGLE_TLV("DMIC2 Volume", NAU8824_REG_ADC_CH1_DGAIN_CTRL,
+ 0, 0x164, 0, dmic_vol_tlv),
+ SOC_SINGLE_TLV("DMIC3 Volume", NAU8824_REG_ADC_CH2_DGAIN_CTRL,
+ 0, 0x164, 0, dmic_vol_tlv),
+ SOC_SINGLE_TLV("DMIC4 Volume", NAU8824_REG_ADC_CH3_DGAIN_CTRL,
+ 0, 0x164, 0, dmic_vol_tlv),
+
+ SOC_ENUM("ADC CH0 Select", nau8824_adc_ch0_enum),
+ SOC_ENUM("ADC CH1 Select", nau8824_adc_ch1_enum),
+ SOC_ENUM("ADC CH2 Select", nau8824_adc_ch2_enum),
+ SOC_ENUM("ADC CH3 Select", nau8824_adc_ch3_enum),
+
+ SOC_SINGLE("ADC CH0 TX Switch", NAU8824_REG_TDM_CTRL, 0, 1, 0),
+ SOC_SINGLE("ADC CH1 TX Switch", NAU8824_REG_TDM_CTRL, 1, 1, 0),
+ SOC_SINGLE("ADC CH2 TX Switch", NAU8824_REG_TDM_CTRL, 2, 1, 0),
+ SOC_SINGLE("ADC CH3 TX Switch", NAU8824_REG_TDM_CTRL, 3, 1, 0),
+
+ SOC_ENUM("DACL Channel Source", nau8824_dac_left_sel_enum),
+ SOC_ENUM("DACR Channel Source", nau8824_dac_right_sel_enum),
+
+ SOC_SINGLE("DACL LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 0, 1, 0),
+ SOC_SINGLE("DACR LR Mix", NAU8824_REG_DAC_MUTE_CTRL, 1, 1, 0),
+};
+
+static int nau8824_output_dac_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ /* Disables the TESTDAC to let DAC signal pass through. */
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_ENABLE_LO,
+ NAU8824_TEST_DAC_EN, 0);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_ENABLE_LO,
+ NAU8824_TEST_DAC_EN, NAU8824_TEST_DAC_EN);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8824_spk_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_ANALOG_CONTROL_2,
+ NAU8824_CLASSD_CLAMP_DIS, NAU8824_CLASSD_CLAMP_DIS);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_ANALOG_CONTROL_2,
+ NAU8824_CLASSD_CLAMP_DIS, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8824_pump_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+ switch (event) {
+ case SND_SOC_DAPM_POST_PMU:
+ /* Prevent startup click by letting charge pump to ramp up */
+ msleep(10);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_CHARGE_PUMP_CONTROL,
+ NAU8824_JAMNODCLOW, NAU8824_JAMNODCLOW);
+ break;
+ case SND_SOC_DAPM_PRE_PMD:
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_CHARGE_PUMP_CONTROL,
+ NAU8824_JAMNODCLOW, 0);
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int system_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ /* Set clock source to disable or internal clock before the
+ * playback or capture end. Codec needs clock for Jack
+ * detection and button press if jack inserted; otherwise,
+ * the clock should be closed.
+ */
+ if (nau8824_is_jack_inserted(nau8824)) {
+ nau8824_config_sysclk(nau8824,
+ NAU8824_CLK_INTERNAL, 0);
+ } else {
+ nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0);
+ }
+ }
+ return 0;
+}
+
+static int dmic_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ int src;
+
+ /* The DMIC clock is gotten from system clock (256fs) divided by
+ * DMIC_SRC (1, 2, 4, 8, 16, 32). The clock has to be equal or
+ * less than 3.072 MHz.
+ */
+ for (src = 0; src < 5; src++) {
+ if ((0x1 << (8 - src)) * nau8824->fs <= DMIC_CLK)
+ break;
+ }
+ dev_dbg(nau8824->dev, "dmic src %d for mclk %d\n", src, nau8824->fs * 256);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_DMIC_SRC_MASK, (src << NAU8824_CLK_DMIC_SRC_SFT));
+
+ return 0;
+}
+
+static const struct snd_kcontrol_new nau8824_adc_ch0_dmic =
+ SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+ NAU8824_ADC_CH0_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_ch1_dmic =
+ SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+ NAU8824_ADC_CH1_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_ch2_dmic =
+ SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+ NAU8824_ADC_CH2_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_ch3_dmic =
+ SOC_DAPM_SINGLE("Switch", NAU8824_REG_ENA_CTRL,
+ NAU8824_ADC_CH3_DMIC_SFT, 1, 0);
+
+static const struct snd_kcontrol_new nau8824_adc_left_mixer[] = {
+ SOC_DAPM_SINGLE("MIC Switch", NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODEL_MIC1_SFT, 1, 0),
+ SOC_DAPM_SINGLE("HSMIC Switch", NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODEL_HSMIC_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8824_adc_right_mixer[] = {
+ SOC_DAPM_SINGLE("MIC Switch", NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODER_MIC2_SFT, 1, 0),
+ SOC_DAPM_SINGLE("HSMIC Switch", NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODER_HSMIC_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8824_hp_left_mixer[] = {
+ SOC_DAPM_SINGLE("DAC Right Switch", NAU8824_REG_ENABLE_LO,
+ NAU8824_DACR_HPL_EN_SFT, 1, 0),
+ SOC_DAPM_SINGLE("DAC Left Switch", NAU8824_REG_ENABLE_LO,
+ NAU8824_DACL_HPL_EN_SFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new nau8824_hp_right_mixer[] = {
+ SOC_DAPM_SINGLE("DAC Left Switch", NAU8824_REG_ENABLE_LO,
+ NAU8824_DACL_HPR_EN_SFT, 1, 0),
+ SOC_DAPM_SINGLE("DAC Right Switch", NAU8824_REG_ENABLE_LO,
+ NAU8824_DACR_HPR_EN_SFT, 1, 0),
+};
+
+static const char * const nau8824_dac_src[] = { "DACL", "DACR" };
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8824_dacl_enum, NAU8824_REG_DAC_CH0_DGAIN_CTRL,
+ NAU8824_DAC_CH0_SEL_SFT, nau8824_dac_src);
+
+static SOC_ENUM_SINGLE_DECL(
+ nau8824_dacr_enum, NAU8824_REG_DAC_CH1_DGAIN_CTRL,
+ NAU8824_DAC_CH1_SEL_SFT, nau8824_dac_src);
+
+static const struct snd_kcontrol_new nau8824_dacl_mux =
+ SOC_DAPM_ENUM("DACL Source", nau8824_dacl_enum);
+
+static const struct snd_kcontrol_new nau8824_dacr_mux =
+ SOC_DAPM_ENUM("DACR Source", nau8824_dacr_enum);
+
+
+static const struct snd_soc_dapm_widget nau8824_dapm_widgets[] = {
+ SND_SOC_DAPM_SUPPLY("System Clock", SND_SOC_NOPM, 0, 0,
+ system_clock_control, SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_INPUT("HSMIC1"),
+ SND_SOC_DAPM_INPUT("HSMIC2"),
+ SND_SOC_DAPM_INPUT("MIC1"),
+ SND_SOC_DAPM_INPUT("MIC2"),
+ SND_SOC_DAPM_INPUT("DMIC1"),
+ SND_SOC_DAPM_INPUT("DMIC2"),
+ SND_SOC_DAPM_INPUT("DMIC3"),
+ SND_SOC_DAPM_INPUT("DMIC4"),
+
+ SND_SOC_DAPM_SUPPLY("SAR", NAU8824_REG_SAR_ADC,
+ NAU8824_SAR_ADC_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("MICBIAS", NAU8824_REG_MIC_BIAS,
+ NAU8824_MICBIAS_POWERUP_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC12 Power", NAU8824_REG_BIAS_ADJ,
+ NAU8824_DMIC1_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC34 Power", NAU8824_REG_BIAS_ADJ,
+ NAU8824_DMIC2_EN_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_SUPPLY("DMIC Clock", SND_SOC_NOPM, 0, 0,
+ dmic_clock_control, SND_SOC_DAPM_POST_PMU),
+
+ SND_SOC_DAPM_SWITCH("DMIC1 Enable", SND_SOC_NOPM,
+ 0, 0, &nau8824_adc_ch0_dmic),
+ SND_SOC_DAPM_SWITCH("DMIC2 Enable", SND_SOC_NOPM,
+ 0, 0, &nau8824_adc_ch1_dmic),
+ SND_SOC_DAPM_SWITCH("DMIC3 Enable", SND_SOC_NOPM,
+ 0, 0, &nau8824_adc_ch2_dmic),
+ SND_SOC_DAPM_SWITCH("DMIC4 Enable", SND_SOC_NOPM,
+ 0, 0, &nau8824_adc_ch3_dmic),
+
+ SND_SOC_DAPM_MIXER("Left ADC", NAU8824_REG_POWER_UP_CONTROL,
+ 12, 0, nau8824_adc_left_mixer,
+ ARRAY_SIZE(nau8824_adc_left_mixer)),
+ SND_SOC_DAPM_MIXER("Right ADC", NAU8824_REG_POWER_UP_CONTROL,
+ 13, 0, nau8824_adc_right_mixer,
+ ARRAY_SIZE(nau8824_adc_right_mixer)),
+
+ SND_SOC_DAPM_ADC("ADCL", NULL, NAU8824_REG_ANALOG_ADC_2,
+ NAU8824_ADCL_EN_SFT, 0),
+ SND_SOC_DAPM_ADC("ADCR", NULL, NAU8824_REG_ANALOG_ADC_2,
+ NAU8824_ADCR_EN_SFT, 0),
+
+ SND_SOC_DAPM_AIF_OUT("AIFTX", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+ SND_SOC_DAPM_AIF_IN("AIFRX", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+
+ SND_SOC_DAPM_DAC("DACL", NULL, NAU8824_REG_RDAC,
+ NAU8824_DACL_EN_SFT, 0),
+ SND_SOC_DAPM_SUPPLY("DACL Clock", NAU8824_REG_RDAC,
+ NAU8824_DACL_CLK_SFT, 0, NULL, 0),
+ SND_SOC_DAPM_DAC("DACR", NULL, NAU8824_REG_RDAC,
+ NAU8824_DACR_EN_SFT, 0),
+ SND_SOC_DAPM_SUPPLY("DACR Clock", NAU8824_REG_RDAC,
+ NAU8824_DACR_CLK_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8824_dacl_mux),
+ SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8824_dacr_mux),
+
+ SND_SOC_DAPM_PGA_S("Output DACL", 0, NAU8824_REG_CHARGE_PUMP_CONTROL,
+ 8, 1, nau8824_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+ SND_SOC_DAPM_PGA_S("Output DACR", 0, NAU8824_REG_CHARGE_PUMP_CONTROL,
+ 9, 1, nau8824_output_dac_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_PGA_S("ClassD", 0, NAU8824_REG_CLASSD_GAIN_1,
+ NAU8824_CLASSD_EN_SFT, 0, nau8824_spk_event,
+ SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+ SND_SOC_DAPM_MIXER("Left Headphone", NAU8824_REG_CLASSG,
+ NAU8824_CLASSG_LDAC_EN_SFT, 0, nau8824_hp_left_mixer,
+ ARRAY_SIZE(nau8824_hp_left_mixer)),
+ SND_SOC_DAPM_MIXER("Right Headphone", NAU8824_REG_CLASSG,
+ NAU8824_CLASSG_RDAC_EN_SFT, 0, nau8824_hp_right_mixer,
+ ARRAY_SIZE(nau8824_hp_right_mixer)),
+ SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8824_REG_CHARGE_PUMP_CONTROL,
+ NAU8824_CHARGE_PUMP_EN_SFT, 0, nau8824_pump_event,
+ SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD),
+ SND_SOC_DAPM_PGA("Output Driver L",
+ NAU8824_REG_POWER_UP_CONTROL, 3, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Output Driver R",
+ NAU8824_REG_POWER_UP_CONTROL, 2, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Main Driver L",
+ NAU8824_REG_POWER_UP_CONTROL, 1, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("Main Driver R",
+ NAU8824_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
+ SND_SOC_DAPM_PGA("HP Boost Driver", NAU8824_REG_BOOST,
+ NAU8824_HP_BOOST_DIS_SFT, 1, NULL, 0),
+ SND_SOC_DAPM_PGA("Class G", NAU8824_REG_CLASSG,
+ NAU8824_CLASSG_EN_SFT, 0, NULL, 0),
+
+ SND_SOC_DAPM_OUTPUT("SPKOUTL"),
+ SND_SOC_DAPM_OUTPUT("SPKOUTR"),
+ SND_SOC_DAPM_OUTPUT("HPOL"),
+ SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8824_dapm_routes[] = {
+ {"DMIC1 Enable", "Switch", "DMIC1"},
+ {"DMIC2 Enable", "Switch", "DMIC2"},
+ {"DMIC3 Enable", "Switch", "DMIC3"},
+ {"DMIC4 Enable", "Switch", "DMIC4"},
+
+ {"DMIC1", NULL, "DMIC12 Power"},
+ {"DMIC2", NULL, "DMIC12 Power"},
+ {"DMIC3", NULL, "DMIC34 Power"},
+ {"DMIC4", NULL, "DMIC34 Power"},
+ {"DMIC12 Power", NULL, "DMIC Clock"},
+ {"DMIC34 Power", NULL, "DMIC Clock"},
+
+ {"Left ADC", "MIC Switch", "MIC1"},
+ {"Left ADC", "HSMIC Switch", "HSMIC1"},
+ {"Right ADC", "MIC Switch", "MIC2"},
+ {"Right ADC", "HSMIC Switch", "HSMIC2"},
+
+ {"ADCL", NULL, "Left ADC"},
+ {"ADCR", NULL, "Right ADC"},
+
+ {"AIFTX", NULL, "MICBIAS"},
+ {"AIFTX", NULL, "ADCL"},
+ {"AIFTX", NULL, "ADCR"},
+ {"AIFTX", NULL, "DMIC1 Enable"},
+ {"AIFTX", NULL, "DMIC2 Enable"},
+ {"AIFTX", NULL, "DMIC3 Enable"},
+ {"AIFTX", NULL, "DMIC4 Enable"},
+
+ {"AIFTX", NULL, "System Clock"},
+ {"AIFRX", NULL, "System Clock"},
+
+ {"DACL", NULL, "AIFRX"},
+ {"DACL", NULL, "DACL Clock"},
+ {"DACR", NULL, "AIFRX"},
+ {"DACR", NULL, "DACR Clock"},
+
+ {"DACL Mux", "DACL", "DACL"},
+ {"DACL Mux", "DACR", "DACR"},
+ {"DACR Mux", "DACL", "DACL"},
+ {"DACR Mux", "DACR", "DACR"},
+
+ {"Output DACL", NULL, "DACL Mux"},
+ {"Output DACR", NULL, "DACR Mux"},
+
+ {"ClassD", NULL, "Output DACL"},
+ {"ClassD", NULL, "Output DACR"},
+
+ {"Left Headphone", "DAC Left Switch", "Output DACL"},
+ {"Left Headphone", "DAC Right Switch", "Output DACR"},
+ {"Right Headphone", "DAC Left Switch", "Output DACL"},
+ {"Right Headphone", "DAC Right Switch", "Output DACR"},
+
+ {"Charge Pump", NULL, "Left Headphone"},
+ {"Charge Pump", NULL, "Right Headphone"},
+ {"Output Driver L", NULL, "Charge Pump"},
+ {"Output Driver R", NULL, "Charge Pump"},
+ {"Main Driver L", NULL, "Output Driver L"},
+ {"Main Driver R", NULL, "Output Driver R"},
+ {"Class G", NULL, "Main Driver L"},
+ {"Class G", NULL, "Main Driver R"},
+ {"HP Boost Driver", NULL, "Class G"},
+
+ {"SPKOUTL", NULL, "ClassD"},
+ {"SPKOUTR", NULL, "ClassD"},
+ {"HPOL", NULL, "HP Boost Driver"},
+ {"HPOR", NULL, "HP Boost Driver"},
+};
+
+static bool nau8824_is_jack_inserted(struct nau8824 *nau8824)
+{
+ struct snd_soc_jack *jack = nau8824->jack;
+ bool insert = FALSE;
+
+ if (nau8824->irq && jack)
+ insert = jack->status & SND_JACK_HEADPHONE;
+
+ return insert;
+}
+
+static void nau8824_int_status_clear_all(struct regmap *regmap)
+{
+ int active_irq, clear_irq, i;
+
+ /* Reset the intrruption status from rightmost bit if the corres-
+ * ponding irq event occurs.
+ */
+ regmap_read(regmap, NAU8824_REG_IRQ, &active_irq);
+ for (i = 0; i < NAU8824_REG_DATA_LEN; i++) {
+ clear_irq = (0x1 << i);
+ if (active_irq & clear_irq)
+ regmap_write(regmap,
+ NAU8824_REG_CLEAR_INT_REG, clear_irq);
+ }
+}
+
+static void nau8824_eject_jack(struct nau8824 *nau8824)
+{
+ struct snd_soc_dapm_context *dapm = nau8824->dapm;
+ struct regmap *regmap = nau8824->regmap;
+
+ /* Clear all interruption status */
+ nau8824_int_status_clear_all(regmap);
+
+ snd_soc_dapm_disable_pin(dapm, "SAR");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+
+ /* Enable the insertion interruption, disable the ejection
+ * interruption, and then bypass de-bounce circuit.
+ */
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_KEY_RELEASE_DIS | NAU8824_IRQ_KEY_SHORT_PRESS_DIS |
+ NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_INSERT_DIS,
+ NAU8824_IRQ_KEY_RELEASE_DIS | NAU8824_IRQ_KEY_SHORT_PRESS_DIS |
+ NAU8824_IRQ_EJECT_DIS);
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_INSERT_EN | NAU8824_IRQ_EJECT_EN,
+ NAU8824_IRQ_INSERT_EN);
+ regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE);
+
+ /* Close clock for jack type detection at manual mode */
+ nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0);
+}
+
+static void nau8824_jdet_work(struct work_struct *work)
+{
+ struct nau8824 *nau8824 = container_of(
+ work, struct nau8824, jdet_work);
+ struct snd_soc_dapm_context *dapm = nau8824->dapm;
+ struct regmap *regmap = nau8824->regmap;
+ int adc_value, event = 0, event_mask = 0;
+
+ snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_force_enable_pin(dapm, "SAR");
+ snd_soc_dapm_sync(dapm);
+
+ msleep(100);
+
+ regmap_read(regmap, NAU8824_REG_SAR_ADC_DATA_OUT, &adc_value);
+ adc_value = adc_value & NAU8824_SAR_ADC_DATA_MASK;
+ dev_dbg(nau8824->dev, "SAR ADC data 0x%02x\n", adc_value);
+ if (adc_value < HEADSET_SARADC_THD) {
+ event |= SND_JACK_HEADPHONE;
+
+ snd_soc_dapm_disable_pin(dapm, "SAR");
+ snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+ snd_soc_dapm_sync(dapm);
+ } else {
+ event |= SND_JACK_HEADSET;
+ }
+ event_mask |= SND_JACK_HEADSET;
+ snd_soc_jack_report(nau8824->jack, event, event_mask);
+
+ nau8824_sema_release(nau8824);
+}
+
+static void nau8824_setup_auto_irq(struct nau8824 *nau8824)
+{
+ struct regmap *regmap = nau8824->regmap;
+
+ /* Enable jack ejection, short key press and release interruption. */
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_INSERT_EN | NAU8824_IRQ_EJECT_EN,
+ NAU8824_IRQ_EJECT_EN);
+ regmap_update_bits(regmap, NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_KEY_RELEASE_DIS |
+ NAU8824_IRQ_KEY_SHORT_PRESS_DIS, 0);
+ /* Enable internal VCO needed for interruptions */
+ nau8824_config_sysclk(nau8824, NAU8824_CLK_INTERNAL, 0);
+ regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_JD_SLEEP_MODE, 0);
+}
+
+static int nau8824_button_decode(int value)
+{
+ int buttons = 0;
+
+ /* The chip supports up to 8 buttons, but ALSA defines
+ * only 6 buttons.
+ */
+ if (value & BIT(0))
+ buttons |= SND_JACK_BTN_0;
+ if (value & BIT(1))
+ buttons |= SND_JACK_BTN_1;
+ if (value & BIT(2))
+ buttons |= SND_JACK_BTN_2;
+ if (value & BIT(3))
+ buttons |= SND_JACK_BTN_3;
+ if (value & BIT(4))
+ buttons |= SND_JACK_BTN_4;
+ if (value & BIT(5))
+ buttons |= SND_JACK_BTN_5;
+
+ return buttons;
+}
+
+#define NAU8824_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+ SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+static irqreturn_t nau8824_interrupt(int irq, void *data)
+{
+ struct nau8824 *nau8824 = (struct nau8824 *)data;
+ struct regmap *regmap = nau8824->regmap;
+ int active_irq, clear_irq = 0, event = 0, event_mask = 0;
+
+ if (regmap_read(regmap, NAU8824_REG_IRQ, &active_irq)) {
+ dev_err(nau8824->dev, "failed to read irq status\n");
+ return IRQ_NONE;
+ }
+ dev_dbg(nau8824->dev, "IRQ %x\n", active_irq);
+
+ if (active_irq & NAU8824_JACK_EJECTION_DETECTED) {
+ nau8824_eject_jack(nau8824);
+ event_mask |= SND_JACK_HEADSET;
+ clear_irq = NAU8824_JACK_EJECTION_DETECTED;
+ /* release semaphore held after resume,
+ * and cancel jack detection
+ */
+ nau8824_sema_release(nau8824);
+ cancel_work_sync(&nau8824->jdet_work);
+ } else if (active_irq & NAU8824_KEY_SHORT_PRESS_IRQ) {
+ int key_status, button_pressed;
+
+ regmap_read(regmap, NAU8824_REG_CLEAR_INT_REG,
+ &key_status);
+
+ /* lower 8 bits of the register are for pressed keys */
+ button_pressed = nau8824_button_decode(key_status);
+
+ event |= button_pressed;
+ dev_dbg(nau8824->dev, "button %x pressed\n", event);
+ event_mask |= NAU8824_BUTTONS;
+ clear_irq = NAU8824_KEY_SHORT_PRESS_IRQ;
+ } else if (active_irq & NAU8824_KEY_RELEASE_IRQ) {
+ event_mask = NAU8824_BUTTONS;
+ clear_irq = NAU8824_KEY_RELEASE_IRQ;
+ } else if (active_irq & NAU8824_JACK_INSERTION_DETECTED) {
+ /* Turn off insertion interruption at manual mode */
+ regmap_update_bits(regmap,
+ NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_INSERT_DIS,
+ NAU8824_IRQ_INSERT_DIS);
+ regmap_update_bits(regmap,
+ NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_INSERT_EN, 0);
+ /* detect microphone and jack type */
+ cancel_work_sync(&nau8824->jdet_work);
+ schedule_work(&nau8824->jdet_work);
+
+ /* Enable interruption for jack type detection at audo
+ * mode which can detect microphone and jack type.
+ */
+ nau8824_setup_auto_irq(nau8824);
+ }
+
+ if (!clear_irq)
+ clear_irq = active_irq;
+ /* clears the rightmost interruption */
+ regmap_write(regmap, NAU8824_REG_CLEAR_INT_REG, clear_irq);
+
+ if (event_mask)
+ snd_soc_jack_report(nau8824->jack, event, event_mask);
+
+ return IRQ_HANDLED;
+}
+
+static int nau8824_clock_check(struct nau8824 *nau8824,
+ int stream, int rate, int osr)
+{
+ int osrate;
+
+ if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ if (osr >= ARRAY_SIZE(osr_dac_sel))
+ return -EINVAL;
+ osrate = osr_dac_sel[osr].osr;
+ } else {
+ if (osr >= ARRAY_SIZE(osr_adc_sel))
+ return -EINVAL;
+ osrate = osr_adc_sel[osr].osr;
+ }
+
+ if (!osrate || rate * osr > CLK_DA_AD_MAX) {
+ dev_err(nau8824->dev, "exceed the maximum frequency of CLK_ADC or CLK_DAC\n");
+ return -EINVAL;
+ }
+
+ return 0;
+}
+
+static int nau8824_hw_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 nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ unsigned int val_len = 0, osr, ctrl_val, bclk_fs, bclk_div;
+
+ nau8824_sema_acquire(nau8824, HZ);
+
+ /* CLK_DAC or CLK_ADC = OSR * FS
+ * DAC or ADC clock frequency is defined as Over Sampling Rate (OSR)
+ * multiplied by the audio sample rate (Fs). Note that the OSR and Fs
+ * values must be selected such that the maximum frequency is less
+ * than 6.144 MHz.
+ */
+ nau8824->fs = params_rate(params);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+ regmap_read(nau8824->regmap,
+ NAU8824_REG_DAC_FILTER_CTRL_1, &osr);
+ osr &= NAU8824_DAC_OVERSAMPLE_MASK;
+ if (nau8824_clock_check(nau8824, substream->stream,
+ nau8824->fs, osr))
+ return -EINVAL;
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_DAC_SRC_MASK,
+ osr_dac_sel[osr].clk_src << NAU8824_CLK_DAC_SRC_SFT);
+ } else {
+ regmap_read(nau8824->regmap,
+ NAU8824_REG_ADC_FILTER_CTRL, &osr);
+ osr &= NAU8824_ADC_SYNC_DOWN_MASK;
+ if (nau8824_clock_check(nau8824, substream->stream,
+ nau8824->fs, osr))
+ return -EINVAL;
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_ADC_SRC_MASK,
+ osr_adc_sel[osr].clk_src << NAU8824_CLK_ADC_SRC_SFT);
+ }
+
+ /* make BCLK and LRC divde configuration if the codec as master. */
+ regmap_read(nau8824->regmap,
+ NAU8824_REG_PORT0_I2S_PCM_CTRL_2, &ctrl_val);
+ if (ctrl_val & NAU8824_I2S_MS_MASTER) {
+ /* get the bclk and fs ratio */
+ bclk_fs = snd_soc_params_to_bclk(params) / nau8824->fs;
+ if (bclk_fs <= 32)
+ bclk_div = 0x3;
+ else if (bclk_fs <= 64)
+ bclk_div = 0x2;
+ else if (bclk_fs <= 128)
+ bclk_div = 0x1;
+ else if (bclk_fs <= 256)
+ bclk_div = 0;
+ else
+ return -EINVAL;
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_PORT0_I2S_PCM_CTRL_2,
+ NAU8824_I2S_LRC_DIV_MASK | NAU8824_I2S_BLK_DIV_MASK,
+ (bclk_div << NAU8824_I2S_LRC_DIV_SFT) | bclk_div);
+ }
+
+ switch (params_width(params)) {
+ case 16:
+ val_len |= NAU8824_I2S_DL_16;
+ break;
+ case 20:
+ val_len |= NAU8824_I2S_DL_20;
+ break;
+ case 24:
+ val_len |= NAU8824_I2S_DL_24;
+ break;
+ case 32:
+ val_len |= NAU8824_I2S_DL_32;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1,
+ NAU8824_I2S_DL_MASK, val_len);
+
+ nau8824_sema_release(nau8824);
+
+ return 0;
+}
+
+static int nau8824_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+ nau8824_sema_acquire(nau8824, HZ);
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ ctrl2_val |= NAU8824_I2S_MS_MASTER;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ ctrl1_val |= NAU8824_I2S_BP_INV;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ ctrl1_val |= NAU8824_I2S_DF_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ ctrl1_val |= NAU8824_I2S_DF_LEFT;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ ctrl1_val |= NAU8824_I2S_DF_RIGTH;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ ctrl1_val |= NAU8824_I2S_DF_PCM_AB;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ ctrl1_val |= NAU8824_I2S_DF_PCM_AB;
+ ctrl1_val |= NAU8824_I2S_PCMB_EN;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_1,
+ NAU8824_I2S_DF_MASK | NAU8824_I2S_BP_MASK |
+ NAU8824_I2S_PCMB_EN, ctrl1_val);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_PORT0_I2S_PCM_CTRL_2,
+ NAU8824_I2S_MS_MASK, ctrl2_val);
+
+ nau8824_sema_release(nau8824);
+
+ return 0;
+}
+
+/**
+ * nau8824_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8824_calc_fll_param(unsigned int fll_in,
+ unsigned int fs, struct nau8824_fll *fll_param)
+{
+ u64 fvco, fvco_max;
+ unsigned int fref, i, fvco_sel;
+
+ /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+ * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+ * FREF = freq_in / NAU8824_FLL_REF_DIV_MASK
+ */
+ for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+ fref = fll_in / fll_pre_scalar[i].param;
+ if (fref <= NAU_FREF_MAX)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_pre_scalar))
+ return -EINVAL;
+ fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+ /* Choose the FLL ratio based on FREF */
+ for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+ if (fref >= fll_ratio[i].param)
+ break;
+ }
+ if (i == ARRAY_SIZE(fll_ratio))
+ return -EINVAL;
+ fll_param->ratio = fll_ratio[i].val;
+
+ /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+ * FDCO must be within the 90MHz - 124MHz or the FFL cannot be
+ * guaranteed across the full range of operation.
+ * FDCO = freq_out * 2 * mclk_src_scaling
+ */
+ fvco_max = 0;
+ fvco_sel = ARRAY_SIZE(mclk_src_scaling);
+ for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+ fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
+ if (fvco > NAU_FVCO_MIN && fvco < NAU_FVCO_MAX &&
+ fvco_max < fvco) {
+ fvco_max = fvco;
+ fvco_sel = i;
+ }
+ }
+ if (ARRAY_SIZE(mclk_src_scaling) == fvco_sel)
+ return -EINVAL;
+ fll_param->mclk_src = mclk_src_scaling[fvco_sel].val;
+
+ /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+ * input based on FDCO, FREF and FLL ratio.
+ */
+ fvco = div_u64(fvco_max << 16, fref * fll_param->ratio);
+ fll_param->fll_int = (fvco >> 16) & 0x3FF;
+ fll_param->fll_frac = fvco & 0xFFFF;
+ return 0;
+}
+
+static void nau8824_fll_apply(struct regmap *regmap,
+ struct nau8824_fll *fll_param)
+{
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK | NAU8824_CLK_MCLK_SRC_MASK,
+ NAU8824_CLK_SRC_MCLK | fll_param->mclk_src);
+ regmap_update_bits(regmap, NAU8824_REG_FLL1,
+ NAU8824_FLL_RATIO_MASK, fll_param->ratio);
+ /* FLL 16-bit fractional input */
+ regmap_write(regmap, NAU8824_REG_FLL2, fll_param->fll_frac);
+ /* FLL 10-bit integer input */
+ regmap_update_bits(regmap, NAU8824_REG_FLL3,
+ NAU8824_FLL_INTEGER_MASK, fll_param->fll_int);
+ /* FLL pre-scaler */
+ regmap_update_bits(regmap, NAU8824_REG_FLL4,
+ NAU8824_FLL_REF_DIV_MASK,
+ fll_param->clk_ref_div << NAU8824_FLL_REF_DIV_SFT);
+ /* select divided VCO input */
+ regmap_update_bits(regmap, NAU8824_REG_FLL5,
+ NAU8824_FLL_CLK_SW_MASK, NAU8824_FLL_CLK_SW_REF);
+ /* Disable free-running mode */
+ regmap_update_bits(regmap,
+ NAU8824_REG_FLL6, NAU8824_DCO_EN, 0);
+ if (fll_param->fll_frac) {
+ regmap_update_bits(regmap, NAU8824_REG_FLL5,
+ NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN |
+ NAU8824_FLL_FTR_SW_MASK,
+ NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN |
+ NAU8824_FLL_FTR_SW_FILTER);
+ regmap_update_bits(regmap, NAU8824_REG_FLL6,
+ NAU8824_SDM_EN, NAU8824_SDM_EN);
+ } else {
+ regmap_update_bits(regmap, NAU8824_REG_FLL5,
+ NAU8824_FLL_PDB_DAC_EN | NAU8824_FLL_LOOP_FTR_EN |
+ NAU8824_FLL_FTR_SW_MASK, NAU8824_FLL_FTR_SW_ACCU);
+ regmap_update_bits(regmap,
+ NAU8824_REG_FLL6, NAU8824_SDM_EN, 0);
+ }
+}
+
+/* freq_out must be 256*Fs in order to achieve the best performance */
+static int nau8824_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+ unsigned int freq_in, unsigned int freq_out)
+{
+ struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ struct nau8824_fll fll_param;
+ int ret, fs;
+
+ fs = freq_out / 256;
+ ret = nau8824_calc_fll_param(freq_in, fs, &fll_param);
+ if (ret < 0) {
+ dev_err(nau8824->dev, "Unsupported input clock %d\n", freq_in);
+ return ret;
+ }
+ dev_dbg(nau8824->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+ fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+ fll_param.fll_int, fll_param.clk_ref_div);
+
+ nau8824_fll_apply(nau8824->regmap, &fll_param);
+ mdelay(2);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_VCO);
+
+ return 0;
+}
+
+static int nau8824_config_sysclk(struct nau8824 *nau8824,
+ int clk_id, unsigned int freq)
+{
+ struct regmap *regmap = nau8824->regmap;
+
+ switch (clk_id) {
+ case NAU8824_CLK_DIS:
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8824_REG_FLL6,
+ NAU8824_DCO_EN, 0);
+ break;
+
+ case NAU8824_CLK_MCLK:
+ nau8824_sema_acquire(nau8824, HZ);
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_MCLK);
+ regmap_update_bits(regmap, NAU8824_REG_FLL6,
+ NAU8824_DCO_EN, 0);
+ nau8824_sema_release(nau8824);
+ break;
+
+ case NAU8824_CLK_INTERNAL:
+ regmap_update_bits(regmap, NAU8824_REG_FLL6,
+ NAU8824_DCO_EN, NAU8824_DCO_EN);
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_SRC_MASK, NAU8824_CLK_SRC_VCO);
+ break;
+
+ case NAU8824_CLK_FLL_MCLK:
+ nau8824_sema_acquire(nau8824, HZ);
+ regmap_update_bits(regmap, NAU8824_REG_FLL3,
+ NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_MCLK);
+ nau8824_sema_release(nau8824);
+ break;
+
+ case NAU8824_CLK_FLL_BLK:
+ nau8824_sema_acquire(nau8824, HZ);
+ regmap_update_bits(regmap, NAU8824_REG_FLL3,
+ NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_BLK);
+ nau8824_sema_release(nau8824);
+ break;
+
+ case NAU8824_CLK_FLL_FS:
+ nau8824_sema_acquire(nau8824, HZ);
+ regmap_update_bits(regmap, NAU8824_REG_FLL3,
+ NAU8824_FLL_CLK_SRC_MASK, NAU8824_FLL_CLK_SRC_FS);
+ nau8824_sema_release(nau8824);
+ break;
+
+ default:
+ dev_err(nau8824->dev, "Invalid clock id (%d)\n", clk_id);
+ return -EINVAL;
+ }
+
+ dev_dbg(nau8824->dev, "Sysclk is %dHz and clock id is %d\n", freq,
+ clk_id);
+
+ return 0;
+}
+
+static int nau8824_set_sysclk(struct snd_soc_codec *codec,
+ int clk_id, int source, unsigned int freq, int dir)
+{
+ struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+ return nau8824_config_sysclk(nau8824, clk_id, freq);
+}
+
+static void nau8824_resume_setup(struct nau8824 *nau8824)
+{
+ nau8824_config_sysclk(nau8824, NAU8824_CLK_DIS, 0);
+ if (nau8824->irq) {
+ /* Clear all interruption status */
+ nau8824_int_status_clear_all(nau8824->regmap);
+ /* Enable jack detection at sleep mode, insertion detection,
+ * and ejection detection.
+ */
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN,
+ NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING,
+ NAU8824_IRQ_EJECT_DIS | NAU8824_IRQ_INSERT_DIS, 0);
+ }
+}
+
+static int nau8824_set_bias_level(struct snd_soc_codec *codec,
+ enum snd_soc_bias_level level)
+{
+ struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+ switch (level) {
+ case SND_SOC_BIAS_ON:
+ break;
+
+ case SND_SOC_BIAS_PREPARE:
+ break;
+
+ case SND_SOC_BIAS_STANDBY:
+ if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+ /* Setup codec configuration after resume */
+ nau8824_resume_setup(nau8824);
+ }
+ break;
+
+ case SND_SOC_BIAS_OFF:
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING, 0x3ff, 0x3ff);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, 0);
+ break;
+ }
+
+ return 0;
+}
+
+static int nau8824_codec_probe(struct snd_soc_codec *codec)
+{
+ struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+ nau8824->dapm = dapm;
+
+ return 0;
+}
+
+static int __maybe_unused nau8824_suspend(struct snd_soc_codec *codec)
+{
+ struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+ if (nau8824->irq) {
+ disable_irq(nau8824->irq);
+ snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+ }
+ regcache_cache_only(nau8824->regmap, true);
+ regcache_mark_dirty(nau8824->regmap);
+
+ return 0;
+}
+
+static int __maybe_unused nau8824_resume(struct snd_soc_codec *codec)
+{
+ struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+
+ regcache_cache_only(nau8824->regmap, false);
+ regcache_sync(nau8824->regmap);
+ if (nau8824->irq) {
+ /* Hold semaphore to postpone playback happening
+ * until jack detection done.
+ */
+ nau8824_sema_acquire(nau8824, 0);
+ enable_irq(nau8824->irq);
+ }
+
+ return 0;
+}
+
+static struct snd_soc_codec_driver nau8824_codec_driver = {
+ .probe = nau8824_codec_probe,
+ .set_sysclk = nau8824_set_sysclk,
+ .set_pll = nau8824_set_pll,
+ .set_bias_level = nau8824_set_bias_level,
+ .suspend = nau8824_suspend,
+ .resume = nau8824_resume,
+ .suspend_bias_off = true,
+
+ .component_driver = {
+ .controls = nau8824_snd_controls,
+ .num_controls = ARRAY_SIZE(nau8824_snd_controls),
+ .dapm_widgets = nau8824_dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(nau8824_dapm_widgets),
+ .dapm_routes = nau8824_dapm_routes,
+ .num_dapm_routes = ARRAY_SIZE(nau8824_dapm_routes),
+ },
+};
+
+static const struct snd_soc_dai_ops nau8824_dai_ops = {
+ .hw_params = nau8824_hw_params,
+ .set_fmt = nau8824_set_fmt,
+};
+
+#define NAU8824_RATES SNDRV_PCM_RATE_8000_192000
+#define NAU8824_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+ | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8824_dai = {
+ .name = NAU8824_CODEC_DAI,
+ .playback = {
+ .stream_name = "Playback",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8824_RATES,
+ .formats = NAU8824_FORMATS,
+ },
+ .capture = {
+ .stream_name = "Capture",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = NAU8824_RATES,
+ .formats = NAU8824_FORMATS,
+ },
+ .ops = &nau8824_dai_ops,
+};
+
+static const struct regmap_config nau8824_regmap_config = {
+ .val_bits = NAU8824_REG_ADDR_LEN,
+ .reg_bits = NAU8824_REG_DATA_LEN,
+
+ .max_register = NAU8824_REG_MAX,
+ .readable_reg = nau8824_readable_reg,
+ .writeable_reg = nau8824_writeable_reg,
+ .volatile_reg = nau8824_volatile_reg,
+
+ .cache_type = REGCACHE_RBTREE,
+ .reg_defaults = nau8824_reg_defaults,
+ .num_reg_defaults = ARRAY_SIZE(nau8824_reg_defaults),
+};
+
+/**
+ * nau8824_enable_jack_detect - Specify a jack for event reporting
+ *
+ * @component: component to register the jack with
+ * @jack: jack to use to report headset and button events on
+ *
+ * After this function has been called the headset insert/remove and button
+ * events will be routed to the given jack. Jack can be null to stop
+ * reporting.
+ */
+int nau8824_enable_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack)
+{
+ struct nau8824 *nau8824 = snd_soc_codec_get_drvdata(codec);
+ int ret;
+
+ nau8824->jack = jack;
+ /* Initiate jack detection work queue */
+ INIT_WORK(&nau8824->jdet_work, nau8824_jdet_work);
+ ret = devm_request_threaded_irq(nau8824->dev, nau8824->irq, NULL,
+ nau8824_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+ "nau8824", nau8824);
+ if (ret) {
+ dev_err(nau8824->dev, "Cannot request irq %d (%d)\n",
+ nau8824->irq, ret);
+ }
+
+ return ret;
+}
+EXPORT_SYMBOL_GPL(nau8824_enable_jack_detect);
+
+static void nau8824_reset_chip(struct regmap *regmap)
+{
+ regmap_write(regmap, NAU8824_REG_RESET, 0x00);
+ regmap_write(regmap, NAU8824_REG_RESET, 0x00);
+}
+
+static void nau8824_setup_buttons(struct nau8824 *nau8824)
+{
+ struct regmap *regmap = nau8824->regmap;
+
+ regmap_update_bits(regmap, NAU8824_REG_SAR_ADC,
+ NAU8824_SAR_TRACKING_GAIN_MASK,
+ nau8824->sar_voltage << NAU8824_SAR_TRACKING_GAIN_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_SAR_ADC,
+ NAU8824_SAR_COMPARE_TIME_MASK,
+ nau8824->sar_compare_time << NAU8824_SAR_COMPARE_TIME_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_SAR_ADC,
+ NAU8824_SAR_SAMPLING_TIME_MASK,
+ nau8824->sar_sampling_time << NAU8824_SAR_SAMPLING_TIME_SFT);
+
+ regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT,
+ NAU8824_LEVELS_NR_MASK,
+ (nau8824->sar_threshold_num - 1) << NAU8824_LEVELS_NR_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT,
+ NAU8824_HYSTERESIS_MASK,
+ nau8824->sar_hysteresis << NAU8824_HYSTERESIS_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_VDET_COEFFICIENT,
+ NAU8824_SHORTKEY_DEBOUNCE_MASK,
+ nau8824->key_debounce << NAU8824_SHORTKEY_DEBOUNCE_SFT);
+
+ regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_1,
+ (nau8824->sar_threshold[0] << 8) | nau8824->sar_threshold[1]);
+ regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_2,
+ (nau8824->sar_threshold[2] << 8) | nau8824->sar_threshold[3]);
+ regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_3,
+ (nau8824->sar_threshold[4] << 8) | nau8824->sar_threshold[5]);
+ regmap_write(regmap, NAU8824_REG_VDET_THRESHOLD_4,
+ (nau8824->sar_threshold[6] << 8) | nau8824->sar_threshold[7]);
+}
+
+static void nau8824_init_regs(struct nau8824 *nau8824)
+{
+ struct regmap *regmap = nau8824->regmap;
+
+ /* Enable Bias/VMID/VMID Tieoff */
+ regmap_update_bits(regmap, NAU8824_REG_BIAS_ADJ,
+ NAU8824_VMID | NAU8824_VMID_SEL_MASK, NAU8824_VMID |
+ (nau8824->vref_impedance << NAU8824_VMID_SEL_SFT));
+ regmap_update_bits(regmap, NAU8824_REG_BOOST,
+ NAU8824_GLOBAL_BIAS_EN, NAU8824_GLOBAL_BIAS_EN);
+ mdelay(2);
+ regmap_update_bits(regmap, NAU8824_REG_MIC_BIAS,
+ NAU8824_MICBIAS_VOLTAGE_MASK, nau8824->micbias_voltage);
+ /* Disable Boost Driver, Automatic Short circuit protection enable */
+ regmap_update_bits(regmap, NAU8824_REG_BOOST,
+ NAU8824_PRECHARGE_DIS | NAU8824_HP_BOOST_DIS |
+ NAU8824_HP_BOOST_G_DIS | NAU8824_SHORT_SHUTDOWN_EN,
+ NAU8824_PRECHARGE_DIS | NAU8824_HP_BOOST_DIS |
+ NAU8824_HP_BOOST_G_DIS | NAU8824_SHORT_SHUTDOWN_EN);
+ /* Scaling for ADC and DAC clock */
+ regmap_update_bits(regmap, NAU8824_REG_CLK_DIVIDER,
+ NAU8824_CLK_ADC_SRC_MASK | NAU8824_CLK_DAC_SRC_MASK,
+ (0x1 << NAU8824_CLK_ADC_SRC_SFT) |
+ (0x1 << NAU8824_CLK_DAC_SRC_SFT));
+ regmap_update_bits(regmap, NAU8824_REG_DAC_MUTE_CTRL,
+ NAU8824_DAC_ZC_EN, NAU8824_DAC_ZC_EN);
+ regmap_update_bits(regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_DAC_CH1_EN | NAU8824_DAC_CH0_EN |
+ NAU8824_ADC_CH0_EN | NAU8824_ADC_CH1_EN |
+ NAU8824_ADC_CH2_EN | NAU8824_ADC_CH3_EN,
+ NAU8824_DAC_CH1_EN | NAU8824_DAC_CH0_EN |
+ NAU8824_ADC_CH0_EN | NAU8824_ADC_CH1_EN |
+ NAU8824_ADC_CH2_EN | NAU8824_ADC_CH3_EN);
+ regmap_update_bits(regmap, NAU8824_REG_CLK_GATING_ENA,
+ NAU8824_CLK_ADC_CH23_EN | NAU8824_CLK_ADC_CH01_EN |
+ NAU8824_CLK_DAC_CH1_EN | NAU8824_CLK_DAC_CH0_EN |
+ NAU8824_CLK_I2S_EN | NAU8824_CLK_GAIN_EN |
+ NAU8824_CLK_SAR_EN | NAU8824_CLK_DMIC_CH23_EN,
+ NAU8824_CLK_ADC_CH23_EN | NAU8824_CLK_ADC_CH01_EN |
+ NAU8824_CLK_DAC_CH1_EN | NAU8824_CLK_DAC_CH0_EN |
+ NAU8824_CLK_I2S_EN | NAU8824_CLK_GAIN_EN |
+ NAU8824_CLK_SAR_EN | NAU8824_CLK_DMIC_CH23_EN);
+ /* Class G timer 64ms */
+ regmap_update_bits(regmap, NAU8824_REG_CLASSG,
+ NAU8824_CLASSG_TIMER_MASK,
+ 0x20 << NAU8824_CLASSG_TIMER_SFT);
+ regmap_update_bits(regmap, NAU8824_REG_TRIM_SETTINGS,
+ NAU8824_DRV_CURR_INC, NAU8824_DRV_CURR_INC);
+ /* Disable DACR/L power */
+ regmap_update_bits(regmap, NAU8824_REG_CHARGE_PUMP_CONTROL,
+ NAU8824_SPKR_PULL_DOWN | NAU8824_SPKL_PULL_DOWN |
+ NAU8824_POWER_DOWN_DACR | NAU8824_POWER_DOWN_DACL,
+ NAU8824_SPKR_PULL_DOWN | NAU8824_SPKL_PULL_DOWN |
+ NAU8824_POWER_DOWN_DACR | NAU8824_POWER_DOWN_DACL);
+ /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input
+ * signal to avoid any glitches due to power up transients in both
+ * the analog and digital DAC circuit.
+ */
+ regmap_update_bits(regmap, NAU8824_REG_ENABLE_LO,
+ NAU8824_TEST_DAC_EN, NAU8824_TEST_DAC_EN);
+ /* Config L/R channel */
+ regmap_update_bits(regmap, NAU8824_REG_DAC_CH0_DGAIN_CTRL,
+ NAU8824_DAC_CH0_SEL_MASK, NAU8824_DAC_CH0_SEL_I2S0);
+ regmap_update_bits(regmap, NAU8824_REG_DAC_CH1_DGAIN_CTRL,
+ NAU8824_DAC_CH1_SEL_MASK, NAU8824_DAC_CH1_SEL_I2S1);
+ regmap_update_bits(regmap, NAU8824_REG_ENABLE_LO,
+ NAU8824_DACR_HPR_EN | NAU8824_DACL_HPL_EN,
+ NAU8824_DACR_HPR_EN | NAU8824_DACL_HPL_EN);
+ /* Default oversampling/decimations settings are unusable
+ * (audible hiss). Set it to something better.
+ */
+ regmap_update_bits(regmap, NAU8824_REG_ADC_FILTER_CTRL,
+ NAU8824_ADC_SYNC_DOWN_MASK, NAU8824_ADC_SYNC_DOWN_64);
+ regmap_update_bits(regmap, NAU8824_REG_DAC_FILTER_CTRL_1,
+ NAU8824_DAC_CICCLP_OFF | NAU8824_DAC_OVERSAMPLE_MASK,
+ NAU8824_DAC_CICCLP_OFF | NAU8824_DAC_OVERSAMPLE_64);
+ /* DAC clock delay 2ns, VREF */
+ regmap_update_bits(regmap, NAU8824_REG_RDAC,
+ NAU8824_RDAC_CLK_DELAY_MASK | NAU8824_RDAC_VREF_MASK,
+ (0x2 << NAU8824_RDAC_CLK_DELAY_SFT) |
+ (0x3 << NAU8824_RDAC_VREF_SFT));
+ /* PGA input mode selection */
+ regmap_update_bits(regmap, NAU8824_REG_FEPGA,
+ NAU8824_FEPGA_MODEL_SHORT_EN | NAU8824_FEPGA_MODER_SHORT_EN,
+ NAU8824_FEPGA_MODEL_SHORT_EN | NAU8824_FEPGA_MODER_SHORT_EN);
+ /* Digital microphone control */
+ regmap_update_bits(regmap, NAU8824_REG_ANALOG_CONTROL_1,
+ NAU8824_DMIC_CLK_DRV_STRG | NAU8824_DMIC_CLK_SLEW_FAST,
+ NAU8824_DMIC_CLK_DRV_STRG | NAU8824_DMIC_CLK_SLEW_FAST);
+ regmap_update_bits(regmap, NAU8824_REG_JACK_DET_CTRL,
+ NAU8824_JACK_LOGIC,
+ /* jkdet_polarity - 1 is for active-low */
+ nau8824->jkdet_polarity ? 0 : NAU8824_JACK_LOGIC);
+ regmap_update_bits(regmap,
+ NAU8824_REG_JACK_DET_CTRL, NAU8824_JACK_EJECT_DT_MASK,
+ (nau8824->jack_eject_debounce << NAU8824_JACK_EJECT_DT_SFT));
+ if (nau8824->sar_threshold_num)
+ nau8824_setup_buttons(nau8824);
+}
+
+static int nau8824_setup_irq(struct nau8824 *nau8824)
+{
+ /* Disable interruption before codec initiation done */
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_ENA_CTRL,
+ NAU8824_JD_SLEEP_MODE, NAU8824_JD_SLEEP_MODE);
+ regmap_update_bits(nau8824->regmap,
+ NAU8824_REG_INTERRUPT_SETTING, 0x3ff, 0x3ff);
+ regmap_update_bits(nau8824->regmap, NAU8824_REG_INTERRUPT_SETTING_1,
+ NAU8824_IRQ_EJECT_EN | NAU8824_IRQ_INSERT_EN, 0);
+
+ return 0;
+}
+
+static void nau8824_print_device_properties(struct nau8824 *nau8824)
+{
+ struct device *dev = nau8824->dev;
+ int i;
+
+ dev_dbg(dev, "jkdet-polarity: %d\n", nau8824->jkdet_polarity);
+ dev_dbg(dev, "micbias-voltage: %d\n", nau8824->micbias_voltage);
+ dev_dbg(dev, "vref-impedance: %d\n", nau8824->vref_impedance);
+
+ dev_dbg(dev, "sar-threshold-num: %d\n", nau8824->sar_threshold_num);
+ for (i = 0; i < nau8824->sar_threshold_num; i++)
+ dev_dbg(dev, "sar-threshold[%d]=%x\n", i,
+ nau8824->sar_threshold[i]);
+
+ dev_dbg(dev, "sar-hysteresis: %d\n", nau8824->sar_hysteresis);
+ dev_dbg(dev, "sar-voltage: %d\n", nau8824->sar_voltage);
+ dev_dbg(dev, "sar-compare-time: %d\n", nau8824->sar_compare_time);
+ dev_dbg(dev, "sar-sampling-time: %d\n", nau8824->sar_sampling_time);
+ dev_dbg(dev, "short-key-debounce: %d\n", nau8824->key_debounce);
+ dev_dbg(dev, "jack-eject-debounce: %d\n",
+ nau8824->jack_eject_debounce);
+}
+
+static int nau8824_read_device_properties(struct device *dev,
+ struct nau8824 *nau8824) {
+ int ret;
+
+ ret = device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+ &nau8824->jkdet_polarity);
+ if (ret)
+ nau8824->jkdet_polarity = 1;
+ ret = device_property_read_u32(dev, "nuvoton,micbias-voltage",
+ &nau8824->micbias_voltage);
+ if (ret)
+ nau8824->micbias_voltage = 6;
+ ret = device_property_read_u32(dev, "nuvoton,vref-impedance",
+ &nau8824->vref_impedance);
+ if (ret)
+ nau8824->vref_impedance = 2;
+ ret = device_property_read_u32(dev, "nuvoton,sar-threshold-num",
+ &nau8824->sar_threshold_num);
+ if (ret)
+ nau8824->sar_threshold_num = 4;
+ ret = device_property_read_u32_array(dev, "nuvoton,sar-threshold",
+ nau8824->sar_threshold, nau8824->sar_threshold_num);
+ if (ret) {
+ nau8824->sar_threshold[0] = 0x0a;
+ nau8824->sar_threshold[1] = 0x14;
+ nau8824->sar_threshold[2] = 0x26;
+ nau8824->sar_threshold[3] = 0x73;
+ }
+ ret = device_property_read_u32(dev, "nuvoton,sar-hysteresis",
+ &nau8824->sar_hysteresis);
+ if (ret)
+ nau8824->sar_hysteresis = 0;
+ ret = device_property_read_u32(dev, "nuvoton,sar-voltage",
+ &nau8824->sar_voltage);
+ if (ret)
+ nau8824->sar_voltage = 6;
+ ret = device_property_read_u32(dev, "nuvoton,sar-compare-time",
+ &nau8824->sar_compare_time);
+ if (ret)
+ nau8824->sar_compare_time = 1;
+ ret = device_property_read_u32(dev, "nuvoton,sar-sampling-time",
+ &nau8824->sar_sampling_time);
+ if (ret)
+ nau8824->sar_sampling_time = 1;
+ ret = device_property_read_u32(dev, "nuvoton,short-key-debounce",
+ &nau8824->key_debounce);
+ if (ret)
+ nau8824->key_debounce = 0;
+ ret = device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+ &nau8824->jack_eject_debounce);
+ if (ret)
+ nau8824->jack_eject_debounce = 1;
+
+ return 0;
+}
+
+static int nau8824_i2c_probe(struct i2c_client *i2c,
+ const struct i2c_device_id *id)
+{
+ struct device *dev = &i2c->dev;
+ struct nau8824 *nau8824 = dev_get_platdata(dev);
+ int ret, value;
+
+ if (!nau8824) {
+ nau8824 = devm_kzalloc(dev, sizeof(*nau8824), GFP_KERNEL);
+ if (!nau8824)
+ return -ENOMEM;
+ ret = nau8824_read_device_properties(dev, nau8824);
+ if (ret)
+ return ret;
+ }
+ i2c_set_clientdata(i2c, nau8824);
+
+ nau8824->regmap = devm_regmap_init_i2c(i2c, &nau8824_regmap_config);
+ if (IS_ERR(nau8824->regmap))
+ return PTR_ERR(nau8824->regmap);
+ nau8824->dev = dev;
+ nau8824->irq = i2c->irq;
+ sema_init(&nau8824->jd_sem, 1);
+
+ nau8824_print_device_properties(nau8824);
+
+ ret = regmap_read(nau8824->regmap, NAU8824_REG_I2C_DEVICE_ID, &value);
+ if (ret < 0) {
+ dev_err(dev, "Failed to read device id from the NAU8824: %d\n",
+ ret);
+ return ret;
+ }
+ nau8824_reset_chip(nau8824->regmap);
+ nau8824_init_regs(nau8824);
+
+ if (i2c->irq)
+ nau8824_setup_irq(nau8824);
+
+ return snd_soc_register_codec(dev,
+ &nau8824_codec_driver, &nau8824_dai, 1);
+}
+
+
+static int nau8824_i2c_remove(struct i2c_client *client)
+{
+ snd_soc_unregister_codec(&client->dev);
+ return 0;
+}
+
+static const struct i2c_device_id nau8824_i2c_ids[] = {
+ { "nau8824", 0 },
+ { }
+};
+MODULE_DEVICE_TABLE(i2c, nau8824_i2c_ids);
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8824_of_ids[] = {
+ { .compatible = "nuvoton,nau8824", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, nau8824_of_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8824_acpi_match[] = {
+ { "10508824", 0 },
+ {},
+};
+MODULE_DEVICE_TABLE(acpi, nau8824_acpi_match);
+#endif
+
+static struct i2c_driver nau8824_i2c_driver = {
+ .driver = {
+ .name = "nau8824",
+ .of_match_table = of_match_ptr(nau8824_of_ids),
+ .acpi_match_table = ACPI_PTR(nau8824_acpi_match),
+ },
+ .probe = nau8824_i2c_probe,
+ .remove = nau8824_i2c_remove,
+ .id_table = nau8824_i2c_ids,
+};
+module_i2c_driver(nau8824_i2c_driver);
+
+
+MODULE_DESCRIPTION("ASoC NAU88L24 driver");
+MODULE_AUTHOR("John Hsu <KCHSU0@nuvoton.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/nau8824.h b/sound/soc/codecs/nau8824.h
new file mode 100644
index 000000000000..87ac9a382aed
--- /dev/null
+++ b/sound/soc/codecs/nau8824.h
@@ -0,0 +1,466 @@
+/*
+ * NAU88L24 ALSA SoC audio driver
+ *
+ * Copyright 2016 Nuvoton Technology Corp.
+ * Author: John Hsu <KCHSU0@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 __NAU8824_H__
+#define __NAU8824_H__
+
+#define NAU8824_REG_RESET 0x00
+#define NAU8824_REG_ENA_CTRL 0x01
+#define NAU8824_REG_CLK_GATING_ENA 0x02
+#define NAU8824_REG_CLK_DIVIDER 0x03
+#define NAU8824_REG_FLL1 0x04
+#define NAU8824_REG_FLL2 0x05
+#define NAU8824_REG_FLL3 0x06
+#define NAU8824_REG_FLL4 0x07
+#define NAU8824_REG_FLL5 0x08
+#define NAU8824_REG_FLL6 0x09
+#define NAU8824_REG_FLL_VCO_RSV 0x0A
+#define NAU8824_REG_JACK_DET_CTRL 0x0D
+#define NAU8824_REG_INTERRUPT_SETTING_1 0x0F
+#define NAU8824_REG_IRQ 0x10
+#define NAU8824_REG_CLEAR_INT_REG 0x11
+#define NAU8824_REG_INTERRUPT_SETTING 0x12
+#define NAU8824_REG_SAR_ADC 0x13
+#define NAU8824_REG_VDET_COEFFICIENT 0x14
+#define NAU8824_REG_VDET_THRESHOLD_1 0x15
+#define NAU8824_REG_VDET_THRESHOLD_2 0x16
+#define NAU8824_REG_VDET_THRESHOLD_3 0x17
+#define NAU8824_REG_VDET_THRESHOLD_4 0x18
+#define NAU8824_REG_GPIO_SEL 0x1A
+#define NAU8824_REG_PORT0_I2S_PCM_CTRL_1 0x1C
+#define NAU8824_REG_PORT0_I2S_PCM_CTRL_2 0x1D
+#define NAU8824_REG_PORT0_LEFT_TIME_SLOT 0x1E
+#define NAU8824_REG_PORT0_RIGHT_TIME_SLOT 0x1F
+#define NAU8824_REG_TDM_CTRL 0x20
+#define NAU8824_REG_ADC_HPF_FILTER 0x23
+#define NAU8824_REG_ADC_FILTER_CTRL 0x24
+#define NAU8824_REG_DAC_FILTER_CTRL_1 0x25
+#define NAU8824_REG_DAC_FILTER_CTRL_2 0x26
+#define NAU8824_REG_NOTCH_FILTER_1 0x27
+#define NAU8824_REG_NOTCH_FILTER_2 0x28
+#define NAU8824_REG_EQ1_LOW 0x29
+#define NAU8824_REG_EQ2_EQ3 0x2A
+#define NAU8824_REG_EQ4_EQ5 0x2B
+#define NAU8824_REG_ADC_CH0_DGAIN_CTRL 0x2D
+#define NAU8824_REG_ADC_CH1_DGAIN_CTRL 0x2E
+#define NAU8824_REG_ADC_CH2_DGAIN_CTRL 0x2F
+#define NAU8824_REG_ADC_CH3_DGAIN_CTRL 0x30
+#define NAU8824_REG_DAC_MUTE_CTRL 0x31
+#define NAU8824_REG_DAC_CH0_DGAIN_CTRL 0x32
+#define NAU8824_REG_DAC_CH1_DGAIN_CTRL 0x33
+#define NAU8824_REG_ADC_TO_DAC_ST 0x34
+#define NAU8824_REG_DRC_KNEE_IP12_ADC_CH01 0x38
+#define NAU8824_REG_DRC_KNEE_IP34_ADC_CH01 0x39
+#define NAU8824_REG_DRC_SLOPE_ADC_CH01 0x3A
+#define NAU8824_REG_DRC_ATKDCY_ADC_CH01 0x3B
+#define NAU8824_REG_DRC_KNEE_IP12_ADC_CH23 0x3C
+#define NAU8824_REG_DRC_KNEE_IP34_ADC_CH23 0x3D
+#define NAU8824_REG_DRC_SLOPE_ADC_CH23 0x3E
+#define NAU8824_REG_DRC_ATKDCY_ADC_CH23 0x3F
+#define NAU8824_REG_DRC_GAINL_ADC0 0x40
+#define NAU8824_REG_DRC_GAINL_ADC1 0x41
+#define NAU8824_REG_DRC_GAINL_ADC2 0x42
+#define NAU8824_REG_DRC_GAINL_ADC3 0x43
+#define NAU8824_REG_DRC_KNEE_IP12_DAC 0x45
+#define NAU8824_REG_DRC_KNEE_IP34_DAC 0x46
+#define NAU8824_REG_DRC_SLOPE_DAC 0x47
+#define NAU8824_REG_DRC_ATKDCY_DAC 0x48
+#define NAU8824_REG_DRC_GAIN_DAC_CH0 0x49
+#define NAU8824_REG_DRC_GAIN_DAC_CH1 0x4A
+#define NAU8824_REG_MODE 0x4C
+#define NAU8824_REG_MODE1 0x4D
+#define NAU8824_REG_MODE2 0x4E
+#define NAU8824_REG_CLASSG 0x50
+#define NAU8824_REG_OTP_EFUSE 0x51
+#define NAU8824_REG_OTPDOUT_1 0x53
+#define NAU8824_REG_OTPDOUT_2 0x54
+#define NAU8824_REG_MISC_CTRL 0x55
+#define NAU8824_REG_I2C_TIMEOUT 0x56
+#define NAU8824_REG_TEST_MODE 0x57
+#define NAU8824_REG_I2C_DEVICE_ID 0x58
+#define NAU8824_REG_SAR_ADC_DATA_OUT 0x59
+#define NAU8824_REG_BIAS_ADJ 0x66
+#define NAU8824_REG_PGA_GAIN 0x67
+#define NAU8824_REG_TRIM_SETTINGS 0x68
+#define NAU8824_REG_ANALOG_CONTROL_1 0x69
+#define NAU8824_REG_ANALOG_CONTROL_2 0x6A
+#define NAU8824_REG_ENABLE_LO 0x6B
+#define NAU8824_REG_GAIN_LO 0x6C
+#define NAU8824_REG_CLASSD_GAIN_1 0x6D
+#define NAU8824_REG_CLASSD_GAIN_2 0x6E
+#define NAU8824_REG_ANALOG_ADC_1 0x71
+#define NAU8824_REG_ANALOG_ADC_2 0x72
+#define NAU8824_REG_RDAC 0x73
+#define NAU8824_REG_MIC_BIAS 0x74
+#define NAU8824_REG_HS_VOLUME_CONTROL 0x75
+#define NAU8824_REG_BOOST 0x76
+#define NAU8824_REG_FEPGA 0x77
+#define NAU8824_REG_FEPGA_II 0x78
+#define NAU8824_REG_FEPGA_SE 0x79
+#define NAU8824_REG_FEPGA_ATTENUATION 0x7A
+#define NAU8824_REG_ATT_PORT0 0x7B
+#define NAU8824_REG_ATT_PORT1 0x7C
+#define NAU8824_REG_POWER_UP_CONTROL 0x7F
+#define NAU8824_REG_CHARGE_PUMP_CONTROL 0x80
+#define NAU8824_REG_CHARGE_PUMP_INPUT 0x81
+#define NAU8824_REG_MAX NAU8824_REG_CHARGE_PUMP_INPUT
+/* 16-bit control register address, and 16-bits control register data */
+#define NAU8824_REG_ADDR_LEN 16
+#define NAU8824_REG_DATA_LEN 16
+
+
+/* ENA_CTRL (0x1) */
+#define NAU8824_DMIC_LCH_EDGE_CH23 (0x1 << 12)
+#define NAU8824_DMIC_LCH_EDGE_CH01 (0x1 << 11)
+#define NAU8824_JD_SLEEP_MODE (0x1 << 10)
+#define NAU8824_ADC_CH3_DMIC_SFT 9
+#define NAU8824_ADC_CH3_DMIC_EN (0x1 << NAU8824_ADC_CH3_DMIC_SFT)
+#define NAU8824_ADC_CH2_DMIC_SFT 8
+#define NAU8824_ADC_CH2_DMIC_EN (0x1 << NAU8824_ADC_CH2_DMIC_SFT)
+#define NAU8824_ADC_CH1_DMIC_SFT 7
+#define NAU8824_ADC_CH1_DMIC_EN (0x1 << NAU8824_ADC_CH1_DMIC_SFT)
+#define NAU8824_ADC_CH0_DMIC_SFT 6
+#define NAU8824_ADC_CH0_DMIC_EN (0x1 << NAU8824_ADC_CH0_DMIC_SFT)
+#define NAU8824_DAC_CH1_EN (0x1 << 5)
+#define NAU8824_DAC_CH0_EN (0x1 << 4)
+#define NAU8824_ADC_CH3_EN (0x1 << 3)
+#define NAU8824_ADC_CH2_EN (0x1 << 2)
+#define NAU8824_ADC_CH1_EN (0x1 << 1)
+#define NAU8824_ADC_CH0_EN 0x1
+
+/* CLK_GATING_ENA (0x02) */
+#define NAU8824_CLK_ADC_CH23_EN (0x1 << 15)
+#define NAU8824_CLK_ADC_CH01_EN (0x1 << 14)
+#define NAU8824_CLK_DAC_CH1_EN (0x1 << 13)
+#define NAU8824_CLK_DAC_CH0_EN (0x1 << 12)
+#define NAU8824_CLK_I2S_EN (0x1 << 7)
+#define NAU8824_CLK_GAIN_EN (0x1 << 5)
+#define NAU8824_CLK_SAR_EN (0x1 << 3)
+#define NAU8824_CLK_DMIC_CH23_EN (0x1 << 1)
+
+/* CLK_DIVIDER (0x3) */
+#define NAU8824_CLK_SRC_SFT 15
+#define NAU8824_CLK_SRC_MASK (1 << NAU8824_CLK_SRC_SFT)
+#define NAU8824_CLK_SRC_VCO (1 << NAU8824_CLK_SRC_SFT)
+#define NAU8824_CLK_SRC_MCLK (0 << NAU8824_CLK_SRC_SFT)
+#define NAU8824_CLK_MCLK_SRC_MASK (0xf << 0)
+#define NAU8824_CLK_DMIC_SRC_SFT 10
+#define NAU8824_CLK_DMIC_SRC_MASK (0x7 << NAU8824_CLK_DMIC_SRC_SFT)
+#define NAU8824_CLK_ADC_SRC_SFT 6
+#define NAU8824_CLK_ADC_SRC_MASK (0x3 << NAU8824_CLK_ADC_SRC_SFT)
+#define NAU8824_CLK_DAC_SRC_SFT 4
+#define NAU8824_CLK_DAC_SRC_MASK (0x3 << NAU8824_CLK_DAC_SRC_SFT)
+
+/* FLL1 (0x04) */
+#define NAU8824_FLL_RATIO_MASK (0x7f << 0)
+
+/* FLL3 (0x06) */
+#define NAU8824_FLL_INTEGER_MASK (0x3ff << 0)
+#define NAU8824_FLL_CLK_SRC_SFT 10
+#define NAU8824_FLL_CLK_SRC_MASK (0x3 << NAU8824_FLL_CLK_SRC_SFT)
+#define NAU8824_FLL_CLK_SRC_MCLK (0 << NAU8824_FLL_CLK_SRC_SFT)
+#define NAU8824_FLL_CLK_SRC_BLK (0x2 << NAU8824_FLL_CLK_SRC_SFT)
+#define NAU8824_FLL_CLK_SRC_FS (0x3 << NAU8824_FLL_CLK_SRC_SFT)
+
+/* FLL4 (0x07) */
+#define NAU8824_FLL_REF_DIV_SFT 10
+#define NAU8824_FLL_REF_DIV_MASK (0x3 << NAU8824_FLL_REF_DIV_SFT)
+
+/* FLL5 (0x08) */
+#define NAU8824_FLL_PDB_DAC_EN (0x1 << 15)
+#define NAU8824_FLL_LOOP_FTR_EN (0x1 << 14)
+#define NAU8824_FLL_CLK_SW_MASK (0x1 << 13)
+#define NAU8824_FLL_CLK_SW_N2 (0x1 << 13)
+#define NAU8824_FLL_CLK_SW_REF (0x0 << 13)
+#define NAU8824_FLL_FTR_SW_MASK (0x1 << 12)
+#define NAU8824_FLL_FTR_SW_ACCU (0x1 << 12)
+#define NAU8824_FLL_FTR_SW_FILTER (0x0 << 12)
+
+/* FLL6 (0x9) */
+#define NAU8824_DCO_EN (0x1 << 15)
+#define NAU8824_SDM_EN (0x1 << 14)
+
+/* IRQ (0x10) */
+#define NAU8824_SHORT_CIRCUIT_IRQ (0x1 << 7)
+#define NAU8824_IMPEDANCE_MEAS_IRQ (0x1 << 6)
+#define NAU8824_KEY_RELEASE_IRQ (0x1 << 5)
+#define NAU8824_KEY_LONG_PRESS_IRQ (0x1 << 4)
+#define NAU8824_KEY_SHORT_PRESS_IRQ (0x1 << 3)
+#define NAU8824_JACK_EJECTION_DETECTED (0x1 << 1)
+#define NAU8824_JACK_INSERTION_DETECTED 0x1
+
+/* JACK_DET_CTRL (0x0D) */
+#define NAU8824_JACK_EJECT_DT_SFT 2
+#define NAU8824_JACK_EJECT_DT_MASK (0x3 << NAU8824_JACK_EJECT_DT_SFT)
+#define NAU8824_JACK_LOGIC 0x1
+
+
+/* INTERRUPT_SETTING_1 (0x0F) */
+#define NAU8824_IRQ_EJECT_EN (0x1 << 9)
+#define NAU8824_IRQ_INSERT_EN (0x1 << 8)
+
+/* INTERRUPT_SETTING (0x12) */
+#define NAU8824_IRQ_KEY_RELEASE_DIS (0x1 << 5)
+#define NAU8824_IRQ_KEY_SHORT_PRESS_DIS (0x1 << 3)
+#define NAU8824_IRQ_EJECT_DIS (0x1 << 1)
+#define NAU8824_IRQ_INSERT_DIS 0x1
+
+/* SAR_ADC (0x13) */
+#define NAU8824_SAR_ADC_EN_SFT 12
+#define NAU8824_SAR_TRACKING_GAIN_SFT 8
+#define NAU8824_SAR_TRACKING_GAIN_MASK (0x7 << NAU8824_SAR_TRACKING_GAIN_SFT)
+#define NAU8824_SAR_COMPARE_TIME_SFT 2
+#define NAU8824_SAR_COMPARE_TIME_MASK (3 << 2)
+#define NAU8824_SAR_SAMPLING_TIME_SFT 0
+#define NAU8824_SAR_SAMPLING_TIME_MASK (3 << 0)
+
+/* VDET_COEFFICIENT (0x14) */
+#define NAU8824_SHORTKEY_DEBOUNCE_SFT 12
+#define NAU8824_SHORTKEY_DEBOUNCE_MASK (0x3 << NAU8824_SHORTKEY_DEBOUNCE_SFT)
+#define NAU8824_LEVELS_NR_SFT 8
+#define NAU8824_LEVELS_NR_MASK (0x7 << 8)
+#define NAU8824_HYSTERESIS_SFT 0
+#define NAU8824_HYSTERESIS_MASK 0xf
+
+/* PORT0_I2S_PCM_CTRL_1 (0x1C) */
+#define NAU8824_I2S_BP_SFT 7
+#define NAU8824_I2S_BP_MASK (1 << NAU8824_I2S_BP_SFT)
+#define NAU8824_I2S_BP_INV (1 << NAU8824_I2S_BP_SFT)
+#define NAU8824_I2S_PCMB_SFT 6
+#define NAU8824_I2S_PCMB_EN (1 << NAU8824_I2S_PCMB_SFT)
+#define NAU8824_I2S_DL_SFT 2
+#define NAU8824_I2S_DL_MASK (0x3 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_16 (0 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_20 (1 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_24 (2 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DL_32 (3 << NAU8824_I2S_DL_SFT)
+#define NAU8824_I2S_DF_MASK 0x3
+#define NAU8824_I2S_DF_RIGTH 0
+#define NAU8824_I2S_DF_LEFT 1
+#define NAU8824_I2S_DF_I2S 2
+#define NAU8824_I2S_DF_PCM_AB 3
+
+
+/* PORT0_I2S_PCM_CTRL_2 (0x1D) */
+#define NAU8824_I2S_LRC_DIV_SFT 12
+#define NAU8824_I2S_LRC_DIV_MASK (0x3 << NAU8824_I2S_LRC_DIV_SFT)
+#define NAU8824_I2S_MS_SFT 3
+#define NAU8824_I2S_MS_MASK (1 << NAU8824_I2S_MS_SFT)
+#define NAU8824_I2S_MS_MASTER (1 << NAU8824_I2S_MS_SFT)
+#define NAU8824_I2S_MS_SLAVE (0 << NAU8824_I2S_MS_SFT)
+#define NAU8824_I2S_BLK_DIV_MASK 0x7
+
+/* ADC_FILTER_CTRL (0x24) */
+#define NAU8824_ADC_SYNC_DOWN_MASK 0x3
+#define NAU8824_ADC_SYNC_DOWN_32 0
+#define NAU8824_ADC_SYNC_DOWN_64 1
+#define NAU8824_ADC_SYNC_DOWN_128 2
+#define NAU8824_ADC_SYNC_DOWN_256 3
+
+/* DAC_FILTER_CTRL_1 (0x25) */
+#define NAU8824_DAC_CICCLP_OFF (0x1 << 7)
+#define NAU8824_DAC_OVERSAMPLE_MASK 0x7
+#define NAU8824_DAC_OVERSAMPLE_64 0
+#define NAU8824_DAC_OVERSAMPLE_256 1
+#define NAU8824_DAC_OVERSAMPLE_128 2
+#define NAU8824_DAC_OVERSAMPLE_32 4
+
+/* DAC_MUTE_CTRL (0x31) */
+#define NAU8824_DAC_CH01_MIX 0x3
+#define NAU8824_DAC_ZC_EN (0x1 << 11)
+
+/* DAC_CH0_DGAIN_CTRL (0x32) */
+#define NAU8824_DAC_CH0_SEL_SFT 9
+#define NAU8824_DAC_CH0_SEL_MASK (0x1 << NAU8824_DAC_CH0_SEL_SFT)
+#define NAU8824_DAC_CH0_SEL_I2S0 (0x0 << NAU8824_DAC_CH0_SEL_SFT)
+#define NAU8824_DAC_CH0_SEL_I2S1 (0x1 << NAU8824_DAC_CH0_SEL_SFT)
+#define NAU8824_DAC_CH0_VOL_MASK 0x1ff
+
+/* DAC_CH1_DGAIN_CTRL (0x33) */
+#define NAU8824_DAC_CH1_SEL_SFT 9
+#define NAU8824_DAC_CH1_SEL_MASK (0x1 << NAU8824_DAC_CH1_SEL_SFT)
+#define NAU8824_DAC_CH1_SEL_I2S0 (0x0 << NAU8824_DAC_CH1_SEL_SFT)
+#define NAU8824_DAC_CH1_SEL_I2S1 (0x1 << NAU8824_DAC_CH1_SEL_SFT)
+#define NAU8824_DAC_CH1_VOL_MASK 0x1ff
+
+/* CLASSG (0x50) */
+#define NAU8824_CLASSG_TIMER_SFT 8
+#define NAU8824_CLASSG_TIMER_MASK (0x3f << NAU8824_CLASSG_TIMER_SFT)
+#define NAU8824_CLASSG_LDAC_EN_SFT 2
+#define NAU8824_CLASSG_RDAC_EN_SFT 1
+#define NAU8824_CLASSG_EN_SFT 0
+
+/* SAR_ADC_DATA_OUT (0x59) */
+#define NAU8824_SAR_ADC_DATA_MASK 0xff
+
+/* BIAS_ADJ (0x66) */
+#define NAU8824_VMID (1 << 6)
+#define NAU8824_VMID_SEL_SFT 4
+#define NAU8824_VMID_SEL_MASK (3 << NAU8824_VMID_SEL_SFT)
+#define NAU8824_DMIC2_EN_SFT 3
+#define NAU8824_DMIC1_EN_SFT 2
+
+/* TRIM_SETTINGS (0x68) */
+#define NAU8824_DRV_CURR_INC (1 << 15)
+
+/* ANALOG_CONTROL_1 (0x69) */
+#define NAU8824_DMIC_CLK_DRV_STRG (1 << 3)
+#define NAU8824_DMIC_CLK_SLEW_FAST (0x7)
+
+/* ANALOG_CONTROL_2 (0x6A) */
+#define NAU8824_CLASSD_CLAMP_DIS_SFT 3
+#define NAU8824_CLASSD_CLAMP_DIS (0x1 << NAU8824_CLASSD_CLAMP_DIS_SFT)
+
+/* ENABLE_LO (0x6B) */
+#define NAU8824_TEST_DAC_SFT 14
+#define NAU8824_TEST_DAC_EN (0x3 << NAU8824_TEST_DAC_SFT)
+#define NAU8824_DACL_HPR_EN_SFT 3
+#define NAU8824_DACL_HPR_EN (0x1 << NAU8824_DACL_HPR_EN_SFT)
+#define NAU8824_DACR_HPR_EN_SFT 2
+#define NAU8824_DACR_HPR_EN (0x1 << NAU8824_DACR_HPR_EN_SFT)
+#define NAU8824_DACR_HPL_EN_SFT 1
+#define NAU8824_DACR_HPL_EN (0x1 << NAU8824_DACR_HPL_EN_SFT)
+#define NAU8824_DACL_HPL_EN_SFT 0
+#define NAU8824_DACL_HPL_EN 0x1
+
+/* CLASSD_GAIN_1 (0x6D) */
+#define NAU8824_CLASSD_GAIN_1R_SFT 8
+#define NAU8824_CLASSD_GAIN_1R_MASK (0x1f << NAU8824_CLASSD_GAIN_1R_SFT)
+#define NAU8824_CLASSD_EN_SFT 7
+#define NAU8824_CLASSD_EN (0x1 << NAU8824_CLASSD_EN_SFT)
+#define NAU8824_CLASSD_GAIN_1L_MASK 0x1f
+
+/* CLASSD_GAIN_2 (0x6E) */
+#define NAU8824_CLASSD_GAIN_2R_SFT 8
+#define NAU8824_CLASSD_GAIN_2R_MASK (0x1f << NAU8824_CLASSD_GAIN_1R_SFT)
+#define NAU8824_CLASSD_EN_SFT 7
+#define NAU8824_CLASSD_EN (0x1 << NAU8824_CLASSD_EN_SFT)
+#define NAU8824_CLASSD_GAIN_2L_MASK 0x1f
+
+/* ANALOG_ADC_2 (0x72) */
+#define NAU8824_ADCR_EN_SFT 7
+#define NAU8824_ADCL_EN_SFT 6
+
+/* RDAC (0x73) */
+#define NAU8824_DACR_EN_SFT 13
+#define NAU8824_DACL_EN_SFT 12
+#define NAU8824_DACR_CLK_SFT 9
+#define NAU8824_DACL_CLK_SFT 8
+#define NAU8824_RDAC_CLK_DELAY_SFT 4
+#define NAU8824_RDAC_CLK_DELAY_MASK (0x7 << NAU8824_RDAC_CLK_DELAY_SFT)
+#define NAU8824_RDAC_VREF_SFT 2
+#define NAU8824_RDAC_VREF_MASK (0x3 << NAU8824_RDAC_VREF_SFT)
+
+/* MIC_BIAS (0x74) */
+#define NAU8824_MICBIAS_JKSLV (1 << 14)
+#define NAU8824_MICBIAS_JKR2 (1 << 12)
+#define NAU8824_MICBIAS_POWERUP_SFT 8
+#define NAU8824_MICBIAS_VOLTAGE_SFT 0
+#define NAU8824_MICBIAS_VOLTAGE_MASK 0x7
+
+/* BOOST (0x76) */
+#define NAU8824_PRECHARGE_DIS (0x1 << 13)
+#define NAU8824_GLOBAL_BIAS_EN (0x1 << 12)
+#define NAU8824_HP_BOOST_DIS_SFT 9
+#define NAU8824_HP_BOOST_DIS (0x1 << NAU8824_HP_BOOST_DIS_SFT)
+#define NAU8824_HP_BOOST_G_DIS_SFT 8
+#define NAU8824_HP_BOOST_G_DIS (0x1 << NAU8824_HP_BOOST_G_DIS_SFT)
+#define NAU8824_SHORT_SHUTDOWN_DIG_EN (1 << 7)
+#define NAU8824_SHORT_SHUTDOWN_EN (1 << 6)
+
+/* FEPGA (0x77) */
+#define NAU8824_FEPGA_MODER_SHORT_SFT 7
+#define NAU8824_FEPGA_MODER_SHORT_EN (0x1 << NAU8824_FEPGA_MODER_SHORT_SFT)
+#define NAU8824_FEPGA_MODER_MIC2_SFT 5
+#define NAU8824_FEPGA_MODER_MIC2_EN (0x1 << NAU8824_FEPGA_MODER_MIC2_SFT)
+#define NAU8824_FEPGA_MODER_HSMIC_SFT 4
+#define NAU8824_FEPGA_MODER_HSMIC_EN (0x1 << NAU8824_FEPGA_MODER_HSMIC_SFT)
+#define NAU8824_FEPGA_MODEL_SHORT_SFT 3
+#define NAU8824_FEPGA_MODEL_SHORT_EN (0x1 << NAU8824_FEPGA_MODEL_SHORT_SFT)
+#define NAU8824_FEPGA_MODEL_MIC1_SFT 1
+#define NAU8824_FEPGA_MODEL_MIC1_EN (0x1 << NAU8824_FEPGA_MODEL_MIC1_SFT)
+#define NAU8824_FEPGA_MODEL_HSMIC_SFT 0
+#define NAU8824_FEPGA_MODEL_HSMIC_EN (0x1 << NAU8824_FEPGA_MODEL_HSMIC_SFT)
+
+/* FEPGA_II (0x78) */
+#define NAU8824_FEPGA_GAINR_SFT 5
+#define NAU8824_FEPGA_GAINR_MASK (0x1f << NAU8824_FEPGA_GAINR_SFT)
+#define NAU8824_FEPGA_GAINL_SFT 0
+#define NAU8824_FEPGA_GAINL_MASK 0x1f
+
+/* CHARGE_PUMP_CONTROL (0x80) */
+#define NAU8824_JAMNODCLOW (0x1 << 15)
+#define NAU8824_SPKR_PULL_DOWN (0x1 << 13)
+#define NAU8824_SPKL_PULL_DOWN (0x1 << 12)
+#define NAU8824_POWER_DOWN_DACR (0x1 << 9)
+#define NAU8824_POWER_DOWN_DACL (0x1 << 8)
+#define NAU8824_CHARGE_PUMP_EN_SFT 5
+#define NAU8824_CHARGE_PUMP_EN (0x1 << NAU8824_CHARGE_PUMP_EN_SFT)
+
+
+#define NAU8824_CODEC_DAI "nau8824-hifi"
+
+/* System Clock Source */
+enum {
+ NAU8824_CLK_DIS,
+ NAU8824_CLK_MCLK,
+ NAU8824_CLK_INTERNAL,
+ NAU8824_CLK_FLL_MCLK,
+ NAU8824_CLK_FLL_BLK,
+ NAU8824_CLK_FLL_FS,
+};
+
+struct nau8824 {
+ struct device *dev;
+ struct regmap *regmap;
+ struct snd_soc_dapm_context *dapm;
+ struct snd_soc_jack *jack;
+ struct work_struct jdet_work;
+ struct semaphore jd_sem;
+ int fs;
+ int irq;
+ int micbias_voltage;
+ int vref_impedance;
+ int jkdet_polarity;
+ int sar_threshold_num;
+ int sar_threshold[8];
+ int sar_hysteresis;
+ int sar_voltage;
+ int sar_compare_time;
+ int sar_sampling_time;
+ int key_debounce;
+ int jack_eject_debounce;
+};
+
+struct nau8824_fll {
+ int mclk_src;
+ int ratio;
+ int fll_frac;
+ int fll_int;
+ int clk_ref_div;
+};
+
+struct nau8824_fll_attr {
+ unsigned int param;
+ unsigned int val;
+};
+
+struct nau8824_osr_attr {
+ unsigned int osr;
+ unsigned int clk_src;
+};
+
+
+int nau8824_enable_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack);
+
+#endif /* _NAU8824_H */
+
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
index b281a46d769d..f91221b1ddf0 100644
--- a/sound/soc/codecs/rt5514.c
+++ b/sound/soc/codecs/rt5514.c
@@ -1084,13 +1084,28 @@ static int rt5514_parse_dt(struct rt5514_priv *rt5514, struct device *dev)
return 0;
}
+static __maybe_unused int rt5514_i2c_resume(struct device *dev)
+{
+ struct rt5514_priv *rt5514 = dev_get_drvdata(dev);
+ unsigned int val;
+
+ /*
+ * Add a bogus read to avoid rt5514's confusion after s2r in case it
+ * saw glitches on the i2c lines and thought the other side sent a
+ * start bit.
+ */
+ regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+
+ return 0;
+}
+
static int rt5514_i2c_probe(struct i2c_client *i2c,
const struct i2c_device_id *id)
{
struct rt5514_platform_data *pdata = dev_get_platdata(&i2c->dev);
struct rt5514_priv *rt5514;
int ret;
- unsigned int val;
+ unsigned int val = ~0;
rt5514 = devm_kzalloc(&i2c->dev, sizeof(struct rt5514_priv),
GFP_KERNEL);
@@ -1120,8 +1135,16 @@ static int rt5514_i2c_probe(struct i2c_client *i2c,
return ret;
}
- regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
- if (val != RT5514_DEVICE_ID) {
+ /*
+ * The rt5514 can get confused if the i2c lines glitch together, as
+ * can happen at bootup as regulators are turned off and on. If it's
+ * in this glitched state the first i2c read will fail, so we'll give
+ * it one change to retry.
+ */
+ ret = regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+ if (ret || val != RT5514_DEVICE_ID)
+ ret = regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+ if (ret || val != RT5514_DEVICE_ID) {
dev_err(&i2c->dev,
"Device with ID register %x is not rt5514\n", val);
return -ENODEV;
@@ -1149,10 +1172,15 @@ static int rt5514_i2c_remove(struct i2c_client *i2c)
return 0;
}
-struct i2c_driver rt5514_i2c_driver = {
+static const struct dev_pm_ops rt5514_i2_pm_ops = {
+ SET_SYSTEM_SLEEP_PM_OPS(NULL, rt5514_i2c_resume)
+};
+
+static struct i2c_driver rt5514_i2c_driver = {
.driver = {
.name = "rt5514",
.of_match_table = of_match_ptr(rt5514_of_match),
+ .pm = &rt5514_i2_pm_ops,
},
.probe = rt5514_i2c_probe,
.remove = rt5514_i2c_remove,
diff --git a/sound/soc/codecs/rt5645.c b/sound/soc/codecs/rt5645.c
index e149f3ce5401..87844a45886a 100644
--- a/sound/soc/codecs/rt5645.c
+++ b/sound/soc/codecs/rt5645.c
@@ -3542,6 +3542,15 @@ static const struct i2c_device_id rt5645_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, rt5645_i2c_id);
+#ifdef CONFIG_OF
+static const struct of_device_id rt5645_of_match[] = {
+ { .compatible = "realtek,rt5645", },
+ { .compatible = "realtek,rt5650", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5645_of_match);
+#endif
+
#ifdef CONFIG_ACPI
static const struct acpi_device_id rt5645_acpi_match[] = {
{ "10EC5645", 0 },
@@ -3912,6 +3921,7 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c)
static struct i2c_driver rt5645_i2c_driver = {
.driver = {
.name = "rt5645",
+ .of_match_table = of_match_ptr(rt5645_of_match),
.acpi_match_table = ACPI_PTR(rt5645_acpi_match),
},
.probe = rt5645_i2c_probe,
diff --git a/sound/soc/codecs/rt5665.c b/sound/soc/codecs/rt5665.c
index 476135ec5726..8cd22307f5b6 100644
--- a/sound/soc/codecs/rt5665.c
+++ b/sound/soc/codecs/rt5665.c
@@ -1139,7 +1139,8 @@ static void rt5665_enable_push_button_irq(struct snd_soc_codec *codec,
bool enable)
{
if (enable) {
- snd_soc_write(codec, RT5665_4BTN_IL_CMD_1, 0x000b);
+ snd_soc_write(codec, RT5665_4BTN_IL_CMD_1, 0x0003);
+ snd_soc_update_bits(codec, RT5665_SAR_IL_CMD_9, 0x1, 0x1);
snd_soc_write(codec, RT5665_IL_CMD_1, 0x0048);
snd_soc_update_bits(codec, RT5665_4BTN_IL_CMD_2,
RT5665_4BTN_IL_MASK | RT5665_4BTN_IL_RST_MASK,
@@ -1192,10 +1193,13 @@ static int rt5665_headset_detect(struct snd_soc_codec *codec, int jack_insert)
}
regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1,
- 0x180, 0x180);
+ 0x1a0, 0x120);
regmap_write(rt5665->regmap, RT5665_EJD_CTRL_3, 0x3424);
+ regmap_write(rt5665->regmap, RT5665_IL_CMD_1, 0x0048);
regmap_write(rt5665->regmap, RT5665_SAR_IL_CMD_1, 0xa291);
+ usleep_range(10000, 15000);
+
rt5665->sar_adc_value = snd_soc_read(rt5665->codec,
RT5665_SAR_IL_CMD_4) & 0x7ff;
@@ -1256,8 +1260,8 @@ static void rt5665_jd_check_handler(struct work_struct *work)
}
}
-int rt5665_set_jack_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *hs_jack)
+static int rt5665_set_jack_detect(struct snd_soc_codec *codec,
+ struct snd_soc_jack *hs_jack, void *data)
{
struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
@@ -1284,7 +1288,6 @@ int rt5665_set_jack_detect(struct snd_soc_codec *codec,
return 0;
}
-EXPORT_SYMBOL_GPL(rt5665_set_jack_detect);
static void rt5665_jack_detect_handler(struct work_struct *work)
{
@@ -2600,6 +2603,55 @@ static int rt5655_set_verf(struct snd_soc_dapm_widget *w,
return 0;
}
+static int rt5665_i2s_pin_event(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *kcontrol, int event)
+{
+ struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+ unsigned int val1, val2, mask1, mask2 = 0;
+
+ switch (w->shift) {
+ case RT5665_PWR_I2S2_1_BIT:
+ mask1 = RT5665_GP2_PIN_MASK | RT5665_GP3_PIN_MASK |
+ RT5665_GP4_PIN_MASK | RT5665_GP5_PIN_MASK;
+ val1 = RT5665_GP2_PIN_BCLK2 | RT5665_GP3_PIN_LRCK2 |
+ RT5665_GP4_PIN_DACDAT2_1 | RT5665_GP5_PIN_ADCDAT2_1;
+ break;
+ case RT5665_PWR_I2S2_2_BIT:
+ mask1 = RT5665_GP2_PIN_MASK | RT5665_GP3_PIN_MASK |
+ RT5665_GP8_PIN_MASK;
+ val1 = RT5665_GP2_PIN_BCLK2 | RT5665_GP3_PIN_LRCK2 |
+ RT5665_GP8_PIN_DACDAT2_2;
+ mask2 = RT5665_GP9_PIN_MASK;
+ val2 = RT5665_GP9_PIN_ADCDAT2_2;
+ break;
+ case RT5665_PWR_I2S3_BIT:
+ mask1 = RT5665_GP6_PIN_MASK | RT5665_GP7_PIN_MASK |
+ RT5665_GP8_PIN_MASK;
+ val1 = RT5665_GP6_PIN_BCLK3 | RT5665_GP7_PIN_LRCK3 |
+ RT5665_GP8_PIN_DACDAT3;
+ mask2 = RT5665_GP9_PIN_MASK;
+ val2 = RT5665_GP9_PIN_ADCDAT3;
+ break;
+ }
+ switch (event) {
+ case SND_SOC_DAPM_PRE_PMU:
+ snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, mask1, val1);
+ if (mask2)
+ snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2,
+ mask2, val2);
+ break;
+ case SND_SOC_DAPM_POST_PMD:
+ snd_soc_update_bits(codec, RT5665_GPIO_CTRL_1, mask1, 0);
+ if (mask2)
+ snd_soc_update_bits(codec, RT5665_GPIO_CTRL_2,
+ mask2, 0);
+ break;
+ default:
+ return 0;
+ }
+
+ return 0;
+}
static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("LDO2", RT5665_PWR_ANLG_3, RT5665_PWR_LDO2_BIT, 0,
@@ -2852,11 +2904,14 @@ static const struct snd_soc_dapm_widget rt5665_dapm_widgets[] = {
SND_SOC_DAPM_SUPPLY("I2S1_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S1_2_BIT,
0, NULL, 0),
SND_SOC_DAPM_SUPPLY("I2S2_1", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_1_BIT,
- 0, NULL, 0),
+ 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("I2S2_2", RT5665_PWR_DIG_1, RT5665_PWR_I2S2_2_BIT,
- 0, NULL, 0),
+ 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_SUPPLY("I2S3", RT5665_PWR_DIG_1, RT5665_PWR_I2S3_BIT,
- 0, NULL, 0),
+ 0, rt5665_i2s_pin_event, SND_SOC_DAPM_PRE_PMU |
+ SND_SOC_DAPM_POST_PMD),
SND_SOC_DAPM_PGA("IF1 DAC1", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 DAC2", SND_SOC_NOPM, 0, 0, NULL, 0),
SND_SOC_DAPM_PGA("IF1 DAC3", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -3963,12 +4018,68 @@ static const struct snd_soc_dapm_route rt5665_dapm_routes[] = {
{"PDMR", NULL, "PDM R Playback"},
};
+static int rt5665_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+ unsigned int rx_mask, int slots, int slot_width)
+{
+ struct snd_soc_codec *codec = dai->codec;
+ unsigned int val = 0;
+
+ if (rx_mask || tx_mask)
+ val |= RT5665_I2S1_MODE_TDM;
+
+ switch (slots) {
+ case 4:
+ val |= RT5665_TDM_IN_CH_4;
+ val |= RT5665_TDM_OUT_CH_4;
+ break;
+ case 6:
+ val |= RT5665_TDM_IN_CH_6;
+ val |= RT5665_TDM_OUT_CH_6;
+ break;
+ case 8:
+ val |= RT5665_TDM_IN_CH_8;
+ val |= RT5665_TDM_OUT_CH_8;
+ break;
+ case 2:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (slot_width) {
+ case 20:
+ val |= RT5665_TDM_IN_LEN_20;
+ val |= RT5665_TDM_OUT_LEN_20;
+ break;
+ case 24:
+ val |= RT5665_TDM_IN_LEN_24;
+ val |= RT5665_TDM_OUT_LEN_24;
+ break;
+ case 32:
+ val |= RT5665_TDM_IN_LEN_32;
+ val |= RT5665_TDM_OUT_LEN_32;
+ break;
+ case 16:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ snd_soc_update_bits(codec, RT5665_TDM_CTRL_1,
+ RT5665_I2S1_MODE_MASK | RT5665_TDM_IN_CH_MASK |
+ RT5665_TDM_OUT_CH_MASK | RT5665_TDM_IN_LEN_MASK |
+ RT5665_TDM_OUT_LEN_MASK, val);
+
+ return 0;
+}
+
+
static int rt5665_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
{
struct snd_soc_codec *codec = dai->codec;
struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
- unsigned int val_len = 0, val_clk, mask_clk, val_bits = 0x0100;
+ unsigned int val_len = 0, val_clk, reg_clk, mask_clk, val_bits = 0x0100;
int pre_div, frame_size;
rt5665->lrck[dai->id] = params_rate(params);
@@ -4009,6 +4120,10 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream,
switch (dai->id) {
case RT5665_AIF1_1:
case RT5665_AIF1_2:
+ if (params_channels(params) > 2)
+ rt5665_set_tdm_slot(dai, 0xf, 0xf,
+ params_channels(params), params_width(params));
+ reg_clk = RT5665_ADDA_CLK_1;
mask_clk = RT5665_I2S_PD1_MASK;
val_clk = pre_div << RT5665_I2S_PD1_SFT;
snd_soc_update_bits(codec, RT5665_I2S1_SDP,
@@ -4016,12 +4131,14 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream,
break;
case RT5665_AIF2_1:
case RT5665_AIF2_2:
+ reg_clk = RT5665_ADDA_CLK_2;
mask_clk = RT5665_I2S_PD2_MASK;
val_clk = pre_div << RT5665_I2S_PD2_SFT;
snd_soc_update_bits(codec, RT5665_I2S2_SDP,
RT5665_I2S_DL_MASK, val_len);
break;
case RT5665_AIF3:
+ reg_clk = RT5665_ADDA_CLK_2;
mask_clk = RT5665_I2S_PD3_MASK;
val_clk = pre_div << RT5665_I2S_PD3_SFT;
snd_soc_update_bits(codec, RT5665_I2S3_SDP,
@@ -4032,7 +4149,7 @@ static int rt5665_hw_params(struct snd_pcm_substream *substream,
return -EINVAL;
}
- snd_soc_update_bits(codec, RT5665_ADDA_CLK_1, mask_clk, val_clk);
+ snd_soc_update_bits(codec, reg_clk, mask_clk, val_clk);
snd_soc_update_bits(codec, RT5665_STO1_DAC_SIL_DET, 0x3700, val_bits);
switch (rt5665->lrck[dai->id]) {
@@ -4125,10 +4242,9 @@ static int rt5665_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
return 0;
}
-static int rt5665_set_dai_sysclk(struct snd_soc_dai *dai,
- int clk_id, unsigned int freq, int dir)
+static int rt5665_set_codec_sysclk(struct snd_soc_codec *codec, int clk_id,
+ int source, unsigned int freq, int dir)
{
- struct snd_soc_codec *codec = dai->codec;
struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
unsigned int reg_val = 0;
@@ -4154,20 +4270,20 @@ static int rt5665_set_dai_sysclk(struct snd_soc_dai *dai,
rt5665->sysclk = freq;
rt5665->sysclk_src = clk_id;
- dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+ dev_dbg(codec->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
return 0;
}
-static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
- unsigned int freq_in, unsigned int freq_out)
+static int rt5665_set_codec_pll(struct snd_soc_codec *codec, int pll_id,
+ int source, unsigned int freq_in,
+ unsigned int freq_out)
{
- struct snd_soc_codec *codec = dai->codec;
struct rt5665_priv *rt5665 = snd_soc_codec_get_drvdata(codec);
struct rl6231_pll_code pll_code;
int ret;
- if (Source == rt5665->pll_src && freq_in == rt5665->pll_in &&
+ if (source == rt5665->pll_src && freq_in == rt5665->pll_in &&
freq_out == rt5665->pll_out)
return 0;
@@ -4181,7 +4297,7 @@ static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
return 0;
}
- switch (Source) {
+ switch (source) {
case RT5665_PLL1_S_MCLK:
snd_soc_update_bits(codec, RT5665_GLB_CLK,
RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_MCLK);
@@ -4199,7 +4315,7 @@ static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
RT5665_PLL1_SRC_MASK, RT5665_PLL1_SRC_BCLK3);
break;
default:
- dev_err(codec->dev, "Unknown PLL Source %d\n", Source);
+ dev_err(codec->dev, "Unknown PLL Source %d\n", source);
return -EINVAL;
}
@@ -4221,62 +4337,7 @@ static int rt5665_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int Source,
rt5665->pll_in = freq_in;
rt5665->pll_out = freq_out;
- rt5665->pll_src = Source;
-
- return 0;
-}
-
-static int rt5665_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
- unsigned int rx_mask, int slots, int slot_width)
-{
- struct snd_soc_codec *codec = dai->codec;
- unsigned int val = 0;
-
- if (rx_mask || tx_mask)
- val |= RT5665_I2S1_MODE_TDM;
-
- switch (slots) {
- case 4:
- val |= RT5665_TDM_IN_CH_4;
- val |= RT5665_TDM_OUT_CH_4;
- break;
- case 6:
- val |= RT5665_TDM_IN_CH_6;
- val |= RT5665_TDM_OUT_CH_6;
- break;
- case 8:
- val |= RT5665_TDM_IN_CH_8;
- val |= RT5665_TDM_OUT_CH_8;
- break;
- case 2:
- break;
- default:
- return -EINVAL;
- }
-
- switch (slot_width) {
- case 20:
- val |= RT5665_TDM_IN_LEN_20;
- val |= RT5665_TDM_OUT_LEN_20;
- break;
- case 24:
- val |= RT5665_TDM_IN_LEN_24;
- val |= RT5665_TDM_OUT_LEN_24;
- break;
- case 32:
- val |= RT5665_TDM_IN_LEN_32;
- val |= RT5665_TDM_OUT_LEN_32;
- break;
- case 16:
- break;
- default:
- return -EINVAL;
- }
-
- snd_soc_update_bits(codec, RT5665_TDM_CTRL_1,
- RT5665_I2S1_MODE_MASK | RT5665_TDM_IN_CH_MASK |
- RT5665_TDM_OUT_CH_MASK | RT5665_TDM_IN_LEN_MASK |
- RT5665_TDM_OUT_LEN_MASK, val);
+ rt5665->pll_src = source;
return 0;
}
@@ -4393,9 +4454,7 @@ static int rt5665_resume(struct snd_soc_codec *codec)
static const struct snd_soc_dai_ops rt5665_aif_dai_ops = {
.hw_params = rt5665_hw_params,
.set_fmt = rt5665_set_dai_fmt,
- .set_sysclk = rt5665_set_dai_sysclk,
.set_tdm_slot = rt5665_set_tdm_slot,
- .set_pll = rt5665_set_dai_pll,
.set_bclk_ratio = rt5665_set_bclk_ratio,
};
@@ -4504,7 +4563,10 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5665 = {
.num_dapm_widgets = ARRAY_SIZE(rt5665_dapm_widgets),
.dapm_routes = rt5665_dapm_routes,
.num_dapm_routes = ARRAY_SIZE(rt5665_dapm_routes),
- }
+ },
+ .set_sysclk = rt5665_set_codec_sysclk,
+ .set_pll = rt5665_set_codec_pll,
+ .set_jack = rt5665_set_jack_detect,
};
@@ -4783,7 +4845,7 @@ static int rt5665_i2c_probe(struct i2c_client *i2c,
regmap_write(rt5665->regmap, RT5665_HP_LOGIC_CTRL_2, 0x0002);
regmap_update_bits(rt5665->regmap, RT5665_EJD_CTRL_1,
- 0xf000 | RT5665_VREF_POW_MASK, 0xd000 | RT5665_VREF_POW_REG);
+ 0xf000 | RT5665_VREF_POW_MASK, 0xe000 | RT5665_VREF_POW_REG);
/* Work around for pow_pump */
regmap_update_bits(rt5665->regmap, RT5665_STO1_DAC_SIL_DET,
RT5665_DEB_STO_DAC_MASK, RT5665_DEB_80_MS);
diff --git a/sound/soc/codecs/rt5665.h b/sound/soc/codecs/rt5665.h
index a30f5e6d0628..1db5c6a62a8e 100644
--- a/sound/soc/codecs/rt5665.h
+++ b/sound/soc/codecs/rt5665.h
@@ -1984,7 +1984,5 @@ enum {
int rt5665_sel_asrc_clk_src(struct snd_soc_codec *codec,
unsigned int filter_mask, unsigned int clk_src);
-int rt5665_set_jack_detect(struct snd_soc_codec *codec,
- struct snd_soc_jack *hs_jack);
#endif /* __RT5665_H__ */
diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c
index 17d20b99f041..e27c5a4a0a15 100644
--- a/sound/soc/codecs/rt5670.c
+++ b/sound/soc/codecs/rt5670.c
@@ -2835,6 +2835,27 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
DMI_MATCH(DMI_PRODUCT_NAME, "Wyse 3040"),
},
},
+ {
+ .ident = "Lenovo Thinkpad Tablet 10",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"),
+ },
+ },
+ {
+ .ident = "Lenovo Thinkpad Tablet 10",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"),
+ },
+ },
+ {
+ .ident = "Lenovo Thinkpad Tablet 10",
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"),
+ },
+ },
{}
};
diff --git a/sound/soc/codecs/rt5677.c b/sound/soc/codecs/rt5677.c
index abc802a5a479..65ac4518ad06 100644
--- a/sound/soc/codecs/rt5677.c
+++ b/sound/soc/codecs/rt5677.c
@@ -5035,6 +5035,12 @@ static const struct i2c_device_id rt5677_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, rt5677_i2c_id);
+static const struct of_device_id rt5677_of_match[] = {
+ { .compatible = "realtek,rt5677", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, rt5677_of_match);
+
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 };
@@ -5294,6 +5300,7 @@ static int rt5677_i2c_remove(struct i2c_client *i2c)
static struct i2c_driver rt5677_i2c_driver = {
.driver = {
.name = "rt5677",
+ .of_match_table = rt5677_of_match,
},
.probe = rt5677_i2c_probe,
.remove = rt5677_i2c_remove,
diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c
index 1589325855bc..5a2702edeb77 100644
--- a/sound/soc/codecs/sgtl5000.c
+++ b/sound/soc/codecs/sgtl5000.c
@@ -99,6 +99,13 @@ enum sgtl5000_micbias_resistor {
SGTL5000_MICBIAS_8K = 8,
};
+enum {
+ I2S_LRCLK_STRENGTH_DISABLE,
+ I2S_LRCLK_STRENGTH_LOW,
+ I2S_LRCLK_STRENGTH_MEDIUM,
+ I2S_LRCLK_STRENGTH_HIGH,
+};
+
/* sgtl5000 private structure in codec */
struct sgtl5000_priv {
int sysclk; /* sysclk rate */
@@ -111,6 +118,7 @@ struct sgtl5000_priv {
int revision;
u8 micbias_resistor;
u8 micbias_voltage;
+ u8 lrclk_strength;
};
/*
@@ -1089,6 +1097,7 @@ static int sgtl5000_enable_regulators(struct i2c_client *client)
static int sgtl5000_probe(struct snd_soc_codec *codec)
{
int ret;
+ u16 reg;
struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(codec);
/* power up sgtl5000 */
@@ -1118,7 +1127,8 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
SGTL5000_DAC_MUTE_RIGHT |
SGTL5000_DAC_MUTE_LEFT);
- snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, 0x015f);
+ reg = ((sgtl5000->lrclk_strength) << SGTL5000_PAD_I2S_LRCLK_SHIFT | 0x5f);
+ snd_soc_write(codec, SGTL5000_CHIP_PAD_STRENGTH, reg);
snd_soc_write(codec, SGTL5000_CHIP_ANA_CTRL,
SGTL5000_HP_ZCD_EN |
@@ -1347,6 +1357,13 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
}
}
+ sgtl5000->lrclk_strength = I2S_LRCLK_STRENGTH_LOW;
+ if (!of_property_read_u32(np, "lrclk-strength", &value)) {
+ if (value > I2S_LRCLK_STRENGTH_HIGH)
+ value = I2S_LRCLK_STRENGTH_LOW;
+ sgtl5000->lrclk_strength = value;
+ }
+
/* Ensure sgtl5000 will start with sane register values */
sgtl5000_fill_defaults(client);
diff --git a/sound/soc/codecs/ssm4567.c b/sound/soc/codecs/ssm4567.c
index 2bb5a11c9ba1..a622623e8558 100644
--- a/sound/soc/codecs/ssm4567.c
+++ b/sound/soc/codecs/ssm4567.c
@@ -485,6 +485,14 @@ static const struct i2c_device_id ssm4567_i2c_ids[] = {
};
MODULE_DEVICE_TABLE(i2c, ssm4567_i2c_ids);
+#ifdef CONFIG_OF
+static const struct of_device_id ssm4567_of_match[] = {
+ { .compatible = "adi,ssm4567", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, ssm4567_of_match);
+#endif
+
#ifdef CONFIG_ACPI
static const struct acpi_device_id ssm4567_acpi_match[] = {
@@ -498,6 +506,7 @@ MODULE_DEVICE_TABLE(acpi, ssm4567_acpi_match);
static struct i2c_driver ssm4567_driver = {
.driver = {
.name = "ssm4567",
+ .of_match_table = of_match_ptr(ssm4567_of_match),
.acpi_match_table = ACPI_PTR(ssm4567_acpi_match),
},
.probe = ssm4567_i2c_probe,
diff --git a/sound/soc/codecs/sta529.c b/sound/soc/codecs/sta529.c
index d4b384e4b266..660734359bf3 100644
--- a/sound/soc/codecs/sta529.c
+++ b/sound/soc/codecs/sta529.c
@@ -375,9 +375,16 @@ static const struct i2c_device_id sta529_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, sta529_i2c_id);
+static const struct of_device_id sta529_of_match[] = {
+ { .compatible = "st,sta529", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, sta529_of_match);
+
static struct i2c_driver sta529_i2c_driver = {
.driver = {
.name = "sta529",
+ .of_match_table = sta529_of_match,
},
.probe = sta529_i2c_probe,
.remove = sta529_i2c_remove,
diff --git a/sound/soc/codecs/tas2552.c b/sound/soc/codecs/tas2552.c
index baf455e8c2f7..8840f72f3c4a 100644
--- a/sound/soc/codecs/tas2552.c
+++ b/sound/soc/codecs/tas2552.c
@@ -611,7 +611,7 @@ probe_fail:
regulator_bulk_disable(ARRAY_SIZE(tas2552->supplies),
tas2552->supplies);
- return -EIO;
+ return ret;
}
static int tas2552_codec_remove(struct snd_soc_codec *codec)
@@ -637,7 +637,7 @@ static int tas2552_suspend(struct snd_soc_codec *codec)
if (ret != 0)
dev_err(codec->dev, "Failed to disable supplies: %d\n",
ret);
- return 0;
+ return ret;
}
static int tas2552_resume(struct snd_soc_codec *codec)
@@ -653,7 +653,7 @@ static int tas2552_resume(struct snd_soc_codec *codec)
ret);
}
- return 0;
+ return ret;
}
#else
#define tas2552_suspend NULL
diff --git a/sound/soc/codecs/tlv320aic23.c b/sound/soc/codecs/tlv320aic23.c
index 410cae0f2060..628a8eeaab68 100644
--- a/sound/soc/codecs/tlv320aic23.c
+++ b/sound/soc/codecs/tlv320aic23.c
@@ -174,10 +174,9 @@ static const struct snd_soc_dapm_route tlv320aic23_intercon[] = {
{"ROUT", NULL, "Output Mixer"},
/* Inputs */
- {"Line Input", "NULL", "LLINEIN"},
- {"Line Input", "NULL", "RLINEIN"},
-
- {"Mic Input", "NULL", "MICIN"},
+ {"Line Input", NULL, "LLINEIN"},
+ {"Line Input", NULL, "RLINEIN"},
+ {"Mic Input", NULL, "MICIN"},
/* input mux */
{"Capture Source", "Line", "Line Input"},
diff --git a/sound/soc/codecs/twl6040.c b/sound/soc/codecs/twl6040.c
index 748036e851ea..2b6ad09e0886 100644
--- a/sound/soc/codecs/twl6040.c
+++ b/sound/soc/codecs/twl6040.c
@@ -606,6 +606,14 @@ static const struct snd_kcontrol_new twl6040_snd_controls[] = {
twl6040_headset_power_get_enum,
twl6040_headset_power_put_enum),
+ /* Left HS PDM data routed to Right HSDAC */
+ SOC_SINGLE("Headset Mono to Stereo Playback Switch",
+ TWL6040_REG_HSRCTL, 7, 1, 0),
+
+ /* Left HF PDM data routed to Right HFDAC */
+ SOC_SINGLE("Handsfree Mono to Stereo Playback Switch",
+ TWL6040_REG_HFRCTL, 5, 1, 0),
+
SOC_ENUM_EXT("PLL Selection", twl6040_power_mode_enum,
twl6040_pll_get_enum, twl6040_pll_put_enum),
};
diff --git a/sound/soc/codecs/uda1380.c b/sound/soc/codecs/uda1380.c
index 2918fdb95e58..61cdc79840e7 100644
--- a/sound/soc/codecs/uda1380.c
+++ b/sound/soc/codecs/uda1380.c
@@ -791,9 +791,16 @@ static const struct i2c_device_id uda1380_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, uda1380_i2c_id);
+static const struct of_device_id uda1380_of_match[] = {
+ { .compatible = "nxp,uda1380", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, uda1380_of_match);
+
static struct i2c_driver uda1380_i2c_driver = {
.driver = {
.name = "uda1380-codec",
+ .of_match_table = uda1380_of_match,
},
.probe = uda1380_i2c_probe,
.remove = uda1380_i2c_remove,
diff --git a/sound/soc/codecs/wm5100.c b/sound/soc/codecs/wm5100.c
index 560575000cc5..138a84efdd54 100644
--- a/sound/soc/codecs/wm5100.c
+++ b/sound/soc/codecs/wm5100.c
@@ -2014,7 +2014,7 @@ static void wm5100_micd_irq(struct wm5100_priv *wm5100)
ret = regmap_read(wm5100->regmap, WM5100_MIC_DETECT_3, &val);
if (ret != 0) {
- dev_err(wm5100->dev, "Failed to read micropone status: %d\n",
+ dev_err(wm5100->dev, "Failed to read microphone status: %d\n",
ret);
return;
}
diff --git a/sound/soc/codecs/wm8903.c b/sound/soc/codecs/wm8903.c
index 6e887c2c42b1..237eeb9a8b97 100644
--- a/sound/soc/codecs/wm8903.c
+++ b/sound/soc/codecs/wm8903.c
@@ -24,6 +24,7 @@
#include <linux/pm.h>
#include <linux/i2c.h>
#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
#include <linux/slab.h>
#include <linux/irq.h>
#include <linux/mutex.h>
@@ -115,10 +116,19 @@ static const struct reg_default wm8903_reg_defaults[] = {
{ 172, 0x0000 }, /* R172 - Analogue Output Bias 0 */
};
+#define WM8903_NUM_SUPPLIES 4
+static const char *wm8903_supply_names[WM8903_NUM_SUPPLIES] = {
+ "AVDD",
+ "CPVDD",
+ "DBVDD",
+ "DCVDD",
+};
+
struct wm8903_priv {
struct wm8903_platform_data *pdata;
struct device *dev;
struct regmap *regmap;
+ struct regulator_bulk_data supplies[WM8903_NUM_SUPPLIES];
int sysclk;
int irq;
@@ -2030,6 +2040,23 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
pdata = wm8903->pdata;
+ for (i = 0; i < ARRAY_SIZE(wm8903->supplies); i++)
+ wm8903->supplies[i].supply = wm8903_supply_names[i];
+
+ ret = devm_regulator_bulk_get(&i2c->dev, ARRAY_SIZE(wm8903->supplies),
+ wm8903->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to request supplies: %d\n", ret);
+ return ret;
+ }
+
+ ret = regulator_bulk_enable(ARRAY_SIZE(wm8903->supplies),
+ wm8903->supplies);
+ if (ret != 0) {
+ dev_err(&i2c->dev, "Failed to enable supplies: %d\n", ret);
+ return ret;
+ }
+
ret = regmap_read(wm8903->regmap, WM8903_SW_RESET_AND_ID, &val);
if (ret != 0) {
dev_err(&i2c->dev, "Failed to read chip ID: %d\n", ret);
@@ -2160,6 +2187,8 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
return 0;
err:
+ regulator_bulk_disable(ARRAY_SIZE(wm8903->supplies),
+ wm8903->supplies);
return ret;
}
@@ -2167,6 +2196,8 @@ static int wm8903_i2c_remove(struct i2c_client *client)
{
struct wm8903_priv *wm8903 = i2c_get_clientdata(client);
+ regulator_bulk_disable(ARRAY_SIZE(wm8903->supplies),
+ wm8903->supplies);
if (client->irq)
free_irq(client->irq, wm8903);
wm8903_free_gpio(wm8903);
diff --git a/sound/soc/codecs/wm8960.c b/sound/soc/codecs/wm8960.c
index 3bf081a7e450..9ed455700954 100644
--- a/sound/soc/codecs/wm8960.c
+++ b/sound/soc/codecs/wm8960.c
@@ -604,12 +604,150 @@ static const int bclk_divs[] = {
120, 160, 220, 240, 320, 320, 320
};
+/**
+ * wm8960_configure_sysclk - checks if there is a sysclk frequency available
+ * The sysclk must be chosen such that:
+ * - sysclk = MCLK / sysclk_divs
+ * - lrclk = sysclk / dac_divs
+ * - 10 * bclk = sysclk / bclk_divs
+ *
+ * If we cannot find an exact match for (sysclk, lrclk, bclk)
+ * triplet, we relax the bclk such that bclk is chosen as the
+ * closest available frequency greater than expected bclk.
+ *
+ * @wm8960_priv: wm8960 codec private data
+ * @mclk: MCLK used to derive sysclk
+ * @sysclk_idx: sysclk_divs index for found sysclk
+ * @dac_idx: dac_divs index for found lrclk
+ * @bclk_idx: bclk_divs index for found bclk
+ *
+ * Returns:
+ * -1, in case no sysclk frequency available found
+ * >=0, in case we could derive bclk and lrclk from sysclk using
+ * (@sysclk_idx, @dac_idx, @bclk_idx) dividers
+ */
+static
+int wm8960_configure_sysclk(struct wm8960_priv *wm8960, int mclk,
+ int *sysclk_idx, int *dac_idx, int *bclk_idx)
+{
+ int sysclk, bclk, lrclk;
+ int i, j, k;
+ int diff, closest = mclk;
+
+ /* marker for no match */
+ *bclk_idx = -1;
+
+ bclk = wm8960->bclk;
+ lrclk = wm8960->lrclk;
+
+ /* check if the sysclk frequency is available. */
+ for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+ if (sysclk_divs[i] == -1)
+ continue;
+ sysclk = mclk / sysclk_divs[i];
+ for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+ if (sysclk != dac_divs[j] * lrclk)
+ continue;
+ for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
+ diff = sysclk - bclk * bclk_divs[k] / 10;
+ if (diff == 0) {
+ *sysclk_idx = i;
+ *dac_idx = j;
+ *bclk_idx = k;
+ break;
+ }
+ if (diff > 0 && closest > diff) {
+ *sysclk_idx = i;
+ *dac_idx = j;
+ *bclk_idx = k;
+ closest = diff;
+ }
+ }
+ if (k != ARRAY_SIZE(bclk_divs))
+ break;
+ }
+ if (j != ARRAY_SIZE(dac_divs))
+ break;
+ }
+ return *bclk_idx;
+}
+
+/**
+ * wm8960_configure_pll - checks if there is a PLL out frequency available
+ * The PLL out frequency must be chosen such that:
+ * - sysclk = lrclk * dac_divs
+ * - freq_out = sysclk * sysclk_divs
+ * - 10 * sysclk = bclk * bclk_divs
+ *
+ * If we cannot find an exact match for (sysclk, lrclk, bclk)
+ * triplet, we relax the bclk such that bclk is chosen as the
+ * closest available frequency greater than expected bclk.
+ *
+ * @codec: codec structure
+ * @freq_in: input frequency used to derive freq out via PLL
+ * @sysclk_idx: sysclk_divs index for found sysclk
+ * @dac_idx: dac_divs index for found lrclk
+ * @bclk_idx: bclk_divs index for found bclk
+ *
+ * Returns:
+ * < 0, in case no PLL frequency out available was found
+ * >=0, in case we could derive bclk, lrclk, sysclk from PLL out using
+ * (@sysclk_idx, @dac_idx, @bclk_idx) dividers
+ */
+static
+int wm8960_configure_pll(struct snd_soc_codec *codec, int freq_in,
+ int *sysclk_idx, int *dac_idx, int *bclk_idx)
+{
+ struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+ int sysclk, bclk, lrclk, freq_out;
+ int diff, closest, best_freq_out;
+ int i, j, k;
+
+ bclk = wm8960->bclk;
+ lrclk = wm8960->lrclk;
+ closest = freq_in;
+
+ best_freq_out = -EINVAL;
+ *sysclk_idx = *dac_idx = *bclk_idx = -1;
+
+ for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
+ if (sysclk_divs[i] == -1)
+ continue;
+ for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
+ sysclk = lrclk * dac_divs[j];
+ freq_out = sysclk * sysclk_divs[i];
+
+ for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
+ if (!is_pll_freq_available(freq_in, freq_out))
+ continue;
+
+ diff = sysclk - bclk * bclk_divs[k] / 10;
+ if (diff == 0) {
+ *sysclk_idx = i;
+ *dac_idx = j;
+ *bclk_idx = k;
+ return freq_out;
+ }
+ if (diff > 0 && closest > diff) {
+ *sysclk_idx = i;
+ *dac_idx = j;
+ *bclk_idx = k;
+ closest = diff;
+ best_freq_out = freq_out;
+ }
+ }
+ }
+ }
+
+ return best_freq_out;
+}
static int wm8960_configure_clocking(struct snd_soc_codec *codec)
{
struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
- int sysclk, bclk, lrclk, freq_out, freq_in;
+ int freq_out, freq_in;
u16 iface1 = snd_soc_read(codec, WM8960_IFACE1);
int i, j, k;
+ int ret;
if (!(iface1 & (1<<6))) {
dev_dbg(codec->dev,
@@ -623,8 +761,6 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec)
}
freq_in = wm8960->freq_in;
- bclk = wm8960->bclk;
- lrclk = wm8960->lrclk;
/*
* If it's sysclk auto mode, check if the MCLK can provide sysclk or
* not. If MCLK can provide sysclk, using MCLK to provide sysclk
@@ -643,60 +779,21 @@ static int wm8960_configure_clocking(struct snd_soc_codec *codec)
}
if (wm8960->clk_id != WM8960_SYSCLK_PLL) {
- /* check if the sysclk frequency is available. */
- for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
- if (sysclk_divs[i] == -1)
- continue;
- sysclk = freq_out / sysclk_divs[i];
- for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
- if (sysclk != dac_divs[j] * lrclk)
- continue;
- for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k)
- if (sysclk == bclk * bclk_divs[k] / 10)
- break;
- if (k != ARRAY_SIZE(bclk_divs))
- break;
- }
- if (j != ARRAY_SIZE(dac_divs))
- break;
- }
-
- if (i != ARRAY_SIZE(sysclk_divs)) {
+ ret = wm8960_configure_sysclk(wm8960, freq_out, &i, &j, &k);
+ if (ret >= 0) {
goto configure_clock;
} else if (wm8960->clk_id != WM8960_SYSCLK_AUTO) {
dev_err(codec->dev, "failed to configure clock\n");
return -EINVAL;
}
}
- /* get a available pll out frequency and set pll */
- for (i = 0; i < ARRAY_SIZE(sysclk_divs); ++i) {
- if (sysclk_divs[i] == -1)
- continue;
- for (j = 0; j < ARRAY_SIZE(dac_divs); ++j) {
- sysclk = lrclk * dac_divs[j];
- freq_out = sysclk * sysclk_divs[i];
- for (k = 0; k < ARRAY_SIZE(bclk_divs); ++k) {
- if (sysclk == bclk * bclk_divs[k] / 10 &&
- is_pll_freq_available(freq_in, freq_out)) {
- wm8960_set_pll(codec,
- freq_in, freq_out);
- break;
- } else {
- continue;
- }
- }
- if (k != ARRAY_SIZE(bclk_divs))
- break;
- }
- if (j != ARRAY_SIZE(dac_divs))
- break;
- }
-
- if (i == ARRAY_SIZE(sysclk_divs)) {
- dev_err(codec->dev, "failed to configure clock\n");
- return -EINVAL;
+ freq_out = wm8960_configure_pll(codec, freq_in, &i, &j, &k);
+ if (freq_out < 0) {
+ dev_err(codec->dev, "failed to configure clock via PLL\n");
+ return freq_out;
}
+ wm8960_set_pll(codec, freq_in, freq_out);
configure_clock:
/* configure sysclk clock */
diff --git a/sound/soc/codecs/wm8978.c b/sound/soc/codecs/wm8978.c
index 90b2d418ef60..cf761e2d7546 100644
--- a/sound/soc/codecs/wm8978.c
+++ b/sound/soc/codecs/wm8978.c
@@ -1071,9 +1071,16 @@ static const struct i2c_device_id wm8978_i2c_id[] = {
};
MODULE_DEVICE_TABLE(i2c, wm8978_i2c_id);
+static const struct of_device_id wm8978_of_match[] = {
+ { .compatible = "wlf,wm8978", },
+ { }
+};
+MODULE_DEVICE_TABLE(of, wm8978_of_match);
+
static struct i2c_driver wm8978_i2c_driver = {
.driver = {
.name = "wm8978",
+ .of_match_table = wm8978_of_match,
},
.probe = wm8978_i2c_probe,
.remove = wm8978_i2c_remove,
diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c
index bbdb72f73df1..20695b691aff 100644
--- a/sound/soc/codecs/wm_adsp.c
+++ b/sound/soc/codecs/wm_adsp.c
@@ -112,17 +112,22 @@
#define ADSP1_CLK_SEL_SHIFT 0 /* CLK_SEL_ENA */
#define ADSP1_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
-#define ADSP2_CONTROL 0x0
-#define ADSP2_CLOCKING 0x1
-#define ADSP2_STATUS1 0x4
-#define ADSP2_WDMA_CONFIG_1 0x30
-#define ADSP2_WDMA_CONFIG_2 0x31
-#define ADSP2_RDMA_CONFIG_1 0x34
-
-#define ADSP2_SCRATCH0 0x40
-#define ADSP2_SCRATCH1 0x41
-#define ADSP2_SCRATCH2 0x42
-#define ADSP2_SCRATCH3 0x43
+#define ADSP2_CONTROL 0x0
+#define ADSP2_CLOCKING 0x1
+#define ADSP2V2_CLOCKING 0x2
+#define ADSP2_STATUS1 0x4
+#define ADSP2_WDMA_CONFIG_1 0x30
+#define ADSP2_WDMA_CONFIG_2 0x31
+#define ADSP2V2_WDMA_CONFIG_2 0x32
+#define ADSP2_RDMA_CONFIG_1 0x34
+
+#define ADSP2_SCRATCH0 0x40
+#define ADSP2_SCRATCH1 0x41
+#define ADSP2_SCRATCH2 0x42
+#define ADSP2_SCRATCH3 0x43
+
+#define ADSP2V2_SCRATCH0_1 0x40
+#define ADSP2V2_SCRATCH2_3 0x42
/*
* ADSP2 Control
@@ -153,6 +158,17 @@
#define ADSP2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
/*
+ * ADSP2V2 clocking
+ */
+#define ADSP2V2_CLK_SEL_MASK 0x70000 /* CLK_SEL_ENA */
+#define ADSP2V2_CLK_SEL_SHIFT 16 /* CLK_SEL_ENA */
+#define ADSP2V2_CLK_SEL_WIDTH 3 /* CLK_SEL_ENA */
+
+#define ADSP2V2_RATE_MASK 0x7800 /* DSP_RATE */
+#define ADSP2V2_RATE_SHIFT 11 /* DSP_RATE */
+#define ADSP2V2_RATE_WIDTH 4 /* DSP_RATE */
+
+/*
* ADSP2 Status 1
*/
#define ADSP2_RAM_RDY 0x0001
@@ -160,6 +176,37 @@
#define ADSP2_RAM_RDY_SHIFT 0
#define ADSP2_RAM_RDY_WIDTH 1
+/*
+ * ADSP2 Lock support
+ */
+#define ADSP2_LOCK_CODE_0 0x5555
+#define ADSP2_LOCK_CODE_1 0xAAAA
+
+#define ADSP2_WATCHDOG 0x0A
+#define ADSP2_BUS_ERR_ADDR 0x52
+#define ADSP2_REGION_LOCK_STATUS 0x64
+#define ADSP2_LOCK_REGION_1_LOCK_REGION_0 0x66
+#define ADSP2_LOCK_REGION_3_LOCK_REGION_2 0x68
+#define ADSP2_LOCK_REGION_5_LOCK_REGION_4 0x6A
+#define ADSP2_LOCK_REGION_7_LOCK_REGION_6 0x6C
+#define ADSP2_LOCK_REGION_9_LOCK_REGION_8 0x6E
+#define ADSP2_LOCK_REGION_CTRL 0x7A
+#define ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR 0x7C
+
+#define ADSP2_REGION_LOCK_ERR_MASK 0x8000
+#define ADSP2_SLAVE_ERR_MASK 0x4000
+#define ADSP2_WDT_TIMEOUT_STS_MASK 0x2000
+#define ADSP2_CTRL_ERR_PAUSE_ENA 0x0002
+#define ADSP2_CTRL_ERR_EINT 0x0001
+
+#define ADSP2_BUS_ERR_ADDR_MASK 0x00FFFFFF
+#define ADSP2_XMEM_ERR_ADDR_MASK 0x0000FFFF
+#define ADSP2_PMEM_ERR_ADDR_MASK 0x7FFF0000
+#define ADSP2_PMEM_ERR_ADDR_SHIFT 16
+#define ADSP2_WDT_ENA_MASK 0xFFFFFFFD
+
+#define ADSP2_LOCK_REGION_SHIFT 16
+
#define ADSP_MAX_STD_CTRL_SIZE 512
#define WM_ADSP_ACKED_CTL_TIMEOUT_MS 100
@@ -683,6 +730,9 @@ static const struct soc_enum wm_adsp_fw_enum[] = {
SOC_ENUM_SINGLE(0, 1, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
SOC_ENUM_SINGLE(0, 2, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
SOC_ENUM_SINGLE(0, 3, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 4, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 5, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
+ SOC_ENUM_SINGLE(0, 6, ARRAY_SIZE(wm_adsp_fw_text), wm_adsp_fw_text),
};
const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
@@ -694,6 +744,12 @@ const struct snd_kcontrol_new wm_adsp_fw_controls[] = {
wm_adsp_fw_get, wm_adsp_fw_put),
SOC_ENUM_EXT("DSP4 Firmware", wm_adsp_fw_enum[3],
wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM_EXT("DSP5 Firmware", wm_adsp_fw_enum[4],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM_EXT("DSP6 Firmware", wm_adsp_fw_enum[5],
+ wm_adsp_fw_get, wm_adsp_fw_put),
+ SOC_ENUM_EXT("DSP7 Firmware", wm_adsp_fw_enum[6],
+ wm_adsp_fw_get, wm_adsp_fw_put),
};
EXPORT_SYMBOL_GPL(wm_adsp_fw_controls);
@@ -750,6 +806,29 @@ static void wm_adsp2_show_fw_status(struct wm_adsp *dsp)
be16_to_cpu(scratch[3]));
}
+static void wm_adsp2v2_show_fw_status(struct wm_adsp *dsp)
+{
+ u32 scratch[2];
+ int ret;
+
+ ret = regmap_raw_read(dsp->regmap, dsp->base + ADSP2V2_SCRATCH0_1,
+ scratch, sizeof(scratch));
+
+ if (ret) {
+ adsp_err(dsp, "Failed to read SCRATCH regs: %d\n", ret);
+ return;
+ }
+
+ scratch[0] = be32_to_cpu(scratch[0]);
+ scratch[1] = be32_to_cpu(scratch[1]);
+
+ adsp_dbg(dsp, "FW SCRATCH 0:0x%x 1:0x%x 2:0x%x 3:0x%x\n",
+ scratch[0] & 0xFFFF,
+ scratch[0] >> 16,
+ scratch[1] & 0xFFFF,
+ scratch[1] >> 16);
+}
+
static inline struct wm_coeff_ctl *bytes_ext_to_ctl(struct soc_bytes_ext *ext)
{
return container_of(ext, struct wm_coeff_ctl, bytes_ext);
@@ -2435,10 +2514,17 @@ static int wm_adsp2_ena(struct wm_adsp *dsp)
unsigned int val;
int ret, count;
- ret = regmap_update_bits_async(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA, ADSP2_SYS_ENA);
- if (ret != 0)
- return ret;
+ switch (dsp->rev) {
+ case 0:
+ ret = regmap_update_bits_async(dsp->regmap,
+ dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, ADSP2_SYS_ENA);
+ if (ret != 0)
+ return ret;
+ break;
+ default:
+ break;
+ }
/* Wait for the RAM to start, should be near instantaneous */
for (count = 0; count < 10; ++count) {
@@ -2497,11 +2583,17 @@ static void wm_adsp2_boot_work(struct work_struct *work)
if (ret != 0)
goto err_ena;
- /* 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;
+ switch (dsp->rev) {
+ case 0:
+ /* 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;
+ break;
+ default:
+ break;
+ }
dsp->booted = true;
@@ -2523,12 +2615,21 @@ static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)
{
int ret;
- ret = regmap_update_bits_async(dsp->regmap,
- dsp->base + ADSP2_CLOCKING,
- ADSP2_CLK_SEL_MASK,
- freq << ADSP2_CLK_SEL_SHIFT);
- if (ret != 0)
- adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
+ switch (dsp->rev) {
+ case 0:
+ ret = regmap_update_bits_async(dsp->regmap,
+ dsp->base + ADSP2_CLOCKING,
+ ADSP2_CLK_SEL_MASK,
+ freq << ADSP2_CLK_SEL_SHIFT);
+ if (ret) {
+ adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
+ return;
+ }
+ break;
+ default:
+ /* clock is handled by parent codec driver */
+ break;
+ }
}
int wm_adsp2_preloader_get(struct snd_kcontrol *kcontrol,
@@ -2568,6 +2669,18 @@ int wm_adsp2_preloader_put(struct snd_kcontrol *kcontrol,
}
EXPORT_SYMBOL_GPL(wm_adsp2_preloader_put);
+static void wm_adsp_stop_watchdog(struct wm_adsp *dsp)
+{
+ switch (dsp->rev) {
+ case 0:
+ case 1:
+ return;
+ default:
+ regmap_update_bits(dsp->regmap, dsp->base + ADSP2_WATCHDOG,
+ ADSP2_WDT_ENA_MASK, 0);
+ }
+}
+
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event,
unsigned int freq)
@@ -2640,6 +2753,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
if (ret != 0)
goto err;
+ wm_adsp2_lock(dsp, dsp->lock_regions);
+
ret = regmap_update_bits(dsp->regmap,
dsp->base + ADSP2_CONTROL,
ADSP2_CORE_ENA | ADSP2_START,
@@ -2663,23 +2778,49 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
/* Tell the firmware to cleanup */
wm_adsp_signal_event_controls(dsp, WM_ADSP_FW_EVENT_SHUTDOWN);
+ wm_adsp_stop_watchdog(dsp);
+
/* Log firmware state, it can be useful for analysis */
- wm_adsp2_show_fw_status(dsp);
+ switch (dsp->rev) {
+ case 0:
+ wm_adsp2_show_fw_status(dsp);
+ break;
+ default:
+ wm_adsp2v2_show_fw_status(dsp);
+ break;
+ }
mutex_lock(&dsp->pwr_lock);
dsp->running = false;
- regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ regmap_update_bits(dsp->regmap,
+ dsp->base + ADSP2_CONTROL,
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_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_SYS_ENA, 0);
+ switch (dsp->rev) {
+ case 0:
+ 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_update_bits(dsp->regmap,
+ dsp->base + ADSP2_CONTROL,
+ ADSP2_SYS_ENA, 0);
+ break;
+ default:
+ 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 + ADSP2V2_WDMA_CONFIG_2, 0);
+ break;
+ }
if (wm_adsp_fw[dsp->fw].num_caps != 0)
wm_adsp_buffer_free(dsp);
@@ -2732,15 +2873,22 @@ int wm_adsp2_init(struct wm_adsp *dsp)
{
int ret;
- /*
- * Disable the DSP memory by default when in reset for a small
- * power saving.
- */
- ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
- ADSP2_MEM_ENA, 0);
- if (ret != 0) {
- adsp_err(dsp, "Failed to clear memory retention: %d\n", ret);
- return ret;
+ switch (dsp->rev) {
+ case 0:
+ /*
+ * Disable the DSP memory by default when in reset for a small
+ * power saving.
+ */
+ ret = regmap_update_bits(dsp->regmap, dsp->base + ADSP2_CONTROL,
+ ADSP2_MEM_ENA, 0);
+ if (ret) {
+ adsp_err(dsp,
+ "Failed to clear memory retention: %d\n", ret);
+ return ret;
+ }
+ break;
+ default:
+ break;
}
INIT_LIST_HEAD(&dsp->alg_regions);
@@ -3523,4 +3671,94 @@ int wm_adsp_compr_copy(struct snd_compr_stream *stream, char __user *buf,
}
EXPORT_SYMBOL_GPL(wm_adsp_compr_copy);
+int wm_adsp2_lock(struct wm_adsp *dsp, unsigned int lock_regions)
+{
+ struct regmap *regmap = dsp->regmap;
+ unsigned int code0, code1, lock_reg;
+
+ if (!(lock_regions & WM_ADSP2_REGION_ALL))
+ return 0;
+
+ lock_regions &= WM_ADSP2_REGION_ALL;
+ lock_reg = dsp->base + ADSP2_LOCK_REGION_1_LOCK_REGION_0;
+
+ while (lock_regions) {
+ code0 = code1 = 0;
+ if (lock_regions & BIT(0)) {
+ code0 = ADSP2_LOCK_CODE_0;
+ code1 = ADSP2_LOCK_CODE_1;
+ }
+ if (lock_regions & BIT(1)) {
+ code0 |= ADSP2_LOCK_CODE_0 << ADSP2_LOCK_REGION_SHIFT;
+ code1 |= ADSP2_LOCK_CODE_1 << ADSP2_LOCK_REGION_SHIFT;
+ }
+ regmap_write(regmap, lock_reg, code0);
+ regmap_write(regmap, lock_reg, code1);
+ lock_regions >>= 2;
+ lock_reg += 2;
+ }
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_lock);
+
+irqreturn_t wm_adsp2_bus_error(struct wm_adsp *dsp)
+{
+ unsigned int val;
+ struct regmap *regmap = dsp->regmap;
+ int ret = 0;
+
+ ret = regmap_read(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL, &val);
+ if (ret) {
+ adsp_err(dsp,
+ "Failed to read Region Lock Ctrl register: %d\n", ret);
+ return IRQ_HANDLED;
+ }
+
+ if (val & ADSP2_WDT_TIMEOUT_STS_MASK) {
+ adsp_err(dsp, "watchdog timeout error\n");
+ wm_adsp_stop_watchdog(dsp);
+ }
+
+ if (val & (ADSP2_SLAVE_ERR_MASK | ADSP2_REGION_LOCK_ERR_MASK)) {
+ if (val & ADSP2_SLAVE_ERR_MASK)
+ adsp_err(dsp, "bus error: slave error\n");
+ else
+ adsp_err(dsp, "bus error: region lock error\n");
+
+ ret = regmap_read(regmap, dsp->base + ADSP2_BUS_ERR_ADDR, &val);
+ if (ret) {
+ adsp_err(dsp,
+ "Failed to read Bus Err Addr register: %d\n",
+ ret);
+ return IRQ_HANDLED;
+ }
+
+ adsp_err(dsp, "bus error address = 0x%x\n",
+ val & ADSP2_BUS_ERR_ADDR_MASK);
+
+ ret = regmap_read(regmap,
+ dsp->base + ADSP2_PMEM_ERR_ADDR_XMEM_ERR_ADDR,
+ &val);
+ if (ret) {
+ adsp_err(dsp,
+ "Failed to read Pmem Xmem Err Addr register: %d\n",
+ ret);
+ return IRQ_HANDLED;
+ }
+
+ adsp_err(dsp, "xmem error address = 0x%x\n",
+ val & ADSP2_XMEM_ERR_ADDR_MASK);
+ adsp_err(dsp, "pmem error address = 0x%x\n",
+ (val & ADSP2_PMEM_ERR_ADDR_MASK) >>
+ ADSP2_PMEM_ERR_ADDR_SHIFT);
+ }
+
+ regmap_update_bits(regmap, dsp->base + ADSP2_LOCK_REGION_CTRL,
+ ADSP2_CTRL_ERR_EINT, ADSP2_CTRL_ERR_EINT);
+
+ return IRQ_HANDLED;
+}
+EXPORT_SYMBOL_GPL(wm_adsp2_bus_error);
+
MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/wm_adsp.h b/sound/soc/codecs/wm_adsp.h
index 3706b11053a3..41cc11c19b83 100644
--- a/sound/soc/codecs/wm_adsp.h
+++ b/sound/soc/codecs/wm_adsp.h
@@ -23,6 +23,23 @@
#define WM_ADSP_COMPR_OK 0
#define WM_ADSP_COMPR_VOICE_TRIGGER 1
+#define WM_ADSP2_REGION_0 BIT(0)
+#define WM_ADSP2_REGION_1 BIT(1)
+#define WM_ADSP2_REGION_2 BIT(2)
+#define WM_ADSP2_REGION_3 BIT(3)
+#define WM_ADSP2_REGION_4 BIT(4)
+#define WM_ADSP2_REGION_5 BIT(5)
+#define WM_ADSP2_REGION_6 BIT(6)
+#define WM_ADSP2_REGION_7 BIT(7)
+#define WM_ADSP2_REGION_8 BIT(8)
+#define WM_ADSP2_REGION_9 BIT(9)
+#define WM_ADSP2_REGION_1_9 (WM_ADSP2_REGION_1 | \
+ WM_ADSP2_REGION_2 | WM_ADSP2_REGION_3 | \
+ WM_ADSP2_REGION_4 | WM_ADSP2_REGION_5 | \
+ WM_ADSP2_REGION_6 | WM_ADSP2_REGION_7 | \
+ WM_ADSP2_REGION_8 | WM_ADSP2_REGION_9)
+#define WM_ADSP2_REGION_ALL (WM_ADSP2_REGION_0 | WM_ADSP2_REGION_1_9)
+
struct wm_adsp_region {
int type;
unsigned int base;
@@ -40,6 +57,7 @@ struct wm_adsp_compr_buf;
struct wm_adsp {
const char *part;
+ int rev;
int num;
int type;
struct device *dev;
@@ -75,6 +93,8 @@ struct wm_adsp {
struct mutex pwr_lock;
+ unsigned int lock_regions;
+
#ifdef CONFIG_DEBUG_FS
struct dentry *debugfs_root;
char *wmfw_file_name;
@@ -113,6 +133,10 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event,
unsigned int freq);
+
+int wm_adsp2_lock(struct wm_adsp *adsp, unsigned int regions);
+irqreturn_t wm_adsp2_bus_error(struct wm_adsp *adsp);
+
int wm_adsp2_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *kcontrol, int event);
diff --git a/sound/soc/dwc/Kconfig b/sound/soc/dwc/Kconfig
index c297efe43861..c6fd95fa5ca6 100644
--- a/sound/soc/dwc/Kconfig
+++ b/sound/soc/dwc/Kconfig
@@ -8,10 +8,10 @@ config SND_DESIGNWARE_I2S
maximum of 8 channels each for play and record.
config SND_DESIGNWARE_PCM
- tristate "PCM PIO extension for I2S driver"
+ bool "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
+ Say Y or N if you want to add a custom ALSA extension that registers
a PCM and uses PIO to transfer data.
This functionality is specially suited for I2S devices that don't have
diff --git a/sound/soc/dwc/Makefile b/sound/soc/dwc/Makefile
index 38f1ca31c5fa..3e24c0ff95fb 100644
--- a/sound/soc/dwc/Makefile
+++ b/sound/soc/dwc/Makefile
@@ -1,5 +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
+
+designware_i2s-y := dwc-i2s.o
+designware_i2s-$(CONFIG_SND_DESIGNWARE_PCM) += dwc-pcm.o
diff --git a/sound/soc/dwc/designware_i2s.c b/sound/soc/dwc/dwc-i2s.c
index 9c46e4112026..9c46e4112026 100644
--- a/sound/soc/dwc/designware_i2s.c
+++ b/sound/soc/dwc/dwc-i2s.c
diff --git a/sound/soc/dwc/designware_pcm.c b/sound/soc/dwc/dwc-pcm.c
index 459ec861e6b6..406fd867117b 100644
--- a/sound/soc/dwc/designware_pcm.c
+++ b/sound/soc/dwc/dwc-pcm.c
@@ -129,13 +129,11 @@ void dw_pcm_push_tx(struct dw_i2s_dev *dev)
{
dw_pcm_transfer(dev, true);
}
-EXPORT_SYMBOL_GPL(dw_pcm_push_tx);
void dw_pcm_pop_rx(struct dw_i2s_dev *dev)
{
dw_pcm_transfer(dev, false);
}
-EXPORT_SYMBOL_GPL(dw_pcm_pop_rx);
static int dw_pcm_open(struct snd_pcm_substream *substream)
{
@@ -281,4 +279,3 @@ 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/fsl/eukrea-tlv320.c b/sound/soc/fsl/eukrea-tlv320.c
index 883087f2b092..84ef6385736c 100644
--- a/sound/soc/fsl/eukrea-tlv320.c
+++ b/sound/soc/fsl/eukrea-tlv320.c
@@ -64,7 +64,7 @@ static int eukrea_tlv320_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops eukrea_tlv320_snd_ops = {
+static const struct snd_soc_ops eukrea_tlv320_snd_ops = {
.hw_params = eukrea_tlv320_hw_params,
};
diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c
index dc30d780f874..282d841840b1 100644
--- a/sound/soc/fsl/fsl_asrc_dma.c
+++ b/sound/soc/fsl/fsl_asrc_dma.c
@@ -76,7 +76,7 @@ static int fsl_asrc_dma_prepare_and_submit(struct snd_pcm_substream *substream)
pair->dma_chan[!dir], runtime->dma_addr,
snd_pcm_lib_buffer_bytes(substream),
snd_pcm_lib_period_bytes(substream),
- dir == OUT ? DMA_TO_DEVICE : DMA_FROM_DEVICE, flags);
+ dir == OUT ? DMA_MEM_TO_DEV : DMA_DEV_TO_MEM, flags);
if (!pair->desc[!dir]) {
dev_err(dev, "failed to prepare slave DMA for Front-End\n");
return -ENOMEM;
diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c
index 38bfd46f4ad8..809a069d490b 100644
--- a/sound/soc/fsl/fsl_esai.c
+++ b/sound/soc/fsl/fsl_esai.c
@@ -19,7 +19,6 @@
#include "fsl_esai.h"
#include "imx-pcm.h"
-#define FSL_ESAI_RATES SNDRV_PCM_RATE_8000_192000
#define FSL_ESAI_FORMATS (SNDRV_PCM_FMTBIT_S8 | \
SNDRV_PCM_FMTBIT_S16_LE | \
SNDRV_PCM_FMTBIT_S20_3LE | \
@@ -647,14 +646,14 @@ static struct snd_soc_dai_driver fsl_esai_dai = {
.stream_name = "CPU-Playback",
.channels_min = 1,
.channels_max = 12,
- .rates = FSL_ESAI_RATES,
+ .rates = SNDRV_PCM_RATE_8000_192000,
.formats = FSL_ESAI_FORMATS,
},
.capture = {
.stream_name = "CPU-Capture",
.channels_min = 1,
.channels_max = 8,
- .rates = FSL_ESAI_RATES,
+ .rates = SNDRV_PCM_RATE_8000_192000,
.formats = FSL_ESAI_FORMATS,
},
.ops = &fsl_esai_dai_ops,
diff --git a/sound/soc/fsl/fsl_ssi.c b/sound/soc/fsl/fsl_ssi.c
index fde08660b63b..173cb8496641 100644
--- a/sound/soc/fsl/fsl_ssi.c
+++ b/sound/soc/fsl/fsl_ssi.c
@@ -35,6 +35,7 @@
#include <linux/module.h>
#include <linux/interrupt.h>
#include <linux/clk.h>
+#include <linux/ctype.h>
#include <linux/device.h>
#include <linux/delay.h>
#include <linux/slab.h>
@@ -55,16 +56,6 @@
#include "imx-pcm.h"
/**
- * FSLSSI_I2S_RATES: sample rates supported by the I2S
- *
- * This driver currently only supports the SSI running in I2S slave mode,
- * which means the codec determines the sample rate. Therefore, we tell
- * ALSA that we support all rates and let the codec driver decide what rates
- * are really supported.
- */
-#define FSLSSI_I2S_RATES SNDRV_PCM_RATE_CONTINUOUS
-
-/**
* FSLSSI_I2S_FORMATS: audio formats supported by the SSI
*
* The SSI has a limitation in that the samples must be in the same byte
@@ -1212,14 +1203,14 @@ static struct snd_soc_dai_driver fsl_ssi_dai_template = {
.stream_name = "CPU-Playback",
.channels_min = 1,
.channels_max = 32,
- .rates = FSLSSI_I2S_RATES,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = FSLSSI_I2S_FORMATS,
},
.capture = {
.stream_name = "CPU-Capture",
.channels_min = 1,
.channels_max = 32,
- .rates = FSLSSI_I2S_RATES,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
.formats = FSLSSI_I2S_FORMATS,
},
.ops = &fsl_ssi_dai_ops,
@@ -1325,14 +1316,10 @@ static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = {
*/
static void make_lowercase(char *s)
{
- char *p = s;
- char c;
-
- while ((c = *p)) {
- if ((c >= 'A') && (c <= 'Z'))
- *p = c + ('a' - 'A');
- p++;
- }
+ if (!s)
+ return;
+ for (; *s; s++)
+ *s = tolower(*s);
}
static int fsl_ssi_imx_probe(struct platform_device *pdev,
diff --git a/sound/soc/fsl/imx-mc13783.c b/sound/soc/fsl/imx-mc13783.c
index bb0459018b45..9d19b808f634 100644
--- a/sound/soc/fsl/imx-mc13783.c
+++ b/sound/soc/fsl/imx-mc13783.c
@@ -48,7 +48,7 @@ static int imx_mc13783_hifi_hw_params(struct snd_pcm_substream *substream,
return snd_soc_dai_set_tdm_slot(cpu_dai, 0x3, 0x3, 2, 16);
}
-static struct snd_soc_ops imx_mc13783_hifi_ops = {
+static const struct snd_soc_ops imx_mc13783_hifi_ops = {
.hw_params = imx_mc13783_hifi_hw_params,
};
diff --git a/sound/soc/fsl/imx-pcm-dma.c b/sound/soc/fsl/imx-pcm-dma.c
index f3d3d1ffa84e..314814ddd2b0 100644
--- a/sound/soc/fsl/imx-pcm-dma.c
+++ b/sound/soc/fsl/imx-pcm-dma.c
@@ -33,48 +33,20 @@ static bool filter(struct dma_chan *chan, void *param)
return true;
}
-static const struct snd_pcm_hardware imx_pcm_hardware = {
- .info = SNDRV_PCM_INFO_INTERLEAVED |
- SNDRV_PCM_INFO_BLOCK_TRANSFER |
- SNDRV_PCM_INFO_MMAP |
- SNDRV_PCM_INFO_MMAP_VALID |
- SNDRV_PCM_INFO_PAUSE |
- SNDRV_PCM_INFO_RESUME,
- .buffer_bytes_max = IMX_DEFAULT_DMABUF_SIZE,
- .period_bytes_min = 128,
- .period_bytes_max = 65535, /* Limited by SDMA engine */
- .periods_min = 2,
- .periods_max = 255,
- .fifo_size = 0,
-};
-
static const struct snd_dmaengine_pcm_config imx_dmaengine_pcm_config = {
- .pcm_hardware = &imx_pcm_hardware,
.prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
.compat_filter_fn = filter,
- .prealloc_buffer_size = IMX_DEFAULT_DMABUF_SIZE,
};
int imx_pcm_dma_init(struct platform_device *pdev, size_t size)
{
struct snd_dmaengine_pcm_config *config;
- struct snd_pcm_hardware *pcm_hardware;
config = devm_kzalloc(&pdev->dev,
sizeof(struct snd_dmaengine_pcm_config), GFP_KERNEL);
if (!config)
return -ENOMEM;
*config = imx_dmaengine_pcm_config;
- if (size)
- config->prealloc_buffer_size = size;
-
- pcm_hardware = devm_kzalloc(&pdev->dev,
- sizeof(struct snd_pcm_hardware), GFP_KERNEL);
- *pcm_hardware = imx_pcm_hardware;
- if (size)
- pcm_hardware->buffer_bytes_max = size;
-
- config->pcm_hardware = pcm_hardware;
return devm_snd_dmaengine_pcm_register(&pdev->dev,
config,
diff --git a/sound/soc/fsl/imx-pcm-fiq.c b/sound/soc/fsl/imx-pcm-fiq.c
index dac6688540dc..92410f7ca1fa 100644
--- a/sound/soc/fsl/imx-pcm-fiq.c
+++ b/sound/soc/fsl/imx-pcm-fiq.c
@@ -282,7 +282,7 @@ static int imx_pcm_new(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static int ssi_irq = 0;
+static int ssi_irq;
static int imx_pcm_fiq_new(struct snd_soc_pcm_runtime *rtd)
{
diff --git a/sound/soc/fsl/imx-wm8962.c b/sound/soc/fsl/imx-wm8962.c
index 1b60958e2080..206b898e554c 100644
--- a/sound/soc/fsl/imx-wm8962.c
+++ b/sound/soc/fsl/imx-wm8962.c
@@ -33,14 +33,14 @@ struct imx_wm8962_data {
struct snd_soc_card card;
char codec_dai_name[DAI_NAME_SIZE];
char platform_name[DAI_NAME_SIZE];
- struct clk *codec_clk;
unsigned int clk_frequency;
};
struct imx_priv {
struct platform_device *pdev;
+ int sample_rate;
+ snd_pcm_format_t sample_format;
};
-static struct imx_priv card_priv;
static const struct snd_soc_dapm_widget imx_wm8962_dapm_widgets[] = {
SND_SOC_DAPM_HP("Headphone Jack", NULL),
@@ -49,14 +49,14 @@ static const struct snd_soc_dapm_widget imx_wm8962_dapm_widgets[] = {
SND_SOC_DAPM_MIC("DMIC", NULL),
};
-static int sample_rate = 44100;
-static snd_pcm_format_t sample_format = SNDRV_PCM_FORMAT_S16_LE;
-
static int imx_hifi_hw_params(struct snd_pcm_substream *substream,
struct snd_pcm_hw_params *params)
{
- sample_rate = params_rate(params);
- sample_format = params_format(params);
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct imx_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+
+ priv->sample_rate = params_rate(params);
+ priv->sample_format = params_format(params);
return 0;
}
@@ -71,7 +71,7 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
{
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *codec_dai;
- struct imx_priv *priv = &card_priv;
+ struct imx_priv *priv = snd_soc_card_get_drvdata(card);
struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
struct device *dev = &priv->pdev->dev;
unsigned int pll_out;
@@ -85,10 +85,10 @@ static int imx_wm8962_set_bias_level(struct snd_soc_card *card,
switch (level) {
case SND_SOC_BIAS_PREPARE:
if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
- if (sample_format == SNDRV_PCM_FORMAT_S24_LE)
- pll_out = sample_rate * 384;
+ if (priv->sample_format == SNDRV_PCM_FORMAT_S24_LE)
+ pll_out = priv->sample_rate * 384;
else
- pll_out = sample_rate * 256;
+ pll_out = priv->sample_rate * 256;
ret = snd_soc_dai_set_pll(codec_dai, WM8962_FLL,
WM8962_FLL_MCLK, data->clk_frequency,
@@ -140,7 +140,7 @@ static int imx_wm8962_late_probe(struct snd_soc_card *card)
{
struct snd_soc_pcm_runtime *rtd;
struct snd_soc_dai *codec_dai;
- struct imx_priv *priv = &card_priv;
+ struct imx_priv *priv = snd_soc_card_get_drvdata(card);
struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
struct device *dev = &priv->pdev->dev;
int ret;
@@ -160,13 +160,20 @@ static int imx_wm8962_probe(struct platform_device *pdev)
struct device_node *np = pdev->dev.of_node;
struct device_node *ssi_np, *codec_np;
struct platform_device *ssi_pdev;
- struct imx_priv *priv = &card_priv;
struct i2c_client *codec_dev;
struct imx_wm8962_data *data;
+ struct imx_priv *priv;
+ struct clk *codec_clk;
int int_port, ext_port;
int ret;
+ priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
priv->pdev = pdev;
+ priv->sample_rate = 44100;
+ priv->sample_format = SNDRV_PCM_FORMAT_S16_LE;
ret = of_property_read_u32(np, "mux-int-port", &int_port);
if (ret) {
@@ -231,19 +238,15 @@ static int imx_wm8962_probe(struct platform_device *pdev)
goto fail;
}
- data->codec_clk = devm_clk_get(&codec_dev->dev, NULL);
- if (IS_ERR(data->codec_clk)) {
- ret = PTR_ERR(data->codec_clk);
+ codec_clk = clk_get(&codec_dev->dev, NULL);
+ if (IS_ERR(codec_clk)) {
+ ret = PTR_ERR(codec_clk);
dev_err(&codec_dev->dev, "failed to get codec clk: %d\n", ret);
goto fail;
}
- data->clk_frequency = clk_get_rate(data->codec_clk);
- ret = clk_prepare_enable(data->codec_clk);
- if (ret) {
- dev_err(&codec_dev->dev, "failed to enable codec clk: %d\n", ret);
- goto fail;
- }
+ data->clk_frequency = clk_get_rate(codec_clk);
+ clk_put(codec_clk);
data->dai.name = "HiFi";
data->dai.stream_name = "HiFi";
@@ -258,10 +261,10 @@ static int imx_wm8962_probe(struct platform_device *pdev)
data->card.dev = &pdev->dev;
ret = snd_soc_of_parse_card_name(&data->card, "model");
if (ret)
- goto clk_fail;
+ goto fail;
ret = snd_soc_of_parse_audio_routing(&data->card, "audio-routing");
if (ret)
- goto clk_fail;
+ goto fail;
data->card.num_links = 1;
data->card.owner = THIS_MODULE;
data->card.dai_link = &data->dai;
@@ -277,16 +280,9 @@ static int imx_wm8962_probe(struct platform_device *pdev)
ret = devm_snd_soc_register_card(&pdev->dev, &data->card);
if (ret) {
dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
- goto clk_fail;
+ goto fail;
}
- of_node_put(ssi_np);
- of_node_put(codec_np);
-
- return 0;
-
-clk_fail:
- clk_disable_unprepare(data->codec_clk);
fail:
of_node_put(ssi_np);
of_node_put(codec_np);
@@ -294,17 +290,6 @@ fail:
return ret;
}
-static int imx_wm8962_remove(struct platform_device *pdev)
-{
- struct snd_soc_card *card = platform_get_drvdata(pdev);
- struct imx_wm8962_data *data = snd_soc_card_get_drvdata(card);
-
- if (!IS_ERR(data->codec_clk))
- clk_disable_unprepare(data->codec_clk);
-
- return 0;
-}
-
static const struct of_device_id imx_wm8962_dt_ids[] = {
{ .compatible = "fsl,imx-audio-wm8962", },
{ /* sentinel */ }
@@ -318,7 +303,6 @@ static struct platform_driver imx_wm8962_driver = {
.of_match_table = imx_wm8962_dt_ids,
},
.probe = imx_wm8962_probe,
- .remove = imx_wm8962_remove,
};
module_platform_driver(imx_wm8962_driver);
diff --git a/sound/soc/fsl/mpc8610_hpcd.c b/sound/soc/fsl/mpc8610_hpcd.c
index ddf49f30b23f..a639b52c16f6 100644
--- a/sound/soc/fsl/mpc8610_hpcd.c
+++ b/sound/soc/fsl/mpc8610_hpcd.c
@@ -174,7 +174,7 @@ static int mpc8610_hpcd_machine_remove(struct snd_soc_card *card)
/**
* mpc8610_hpcd_ops: ASoC machine driver operations
*/
-static struct snd_soc_ops mpc8610_hpcd_ops = {
+static const struct snd_soc_ops mpc8610_hpcd_ops = {
.startup = mpc8610_hpcd_startup,
};
diff --git a/sound/soc/fsl/mx27vis-aic32x4.c b/sound/soc/fsl/mx27vis-aic32x4.c
index 198eeb3f3f7a..d7ec3d20065c 100644
--- a/sound/soc/fsl/mx27vis-aic32x4.c
+++ b/sound/soc/fsl/mx27vis-aic32x4.c
@@ -73,7 +73,7 @@ static int mx27vis_aic32x4_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops mx27vis_aic32x4_snd_ops = {
+static const struct snd_soc_ops mx27vis_aic32x4_snd_ops = {
.hw_params = mx27vis_aic32x4_hw_params,
};
diff --git a/sound/soc/fsl/p1022_ds.c b/sound/soc/fsl/p1022_ds.c
index a1f780ecadf5..41c623c55c16 100644
--- a/sound/soc/fsl/p1022_ds.c
+++ b/sound/soc/fsl/p1022_ds.c
@@ -184,7 +184,7 @@ static int p1022_ds_machine_remove(struct snd_soc_card *card)
/**
* p1022_ds_ops: ASoC machine driver operations
*/
-static struct snd_soc_ops p1022_ds_ops = {
+static const struct snd_soc_ops p1022_ds_ops = {
.startup = p1022_ds_startup,
};
diff --git a/sound/soc/fsl/p1022_rdk.c b/sound/soc/fsl/p1022_rdk.c
index d4d88a8cb9c0..4afbdd610bfa 100644
--- a/sound/soc/fsl/p1022_rdk.c
+++ b/sound/soc/fsl/p1022_rdk.c
@@ -188,7 +188,7 @@ static int p1022_rdk_machine_remove(struct snd_soc_card *card)
/**
* p1022_rdk_ops: ASoC machine driver operations
*/
-static struct snd_soc_ops p1022_rdk_ops = {
+static const struct snd_soc_ops p1022_rdk_ops = {
.startup = p1022_rdk_startup,
};
diff --git a/sound/soc/fsl/phycore-ac97.c b/sound/soc/fsl/phycore-ac97.c
index ae403c29688f..66fb6c4614d2 100644
--- a/sound/soc/fsl/phycore-ac97.c
+++ b/sound/soc/fsl/phycore-ac97.c
@@ -23,7 +23,7 @@
static struct snd_soc_card imx_phycore;
-static struct snd_soc_ops imx_phycore_hifi_ops = {
+static const struct snd_soc_ops imx_phycore_hifi_ops = {
};
static struct snd_soc_dai_link imx_phycore_dai_ac97[] = {
diff --git a/sound/soc/fsl/wm1133-ev1.c b/sound/soc/fsl/wm1133-ev1.c
index b454972dce35..cdaf16367b47 100644
--- a/sound/soc/fsl/wm1133-ev1.c
+++ b/sound/soc/fsl/wm1133-ev1.c
@@ -139,7 +139,7 @@ static int wm1133_ev1_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops wm1133_ev1_ops = {
+static const struct snd_soc_ops wm1133_ev1_ops = {
.hw_params = wm1133_ev1_hw_params,
};
diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c
index 85b4f1806514..2c9dedab5184 100644
--- a/sound/soc/generic/simple-card.c
+++ b/sound/soc/generic/simple-card.c
@@ -40,9 +40,10 @@ struct simple_card_data {
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_card(priv) (&(priv)->snd_card)
#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
+#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
+#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i))
#define DAI "sound-dai"
#define CELL "#sound-dai-cells"
@@ -323,6 +324,7 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node,
{
struct device *dev = simple_priv_to_dev(priv);
struct device_node *aux_node;
+ struct snd_soc_card *card = simple_priv_to_card(priv);
int i, n, len;
if (!of_find_property(node, PREFIX "aux-devs", &len))
@@ -332,19 +334,19 @@ static int asoc_simple_card_parse_aux_devs(struct device_node *node,
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)
+ card->aux_dev = devm_kzalloc(dev,
+ n * sizeof(*card->aux_dev), GFP_KERNEL);
+ if (!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;
+ card->aux_dev[i].codec_of_node = aux_node;
}
- priv->snd_card.num_aux_devs = n;
+ card->num_aux_devs = n;
return 0;
}
@@ -352,6 +354,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
struct simple_card_data *priv)
{
struct device *dev = simple_priv_to_dev(priv);
+ struct snd_soc_card *card = simple_priv_to_card(priv);
struct device_node *dai_link;
int ret;
@@ -362,7 +365,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
/* The off-codec widgets */
if (of_property_read_bool(node, PREFIX "widgets")) {
- ret = snd_soc_of_parse_audio_simple_widgets(&priv->snd_card,
+ ret = snd_soc_of_parse_audio_simple_widgets(card,
PREFIX "widgets");
if (ret)
goto card_parse_end;
@@ -370,7 +373,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
/* DAPM routes */
if (of_property_read_bool(node, PREFIX "routing")) {
- ret = snd_soc_of_parse_audio_routing(&priv->snd_card,
+ ret = snd_soc_of_parse_audio_routing(card,
PREFIX "routing");
if (ret)
goto card_parse_end;
@@ -401,7 +404,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
goto card_parse_end;
}
- ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
+ ret = asoc_simple_card_parse_card_name(card, PREFIX);
if (ret < 0)
goto card_parse_end;
@@ -418,8 +421,9 @@ 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;
+ struct device_node *np = dev->of_node;
+ struct snd_soc_card *card;
int num, ret;
/* Get the number of DAI links */
@@ -442,10 +446,11 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
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;
+ card = simple_priv_to_card(priv);
+ card->owner = THIS_MODULE;
+ card->dev = dev;
+ card->dai_link = priv->dai_link;
+ card->num_links = num;
if (np && of_device_is_available(np)) {
@@ -474,7 +479,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
return -EINVAL;
}
- priv->snd_card.name = (cinfo->card) ? cinfo->card : cinfo->name;
+ card->name = (cinfo->card) ? cinfo->card : cinfo->name;
dai_link->name = cinfo->name;
dai_link->stream_name = cinfo->name;
dai_link->platform_name = cinfo->platform;
@@ -489,13 +494,13 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
sizeof(priv->dai_props->codec_dai));
}
- snd_soc_card_set_drvdata(&priv->snd_card, priv);
+ snd_soc_card_set_drvdata(card, priv);
- ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
+ ret = devm_snd_soc_register_card(dev, card);
if (ret >= 0)
return ret;
err:
- asoc_simple_card_clean_reference(&priv->snd_card);
+ asoc_simple_card_clean_reference(card);
return ret;
}
diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c
index 308ff4c11a8d..dcbcab230d1b 100644
--- a/sound/soc/generic/simple-scu-card.c
+++ b/sound/soc/generic/simple-scu-card.c
@@ -31,9 +31,10 @@ struct simple_card_data {
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_card(priv) (&(priv)->snd_card)
#define simple_priv_to_props(priv, i) ((priv)->dai_props + (i))
+#define simple_priv_to_dev(priv) (simple_priv_to_card(priv)->dev)
+#define simple_priv_to_link(priv, i) (simple_priv_to_card(priv)->dai_link + (i))
#define DAI "sound-dai"
#define CELL "#sound-dai-cells"
@@ -109,6 +110,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,
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);
+ struct snd_soc_card *card = simple_priv_to_card(priv);
int ret;
if (is_fe) {
@@ -163,7 +165,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np,
if (ret < 0)
return ret;
- snd_soc_of_parse_audio_prefix(&priv->snd_card,
+ snd_soc_of_parse_audio_prefix(card,
&priv->codec_conf,
dai_link->codec_of_node,
PREFIX "prefix");
@@ -201,6 +203,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
{
struct device *dev = simple_priv_to_dev(priv);
struct device_node *np;
+ struct snd_soc_card *card = simple_priv_to_card(priv);
unsigned int daifmt = 0;
bool is_fe;
int ret, i;
@@ -208,7 +211,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
if (!node)
return -EINVAL;
- ret = snd_soc_of_parse_audio_routing(&priv->snd_card, PREFIX "routing");
+ ret = snd_soc_of_parse_audio_routing(card, PREFIX "routing");
if (ret < 0)
return ret;
@@ -239,12 +242,12 @@ static int asoc_simple_card_parse_of(struct device_node *node,
i++;
}
- ret = asoc_simple_card_parse_card_name(&priv->snd_card, PREFIX);
+ ret = asoc_simple_card_parse_card_name(card, PREFIX);
if (ret < 0)
return ret;
dev_dbg(dev, "New card: %s\n",
- priv->snd_card.name ? priv->snd_card.name : "");
+ card->name ? card->name : "");
dev_dbg(dev, "convert_rate %d\n", priv->convert_rate);
dev_dbg(dev, "convert_channels %d\n", priv->convert_channels);
@@ -256,8 +259,9 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
struct simple_card_data *priv;
struct snd_soc_dai_link *dai_link;
struct asoc_simple_dai *dai_props;
+ struct snd_soc_card *card;
struct device *dev = &pdev->dev;
- struct device_node *np = pdev->dev.of_node;
+ struct device_node *np = dev->of_node;
int num, ret;
/* Allocate the private data */
@@ -276,12 +280,13 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
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;
- priv->snd_card.codec_conf = &priv->codec_conf;
- priv->snd_card.num_configs = 1;
+ card = simple_priv_to_card(priv);
+ card->owner = THIS_MODULE;
+ card->dev = dev;
+ card->dai_link = priv->dai_link;
+ card->num_links = num;
+ card->codec_conf = &priv->codec_conf;
+ card->num_configs = 1;
ret = asoc_simple_card_parse_of(np, priv);
if (ret < 0) {
@@ -290,13 +295,13 @@ static int asoc_simple_card_probe(struct platform_device *pdev)
goto err;
}
- snd_soc_card_set_drvdata(&priv->snd_card, priv);
+ snd_soc_card_set_drvdata(card, priv);
- ret = devm_snd_soc_register_card(&pdev->dev, &priv->snd_card);
+ ret = devm_snd_soc_register_card(dev, card);
if (ret >= 0)
return ret;
err:
- asoc_simple_card_clean_reference(&priv->snd_card);
+ asoc_simple_card_clean_reference(card);
return ret;
}
diff --git a/sound/soc/hisilicon/Kconfig b/sound/soc/hisilicon/Kconfig
new file mode 100644
index 000000000000..4356d5a1d338
--- /dev/null
+++ b/sound/soc/hisilicon/Kconfig
@@ -0,0 +1,5 @@
+config SND_I2S_HI6210_I2S
+ tristate "Hisilicon I2S controller"
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Hisilicon I2S
diff --git a/sound/soc/hisilicon/Makefile b/sound/soc/hisilicon/Makefile
new file mode 100644
index 000000000000..e8095e2af91a
--- /dev/null
+++ b/sound/soc/hisilicon/Makefile
@@ -0,0 +1 @@
+obj-$(CONFIG_SND_I2S_HI6210_I2S) += hi6210-i2s.o
diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c
new file mode 100644
index 000000000000..45163e5202f5
--- /dev/null
+++ b/sound/soc/hisilicon/hi6210-i2s.c
@@ -0,0 +1,618 @@
+/*
+ * linux/sound/soc/m8m/hi6210_i2s.c - I2S IP driver
+ *
+ * Copyright (C) 2015 Linaro, Ltd
+ * Author: Andy Green <andy.green@linaro.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * This driver only deals with S2 interface (BT)
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/device.h>
+#include <linux/delay.h>
+#include <linux/clk.h>
+#include <linux/jiffies.h>
+#include <linux/io.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <linux/interrupt.h>
+#include <linux/reset.h>
+#include <linux/of_address.h>
+#include <linux/of_irq.h>
+#include <linux/mfd/syscon.h>
+#include <linux/reset-controller.h>
+#include <linux/clk.h>
+
+#include "hi6210-i2s.h"
+
+struct hi6210_i2s {
+ struct device *dev;
+ struct reset_control *rc;
+ struct clk *clk[8];
+ int clocks;
+ struct snd_soc_dai_driver dai;
+ void __iomem *base;
+ struct regmap *sysctrl;
+ phys_addr_t base_phys;
+ struct snd_dmaengine_dai_dma_data dma_data[2];
+ int clk_rate;
+ spinlock_t lock;
+ int rate;
+ int format;
+ u8 bits;
+ u8 channels;
+ u8 id;
+ u8 channel_length;
+ u8 use;
+ u32 master:1;
+ u32 status:1;
+};
+
+#define SC_PERIPH_CLKEN1 0x210
+#define SC_PERIPH_CLKDIS1 0x214
+
+#define SC_PERIPH_CLKEN3 0x230
+#define SC_PERIPH_CLKDIS3 0x234
+
+#define SC_PERIPH_CLKEN12 0x270
+#define SC_PERIPH_CLKDIS12 0x274
+
+#define SC_PERIPH_RSTEN1 0x310
+#define SC_PERIPH_RSTDIS1 0x314
+#define SC_PERIPH_RSTSTAT1 0x318
+
+#define SC_PERIPH_RSTEN2 0x320
+#define SC_PERIPH_RSTDIS2 0x324
+#define SC_PERIPH_RSTSTAT2 0x328
+
+#define SOC_PMCTRL_BBPPLLALIAS 0x48
+
+enum {
+ CLK_DACODEC,
+ CLK_I2S_BASE,
+};
+
+static inline void hi6210_write_reg(struct hi6210_i2s *i2s, int reg, u32 val)
+{
+ writel(val, i2s->base + reg);
+}
+
+static inline u32 hi6210_read_reg(struct hi6210_i2s *i2s, int reg)
+{
+ return readl(i2s->base + reg);
+}
+
+int hi6210_i2s_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+ int ret, n;
+ u32 val;
+
+ /* deassert reset on ABB */
+ regmap_read(i2s->sysctrl, SC_PERIPH_RSTSTAT2, &val);
+ if (val & BIT(4))
+ regmap_write(i2s->sysctrl, SC_PERIPH_RSTDIS2, BIT(4));
+
+ for (n = 0; n < i2s->clocks; n++) {
+ ret = clk_prepare_enable(i2s->clk[n]);
+ if (ret) {
+ while (n--)
+ clk_disable_unprepare(i2s->clk[n]);
+ return ret;
+ }
+ }
+
+ ret = clk_set_rate(i2s->clk[CLK_I2S_BASE], 49152000);
+ if (ret) {
+ dev_err(i2s->dev, "%s: setting 49.152MHz base rate failed %d\n",
+ __func__, ret);
+ return ret;
+ }
+
+ /* enable clock before frequency division */
+ regmap_write(i2s->sysctrl, SC_PERIPH_CLKEN12, BIT(9));
+
+ /* enable codec working clock / == "codec bus clock" */
+ regmap_write(i2s->sysctrl, SC_PERIPH_CLKEN1, BIT(5));
+
+ /* deassert reset on codec / interface clock / working clock */
+ regmap_write(i2s->sysctrl, SC_PERIPH_RSTEN1, BIT(5));
+ regmap_write(i2s->sysctrl, SC_PERIPH_RSTDIS1, BIT(5));
+
+ /* not interested in i2s irqs */
+ val = hi6210_read_reg(i2s, HII2S_CODEC_IRQ_MASK);
+ val |= 0x3f;
+ hi6210_write_reg(i2s, HII2S_CODEC_IRQ_MASK, val);
+
+
+ /* reset the stereo downlink fifo */
+ val = hi6210_read_reg(i2s, HII2S_APB_AFIFO_CFG_1);
+ val |= (BIT(5) | BIT(4));
+ hi6210_write_reg(i2s, HII2S_APB_AFIFO_CFG_1, val);
+
+ val = hi6210_read_reg(i2s, HII2S_APB_AFIFO_CFG_1);
+ val &= ~(BIT(5) | BIT(4));
+ hi6210_write_reg(i2s, HII2S_APB_AFIFO_CFG_1, val);
+
+
+ val = hi6210_read_reg(i2s, HII2S_SW_RST_N);
+ val &= ~(HII2S_SW_RST_N__ST_DL_WORDLEN_MASK <<
+ HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT);
+ val |= (HII2S_BITS_16 << HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT);
+ hi6210_write_reg(i2s, HII2S_SW_RST_N, val);
+
+ val = hi6210_read_reg(i2s, HII2S_MISC_CFG);
+ /* mux 11/12 = APB not i2s */
+ val &= ~HII2S_MISC_CFG__ST_DL_TEST_SEL;
+ /* BT R ch 0 = mixer op of DACR ch */
+ val &= ~HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL;
+ val &= ~HII2S_MISC_CFG__S2_DOUT_TEST_SEL;
+
+ val |= HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL;
+ /* BT L ch = 1 = mux 7 = "mixer output of DACL */
+ val |= HII2S_MISC_CFG__S2_DOUT_TEST_SEL;
+ hi6210_write_reg(i2s, HII2S_MISC_CFG, val);
+
+ val = hi6210_read_reg(i2s, HII2S_SW_RST_N);
+ val |= HII2S_SW_RST_N__SW_RST_N;
+ hi6210_write_reg(i2s, HII2S_SW_RST_N, val);
+
+ return 0;
+}
+void hi6210_i2s_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+ int n;
+
+ for (n = 0; n < i2s->clocks; n++)
+ clk_disable_unprepare(i2s->clk[n]);
+
+ regmap_write(i2s->sysctrl, SC_PERIPH_RSTEN1, BIT(5));
+}
+
+static void hi6210_i2s_txctrl(struct snd_soc_dai *cpu_dai, int on)
+{
+ struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+ u32 val;
+
+ spin_lock(&i2s->lock);
+ if (on) {
+ /* enable S2 TX */
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val |= HII2S_I2S_CFG__S2_IF_TX_EN;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ } else {
+ /* disable S2 TX */
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val &= ~HII2S_I2S_CFG__S2_IF_TX_EN;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ }
+ spin_unlock(&i2s->lock);
+}
+
+static void hi6210_i2s_rxctrl(struct snd_soc_dai *cpu_dai, int on)
+{
+ struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+ u32 val;
+
+ spin_lock(&i2s->lock);
+ if (on) {
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val |= HII2S_I2S_CFG__S2_IF_RX_EN;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ } else {
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val &= ~HII2S_I2S_CFG__S2_IF_RX_EN;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ }
+ spin_unlock(&i2s->lock);
+}
+
+static int hi6210_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+
+ /*
+ * We don't actually set the hardware until the hw_params
+ * call, but we need to validate the user input here.
+ */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ case SND_SOC_DAIFMT_CBS_CFS:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ case SND_SOC_DAIFMT_LEFT_J:
+ case SND_SOC_DAIFMT_RIGHT_J:
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ i2s->format = fmt;
+ i2s->master = (i2s->format & SND_SOC_DAIFMT_MASTER_MASK) ==
+ SND_SOC_DAIFMT_CBS_CFS;
+
+ return 0;
+}
+
+static int hi6210_i2s_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct hi6210_i2s *i2s = dev_get_drvdata(cpu_dai->dev);
+ u32 bits = 0, rate = 0, signed_data = 0, fmt = 0;
+ u32 val;
+ struct snd_dmaengine_dai_dma_data *dma_data;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_U16_LE:
+ signed_data = HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT;
+ /* fallthru */
+ case SNDRV_PCM_FORMAT_S16_LE:
+ bits = HII2S_BITS_16;
+ break;
+ case SNDRV_PCM_FORMAT_U24_LE:
+ signed_data = HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT;
+ /* fallthru */
+ case SNDRV_PCM_FORMAT_S24_LE:
+ bits = HII2S_BITS_24;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Bad format\n");
+ return -EINVAL;
+ }
+
+
+ switch (params_rate(params)) {
+ case 8000:
+ rate = HII2S_FS_RATE_8KHZ;
+ break;
+ case 16000:
+ rate = HII2S_FS_RATE_16KHZ;
+ break;
+ case 32000:
+ rate = HII2S_FS_RATE_32KHZ;
+ break;
+ case 48000:
+ rate = HII2S_FS_RATE_48KHZ;
+ break;
+ case 96000:
+ rate = HII2S_FS_RATE_96KHZ;
+ break;
+ case 192000:
+ rate = HII2S_FS_RATE_192KHZ;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Bad rate: %d\n", params_rate(params));
+ return -EINVAL;
+ }
+
+ if (!(params_channels(params))) {
+ dev_err(cpu_dai->dev, "Bad channels\n");
+ return -EINVAL;
+ }
+
+ dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
+
+ switch (bits) {
+ case HII2S_BITS_24:
+ i2s->bits = 32;
+ dma_data->addr_width = 3;
+ break;
+ default:
+ i2s->bits = 16;
+ dma_data->addr_width = 2;
+ break;
+ }
+ i2s->rate = params_rate(params);
+ i2s->channels = params_channels(params);
+ i2s->channel_length = i2s->channels * i2s->bits;
+
+ val = hi6210_read_reg(i2s, HII2S_ST_DL_FIFO_TH_CFG);
+ val &= ~((HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_MASK <<
+ HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_SHIFT) |
+ (HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_MASK <<
+ HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_SHIFT) |
+ (HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_MASK <<
+ HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_SHIFT) |
+ (HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_MASK <<
+ HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_SHIFT));
+ val |= ((16 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_SHIFT) |
+ (30 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_SHIFT) |
+ (16 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_SHIFT) |
+ (30 << HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_SHIFT));
+ hi6210_write_reg(i2s, HII2S_ST_DL_FIFO_TH_CFG, val);
+
+
+ val = hi6210_read_reg(i2s, HII2S_IF_CLK_EN_CFG);
+ val |= (BIT(19) | BIT(18) | BIT(17) |
+ HII2S_IF_CLK_EN_CFG__S2_IF_CLK_EN |
+ HII2S_IF_CLK_EN_CFG__S2_OL_MIXER_EN |
+ HII2S_IF_CLK_EN_CFG__S2_OL_SRC_EN |
+ HII2S_IF_CLK_EN_CFG__ST_DL_R_EN |
+ HII2S_IF_CLK_EN_CFG__ST_DL_L_EN);
+ hi6210_write_reg(i2s, HII2S_IF_CLK_EN_CFG, val);
+
+
+ val = hi6210_read_reg(i2s, HII2S_DIG_FILTER_CLK_EN_CFG);
+ val &= ~(HII2S_DIG_FILTER_CLK_EN_CFG__DACR_SDM_EN |
+ HII2S_DIG_FILTER_CLK_EN_CFG__DACR_HBF2I_EN |
+ HII2S_DIG_FILTER_CLK_EN_CFG__DACR_AGC_EN |
+ HII2S_DIG_FILTER_CLK_EN_CFG__DACL_SDM_EN |
+ HII2S_DIG_FILTER_CLK_EN_CFG__DACL_HBF2I_EN |
+ HII2S_DIG_FILTER_CLK_EN_CFG__DACL_AGC_EN);
+ val |= (HII2S_DIG_FILTER_CLK_EN_CFG__DACR_MIXER_EN |
+ HII2S_DIG_FILTER_CLK_EN_CFG__DACL_MIXER_EN);
+ hi6210_write_reg(i2s, HII2S_DIG_FILTER_CLK_EN_CFG, val);
+
+
+ val = hi6210_read_reg(i2s, HII2S_DIG_FILTER_MODULE_CFG);
+ val &= ~(HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN2_MUTE |
+ HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN2_MUTE);
+ hi6210_write_reg(i2s, HII2S_DIG_FILTER_MODULE_CFG, val);
+
+ val = hi6210_read_reg(i2s, HII2S_MUX_TOP_MODULE_CFG);
+ val &= ~(HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN1_MUTE |
+ HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN2_MUTE |
+ HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN1_MUTE |
+ HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN2_MUTE);
+ hi6210_write_reg(i2s, HII2S_MUX_TOP_MODULE_CFG, val);
+
+
+ switch (i2s->format & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ i2s->master = false;
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val |= HII2S_I2S_CFG__S2_MST_SLV;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ i2s->master = true;
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val &= ~HII2S_I2S_CFG__S2_MST_SLV;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ break;
+ default:
+ WARN_ONCE(1, "Invalid i2s->fmt MASTER_MASK. This shouldn't happen\n");
+ return -EINVAL;
+ }
+
+ switch (i2s->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+ case SND_SOC_DAIFMT_I2S:
+ fmt = HII2S_FORMAT_I2S;
+ break;
+ case SND_SOC_DAIFMT_LEFT_J:
+ fmt = HII2S_FORMAT_LEFT_JUST;
+ break;
+ case SND_SOC_DAIFMT_RIGHT_J:
+ fmt = HII2S_FORMAT_RIGHT_JUST;
+ break;
+ default:
+ WARN_ONCE(1, "Invalid i2s->fmt FORMAT_MASK. This shouldn't happen\n");
+ return -EINVAL;
+ }
+
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val &= ~(HII2S_I2S_CFG__S2_FUNC_MODE_MASK <<
+ HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT);
+ val |= fmt << HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+
+
+ val = hi6210_read_reg(i2s, HII2S_CLK_SEL);
+ val &= ~(HII2S_CLK_SEL__I2S_BT_FM_SEL | /* BT gets the I2S */
+ HII2S_CLK_SEL__EXT_12_288MHZ_SEL);
+ hi6210_write_reg(i2s, HII2S_CLK_SEL, val);
+
+ dma_data->maxburst = 2;
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ dma_data->addr = i2s->base_phys + HII2S_ST_DL_CHANNEL;
+ else
+ dma_data->addr = i2s->base_phys + HII2S_STEREO_UPLINK_CHANNEL;
+
+ switch (i2s->channels) {
+ case 1:
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val |= HII2S_I2S_CFG__S2_FRAME_MODE;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ break;
+ default:
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val &= ~HII2S_I2S_CFG__S2_FRAME_MODE;
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+ break;
+ }
+
+ /* clear loopback, set signed type and word length */
+ val = hi6210_read_reg(i2s, HII2S_I2S_CFG);
+ val &= ~HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT;
+ val &= ~(HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_MASK <<
+ HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT);
+ val &= ~(HII2S_I2S_CFG__S2_DIRECT_LOOP_MASK <<
+ HII2S_I2S_CFG__S2_DIRECT_LOOP_SHIFT);
+ val |= signed_data;
+ val |= (bits << HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT);
+ hi6210_write_reg(i2s, HII2S_I2S_CFG, val);
+
+
+ if (!i2s->master)
+ return 0;
+
+ /* set DAC and related units to correct rate */
+ val = hi6210_read_reg(i2s, HII2S_FS_CFG);
+ val &= ~(HII2S_FS_CFG__FS_S2_MASK << HII2S_FS_CFG__FS_S2_SHIFT);
+ val &= ~(HII2S_FS_CFG__FS_DACLR_MASK << HII2S_FS_CFG__FS_DACLR_SHIFT);
+ val &= ~(HII2S_FS_CFG__FS_ST_DL_R_MASK <<
+ HII2S_FS_CFG__FS_ST_DL_R_SHIFT);
+ val &= ~(HII2S_FS_CFG__FS_ST_DL_L_MASK <<
+ HII2S_FS_CFG__FS_ST_DL_L_SHIFT);
+ val |= (rate << HII2S_FS_CFG__FS_S2_SHIFT);
+ val |= (rate << HII2S_FS_CFG__FS_DACLR_SHIFT);
+ val |= (rate << HII2S_FS_CFG__FS_ST_DL_R_SHIFT);
+ val |= (rate << HII2S_FS_CFG__FS_ST_DL_L_SHIFT);
+ hi6210_write_reg(i2s, HII2S_FS_CFG, val);
+
+ return 0;
+}
+
+static int hi6210_i2s_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *cpu_dai)
+{
+ pr_debug("%s\n", __func__);
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ hi6210_i2s_rxctrl(cpu_dai, 1);
+ else
+ hi6210_i2s_txctrl(cpu_dai, 1);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+ hi6210_i2s_rxctrl(cpu_dai, 0);
+ else
+ hi6210_i2s_txctrl(cpu_dai, 0);
+ break;
+ default:
+ dev_err(cpu_dai->dev, "uknown cmd\n");
+ return -EINVAL;
+ }
+ return 0;
+}
+
+static int hi6210_i2s_dai_probe(struct snd_soc_dai *dai)
+{
+ struct hi6210_i2s *i2s = snd_soc_dai_get_drvdata(dai);
+
+ snd_soc_dai_init_dma_data(dai,
+ &i2s->dma_data[SNDRV_PCM_STREAM_PLAYBACK],
+ &i2s->dma_data[SNDRV_PCM_STREAM_CAPTURE]);
+
+ return 0;
+}
+
+
+static struct snd_soc_dai_ops hi6210_i2s_dai_ops = {
+ .trigger = hi6210_i2s_trigger,
+ .hw_params = hi6210_i2s_hw_params,
+ .set_fmt = hi6210_i2s_set_fmt,
+ .startup = hi6210_i2s_startup,
+ .shutdown = hi6210_i2s_shutdown,
+};
+
+struct snd_soc_dai_driver hi6210_i2s_dai_init = {
+ .probe = hi6210_i2s_dai_probe,
+ .playback = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_U16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ },
+ .capture = {
+ .channels_min = 2,
+ .channels_max = 2,
+ .formats = SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_U16_LE,
+ .rates = SNDRV_PCM_RATE_48000,
+ },
+ .ops = &hi6210_i2s_dai_ops,
+};
+
+static const struct snd_soc_component_driver hi6210_i2s_i2s_comp = {
+ .name = "hi6210_i2s-i2s",
+};
+
+static int hi6210_i2s_probe(struct platform_device *pdev)
+{
+ struct device_node *node = pdev->dev.of_node;
+ struct device *dev = &pdev->dev;
+ struct hi6210_i2s *i2s;
+ struct resource *res;
+ int ret;
+
+ i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
+ if (!i2s)
+ return -ENOMEM;
+
+ i2s->dev = dev;
+ spin_lock_init(&i2s->lock);
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ i2s->base = devm_ioremap_resource(dev, res);
+ if (IS_ERR(i2s->base))
+ return PTR_ERR(i2s->base);
+
+ i2s->base_phys = (phys_addr_t)res->start;
+ i2s->dai = hi6210_i2s_dai_init;
+
+ dev_set_drvdata(&pdev->dev, i2s);
+
+ i2s->sysctrl = syscon_regmap_lookup_by_phandle(node,
+ "hisilicon,sysctrl-syscon");
+ if (IS_ERR(i2s->sysctrl))
+ return PTR_ERR(i2s->sysctrl);
+
+ i2s->clk[CLK_DACODEC] = devm_clk_get(&pdev->dev, "dacodec");
+ if (IS_ERR_OR_NULL(i2s->clk[CLK_DACODEC]))
+ return PTR_ERR(i2s->clk[CLK_DACODEC]);
+ i2s->clocks++;
+
+ i2s->clk[CLK_I2S_BASE] = devm_clk_get(&pdev->dev, "i2s-base");
+ if (IS_ERR_OR_NULL(i2s->clk[CLK_I2S_BASE]))
+ return PTR_ERR(i2s->clk[CLK_I2S_BASE]);
+ i2s->clocks++;
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &hi6210_i2s_i2s_comp,
+ &i2s->dai, 1);
+ return ret;
+}
+
+static const struct of_device_id hi6210_i2s_dt_ids[] = {
+ { .compatible = "hisilicon,hi6210-i2s" },
+ { /* sentinel */ }
+};
+
+MODULE_DEVICE_TABLE(of, hi6210_i2s_dt_ids);
+
+static struct platform_driver hi6210_i2s_driver = {
+ .probe = hi6210_i2s_probe,
+ .driver = {
+ .name = "hi6210_i2s",
+ .of_match_table = hi6210_i2s_dt_ids,
+ },
+};
+
+module_platform_driver(hi6210_i2s_driver);
+
+MODULE_DESCRIPTION("Hisilicon HI6210 I2S driver");
+MODULE_AUTHOR("Andy Green <andy.green@linaro.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/hisilicon/hi6210-i2s.h b/sound/soc/hisilicon/hi6210-i2s.h
new file mode 100644
index 000000000000..85cecc4939a0
--- /dev/null
+++ b/sound/soc/hisilicon/hi6210-i2s.h
@@ -0,0 +1,276 @@
+/*
+ * linux/sound/soc/hisilicon/hi6210-i2s.h
+ *
+ * Copyright (C) 2015 Linaro, Ltd
+ * Author: Andy Green <andy.green@linaro.org>
+ *
+ * This program is free software: you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation, version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the
+ * GNU General Public License for more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program. If not, see <http://www.gnu.org/licenses/>.
+ *
+ * Note at least on 6220, S2 == BT, S1 == Digital FM Radio IF
+ */
+
+#ifndef _HI6210_I2S_H
+#define _HI6210_I2S_H
+
+#define HII2S_SW_RST_N 0
+
+#define HII2S_SW_RST_N__STEREO_UPLINK_WORDLEN_SHIFT 28
+#define HII2S_SW_RST_N__STEREO_UPLINK_WORDLEN_MASK 3
+#define HII2S_SW_RST_N__THIRDMD_UPLINK_WORDLEN_SHIFT 26
+#define HII2S_SW_RST_N__THIRDMD_UPLINK_WORDLEN_MASK 3
+#define HII2S_SW_RST_N__VOICE_UPLINK_WORDLEN_SHIFT 24
+#define HII2S_SW_RST_N__VOICE_UPLINK_WORDLEN_MASK 3
+#define HII2S_SW_RST_N__ST_DL_WORDLEN_SHIFT 20
+#define HII2S_SW_RST_N__ST_DL_WORDLEN_MASK 3
+#define HII2S_SW_RST_N__THIRDMD_DLINK_WORDLEN_SHIFT 18
+#define HII2S_SW_RST_N__THIRDMD_DLINK_WORDLEN_MASK 3
+#define HII2S_SW_RST_N__VOICE_DLINK_WORDLEN_SHIFT 16
+#define HII2S_SW_RST_N__VOICE_DLINK_WORDLEN_MASK 3
+
+#define HII2S_SW_RST_N__SW_RST_N BIT(0)
+
+enum hi6210_bits {
+ HII2S_BITS_16,
+ HII2S_BITS_18,
+ HII2S_BITS_20,
+ HII2S_BITS_24,
+};
+
+
+#define HII2S_IF_CLK_EN_CFG 4
+
+#define HII2S_IF_CLK_EN_CFG__THIRDMD_UPLINK_EN BIT(25)
+#define HII2S_IF_CLK_EN_CFG__THIRDMD_DLINK_EN BIT(24)
+#define HII2S_IF_CLK_EN_CFG__S3_IF_CLK_EN BIT(20)
+#define HII2S_IF_CLK_EN_CFG__S2_IF_CLK_EN BIT(16)
+#define HII2S_IF_CLK_EN_CFG__S2_OL_MIXER_EN BIT(15)
+#define HII2S_IF_CLK_EN_CFG__S2_OL_SRC_EN BIT(14)
+#define HII2S_IF_CLK_EN_CFG__S2_IR_PGA_EN BIT(13)
+#define HII2S_IF_CLK_EN_CFG__S2_IL_PGA_EN BIT(12)
+#define HII2S_IF_CLK_EN_CFG__S1_IR_PGA_EN BIT(10)
+#define HII2S_IF_CLK_EN_CFG__S1_IL_PGA_EN BIT(9)
+#define HII2S_IF_CLK_EN_CFG__S1_IF_CLK_EN BIT(8)
+#define HII2S_IF_CLK_EN_CFG__VOICE_DLINK_SRC_EN BIT(7)
+#define HII2S_IF_CLK_EN_CFG__VOICE_DLINK_EN BIT(6)
+#define HII2S_IF_CLK_EN_CFG__ST_DL_R_EN BIT(5)
+#define HII2S_IF_CLK_EN_CFG__ST_DL_L_EN BIT(4)
+#define HII2S_IF_CLK_EN_CFG__VOICE_UPLINK_R_EN BIT(3)
+#define HII2S_IF_CLK_EN_CFG__VOICE_UPLINK_L_EN BIT(2)
+#define HII2S_IF_CLK_EN_CFG__STEREO_UPLINK_R_EN BIT(1)
+#define HII2S_IF_CLK_EN_CFG__STEREO_UPLINK_L_EN BIT(0)
+
+#define HII2S_DIG_FILTER_CLK_EN_CFG 8
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_SDM_EN BIT(30)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_HBF2I_EN BIT(28)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_MIXER_EN BIT(25)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACR_AGC_EN BIT(24)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_SDM_EN BIT(22)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_HBF2I_EN BIT(20)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_MIXER_EN BIT(17)
+#define HII2S_DIG_FILTER_CLK_EN_CFG__DACL_AGC_EN BIT(16)
+
+#define HII2S_FS_CFG 0xc
+
+#define HII2S_FS_CFG__FS_S2_SHIFT 28
+#define HII2S_FS_CFG__FS_S2_MASK 7
+#define HII2S_FS_CFG__FS_S1_SHIFT 24
+#define HII2S_FS_CFG__FS_S1_MASK 7
+#define HII2S_FS_CFG__FS_ADCLR_SHIFT 20
+#define HII2S_FS_CFG__FS_ADCLR_MASK 7
+#define HII2S_FS_CFG__FS_DACLR_SHIFT 16
+#define HII2S_FS_CFG__FS_DACLR_MASK 7
+#define HII2S_FS_CFG__FS_ST_DL_R_SHIFT 8
+#define HII2S_FS_CFG__FS_ST_DL_R_MASK 7
+#define HII2S_FS_CFG__FS_ST_DL_L_SHIFT 4
+#define HII2S_FS_CFG__FS_ST_DL_L_MASK 7
+#define HII2S_FS_CFG__FS_VOICE_DLINK_SHIFT 0
+#define HII2S_FS_CFG__FS_VOICE_DLINK_MASK 7
+
+enum hi6210_i2s_rates {
+ HII2S_FS_RATE_8KHZ = 0,
+ HII2S_FS_RATE_16KHZ = 1,
+ HII2S_FS_RATE_32KHZ = 2,
+ HII2S_FS_RATE_48KHZ = 4,
+ HII2S_FS_RATE_96KHZ = 5,
+ HII2S_FS_RATE_192KHZ = 6,
+};
+
+#define HII2S_I2S_CFG 0x10
+
+#define HII2S_I2S_CFG__S2_IF_TX_EN BIT(31)
+#define HII2S_I2S_CFG__S2_IF_RX_EN BIT(30)
+#define HII2S_I2S_CFG__S2_FRAME_MODE BIT(29)
+#define HII2S_I2S_CFG__S2_MST_SLV BIT(28)
+#define HII2S_I2S_CFG__S2_LRCK_MODE BIT(27)
+#define HII2S_I2S_CFG__S2_CHNNL_MODE BIT(26)
+#define HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_SHIFT 24
+#define HII2S_I2S_CFG__S2_CODEC_IO_WORDLENGTH_MASK 3
+#define HII2S_I2S_CFG__S2_DIRECT_LOOP_SHIFT 22
+#define HII2S_I2S_CFG__S2_DIRECT_LOOP_MASK 3
+#define HII2S_I2S_CFG__S2_TX_CLK_SEL BIT(21)
+#define HII2S_I2S_CFG__S2_RX_CLK_SEL BIT(20)
+#define HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT BIT(19)
+#define HII2S_I2S_CFG__S2_FUNC_MODE_SHIFT 16
+#define HII2S_I2S_CFG__S2_FUNC_MODE_MASK 7
+#define HII2S_I2S_CFG__S1_IF_TX_EN BIT(15)
+#define HII2S_I2S_CFG__S1_IF_RX_EN BIT(14)
+#define HII2S_I2S_CFG__S1_FRAME_MODE BIT(13)
+#define HII2S_I2S_CFG__S1_MST_SLV BIT(12)
+#define HII2S_I2S_CFG__S1_LRCK_MODE BIT(11)
+#define HII2S_I2S_CFG__S1_CHNNL_MODE BIT(10)
+#define HII2S_I2S_CFG__S1_CODEC_IO_WORDLENGTH_SHIFT 8
+#define HII2S_I2S_CFG__S1_CODEC_IO_WORDLENGTH_MASK 3
+#define HII2S_I2S_CFG__S1_DIRECT_LOOP_SHIFT 6
+#define HII2S_I2S_CFG__S1_DIRECT_LOOP_MASK 3
+#define HII2S_I2S_CFG__S1_TX_CLK_SEL BIT(5)
+#define HII2S_I2S_CFG__S1_RX_CLK_SEL BIT(4)
+#define HII2S_I2S_CFG__S1_CODEC_DATA_FORMAT BIT(3)
+#define HII2S_I2S_CFG__S1_FUNC_MODE_SHIFT 0
+#define HII2S_I2S_CFG__S1_FUNC_MODE_MASK 7
+
+enum hi6210_i2s_formats {
+ HII2S_FORMAT_I2S,
+ HII2S_FORMAT_PCM_STD,
+ HII2S_FORMAT_PCM_USER,
+ HII2S_FORMAT_LEFT_JUST,
+ HII2S_FORMAT_RIGHT_JUST,
+};
+
+#define HII2S_DIG_FILTER_MODULE_CFG 0x14
+
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_GAIN_SHIFT 28
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_GAIN_MASK 3
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN4_MUTE BIT(27)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN3_MUTE BIT(26)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN2_MUTE BIT(25)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACR_MIXER_IN1_MUTE BIT(24)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_GAIN_SHIFT 20
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_GAIN_MASK 3
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN4_MUTE BIT(19)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN3_MUTE BIT(18)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN2_MUTE BIT(17)
+#define HII2S_DIG_FILTER_MODULE_CFG__DACL_MIXER_IN1_MUTE BIT(16)
+#define HII2S_DIG_FILTER_MODULE_CFG__SW_DACR_SDM_DITHER BIT(9)
+#define HII2S_DIG_FILTER_MODULE_CFG__SW_DACL_SDM_DITHER BIT(8)
+#define HII2S_DIG_FILTER_MODULE_CFG__LM_CODEC_DAC2ADC_SHIFT 4
+#define HII2S_DIG_FILTER_MODULE_CFG__LM_CODEC_DAC2ADC_MASK 7
+#define HII2S_DIG_FILTER_MODULE_CFG__RM_CODEC_DAC2ADC_SHIFT 0
+#define HII2S_DIG_FILTER_MODULE_CFG__RM_CODEC_DAC2ADC_MASK 7
+
+enum hi6210_gains {
+ HII2S_GAIN_100PC,
+ HII2S_GAIN_50PC,
+ HII2S_GAIN_25PC,
+};
+
+#define HII2S_MUX_TOP_MODULE_CFG 0x18
+
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_GAIN_SHIFT 14
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_GAIN_MASK 3
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN2_MUTE BIT(13)
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_MIXER_IN1_MUTE BIT(12)
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_GAIN_SHIFT 10
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_GAIN_MASK 3
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN2_MUTE BIT(9)
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_MIXER_IN1_MUTE BIT(8)
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_SRC_RDY BIT(6)
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_SRC_MODE_SHIFT 4
+#define HII2S_MUX_TOP_MODULE_CFG__S2_OL_SRC_MODE_MASK 3
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_SRC_RDY BIT(3)
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_SRC_MODE_SHIFT 0
+#define HII2S_MUX_TOP_MODULE_CFG__VOICE_DLINK_SRC_MODE_MASK 7
+
+enum hi6210_s2_src_mode {
+ HII2S_S2_SRC_MODE_3,
+ HII2S_S2_SRC_MODE_12,
+ HII2S_S2_SRC_MODE_6,
+ HII2S_S2_SRC_MODE_2,
+};
+
+enum hi6210_voice_dlink_src_mode {
+ HII2S_VOICE_DL_SRC_MODE_12 = 1,
+ HII2S_VOICE_DL_SRC_MODE_6,
+ HII2S_VOICE_DL_SRC_MODE_2,
+ HII2S_VOICE_DL_SRC_MODE_3,
+};
+
+#define HII2S_ADC_PGA_CFG 0x1c
+#define HII2S_S1_INPUT_PGA_CFG 0x20
+#define HII2S_S2_INPUT_PGA_CFG 0x24
+#define HII2S_ST_DL_PGA_CFG 0x28
+#define HII2S_VOICE_SIDETONE_DLINK_PGA_CFG 0x2c
+#define HII2S_APB_AFIFO_CFG_1 0x30
+#define HII2S_APB_AFIFO_CFG_2 0x34
+#define HII2S_ST_DL_FIFO_TH_CFG 0x38
+
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_SHIFT 24
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AEMPTY_MASK 0x1f
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_SHIFT 16
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_R_AFULL_MASK 0x1f
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_SHIFT 8
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AEMPTY_MASK 0x1f
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_SHIFT 0
+#define HII2S_ST_DL_FIFO_TH_CFG__ST_DL_L_AFULL_MASK 0x1f
+
+#define HII2S_STEREO_UPLINK_FIFO_TH_CFG 0x3c
+#define HII2S_VOICE_UPLINK_FIFO_TH_CFG 0x40
+#define HII2S_CODEC_IRQ_MASK 0x44
+#define HII2S_CODEC_IRQ 0x48
+#define HII2S_DACL_AGC_CFG_1 0x4c
+#define HII2S_DACL_AGC_CFG_2 0x50
+#define HII2S_DACR_AGC_CFG_1 0x54
+#define HII2S_DACR_AGC_CFG_2 0x58
+#define HII2S_DMIC_SIF_CFG 0x5c
+#define HII2S_MISC_CFG 0x60
+
+#define HII2S_MISC_CFG__THIRDMD_DLINK_TEST_SEL BIT(17)
+#define HII2S_MISC_CFG__THIRDMD_DLINK_DIN_SEL BIT(16)
+#define HII2S_MISC_CFG__S3_DOUT_RIGHT_SEL BIT(14)
+#define HII2S_MISC_CFG__S3_DOUT_LEFT_SEL BIT(13)
+#define HII2S_MISC_CFG__S3_DIN_TEST_SEL BIT(12)
+#define HII2S_MISC_CFG__VOICE_DLINK_SRC_UP_DOUT_VLD_SEL BIT(8)
+#define HII2S_MISC_CFG__VOICE_DLINK_TEST_SEL BIT(7)
+#define HII2S_MISC_CFG__VOICE_DLINK_DIN_SEL BIT(6)
+#define HII2S_MISC_CFG__ST_DL_TEST_SEL BIT(4)
+#define HII2S_MISC_CFG__S2_DOUT_RIGHT_SEL BIT(3)
+#define HII2S_MISC_CFG__S2_DOUT_TEST_SEL BIT(2)
+#define HII2S_MISC_CFG__S1_DOUT_TEST_SEL BIT(1)
+#define HII2S_MISC_CFG__S2_DOUT_LEFT_SEL BIT(0)
+
+#define HII2S_S2_SRC_CFG 0x64
+#define HII2S_MEM_CFG 0x68
+#define HII2S_THIRDMD_PCM_PGA_CFG 0x6c
+#define HII2S_THIRD_MODEM_FIFO_TH 0x70
+#define HII2S_S3_ANTI_FREQ_JITTER_TX_INC_CNT 0x74
+#define HII2S_S3_ANTI_FREQ_JITTER_TX_DEC_CNT 0x78
+#define HII2S_S3_ANTI_FREQ_JITTER_RX_INC_CNT 0x7c
+#define HII2S_S3_ANTI_FREQ_JITTER_RX_DEC_CNT 0x80
+#define HII2S_ANTI_FREQ_JITTER_EN 0x84
+#define HII2S_CLK_SEL 0x88
+
+/* 0 = BT owns the i2s */
+#define HII2S_CLK_SEL__I2S_BT_FM_SEL BIT(0)
+/* 0 = internal source, 1 = ext */
+#define HII2S_CLK_SEL__EXT_12_288MHZ_SEL BIT(1)
+
+
+#define HII2S_THIRDMD_DLINK_CHANNEL 0xe8
+#define HII2S_THIRDMD_ULINK_CHANNEL 0xec
+#define HII2S_VOICE_DLINK_CHANNEL 0xf0
+
+/* shovel data in here for playback */
+#define HII2S_ST_DL_CHANNEL 0xf4
+#define HII2S_STEREO_UPLINK_CHANNEL 0xf8
+#define HII2S_VOICE_UPLINK_CHANNEL 0xfc
+
+#endif/* _HI6210_I2S_H */
diff --git a/sound/soc/intel/Kconfig b/sound/soc/intel/Kconfig
index 526855ad479e..67968ef3bbda 100644
--- a/sound/soc/intel/Kconfig
+++ b/sound/soc/intel/Kconfig
@@ -202,6 +202,30 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
platforms with MAX98090 audio codec it also can support TI jack chip as aux device.
If unsure select "N".
+config SND_SOC_INTEL_BYT_CHT_DA7213_MACH
+ tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail with DA7212/7213 codec"
+ depends on X86_INTEL_LPSS && I2C && ACPI
+ select SND_SOC_DA7213
+ select SND_SST_ATOM_HIFI2_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) Baytrail & CherryTrail
+ platforms with DA7212/7213 audio codec.
+ If unsure select "N".
+
+config SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH
+ tristate "ASoC Audio driver for Intel Baytrail & Cherrytrail platform with no codec (MinnowBoard MAX, Up)"
+ depends on X86_INTEL_LPSS && I2C && ACPI
+ select SND_SST_ATOM_HIFI2_PLATFORM
+ select SND_SST_IPC_ACPI
+ select SND_SOC_INTEL_SST_MATCH if ACPI
+ help
+ This adds support for ASoC machine driver for the MinnowBoard Max or
+ Up boards and provides access to I2S signals on the Low-Speed
+ connector
+ If unsure select "N".
+
config SND_SOC_INTEL_SKYLAKE
tristate
select SND_HDA_EXT_CORE
diff --git a/sound/soc/intel/atom/sst/sst_acpi.c b/sound/soc/intel/atom/sst/sst_acpi.c
index 747c0f393d2d..dd250b8b26f2 100644
--- a/sound/soc/intel/atom/sst/sst_acpi.c
+++ b/sound/soc/intel/atom/sst/sst_acpi.c
@@ -420,7 +420,21 @@ static const struct dmi_system_id byt_table[] = {
.callback = byt_thinkpad10_quirk_cb,
.matches = {
DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
- DMI_MATCH(DMI_PRODUCT_NAME, "20C3001VHH"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"),
+ },
+ },
+ {
+ .callback = byt_thinkpad10_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad Tablet B"),
+ },
+ },
+ {
+ .callback = byt_thinkpad10_quirk_cb,
+ .matches = {
+ DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"),
+ DMI_MATCH(DMI_PRODUCT_VERSION, "Lenovo Miix 2 10"),
},
},
{ }
@@ -480,12 +494,23 @@ static struct sst_acpi_mach sst_acpi_bytcr[] = {
&byt_rvp_platform_data },
{"10EC5651", "bytcr_rt5651", "intel/fw_sst_0f28.bin", "bytcr_rt5651", NULL,
&byt_rvp_platform_data },
+ {"DLGS7212", "bytcht_da7213", "intel/fw_sst_0f28.bin", "bytcht_da7213", NULL,
+ &byt_rvp_platform_data },
+ {"DLGS7213", "bytcht_da7213", "intel/fw_sst_0f28.bin", "bytcht_da7213", NULL,
+ &byt_rvp_platform_data },
/* some Baytrail platforms rely on RT5645, use CHT machine driver */
{"10EC5645", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
&byt_rvp_platform_data },
{"10EC5648", "cht-bsw-rt5645", "intel/fw_sst_0f28.bin", "cht-bsw", NULL,
&byt_rvp_platform_data },
-
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH)
+ /*
+ * This is always last in the table so that it is selected only when
+ * enabled explicitly and there is no codec-related information in SSDT
+ */
+ {"80860F28", "bytcht_nocodec", "intel/fw_sst_0f28.bin", "bytcht_nocodec", NULL,
+ &byt_rvp_platform_data },
+#endif
{},
};
@@ -504,6 +529,10 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
{"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
&chv_platform_data },
+ {"DLGS7212", "bytcht_da7213", "intel/fw_sst_22a8.bin", "bytcht_da7213", NULL,
+ &chv_platform_data },
+ {"DLGS7213", "bytcht_da7213", "intel/fw_sst_22a8.bin", "bytcht_da7213", 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", cht_quirk,
&chv_platform_data },
@@ -512,6 +541,14 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
/* some CHT-T platforms rely on RT5651, use Baytrail machine driver */
{"10EC5651", "bytcr_rt5651", "intel/fw_sst_22a8.bin", "bytcr_rt5651", NULL,
&chv_platform_data },
+#if IS_ENABLED(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH)
+ /*
+ * This is always last in the table so that it is selected only when
+ * enabled explicitly and there is no codec-related information in SSDT
+ */
+ {"808622A8", "bytcht_nocodec", "intel/fw_sst_22a8.bin", "bytcht_nocodec", NULL,
+ &chv_platform_data },
+#endif
{},
};
diff --git a/sound/soc/intel/atom/sst/sst_ipc.c b/sound/soc/intel/atom/sst/sst_ipc.c
index 14c2d9d18180..20b01e02ed8f 100644
--- a/sound/soc/intel/atom/sst/sst_ipc.c
+++ b/sound/soc/intel/atom/sst/sst_ipc.c
@@ -236,7 +236,9 @@ static void process_fw_init(struct intel_sst_drv *sst_drv_ctx,
retval = init->result;
goto ret;
}
- dev_info(sst_drv_ctx->dev, "FW Version %02x.%02x.%02x.%02x\n",
+ if (memcmp(&sst_drv_ctx->fw_version, &init->fw_version,
+ sizeof(init->fw_version)))
+ dev_info(sst_drv_ctx->dev, "FW Version %02x.%02x.%02x.%02x\n",
init->fw_version.type, init->fw_version.major,
init->fw_version.minor, init->fw_version.build);
dev_dbg(sst_drv_ctx->dev, "Build date %s Time %s\n",
diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile
index 5639f10774e6..56896e09445d 100644
--- a/sound/soc/intel/boards/Makefile
+++ b/sound/soc/intel/boards/Makefile
@@ -10,6 +10,8 @@ snd-soc-sst-bytcr-rt5651-objs := bytcr_rt5651.o
snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
+snd-soc-sst-byt-cht-da7213-objs := bytcht_da7213.o
+snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o
snd-soc-skl_rt286-objs := skl_rt286.o
snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o
snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o
@@ -26,6 +28,8 @@ 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
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH) += snd-soc-sst-byt-cht-da7213.o
+obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) += snd-soc-sst-byt-cht-nocodec.o
obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o
obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o
obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o
diff --git a/sound/soc/intel/boards/bdw-rt5677.c b/sound/soc/intel/boards/bdw-rt5677.c
index 53c6b4cbb1e1..14d9693c1641 100644
--- a/sound/soc/intel/boards/bdw-rt5677.c
+++ b/sound/soc/intel/boards/bdw-rt5677.c
@@ -193,13 +193,12 @@ static int bdw_rt5677_init(struct snd_soc_pcm_runtime *rtd)
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);
+ bdw_rt5677->gpio_hp_en = devm_gpiod_get(codec->dev, "headphone-enable",
+ GPIOD_OUT_LOW);
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",
diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c
index faf865bb1765..6dcbbcefc25b 100644
--- a/sound/soc/intel/boards/broadwell.c
+++ b/sound/soc/intel/boards/broadwell.c
@@ -269,9 +269,6 @@ static struct snd_soc_card broadwell_rt286 = {
static int broadwell_audio_probe(struct platform_device *pdev)
{
broadwell_rt286.dev = &pdev->dev;
-
- snd_soc_set_dmi_name(&broadwell_rt286, NULL);
-
return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286);
}
diff --git a/sound/soc/intel/boards/bxt_da7219_max98357a.c b/sound/soc/intel/boards/bxt_da7219_max98357a.c
index 2cda06cde4d1..3a8c4d954a91 100644
--- a/sound/soc/intel/boards/bxt_da7219_max98357a.c
+++ b/sound/soc/intel/boards/bxt_da7219_max98357a.c
@@ -55,6 +55,54 @@ enum {
BXT_DPCM_AUDIO_HDMI3_PB,
};
+static inline struct snd_soc_dai *bxt_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, BXT_DIALOG_CODEC_DAI,
+ strlen(BXT_DIALOG_CODEC_DAI)))
+ return rtd->codec_dai;
+ }
+
+ return NULL;
+}
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+ struct snd_kcontrol *k, int event)
+{
+ int ret = 0;
+ struct snd_soc_dapm_context *dapm = w->dapm;
+ struct snd_soc_card *card = dapm->card;
+ struct snd_soc_dai *codec_dai;
+
+ codec_dai = bxt_get_codec_dai(card);
+ if (!codec_dai) {
+ dev_err(card->dev, "Codec dai not found; Unable to set/unset codec pll\n");
+ return -EIO;
+ }
+
+ if (SND_SOC_DAPM_EVENT_OFF(event)) {
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ DA7219_SYSCLK_MCLK, 0, 0);
+ if (ret)
+ dev_err(card->dev, "failed to stop PLL: %d\n", ret);
+ } else if(SND_SOC_DAPM_EVENT_ON(event)) {
+ ret = snd_soc_dai_set_sysclk(codec_dai,
+ DA7219_CLKSRC_MCLK, 19200000, SND_SOC_CLOCK_IN);
+ if (ret)
+ dev_err(card->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)
+ dev_err(card->dev, "failed to start PLL: %d\n", ret);
+ }
+
+ return ret;
+}
+
static const struct snd_kcontrol_new broxton_controls[] = {
SOC_DAPM_PIN_SWITCH("Headphone Jack"),
SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -69,6 +117,8 @@ static const struct snd_soc_dapm_widget broxton_widgets[] = {
SND_SOC_DAPM_SPK("HDMI1", NULL),
SND_SOC_DAPM_SPK("HDMI2", NULL),
SND_SOC_DAPM_SPK("HDMI3", NULL),
+ SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+ platform_clock_control, SND_SOC_DAPM_POST_PMD|SND_SOC_DAPM_PRE_PMU),
};
static const struct snd_soc_dapm_route broxton_map[] = {
@@ -109,6 +159,9 @@ static const struct snd_soc_dapm_route broxton_map[] = {
/* DMIC */
{"dmic01_hifi", NULL, "DMIC01 Rx"},
{"DMIC01 Rx", NULL, "DMIC AIF"},
+
+ { "Headphone Jack", NULL, "Platform Clock" },
+ { "Headset Mic", NULL, "Platform Clock" },
};
static int broxton_ssp_fixup(struct snd_soc_pcm_runtime *rtd,
@@ -243,49 +296,6 @@ 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 const 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)
{
@@ -467,7 +477,6 @@ static struct snd_soc_dai_link broxton_dais[] = {
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,
},
diff --git a/sound/soc/intel/boards/bxt_rt298.c b/sound/soc/intel/boards/bxt_rt298.c
index 176c080a9818..1a68d043c803 100644
--- a/sound/soc/intel/boards/bxt_rt298.c
+++ b/sound/soc/intel/boards/bxt_rt298.c
@@ -274,12 +274,15 @@ static int bxt_fe_startup(struct snd_pcm_substream *substream)
* on this platform for PCM device we support:
* 48Khz
* stereo
+ * 16-bit audio
*/
runtime->hw.channels_max = 2;
snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
&constraints_channels);
+ runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE;
+ snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16);
snd_pcm_hw_constraint_list(runtime, 0,
SNDRV_PCM_HW_PARAM_RATE, &constraints_rates);
diff --git a/sound/soc/intel/boards/bytcht_da7213.c b/sound/soc/intel/boards/bytcht_da7213.c
new file mode 100644
index 000000000000..18873e23f404
--- /dev/null
+++ b/sound/soc/intel/boards/bytcht_da7213.c
@@ -0,0 +1,283 @@
+/*
+ * bytcht-da7213.c - ASoc Machine driver for Intel Baytrail and
+ * Cherrytrail-based platforms, with Dialog DA7213 codec
+ *
+ * Copyright (C) 2017 Intel Corporation
+ * Author: Pierre-Louis Bossart <pierre-louis.bossart@linux.intel.com>
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <linux/acpi.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <asm/platform_sst_audio.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../../codecs/da7213.h"
+#include "../atom/sst-atom-controls.h"
+#include "../common/sst-acpi.h"
+
+static const struct snd_kcontrol_new controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+ SOC_DAPM_PIN_SWITCH("Headset Mic"),
+ SOC_DAPM_PIN_SWITCH("Mic"),
+ SOC_DAPM_PIN_SWITCH("Aux In"),
+};
+
+static const struct snd_soc_dapm_widget dapm_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone Jack", NULL),
+ SND_SOC_DAPM_MIC("Headset Mic", NULL),
+ SND_SOC_DAPM_MIC("Mic", NULL),
+ SND_SOC_DAPM_LINE("Aux In", NULL),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"Headphone Jack", NULL, "HPL"},
+ {"Headphone Jack", NULL, "HPR"},
+
+ {"AUXL", NULL, "Aux In"},
+ {"AUXR", NULL, "Aux In"},
+
+ /* Assume Mic1 is linked to Headset and Mic2 to on-board mic */
+ {"MIC1", NULL, "Headset Mic"},
+ {"MIC2", NULL, "Mic"},
+
+ /* SOC-codec link */
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
+
+ {"Playback", NULL, "ssp2 Tx"},
+ {"ssp2 Rx", NULL, "Capture"},
+};
+
+static int codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ int ret;
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+
+ /* The DSP will convert the FE rate to 48k, stereo, 24bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ /*
+ * Default mode for SSP configuration is TDM 4 slot, override config
+ * with explicit setting to I2S 2ch 24-bit. The word length is set with
+ * dai_set_tdm_slot() since there is no other API exposed
+ */
+ ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ 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;
+}
+
+static int aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_single(substream->runtime,
+ SNDRV_PCM_HW_PARAM_RATE, 48000);
+}
+
+static int aif1_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, DA7213_CLKSRC_MCLK,
+ 19200000, SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ dev_err(codec_dai->dev, "can't set codec sysclk configuration\n");
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ DA7213_SYSCLK_PLL_SRM, 0, DA7213_PLL_FREQ_OUT_98304000);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "failed to start PLL: %d\n", ret);
+ return -EIO;
+ }
+
+ return ret;
+}
+
+static int aif1_hw_free(struct snd_pcm_substream *substream)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ int ret;
+
+ ret = snd_soc_dai_set_pll(codec_dai, 0,
+ DA7213_SYSCLK_MCLK, 0, 0);
+ if (ret < 0) {
+ dev_err(codec_dai->dev, "failed to stop PLL: %d\n", ret);
+ return -EIO;
+ }
+
+ return ret;
+}
+
+static const struct snd_soc_ops aif1_ops = {
+ .startup = aif1_startup,
+};
+
+static const struct snd_soc_ops ssp2_ops = {
+ .hw_params = aif1_hw_params,
+ .hw_free = aif1_hw_free,
+
+};
+
+static struct snd_soc_dai_link dailink[] = {
+ [MERR_DPCM_AUDIO] = {
+ .name = "Audio Port",
+ .stream_name = "Audio",
+ .cpu_dai_name = "media-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ .nonatomic = true,
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &aif1_ops,
+ },
+ [MERR_DPCM_DEEP_BUFFER] = {
+ .name = "Deep-Buffer Audio Port",
+ .stream_name = "Deep-Buffer Audio",
+ .cpu_dai_name = "deepbuffer-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ .nonatomic = true,
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .ops = &aif1_ops,
+ },
+ [MERR_DPCM_COMPR] = {
+ .name = "Compressed Port",
+ .stream_name = "Compress",
+ .cpu_dai_name = "compress-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ },
+ /* CODEC<->CODEC link */
+ /* back ends */
+ {
+ .name = "SSP2-Codec",
+ .id = 1,
+ .cpu_dai_name = "ssp2-port",
+ .platform_name = "sst-mfld-platform",
+ .no_pcm = 1,
+ .codec_dai_name = "da7213-hifi",
+ .codec_name = "i2c-DLGS7213:00",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBS_CFS,
+ .be_hw_params_fixup = codec_fixup,
+ .nonatomic = true,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &ssp2_ops,
+ },
+};
+
+/* SoC card */
+static struct snd_soc_card bytcht_da7213_card = {
+ .name = "bytcht-da7213",
+ .owner = THIS_MODULE,
+ .dai_link = dailink,
+ .num_links = ARRAY_SIZE(dailink),
+ .controls = controls,
+ .num_controls = ARRAY_SIZE(controls),
+ .dapm_widgets = dapm_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(dapm_widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+};
+
+static char codec_name[16]; /* i2c-<HID>:00 with HID being 8 chars */
+
+static int bytcht_da7213_probe(struct platform_device *pdev)
+{
+ int ret_val = 0;
+ int i;
+ struct snd_soc_card *card;
+ struct sst_acpi_mach *mach;
+ const char *i2c_name = NULL;
+ int dai_index = 0;
+
+ mach = (&pdev->dev)->platform_data;
+ card = &bytcht_da7213_card;
+ card->dev = &pdev->dev;
+
+ /* fix index of codec dai */
+ dai_index = MERR_DPCM_COMPR + 1;
+ for (i = 0; i < ARRAY_SIZE(dailink); i++) {
+ if (!strcmp(dailink[i].codec_name, "i2c-DLGS7213:00")) {
+ dai_index = i;
+ break;
+ }
+ }
+
+ /* fixup codec name based on HID */
+ i2c_name = sst_acpi_find_name_from_hid(mach->id);
+ if (i2c_name != NULL) {
+ snprintf(codec_name, sizeof(codec_name),
+ "%s%s", "i2c-", i2c_name);
+ dailink[dai_index].codec_name = codec_name;
+ }
+
+ ret_val = devm_snd_soc_register_card(&pdev->dev, card);
+ if (ret_val) {
+ dev_err(&pdev->dev,
+ "snd_soc_register_card failed %d\n", ret_val);
+ return ret_val;
+ }
+ platform_set_drvdata(pdev, card);
+ return ret_val;
+}
+
+static struct platform_driver bytcht_da7213_driver = {
+ .driver = {
+ .name = "bytcht_da7213",
+ },
+ .probe = bytcht_da7213_probe,
+};
+module_platform_driver(bytcht_da7213_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail+DA7213 Machine driver");
+MODULE_AUTHOR("Pierre-Louis Bossart");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytcht_da7213");
diff --git a/sound/soc/intel/boards/bytcht_nocodec.c b/sound/soc/intel/boards/bytcht_nocodec.c
new file mode 100644
index 000000000000..89853eeaaf9d
--- /dev/null
+++ b/sound/soc/intel/boards/bytcht_nocodec.c
@@ -0,0 +1,208 @@
+/*
+ * bytcht_nocodec.c - ASoc Machine driver for MinnowBoard Max and Up
+ * to make I2S signals observable on the Low-Speed connector. Audio codec
+ * is not managed by ASoC/DAPM
+ *
+ * Copyright (C) 2015-2017 Intel Corp
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; version 2 of the License.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE. See the GNU
+ * General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/module.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../atom/sst-atom-controls.h"
+
+static const struct snd_soc_dapm_widget widgets[] = {
+ SND_SOC_DAPM_MIC("Mic", NULL),
+ SND_SOC_DAPM_SPK("Speaker", NULL),
+};
+
+static const struct snd_kcontrol_new controls[] = {
+ SOC_DAPM_PIN_SWITCH("Mic"),
+ SOC_DAPM_PIN_SWITCH("Speaker"),
+};
+
+static const struct snd_soc_dapm_route audio_map[] = {
+ {"ssp2 Tx", NULL, "codec_out0"},
+ {"ssp2 Tx", NULL, "codec_out1"},
+ {"codec_in0", NULL, "ssp2 Rx"},
+ {"codec_in1", NULL, "ssp2 Rx"},
+
+ {"ssp2 Rx", NULL, "Mic"},
+ {"Speaker", NULL, "ssp2 Tx"},
+};
+
+static int codec_fixup(struct snd_soc_pcm_runtime *rtd,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_interval *rate = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_RATE);
+ struct snd_interval *channels = hw_param_interval(params,
+ SNDRV_PCM_HW_PARAM_CHANNELS);
+ int ret;
+
+ /* The DSP will convert the FE rate to 48k, stereo, 24bits */
+ rate->min = rate->max = 48000;
+ channels->min = channels->max = 2;
+
+ /* set SSP2 to 24-bit */
+ params_set_format(params, SNDRV_PCM_FORMAT_S24_LE);
+
+ /*
+ * Default mode for SSP configuration is TDM 4 slot, override config
+ * with explicit setting to I2S 2ch 24-bit. The word length is set with
+ * dai_set_tdm_slot() since there is no other API exposed
+ */
+ ret = snd_soc_dai_set_fmt(rtd->cpu_dai,
+ SND_SOC_DAIFMT_I2S |
+ SND_SOC_DAIFMT_NB_NF |
+ 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;
+}
+
+static unsigned int rates_48000[] = {
+ 48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+ .count = ARRAY_SIZE(rates_48000),
+ .list = rates_48000,
+};
+
+static int aif1_startup(struct snd_pcm_substream *substream)
+{
+ return snd_pcm_hw_constraint_list(substream->runtime, 0,
+ SNDRV_PCM_HW_PARAM_RATE,
+ &constraints_48000);
+}
+
+static struct snd_soc_ops aif1_ops = {
+ .startup = aif1_startup,
+};
+
+static struct snd_soc_dai_link dais[] = {
+ [MERR_DPCM_AUDIO] = {
+ .name = "Audio Port",
+ .stream_name = "Audio",
+ .cpu_dai_name = "media-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ .ignore_suspend = 1,
+ .nonatomic = true,
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ .ops = &aif1_ops,
+ },
+ [MERR_DPCM_DEEP_BUFFER] = {
+ .name = "Deep-Buffer Audio Port",
+ .stream_name = "Deep-Buffer Audio",
+ .cpu_dai_name = "deepbuffer-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ .ignore_suspend = 1,
+ .nonatomic = true,
+ .dynamic = 1,
+ .dpcm_playback = 1,
+ .ops = &aif1_ops,
+ },
+ [MERR_DPCM_COMPR] = {
+ .name = "Compressed Port",
+ .stream_name = "Compress",
+ .cpu_dai_name = "compress-cpu-dai",
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .platform_name = "sst-mfld-platform",
+ },
+ /* CODEC<->CODEC link */
+ /* back ends */
+ {
+ .name = "SSP2-LowSpeed Connector",
+ .id = 1,
+ .cpu_dai_name = "ssp2-port",
+ .platform_name = "sst-mfld-platform",
+ .no_pcm = 1,
+ .codec_dai_name = "snd-soc-dummy-dai",
+ .codec_name = "snd-soc-dummy",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+ | SND_SOC_DAIFMT_CBS_CFS,
+ .be_hw_params_fixup = codec_fixup,
+ .ignore_suspend = 1,
+ .nonatomic = true,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+};
+
+/* SoC card */
+static struct snd_soc_card bytcht_nocodec_card = {
+ .name = "bytcht-nocodec",
+ .owner = THIS_MODULE,
+ .dai_link = dais,
+ .num_links = ARRAY_SIZE(dais),
+ .dapm_widgets = widgets,
+ .num_dapm_widgets = ARRAY_SIZE(widgets),
+ .dapm_routes = audio_map,
+ .num_dapm_routes = ARRAY_SIZE(audio_map),
+ .controls = controls,
+ .num_controls = ARRAY_SIZE(controls),
+ .fully_routed = true,
+};
+
+static int snd_bytcht_nocodec_mc_probe(struct platform_device *pdev)
+{
+ int ret_val = 0;
+
+ /* register the soc card */
+ bytcht_nocodec_card.dev = &pdev->dev;
+
+ ret_val = devm_snd_soc_register_card(&pdev->dev, &bytcht_nocodec_card);
+
+ if (ret_val) {
+ dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n",
+ ret_val);
+ return ret_val;
+ }
+ platform_set_drvdata(pdev, &bytcht_nocodec_card);
+ return ret_val;
+}
+
+static struct platform_driver snd_bytcht_nocodec_mc_driver = {
+ .driver = {
+ .name = "bytcht_nocodec",
+ },
+ .probe = snd_bytcht_nocodec_mc_probe,
+};
+module_platform_driver(snd_bytcht_nocodec_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail/Cherrytrail Nocodec Machine driver");
+MODULE_AUTHOR("Pierre-Louis Bossart <pierre-louis.bossart at linux.intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytcht_nocodec");
diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c
index 5c7219fb3aa8..4a76b099a508 100644
--- a/sound/soc/intel/boards/bytcr_rt5640.c
+++ b/sound/soc/intel/boards/bytcr_rt5640.c
@@ -19,6 +19,7 @@
#include <linux/init.h>
#include <linux/module.h>
+#include <linux/moduleparam.h>
#include <linux/platform_device.h>
#include <linux/acpi.h>
#include <linux/device.h>
@@ -56,35 +57,88 @@ enum {
struct byt_rt5640_private {
struct clk *mclk;
};
+static bool is_bytcr;
static unsigned long byt_rt5640_quirk = BYT_RT5640_MCLK_EN;
+static unsigned int quirk_override;
+module_param_named(quirk, quirk_override, uint, 0444);
+MODULE_PARM_DESC(quirk, "Board-specific quirk override");
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");
+ int map;
+ bool has_dmic = false;
+ bool has_mclk = false;
+ bool has_ssp0 = false;
+ bool has_ssp0_aif1 = false;
+ bool has_ssp0_aif2 = false;
+ bool has_ssp2_aif2 = false;
+
+ map = BYT_RT5640_MAP(byt_rt5640_quirk);
+ switch (map) {
+ case BYT_RT5640_DMIC1_MAP:
+ dev_info(dev, "quirk DMIC1_MAP enabled\n");
+ has_dmic = true;
+ break;
+ case BYT_RT5640_DMIC2_MAP:
+ dev_info(dev, "quirk DMIC2_MAP enabled\n");
+ has_dmic = true;
+ break;
+ case BYT_RT5640_IN1_MAP:
+ dev_info(dev, "quirk IN1_MAP enabled\n");
+ break;
+ case BYT_RT5640_IN3_MAP:
+ dev_info(dev, "quirk IN3_MAP enabled\n");
+ break;
+ default:
+ dev_err(dev, "quirk map 0x%x is not supported, microphone input will not work\n", map);
+ break;
+ }
+ if (byt_rt5640_quirk & BYT_RT5640_DMIC_EN) {
+ if (has_dmic)
+ dev_info(dev, "quirk DMIC enabled\n");
+ else
+ dev_err(dev, "quirk DMIC enabled but no DMIC input set, will be ignored\n");
+ }
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");
+ dev_info(dev, "quirk MONO_SPEAKER enabled\n");
+ if (byt_rt5640_quirk & BYT_RT5640_DIFF_MIC) {
+ if (!has_dmic)
+ dev_info(dev, "quirk DIFF_MIC enabled\n");
+ else
+ dev_info(dev, "quirk DIFF_MIC enabled but DMIC input selected, will be ignored\n");
+ }
+ if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF1) {
+ dev_info(dev, "quirk SSP0_AIF1 enabled\n");
+ has_ssp0 = true;
+ has_ssp0_aif1 = true;
+ }
+ if (byt_rt5640_quirk & BYT_RT5640_SSP0_AIF2) {
+ dev_info(dev, "quirk SSP0_AIF2 enabled\n");
+ has_ssp0 = true;
+ has_ssp0_aif2 = true;
+ }
+ if (byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) {
+ dev_info(dev, "quirk SSP2_AIF2 enabled\n");
+ has_ssp2_aif2 = true;
+ }
+ if (is_bytcr && !has_ssp0)
+ dev_err(dev, "Invalid routing, bytcr detected but no SSP0-based quirk, audio cannot work with SSP2 on bytcr\n");
+ if (has_ssp0_aif1 && has_ssp0_aif2)
+ dev_err(dev, "Invalid routing, SSP0 cannot be connected to both AIF1 and AIF2\n");
+ if (has_ssp0 && has_ssp2_aif2)
+ dev_err(dev, "Invalid routing, cannot have both SSP0 and SSP2 connected to codec\n");
+
+ if (byt_rt5640_quirk & BYT_RT5640_MCLK_EN) {
+ dev_info(dev, "quirk MCLK_EN enabled\n");
+ has_mclk = true;
+ }
+ if (byt_rt5640_quirk & BYT_RT5640_MCLK_25MHZ) {
+ if (has_mclk)
+ dev_info(dev, "quirk MCLK_25MHZ enabled\n");
+ else
+ dev_err(dev, "quirk MCLK_25MHZ enabled but quirk MCLK not selected, will be ignored\n");
+ }
}
@@ -128,7 +182,7 @@ static int platform_clock_control(struct snd_soc_dapm_widget *w,
ret = clk_prepare_enable(priv->mclk);
if (ret < 0) {
dev_err(card->dev,
- "could not configure MCLK state");
+ "could not configure MCLK state\n");
return ret;
}
}
@@ -621,7 +675,7 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.platform_name = "sst-mfld-platform",
- .ignore_suspend = 1,
+ .nonatomic = true,
.dynamic = 1,
.dpcm_playback = 1,
.dpcm_capture = 1,
@@ -634,7 +688,6 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.platform_name = "sst-mfld-platform",
- .ignore_suspend = 1,
.nonatomic = true,
.dynamic = 1,
.dpcm_playback = 1,
@@ -661,6 +714,7 @@ static struct snd_soc_dai_link byt_rt5640_dais[] = {
| SND_SOC_DAIFMT_CBS_CFS,
.be_hw_params_fixup = byt_rt5640_codec_fixup,
.ignore_suspend = 1,
+ .nonatomic = true,
.dpcm_playback = 1,
.dpcm_capture = 1,
.init = byt_rt5640_init,
@@ -710,8 +764,8 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
int i;
int dai_index;
struct byt_rt5640_private *priv;
- bool is_bytcr = false;
+ is_bytcr = false;
priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_ATOMIC);
if (!priv)
return -ENOMEM;
@@ -806,6 +860,11 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
/* check quirks before creating card */
dmi_check_system(byt_rt5640_quirk_table);
+ if (quirk_override) {
+ dev_info(&pdev->dev, "Overriding quirk 0x%x => 0x%x\n",
+ (unsigned int)byt_rt5640_quirk, quirk_override);
+ byt_rt5640_quirk = quirk_override;
+ }
log_quirks(&pdev->dev);
if ((byt_rt5640_quirk & BYT_RT5640_SSP2_AIF2) ||
diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c
index 3186f015939f..8164bec63bf1 100644
--- a/sound/soc/intel/boards/bytcr_rt5651.c
+++ b/sound/soc/intel/boards/bytcr_rt5651.c
@@ -235,7 +235,6 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = {
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.platform_name = "sst-mfld-platform",
- .ignore_suspend = 1,
.nonatomic = true,
.dynamic = 1,
.dpcm_playback = 1,
@@ -249,7 +248,6 @@ static struct snd_soc_dai_link byt_rt5651_dais[] = {
.codec_dai_name = "snd-soc-dummy-dai",
.codec_name = "snd-soc-dummy",
.platform_name = "sst-mfld-platform",
- .ignore_suspend = 1,
.nonatomic = true,
.dynamic = 1,
.dpcm_playback = 1,
diff --git a/sound/soc/intel/haswell/sst-haswell-ipc.c b/sound/soc/intel/haswell/sst-haswell-ipc.c
index a3459d1682a6..d33bdaf92c57 100644
--- a/sound/soc/intel/haswell/sst-haswell-ipc.c
+++ b/sound/soc/intel/haswell/sst-haswell-ipc.c
@@ -2000,10 +2000,8 @@ int sst_hsw_module_set_param(struct sst_hsw *hsw,
u32 param_size, char *param)
{
int ret;
- unsigned char *data = NULL;
u32 header = 0;
u32 payload_size = 0, transfer_parameter_size = 0;
- dma_addr_t dma_addr = 0;
struct sst_hsw_transfer_parameter *parameter;
struct device *dev = hsw->dev;
@@ -2047,10 +2045,6 @@ int sst_hsw_module_set_param(struct sst_hsw *hsw,
kfree(parameter);
- if (data)
- dma_free_coherent(hsw->dsp->dma_dev,
- param_size, (void *)data, dma_addr);
-
return ret;
}
diff --git a/sound/soc/intel/skylake/bxt-sst.c b/sound/soc/intel/skylake/bxt-sst.c
index 15a063a403cc..f5e7dbb1ba39 100644
--- a/sound/soc/intel/skylake/bxt-sst.c
+++ b/sound/soc/intel/skylake/bxt-sst.c
@@ -25,7 +25,8 @@
#include "skl-sst-ipc.h"
#define BXT_BASEFW_TIMEOUT 3000
-#define BXT_INIT_TIMEOUT 500
+#define BXT_INIT_TIMEOUT 300
+#define BXT_ROM_INIT_TIMEOUT 70
#define BXT_IPC_PURGE_FW 0x01004000
#define BXT_ROM_INIT 0x5
@@ -45,6 +46,8 @@
/* Delay before scheduling D0i3 entry */
#define BXT_D0I3_DELAY 5000
+#define BXT_FW_ROM_INIT_RETRY 3
+
static unsigned int bxt_get_errorcode(struct sst_dsp *ctx)
{
return sst_dsp_shim_read(ctx, BXT_ADSP_ERROR_CODE);
@@ -55,29 +58,15 @@ bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count)
{
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 < lib_count; i++) {
- ret = request_firmware(&fw, linfo[i].name, ctx->dev);
- if (ret < 0) {
- dev_err(ctx->dev, "Request lib %s failed:%d\n",
- linfo[i].name, ret);
- return ret;
- }
-
- if (skl->is_first_boot) {
- ret = snd_skl_parse_uuids(ctx, fw,
+ ret = skl_prepare_lib_load(skl, &skl->lib_info[i], &stripped_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);
+ if (ret < 0)
+ goto load_library_failed;
stream_tag = ctx->dsp_ops.prepare(ctx->dev, 0x40,
stripped_fw.size, &dmab);
@@ -92,21 +81,19 @@ bxt_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count)
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);
+ ret = skl_sst_ipc_load_library(&skl->ipc, dma_id, i, true);
if (ret < 0)
dev_err(ctx->dev, "IPC Load Lib for %s fail: %d\n",
linfo[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);
+ skl_release_library(linfo, lib_count);
return ret;
}
@@ -156,7 +143,7 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
SKL_ADSP_REG_HIPCIE_DONE,
BXT_INIT_TIMEOUT, "HIPCIE Done");
if (ret < 0) {
- dev_err(ctx->dev, "Timout for Purge Request%d\n", ret);
+ dev_err(ctx->dev, "Timeout for Purge Request%d\n", ret);
goto base_fw_load_failed;
}
@@ -173,7 +160,7 @@ static int sst_bxt_prepare_fw(struct sst_dsp *ctx,
/* Step 7: Wait for ROM init */
ret = sst_dsp_register_poll(ctx, BXT_ADSP_FW_STATUS, SKL_FW_STS_MASK,
- SKL_FW_INIT, BXT_INIT_TIMEOUT, "ROM Load");
+ SKL_FW_INIT, BXT_ROM_INIT_TIMEOUT, "ROM Load");
if (ret < 0) {
dev_err(ctx->dev, "Timeout for ROM init, ret:%d\n", ret);
goto base_fw_load_failed;
@@ -206,18 +193,16 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx)
{
struct firmware stripped_fw;
struct skl_sst *skl = ctx->thread_context;
- int ret;
+ int ret, i;
- 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;
+ if (ctx->fw == NULL) {
+ ret = request_firmware(&ctx->fw, ctx->fw_name, ctx->dev);
+ if (ret < 0) {
+ dev_err(ctx->dev, "Request firmware failed %d\n", ret);
+ return ret;
+ }
}
- /* 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);
@@ -229,18 +214,20 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx)
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) {
+
+ for (i = 0; i < BXT_FW_ROM_INIT_RETRY; i++) {
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",
+ if (ret == 0)
+ break;
+ }
+
+ 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;
- }
+ dev_err(ctx->dev, "Core En/ROM load fail:%d\n", ret);
+ goto sst_load_base_firmware_failed;
}
ret = sst_transfer_fw_host_dma(ctx);
@@ -265,8 +252,11 @@ static int bxt_load_base_firmware(struct sst_dsp *ctx)
}
}
+ return ret;
+
sst_load_base_firmware_failed:
release_firmware(ctx->fw);
+ ctx->fw = NULL;
return ret;
}
@@ -428,6 +418,7 @@ static int bxt_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
return ret;
}
}
+ skl->cores.state[core_id] = SKL_DSP_RUNNING;
return ret;
}
@@ -514,11 +505,22 @@ static int bxt_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
ret = skl_ipc_set_dx(&skl->ipc, BXT_INSTANCE_ID,
BXT_BASE_FW_MODULE_ID, &dx);
- if (ret < 0)
+ if (ret < 0) {
dev_err(ctx->dev,
"Failed to set DSP to D3:core id = %d;Continue reset\n",
core_id);
+ /*
+ * In case of D3 failure, re-download the firmware, so set
+ * fw_loaded to false.
+ */
+ skl->fw_loaded = false;
+ }
+ if (core_id == SKL_DSP_CORE0_ID) {
+ /* disable Interrupt */
+ skl_ipc_op_int_disable(ctx);
+ skl_ipc_int_disable(ctx);
+ }
ret = skl_dsp_disable_core(ctx, core_mask);
if (ret < 0) {
dev_err(ctx->dev, "Failed to disable core %d\n", ret);
@@ -560,23 +562,14 @@ int bxt_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
struct sst_dsp *sst;
int ret;
- skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL);
- if (skl == NULL)
- return -ENOMEM;
-
- skl->dev = dev;
- skl_dev.thread_context = skl;
- INIT_LIST_HEAD(&skl->uuid_list);
-
- skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
- if (!skl->dsp) {
- dev_err(skl->dev, "skl_dsp_ctx_init failed\n");
- return -ENODEV;
+ ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev);
+ if (ret < 0) {
+ dev_err(dev, "%s: no device\n", __func__);
+ return ret;
}
+ skl = *dsp;
sst = skl->dsp;
- sst->fw_name = fw_name;
- sst->dsp_ops = dsp_ops;
sst->fw_ops = bxt_fw_ops;
sst->addr.lpe = mmio_base;
sst->addr.shim = mmio_base;
@@ -584,24 +577,15 @@ 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;
-
/* set the D0i3 check */
skl->ipc.ops.check_dsp_lp_on = skl_ipc_check_D0i0;
skl->cores.count = 2;
skl->boot_complete = false;
init_waitqueue_head(&skl->boot_wait);
- skl->is_first_boot = true;
INIT_DELAYED_WORK(&skl->d0i3.work, bxt_set_dsp_D0i3);
skl->d0i3.state = SKL_DSP_D0I3_NONE;
- if (dsp)
- *dsp = skl;
-
return 0;
}
EXPORT_SYMBOL_GPL(bxt_sst_dsp_init);
@@ -635,6 +619,10 @@ EXPORT_SYMBOL_GPL(bxt_sst_init_fw);
void bxt_sst_dsp_cleanup(struct device *dev, struct skl_sst *ctx)
{
+
+ skl_release_library(ctx->lib_info, ctx->lib_count);
+ if (ctx->dsp->fw)
+ release_firmware(ctx->dsp->fw);
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 e66870474f10..ab1adc0c9cc3 100644
--- a/sound/soc/intel/skylake/skl-messages.c
+++ b/sound/soc/intel/skylake/skl-messages.c
@@ -58,7 +58,7 @@ static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab)
#define NOTIFICATION_MASK 0xf
/* disable notfication for underruns/overruns from firmware module */
-static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
+void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
{
struct notification_mask mask;
struct skl_ipc_large_config_msg msg = {0};
@@ -209,7 +209,7 @@ static const struct skl_dsp_ops dsp_ops[] = {
{
.id = 0x9d71,
.loader_ops = skl_get_loader_ops,
- .init = skl_sst_dsp_init,
+ .init = kbl_sst_dsp_init,
.init_fw = skl_sst_init_fw,
.cleanup = skl_sst_dsp_cleanup
},
@@ -274,6 +274,7 @@ int skl_init_dsp(struct skl *skl)
if (ret < 0)
return ret;
+ skl->skl_sst->dsp_ops = ops;
dev_dbg(bus->dev, "dsp registration status=%d\n", ret);
return ret;
@@ -284,16 +285,11 @@ 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;
- const struct skl_dsp_ops *ops;
/* disable ppcap interrupt */
snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false);
- ops = skl_get_dsp_ops(skl->pci->device);
- if (!ops)
- return -EIO;
-
- ops->cleanup(bus->dev, ctx);
+ ctx->dsp_ops->cleanup(bus->dev, ctx);
if (ctx->dsp->addr.lpe)
iounmap(ctx->dsp->addr.lpe);
@@ -866,7 +862,7 @@ static void skl_clear_module_state(struct skl_module_pin *mpin, int max,
}
if (!found)
- mcfg->m_state = SKL_MODULE_UNINIT;
+ mcfg->m_state = SKL_MODULE_INIT_DONE;
return;
}
@@ -1098,7 +1094,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 started, do stop the pipe in FW. */
- if (pipe->state > SKL_PIPE_STARTED) {
+ if (pipe->state >= SKL_PIPE_STARTED) {
ret = skl_set_pipe_state(ctx, pipe, PPL_PAUSED);
if (ret < 0) {
dev_err(ctx->dev, "Failed to stop pipeline\n");
diff --git a/sound/soc/intel/skylake/skl-nhlt.c b/sound/soc/intel/skylake/skl-nhlt.c
index 7eb9c419dc7f..e3f06672fd6d 100644
--- a/sound/soc/intel/skylake/skl-nhlt.c
+++ b/sound/soc/intel/skylake/skl-nhlt.c
@@ -24,8 +24,6 @@
static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
0xA4, 0x1F, 0x7B, 0x5D, 0xCE, 0x24, 0xC5, 0x53};
-#define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS"
-
struct nhlt_acpi_table *skl_nhlt_init(struct device *dev)
{
acpi_handle handle;
@@ -33,8 +31,9 @@ struct nhlt_acpi_table *skl_nhlt_init(struct device *dev)
struct nhlt_resource_desc *nhlt_ptr = NULL;
struct nhlt_acpi_table *nhlt_table = NULL;
- if (ACPI_FAILURE(acpi_get_handle(NULL, DSDT_NHLT_PATH, &handle))) {
- dev_err(dev, "Requested NHLT device not found\n");
+ handle = ACPI_HANDLE(dev);
+ if (!handle) {
+ dev_err(dev, "Didn't find ACPI_HANDLE\n");
return NULL;
}
diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c
index e12520e142ff..e91bbcffc856 100644
--- a/sound/soc/intel/skylake/skl-pcm.c
+++ b/sound/soc/intel/skylake/skl-pcm.c
@@ -21,6 +21,7 @@
#include <linux/pci.h>
#include <linux/pm_runtime.h>
+#include <linux/delay.h>
#include <sound/pcm_params.h>
#include <sound/soc.h>
#include "skl.h"
@@ -155,7 +156,7 @@ int skl_pcm_host_dma_prepare(struct device *dev, struct skl_pipe_params *params)
snd_hdac_ext_stream_decouple(ebus, stream, true);
format_val = snd_hdac_calc_stream_format(params->s_freq,
- params->ch, params->format, 32, 0);
+ params->ch, params->format, params->host_bps, 0);
dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
format_val, params->s_freq, params->ch, params->format);
@@ -190,8 +191,8 @@ int skl_pcm_link_dma_prepare(struct device *dev, struct skl_pipe_params *params)
stream = stream_to_hdac_ext_stream(hstream);
snd_hdac_ext_stream_decouple(ebus, stream, true);
- format_val = snd_hdac_calc_stream_format(params->s_freq,
- params->ch, params->format, 24, 0);
+ format_val = snd_hdac_calc_stream_format(params->s_freq, params->ch,
+ params->format, params->link_bps, 0);
dev_dbg(dev, "format_val=%d, rate=%d, ch=%d, format=%d\n",
format_val, params->s_freq, params->ch, params->format);
@@ -262,23 +263,6 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
return 0;
}
-static int skl_be_prepare(struct snd_pcm_substream *substream,
- struct snd_soc_dai *dai)
-{
- struct skl *skl = get_skl_ctx(dai->dev);
- struct skl_sst *ctx = skl->skl_sst;
- struct skl_module_cfg *mconfig;
-
- if (dai->playback_widget->power || dai->capture_widget->power)
- return 0;
-
- mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
- if (mconfig == NULL)
- return -EINVAL;
-
- return skl_dsp_set_dma_control(ctx, mconfig);
-}
-
static int skl_pcm_prepare(struct snd_pcm_substream *substream,
struct snd_soc_dai *dai)
{
@@ -326,6 +310,11 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
p_params.host_dma_id = dma_id;
p_params.stream = substream->stream;
p_params.format = params_format(params);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ p_params.host_bps = dai->driver->playback.sig_bits;
+ else
+ p_params.host_bps = dai->driver->capture.sig_bits;
+
m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream);
if (m_cfg)
@@ -564,6 +553,11 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
p_params.link_index = link->index;
p_params.format = params_format(params);
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+ p_params.link_bps = codec_dai->driver->playback.sig_bits;
+ else
+ p_params.link_bps = codec_dai->driver->capture.sig_bits;
+
return skl_tplg_be_update_params(dai, &p_params);
}
@@ -649,7 +643,6 @@ static struct snd_soc_dai_ops skl_dmic_dai_ops = {
static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {
.hw_params = skl_be_hw_params,
- .prepare = skl_be_prepare,
};
static struct snd_soc_dai_ops skl_link_dai_ops = {
@@ -670,6 +663,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_8000,
.formats = SNDRV_PCM_FMTBIT_S16_LE |
SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 32,
},
.capture = {
.stream_name = "System Capture",
@@ -677,6 +671,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.channels_max = HDA_STEREO,
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ .sig_bits = 32,
},
},
{
@@ -688,6 +683,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.channels_max = HDA_QUAD,
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ .sig_bits = 32,
},
},
{
@@ -699,6 +695,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.channels_max = HDA_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ .sig_bits = 32,
},
},
{
@@ -710,6 +707,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.channels_max = HDA_STEREO,
.rates = SNDRV_PCM_RATE_48000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ .sig_bits = 32,
},
},
{
@@ -721,6 +719,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
.channels_max = HDA_QUAD,
.rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
+ .sig_bits = 32,
},
},
{
@@ -736,6 +735,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 32,
},
},
{
@@ -751,6 +751,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 32,
},
},
{
@@ -766,6 +767,7 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
SNDRV_PCM_RATE_192000,
.formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
SNDRV_PCM_FMTBIT_S32_LE,
+ .sig_bits = 32,
},
},
@@ -949,14 +951,12 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
static int skl_platform_open(struct snd_pcm_substream *substream)
{
- struct snd_pcm_runtime *runtime;
struct snd_soc_pcm_runtime *rtd = substream->private_data;
struct snd_soc_dai_link *dai_link = rtd->dai_link;
dev_dbg(rtd->cpu_dai->dev, "In %s:%s\n", __func__,
dai_link->cpu_dai_name);
- runtime = substream->runtime;
snd_soc_set_runtime_hwparams(substream, &azx_pcm_hw);
return 0;
@@ -1062,13 +1062,31 @@ static snd_pcm_uframes_t skl_platform_pcm_pointer
* HAD space reflects the actual data that is transferred.
* Use the position buffer for capture, as DPIB write gets
* completed earlier than the actual data written to the DDR.
+ *
+ * For capture stream following workaround is required to fix the
+ * incorrect position reporting.
+ *
+ * 1. Wait for 20us before reading the DMA position in buffer once
+ * the interrupt is generated for stream completion as update happens
+ * on the HDA frame boundary i.e. 20.833uSec.
+ * 2. Read DPIB register to flush the DMA position value. This dummy
+ * read is required to flush DMA position value.
+ * 3. Read the DMA Position-in-Buffer. This value now will be equal to
+ * or greater than period boundary.
*/
- if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+
+ if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
pos = readl(ebus->bus.remap_addr + AZX_REG_VS_SDXDPIB_XBASE +
(AZX_REG_VS_SDXDPIB_XINTERVAL *
hdac_stream(hstream)->index));
- else
+ } else {
+ udelay(20);
+ readl(ebus->bus.remap_addr +
+ AZX_REG_VS_SDXDPIB_XBASE +
+ (AZX_REG_VS_SDXDPIB_XINTERVAL *
+ hdac_stream(hstream)->index));
pos = snd_hdac_stream_get_pos_posbuf(hdac_stream(hstream));
+ }
if (pos >= hdac_stream(hstream)->bufsize)
pos = 0;
@@ -1165,7 +1183,7 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
snd_dma_pci_data(skl->pci),
size, MAX_PREALLOC_SIZE);
if (retval) {
- dev_err(dai->dev, "dma buffer allocationf fail\n");
+ dev_err(dai->dev, "dma buffer allocation fail\n");
return retval;
}
}
@@ -1173,29 +1191,52 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
return retval;
}
+static int skl_get_module_info(struct skl *skl, struct skl_module_cfg *mconfig)
+{
+ struct skl_sst *ctx = skl->skl_sst;
+ 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 -EIO;
+ }
+
+ 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 -EIO;
+}
+
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;
+ int ret = 0;
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);
+ ret = skl_get_module_info(skl, mconfig);
if (ret < 0) {
dev_err(skl->skl_sst->dev,
- "query module info failed:%d\n", ret);
- goto err;
+ "query module info failed\n");
+ return ret;
}
}
}
-err:
+
return ret;
}
@@ -1232,6 +1273,7 @@ static int skl_platform_soc_probe(struct snd_soc_platform *platform)
}
skl_populate_modules(skl);
skl->skl_sst->update_d0i3c = skl_update_d0i3c;
+ skl_dsp_enable_notification(skl->skl_sst, false);
}
pm_runtime_mark_last_busy(platform->dev);
pm_runtime_put_autosuspend(platform->dev);
@@ -1256,6 +1298,7 @@ int skl_platform_register(struct device *dev)
struct skl *skl = ebus_to_skl(ebus);
INIT_LIST_HEAD(&skl->ppl_list);
+ INIT_LIST_HEAD(&skl->bind_list);
ret = snd_soc_register_platform(dev, &skl_platform_drv);
if (ret) {
@@ -1276,6 +1319,17 @@ int skl_platform_register(struct device *dev)
int skl_platform_unregister(struct device *dev)
{
+ struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+ struct skl *skl = ebus_to_skl(ebus);
+ struct skl_module_deferred_bind *modules, *tmp;
+
+ if (!list_empty(&skl->bind_list)) {
+ list_for_each_entry_safe(modules, tmp, &skl->bind_list, node) {
+ list_del(&modules->node);
+ kfree(modules);
+ }
+ }
+
snd_soc_unregister_component(dev);
snd_soc_unregister_platform(dev);
return 0;
diff --git a/sound/soc/intel/skylake/skl-sst-cldma.c b/sound/soc/intel/skylake/skl-sst-cldma.c
index c9f6d87381db..d2b1d60fec02 100644
--- a/sound/soc/intel/skylake/skl-sst-cldma.c
+++ b/sound/soc/intel/skylake/skl-sst-cldma.c
@@ -164,7 +164,7 @@ static void skl_cldma_cleanup(struct sst_dsp *ctx)
ctx->dsp_ops.free_dma_buf(ctx->dev, &ctx->cl_dev.dmab_bdl);
}
-static int skl_cldma_wait_interruptible(struct sst_dsp *ctx)
+int skl_cldma_wait_interruptible(struct sst_dsp *ctx)
{
int ret = 0;
@@ -243,9 +243,14 @@ static void skl_cldma_fill_buffer(struct sst_dsp *ctx, unsigned int size,
* 2. Polling on fw register to identify if data left to transferred doesn't
* fill the ring buffer. Caller takes care of polling the required status
* register to identify the transfer status.
+ * 3. if wait flag is set, waits for DBL interrupt to copy the next chunk till
+ * bytes_left is 0.
+ * if wait flag is not set, doesn't wait for BDL interrupt. after ccopying
+ * the first chunk return the no of bytes_left to be copied.
*/
static int
-skl_cldma_copy_to_buf(struct sst_dsp *ctx, const void *bin, u32 total_size)
+skl_cldma_copy_to_buf(struct sst_dsp *ctx, const void *bin,
+ u32 total_size, bool wait)
{
int ret = 0;
bool start = true;
@@ -272,13 +277,14 @@ skl_cldma_copy_to_buf(struct sst_dsp *ctx, const void *bin, u32 total_size)
size = ctx->cl_dev.bufsize;
skl_cldma_fill_buffer(ctx, size, curr_pos, true, start);
- start = false;
- ret = skl_cldma_wait_interruptible(ctx);
- if (ret < 0) {
- skl_cldma_stop(ctx);
- return ret;
+ if (wait) {
+ start = false;
+ ret = skl_cldma_wait_interruptible(ctx);
+ if (ret < 0) {
+ skl_cldma_stop(ctx);
+ return ret;
+ }
}
-
} else {
skl_cldma_int_disable(ctx);
@@ -298,9 +304,11 @@ skl_cldma_copy_to_buf(struct sst_dsp *ctx, const void *bin, u32 total_size)
}
bytes_left -= size;
curr_pos = curr_pos + size;
+ if (!wait)
+ return bytes_left;
}
- return ret;
+ return bytes_left;
}
void skl_cldma_process_intr(struct sst_dsp *ctx)
diff --git a/sound/soc/intel/skylake/skl-sst-cldma.h b/sound/soc/intel/skylake/skl-sst-cldma.h
index 99e4c86b6358..5b730a1a0ae4 100644
--- a/sound/soc/intel/skylake/skl-sst-cldma.h
+++ b/sound/soc/intel/skylake/skl-sst-cldma.h
@@ -213,7 +213,7 @@ struct skl_cl_dev_ops {
void (*cl_trigger)(struct sst_dsp *ctx, bool enable);
void (*cl_cleanup_controller)(struct sst_dsp *ctx);
int (*cl_copy_to_dmabuf)(struct sst_dsp *ctx,
- const void *bin, u32 size);
+ const void *bin, u32 size, bool wait);
void (*cl_stop_dma)(struct sst_dsp *ctx);
};
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.c b/sound/soc/intel/skylake/skl-sst-dsp.c
index c3deefab65d6..08332723c700 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.c
+++ b/sound/soc/intel/skylake/skl-sst-dsp.c
@@ -355,12 +355,13 @@ int skl_dsp_get_core(struct sst_dsp *ctx, unsigned int core_id)
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;
+ goto out;
}
}
skl->cores.usage_count[core_id]++;
+out:
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]);
@@ -379,7 +380,8 @@ int skl_dsp_put_core(struct sst_dsp *ctx, unsigned int core_id)
return -EINVAL;
}
- if (--skl->cores.usage_count[core_id] == 0) {
+ if ((--skl->cores.usage_count[core_id] == 0) &&
+ (skl->cores.state[core_id] != SKL_DSP_RESET)) {
ret = ctx->fw_ops.set_state_D3(ctx, core_id);
if (ret < 0) {
dev_err(ctx->dev, "unable to put core %d: %d\n",
diff --git a/sound/soc/intel/skylake/skl-sst-dsp.h b/sound/soc/intel/skylake/skl-sst-dsp.h
index 849410d0823e..eba20d37ba8c 100644
--- a/sound/soc/intel/skylake/skl-sst-dsp.h
+++ b/sound/soc/intel/skylake/skl-sst-dsp.h
@@ -17,13 +17,15 @@
#define __SKL_SST_DSP_H__
#include <linux/interrupt.h>
+#include <linux/uuid.h>
+#include <linux/firmware.h>
#include <sound/memalloc.h>
#include "skl-sst-cldma.h"
-#include "skl-topology.h"
struct sst_dsp;
struct skl_sst;
struct sst_dsp_device;
+struct skl_lib_info;
/* Intel HD Audio General DSP Registers */
#define SKL_ADSP_GEN_BASE 0x0
@@ -144,7 +146,7 @@ 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_lib_info *linfo, int count);
+ struct skl_lib_info *linfo, int lib_count);
int (*parse_fw)(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);
@@ -172,6 +174,19 @@ struct skl_dsp_loader_ops {
int stream_tag);
};
+#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_load_module_info {
u16 mod_id;
const struct firmware *fw;
@@ -186,6 +201,7 @@ struct skl_module_table {
void skl_cldma_process_intr(struct sst_dsp *ctx);
void skl_cldma_int_disable(struct sst_dsp *ctx);
int skl_cldma_prepare(struct sst_dsp *ctx);
+int skl_cldma_wait_interruptible(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,
@@ -214,6 +230,9 @@ int skl_dsp_boot(struct sst_dsp *ctx);
int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
struct skl_sst **dsp);
+int kbl_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 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);
@@ -222,17 +241,22 @@ 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_id(struct skl_sst *ctx, uuid_le *uuid_mod, int instance_id);
+int skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id);
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);
+void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable);
+int skl_sst_ctx_init(struct device *dev, int irq, const char *fw_name,
+ struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp,
+ struct sst_dsp_device *skl_dev);
+int skl_prepare_lib_load(struct skl_sst *skl, struct skl_lib_info *linfo,
+ struct firmware *stripped_fw,
+ unsigned int hdr_offset, int index);
+void skl_release_library(struct skl_lib_info *linfo, int lib_count);
+
#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 e1391dfbc9e9..58c525096a7c 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.c
+++ b/sound/soc/intel/skylake/skl-sst-ipc.c
@@ -34,6 +34,11 @@
#define IPC_GLB_REPLY_STATUS_MASK ((0x1 << IPC_GLB_REPLY_STATUS_SHIFT) - 1)
#define IPC_GLB_REPLY_STATUS(x) ((x) << IPC_GLB_REPLY_STATUS_SHIFT)
+#define IPC_GLB_REPLY_TYPE_SHIFT 29
+#define IPC_GLB_REPLY_TYPE_MASK 0x1F
+#define IPC_GLB_REPLY_TYPE(x) (((x) >> IPC_GLB_REPLY_TYPE_SHIFT) \
+ & IPC_GLB_RPLY_TYPE_MASK)
+
#define IPC_TIMEOUT_MSECS 3000
#define IPC_EMPTY_LIST_SIZE 8
@@ -387,12 +392,27 @@ static int skl_ipc_process_notification(struct sst_generic_ipc *ipc,
return 0;
}
+static int skl_ipc_set_reply_error_code(u32 reply)
+{
+ switch (reply) {
+ case IPC_GLB_REPLY_OUT_OF_MEMORY:
+ return -ENOMEM;
+
+ case IPC_GLB_REPLY_BUSY:
+ return -EBUSY;
+
+ default:
+ return -EINVAL;
+ }
+}
+
static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
struct skl_ipc_header header)
{
struct ipc_message *msg;
u32 reply = header.primary & IPC_GLB_REPLY_STATUS_MASK;
u64 *ipc_header = (u64 *)(&header);
+ struct skl_sst *skl = container_of(ipc, struct skl_sst, ipc);
msg = skl_ipc_reply_get_msg(ipc, *ipc_header);
if (msg == NULL) {
@@ -401,33 +421,39 @@ static void skl_ipc_process_reply(struct sst_generic_ipc *ipc,
}
/* first process the header */
- switch (reply) {
- case IPC_GLB_REPLY_SUCCESS:
+ if (reply == IPC_GLB_REPLY_SUCCESS) {
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;
-
- case IPC_GLB_REPLY_OUT_OF_MEMORY:
- dev_err(ipc->dev, "ipc fw reply: %x: no memory\n", header.primary);
- msg->errno = -ENOMEM;
- break;
-
- case IPC_GLB_REPLY_BUSY:
- dev_err(ipc->dev, "ipc fw reply: %x: Busy\n", header.primary);
- msg->errno = -EBUSY;
- break;
+ switch (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) {
+ case IPC_GLB_LOAD_MULTIPLE_MODS:
+ case IPC_GLB_LOAD_LIBRARY:
+ skl->mod_load_complete = true;
+ skl->mod_load_status = true;
+ wake_up(&skl->mod_load_wait);
+ break;
- default:
- dev_err(ipc->dev, "Unknown ipc reply: 0x%x\n", reply);
- msg->errno = -EINVAL;
- break;
- }
+ default:
+ break;
- if (reply != IPC_GLB_REPLY_SUCCESS) {
+ }
+ } else {
+ msg->errno = skl_ipc_set_reply_error_code(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));
+ switch (IPC_GLB_NOTIFY_MSG_TYPE(header.primary)) {
+ case IPC_GLB_LOAD_MULTIPLE_MODS:
+ case IPC_GLB_LOAD_LIBRARY:
+ skl->mod_load_complete = true;
+ skl->mod_load_status = false;
+ wake_up(&skl->mod_load_wait);
+ break;
+
+ default:
+ break;
+
+ }
}
list_del(&msg->list);
@@ -811,8 +837,8 @@ int skl_ipc_load_modules(struct sst_generic_ipc *ipc,
header.primary |= IPC_GLB_TYPE(IPC_GLB_LOAD_MULTIPLE_MODS);
header.primary |= IPC_LOAD_MODULE_CNT(module_cnt);
- ret = sst_ipc_tx_message_wait(ipc, *ipc_header, data,
- (sizeof(u16) * module_cnt), NULL, 0);
+ ret = sst_ipc_tx_message_nowait(ipc, *ipc_header, data,
+ (sizeof(u16) * module_cnt));
if (ret < 0)
dev_err(ipc->dev, "ipc: load modules failed :%d\n", ret);
@@ -947,7 +973,7 @@ int skl_ipc_get_large_config(struct sst_generic_ipc *ipc,
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)
+ u8 dma_id, u8 table_id, bool wait)
{
struct skl_ipc_header header = {0};
u64 *ipc_header = (u64 *)(&header);
@@ -959,7 +985,11 @@ int skl_sst_ipc_load_library(struct sst_generic_ipc *ipc,
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 (wait)
+ ret = sst_ipc_tx_message_wait(ipc, *ipc_header,
+ NULL, 0, NULL, 0);
+ else
+ ret = sst_ipc_tx_message_nowait(ipc, *ipc_header, NULL, 0);
if (ret < 0)
dev_err(ipc->dev, "ipc: load lib failed\n");
diff --git a/sound/soc/intel/skylake/skl-sst-ipc.h b/sound/soc/intel/skylake/skl-sst-ipc.h
index 9660ace379ab..e057da2713c6 100644
--- a/sound/soc/intel/skylake/skl-sst-ipc.h
+++ b/sound/soc/intel/skylake/skl-sst-ipc.h
@@ -69,6 +69,14 @@ struct skl_d0i3_data {
struct delayed_work work;
};
+#define SKL_LIB_NAME_LENGTH 128
+#define SKL_MAX_LIB 16
+
+struct skl_lib_info {
+ char name[SKL_LIB_NAME_LENGTH];
+ const struct firmware *fw;
+};
+
struct skl_sst {
struct device *dev;
struct sst_dsp *dsp;
@@ -77,6 +85,11 @@ struct skl_sst {
wait_queue_head_t boot_wait;
bool boot_complete;
+ /* module load */
+ wait_queue_head_t mod_load_wait;
+ bool mod_load_complete;
+ bool mod_load_status;
+
/* IPC messaging */
struct sst_generic_ipc ipc;
@@ -105,6 +118,8 @@ struct skl_sst {
void (*update_d0i3c)(struct device *dev, bool enable);
struct skl_d0i3_data d0i3;
+
+ const struct skl_dsp_ops *dsp_ops;
};
struct skl_ipc_init_instance_msg {
@@ -182,7 +197,7 @@ 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);
+ u8 dma_id, u8 table_id, bool wait);
int skl_ipc_set_d0ix(struct sst_generic_ipc *ipc,
struct skl_ipc_d0ix_msg *msg);
diff --git a/sound/soc/intel/skylake/skl-sst-utils.c b/sound/soc/intel/skylake/skl-sst-utils.c
index ea162fbf68e5..81ee251881b4 100644
--- a/sound/soc/intel/skylake/skl-sst-utils.c
+++ b/sound/soc/intel/skylake/skl-sst-utils.c
@@ -94,19 +94,6 @@ struct adsp_fw_hdr {
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;
@@ -115,32 +102,6 @@ struct skl_ext_manifest_hdr {
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;
@@ -222,21 +183,18 @@ static inline int skl_pvtid_128(struct uuid_module *module)
* 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)
+int skl_get_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int instance_id)
{
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;
+ module->instance_id[pvt_id] = instance_id;
+
return pvt_id;
}
}
@@ -254,23 +212,21 @@ EXPORT_SYMBOL_GPL(skl_get_pvt_id);
*
* 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 skl_put_pvt_id(struct skl_sst *ctx, uuid_le *uuid_mod, int *pvt_id)
{
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;
+ if (*pvt_id != 0)
+ i = (*pvt_id) / 64;
else
i = 0;
- module->pvt_id[i] &= ~(1 << (mconfig->id.pvt_id));
- mconfig->id.pvt_id = -1;
+ module->pvt_id[i] &= ~(1 << (*pvt_id));
+ *pvt_id = -1;
return 0;
}
}
@@ -405,3 +361,83 @@ int skl_dsp_strip_extended_manifest(struct firmware *fw)
return 0;
}
+
+int skl_sst_ctx_init(struct device *dev, int irq, const char *fw_name,
+ struct skl_dsp_loader_ops dsp_ops, struct skl_sst **dsp,
+ struct sst_dsp_device *skl_dev)
+{
+ struct skl_sst *skl;
+ struct sst_dsp *sst;
+ int ret;
+
+ skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL);
+ if (skl == NULL)
+ return -ENOMEM;
+
+ skl->dev = dev;
+ skl_dev->thread_context = skl;
+ INIT_LIST_HEAD(&skl->uuid_list);
+ skl->dsp = skl_dsp_ctx_init(dev, skl_dev, irq);
+ if (!skl->dsp) {
+ dev_err(skl->dev, "%s: no device\n", __func__);
+ return -ENODEV;
+ }
+
+ sst = skl->dsp;
+ sst->fw_name = fw_name;
+ sst->dsp_ops = dsp_ops;
+ init_waitqueue_head(&skl->mod_load_wait);
+ INIT_LIST_HEAD(&sst->module_list);
+ ret = skl_ipc_init(dev, skl);
+ if (ret)
+ return ret;
+
+ skl->is_first_boot = true;
+ if (dsp)
+ *dsp = skl;
+
+ return ret;
+}
+
+int skl_prepare_lib_load(struct skl_sst *skl, struct skl_lib_info *linfo,
+ struct firmware *stripped_fw,
+ unsigned int hdr_offset, int index)
+{
+ int ret;
+ struct sst_dsp *dsp = skl->dsp;
+
+ if (linfo->fw == NULL) {
+ ret = request_firmware(&linfo->fw, linfo->name,
+ skl->dev);
+ if (ret < 0) {
+ dev_err(skl->dev, "Request lib %s failed:%d\n",
+ linfo->name, ret);
+ return ret;
+ }
+ }
+
+ if (skl->is_first_boot) {
+ ret = snd_skl_parse_uuids(dsp, linfo->fw, hdr_offset, index);
+ if (ret < 0)
+ return ret;
+ }
+
+ stripped_fw->data = linfo->fw->data;
+ stripped_fw->size = linfo->fw->size;
+ skl_dsp_strip_extended_manifest(stripped_fw);
+
+ return 0;
+}
+
+void skl_release_library(struct skl_lib_info *linfo, int lib_count)
+{
+ int i;
+
+ /* library indices start from 1 to N. 0 represents base FW */
+ for (i = 1; i < lib_count; i++) {
+ if (linfo[i].fw) {
+ release_firmware(linfo[i].fw);
+ linfo[i].fw = NULL;
+ }
+ }
+}
diff --git a/sound/soc/intel/skylake/skl-sst.c b/sound/soc/intel/skylake/skl-sst.c
index b30bd384c8d3..155e456b7a3a 100644
--- a/sound/soc/intel/skylake/skl-sst.c
+++ b/sound/soc/intel/skylake/skl-sst.c
@@ -52,7 +52,8 @@ static int skl_transfer_firmware(struct sst_dsp *ctx,
{
int ret = 0;
- ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, basefw, base_fw_size);
+ ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, basefw, base_fw_size,
+ true);
if (ret < 0)
return ret;
@@ -178,6 +179,18 @@ static int skl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
dev_err(ctx->dev, "unable to load firmware\n");
return ret;
}
+
+ /* load libs as they are also lost on D3 */
+ if (skl->lib_count > 1) {
+ ret = ctx->fw_ops.load_library(ctx, skl->lib_info,
+ skl->lib_count);
+ if (ret < 0) {
+ dev_err(ctx->dev, "reload libs failed: %d\n",
+ ret);
+ return ret;
+ }
+
+ }
}
/*
@@ -203,7 +216,7 @@ static int skl_set_dsp_D0(struct sst_dsp *ctx, unsigned int core_id)
skl->cores.state[core_id] = SKL_DSP_RUNNING;
- return ret;
+ return 0;
}
static int skl_set_dsp_D3(struct sst_dsp *ctx, unsigned int core_id)
@@ -323,27 +336,85 @@ static struct skl_module_table *skl_module_get_from_id(
return NULL;
}
-static int skl_transfer_module(struct sst_dsp *ctx,
- struct skl_load_module_info *module)
+static int skl_transfer_module(struct sst_dsp *ctx, const void *data,
+ u32 size, u16 mod_id, u8 table_id, bool is_module)
{
- int ret;
+ int ret, bytes_left, curr_pos;
struct skl_sst *skl = ctx->thread_context;
+ skl->mod_load_complete = false;
- ret = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, module->fw->data,
- module->fw->size);
- if (ret < 0)
- return ret;
+ bytes_left = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx, data, size, false);
+ if (bytes_left < 0)
+ return bytes_left;
- ret = skl_ipc_load_modules(&skl->ipc, SKL_NUM_MODULES,
- (void *)&module->mod_id);
- if (ret < 0)
- dev_err(ctx->dev, "Failed to Load module: %d\n", ret);
+ /* check is_module flag to load module or library */
+ if (is_module)
+ ret = skl_ipc_load_modules(&skl->ipc, SKL_NUM_MODULES, &mod_id);
+ else
+ ret = skl_sst_ipc_load_library(&skl->ipc, 0, table_id, false);
+
+ if (ret < 0) {
+ dev_err(ctx->dev, "Failed to Load %s with err %d\n",
+ is_module ? "module" : "lib", ret);
+ goto out;
+ }
+
+ /*
+ * if bytes_left > 0 then wait for BDL complete interrupt and
+ * copy the next chunk till bytes_left is 0. if bytes_left is
+ * is zero, then wait for load module IPC reply
+ */
+ while (bytes_left > 0) {
+ curr_pos = size - bytes_left;
+
+ ret = skl_cldma_wait_interruptible(ctx);
+ if (ret < 0)
+ goto out;
+
+ bytes_left = ctx->cl_dev.ops.cl_copy_to_dmabuf(ctx,
+ data + curr_pos,
+ bytes_left, false);
+ }
+ ret = wait_event_timeout(skl->mod_load_wait, skl->mod_load_complete,
+ msecs_to_jiffies(SKL_IPC_BOOT_MSECS));
+ if (ret == 0 || !skl->mod_load_status) {
+ dev_err(ctx->dev, "Module Load failed\n");
+ ret = -EIO;
+ }
+
+out:
ctx->cl_dev.ops.cl_stop_dma(ctx);
return ret;
}
+static int
+kbl_load_library(struct sst_dsp *ctx, struct skl_lib_info *linfo, int lib_count)
+{
+ struct skl_sst *skl = ctx->thread_context;
+ struct firmware stripped_fw;
+ int ret, i;
+
+ /* library indices start from 1 to N. 0 represents base FW */
+ for (i = 1; i < lib_count; i++) {
+ ret = skl_prepare_lib_load(skl, &skl->lib_info[i], &stripped_fw,
+ SKL_ADSP_FW_BIN_HDR_OFFSET, i);
+ if (ret < 0)
+ goto load_library_failed;
+ ret = skl_transfer_module(ctx, stripped_fw.data,
+ stripped_fw.size, 0, i, false);
+ if (ret < 0)
+ goto load_library_failed;
+ }
+
+ return 0;
+
+load_library_failed:
+ skl_release_library(linfo, lib_count);
+ return ret;
+}
+
static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, u8 *guid)
{
struct skl_module_table *module_entry = NULL;
@@ -365,7 +436,9 @@ static int skl_load_module(struct sst_dsp *ctx, u16 mod_id, u8 *guid)
}
if (!module_entry->usage_cnt) {
- ret = skl_transfer_module(ctx, module_entry->mod_info);
+ ret = skl_transfer_module(ctx, module_entry->mod_info->fw->data,
+ module_entry->mod_info->fw->size,
+ mod_id, 0, true);
if (ret < 0) {
dev_err(ctx->dev, "Failed to Load module\n");
return ret;
@@ -388,6 +461,11 @@ static int skl_unload_module(struct sst_dsp *ctx, u16 mod_id)
dev_err(ctx->dev, "Module bad usage cnt!:%d\n", usage_cnt);
return -EIO;
}
+
+ /* if module is used by others return, no need to unload */
+ if (usage_cnt > 0)
+ return 0;
+
ret = skl_ipc_unload_modules(&skl->ipc,
SKL_NUM_MODULES, &mod_id);
if (ret < 0) {
@@ -434,6 +512,16 @@ static struct skl_dsp_fw_ops skl_fw_ops = {
.unload_mod = skl_unload_module,
};
+static struct skl_dsp_fw_ops kbl_fw_ops = {
+ .set_state_D0 = skl_set_dsp_D0,
+ .set_state_D3 = skl_set_dsp_D3,
+ .load_fw = skl_load_base_firmware,
+ .get_fw_errcode = skl_get_errorcode,
+ .load_library = kbl_load_library,
+ .load_mod = skl_load_module,
+ .unload_mod = skl_unload_module,
+};
+
static struct sst_ops skl_ops = {
.irq_handler = skl_dsp_sst_interrupt,
.write = sst_shim32_write,
@@ -455,45 +543,47 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
struct sst_dsp *sst;
int ret;
- skl = devm_kzalloc(dev, sizeof(*skl), GFP_KERNEL);
- if (skl == NULL)
- return -ENOMEM;
-
- skl->dev = dev;
- skl_dev.thread_context = skl;
- INIT_LIST_HEAD(&skl->uuid_list);
-
- skl->dsp = skl_dsp_ctx_init(dev, &skl_dev, irq);
- if (!skl->dsp) {
- dev_err(skl->dev, "%s: no device\n", __func__);
- return -ENODEV;
+ ret = skl_sst_ctx_init(dev, irq, fw_name, dsp_ops, dsp, &skl_dev);
+ if (ret < 0) {
+ dev_err(dev, "%s: no device\n", __func__);
+ return ret;
}
+ skl = *dsp;
sst = skl->dsp;
-
- sst->fw_name = fw_name;
sst->addr.lpe = mmio_base;
sst->addr.shim = mmio_base;
sst_dsp_mailbox_init(sst, (SKL_ADSP_SRAM0_BASE + SKL_ADSP_W0_STAT_SZ),
SKL_ADSP_W0_UP_SZ, SKL_ADSP_SRAM1_BASE, SKL_ADSP_W1_SZ);
- INIT_LIST_HEAD(&sst->module_list);
- sst->dsp_ops = dsp_ops;
sst->fw_ops = skl_fw_ops;
- ret = skl_ipc_init(dev, skl);
- if (ret)
+ skl->cores.count = 2;
+
+ return 0;
+}
+EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
+
+int kbl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
+ const char *fw_name, struct skl_dsp_loader_ops dsp_ops,
+ struct skl_sst **dsp)
+{
+ struct sst_dsp *sst;
+ int ret;
+
+ ret = skl_sst_dsp_init(dev, mmio_base, irq, fw_name, dsp_ops, dsp);
+ if (ret < 0) {
+ dev_err(dev, "%s: Init failed %d\n", __func__, ret);
return ret;
+ }
- skl->cores.count = 2;
- skl->is_first_boot = true;
+ sst = (*dsp)->dsp;
+ sst->fw_ops = kbl_fw_ops;
- if (dsp)
- *dsp = skl;
+ return 0;
- return ret;
}
-EXPORT_SYMBOL_GPL(skl_sst_dsp_init);
+EXPORT_SYMBOL_GPL(kbl_sst_dsp_init);
int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx)
{
@@ -507,6 +597,15 @@ int skl_sst_init_fw(struct device *dev, struct skl_sst *ctx)
}
skl_dsp_init_core_state(sst);
+
+ if (ctx->lib_count > 1) {
+ ret = sst->fw_ops.load_library(sst, ctx->lib_info,
+ ctx->lib_count);
+ if (ret < 0) {
+ dev_err(dev, "Load Library failed : %x\n", ret);
+ return ret;
+ }
+ }
ctx->is_first_boot = false;
return 0;
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
index 2dbfb1b24ef4..3a99712e44a8 100644
--- a/sound/soc/intel/skylake/skl-topology.c
+++ b/sound/soc/intel/skylake/skl-topology.c
@@ -299,8 +299,6 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
{
int multiplier = 1;
struct skl_module_fmt *in_fmt, *out_fmt;
- int in_rate, out_rate;
-
/* Since fixups is applied to pin 0 only, ibs, obs needs
* change for pin 0 only
@@ -311,22 +309,12 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT)
multiplier = 5;
- if (in_fmt->s_freq % 1000)
- in_rate = (in_fmt->s_freq / 1000) + 1;
- else
- in_rate = (in_fmt->s_freq / 1000);
-
- mcfg->ibs = in_rate * (mcfg->in_fmt->channels) *
- (mcfg->in_fmt->bit_depth >> 3) *
+ mcfg->ibs = DIV_ROUND_UP(in_fmt->s_freq, 1000) *
+ in_fmt->channels * (in_fmt->bit_depth >> 3) *
multiplier;
- if (mcfg->out_fmt->s_freq % 1000)
- out_rate = (mcfg->out_fmt->s_freq / 1000) + 1;
- else
- out_rate = (mcfg->out_fmt->s_freq / 1000);
-
- mcfg->obs = out_rate * (mcfg->out_fmt->channels) *
- (mcfg->out_fmt->bit_depth >> 3) *
+ mcfg->obs = DIV_ROUND_UP(out_fmt->s_freq, 1000) *
+ out_fmt->channels * (out_fmt->bit_depth >> 3) *
multiplier;
}
@@ -551,6 +539,7 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
int ret = 0;
list_for_each_entry(w_module, &pipe->w_list, node) {
+ uuid_le *uuid_mod;
w = w_module->w;
mconfig = w->priv;
@@ -588,13 +577,15 @@ 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);
+ uuid_mod = (uuid_le *)mconfig->guid;
+ mconfig->id.pvt_id = skl_get_pvt_id(ctx, uuid_mod,
+ mconfig->id.instance_id);
if (mconfig->id.pvt_id < 0)
return ret;
skl_tplg_set_module_init_data(w);
ret = skl_init_module(ctx, mconfig);
if (ret < 0) {
- skl_put_pvt_id(ctx, mconfig);
+ skl_put_pvt_id(ctx, uuid_mod, &mconfig->id.pvt_id);
return ret;
}
skl_tplg_alloc_pipe_mcps(skl, mconfig);
@@ -614,7 +605,9 @@ static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
struct skl_module_cfg *mconfig = NULL;
list_for_each_entry(w_module, &pipe->w_list, node) {
+ uuid_le *uuid_mod;
mconfig = w_module->w->priv;
+ uuid_mod = (uuid_le *)mconfig->guid;
if (mconfig->is_loadable && ctx->dsp->fw_ops.unload_mod &&
mconfig->m_state > SKL_MODULE_UNINIT) {
@@ -623,7 +616,7 @@ static int skl_tplg_unload_pipe_modules(struct skl_sst *ctx,
if (ret < 0)
return -EIO;
}
- skl_put_pvt_id(ctx, mconfig);
+ skl_put_pvt_id(ctx, uuid_mod, &mconfig->id.pvt_id);
}
/* no modules to unload in this path, so return */
@@ -645,8 +638,9 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
struct skl_module_cfg *mconfig = w->priv;
struct skl_pipe_module *w_module;
struct skl_pipe *s_pipe = mconfig->pipe;
- struct skl_module_cfg *src_module = NULL, *dst_module;
+ struct skl_module_cfg *src_module = NULL, *dst_module, *module;
struct skl_sst *ctx = skl->skl_sst;
+ struct skl_module_deferred_bind *modules;
/* check resource available */
if (!skl_is_pipe_mcps_avail(skl, mconfig))
@@ -687,29 +681,48 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
src_module = dst_module;
}
+ /*
+ * When the destination module is initialized, check for these modules
+ * in deferred bind list. If found, bind them.
+ */
+ list_for_each_entry(w_module, &s_pipe->w_list, node) {
+ if (list_empty(&skl->bind_list))
+ break;
+
+ list_for_each_entry(modules, &skl->bind_list, node) {
+ module = w_module->w->priv;
+ if (modules->dst == module)
+ skl_bind_modules(ctx, modules->src,
+ modules->dst);
+ }
+ }
+
return 0;
}
-static int skl_fill_sink_instance_id(struct skl_sst *ctx,
- struct skl_algo_data *alg_data)
+static int skl_fill_sink_instance_id(struct skl_sst *ctx, u32 *params,
+ int size, struct skl_module_cfg *mcfg)
{
- 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;
+ if (mcfg->m_type == SKL_MODULE_TYPE_KPB) {
+ struct skl_kpb_params *kpb_params =
+ (struct skl_kpb_params *)params;
+ struct skl_mod_inst_map *inst = kpb_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++;
+ for (i = 0; i < kpb_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.
@@ -726,6 +739,7 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w,
struct soc_bytes_ext *sb;
struct skl_algo_data *bc;
struct skl_specific_cfg *sp_cfg;
+ u32 *params;
/*
* check all out/in pins are in bind state.
@@ -758,11 +772,18 @@ 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);
+ params = kzalloc(bc->max, GFP_KERNEL);
+ if (!params)
+ return -ENOMEM;
+
+ memcpy(params, bc->params, bc->max);
+ skl_fill_sink_instance_id(ctx, params, bc->max,
+ mconfig);
+
+ ret = skl_set_module_params(ctx, params,
+ bc->max, bc->param_id, mconfig);
+ kfree(params);
+
if (ret < 0)
return ret;
}
@@ -772,6 +793,44 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w,
return 0;
}
+
+static int skl_tplg_module_add_deferred_bind(struct skl *skl,
+ struct skl_module_cfg *src, struct skl_module_cfg *dst)
+{
+ struct skl_module_deferred_bind *m_list, *modules;
+ int i;
+
+ /* only supported for module with static pin connection */
+ for (i = 0; i < dst->max_in_queue; i++) {
+ struct skl_module_pin *pin = &dst->m_in_pin[i];
+
+ if (pin->is_dynamic)
+ continue;
+
+ if ((pin->id.module_id == src->id.module_id) &&
+ (pin->id.instance_id == src->id.instance_id)) {
+
+ if (!list_empty(&skl->bind_list)) {
+ list_for_each_entry(modules, &skl->bind_list, node) {
+ if (modules->src == src && modules->dst == dst)
+ return 0;
+ }
+ }
+
+ m_list = kzalloc(sizeof(*m_list), GFP_KERNEL);
+ if (!m_list)
+ return -ENOMEM;
+
+ m_list->src = src;
+ m_list->dst = dst;
+
+ list_add(&m_list->node, &skl->bind_list);
+ }
+ }
+
+ return 0;
+}
+
static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
struct skl *skl,
struct snd_soc_dapm_widget *src_w,
@@ -806,6 +865,28 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
sink = p->sink;
sink_mconfig = sink->priv;
+ /*
+ * Modules other than PGA leaf can be connected
+ * directly or via switch to a module in another
+ * pipeline. EX: reference path
+ * when the path is enabled, the dst module that needs
+ * to be bound may not be initialized. if the module is
+ * not initialized, add these modules in the deferred
+ * bind list and when the dst module is initialised,
+ * bind this module to the dst_module in deferred list.
+ */
+ if (((src_mconfig->m_state == SKL_MODULE_INIT_DONE)
+ && (sink_mconfig->m_state == SKL_MODULE_UNINIT))) {
+
+ ret = skl_tplg_module_add_deferred_bind(skl,
+ src_mconfig, sink_mconfig);
+
+ if (ret < 0)
+ return ret;
+
+ }
+
+
if (src_mconfig->m_state == SKL_MODULE_UNINIT ||
sink_mconfig->m_state == SKL_MODULE_UNINIT)
continue;
@@ -985,15 +1066,6 @@ static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w,
src_mconfig = sink_mconfig->m_in_pin[i].tgt_mcfg;
if (!src_mconfig)
continue;
- /*
- * If path_found == 1, that means pmd for source
- * pipe has not occurred, source is connected to
- * some other sink. so its responsibility of sink
- * to unbind itself from source.
- */
- ret = skl_stop_pipe(ctx, src_mconfig->pipe);
- if (ret < 0)
- return ret;
ret = skl_unbind_modules(ctx,
src_mconfig, sink_mconfig);
@@ -1019,6 +1091,7 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
struct skl_module_cfg *src_module = NULL, *dst_module;
struct skl_sst *ctx = skl->skl_sst;
struct skl_pipe *s_pipe = mconfig->pipe;
+ struct skl_module_deferred_bind *modules, *tmp;
if (s_pipe->state == SKL_PIPE_INVALID)
return -EINVAL;
@@ -1027,6 +1100,35 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
skl_tplg_free_pipe_mem(skl, mconfig);
list_for_each_entry(w_module, &s_pipe->w_list, node) {
+ if (list_empty(&skl->bind_list))
+ break;
+
+ src_module = w_module->w->priv;
+
+ list_for_each_entry_safe(modules, tmp, &skl->bind_list, node) {
+ /*
+ * When the destination module is deleted, Unbind the
+ * modules from deferred bind list.
+ */
+ if (modules->dst == src_module) {
+ skl_unbind_modules(ctx, modules->src,
+ modules->dst);
+ }
+
+ /*
+ * When the source module is deleted, remove this entry
+ * from the deferred bind list.
+ */
+ if (modules->src == src_module) {
+ list_del(&modules->node);
+ modules->src = NULL;
+ modules->dst = NULL;
+ kfree(modules);
+ }
+ }
+ }
+
+ list_for_each_entry(w_module, &s_pipe->w_list, node) {
dst_module = w_module->w->priv;
if (mconfig->m_state >= SKL_MODULE_INIT_DONE)
@@ -1042,6 +1144,11 @@ static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
skl_delete_pipe(ctx, mconfig->pipe);
+ list_for_each_entry(w_module, &s_pipe->w_list, node) {
+ src_module = w_module->w->priv;
+ src_module->m_state = SKL_MODULE_UNINIT;
+ }
+
return skl_tplg_unload_pipe_modules(ctx, s_pipe);
}
@@ -1083,36 +1190,6 @@ static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
}
/*
- * In modelling, we assume there will be ONLY one mixer in a pipeline. If
- * mixer is not required then it is treated as static mixer aka vmixer with
- * a hard path to source module
- * So we don't need to check if source is started or not as hard path puts
- * dependency on each other
- */
-static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w,
- struct snd_kcontrol *k, int event)
-{
- struct snd_soc_dapm_context *dapm = w->dapm;
- struct skl *skl = get_skl_ctx(dapm->dev);
-
- switch (event) {
- case SND_SOC_DAPM_PRE_PMU:
- return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
-
- case SND_SOC_DAPM_POST_PMU:
- return skl_tplg_mixer_dapm_post_pmu_event(w, skl);
-
- case SND_SOC_DAPM_PRE_PMD:
- return skl_tplg_mixer_dapm_pre_pmd_event(w, skl);
-
- case SND_SOC_DAPM_POST_PMD:
- return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
- }
-
- return 0;
-}
-
-/*
* In modelling, we assume there will be ONLY one mixer in a pipeline. If a
* second one is required that is created as another pipe entity.
* The mixer is responsible for pipe management and represent a pipeline
@@ -1252,10 +1329,12 @@ static void skl_tplg_fill_dma_id(struct skl_module_cfg *mcfg,
case SKL_DEVICE_HDALINK:
pipe->p_params->link_dma_id = params->link_dma_id;
pipe->p_params->link_index = params->link_index;
+ pipe->p_params->link_bps = params->link_bps;
break;
case SKL_DEVICE_HDAHOST:
pipe->p_params->host_dma_id = params->host_dma_id;
+ pipe->p_params->host_bps = params->host_bps;
break;
default:
@@ -1578,7 +1657,7 @@ int skl_tplg_be_update_params(struct snd_soc_dai *dai,
static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = {
{SKL_MIXER_EVENT, skl_tplg_mixer_event},
- {SKL_VMIXER_EVENT, skl_tplg_vmixer_event},
+ {SKL_VMIXER_EVENT, skl_tplg_mixer_event},
{SKL_PGA_EVENT, skl_tplg_pga_event},
};
@@ -1632,7 +1711,7 @@ static int skl_tplg_add_pipe(struct device *dev,
list_for_each_entry(ppl, &skl->ppl_list, node) {
if (ppl->pipe->ppl_id == tkn_elem->value) {
mconfig->pipe = ppl->pipe;
- return EEXIST;
+ return -EEXIST;
}
}
@@ -1924,11 +2003,13 @@ static int skl_tplg_get_token(struct device *dev,
ret = skl_tplg_add_pipe(dev,
mconfig, skl, tkn_elem);
- if (ret < 0)
+ if (ret < 0) {
+ if (ret == -EEXIST) {
+ is_pipe_exists = 1;
+ break;
+ }
return is_pipe_exists;
-
- if (ret == EEXIST)
- is_pipe_exists = 1;
+ }
break;
diff --git a/sound/soc/intel/skylake/skl-topology.h b/sound/soc/intel/skylake/skl-topology.h
index fefab0e99a3b..cc64d6bdb4f6 100644
--- a/sound/soc/intel/skylake/skl-topology.h
+++ b/sound/soc/intel/skylake/skl-topology.h
@@ -257,6 +257,8 @@ struct skl_pipe_params {
snd_pcm_format_t format;
int link_index;
int stream;
+ unsigned int host_bps;
+ unsigned int link_bps;
};
struct skl_pipe {
@@ -334,17 +336,10 @@ struct skl_pipeline {
struct list_head node;
};
-#define SKL_LIB_NAME_LENGTH 128
-#define SKL_MAX_LIB 16
-
-struct skl_lib_info {
- char name[SKL_LIB_NAME_LENGTH];
- const struct firmware *fw;
-};
-
-struct skl_manifest {
- u32 lib_count;
- struct skl_lib_info lib[SKL_MAX_LIB];
+struct skl_module_deferred_bind {
+ struct skl_module_cfg *src;
+ struct skl_module_cfg *dst;
+ struct list_head node;
};
static inline struct skl *get_skl_ctx(struct device *dev)
diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c
index 0c57d4eaae3a..6df3b317a476 100644
--- a/sound/soc/intel/skylake/skl.c
+++ b/sound/soc/intel/skylake/skl.c
@@ -512,7 +512,7 @@ static int probe_codec(struct hdac_ext_bus *ebus, int addr)
struct hdac_bus *bus = ebus_to_hbus(ebus);
unsigned int cmd = (addr << 28) | (AC_NODE_ROOT << 20) |
(AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID;
- unsigned int res;
+ unsigned int res = -1;
mutex_lock(&bus->cmd_mutex);
snd_hdac_bus_send_cmd(bus, cmd);
diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h
index bbef77d2b917..a454f6035f3e 100644
--- a/sound/soc/intel/skylake/skl.h
+++ b/sound/soc/intel/skylake/skl.h
@@ -27,27 +27,6 @@
#define SKL_SUSPEND_DELAY 2000
-/* Vendor Specific Registers */
-#define AZX_REG_VS_EM1 0x1000
-#define AZX_REG_VS_INRC 0x1004
-#define AZX_REG_VS_OUTRC 0x1008
-#define AZX_REG_VS_FIFOTRK 0x100C
-#define AZX_REG_VS_FIFOTRK2 0x1010
-#define AZX_REG_VS_EM2 0x1030
-#define AZX_REG_VS_EM3L 0x1038
-#define AZX_REG_VS_EM3U 0x103C
-#define AZX_REG_VS_EM4L 0x1040
-#define AZX_REG_VS_EM4U 0x1044
-#define AZX_REG_VS_LTRC 0x1048
-#define AZX_REG_VS_D0I3C 0x104A
-#define AZX_REG_VS_PCE 0x104B
-#define AZX_REG_VS_L2MAGC 0x1050
-#define AZX_REG_VS_L2LAHPT 0x1054
-#define AZX_REG_VS_SDXDPIB_XBASE 0x1084
-#define AZX_REG_VS_SDXDPIB_XINTERVAL 0x20
-#define AZX_REG_VS_SDXEFIFOS_XBASE 0x1094
-#define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
-
#define AZX_PCIREG_PGCTL 0x44
#define AZX_PGCTL_LSRMD_MASK (1 << 4)
#define AZX_PCIREG_CGCTL 0x48
@@ -77,6 +56,7 @@ struct skl {
struct skl_dsp_resource resource;
struct list_head ppl_list;
+ struct list_head bind_list;
const char *fw_name;
char tplg_name[64];
diff --git a/sound/soc/mediatek/Kconfig b/sound/soc/mediatek/Kconfig
index d7013bde6f45..5c68797f36c4 100644
--- a/sound/soc/mediatek/Kconfig
+++ b/sound/soc/mediatek/Kconfig
@@ -22,6 +22,16 @@ config SND_SOC_MT2701_CS42448
Select Y if you have such device.
If unsure select "N".
+config SND_SOC_MT2701_WM8960
+ tristate "ASoc Audio driver for MT2701 with WM8960 codec"
+ depends on SND_SOC_MT2701 && I2C
+ select SND_SOC_WM8960
+ help
+ This adds ASoC driver for Mediatek MT2701 boards
+ with the WM8960 codecs.
+ Select Y if you have such device.
+ If unsure select "N".
+
config SND_SOC_MT8173
tristate "ASoC support for Mediatek MT8173 chip"
depends on ARCH_MEDIATEK
diff --git a/sound/soc/mediatek/mt2701/Makefile b/sound/soc/mediatek/mt2701/Makefile
index 31c3d04d4942..c91deb6aca21 100644
--- a/sound/soc/mediatek/mt2701/Makefile
+++ b/sound/soc/mediatek/mt2701/Makefile
@@ -17,3 +17,4 @@ obj-$(CONFIG_SND_SOC_MT2701) += snd-soc-mt2701-afe.o
# machine driver
obj-$(CONFIG_SND_SOC_MT2701_CS42448) += mt2701-cs42448.o
+obj-$(CONFIG_SND_SOC_MT2701_WM8960) += mt2701-wm8960.o
diff --git a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
index c7fa3e663463..bc5d4db94de6 100644
--- a/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
+++ b/sound/soc/mediatek/mt2701/mt2701-afe-pcm.c
@@ -604,6 +604,22 @@ static struct snd_soc_dai_ops mt2701_btmrg_ops = {
static struct snd_soc_dai_driver mt2701_afe_pcm_dais[] = {
/* FE DAIs: memory intefaces to CPU */
{
+ .name = "PCMO0",
+ .id = MT2701_MEMIF_DL1,
+ .suspend = mtk_afe_dai_suspend,
+ .resume = mtk_afe_dai_resume,
+ .playback = {
+ .stream_name = "DL1",
+ .channels_min = 1,
+ .channels_max = 2,
+ .rates = SNDRV_PCM_RATE_8000_192000,
+ .formats = (SNDRV_PCM_FMTBIT_S16_LE
+ | SNDRV_PCM_FMTBIT_S24_LE
+ | SNDRV_PCM_FMTBIT_S32_LE)
+ },
+ .ops = &mt2701_single_memif_dai_ops,
+ },
+ {
.name = "PCM_multi",
.id = MT2701_MEMIF_DLM,
.suspend = mtk_afe_dai_suspend,
diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
index 1e7e8d43fd8a..aa5b31b121e3 100644
--- a/sound/soc/mediatek/mt2701/mt2701-cs42448.c
+++ b/sound/soc/mediatek/mt2701/mt2701-cs42448.c
@@ -129,7 +129,7 @@ static int mt2701_cs42448_fe_ops_startup(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
+static const struct snd_soc_ops mt2701_cs42448_48k_fe_ops = {
.startup = mt2701_cs42448_fe_ops_startup,
};
diff --git a/sound/soc/mediatek/mt2701/mt2701-wm8960.c b/sound/soc/mediatek/mt2701/mt2701-wm8960.c
new file mode 100644
index 000000000000..a08ce2323bdc
--- /dev/null
+++ b/sound/soc/mediatek/mt2701/mt2701-wm8960.c
@@ -0,0 +1,176 @@
+/*
+ * mt2701-wm8960.c -- MT2701 WM8960 ALSA SoC machine driver
+ *
+ * Copyright (c) 2017 MediaTek Inc.
+ * Author: Ryder Lee <ryder.lee@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 "mt2701-afe-common.h"
+
+static const struct snd_soc_dapm_widget mt2701_wm8960_widgets[] = {
+ SND_SOC_DAPM_HP("Headphone", NULL),
+ SND_SOC_DAPM_MIC("AMIC", NULL),
+};
+
+static const struct snd_kcontrol_new mt2701_wm8960_controls[] = {
+ SOC_DAPM_PIN_SWITCH("Headphone"),
+ SOC_DAPM_PIN_SWITCH("AMIC"),
+};
+
+static int mt2701_wm8960_be_ops_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct snd_soc_dai *codec_dai = rtd->codec_dai;
+ struct snd_soc_dai *cpu_dai = rtd->cpu_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;
+
+ snd_soc_dai_set_sysclk(cpu_dai, 0, mclk_rate, SND_SOC_CLOCK_OUT);
+ snd_soc_dai_set_sysclk(codec_dai, 0, mclk_rate, SND_SOC_CLOCK_IN);
+
+ return 0;
+}
+
+static struct snd_soc_ops mt2701_wm8960_be_ops = {
+ .hw_params = mt2701_wm8960_be_ops_hw_params
+};
+
+static struct snd_soc_dai_link mt2701_wm8960_dai_links[] = {
+ /* FE */
+ {
+ .name = "wm8960-playback",
+ .stream_name = "wm8960-playback",
+ .cpu_dai_name = "PCMO0",
+ .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,
+ },
+ {
+ .name = "wm8960-capture",
+ .stream_name = "wm8960-capture",
+ .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},
+ .dynamic = 1,
+ .dpcm_capture = 1,
+ },
+ /* BE */
+ {
+ .name = "wm8960-codec",
+ .cpu_dai_name = "I2S0",
+ .no_pcm = 1,
+ .codec_dai_name = "wm8960-hifi",
+ .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS
+ | SND_SOC_DAIFMT_GATED,
+ .ops = &mt2701_wm8960_be_ops,
+ .dpcm_playback = 1,
+ .dpcm_capture = 1,
+ },
+};
+
+static struct snd_soc_card mt2701_wm8960_card = {
+ .name = "mt2701-wm8960",
+ .owner = THIS_MODULE,
+ .dai_link = mt2701_wm8960_dai_links,
+ .num_links = ARRAY_SIZE(mt2701_wm8960_dai_links),
+ .controls = mt2701_wm8960_controls,
+ .num_controls = ARRAY_SIZE(mt2701_wm8960_controls),
+ .dapm_widgets = mt2701_wm8960_widgets,
+ .num_dapm_widgets = ARRAY_SIZE(mt2701_wm8960_widgets),
+};
+
+static int mt2701_wm8960_machine_probe(struct platform_device *pdev)
+{
+ struct snd_soc_card *card = &mt2701_wm8960_card;
+ struct device_node *platform_node, *codec_node;
+ int ret, i;
+
+ platform_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,platform", 0);
+ if (!platform_node) {
+ dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < card->num_links; i++) {
+ if (mt2701_wm8960_dai_links[i].platform_name)
+ continue;
+ mt2701_wm8960_dai_links[i].platform_of_node = platform_node;
+ }
+
+ card->dev = &pdev->dev;
+
+ codec_node = of_parse_phandle(pdev->dev.of_node,
+ "mediatek,audio-codec", 0);
+ if (!codec_node) {
+ dev_err(&pdev->dev,
+ "Property 'audio-codec' missing or invalid\n");
+ return -EINVAL;
+ }
+ for (i = 0; i < card->num_links; i++) {
+ if (mt2701_wm8960_dai_links[i].codec_name)
+ continue;
+ mt2701_wm8960_dai_links[i].codec_of_node = codec_node;
+ }
+
+ ret = snd_soc_of_parse_audio_routing(card, "audio-routing");
+ if (ret) {
+ dev_err(&pdev->dev, "failed to parse audio-routing: %d\n", ret);
+ return ret;
+ }
+
+ 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_wm8960_machine_dt_match[] = {
+ {.compatible = "mediatek,mt2701-wm8960-machine",},
+ {}
+};
+#endif
+
+static struct platform_driver mt2701_wm8960_machine = {
+ .driver = {
+ .name = "mt2701-wm8960",
+ .owner = THIS_MODULE,
+#ifdef CONFIG_OF
+ .of_match_table = mt2701_wm8960_machine_dt_match,
+#endif
+ },
+ .probe = mt2701_wm8960_machine_probe,
+};
+
+module_platform_driver(mt2701_wm8960_machine);
+
+/* Module information */
+MODULE_DESCRIPTION("MT2701 WM8960 ALSA SoC machine driver");
+MODULE_AUTHOR("Ryder Lee <ryder.lee@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("mt2701 wm8960 soc card");
+
diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c
index 46c8e6ae00b4..e0c2b23ec711 100644
--- a/sound/soc/mediatek/mt8173/mt8173-max98090.c
+++ b/sound/soc/mediatek/mt8173/mt8173-max98090.c
@@ -67,7 +67,7 @@ static int mt8173_max98090_hw_params(struct snd_pcm_substream *substream,
SND_SOC_CLOCK_IN);
}
-static struct snd_soc_ops mt8173_max98090_ops = {
+static const struct snd_soc_ops mt8173_max98090_ops = {
.hw_params = mt8173_max98090_hw_params,
};
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
index 467f7049a288..5e383eb456a4 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c
@@ -75,7 +75,7 @@ static int mt8173_rt5650_rt5514_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops mt8173_rt5650_rt5514_ops = {
+static const struct snd_soc_ops mt8173_rt5650_rt5514_ops = {
.hw_params = mt8173_rt5650_rt5514_hw_params,
};
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
index 1b8b2a778845..fed1f15a39c2 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c
@@ -79,7 +79,7 @@ static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops mt8173_rt5650_rt5676_ops = {
+static const struct snd_soc_ops mt8173_rt5650_rt5676_ops = {
.hw_params = mt8173_rt5650_rt5676_hw_params,
};
diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
index ba65f4157a7e..a78470839b65 100644
--- a/sound/soc/mediatek/mt8173/mt8173-rt5650.c
+++ b/sound/soc/mediatek/mt8173/mt8173-rt5650.c
@@ -105,7 +105,7 @@ static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops mt8173_rt5650_ops = {
+static const struct snd_soc_ops mt8173_rt5650_ops = {
.hw_params = mt8173_rt5650_hw_params,
};
diff --git a/sound/soc/omap/am3517evm.c b/sound/soc/omap/am3517evm.c
index 25a33e9d417a..d5651026ec10 100644
--- a/sound/soc/omap/am3517evm.c
+++ b/sound/soc/omap/am3517evm.c
@@ -49,7 +49,7 @@ static int am3517evm_hw_params(struct snd_pcm_substream *substream,
return ret;
}
-static struct snd_soc_ops am3517evm_ops = {
+static const struct snd_soc_ops am3517evm_ops = {
.hw_params = am3517evm_hw_params,
};
diff --git a/sound/soc/omap/n810.c b/sound/soc/omap/n810.c
index fdecb7043174..71e5f31fa306 100644
--- a/sound/soc/omap/n810.c
+++ b/sound/soc/omap/n810.c
@@ -124,7 +124,7 @@ static int n810_hw_params(struct snd_pcm_substream *substream,
return err;
}
-static struct snd_soc_ops n810_ops = {
+static const struct snd_soc_ops n810_ops = {
.startup = n810_startup,
.hw_params = n810_hw_params,
.shutdown = n810_shutdown,
diff --git a/sound/soc/omap/omap-abe-twl6040.c b/sound/soc/omap/omap-abe-twl6040.c
index 89fe95e877db..614b18d2f631 100644
--- a/sound/soc/omap/omap-abe-twl6040.c
+++ b/sound/soc/omap/omap-abe-twl6040.c
@@ -70,7 +70,7 @@ static int omap_abe_hw_params(struct snd_pcm_substream *substream,
return ret;
}
-static struct snd_soc_ops omap_abe_ops = {
+static const struct snd_soc_ops omap_abe_ops = {
.hw_params = omap_abe_hw_params,
};
diff --git a/sound/soc/omap/omap-twl4030.c b/sound/soc/omap/omap-twl4030.c
index 743131473056..a24b0dedabb9 100644
--- a/sound/soc/omap/omap-twl4030.c
+++ b/sound/soc/omap/omap-twl4030.c
@@ -73,7 +73,7 @@ static int omap_twl4030_hw_params(struct snd_pcm_substream *substream,
return snd_soc_runtime_set_dai_fmt(rtd, fmt);
}
-static struct snd_soc_ops omap_twl4030_ops = {
+static const struct snd_soc_ops omap_twl4030_ops = {
.hw_params = omap_twl4030_hw_params,
};
diff --git a/sound/soc/omap/omap3pandora.c b/sound/soc/omap/omap3pandora.c
index 732e749a1f8e..4e3de712159c 100644
--- a/sound/soc/omap/omap3pandora.c
+++ b/sound/soc/omap/omap3pandora.c
@@ -184,7 +184,7 @@ static int omap3pandora_in_init(struct snd_soc_pcm_runtime *rtd)
return 0;
}
-static struct snd_soc_ops omap3pandora_ops = {
+static const struct snd_soc_ops omap3pandora_ops = {
.hw_params = omap3pandora_hw_params,
};
diff --git a/sound/soc/omap/osk5912.c b/sound/soc/omap/osk5912.c
index aa4053bf6710..e4096779ca05 100644
--- a/sound/soc/omap/osk5912.c
+++ b/sound/soc/omap/osk5912.c
@@ -68,7 +68,7 @@ static int osk_hw_params(struct snd_pcm_substream *substream,
return err;
}
-static struct snd_soc_ops osk_ops = {
+static const struct snd_soc_ops osk_ops = {
.startup = osk_startup,
.hw_params = osk_hw_params,
.shutdown = osk_shutdown,
diff --git a/sound/soc/omap/rx51.c b/sound/soc/omap/rx51.c
index a76845748a10..3aeb65feaea1 100644
--- a/sound/soc/omap/rx51.c
+++ b/sound/soc/omap/rx51.c
@@ -123,7 +123,7 @@ static int rx51_hw_params(struct snd_pcm_substream *substream,
SND_SOC_CLOCK_IN);
}
-static struct snd_soc_ops rx51_ops = {
+static const struct snd_soc_ops rx51_ops = {
.startup = rx51_startup,
.hw_params = rx51_hw_params,
};
@@ -433,10 +433,9 @@ static int rx51_soc_probe(struct platform_device *pdev)
}
pdata = devm_kzalloc(&pdev->dev, sizeof(*pdata), GFP_KERNEL);
- if (pdata == NULL) {
- dev_err(card->dev, "failed to create private data\n");
+ if (pdata == NULL)
return -ENOMEM;
- }
+
snd_soc_card_set_drvdata(card, pdata);
pdata->tvout_selection_gpio = devm_gpiod_get(card->dev,
diff --git a/sound/soc/pxa/brownstone.c b/sound/soc/pxa/brownstone.c
index b6cb9950f05d..9a3f5b799720 100644
--- a/sound/soc/pxa/brownstone.c
+++ b/sound/soc/pxa/brownstone.c
@@ -74,7 +74,7 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
}
/* machine stream operations */
-static struct snd_soc_ops brownstone_ops = {
+static const struct snd_soc_ops brownstone_ops = {
.hw_params = brownstone_wm8994_hw_params,
};
diff --git a/sound/soc/pxa/corgi.c b/sound/soc/pxa/corgi.c
index 311774e9ca46..054e0d65db9d 100644
--- a/sound/soc/pxa/corgi.c
+++ b/sound/soc/pxa/corgi.c
@@ -154,7 +154,7 @@ static int corgi_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops corgi_ops = {
+static const struct snd_soc_ops corgi_ops = {
.startup = corgi_startup,
.hw_params = corgi_hw_params,
.shutdown = corgi_shutdown,
diff --git a/sound/soc/pxa/e750_wm9705.c b/sound/soc/pxa/e750_wm9705.c
index fdcd94adee7c..82bcbbb1841b 100644
--- a/sound/soc/pxa/e750_wm9705.c
+++ b/sound/soc/pxa/e750_wm9705.c
@@ -81,7 +81,7 @@ static struct snd_soc_dai_link e750_dai[] = {
.name = "AC97 Aux",
.stream_name = "AC97 Aux",
.cpu_dai_name = "pxa2xx-ac97-aux",
- .codec_dai_name ="wm9705-aux",
+ .codec_dai_name = "wm9705-aux",
.platform_name = "pxa-pcm-audio",
.codec_name = "wm9705-codec",
},
diff --git a/sound/soc/pxa/e800_wm9712.c b/sound/soc/pxa/e800_wm9712.c
index 2df714f70ec0..1ed8aa2348f1 100644
--- a/sound/soc/pxa/e800_wm9712.c
+++ b/sound/soc/pxa/e800_wm9712.c
@@ -81,7 +81,7 @@ static struct snd_soc_dai_link e800_dai[] = {
.name = "AC97 Aux",
.stream_name = "AC97 Aux",
.cpu_dai_name = "pxa2xx-ac97-aux",
- .codec_dai_name ="wm9712-aux",
+ .codec_dai_name = "wm9712-aux",
.platform_name = "pxa-pcm-audio",
.codec_name = "wm9712-codec",
},
diff --git a/sound/soc/pxa/em-x270.c b/sound/soc/pxa/em-x270.c
index 6f2020f6c8d3..e046770ce70e 100644
--- a/sound/soc/pxa/em-x270.c
+++ b/sound/soc/pxa/em-x270.c
@@ -43,7 +43,7 @@ static struct snd_soc_dai_link em_x270_dai[] = {
.name = "AC97 Aux",
.stream_name = "AC97 Aux",
.cpu_dai_name = "pxa2xx-ac97-aux",
- .codec_dai_name ="wm9712-aux",
+ .codec_dai_name = "wm9712-aux",
.platform_name = "pxa-pcm-audio",
.codec_name = "wm9712-codec",
},
diff --git a/sound/soc/pxa/hx4700.c b/sound/soc/pxa/hx4700.c
index 85483049b916..a9ac881c2e14 100644
--- a/sound/soc/pxa/hx4700.c
+++ b/sound/soc/pxa/hx4700.c
@@ -79,7 +79,7 @@ static int hx4700_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops hx4700_ops = {
+static const struct snd_soc_ops hx4700_ops = {
.hw_params = hx4700_hw_params,
};
diff --git a/sound/soc/pxa/imote2.c b/sound/soc/pxa/imote2.c
index 9d0e40771ef5..78475376f971 100644
--- a/sound/soc/pxa/imote2.c
+++ b/sound/soc/pxa/imote2.c
@@ -42,7 +42,7 @@ static int imote2_asoc_hw_params(struct snd_pcm_substream *substream,
return ret;
}
-static struct snd_soc_ops imote2_asoc_ops = {
+static const struct snd_soc_ops imote2_asoc_ops = {
.hw_params = imote2_asoc_hw_params,
};
diff --git a/sound/soc/pxa/magician.c b/sound/soc/pxa/magician.c
index 2d4d4455fe87..2fc012b06c43 100644
--- a/sound/soc/pxa/magician.c
+++ b/sound/soc/pxa/magician.c
@@ -255,12 +255,12 @@ static int magician_capture_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops magician_capture_ops = {
+static const struct snd_soc_ops magician_capture_ops = {
.startup = magician_startup,
.hw_params = magician_capture_hw_params,
};
-static struct snd_soc_ops magician_playback_ops = {
+static const struct snd_soc_ops magician_playback_ops = {
.startup = magician_startup,
.hw_params = magician_playback_hw_params,
};
diff --git a/sound/soc/pxa/mioa701_wm9713.c b/sound/soc/pxa/mioa701_wm9713.c
index 8760a6687885..c4c6fbedc723 100644
--- a/sound/soc/pxa/mioa701_wm9713.c
+++ b/sound/soc/pxa/mioa701_wm9713.c
@@ -157,7 +157,7 @@ static struct snd_soc_dai_link mioa701_dai[] = {
.name = "AC97 Aux",
.stream_name = "AC97 Aux",
.cpu_dai_name = "pxa2xx-ac97-aux",
- .codec_dai_name ="wm9713-aux",
+ .codec_dai_name = "wm9713-aux",
.codec_name = "wm9713-codec",
.platform_name = "pxa-pcm-audio",
.ops = &mioa701_ops,
diff --git a/sound/soc/pxa/mmp-pcm.c b/sound/soc/pxa/mmp-pcm.c
index 96df9b2d8fc4..5b5f1a442891 100644
--- a/sound/soc/pxa/mmp-pcm.c
+++ b/sound/soc/pxa/mmp-pcm.c
@@ -166,7 +166,6 @@ static void mmp_pcm_free_dma_buffers(struct snd_pcm *pcm)
buf->area = NULL;
}
- return;
}
static int mmp_pcm_preallocate_dma_buffer(struct snd_pcm_substream *substream,
diff --git a/sound/soc/pxa/mmp-sspa.c b/sound/soc/pxa/mmp-sspa.c
index ca8b23f8c525..9cc35012e6e5 100644
--- a/sound/soc/pxa/mmp-sspa.c
+++ b/sound/soc/pxa/mmp-sspa.c
@@ -119,7 +119,6 @@ static void mmp_sspa_shutdown(struct snd_pcm_substream *substream,
clk_disable(priv->sspa->clk);
clk_disable(priv->sysclk);
- return;
}
/*
diff --git a/sound/soc/pxa/poodle.c b/sound/soc/pxa/poodle.c
index a879aba0691f..b6693f32fc02 100644
--- a/sound/soc/pxa/poodle.c
+++ b/sound/soc/pxa/poodle.c
@@ -129,7 +129,7 @@ static int poodle_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops poodle_ops = {
+static const struct snd_soc_ops poodle_ops = {
.startup = poodle_startup,
.hw_params = poodle_hw_params,
.shutdown = poodle_shutdown,
diff --git a/sound/soc/pxa/pxa-ssp.c b/sound/soc/pxa/pxa-ssp.c
index 3cad990dad2c..0291c7cb64eb 100644
--- a/sound/soc/pxa/pxa-ssp.c
+++ b/sound/soc/pxa/pxa-ssp.c
@@ -354,6 +354,7 @@ static int pxa_ssp_set_dai_pll(struct snd_soc_dai *cpu_dai, int pll_id,
if (ssp->type == PXA3xx_SSP) {
u32 val;
u64 tmp = 19968;
+
tmp *= 1000000;
do_div(tmp, freq_out);
val = tmp;
@@ -590,13 +591,13 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
if ((pxa_ssp_get_scr(ssp) == 4) && (width == 16)) {
/* This is a special case where the bitclk is 64fs
- * and we're not dealing with 2*32 bits of audio
- * samples.
- *
- * The SSP values used for that are all found out by
- * trying and failing a lot; some of the registers
- * needed for that mode are only available on PXA3xx.
- */
+ * and we're not dealing with 2*32 bits of audio
+ * samples.
+ *
+ * The SSP values used for that are all found out by
+ * trying and failing a lot; some of the registers
+ * needed for that mode are only available on PXA3xx.
+ */
if (ssp->type != PXA3xx_SSP)
return -EINVAL;
diff --git a/sound/soc/pxa/pxa2xx-ac97.c b/sound/soc/pxa/pxa2xx-ac97.c
index 2e2fb1838ec2..f49bf02e5ec2 100644
--- a/sound/soc/pxa/pxa2xx-ac97.c
+++ b/sound/soc/pxa/pxa2xx-ac97.c
@@ -140,9 +140,8 @@ static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream,
{
if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
return -ENODEV;
- else
- snd_soc_dai_set_dma_data(cpu_dai, substream,
- &pxa2xx_ac97_pcm_mic_mono_in);
+ snd_soc_dai_set_dma_data(cpu_dai, substream,
+ &pxa2xx_ac97_pcm_mic_mono_in);
return 0;
}
diff --git a/sound/soc/pxa/pxa2xx-i2s.c b/sound/soc/pxa/pxa2xx-i2s.c
index 0389cf7b4b1e..3fb60baf6eab 100644
--- a/sound/soc/pxa/pxa2xx-i2s.c
+++ b/sound/soc/pxa/pxa2xx-i2s.c
@@ -46,10 +46,10 @@
#define SACR0_STRF (1 << 5) /* FIFO Select for EFWR Special Function */
#define SACR0_EFWR (1 << 4) /* Enable EFWR Function */
#define SACR0_RST (1 << 3) /* FIFO, i2s Register Reset */
-#define SACR0_BCKD (1 << 2) /* Bit Clock Direction */
+#define SACR0_BCKD (1 << 2) /* Bit Clock Direction */
#define SACR0_ENB (1 << 0) /* Enable I2S Link */
#define SACR1_ENLBF (1 << 5) /* Enable Loopback */
-#define SACR1_DRPL (1 << 4) /* Disable Replaying Function */
+#define SACR1_DRPL (1 << 4) /* Disable Replaying Function */
#define SACR1_DREC (1 << 3) /* Disable Recording Function */
#define SACR1_AMSL (1 << 0) /* Specify Alternate Mode */
@@ -60,7 +60,7 @@
#define SASR0_TFS (1 << 3) /* Tx FIFO Service Request */
#define SASR0_BSY (1 << 2) /* I2S Busy */
#define SASR0_RNE (1 << 1) /* Rx FIFO Not Empty */
-#define SASR0_TNF (1 << 0) /* Tx FIFO Not Empty */
+#define SASR0_TNF (1 << 0) /* Tx FIFO Not Empty */
#define SAICR_ROR (1 << 6) /* Clear Rx FIFO Overrun Interrupt */
#define SAICR_TUR (1 << 5) /* Clear Tx FIFO Underrun Interrupt */
@@ -119,7 +119,7 @@ static int pxa_i2s_wait(void)
int i;
/* flush the Rx FIFO */
- for(i = 0; i < 16; i++)
+ for (i = 0; i < 16; i++)
SADR;
return 0;
}
diff --git a/sound/soc/pxa/pxa2xx-pcm.c b/sound/soc/pxa/pxa2xx-pcm.c
index 410d48b93031..b51d7a0755d5 100644
--- a/sound/soc/pxa/pxa2xx-pcm.c
+++ b/sound/soc/pxa/pxa2xx-pcm.c
@@ -85,7 +85,7 @@ static int pxa2xx_soc_pcm_new(struct snd_soc_pcm_runtime *rtd)
}
static struct snd_soc_platform_driver pxa2xx_soc_platform = {
- .ops = &pxa2xx_pcm_ops,
+ .ops = &pxa2xx_pcm_ops,
.pcm_new = pxa2xx_soc_pcm_new,
.pcm_free = pxa2xx_pcm_free_dma_buffers,
};
diff --git a/sound/soc/pxa/raumfeld.c b/sound/soc/pxa/raumfeld.c
index 552b763005ed..111a907c4eb9 100644
--- a/sound/soc/pxa/raumfeld.c
+++ b/sound/soc/pxa/raumfeld.c
@@ -132,7 +132,7 @@ static int raumfeld_cs4270_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops raumfeld_cs4270_ops = {
+static const struct snd_soc_ops raumfeld_cs4270_ops = {
.startup = raumfeld_cs4270_startup,
.shutdown = raumfeld_cs4270_shutdown,
.hw_params = raumfeld_cs4270_hw_params,
@@ -228,14 +228,12 @@ static struct snd_soc_ops raumfeld_ak4104_ops = {
.codec_name = "spi0.0", \
}
-static struct snd_soc_dai_link snd_soc_raumfeld_connector_dai[] =
-{
+static struct snd_soc_dai_link snd_soc_raumfeld_connector_dai[] = {
DAI_LINK_CS4270,
DAI_LINK_AK4104,
};
-static struct snd_soc_dai_link snd_soc_raumfeld_speaker_dai[] =
-{
+static struct snd_soc_dai_link snd_soc_raumfeld_speaker_dai[] = {
DAI_LINK_CS4270,
};
diff --git a/sound/soc/pxa/spitz.c b/sound/soc/pxa/spitz.c
index 07d77cddac60..1671da648e95 100644
--- a/sound/soc/pxa/spitz.c
+++ b/sound/soc/pxa/spitz.c
@@ -156,7 +156,7 @@ static int spitz_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops spitz_ops = {
+static const struct snd_soc_ops spitz_ops = {
.startup = spitz_startup,
.hw_params = spitz_hw_params,
};
@@ -230,8 +230,8 @@ static const struct snd_soc_dapm_route spitz_audio_map[] = {
{"Headset Jack", NULL, "ROUT1"},
/* ext speaker connected to LOUT2, ROUT2 */
- {"Ext Spk", NULL , "ROUT2"},
- {"Ext Spk", NULL , "LOUT2"},
+ {"Ext Spk", NULL, "ROUT2"},
+ {"Ext Spk", NULL, "LOUT2"},
/* mic is connected to input 1 - with bias */
{"LINPUT1", NULL, "Mic Bias"},
diff --git a/sound/soc/pxa/tosa.c b/sound/soc/pxa/tosa.c
index e022b2a777f6..ae9c12e1ea2a 100644
--- a/sound/soc/pxa/tosa.c
+++ b/sound/soc/pxa/tosa.c
@@ -85,7 +85,7 @@ static int tosa_startup(struct snd_pcm_substream *substream)
return 0;
}
-static struct snd_soc_ops tosa_ops = {
+static const struct snd_soc_ops tosa_ops = {
.startup = tosa_startup,
};
@@ -133,7 +133,7 @@ static int tosa_set_spk(struct snd_kcontrol *kcontrol,
static int tosa_hp_event(struct snd_soc_dapm_widget *w,
struct snd_kcontrol *k, int event)
{
- gpio_set_value(TOSA_GPIO_L_MUTE, SND_SOC_DAPM_EVENT_ON(event) ? 1 :0);
+ gpio_set_value(TOSA_GPIO_L_MUTE, SND_SOC_DAPM_EVENT_ON(event) ? 1 : 0);
return 0;
}
diff --git a/sound/soc/pxa/z2.c b/sound/soc/pxa/z2.c
index 990b1aa6d7f6..5b0eccd2b4dd 100644
--- a/sound/soc/pxa/z2.c
+++ b/sound/soc/pxa/z2.c
@@ -119,8 +119,8 @@ static const struct snd_soc_dapm_route z2_audio_map[] = {
{"Headphone Jack", NULL, "ROUT1"},
/* ext speaker connected to LOUT2, ROUT2 */
- {"Ext Spk", NULL , "ROUT2"},
- {"Ext Spk", NULL , "LOUT2"},
+ {"Ext Spk", NULL, "ROUT2"},
+ {"Ext Spk", NULL, "LOUT2"},
/* mic is connected to R input 2 - with bias */
{"RINPUT2", NULL, "Mic Bias"},
@@ -152,7 +152,7 @@ err:
return ret;
}
-static struct snd_soc_ops z2_ops = {
+static const struct snd_soc_ops z2_ops = {
.hw_params = z2_hw_params,
};
diff --git a/sound/soc/pxa/zylonite.c b/sound/soc/pxa/zylonite.c
index 6fbcdf02c88d..ba468e560dd2 100644
--- a/sound/soc/pxa/zylonite.c
+++ b/sound/soc/pxa/zylonite.c
@@ -132,7 +132,7 @@ static int zylonite_voice_hw_params(struct snd_pcm_substream *substream,
return 0;
}
-static struct snd_soc_ops zylonite_voice_ops = {
+static const struct snd_soc_ops zylonite_voice_ops = {
.hw_params = zylonite_voice_hw_params,
};
diff --git a/sound/soc/qcom/lpass-apq8016.c b/sound/soc/qcom/lpass-apq8016.c
index 8aed72be3224..8a74844d99e2 100644
--- a/sound/soc/qcom/lpass-apq8016.c
+++ b/sound/soc/qcom/lpass-apq8016.c
@@ -231,6 +231,18 @@ static struct lpass_variant apq8016_data = {
.wrdma_channels = 2,
.dai_driver = apq8016_lpass_cpu_dai_driver,
.num_dai = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),
+ .dai_osr_clk_names = (const char *[]) {
+ "mi2s-osr-clk0",
+ "mi2s-osr-clk1",
+ "mi2s-osr-clk2",
+ "mi2s-osr-clk3",
+ },
+ .dai_bit_clk_names = (const char *[]) {
+ "mi2s-bit-clk0",
+ "mi2s-bit-clk1",
+ "mi2s-bit-clk2",
+ "mi2s-bit-clk3",
+ },
.init = apq8016_lpass_init,
.exit = apq8016_lpass_exit,
.alloc_dma_channel = apq8016_lpass_alloc_dma_channel,
diff --git a/sound/soc/qcom/lpass-cpu.c b/sound/soc/qcom/lpass-cpu.c
index 5202a584e0c6..292b103abada 100644
--- a/sound/soc/qcom/lpass-cpu.c
+++ b/sound/soc/qcom/lpass-cpu.c
@@ -429,7 +429,6 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
struct lpass_variant *variant;
struct device *dev = &pdev->dev;
const struct of_device_id *match;
- char clk_name[16];
int ret, i, dai_id;
dsp_of_node = of_parse_phandle(pdev->dev.of_node, "qcom,adsp", 0);
@@ -477,31 +476,24 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
for (i = 0; i < variant->num_dai; i++) {
dai_id = variant->dai_driver[i].id;
- if (variant->num_dai > 1)
- sprintf(clk_name, "mi2s-osr-clk%d", i);
- else
- sprintf(clk_name, "mi2s-osr-clk");
-
drvdata->mi2s_osr_clk[dai_id] = devm_clk_get(&pdev->dev,
- clk_name);
+ variant->dai_osr_clk_names[i]);
if (IS_ERR(drvdata->mi2s_osr_clk[dai_id])) {
dev_warn(&pdev->dev,
- "error getting optional mi2s-osr-clk: %ld\n",
+ "%s() error getting optional %s: %ld\n",
+ __func__,
+ variant->dai_osr_clk_names[i],
PTR_ERR(drvdata->mi2s_osr_clk[dai_id]));
drvdata->mi2s_osr_clk[dai_id] = NULL;
}
- if (variant->num_dai > 1)
- sprintf(clk_name, "mi2s-bit-clk%d", i);
- else
- sprintf(clk_name, "mi2s-bit-clk");
-
drvdata->mi2s_bit_clk[dai_id] = devm_clk_get(&pdev->dev,
- clk_name);
+ variant->dai_bit_clk_names[i]);
if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
dev_err(&pdev->dev,
- "error getting mi2s-bit-clk: %ld\n",
+ "error getting %s: %ld\n",
+ variant->dai_bit_clk_names[i],
PTR_ERR(drvdata->mi2s_bit_clk[dai_id]));
return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
}
diff --git a/sound/soc/qcom/lpass-ipq806x.c b/sound/soc/qcom/lpass-ipq806x.c
index 608c1a92af8a..ca1e1f2d2787 100644
--- a/sound/soc/qcom/lpass-ipq806x.c
+++ b/sound/soc/qcom/lpass-ipq806x.c
@@ -92,6 +92,12 @@ static struct lpass_variant ipq806x_data = {
.wrdma_channels = 4,
.dai_driver = &ipq806x_lpass_cpu_dai_driver,
.num_dai = 1,
+ .dai_osr_clk_names = (const char *[]) {
+ "mi2s-osr-clk",
+ },
+ .dai_bit_clk_names = (const char *[]) {
+ "mi2s-bit-clk",
+ },
.alloc_dma_channel = ipq806x_lpass_alloc_dma_channel,
.free_dma_channel = ipq806x_lpass_free_dma_channel,
};
diff --git a/sound/soc/qcom/lpass.h b/sound/soc/qcom/lpass.h
index 9b031352ea3c..b848db2d6c3d 100644
--- a/sound/soc/qcom/lpass.h
+++ b/sound/soc/qcom/lpass.h
@@ -91,6 +91,8 @@ struct lpass_variant {
/* SOC specific dais */
struct snd_soc_dai_driver *dai_driver;
int num_dai;
+ const char * const *dai_osr_clk_names;
+ const char * const *dai_bit_clk_names;
};
/* register the platform driver from the CPU DAI driver */
diff --git a/sound/soc/rockchip/rk3288_hdmi_analog.c b/sound/soc/rockchip/rk3288_hdmi_analog.c
index b60abf322ce1..dbc53e48c52c 100644
--- a/sound/soc/rockchip/rk3288_hdmi_analog.c
+++ b/sound/soc/rockchip/rk3288_hdmi_analog.c
@@ -93,6 +93,9 @@ static int rk_hw_params(struct snd_pcm_substream *substream,
case 96000:
mclk = 12288000;
break;
+ case 192000:
+ mclk = 24576000;
+ break;
case 11025:
case 22050:
case 44100:
diff --git a/sound/soc/samsung/Kconfig b/sound/soc/samsung/Kconfig
index f1f1d7959a1b..0520f5afd7cc 100644
--- a/sound/soc/samsung/Kconfig
+++ b/sound/soc/samsung/Kconfig
@@ -185,6 +185,14 @@ 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_ODROID
+ tristate "Audio support for Odroid XU3/XU4"
+ depends on SND_SOC_SAMSUNG && I2C
+ select SND_SOC_MAX98090
+ select SND_SAMSUNG_I2S
+ help
+ Say Y here to enable audio support for the Odroid XU3/XU4.
+
config SND_SOC_ARNDALE_RT5631_ALC5631
tristate "Audio support for RT5631(ALC5631) on Arndale Board"
depends on I2C
diff --git a/sound/soc/samsung/Makefile b/sound/soc/samsung/Makefile
index b5df5e2e3d94..b6c2ee358333 100644
--- a/sound/soc/samsung/Makefile
+++ b/sound/soc/samsung/Makefile
@@ -40,6 +40,7 @@ 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-odroid-objs := odroid.o
snd-soc-arndale-rt5631-objs := arndale_rt5631.o
snd-soc-tm2-wm5110-objs := tm2_wm5110.o
@@ -62,5 +63,6 @@ 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_ODROID) += snd-soc-odroid.o
obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o
obj-$(CONFIG_SND_SOC_SAMSUNG_TM2_WM5110) += snd-soc-tm2-wm5110.o
diff --git a/sound/soc/samsung/bells.c b/sound/soc/samsung/bells.c
index 3dd246fa0059..34deba461ae1 100644
--- a/sound/soc/samsung/bells.c
+++ b/sound/soc/samsung/bells.c
@@ -446,7 +446,6 @@ static struct snd_soc_card bells_cards[] = {
},
};
-
static int bells_probe(struct platform_device *pdev)
{
int ret;
diff --git a/sound/soc/samsung/i2s-regs.h b/sound/soc/samsung/i2s-regs.h
index 9170c311d66e..fe6914005494 100644
--- a/sound/soc/samsung/i2s-regs.h
+++ b/sound/soc/samsung/i2s-regs.h
@@ -160,5 +160,3 @@
#define I2SSIZE_SHIFT (16)
#endif /* __SND_SOC_SAMSUNG_I2S_REGS_H */
-
-
diff --git a/sound/soc/samsung/i2s.c b/sound/soc/samsung/i2s.c
index 52a47ed292a4..af3ba4d4ccc5 100644
--- a/sound/soc/samsung/i2s.c
+++ b/sound/soc/samsung/i2s.c
@@ -1242,7 +1242,6 @@ static int samsung_i2s_probe(struct platform_device *pdev)
i2s_dai_data = (struct samsung_i2s_dai_data *)
platform_get_device_id(pdev)->driver_data;
-
pri_dai = i2s_alloc_dai(pdev, false);
if (!pri_dai) {
dev_err(&pdev->dev, "Unable to alloc I2S_pri\n");
diff --git a/sound/soc/samsung/odroid.c b/sound/soc/samsung/odroid.c
new file mode 100644
index 000000000000..0c0b00e40646
--- /dev/null
+++ b/sound/soc/samsung/odroid.c
@@ -0,0 +1,219 @@
+/*
+ * Copyright (C) 2017 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/clk.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include "i2s.h"
+#include "i2s-regs.h"
+
+struct odroid_priv {
+ struct snd_soc_card card;
+ struct snd_soc_dai_link dai_link;
+
+ struct clk *pll;
+ struct clk *rclk;
+};
+
+static int odroid_card_startup(struct snd_pcm_substream *substream)
+{
+ struct snd_pcm_runtime *runtime = substream->runtime;
+
+ snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+ return 0;
+}
+
+static int odroid_card_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct snd_soc_pcm_runtime *rtd = substream->private_data;
+ struct odroid_priv *priv = snd_soc_card_get_drvdata(rtd->card);
+ unsigned int pll_freq, rclk_freq;
+ int ret;
+
+ switch (params_rate(params)) {
+ case 32000:
+ case 64000:
+ pll_freq = 131072000U;
+ break;
+ case 44100:
+ case 88200:
+ case 176400:
+ pll_freq = 180633600U;
+ break;
+ case 48000:
+ case 96000:
+ case 192000:
+ pll_freq = 196608000U;
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ ret = clk_set_rate(priv->pll, pll_freq + 1);
+ if (ret < 0)
+ return ret;
+
+ rclk_freq = params_rate(params) * 256 * 4;
+
+ ret = clk_set_rate(priv->rclk, rclk_freq);
+ if (ret < 0)
+ return ret;
+
+ if (rtd->num_codecs > 1) {
+ struct snd_soc_dai *codec_dai = rtd->codec_dais[1];
+
+ ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk_freq,
+ SND_SOC_CLOCK_IN);
+ if (ret < 0)
+ return ret;
+ }
+
+ return 0;
+}
+
+static const struct snd_soc_ops odroid_card_ops = {
+ .startup = odroid_card_startup,
+ .hw_params = odroid_card_hw_params,
+};
+
+static void odroid_put_codec_of_nodes(struct snd_soc_dai_link *link)
+{
+ struct snd_soc_dai_link_component *component = link->codecs;
+ int i;
+
+ for (i = 0; i < link->num_codecs; i++, component++) {
+ if (!component->of_node)
+ break;
+ of_node_put(component->of_node);
+ }
+}
+
+static int odroid_audio_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct device_node *cpu, *codec;
+ struct odroid_priv *priv;
+ struct snd_soc_dai_link *link;
+ struct snd_soc_card *card;
+ int ret;
+
+ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+ if (!priv)
+ return -ENOMEM;
+
+ card = &priv->card;
+ card->dev = dev;
+
+ card->owner = THIS_MODULE;
+ card->fully_routed = true;
+
+ snd_soc_card_set_drvdata(card, priv);
+
+ priv->pll = devm_clk_get(dev, "epll");
+ if (IS_ERR(priv->pll))
+ return PTR_ERR(priv->pll);
+
+ priv->rclk = devm_clk_get(dev, "i2s_rclk");
+ if (IS_ERR(priv->rclk))
+ return PTR_ERR(priv->rclk);
+
+ ret = snd_soc_of_parse_card_name(card, "model");
+ if (ret < 0)
+ return ret;
+
+ if (of_property_read_bool(dev->of_node, "samsung,audio-widgets")) {
+ ret = snd_soc_of_parse_audio_simple_widgets(card,
+ "samsung,audio-widgets");
+ if (ret < 0)
+ return ret;
+ }
+
+ if (of_property_read_bool(dev->of_node, "samsung,audio-routing")) {
+ ret = snd_soc_of_parse_audio_routing(card,
+ "samsung,audio-routing");
+ if (ret < 0)
+ return ret;
+ }
+
+ link = &priv->dai_link;
+
+ link->ops = &odroid_card_ops;
+ link->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+ SND_SOC_DAIFMT_CBS_CFS;
+
+ card->dai_link = &priv->dai_link;
+ card->num_links = 1;
+
+ cpu = of_get_child_by_name(dev->of_node, "cpu");
+ codec = of_get_child_by_name(dev->of_node, "codec");
+
+ link->cpu_of_node = of_parse_phandle(cpu, "sound-dai", 0);
+ if (!link->cpu_of_node) {
+ dev_err(dev, "Failed parsing cpu/sound-dai property\n");
+ return -EINVAL;
+ }
+
+ ret = snd_soc_of_get_dai_link_codecs(dev, codec, link);
+ if (ret < 0)
+ goto err_put_codec_n;
+
+ link->platform_of_node = link->cpu_of_node;
+
+ link->name = "Primary";
+ link->stream_name = link->name;
+
+ ret = devm_snd_soc_register_card(dev, card);
+ if (ret < 0) {
+ dev_err(dev, "snd_soc_register_card() failed: %d\n", ret);
+ goto err_put_i2s_n;
+ }
+
+ return 0;
+
+err_put_i2s_n:
+ of_node_put(link->cpu_of_node);
+err_put_codec_n:
+ odroid_put_codec_of_nodes(link);
+ return ret;
+}
+
+static int odroid_audio_remove(struct platform_device *pdev)
+{
+ struct odroid_priv *priv = platform_get_drvdata(pdev);
+
+ of_node_put(priv->dai_link.cpu_of_node);
+ odroid_put_codec_of_nodes(&priv->dai_link);
+
+ return 0;
+}
+
+static const struct of_device_id odroid_audio_of_match[] = {
+ { .compatible = "samsung,odroid-xu3-audio" },
+ { .compatible = "samsung,odroid-xu4-audio"},
+ { },
+};
+MODULE_DEVICE_TABLE(of, odroid_audio_of_match);
+
+static struct platform_driver odroid_audio_driver = {
+ .driver = {
+ .name = "odroid-audio",
+ .of_match_table = odroid_audio_of_match,
+ .pm = &snd_soc_pm_ops,
+ },
+ .probe = odroid_audio_probe,
+ .remove = odroid_audio_remove,
+};
+module_platform_driver(odroid_audio_driver);
+
+MODULE_AUTHOR("Sylwester Nawrocki <s.nawrocki@samsung.com>");
+MODULE_DESCRIPTION("Odroid XU3/XU4 audio support");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/samsung/s3c-i2s-v2.c b/sound/soc/samsung/s3c-i2s-v2.c
index 644f186fd35c..8f42deaa184b 100644
--- a/sound/soc/samsung/s3c-i2s-v2.c
+++ b/sound/soc/samsung/s3c-i2s-v2.c
@@ -72,7 +72,6 @@ static inline void dbg_showcon(const char *fn, u32 con)
}
#endif
-
/* Turn on or off the transmission path. */
static void s3c2412_snd_txctrl(struct s3c_i2sv2_info *i2s, int on)
{
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
index 85a33ac0a5c4..66203d107a11 100644
--- a/sound/soc/sh/rcar/adg.c
+++ b/sound/soc/sh/rcar/adg.c
@@ -43,6 +43,7 @@ struct rsnd_adg {
};
#define LRCLK_ASYNC (1 << 0)
+#define AUDIO_OUT_48 (1 << 1)
#define adg_mode_flags(adg) (adg->flags)
#define for_each_rsnd_clk(pos, adg, i) \
@@ -364,7 +365,10 @@ found_clock:
rsnd_adg_set_ssi_clk(ssi_mod, data);
- if (!(adg_mode_flags(adg) & LRCLK_ASYNC)) {
+ if (adg_mode_flags(adg) & LRCLK_ASYNC) {
+ if (adg_mode_flags(adg) & AUDIO_OUT_48)
+ ckr = 0x80000000;
+ } else {
if (0 == (rate % 8000))
ckr = 0x80000000;
}
@@ -427,11 +431,14 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
struct clk *clk;
struct device *dev = rsnd_priv_to_dev(priv);
struct device_node *np = dev->of_node;
+ struct property *prop;
u32 ckr, rbgx, rbga, rbgb;
- u32 rate, req_rate = 0, div;
+ u32 rate, div;
+#define REQ_SIZE 2
+ u32 req_rate[REQ_SIZE] = {};
uint32_t count = 0;
unsigned long req_48kHz_rate, req_441kHz_rate;
- int i;
+ int i, req_size;
const char *parent_clk_name = NULL;
static const char * const clkout_name[] = {
[CLKOUT] = "audio_clkout",
@@ -446,19 +453,32 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
[CLKI] = 0x2,
};
- of_property_read_u32(np, "#clock-cells", &count);
+ ckr = 0;
+ rbga = 2; /* default 1/6 */
+ rbgb = 2; /* default 1/6 */
/*
* ADG supports BRRA/BRRB output only
* this means all clkout0/1/2/3 will be same rate
*/
- of_property_read_u32(np, "clock-frequency", &req_rate);
+ prop = of_find_property(np, "clock-frequency", NULL);
+ if (!prop)
+ goto rsnd_adg_get_clkout_end;
+
+ req_size = prop->length / sizeof(u32);
+
+ of_property_read_u32_array(np, "clock-frequency", req_rate, req_size);
req_48kHz_rate = 0;
req_441kHz_rate = 0;
- if (0 == (req_rate % 44100))
- req_441kHz_rate = req_rate;
- if (0 == (req_rate % 48000))
- req_48kHz_rate = req_rate;
+ for (i = 0; i < req_size; i++) {
+ if (0 == (req_rate[i] % 44100))
+ req_441kHz_rate = req_rate[i];
+ if (0 == (req_rate[i] % 48000))
+ req_48kHz_rate = req_rate[i];
+ }
+
+ if (req_rate[0] % 48000 == 0)
+ adg->flags = AUDIO_OUT_48;
/*
* This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
@@ -469,9 +489,6 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
* rsnd_adg_ssi_clk_try_start()
* rsnd_ssi_master_clk_start()
*/
- ckr = 0;
- rbga = 2; /* default 1/6 */
- rbgb = 2; /* default 1/6 */
adg->rbga_rate_for_441khz = 0;
adg->rbgb_rate_for_48khz = 0;
for_each_rsnd_clk(clk, adg, i) {
@@ -505,10 +522,8 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
rbgb = rbgx;
adg->rbgb_rate_for_48khz = rate / div;
ckr |= brg_table[i] << 16;
- if (req_48kHz_rate) {
+ if (req_48kHz_rate)
parent_clk_name = __clk_get_name(clk);
- ckr |= 0x80000000;
- }
}
}
}
@@ -518,12 +533,13 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
* this means all clkout0/1/2/3 will be * same rate
*/
+ of_property_read_u32(np, "#clock-cells", &count);
/*
* for clkout
*/
if (!count) {
clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
- parent_clk_name, 0, req_rate);
+ parent_clk_name, 0, req_rate[0]);
if (!IS_ERR(clk)) {
adg->clkout[CLKOUT] = clk;
of_clk_add_provider(np, of_clk_src_simple_get, clk);
@@ -536,19 +552,18 @@ static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
for (i = 0; i < CLKOUTMAX; i++) {
clk = clk_register_fixed_rate(dev, clkout_name[i],
parent_clk_name, 0,
- req_rate);
- if (!IS_ERR(clk)) {
- adg->onecell.clks = adg->clkout;
- adg->onecell.clk_num = CLKOUTMAX;
-
+ req_rate[0]);
+ adg->clkout[i] = ERR_PTR(-ENOENT);
+ if (!IS_ERR(clk))
adg->clkout[i] = clk;
-
- of_clk_add_provider(np, of_clk_src_onecell_get,
- &adg->onecell);
- }
}
+ adg->onecell.clks = adg->clkout;
+ adg->onecell.clk_num = CLKOUTMAX;
+ of_clk_add_provider(np, of_clk_src_onecell_get,
+ &adg->onecell);
}
+rsnd_adg_get_clkout_end:
adg->ckr = ckr;
adg->rbga = rbga;
adg->rbgb = rbgb;
@@ -564,6 +579,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;
+ int ret;
adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
if (!adg) {
@@ -571,8 +587,10 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
return -ENOMEM;
}
- rsnd_mod_init(priv, &adg->mod, &adg_ops,
+ ret = rsnd_mod_init(priv, &adg->mod, &adg_ops,
NULL, NULL, 0, 0);
+ if (ret)
+ return ret;
rsnd_adg_get_clkin(priv, adg);
rsnd_adg_get_clkout(priv, adg);
@@ -589,5 +607,10 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
void rsnd_adg_remove(struct rsnd_priv *priv)
{
+ struct device *dev = rsnd_priv_to_dev(priv);
+ struct device_node *np = dev->of_node;
+
+ of_clk_del_provider(np);
+
rsnd_adg_clk_disable(priv);
}
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
index 47b370cb2d3b..1744015408c3 100644
--- a/sound/soc/sh/rcar/core.c
+++ b/sound/soc/sh/rcar/core.c
@@ -96,7 +96,7 @@
#include <linux/pm_runtime.h>
#include "rsnd.h"
-#define RSND_RATES SNDRV_PCM_RATE_8000_96000
+#define RSND_RATES SNDRV_PCM_RATE_8000_192000
#define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
static const struct of_device_id rsnd_of_match[] = {
@@ -110,7 +110,6 @@ 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) {
@@ -121,7 +120,6 @@ 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)
{
@@ -674,12 +672,10 @@ static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
/* set clock inversion */
switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
case SND_SOC_DAIFMT_NB_IF:
- rdai->bit_clk_inv = rdai->bit_clk_inv;
rdai->frm_clk_inv = !rdai->frm_clk_inv;
break;
case SND_SOC_DAIFMT_IB_NF:
rdai->bit_clk_inv = !rdai->bit_clk_inv;
- rdai->frm_clk_inv = rdai->frm_clk_inv;
break;
case SND_SOC_DAIFMT_IB_IF:
rdai->bit_clk_inv = !rdai->bit_clk_inv;
@@ -1002,13 +998,30 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
return change;
}
-static int __rsnd_kctrl_new(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- struct rsnd_kctrl_cfg *cfg,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod))
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg)
+{
+ cfg->cfg.val = cfg->val;
+
+ return &cfg->cfg;
+}
+
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg)
+{
+ cfg->cfg.val = &cfg->val;
+
+ return &cfg->cfg;
+}
+
+int rsnd_kctrl_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_soc_pcm_runtime *rtd,
+ const unsigned char *name,
+ void (*update)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod),
+ struct rsnd_kctrl_cfg *cfg,
+ const char * const *texts,
+ int size,
+ u32 max)
{
struct snd_card *card = rtd->card->snd_card;
struct snd_kcontrol *kctrl;
@@ -1023,6 +1036,9 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
};
int ret;
+ if (size > RSND_MAX_CHANNELS)
+ return -EINVAL;
+
kctrl = snd_ctl_new1(&knew, mod);
if (!kctrl)
return -ENOMEM;
@@ -1031,74 +1047,17 @@ static int __rsnd_kctrl_new(struct rsnd_mod *mod,
if (ret < 0)
return ret;
- cfg->update = update;
- cfg->card = card;
- cfg->kctrl = kctrl;
- cfg->io = io;
+ cfg->texts = texts;
+ cfg->max = max;
+ cfg->size = size;
+ cfg->update = update;
+ cfg->card = card;
+ cfg->kctrl = kctrl;
+ cfg->io = io;
return 0;
}
-void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg)
-{
- if (cfg->card && cfg->kctrl)
- snd_ctl_remove(cfg->card, cfg->kctrl);
-
- cfg->card = NULL;
- cfg->kctrl = NULL;
-}
-
-int rsnd_kctrl_new_m(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod),
- struct rsnd_kctrl_cfg_m *_cfg,
- int ch_size,
- u32 max)
-{
- if (ch_size > RSND_MAX_CHANNELS)
- return -EINVAL;
-
- _cfg->cfg.max = max;
- _cfg->cfg.size = ch_size;
- _cfg->cfg.val = _cfg->val;
- return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
-}
-
-int rsnd_kctrl_new_s(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod),
- struct rsnd_kctrl_cfg_s *_cfg,
- u32 max)
-{
- _cfg->cfg.max = max;
- _cfg->cfg.size = 1;
- _cfg->cfg.val = &_cfg->val;
- return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
-}
-
-int rsnd_kctrl_new_e(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- struct rsnd_kctrl_cfg_s *_cfg,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod),
- const char * const *texts,
- u32 max)
-{
- _cfg->cfg.max = max;
- _cfg->cfg.size = 1;
- _cfg->cfg.val = &_cfg->val;
- _cfg->cfg.texts = texts;
- return __rsnd_kctrl_new(mod, io, rtd, name, &_cfg->cfg, update);
-}
-
/*
* snd_soc_platform
*/
diff --git a/sound/soc/sh/rcar/dvc.c b/sound/soc/sh/rcar/dvc.c
index cf8f59cdd8d7..463de8360985 100644
--- a/sound/soc/sh/rcar/dvc.c
+++ b/sound/soc/sh/rcar/dvc.c
@@ -218,21 +218,6 @@ static int rsnd_dvc_probe_(struct rsnd_mod *mod,
return rsnd_cmd_attach(io, rsnd_mod_id(mod));
}
-static int rsnd_dvc_remove_(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct rsnd_priv *priv)
-{
- struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
-
- rsnd_kctrl_remove(dvc->volume);
- rsnd_kctrl_remove(dvc->mute);
- rsnd_kctrl_remove(dvc->ren);
- rsnd_kctrl_remove(dvc->rup);
- rsnd_kctrl_remove(dvc->rdown);
-
- return 0;
-}
-
static int rsnd_dvc_init(struct rsnd_mod *mod,
struct rsnd_dai_stream *io,
struct rsnd_priv *priv)
@@ -300,18 +285,18 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
ret = rsnd_kctrl_new_e(mod, io, rtd,
is_play ?
"DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
- &dvc->rup,
rsnd_dvc_volume_update,
- dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+ &dvc->rup,
+ dvc_ramp_rate);
if (ret < 0)
return ret;
ret = rsnd_kctrl_new_e(mod, io, rtd,
is_play ?
"DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
- &dvc->rdown,
rsnd_dvc_volume_update,
- dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+ &dvc->rdown,
+ dvc_ramp_rate);
if (ret < 0)
return ret;
@@ -332,7 +317,6 @@ static struct rsnd_mod_ops rsnd_dvc_ops = {
.name = DVC_NAME,
.dma_req = rsnd_dvc_dma_req,
.probe = rsnd_dvc_probe_,
- .remove = rsnd_dvc_remove_,
.init = rsnd_dvc_init,
.quit = rsnd_dvc_quit,
.pcm_new = rsnd_dvc_pcm_new,
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
index 7410ec0174db..dbf4163427e8 100644
--- a/sound/soc/sh/rcar/rsnd.h
+++ b/sound/soc/sh/rcar/rsnd.h
@@ -611,35 +611,30 @@ struct rsnd_kctrl_cfg_s {
u32 val;
};
-void _rsnd_kctrl_remove(struct rsnd_kctrl_cfg *cfg);
-#define rsnd_kctrl_remove(_cfg) _rsnd_kctrl_remove(&((_cfg).cfg))
-
-int rsnd_kctrl_new_m(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod),
- struct rsnd_kctrl_cfg_m *_cfg,
- int ch_size,
- u32 max);
-int rsnd_kctrl_new_s(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod),
- struct rsnd_kctrl_cfg_s *_cfg,
- u32 max);
-int rsnd_kctrl_new_e(struct rsnd_mod *mod,
- struct rsnd_dai_stream *io,
- struct snd_soc_pcm_runtime *rtd,
- const unsigned char *name,
- struct rsnd_kctrl_cfg_s *_cfg,
- void (*update)(struct rsnd_dai_stream *io,
- struct rsnd_mod *mod),
- const char * const *texts,
- u32 max);
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_m(struct rsnd_kctrl_cfg_m *cfg);
+struct rsnd_kctrl_cfg *rsnd_kctrl_init_s(struct rsnd_kctrl_cfg_s *cfg);
+int rsnd_kctrl_new(struct rsnd_mod *mod,
+ struct rsnd_dai_stream *io,
+ struct snd_soc_pcm_runtime *rtd,
+ const unsigned char *name,
+ void (*update)(struct rsnd_dai_stream *io,
+ struct rsnd_mod *mod),
+ struct rsnd_kctrl_cfg *cfg,
+ const char * const *texts,
+ int size,
+ u32 max);
+
+#define rsnd_kctrl_new_m(mod, io, rtd, name, update, cfg, size, max) \
+ rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_m(cfg), \
+ NULL, size, max)
+
+#define rsnd_kctrl_new_s(mod, io, rtd, name, update, cfg, max) \
+ rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_s(cfg), \
+ NULL, 1, max)
+
+#define rsnd_kctrl_new_e(mod, io, rtd, name, update, cfg, texts) \
+ rsnd_kctrl_new(mod, io, rtd, name, update, rsnd_kctrl_init_s(cfg), \
+ texts, 1, ARRAY_SIZE(texts))
/*
* R-Car SSI
@@ -732,8 +727,8 @@ void rsnd_cmd_remove(struct rsnd_priv *priv);
int rsnd_cmd_attach(struct rsnd_dai_stream *io, int id);
struct rsnd_mod *rsnd_cmd_mod_get(struct rsnd_priv *priv, int id);
-#ifdef DEBUG
void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
+#ifdef DEBUG
#define rsnd_mod_confirm_ssi(mssi) rsnd_mod_make_sure(mssi, RSND_MOD_SSI)
#define rsnd_mod_confirm_src(msrc) rsnd_mod_make_sure(msrc, RSND_MOD_SRC)
#define rsnd_mod_confirm_dvc(mdvc) rsnd_mod_make_sure(mdvc, RSND_MOD_DVC)
diff --git a/sound/soc/sh/rcar/src.c b/sound/soc/sh/rcar/src.c
index 42db48db09ba..20b5b2ec625e 100644
--- a/sound/soc/sh/rcar/src.c
+++ b/sound/soc/sh/rcar/src.c
@@ -167,6 +167,7 @@ static int rsnd_src_hw_params(struct rsnd_mod *mod,
* dpcm_fe_dai_hw_params()
* dpcm_be_dai_hw_params()
*/
+ src->convert_rate = 0;
if (fe->dai_link->dynamic) {
int stream = substream->stream;
struct snd_soc_dpcm *dpcm;
@@ -414,8 +415,6 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
rsnd_mod_power_off(mod);
- src->convert_rate = 0;
-
/* reset sync convert_rate */
src->sync.val = 0;
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
index 411bda2387ad..135c5669f796 100644
--- a/sound/soc/sh/rcar/ssi.c
+++ b/sound/soc/sh/rcar/ssi.c
@@ -228,6 +228,15 @@ static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
/*
+ * It will set SSIWSR.CONT here, but SSICR.CKDV = 000
+ * with it is not allowed. (SSIWSR.WS_MODE with
+ * SSICR.CKDV = 000 is not allowed either).
+ * Skip it. See SSICR.CKDV
+ */
+ if (j == 0)
+ continue;
+
+ /*
* this driver is assuming that
* system word is 32bit x chan
* see rsnd_ssi_init()
diff --git a/sound/soc/sirf/sirf-audio-port.c b/sound/soc/sirf/sirf-audio-port.c
index 3f2cce03275c..be066de74aaa 100644
--- a/sound/soc/sirf/sirf-audio-port.c
+++ b/sound/soc/sirf/sirf-audio-port.c
@@ -19,6 +19,7 @@ struct sirf_audio_port {
static int sirf_audio_port_dai_probe(struct snd_soc_dai *dai)
{
struct sirf_audio_port *port = snd_soc_dai_get_drvdata(dai);
+
snd_soc_dai_init_dma_data(dai, &port->playback_dma_data,
&port->capture_dma_data);
return 0;
diff --git a/sound/soc/sirf/sirf-audio.c b/sound/soc/sirf/sirf-audio.c
index 94ea152e0362..f2bc50790f76 100644
--- a/sound/soc/sirf/sirf-audio.c
+++ b/sound/soc/sirf/sirf-audio.c
@@ -27,6 +27,7 @@ static int sirf_audio_hp_event(struct snd_soc_dapm_widget *w,
struct snd_soc_card *card = dapm->card;
struct sirf_audio_card *sirf_audio_card = snd_soc_card_get_drvdata(card);
int on = !SND_SOC_DAPM_EVENT_OFF(event);
+
if (gpio_is_valid(sirf_audio_card->gpio_hp_pa))
gpio_set_value(sirf_audio_card->gpio_hp_pa, on);
return 0;
diff --git a/sound/soc/sirf/sirf-usp.c b/sound/soc/sirf/sirf-usp.c
index 45fc06c0e0e5..77e7dcf969d0 100644
--- a/sound/soc/sirf/sirf-usp.c
+++ b/sound/soc/sirf/sirf-usp.c
@@ -71,6 +71,7 @@ static void sirf_usp_rx_disable(struct sirf_usp *usp)
static int sirf_usp_pcm_dai_probe(struct snd_soc_dai *dai)
{
struct sirf_usp *usp = snd_soc_dai_get_drvdata(dai);
+
snd_soc_dai_init_dma_data(dai, &usp->playback_dma_data,
&usp->capture_dma_data);
return 0;
@@ -294,6 +295,7 @@ static struct snd_soc_dai_driver sirf_usp_pcm_dai = {
static int sirf_usp_pcm_runtime_suspend(struct device *dev)
{
struct sirf_usp *usp = dev_get_drvdata(dev);
+
clk_disable_unprepare(usp->clk);
return 0;
}
@@ -302,6 +304,7 @@ static int sirf_usp_pcm_runtime_resume(struct device *dev)
{
struct sirf_usp *usp = dev_get_drvdata(dev);
int ret;
+
ret = clk_prepare_enable(usp->clk);
if (ret) {
dev_err(dev, "clk_enable failed: %d\n", ret);
diff --git a/sound/soc/soc-core.c b/sound/soc/soc-core.c
index 2722bb0c5573..aae099c0e502 100644
--- a/sound/soc/soc-core.c
+++ b/sound/soc/soc-core.c
@@ -936,7 +936,7 @@ static struct snd_soc_component *soc_find_component(
*
* @dlc: name of the DAI and optional component info to match
*
- * This function will search all regsitered components and their DAIs to
+ * This function will search all registered components and their DAIs to
* find the DAI of the same name. The component's of_node and name
* should also match if being specified.
*
@@ -1776,7 +1776,6 @@ static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
}
component->init = aux_dev->init;
- component->auxiliary = 1;
list_add(&component->card_aux_list, &card->aux_comp_list);
return 0;
@@ -1788,14 +1787,13 @@ err_defer:
static int soc_probe_aux_devices(struct snd_soc_card *card)
{
- struct snd_soc_component *comp, *tmp;
+ struct snd_soc_component *comp;
int order;
int ret;
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
- list_for_each_entry_safe(comp, tmp, &card->aux_comp_list,
- card_aux_list) {
+ list_for_each_entry(comp, &card->aux_comp_list, card_aux_list) {
if (comp->driver->probe_order == order) {
ret = soc_probe_component(card, comp);
if (ret < 0) {
@@ -1804,7 +1802,6 @@ static int soc_probe_aux_devices(struct snd_soc_card *card)
comp->name, ret);
return ret;
}
- list_del(&comp->card_aux_list);
}
}
}
@@ -1820,14 +1817,12 @@ static void soc_remove_aux_devices(struct snd_soc_card *card)
for (order = SND_SOC_COMP_ORDER_FIRST; order <= SND_SOC_COMP_ORDER_LAST;
order++) {
list_for_each_entry_safe(comp, _comp,
- &card->component_dev_list, card_list) {
-
- if (!comp->auxiliary)
- continue;
+ &card->aux_comp_list, card_aux_list) {
if (comp->driver->remove_order == order) {
soc_remove_component(comp);
- comp->auxiliary = 0;
+ /* remove it from the card's aux_comp_list */
+ list_del(&comp->card_aux_list);
}
}
}
@@ -1918,6 +1913,7 @@ int snd_soc_runtime_set_dai_fmt(struct snd_soc_pcm_runtime *rtd,
EXPORT_SYMBOL_GPL(snd_soc_runtime_set_dai_fmt);
+#ifdef CONFIG_DMI
/* Trim special characters, and replace '-' with '_' since '-' is used to
* separate different DMI fields in the card long name. Only number and
* alphabet characters and a few separator characters are kept.
@@ -2049,6 +2045,7 @@ int snd_soc_set_dmi_name(struct snd_soc_card *card, const char *flavour)
return 0;
}
EXPORT_SYMBOL_GPL(snd_soc_set_dmi_name);
+#endif /* CONFIG_DMI */
static int snd_soc_instantiate_card(struct snd_soc_card *card)
{
@@ -2190,6 +2187,9 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
snd_soc_dapm_add_routes(&card->dapm, card->of_dapm_routes,
card->num_of_dapm_routes);
+ /* try to set some sane longname if DMI is available */
+ snd_soc_set_dmi_name(card, NULL);
+
snprintf(card->snd_card->shortname, sizeof(card->snd_card->shortname),
"%s", card->name);
snprintf(card->snd_card->longname, sizeof(card->snd_card->longname),
@@ -3139,7 +3139,7 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
component->suspend = component->driver->suspend;
component->resume = component->driver->resume;
component->pcm_new = component->driver->pcm_new;
- component->pcm_free= component->driver->pcm_free;
+ component->pcm_free = component->driver->pcm_free;
dapm = &component->dapm;
dapm->dev = dev;
@@ -3240,6 +3240,11 @@ static void snd_soc_component_cleanup(struct snd_soc_component *component)
static void snd_soc_component_del_unlocked(struct snd_soc_component *component)
{
+ struct snd_soc_card *card = component->card;
+
+ if (card)
+ snd_soc_unregister_card(card);
+
list_del(&component->list);
}
diff --git a/sound/soc/soc-jack.c b/sound/soc/soc-jack.c
index fbaa1bb41102..7daf21fee355 100644
--- a/sound/soc/soc-jack.c
+++ b/sound/soc/soc-jack.c
@@ -19,9 +19,28 @@
#include <linux/workqueue.h>
#include <linux/delay.h>
#include <linux/export.h>
+#include <linux/suspend.h>
#include <trace/events/asoc.h>
/**
+ * snd_soc_codec_set_jack - configure codec jack.
+ * @codec: CODEC
+ * @jack: structure to use for the jack
+ * @data: can be used if codec driver need extra data for configuring jack
+ *
+ * Configures and enables jack detection function.
+ */
+int snd_soc_codec_set_jack(struct snd_soc_codec *codec,
+ struct snd_soc_jack *jack, void *data)
+{
+ if (codec->driver->set_jack)
+ return codec->driver->set_jack(codec, jack, data);
+ else
+ return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_codec_set_jack);
+
+/**
* snd_soc_card_jack_new - Create a new jack
* @card: ASoC card
* @id: an identifying string for this jack
@@ -293,6 +312,27 @@ static void gpio_work(struct work_struct *work)
snd_soc_jack_gpio_detect(gpio);
}
+static int snd_soc_jack_pm_notifier(struct notifier_block *nb,
+ unsigned long action, void *data)
+{
+ struct snd_soc_jack_gpio *gpio =
+ container_of(nb, struct snd_soc_jack_gpio, pm_notifier);
+
+ switch (action) {
+ case PM_POST_SUSPEND:
+ case PM_POST_HIBERNATION:
+ case PM_POST_RESTORE:
+ /*
+ * Use workqueue so we do not have to care about running
+ * concurrently with work triggered by the interrupt handler.
+ */
+ queue_delayed_work(system_power_efficient_wq, &gpio->work, 0);
+ break;
+ }
+
+ return NOTIFY_DONE;
+}
+
/**
* snd_soc_jack_add_gpios - Associate GPIO pins with an ASoC jack
*
@@ -369,6 +409,13 @@ got_gpio:
i, ret);
}
+ /*
+ * Register PM notifier so we do not miss state transitions
+ * happening while system is asleep.
+ */
+ gpios[i].pm_notifier.notifier_call = snd_soc_jack_pm_notifier;
+ register_pm_notifier(&gpios[i].pm_notifier);
+
/* Expose GPIO value over sysfs for diagnostic purposes */
gpiod_export(gpios[i].desc, false);
@@ -428,6 +475,7 @@ void snd_soc_jack_free_gpios(struct snd_soc_jack *jack, int count,
for (i = 0; i < count; i++) {
gpiod_unexport(gpios[i].desc);
+ unregister_pm_notifier(&gpios[i].pm_notifier);
free_irq(gpiod_to_irq(gpios[i].desc), &gpios[i]);
cancel_delayed_work_sync(&gpios[i].work);
gpiod_put(gpios[i].desc);
diff --git a/sound/soc/soc-topology.c b/sound/soc/soc-topology.c
index 3e9b1c0bb1ce..002772e3ba2c 100644
--- a/sound/soc/soc-topology.c
+++ b/sound/soc/soc-topology.c
@@ -495,12 +495,13 @@ static void remove_widget(struct snd_soc_component *comp,
struct snd_kcontrol *kcontrol = w->kcontrols[i];
struct soc_enum *se =
(struct soc_enum *)kcontrol->private_value;
+ int j;
snd_ctl_remove(card, kcontrol);
kfree(se->dobj.control.dvalues);
- for (i = 0; i < se->items; i++)
- kfree(se->dobj.control.dtexts[i]);
+ for (j = 0; j < se->items; j++)
+ kfree(se->dobj.control.dtexts[j]);
kfree(se);
}
@@ -933,6 +934,7 @@ static int soc_tplg_denum_create_texts(struct soc_enum *se,
}
}
+ se->texts = (const char * const *)se->dobj.control.dtexts;
return 0;
err:
diff --git a/sound/soc/sti/uniperif.h b/sound/soc/sti/uniperif.h
index d487dd2ef016..cfcb0ea9d99d 100644
--- a/sound/soc/sti/uniperif.h
+++ b/sound/soc/sti/uniperif.h
@@ -1299,6 +1299,7 @@ struct uniperif {
int ver; /* IP version, used by register access macros */
struct regmap_field *clk_sel;
struct regmap_field *valid_sel;
+ spinlock_t irq_lock; /* use to prevent race condition with IRQ */
/* capabilities */
const struct snd_pcm_hardware *hw;
diff --git a/sound/soc/sti/uniperif_player.c b/sound/soc/sti/uniperif_player.c
index 60ae31a303ab..d8b6936e544e 100644
--- a/sound/soc/sti/uniperif_player.c
+++ b/sound/soc/sti/uniperif_player.c
@@ -65,10 +65,13 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
unsigned int status;
unsigned int tmp;
- if (player->state == UNIPERIF_STATE_STOPPED) {
- /* Unexpected IRQ: do nothing */
- return IRQ_NONE;
- }
+ spin_lock(&player->irq_lock);
+ if (!player->substream)
+ goto irq_spin_unlock;
+
+ snd_pcm_stream_lock(player->substream);
+ if (player->state == UNIPERIF_STATE_STOPPED)
+ goto stream_unlock;
/* Get interrupt status & clear them immediately */
status = GET_UNIPERIF_ITS(player);
@@ -88,9 +91,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
SET_UNIPERIF_ITM_BCLR_FIFO_ERROR(player);
/* Stop the player */
- snd_pcm_stream_lock(player->substream);
snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock(player->substream);
}
ret = IRQ_HANDLED;
@@ -104,9 +105,7 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
SET_UNIPERIF_ITM_BCLR_DMA_ERROR(player);
/* Stop the player */
- snd_pcm_stream_lock(player->substream);
snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock(player->substream);
ret = IRQ_HANDLED;
}
@@ -116,7 +115,8 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
if (!player->underflow_enabled) {
dev_err(player->dev,
"unexpected Underflow recovering\n");
- return -EPERM;
+ ret = -EPERM;
+ goto stream_unlock;
}
/* Read the underflow recovery duration */
tmp = GET_UNIPERIF_STATUS_1_UNDERFLOW_DURATION(player);
@@ -138,13 +138,16 @@ static irqreturn_t uni_player_irq_handler(int irq, void *dev_id)
dev_err(player->dev, "Underflow recovery failed\n");
/* Stop the player */
- snd_pcm_stream_lock(player->substream);
snd_pcm_stop(player->substream, SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock(player->substream);
ret = IRQ_HANDLED;
}
+stream_unlock:
+ snd_pcm_stream_unlock(player->substream);
+irq_spin_unlock:
+ spin_unlock(&player->irq_lock);
+
return ret;
}
@@ -588,6 +591,7 @@ static int uni_player_ctl_iec958_put(struct snd_kcontrol *kcontrol,
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
struct uniperif *player = priv->dai_data.uni;
struct snd_aes_iec958 *iec958 = &player->stream_settings.iec958;
+ unsigned long flags;
mutex_lock(&player->ctrl_lock);
iec958->status[0] = ucontrol->value.iec958.status[0];
@@ -596,12 +600,14 @@ static int uni_player_ctl_iec958_put(struct snd_kcontrol *kcontrol,
iec958->status[3] = ucontrol->value.iec958.status[3];
mutex_unlock(&player->ctrl_lock);
+ spin_lock_irqsave(&player->irq_lock, flags);
if (player->substream && player->substream->runtime)
uni_player_set_channel_status(player,
player->substream->runtime);
else
uni_player_set_channel_status(player, NULL);
+ spin_unlock_irqrestore(&player->irq_lock, flags);
return 0;
}
@@ -686,9 +692,12 @@ static int uni_player_startup(struct snd_pcm_substream *substream,
{
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
struct uniperif *player = priv->dai_data.uni;
+ unsigned long flags;
int ret;
+ spin_lock_irqsave(&player->irq_lock, flags);
player->substream = substream;
+ spin_unlock_irqrestore(&player->irq_lock, flags);
player->clk_adj = 0;
@@ -986,12 +995,15 @@ static void uni_player_shutdown(struct snd_pcm_substream *substream,
{
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
struct uniperif *player = priv->dai_data.uni;
+ unsigned long flags;
+ spin_lock_irqsave(&player->irq_lock, flags);
if (player->state != UNIPERIF_STATE_STOPPED)
/* Stop the player */
uni_player_stop(player);
player->substream = NULL;
+ spin_unlock_irqrestore(&player->irq_lock, flags);
}
static int uni_player_parse_dt_audio_glue(struct platform_device *pdev,
@@ -1062,7 +1074,7 @@ int uni_player_init(struct platform_device *pdev,
player->clk = of_clk_get(pdev->dev.of_node, 0);
if (IS_ERR(player->clk)) {
dev_err(player->dev, "Failed to get clock\n");
- ret = PTR_ERR(player->clk);
+ return PTR_ERR(player->clk);
}
/* Select the frequency synthesizer clock */
@@ -1096,6 +1108,7 @@ int uni_player_init(struct platform_device *pdev,
}
mutex_init(&player->ctrl_lock);
+ spin_lock_init(&player->irq_lock);
/* Ensure that disabled by default */
SET_UNIPERIF_CONFIG_BACK_STALL_REQ_DISABLE(player);
diff --git a/sound/soc/sti/uniperif_reader.c b/sound/soc/sti/uniperif_reader.c
index 93a8df6ed880..ee0055e60852 100644
--- a/sound/soc/sti/uniperif_reader.c
+++ b/sound/soc/sti/uniperif_reader.c
@@ -46,10 +46,15 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id)
struct uniperif *reader = dev_id;
unsigned int status;
+ spin_lock(&reader->irq_lock);
+ if (!reader->substream)
+ goto irq_spin_unlock;
+
+ snd_pcm_stream_lock(reader->substream);
if (reader->state == UNIPERIF_STATE_STOPPED) {
/* Unexpected IRQ: do nothing */
dev_warn(reader->dev, "unexpected IRQ\n");
- return IRQ_HANDLED;
+ goto stream_unlock;
}
/* Get interrupt status & clear them immediately */
@@ -60,13 +65,16 @@ static irqreturn_t uni_reader_irq_handler(int irq, void *dev_id)
if (unlikely(status & UNIPERIF_ITS_FIFO_ERROR_MASK(reader))) {
dev_err(reader->dev, "FIFO error detected\n");
- snd_pcm_stream_lock(reader->substream);
snd_pcm_stop(reader->substream, SNDRV_PCM_STATE_XRUN);
- snd_pcm_stream_unlock(reader->substream);
- return IRQ_HANDLED;
+ ret = IRQ_HANDLED;
}
+stream_unlock:
+ snd_pcm_stream_unlock(reader->substream);
+irq_spin_unlock:
+ spin_unlock(&reader->irq_lock);
+
return ret;
}
@@ -347,9 +355,12 @@ static int uni_reader_startup(struct snd_pcm_substream *substream,
{
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
struct uniperif *reader = priv->dai_data.uni;
+ unsigned long flags;
int ret;
+ spin_lock_irqsave(&reader->irq_lock, flags);
reader->substream = substream;
+ spin_unlock_irqrestore(&reader->irq_lock, flags);
if (!UNIPERIF_TYPE_IS_TDM(reader))
return 0;
@@ -375,12 +386,15 @@ static void uni_reader_shutdown(struct snd_pcm_substream *substream,
{
struct sti_uniperiph_data *priv = snd_soc_dai_get_drvdata(dai);
struct uniperif *reader = priv->dai_data.uni;
+ unsigned long flags;
+ spin_lock_irqsave(&reader->irq_lock, flags);
if (reader->state != UNIPERIF_STATE_STOPPED) {
/* Stop the reader */
uni_reader_stop(reader);
}
reader->substream = NULL;
+ spin_unlock_irqrestore(&reader->irq_lock, flags);
}
static const struct snd_soc_dai_ops uni_reader_dai_ops = {
@@ -415,6 +429,8 @@ int uni_reader_init(struct platform_device *pdev,
return -EBUSY;
}
+ spin_lock_init(&reader->irq_lock);
+
return 0;
}
EXPORT_SYMBOL_GPL(uni_reader_init);
diff --git a/sound/soc/stm/Kconfig b/sound/soc/stm/Kconfig
new file mode 100644
index 000000000000..972970f0890a
--- /dev/null
+++ b/sound/soc/stm/Kconfig
@@ -0,0 +1,8 @@
+menuconfig SND_SOC_STM32
+ tristate "STMicroelectronics STM32 SOC audio support"
+ depends on ARCH_STM32 || COMPILE_TEST
+ depends on SND_SOC
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ select REGMAP_MMIO
+ help
+ Say Y if you want to enable ASoC-support for STM32
diff --git a/sound/soc/stm/Makefile b/sound/soc/stm/Makefile
new file mode 100644
index 000000000000..e466a4759698
--- /dev/null
+++ b/sound/soc/stm/Makefile
@@ -0,0 +1,6 @@
+# SAI
+snd-soc-stm32-sai-sub-objs := stm32_sai_sub.o
+obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai-sub.o
+
+snd-soc-stm32-sai-objs := stm32_sai.o
+obj-$(CONFIG_SND_SOC_STM32) += snd-soc-stm32-sai.o
diff --git a/sound/soc/stm/stm32_sai.c b/sound/soc/stm/stm32_sai.c
new file mode 100644
index 000000000000..2a27a26bf7a1
--- /dev/null
+++ b/sound/soc/stm/stm32_sai.c
@@ -0,0 +1,115 @@
+/*
+ * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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/clk.h>
+#include <linux/delay.h>
+#include <linux/module.h>
+#include <linux/of_platform.h>
+#include <linux/reset.h>
+
+#include <sound/dmaengine_pcm.h>
+#include <sound/core.h>
+
+#include "stm32_sai.h"
+
+static const struct of_device_id stm32_sai_ids[] = {
+ { .compatible = "st,stm32f4-sai", .data = (void *)SAI_STM32F4 },
+ {}
+};
+
+static int stm32_sai_probe(struct platform_device *pdev)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct stm32_sai_data *sai;
+ struct reset_control *rst;
+ struct resource *res;
+ void __iomem *base;
+ const struct of_device_id *of_id;
+
+ sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+ if (!sai)
+ return -ENOMEM;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ of_id = of_match_device(stm32_sai_ids, &pdev->dev);
+ if (of_id)
+ sai->version = (enum stm32_sai_version)of_id->data;
+ else
+ return -EINVAL;
+
+ sai->clk_x8k = devm_clk_get(&pdev->dev, "x8k");
+ if (IS_ERR(sai->clk_x8k)) {
+ dev_err(&pdev->dev, "missing x8k parent clock\n");
+ return PTR_ERR(sai->clk_x8k);
+ }
+
+ sai->clk_x11k = devm_clk_get(&pdev->dev, "x11k");
+ if (IS_ERR(sai->clk_x11k)) {
+ dev_err(&pdev->dev, "missing x11k parent clock\n");
+ return PTR_ERR(sai->clk_x11k);
+ }
+
+ /* init irqs */
+ sai->irq = platform_get_irq(pdev, 0);
+ if (sai->irq < 0) {
+ dev_err(&pdev->dev, "no irq for node %s\n", pdev->name);
+ return sai->irq;
+ }
+
+ /* reset */
+ rst = reset_control_get(&pdev->dev, NULL);
+ if (!IS_ERR(rst)) {
+ reset_control_assert(rst);
+ udelay(2);
+ reset_control_deassert(rst);
+ }
+
+ sai->pdev = pdev;
+ platform_set_drvdata(pdev, sai);
+
+ return of_platform_populate(np, NULL, NULL, &pdev->dev);
+}
+
+static int stm32_sai_remove(struct platform_device *pdev)
+{
+ of_platform_depopulate(&pdev->dev);
+
+ return 0;
+}
+
+MODULE_DEVICE_TABLE(of, stm32_sai_ids);
+
+static struct platform_driver stm32_sai_driver = {
+ .driver = {
+ .name = "st,stm32-sai",
+ .of_match_table = stm32_sai_ids,
+ },
+ .probe = stm32_sai_probe,
+ .remove = stm32_sai_remove,
+};
+
+module_platform_driver(stm32_sai_driver);
+
+MODULE_DESCRIPTION("STM32 Soc SAI Interface");
+MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>");
+MODULE_ALIAS("platform:st,stm32-sai");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/stm/stm32_sai.h b/sound/soc/stm/stm32_sai.h
new file mode 100644
index 000000000000..a801fda5066f
--- /dev/null
+++ b/sound/soc/stm/stm32_sai.h
@@ -0,0 +1,200 @@
+/*
+ * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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.
+ */
+
+/******************** SAI Register Map **************************************/
+
+/* common register */
+#define STM_SAI_GCR 0x00
+
+/* Sub-block A&B registers offsets, relative to A&B sub-block addresses */
+#define STM_SAI_CR1_REGX 0x00 /* A offset: 0x04. B offset: 0x24 */
+#define STM_SAI_CR2_REGX 0x04
+#define STM_SAI_FRCR_REGX 0x08
+#define STM_SAI_SLOTR_REGX 0x0C
+#define STM_SAI_IMR_REGX 0x10
+#define STM_SAI_SR_REGX 0x14
+#define STM_SAI_CLRFR_REGX 0x18
+#define STM_SAI_DR_REGX 0x1C
+
+/******************** Bit definition for SAI_GCR register *******************/
+#define SAI_GCR_SYNCIN_SHIFT 0
+#define SAI_GCR_SYNCIN_MASK GENMASK(1, SAI_GCR_SYNCIN_SHIFT)
+#define SAI_GCR_SYNCIN_SET(x) ((x) << SAI_GCR_SYNCIN_SHIFT)
+
+#define SAI_GCR_SYNCOUT_SHIFT 4
+#define SAI_GCR_SYNCOUT_MASK GENMASK(5, SAI_GCR_SYNCOUT_SHIFT)
+#define SAI_GCR_SYNCOUT_SET(x) ((x) << SAI_GCR_SYNCOUT_SHIFT)
+
+/******************* Bit definition for SAI_XCR1 register *******************/
+#define SAI_XCR1_RX_TX_SHIFT 0
+#define SAI_XCR1_RX_TX BIT(SAI_XCR1_RX_TX_SHIFT)
+#define SAI_XCR1_SLAVE_SHIFT 1
+#define SAI_XCR1_SLAVE BIT(SAI_XCR1_SLAVE_SHIFT)
+
+#define SAI_XCR1_PRTCFG_SHIFT 2
+#define SAI_XCR1_PRTCFG_MASK GENMASK(3, SAI_XCR1_PRTCFG_SHIFT)
+#define SAI_XCR1_PRTCFG_SET(x) ((x) << SAI_XCR1_PRTCFG_SHIFT)
+
+#define SAI_XCR1_DS_SHIFT 5
+#define SAI_XCR1_DS_MASK GENMASK(7, SAI_XCR1_DS_SHIFT)
+#define SAI_XCR1_DS_SET(x) ((x) << SAI_XCR1_DS_SHIFT)
+
+#define SAI_XCR1_LSBFIRST_SHIFT 8
+#define SAI_XCR1_LSBFIRST BIT(SAI_XCR1_LSBFIRST_SHIFT)
+#define SAI_XCR1_CKSTR_SHIFT 9
+#define SAI_XCR1_CKSTR BIT(SAI_XCR1_CKSTR_SHIFT)
+
+#define SAI_XCR1_SYNCEN_SHIFT 10
+#define SAI_XCR1_SYNCEN_MASK GENMASK(11, SAI_XCR1_SYNCEN_SHIFT)
+#define SAI_XCR1_SYNCEN_SET(x) ((x) << SAI_XCR1_SYNCEN_SHIFT)
+
+#define SAI_XCR1_MONO_SHIFT 12
+#define SAI_XCR1_MONO BIT(SAI_XCR1_MONO_SHIFT)
+#define SAI_XCR1_OUTDRIV_SHIFT 13
+#define SAI_XCR1_OUTDRIV BIT(SAI_XCR1_OUTDRIV_SHIFT)
+#define SAI_XCR1_SAIEN_SHIFT 16
+#define SAI_XCR1_SAIEN BIT(SAI_XCR1_SAIEN_SHIFT)
+#define SAI_XCR1_DMAEN_SHIFT 17
+#define SAI_XCR1_DMAEN BIT(SAI_XCR1_DMAEN_SHIFT)
+#define SAI_XCR1_NODIV_SHIFT 19
+#define SAI_XCR1_NODIV BIT(SAI_XCR1_NODIV_SHIFT)
+
+#define SAI_XCR1_MCKDIV_SHIFT 20
+#define SAI_XCR1_MCKDIV_WIDTH 4
+#define SAI_XCR1_MCKDIV_MASK GENMASK(24, SAI_XCR1_MCKDIV_SHIFT)
+#define SAI_XCR1_MCKDIV_SET(x) ((x) << SAI_XCR1_MCKDIV_SHIFT)
+#define SAI_XCR1_MCKDIV_MAX ((1 << SAI_XCR1_MCKDIV_WIDTH) - 1)
+
+#define SAI_XCR1_OSR_SHIFT 26
+#define SAI_XCR1_OSR BIT(SAI_XCR1_OSR_SHIFT)
+
+/******************* Bit definition for SAI_XCR2 register *******************/
+#define SAI_XCR2_FTH_SHIFT 0
+#define SAI_XCR2_FTH_MASK GENMASK(2, SAI_XCR2_FTH_SHIFT)
+#define SAI_XCR2_FTH_SET(x) ((x) << SAI_XCR2_FTH_SHIFT)
+
+#define SAI_XCR2_FFLUSH_SHIFT 3
+#define SAI_XCR2_FFLUSH BIT(SAI_XCR2_FFLUSH_SHIFT)
+#define SAI_XCR2_TRIS_SHIFT 4
+#define SAI_XCR2_TRIS BIT(SAI_XCR2_TRIS_SHIFT)
+#define SAI_XCR2_MUTE_SHIFT 5
+#define SAI_XCR2_MUTE BIT(SAI_XCR2_MUTE_SHIFT)
+#define SAI_XCR2_MUTEVAL_SHIFT 6
+#define SAI_XCR2_MUTEVAL BIT(SAI_XCR2_MUTEVAL_SHIFT)
+
+#define SAI_XCR2_MUTECNT_SHIFT 7
+#define SAI_XCR2_MUTECNT_MASK GENMASK(12, SAI_XCR2_MUTECNT_SHIFT)
+#define SAI_XCR2_MUTECNT_SET(x) ((x) << SAI_XCR2_MUTECNT_SHIFT)
+
+#define SAI_XCR2_CPL_SHIFT 13
+#define SAI_XCR2_CPL BIT(SAI_XCR2_CPL_SHIFT)
+
+#define SAI_XCR2_COMP_SHIFT 14
+#define SAI_XCR2_COMP_MASK GENMASK(15, SAI_XCR2_COMP_SHIFT)
+#define SAI_XCR2_COMP_SET(x) ((x) << SAI_XCR2_COMP_SHIFT)
+
+/****************** Bit definition for SAI_XFRCR register *******************/
+#define SAI_XFRCR_FRL_SHIFT 0
+#define SAI_XFRCR_FRL_MASK GENMASK(7, SAI_XFRCR_FRL_SHIFT)
+#define SAI_XFRCR_FRL_SET(x) ((x) << SAI_XFRCR_FRL_SHIFT)
+
+#define SAI_XFRCR_FSALL_SHIFT 8
+#define SAI_XFRCR_FSALL_MASK GENMASK(14, SAI_XFRCR_FSALL_SHIFT)
+#define SAI_XFRCR_FSALL_SET(x) ((x) << SAI_XFRCR_FSALL_SHIFT)
+
+#define SAI_XFRCR_FSDEF_SHIFT 16
+#define SAI_XFRCR_FSDEF BIT(SAI_XFRCR_FSDEF_SHIFT)
+#define SAI_XFRCR_FSPOL_SHIFT 17
+#define SAI_XFRCR_FSPOL BIT(SAI_XFRCR_FSPOL_SHIFT)
+#define SAI_XFRCR_FSOFF_SHIFT 18
+#define SAI_XFRCR_FSOFF BIT(SAI_XFRCR_FSOFF_SHIFT)
+
+/****************** Bit definition for SAI_XSLOTR register ******************/
+
+#define SAI_XSLOTR_FBOFF_SHIFT 0
+#define SAI_XSLOTR_FBOFF_MASK GENMASK(4, SAI_XSLOTR_FBOFF_SHIFT)
+#define SAI_XSLOTR_FBOFF_SET(x) ((x) << SAI_XSLOTR_FBOFF_SHIFT)
+
+#define SAI_XSLOTR_SLOTSZ_SHIFT 6
+#define SAI_XSLOTR_SLOTSZ_MASK GENMASK(7, SAI_XSLOTR_SLOTSZ_SHIFT)
+#define SAI_XSLOTR_SLOTSZ_SET(x) ((x) << SAI_XSLOTR_SLOTSZ_SHIFT)
+
+#define SAI_XSLOTR_NBSLOT_SHIFT 8
+#define SAI_XSLOTR_NBSLOT_MASK GENMASK(11, SAI_XSLOTR_NBSLOT_SHIFT)
+#define SAI_XSLOTR_NBSLOT_SET(x) ((x) << SAI_XSLOTR_NBSLOT_SHIFT)
+
+#define SAI_XSLOTR_SLOTEN_SHIFT 16
+#define SAI_XSLOTR_SLOTEN_WIDTH 16
+#define SAI_XSLOTR_SLOTEN_MASK GENMASK(31, SAI_XSLOTR_SLOTEN_SHIFT)
+#define SAI_XSLOTR_SLOTEN_SET(x) ((x) << SAI_XSLOTR_SLOTEN_SHIFT)
+
+/******************* Bit definition for SAI_XIMR register *******************/
+#define SAI_XIMR_OVRUDRIE BIT(0)
+#define SAI_XIMR_MUTEDETIE BIT(1)
+#define SAI_XIMR_WCKCFGIE BIT(2)
+#define SAI_XIMR_FREQIE BIT(3)
+#define SAI_XIMR_CNRDYIE BIT(4)
+#define SAI_XIMR_AFSDETIE BIT(5)
+#define SAI_XIMR_LFSDETIE BIT(6)
+
+#define SAI_XIMR_SHIFT 0
+#define SAI_XIMR_MASK GENMASK(6, SAI_XIMR_SHIFT)
+
+/******************** Bit definition for SAI_XSR register *******************/
+#define SAI_XSR_OVRUDR BIT(0)
+#define SAI_XSR_MUTEDET BIT(1)
+#define SAI_XSR_WCKCFG BIT(2)
+#define SAI_XSR_FREQ BIT(3)
+#define SAI_XSR_CNRDY BIT(4)
+#define SAI_XSR_AFSDET BIT(5)
+#define SAI_XSR_LFSDET BIT(6)
+
+#define SAI_XSR_SHIFT 0
+#define SAI_XSR_MASK GENMASK(6, SAI_XSR_SHIFT)
+
+/****************** Bit definition for SAI_XCLRFR register ******************/
+#define SAI_XCLRFR_COVRUDR BIT(0)
+#define SAI_XCLRFR_CMUTEDET BIT(1)
+#define SAI_XCLRFR_CWCKCFG BIT(2)
+#define SAI_XCLRFR_CFREQ BIT(3)
+#define SAI_XCLRFR_CCNRDY BIT(4)
+#define SAI_XCLRFR_CAFSDET BIT(5)
+#define SAI_XCLRFR_CLFSDET BIT(6)
+
+#define SAI_XCLRFR_SHIFT 0
+#define SAI_XCLRFR_MASK GENMASK(6, SAI_XCLRFR_SHIFT)
+
+enum stm32_sai_version {
+ SAI_STM32F4
+};
+
+/**
+ * struct stm32_sai_data - private data of SAI instance driver
+ * @pdev: device data pointer
+ * @clk_x8k: SAI parent clock for sampling frequencies multiple of 8kHz
+ * @clk_x11k: SAI parent clock for sampling frequencies multiple of 11kHz
+ * @version: SOC version
+ * @irq: SAI interrupt line
+ */
+struct stm32_sai_data {
+ struct platform_device *pdev;
+ struct clk *clk_x8k;
+ struct clk *clk_x11k;
+ int version;
+ int irq;
+};
diff --git a/sound/soc/stm/stm32_sai_sub.c b/sound/soc/stm/stm32_sai_sub.c
new file mode 100644
index 000000000000..ae4706ca265b
--- /dev/null
+++ b/sound/soc/stm/stm32_sai_sub.c
@@ -0,0 +1,884 @@
+/*
+ * STM32 ALSA SoC Digital Audio Interface (SAI) driver.
+ *
+ * Copyright (C) 2016, STMicroelectronics - All Rights Reserved
+ * Author(s): Olivier Moysan <olivier.moysan@st.com> for STMicroelectronics.
+ *
+ * License terms: GPL V2.0.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms of the GNU General Public License version 2 as published by
+ * the Free Software Foundation.
+ *
+ * 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/clk.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/of_irq.h>
+#include <linux/of_platform.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+
+#include "stm32_sai.h"
+
+#define SAI_FREE_PROTOCOL 0x0
+
+#define SAI_SLOT_SIZE_AUTO 0x0
+#define SAI_SLOT_SIZE_16 0x1
+#define SAI_SLOT_SIZE_32 0x2
+
+#define SAI_DATASIZE_8 0x2
+#define SAI_DATASIZE_10 0x3
+#define SAI_DATASIZE_16 0x4
+#define SAI_DATASIZE_20 0x5
+#define SAI_DATASIZE_24 0x6
+#define SAI_DATASIZE_32 0x7
+
+#define STM_SAI_FIFO_SIZE 8
+#define STM_SAI_DAI_NAME_SIZE 15
+
+#define STM_SAI_IS_PLAYBACK(ip) ((ip)->dir == SNDRV_PCM_STREAM_PLAYBACK)
+#define STM_SAI_IS_CAPTURE(ip) ((ip)->dir == SNDRV_PCM_STREAM_CAPTURE)
+
+#define STM_SAI_A_ID 0x0
+#define STM_SAI_B_ID 0x1
+
+#define STM_SAI_BLOCK_NAME(x) (((x)->id == STM_SAI_A_ID) ? "A" : "B")
+
+/**
+ * struct stm32_sai_sub_data - private data of SAI sub block (block A or B)
+ * @pdev: device data pointer
+ * @regmap: SAI register map pointer
+ * @dma_params: dma configuration data for rx or tx channel
+ * @cpu_dai_drv: DAI driver data pointer
+ * @cpu_dai: DAI runtime data pointer
+ * @substream: PCM substream data pointer
+ * @pdata: SAI block parent data pointer
+ * @sai_ck: kernel clock feeding the SAI clock generator
+ * @phys_addr: SAI registers physical base address
+ * @mclk_rate: SAI block master clock frequency (Hz). set at init
+ * @id: SAI sub block id corresponding to sub-block A or B
+ * @dir: SAI block direction (playback or capture). set at init
+ * @master: SAI block mode flag. (true=master, false=slave) set at init
+ * @fmt: SAI block format. relevant only for custom protocols. set at init
+ * @sync: SAI block synchronization mode. (none, internal or external)
+ * @fs_length: frame synchronization length. depends on protocol settings
+ * @slots: rx or tx slot number
+ * @slot_width: rx or tx slot width in bits
+ * @slot_mask: rx or tx active slots mask. set at init or at runtime
+ * @data_size: PCM data width. corresponds to PCM substream width.
+ */
+struct stm32_sai_sub_data {
+ struct platform_device *pdev;
+ struct regmap *regmap;
+ struct snd_dmaengine_dai_dma_data dma_params;
+ struct snd_soc_dai_driver *cpu_dai_drv;
+ struct snd_soc_dai *cpu_dai;
+ struct snd_pcm_substream *substream;
+ struct stm32_sai_data *pdata;
+ struct clk *sai_ck;
+ dma_addr_t phys_addr;
+ unsigned int mclk_rate;
+ unsigned int id;
+ int dir;
+ bool master;
+ int fmt;
+ int sync;
+ int fs_length;
+ int slots;
+ int slot_width;
+ int slot_mask;
+ int data_size;
+};
+
+enum stm32_sai_fifo_th {
+ STM_SAI_FIFO_TH_EMPTY,
+ STM_SAI_FIFO_TH_QUARTER,
+ STM_SAI_FIFO_TH_HALF,
+ STM_SAI_FIFO_TH_3_QUARTER,
+ STM_SAI_FIFO_TH_FULL,
+};
+
+static bool stm32_sai_sub_readable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case STM_SAI_CR1_REGX:
+ case STM_SAI_CR2_REGX:
+ case STM_SAI_FRCR_REGX:
+ case STM_SAI_SLOTR_REGX:
+ case STM_SAI_IMR_REGX:
+ case STM_SAI_SR_REGX:
+ case STM_SAI_CLRFR_REGX:
+ case STM_SAI_DR_REGX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool stm32_sai_sub_volatile_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case STM_SAI_DR_REGX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static bool stm32_sai_sub_writeable_reg(struct device *dev, unsigned int reg)
+{
+ switch (reg) {
+ case STM_SAI_CR1_REGX:
+ case STM_SAI_CR2_REGX:
+ case STM_SAI_FRCR_REGX:
+ case STM_SAI_SLOTR_REGX:
+ case STM_SAI_IMR_REGX:
+ case STM_SAI_SR_REGX:
+ case STM_SAI_CLRFR_REGX:
+ case STM_SAI_DR_REGX:
+ return true;
+ default:
+ return false;
+ }
+}
+
+static const struct regmap_config stm32_sai_sub_regmap_config = {
+ .reg_bits = 32,
+ .reg_stride = 4,
+ .val_bits = 32,
+ .max_register = STM_SAI_DR_REGX,
+ .readable_reg = stm32_sai_sub_readable_reg,
+ .volatile_reg = stm32_sai_sub_volatile_reg,
+ .writeable_reg = stm32_sai_sub_writeable_reg,
+ .fast_io = true,
+};
+
+static irqreturn_t stm32_sai_isr(int irq, void *devid)
+{
+ struct stm32_sai_sub_data *sai = (struct stm32_sai_sub_data *)devid;
+ struct snd_pcm_substream *substream = sai->substream;
+ struct platform_device *pdev = sai->pdev;
+ unsigned int sr, imr, flags;
+ snd_pcm_state_t status = SNDRV_PCM_STATE_RUNNING;
+
+ regmap_read(sai->regmap, STM_SAI_IMR_REGX, &imr);
+ regmap_read(sai->regmap, STM_SAI_SR_REGX, &sr);
+
+ flags = sr & imr;
+ if (!flags)
+ return IRQ_NONE;
+
+ regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX, SAI_XCLRFR_MASK,
+ SAI_XCLRFR_MASK);
+
+ if (flags & SAI_XIMR_OVRUDRIE) {
+ dev_err(&pdev->dev, "IT %s\n",
+ STM_SAI_IS_PLAYBACK(sai) ? "underrun" : "overrun");
+ status = SNDRV_PCM_STATE_XRUN;
+ }
+
+ if (flags & SAI_XIMR_MUTEDETIE)
+ dev_dbg(&pdev->dev, "IT mute detected\n");
+
+ if (flags & SAI_XIMR_WCKCFGIE) {
+ dev_err(&pdev->dev, "IT wrong clock configuration\n");
+ status = SNDRV_PCM_STATE_DISCONNECTED;
+ }
+
+ if (flags & SAI_XIMR_CNRDYIE)
+ dev_warn(&pdev->dev, "IT Codec not ready\n");
+
+ if (flags & SAI_XIMR_AFSDETIE) {
+ dev_warn(&pdev->dev, "IT Anticipated frame synchro\n");
+ status = SNDRV_PCM_STATE_XRUN;
+ }
+
+ if (flags & SAI_XIMR_LFSDETIE) {
+ dev_warn(&pdev->dev, "IT Late frame synchro\n");
+ status = SNDRV_PCM_STATE_XRUN;
+ }
+
+ if (status != SNDRV_PCM_STATE_RUNNING) {
+ snd_pcm_stream_lock(substream);
+ snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+ snd_pcm_stream_unlock(substream);
+ }
+
+ return IRQ_HANDLED;
+}
+
+static int stm32_sai_set_sysclk(struct snd_soc_dai *cpu_dai,
+ int clk_id, unsigned int freq, int dir)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+
+ if ((dir == SND_SOC_CLOCK_OUT) && sai->master) {
+ sai->mclk_rate = freq;
+ dev_dbg(cpu_dai->dev, "SAI MCLK frequency is %uHz\n", freq);
+ }
+
+ return 0;
+}
+
+static int stm32_sai_set_dai_tdm_slot(struct snd_soc_dai *cpu_dai, u32 tx_mask,
+ u32 rx_mask, int slots, int slot_width)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int slotr, slotr_mask, slot_size;
+
+ dev_dbg(cpu_dai->dev, "masks tx/rx:%#x/%#x, slots:%d, width:%d\n",
+ tx_mask, rx_mask, slots, slot_width);
+
+ switch (slot_width) {
+ case 16:
+ slot_size = SAI_SLOT_SIZE_16;
+ break;
+ case 32:
+ slot_size = SAI_SLOT_SIZE_32;
+ break;
+ default:
+ slot_size = SAI_SLOT_SIZE_AUTO;
+ break;
+ }
+
+ slotr = SAI_XSLOTR_SLOTSZ_SET(slot_size) |
+ SAI_XSLOTR_NBSLOT_SET(slots - 1);
+ slotr_mask = SAI_XSLOTR_SLOTSZ_MASK | SAI_XSLOTR_NBSLOT_MASK;
+
+ /* tx/rx mask set in machine init, if slot number defined in DT */
+ if (STM_SAI_IS_PLAYBACK(sai)) {
+ sai->slot_mask = tx_mask;
+ slotr |= SAI_XSLOTR_SLOTEN_SET(tx_mask);
+ }
+
+ if (STM_SAI_IS_CAPTURE(sai)) {
+ sai->slot_mask = rx_mask;
+ slotr |= SAI_XSLOTR_SLOTEN_SET(rx_mask);
+ }
+
+ slotr_mask |= SAI_XSLOTR_SLOTEN_MASK;
+
+ regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX, slotr_mask, slotr);
+
+ sai->slot_width = slot_width;
+ sai->slots = slots;
+
+ return 0;
+}
+
+static int stm32_sai_set_dai_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int cr1 = 0, frcr = 0;
+ int cr1_mask = 0, frcr_mask = 0;
+ int ret;
+
+ dev_dbg(cpu_dai->dev, "fmt %x\n", fmt);
+
+ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+ /* SCK active high for all protocols */
+ case SND_SOC_DAIFMT_I2S:
+ cr1 |= SAI_XCR1_CKSTR;
+ frcr |= SAI_XFRCR_FSOFF | SAI_XFRCR_FSDEF;
+ break;
+ /* Left justified */
+ case SND_SOC_DAIFMT_MSB:
+ frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF;
+ break;
+ /* Right justified */
+ case SND_SOC_DAIFMT_LSB:
+ frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSDEF;
+ break;
+ case SND_SOC_DAIFMT_DSP_A:
+ frcr |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF;
+ break;
+ case SND_SOC_DAIFMT_DSP_B:
+ frcr |= SAI_XFRCR_FSPOL;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Unsupported protocol %#x\n",
+ fmt & SND_SOC_DAIFMT_FORMAT_MASK);
+ return -EINVAL;
+ }
+
+ cr1_mask |= SAI_XCR1_PRTCFG_MASK | SAI_XCR1_CKSTR;
+ frcr_mask |= SAI_XFRCR_FSPOL | SAI_XFRCR_FSOFF |
+ SAI_XFRCR_FSDEF;
+
+ /* DAI clock strobing. Invert setting previously set */
+ switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+ case SND_SOC_DAIFMT_NB_NF:
+ break;
+ case SND_SOC_DAIFMT_IB_NF:
+ cr1 ^= SAI_XCR1_CKSTR;
+ break;
+ case SND_SOC_DAIFMT_NB_IF:
+ frcr ^= SAI_XFRCR_FSPOL;
+ break;
+ case SND_SOC_DAIFMT_IB_IF:
+ /* Invert fs & sck */
+ cr1 ^= SAI_XCR1_CKSTR;
+ frcr ^= SAI_XFRCR_FSPOL;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Unsupported strobing %#x\n",
+ fmt & SND_SOC_DAIFMT_INV_MASK);
+ return -EINVAL;
+ }
+ cr1_mask |= SAI_XCR1_CKSTR;
+ frcr_mask |= SAI_XFRCR_FSPOL;
+
+ regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr);
+
+ /* DAI clock master masks */
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ /* codec is master */
+ cr1 |= SAI_XCR1_SLAVE;
+ sai->master = false;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ sai->master = true;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Unsupported mode %#x\n",
+ fmt & SND_SOC_DAIFMT_MASTER_MASK);
+ return -EINVAL;
+ }
+ cr1_mask |= SAI_XCR1_SLAVE;
+
+ ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+ return ret;
+ }
+
+ sai->fmt = fmt;
+
+ return 0;
+}
+
+static int stm32_sai_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int imr, cr2, ret;
+
+ sai->substream = substream;
+
+ ret = clk_prepare_enable(sai->sai_ck);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "failed to enable clock: %d\n", ret);
+ return ret;
+ }
+
+ /* Enable ITs */
+ regmap_update_bits(sai->regmap, STM_SAI_SR_REGX,
+ SAI_XSR_MASK, (unsigned int)~SAI_XSR_MASK);
+
+ regmap_update_bits(sai->regmap, STM_SAI_CLRFR_REGX,
+ SAI_XCLRFR_MASK, SAI_XCLRFR_MASK);
+
+ imr = SAI_XIMR_OVRUDRIE;
+ if (STM_SAI_IS_CAPTURE(sai)) {
+ regmap_read(sai->regmap, STM_SAI_CR2_REGX, &cr2);
+ if (cr2 & SAI_XCR2_MUTECNT_MASK)
+ imr |= SAI_XIMR_MUTEDETIE;
+ }
+
+ if (sai->master)
+ imr |= SAI_XIMR_WCKCFGIE;
+ else
+ imr |= SAI_XIMR_AFSDETIE | SAI_XIMR_LFSDETIE;
+
+ regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX,
+ SAI_XIMR_MASK, imr);
+
+ return 0;
+}
+
+static int stm32_sai_set_config(struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int cr1, cr1_mask, ret;
+ int fth = STM_SAI_FIFO_TH_HALF;
+
+ /* FIFO config */
+ regmap_update_bits(sai->regmap, STM_SAI_CR2_REGX,
+ SAI_XCR2_FFLUSH | SAI_XCR2_FTH_MASK,
+ SAI_XCR2_FFLUSH | SAI_XCR2_FTH_SET(fth));
+
+ /* Mode, data format and channel config */
+ cr1 = SAI_XCR1_PRTCFG_SET(SAI_FREE_PROTOCOL);
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_S8:
+ cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_8);
+ break;
+ case SNDRV_PCM_FORMAT_S16_LE:
+ cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_16);
+ break;
+ case SNDRV_PCM_FORMAT_S32_LE:
+ cr1 |= SAI_XCR1_DS_SET(SAI_DATASIZE_32);
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Data format not supported");
+ return -EINVAL;
+ }
+ cr1_mask = SAI_XCR1_DS_MASK | SAI_XCR1_PRTCFG_MASK;
+
+ cr1_mask |= SAI_XCR1_RX_TX;
+ if (STM_SAI_IS_CAPTURE(sai))
+ cr1 |= SAI_XCR1_RX_TX;
+
+ cr1_mask |= SAI_XCR1_MONO;
+ if ((sai->slots == 2) && (params_channels(params) == 1))
+ cr1 |= SAI_XCR1_MONO;
+
+ ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, cr1_mask, cr1);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+ return ret;
+ }
+
+ /* DMA config */
+ sai->dma_params.maxburst = STM_SAI_FIFO_SIZE * fth / sizeof(u32);
+ snd_soc_dai_set_dma_data(cpu_dai, substream, (void *)&sai->dma_params);
+
+ return 0;
+}
+
+static int stm32_sai_set_slots(struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int slotr, slot_sz;
+
+ regmap_read(sai->regmap, STM_SAI_SLOTR_REGX, &slotr);
+
+ /*
+ * If SLOTSZ is set to auto in SLOTR, align slot width on data size
+ * By default slot width = data size, if not forced from DT
+ */
+ slot_sz = slotr & SAI_XSLOTR_SLOTSZ_MASK;
+ if (slot_sz == SAI_XSLOTR_SLOTSZ_SET(SAI_SLOT_SIZE_AUTO))
+ sai->slot_width = sai->data_size;
+
+ if (sai->slot_width < sai->data_size) {
+ dev_err(cpu_dai->dev,
+ "Data size %d larger than slot width\n",
+ sai->data_size);
+ return -EINVAL;
+ }
+
+ /* Slot number is set to 2, if not specified in DT */
+ if (!sai->slots)
+ sai->slots = 2;
+
+ /* The number of slots in the audio frame is equal to NBSLOT[3:0] + 1*/
+ regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX,
+ SAI_XSLOTR_NBSLOT_MASK,
+ SAI_XSLOTR_NBSLOT_SET((sai->slots - 1)));
+
+ /* Set default slots mask if not already set from DT */
+ if (!(slotr & SAI_XSLOTR_SLOTEN_MASK)) {
+ sai->slot_mask = (1 << sai->slots) - 1;
+ regmap_update_bits(sai->regmap,
+ STM_SAI_SLOTR_REGX, SAI_XSLOTR_SLOTEN_MASK,
+ SAI_XSLOTR_SLOTEN_SET(sai->slot_mask));
+ }
+
+ dev_dbg(cpu_dai->dev, "slots %d, slot width %d\n",
+ sai->slots, sai->slot_width);
+
+ return 0;
+}
+
+static void stm32_sai_set_frame(struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int fs_active, offset, format;
+ int frcr, frcr_mask;
+
+ format = sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+ sai->fs_length = sai->slot_width * sai->slots;
+
+ fs_active = sai->fs_length / 2;
+ if ((format == SND_SOC_DAIFMT_DSP_A) ||
+ (format == SND_SOC_DAIFMT_DSP_B))
+ fs_active = 1;
+
+ frcr = SAI_XFRCR_FRL_SET((sai->fs_length - 1));
+ frcr |= SAI_XFRCR_FSALL_SET((fs_active - 1));
+ frcr_mask = SAI_XFRCR_FRL_MASK | SAI_XFRCR_FSALL_MASK;
+
+ dev_dbg(cpu_dai->dev, "frame length %d, frame active %d\n",
+ sai->fs_length, fs_active);
+
+ regmap_update_bits(sai->regmap, STM_SAI_FRCR_REGX, frcr_mask, frcr);
+
+ if ((sai->fmt & SND_SOC_DAIFMT_FORMAT_MASK) == SND_SOC_DAIFMT_LSB) {
+ offset = sai->slot_width - sai->data_size;
+
+ regmap_update_bits(sai->regmap, STM_SAI_SLOTR_REGX,
+ SAI_XSLOTR_FBOFF_MASK,
+ SAI_XSLOTR_FBOFF_SET(offset));
+ }
+}
+
+static int stm32_sai_configure_clock(struct snd_soc_dai *cpu_dai,
+ struct snd_pcm_hw_params *params)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int cr1, mask, div = 0;
+ int sai_clk_rate, ret;
+
+ if (!sai->mclk_rate) {
+ dev_err(cpu_dai->dev, "Mclk rate is null\n");
+ return -EINVAL;
+ }
+
+ if (!(params_rate(params) % 11025))
+ clk_set_parent(sai->sai_ck, sai->pdata->clk_x11k);
+ else
+ clk_set_parent(sai->sai_ck, sai->pdata->clk_x8k);
+ sai_clk_rate = clk_get_rate(sai->sai_ck);
+
+ /*
+ * mclk_rate = 256 * fs
+ * MCKDIV = 0 if sai_ck < 3/2 * mclk_rate
+ * MCKDIV = sai_ck / (2 * mclk_rate) otherwise
+ */
+ if (2 * sai_clk_rate >= 3 * sai->mclk_rate)
+ div = DIV_ROUND_CLOSEST(sai_clk_rate, 2 * sai->mclk_rate);
+
+ if (div > SAI_XCR1_MCKDIV_MAX) {
+ dev_err(cpu_dai->dev, "Divider %d out of range\n", div);
+ return -EINVAL;
+ }
+ dev_dbg(cpu_dai->dev, "SAI clock %d, divider %d\n", sai_clk_rate, div);
+
+ mask = SAI_XCR1_MCKDIV_MASK;
+ cr1 = SAI_XCR1_MCKDIV_SET(div);
+ ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX, mask, cr1);
+ if (ret < 0) {
+ dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static int stm32_sai_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret;
+
+ sai->data_size = params_width(params);
+
+ ret = stm32_sai_set_slots(cpu_dai);
+ if (ret < 0)
+ return ret;
+ stm32_sai_set_frame(cpu_dai);
+
+ ret = stm32_sai_set_config(cpu_dai, substream, params);
+ if (ret)
+ return ret;
+
+ if (sai->master)
+ ret = stm32_sai_configure_clock(cpu_dai, params);
+
+ return ret;
+}
+
+static int stm32_sai_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+ int ret;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ dev_dbg(cpu_dai->dev, "Enable DMA and SAI\n");
+
+ regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+ SAI_XCR1_DMAEN, SAI_XCR1_DMAEN);
+
+ /* Enable SAI */
+ ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+ SAI_XCR1_SAIEN, SAI_XCR1_SAIEN);
+ if (ret < 0)
+ dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ case SNDRV_PCM_TRIGGER_STOP:
+ dev_dbg(cpu_dai->dev, "Disable DMA and SAI\n");
+
+ regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+ SAI_XCR1_DMAEN,
+ (unsigned int)~SAI_XCR1_DMAEN);
+
+ ret = regmap_update_bits(sai->regmap, STM_SAI_CR1_REGX,
+ SAI_XCR1_SAIEN,
+ (unsigned int)~SAI_XCR1_SAIEN);
+ if (ret < 0)
+ dev_err(cpu_dai->dev, "Failed to update CR1 register\n");
+ break;
+ default:
+ return -EINVAL;
+ }
+
+ return ret;
+}
+
+static void stm32_sai_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = snd_soc_dai_get_drvdata(cpu_dai);
+
+ regmap_update_bits(sai->regmap, STM_SAI_IMR_REGX, SAI_XIMR_MASK, 0);
+
+ clk_disable_unprepare(sai->sai_ck);
+ sai->substream = NULL;
+}
+
+static int stm32_sai_dai_probe(struct snd_soc_dai *cpu_dai)
+{
+ struct stm32_sai_sub_data *sai = dev_get_drvdata(cpu_dai->dev);
+
+ sai->dma_params.addr = (dma_addr_t)(sai->phys_addr + STM_SAI_DR_REGX);
+ sai->dma_params.maxburst = 1;
+ /* Buswidth will be set by framework at runtime */
+ sai->dma_params.addr_width = DMA_SLAVE_BUSWIDTH_UNDEFINED;
+
+ if (STM_SAI_IS_PLAYBACK(sai))
+ snd_soc_dai_init_dma_data(cpu_dai, &sai->dma_params, NULL);
+ else
+ snd_soc_dai_init_dma_data(cpu_dai, NULL, &sai->dma_params);
+
+ return 0;
+}
+
+static const struct snd_soc_dai_ops stm32_sai_pcm_dai_ops = {
+ .set_sysclk = stm32_sai_set_sysclk,
+ .set_fmt = stm32_sai_set_dai_fmt,
+ .set_tdm_slot = stm32_sai_set_dai_tdm_slot,
+ .startup = stm32_sai_startup,
+ .hw_params = stm32_sai_hw_params,
+ .trigger = stm32_sai_trigger,
+ .shutdown = stm32_sai_shutdown,
+};
+
+static const struct snd_pcm_hardware stm32_sai_pcm_hw = {
+ .info = SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_MMAP,
+ .buffer_bytes_max = 8 * PAGE_SIZE,
+ .period_bytes_min = 1024, /* 5ms at 48kHz */
+ .period_bytes_max = PAGE_SIZE,
+ .periods_min = 2,
+ .periods_max = 8,
+};
+
+static struct snd_soc_dai_driver stm32_sai_playback_dai[] = {
+{
+ .probe = stm32_sai_dai_probe,
+ .id = 1, /* avoid call to fmt_single_name() */
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ /* DMA does not support 24 bits transfers */
+ .formats =
+ SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &stm32_sai_pcm_dai_ops,
+ }
+};
+
+static struct snd_soc_dai_driver stm32_sai_capture_dai[] = {
+{
+ .probe = stm32_sai_dai_probe,
+ .id = 1, /* avoid call to fmt_single_name() */
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 2,
+ .rate_min = 8000,
+ .rate_max = 192000,
+ .rates = SNDRV_PCM_RATE_CONTINUOUS,
+ /* DMA does not support 24 bits transfers */
+ .formats =
+ SNDRV_PCM_FMTBIT_S8 |
+ SNDRV_PCM_FMTBIT_S16_LE |
+ SNDRV_PCM_FMTBIT_S32_LE,
+ },
+ .ops = &stm32_sai_pcm_dai_ops,
+ }
+};
+
+static const struct snd_dmaengine_pcm_config stm32_sai_pcm_config = {
+ .pcm_hardware = &stm32_sai_pcm_hw,
+ .prepare_slave_config = snd_dmaengine_pcm_prepare_slave_config,
+};
+
+static const struct snd_soc_component_driver stm32_component = {
+ .name = "stm32-sai",
+};
+
+static const struct of_device_id stm32_sai_sub_ids[] = {
+ { .compatible = "st,stm32-sai-sub-a",
+ .data = (void *)STM_SAI_A_ID},
+ { .compatible = "st,stm32-sai-sub-b",
+ .data = (void *)STM_SAI_B_ID},
+ {}
+};
+MODULE_DEVICE_TABLE(of, stm32_sai_sub_ids);
+
+static int stm32_sai_sub_parse_of(struct platform_device *pdev,
+ struct stm32_sai_sub_data *sai)
+{
+ struct device_node *np = pdev->dev.of_node;
+ struct resource *res;
+ void __iomem *base;
+
+ if (!np)
+ return -ENODEV;
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+
+ dev_err(&pdev->dev, "res %pr\n", res);
+
+ base = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(base))
+ return PTR_ERR(base);
+
+ sai->phys_addr = res->start;
+ sai->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+ &stm32_sai_sub_regmap_config);
+
+ /* Get direction property */
+ if (of_property_match_string(np, "dma-names", "tx") >= 0) {
+ sai->dir = SNDRV_PCM_STREAM_PLAYBACK;
+ } else if (of_property_match_string(np, "dma-names", "rx") >= 0) {
+ sai->dir = SNDRV_PCM_STREAM_CAPTURE;
+ } else {
+ dev_err(&pdev->dev, "Unsupported direction\n");
+ return -EINVAL;
+ }
+
+ sai->sai_ck = devm_clk_get(&pdev->dev, "sai_ck");
+ if (IS_ERR(sai->sai_ck)) {
+ dev_err(&pdev->dev, "missing kernel clock sai_ck\n");
+ return PTR_ERR(sai->sai_ck);
+ }
+
+ return 0;
+}
+
+static int stm32_sai_sub_dais_init(struct platform_device *pdev,
+ struct stm32_sai_sub_data *sai)
+{
+ sai->cpu_dai_drv = devm_kzalloc(&pdev->dev,
+ sizeof(struct snd_soc_dai_driver),
+ GFP_KERNEL);
+ if (!sai->cpu_dai_drv)
+ return -ENOMEM;
+
+ sai->cpu_dai_drv->name = dev_name(&pdev->dev);
+ if (STM_SAI_IS_PLAYBACK(sai)) {
+ memcpy(sai->cpu_dai_drv, &stm32_sai_playback_dai,
+ sizeof(stm32_sai_playback_dai));
+ sai->cpu_dai_drv->playback.stream_name = sai->cpu_dai_drv->name;
+ } else {
+ memcpy(sai->cpu_dai_drv, &stm32_sai_capture_dai,
+ sizeof(stm32_sai_capture_dai));
+ sai->cpu_dai_drv->capture.stream_name = sai->cpu_dai_drv->name;
+ }
+
+ return 0;
+}
+
+static int stm32_sai_sub_probe(struct platform_device *pdev)
+{
+ struct stm32_sai_sub_data *sai;
+ const struct of_device_id *of_id;
+ int ret;
+
+ sai = devm_kzalloc(&pdev->dev, sizeof(*sai), GFP_KERNEL);
+ if (!sai)
+ return -ENOMEM;
+
+ of_id = of_match_device(stm32_sai_sub_ids, &pdev->dev);
+ if (!of_id)
+ return -EINVAL;
+ sai->id = (uintptr_t)of_id->data;
+
+ sai->pdev = pdev;
+ platform_set_drvdata(pdev, sai);
+
+ sai->pdata = dev_get_drvdata(pdev->dev.parent);
+ if (!sai->pdata) {
+ dev_err(&pdev->dev, "Parent device data not available\n");
+ return -EINVAL;
+ }
+
+ ret = stm32_sai_sub_parse_of(pdev, sai);
+ if (ret)
+ return ret;
+
+ ret = stm32_sai_sub_dais_init(pdev, sai);
+ if (ret)
+ return ret;
+
+ ret = devm_request_irq(&pdev->dev, sai->pdata->irq, stm32_sai_isr,
+ IRQF_SHARED, dev_name(&pdev->dev), sai);
+ if (ret) {
+ dev_err(&pdev->dev, "irq request returned %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &stm32_component,
+ sai->cpu_dai_drv, 1);
+ if (ret)
+ return ret;
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev,
+ &stm32_sai_pcm_config, 0);
+ if (ret) {
+ dev_err(&pdev->dev, "could not register pcm dma\n");
+ return ret;
+ }
+
+ return 0;
+}
+
+static struct platform_driver stm32_sai_sub_driver = {
+ .driver = {
+ .name = "st,stm32-sai-sub",
+ .of_match_table = stm32_sai_sub_ids,
+ },
+ .probe = stm32_sai_sub_probe,
+};
+
+module_platform_driver(stm32_sai_sub_driver);
+
+MODULE_DESCRIPTION("STM32 Soc SAI sub-block Interface");
+MODULE_AUTHOR("Olivier Moysan, <olivier.moysan@st.com>");
+MODULE_ALIAS("platform:st,stm32-sai-sub");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/sunxi/sun8i-codec-analog.c b/sound/soc/sunxi/sun8i-codec-analog.c
index 72331332b72e..6c17c99c2c8d 100644
--- a/sound/soc/sunxi/sun8i-codec-analog.c
+++ b/sound/soc/sunxi/sun8i-codec-analog.c
@@ -252,24 +252,15 @@ static const DECLARE_TLV_DB_RANGE(sun8i_codec_mic_gain_scale,
);
static const struct snd_kcontrol_new sun8i_codec_common_controls[] = {
- /* Mixer pre-gains */
- SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL,
- SUN8I_ADDA_LINEIN_GCTRL_LINEING,
- 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
+ /* Mixer pre-gain */
SOC_SINGLE_TLV("Mic1 Playback Volume", SUN8I_ADDA_MICIN_GCTRL,
SUN8I_ADDA_MICIN_GCTRL_MIC1G,
0x7, 0, sun8i_codec_out_mixer_pregain_scale),
- SOC_SINGLE_TLV("Mic2 Playback Volume",
- SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G,
- 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
- /* Microphone Amp boost gains */
+ /* Microphone Amp boost gain */
SOC_SINGLE_TLV("Mic1 Boost Volume", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1BOOST, 0x7, 0,
sun8i_codec_mic_gain_scale),
- SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL,
- SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0,
- sun8i_codec_mic_gain_scale),
/* ADC */
SOC_SINGLE_TLV("ADC Gain Capture Volume", SUN8I_ADDA_ADC_AP_EN,
@@ -295,12 +286,8 @@ static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
* stream widgets at the card level.
*/
- /* Line In */
- SND_SOC_DAPM_INPUT("LINEIN"),
-
- /* Microphone inputs */
+ /* Microphone input */
SND_SOC_DAPM_INPUT("MIC1"),
- SND_SOC_DAPM_INPUT("MIC2"),
/* Microphone Bias */
SND_SOC_DAPM_SUPPLY("MBIAS", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
@@ -310,8 +297,6 @@ static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
/* Mic input path */
SND_SOC_DAPM_PGA("Mic1 Amplifier", SUN8I_ADDA_MIC1G_MICBIAS_CTRL,
SUN8I_ADDA_MIC1G_MICBIAS_CTRL_MIC1AMPEN, 0, NULL, 0),
- SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL,
- SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0),
/* Mixers */
SND_SOC_DAPM_MIXER("Left Mixer", SUN8I_ADDA_DAC_PA_SRC,
@@ -335,35 +320,26 @@ static const struct snd_soc_dapm_widget sun8i_codec_common_widgets[] = {
static const struct snd_soc_dapm_route sun8i_codec_common_routes[] = {
/* Microphone Routes */
{ "Mic1 Amplifier", NULL, "MIC1"},
- { "Mic2 Amplifier", NULL, "MIC2"},
/* Left Mixer Routes */
{ "Left Mixer", "DAC Playback Switch", "Left DAC" },
{ "Left Mixer", "DAC Reversed Playback Switch", "Right DAC" },
- { "Left Mixer", "Line In Playback Switch", "LINEIN" },
{ "Left Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
- { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
/* Right Mixer Routes */
{ "Right Mixer", "DAC Playback Switch", "Right DAC" },
{ "Right Mixer", "DAC Reversed Playback Switch", "Left DAC" },
- { "Right Mixer", "Line In Playback Switch", "LINEIN" },
{ "Right Mixer", "Mic1 Playback Switch", "Mic1 Amplifier" },
- { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
/* Left ADC Mixer Routes */
{ "Left ADC Mixer", "Mixer Capture Switch", "Left Mixer" },
{ "Left ADC Mixer", "Mixer Reversed Capture Switch", "Right Mixer" },
- { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
{ "Left ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
- { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
/* Right ADC Mixer Routes */
{ "Right ADC Mixer", "Mixer Capture Switch", "Right Mixer" },
{ "Right ADC Mixer", "Mixer Reversed Capture Switch", "Left Mixer" },
- { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
{ "Right ADC Mixer", "Mic1 Capture Switch", "Mic1 Amplifier" },
- { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
/* ADC Routes */
{ "Left ADC", NULL, "Left ADC Mixer" },
@@ -498,6 +474,61 @@ static int sun8i_codec_add_hmic(struct snd_soc_component *cmpnt)
return ret;
}
+/* line in specific controls, widgets and rines */
+static const struct snd_kcontrol_new sun8i_codec_linein_controls[] = {
+ /* Mixer pre-gain */
+ SOC_SINGLE_TLV("Line In Playback Volume", SUN8I_ADDA_LINEIN_GCTRL,
+ SUN8I_ADDA_LINEIN_GCTRL_LINEING,
+ 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
+};
+
+static const struct snd_soc_dapm_widget sun8i_codec_linein_widgets[] = {
+ /* Line input */
+ SND_SOC_DAPM_INPUT("LINEIN"),
+};
+
+static const struct snd_soc_dapm_route sun8i_codec_linein_routes[] = {
+ { "Left Mixer", "Line In Playback Switch", "LINEIN" },
+
+ { "Right Mixer", "Line In Playback Switch", "LINEIN" },
+
+ { "Left ADC Mixer", "Line In Capture Switch", "LINEIN" },
+
+ { "Right ADC Mixer", "Line In Capture Switch", "LINEIN" },
+};
+
+static int sun8i_codec_add_linein(struct snd_soc_component *cmpnt)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
+ struct device *dev = cmpnt->dev;
+ int ret;
+
+ ret = snd_soc_add_component_controls(cmpnt,
+ sun8i_codec_linein_controls,
+ ARRAY_SIZE(sun8i_codec_linein_controls));
+ if (ret) {
+ dev_err(dev, "Failed to add Line In controls: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_linein_widgets,
+ ARRAY_SIZE(sun8i_codec_linein_widgets));
+ if (ret) {
+ dev_err(dev, "Failed to add Line In DAPM widgets: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_linein_routes,
+ ARRAY_SIZE(sun8i_codec_linein_routes));
+ if (ret) {
+ dev_err(dev, "Failed to add Line In DAPM routes: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
+
/* line out specific controls, widgets and routes */
static const DECLARE_TLV_DB_RANGE(sun8i_codec_lineout_vol_scale,
0, 1, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
@@ -578,19 +609,90 @@ static int sun8i_codec_add_lineout(struct snd_soc_component *cmpnt)
return 0;
}
+/* mic2 specific controls, widgets and routes */
+static const struct snd_kcontrol_new sun8i_codec_mic2_controls[] = {
+ /* Mixer pre-gain */
+ SOC_SINGLE_TLV("Mic2 Playback Volume",
+ SUN8I_ADDA_MICIN_GCTRL, SUN8I_ADDA_MICIN_GCTRL_MIC2G,
+ 0x7, 0, sun8i_codec_out_mixer_pregain_scale),
+
+ /* Microphone Amp boost gain */
+ SOC_SINGLE_TLV("Mic2 Boost Volume", SUN8I_ADDA_MIC2G_CTRL,
+ SUN8I_ADDA_MIC2G_CTRL_MIC2BOOST, 0x7, 0,
+ sun8i_codec_mic_gain_scale),
+};
+
+static const struct snd_soc_dapm_widget sun8i_codec_mic2_widgets[] = {
+ /* Microphone input */
+ SND_SOC_DAPM_INPUT("MIC2"),
+
+ /* Mic input path */
+ SND_SOC_DAPM_PGA("Mic2 Amplifier", SUN8I_ADDA_MIC2G_CTRL,
+ SUN8I_ADDA_MIC2G_CTRL_MIC2AMPEN, 0, NULL, 0),
+};
+
+static const struct snd_soc_dapm_route sun8i_codec_mic2_routes[] = {
+ { "Mic2 Amplifier", NULL, "MIC2"},
+
+ { "Left Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
+
+ { "Right Mixer", "Mic2 Playback Switch", "Mic2 Amplifier" },
+
+ { "Left ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
+
+ { "Right ADC Mixer", "Mic2 Capture Switch", "Mic2 Amplifier" },
+};
+
+static int sun8i_codec_add_mic2(struct snd_soc_component *cmpnt)
+{
+ struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(cmpnt);
+ struct device *dev = cmpnt->dev;
+ int ret;
+
+ ret = snd_soc_add_component_controls(cmpnt,
+ sun8i_codec_mic2_controls,
+ ARRAY_SIZE(sun8i_codec_mic2_controls));
+ if (ret) {
+ dev_err(dev, "Failed to add MIC2 controls: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_new_controls(dapm, sun8i_codec_mic2_widgets,
+ ARRAY_SIZE(sun8i_codec_mic2_widgets));
+ if (ret) {
+ dev_err(dev, "Failed to add MIC2 DAPM widgets: %d\n", ret);
+ return ret;
+ }
+
+ ret = snd_soc_dapm_add_routes(dapm, sun8i_codec_mic2_routes,
+ ARRAY_SIZE(sun8i_codec_mic2_routes));
+ if (ret) {
+ dev_err(dev, "Failed to add MIC2 DAPM routes: %d\n", ret);
+ return ret;
+ }
+
+ return 0;
+}
+
struct sun8i_codec_analog_quirks {
bool has_headphone;
bool has_hmic;
+ bool has_linein;
bool has_lineout;
+ bool has_mic2;
};
static const struct sun8i_codec_analog_quirks sun8i_a23_quirks = {
.has_headphone = true,
.has_hmic = true,
+ .has_linein = true,
+ .has_mic2 = true,
};
static const struct sun8i_codec_analog_quirks sun8i_h3_quirks = {
+ .has_linein = true,
.has_lineout = true,
+ .has_mic2 = true,
};
static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
@@ -620,12 +722,24 @@ static int sun8i_codec_analog_cmpnt_probe(struct snd_soc_component *cmpnt)
return ret;
}
+ if (quirks->has_linein) {
+ ret = sun8i_codec_add_linein(cmpnt);
+ if (ret)
+ return ret;
+ }
+
if (quirks->has_lineout) {
ret = sun8i_codec_add_lineout(cmpnt);
if (ret)
return ret;
}
+ if (quirks->has_mic2) {
+ ret = sun8i_codec_add_mic2(cmpnt);
+ if (ret)
+ return ret;
+ }
+
return 0;
}
diff --git a/sound/soc/sunxi/sun8i-codec.c b/sound/soc/sunxi/sun8i-codec.c
index 7527ba29a5a0..5723c3404f6b 100644
--- a/sound/soc/sunxi/sun8i-codec.c
+++ b/sound/soc/sunxi/sun8i-codec.c
@@ -290,12 +290,10 @@ static const struct snd_soc_dapm_widget sun8i_codec_dapm_widgets[] = {
SUN8I_AIF1_DACDAT_CTRL_AIF1_DA0R_ENA, 0),
/* DAC Mixers */
- SND_SOC_DAPM_MIXER("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
- sun8i_dac_mixer_controls,
- ARRAY_SIZE(sun8i_dac_mixer_controls)),
- SND_SOC_DAPM_MIXER("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
- sun8i_dac_mixer_controls,
- ARRAY_SIZE(sun8i_dac_mixer_controls)),
+ SOC_MIXER_ARRAY("Left Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
+ sun8i_dac_mixer_controls),
+ SOC_MIXER_ARRAY("Right Digital DAC Mixer", SND_SOC_NOPM, 0, 0,
+ sun8i_dac_mixer_controls),
/* Clocks */
SND_SOC_DAPM_SUPPLY("MODCLK AFI1", SUN8I_MOD_CLK_ENA,
diff --git a/sound/soc/tegra/tegra20_ac97.c b/sound/soc/tegra/tegra20_ac97.c
index a68368edab9c..affad46bf188 100644
--- a/sound/soc/tegra/tegra20_ac97.c
+++ b/sound/soc/tegra/tegra20_ac97.c
@@ -318,7 +318,6 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
ac97 = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_ac97),
GFP_KERNEL);
if (!ac97) {
- dev_err(&pdev->dev, "Can't allocate tegra20_ac97\n");
ret = -ENOMEM;
goto err;
}
diff --git a/sound/soc/tegra/tegra20_das.c b/sound/soc/tegra/tegra20_das.c
index 89add13c31cf..4024e3abbeed 100644
--- a/sound/soc/tegra/tegra20_das.c
+++ b/sound/soc/tegra/tegra20_das.c
@@ -41,6 +41,7 @@ static inline void tegra20_das_write(u32 reg, u32 val)
static inline u32 tegra20_das_read(u32 reg)
{
u32 val;
+
regmap_read(das->regmap, reg, &val);
return val;
}
@@ -142,7 +143,6 @@ static int tegra20_das_probe(struct platform_device *pdev)
das = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_das), GFP_KERNEL);
if (!das) {
- dev_err(&pdev->dev, "Can't allocate tegra20_das\n");
ret = -ENOMEM;
goto err;
}
diff --git a/sound/soc/tegra/tegra20_i2s.c b/sound/soc/tegra/tegra20_i2s.c
index 14106fa82bca..26253c2849e7 100644
--- a/sound/soc/tegra/tegra20_i2s.c
+++ b/sound/soc/tegra/tegra20_i2s.c
@@ -345,7 +345,6 @@ static int tegra20_i2s_platform_probe(struct platform_device *pdev)
i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_i2s), GFP_KERNEL);
if (!i2s) {
- dev_err(&pdev->dev, "Can't allocate tegra20_i2s\n");
ret = -ENOMEM;
goto err;
}
diff --git a/sound/soc/tegra/tegra20_spdif.c b/sound/soc/tegra/tegra20_spdif.c
index a0c3640572b9..767c0491e11a 100644
--- a/sound/soc/tegra/tegra20_spdif.c
+++ b/sound/soc/tegra/tegra20_spdif.c
@@ -271,10 +271,9 @@ static int tegra20_spdif_platform_probe(struct platform_device *pdev)
spdif = devm_kzalloc(&pdev->dev, sizeof(struct tegra20_spdif),
GFP_KERNEL);
- if (!spdif) {
- dev_err(&pdev->dev, "Can't allocate tegra20_spdif\n");
+ if (!spdif)
return -ENOMEM;
- }
+
dev_set_drvdata(&pdev->dev, spdif);
spdif->clk_spdif_out = devm_clk_get(&pdev->dev, "spdif_out");
diff --git a/sound/soc/tegra/tegra30_ahub.c b/sound/soc/tegra/tegra30_ahub.c
index fef3b9a21a66..8c10ae7982ba 100644
--- a/sound/soc/tegra/tegra30_ahub.c
+++ b/sound/soc/tegra/tegra30_ahub.c
@@ -41,6 +41,7 @@ static inline void tegra30_apbif_write(u32 reg, u32 val)
static inline u32 tegra30_apbif_read(u32 reg)
{
u32 val;
+
regmap_read(ahub->regmap_apbif, reg, &val);
return val;
}
@@ -560,10 +561,8 @@ static int tegra30_ahub_probe(struct platform_device *pdev)
ahub = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_ahub),
GFP_KERNEL);
- if (!ahub) {
- dev_err(&pdev->dev, "Can't allocate tegra30_ahub\n");
+ if (!ahub)
return -ENOMEM;
- }
dev_set_drvdata(&pdev->dev, ahub);
ahub->soc_data = soc_data;
diff --git a/sound/soc/tegra/tegra30_i2s.c b/sound/soc/tegra/tegra30_i2s.c
index 8e55583aa104..b2b279c96029 100644
--- a/sound/soc/tegra/tegra30_i2s.c
+++ b/sound/soc/tegra/tegra30_i2s.c
@@ -385,7 +385,6 @@ static int tegra30_i2s_platform_probe(struct platform_device *pdev)
i2s = devm_kzalloc(&pdev->dev, sizeof(struct tegra30_i2s), GFP_KERNEL);
if (!i2s) {
- dev_err(&pdev->dev, "Can't allocate tegra30_i2s\n");
ret = -ENOMEM;
goto err;
}
diff --git a/sound/soc/tegra/tegra_alc5632.c b/sound/soc/tegra/tegra_alc5632.c
index eead6e7f205b..0509902512cc 100644
--- a/sound/soc/tegra/tegra_alc5632.c
+++ b/sound/soc/tegra/tegra_alc5632.c
@@ -169,10 +169,8 @@ static int tegra_alc5632_probe(struct platform_device *pdev)
alc5632 = devm_kzalloc(&pdev->dev,
sizeof(struct tegra_alc5632), GFP_KERNEL);
- if (!alc5632) {
- dev_err(&pdev->dev, "Can't allocate tegra_alc5632\n");
+ if (!alc5632)
return -ENOMEM;
- }
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/tegra_max98090.c b/sound/soc/tegra/tegra_max98090.c
index a403db6d563e..c34a54d6e812 100644
--- a/sound/soc/tegra/tegra_max98090.c
+++ b/sound/soc/tegra/tegra_max98090.c
@@ -225,10 +225,8 @@ static int tegra_max98090_probe(struct platform_device *pdev)
machine = devm_kzalloc(&pdev->dev,
sizeof(struct tegra_max98090), GFP_KERNEL);
- if (!machine) {
- dev_err(&pdev->dev, "Can't allocate tegra_max98090\n");
+ if (!machine)
return -ENOMEM;
- }
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/tegra_rt5640.c b/sound/soc/tegra/tegra_rt5640.c
index 25b9fc03ba62..93a356802345 100644
--- a/sound/soc/tegra/tegra_rt5640.c
+++ b/sound/soc/tegra/tegra_rt5640.c
@@ -170,10 +170,8 @@ static int tegra_rt5640_probe(struct platform_device *pdev)
machine = devm_kzalloc(&pdev->dev,
sizeof(struct tegra_rt5640), GFP_KERNEL);
- if (!machine) {
- dev_err(&pdev->dev, "Can't allocate tegra_rt5640\n");
+ if (!machine)
return -ENOMEM;
- }
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/tegra_sgtl5000.c b/sound/soc/tegra/tegra_sgtl5000.c
index 4bbab098f50b..6dda01f69983 100644
--- a/sound/soc/tegra/tegra_sgtl5000.c
+++ b/sound/soc/tegra/tegra_sgtl5000.c
@@ -120,10 +120,8 @@ static int tegra_sgtl5000_driver_probe(struct platform_device *pdev)
machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_sgtl5000),
GFP_KERNEL);
- if (!machine) {
- dev_err(&pdev->dev, "Can't allocate tegra_sgtl5000 struct\n");
+ if (!machine)
return -ENOMEM;
- }
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/tegra_wm8753.c b/sound/soc/tegra/tegra_wm8753.c
index bdedd1028569..d0ab0026a4cd 100644
--- a/sound/soc/tegra/tegra_wm8753.c
+++ b/sound/soc/tegra/tegra_wm8753.c
@@ -128,10 +128,8 @@ static int tegra_wm8753_driver_probe(struct platform_device *pdev)
machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8753),
GFP_KERNEL);
- if (!machine) {
- dev_err(&pdev->dev, "Can't allocate tegra_wm8753 struct\n");
+ if (!machine)
return -ENOMEM;
- }
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/tegra_wm8903.c b/sound/soc/tegra/tegra_wm8903.c
index 2013e9c4bba0..dbfb49298ae8 100644
--- a/sound/soc/tegra/tegra_wm8903.c
+++ b/sound/soc/tegra/tegra_wm8903.c
@@ -248,10 +248,8 @@ static int tegra_wm8903_driver_probe(struct platform_device *pdev)
machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm8903),
GFP_KERNEL);
- if (!machine) {
- dev_err(&pdev->dev, "Can't allocate tegra_wm8903 struct\n");
+ if (!machine)
return -ENOMEM;
- }
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/tegra_wm9712.c b/sound/soc/tegra/tegra_wm9712.c
index 6492f8143ff1..c9cd22432627 100644
--- a/sound/soc/tegra/tegra_wm9712.c
+++ b/sound/soc/tegra/tegra_wm9712.c
@@ -77,10 +77,8 @@ static int tegra_wm9712_driver_probe(struct platform_device *pdev)
machine = devm_kzalloc(&pdev->dev, sizeof(struct tegra_wm9712),
GFP_KERNEL);
- if (!machine) {
- dev_err(&pdev->dev, "Can't allocate tegra_wm9712 struct\n");
+ if (!machine)
return -ENOMEM;
- }
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
diff --git a/sound/soc/tegra/trimslice.c b/sound/soc/tegra/trimslice.c
index 870f84ab5005..c9dcad9bb931 100644
--- a/sound/soc/tegra/trimslice.c
+++ b/sound/soc/tegra/trimslice.c
@@ -123,10 +123,8 @@ static int tegra_snd_trimslice_probe(struct platform_device *pdev)
trimslice = devm_kzalloc(&pdev->dev, sizeof(struct tegra_trimslice),
GFP_KERNEL);
- if (!trimslice) {
- dev_err(&pdev->dev, "Can't allocate tegra_trimslice\n");
+ if (!trimslice)
return -ENOMEM;
- }
card->dev = &pdev->dev;
platform_set_drvdata(pdev, card);
diff --git a/sound/soc/txx9/txx9aclc.c b/sound/soc/txx9/txx9aclc.c
index a8f705bb60dc..7912bf09dc4d 100644
--- a/sound/soc/txx9/txx9aclc.c
+++ b/sound/soc/txx9/txx9aclc.c
@@ -206,7 +206,7 @@ static void txx9aclc_dma_tasklet(unsigned long data)
static int txx9aclc_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
{
struct txx9aclc_dmadata *dmadata = substream->runtime->private_data;
- struct txx9aclc_plat_drvdata *drvdata =txx9aclc_drvdata;
+ struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata;
void __iomem *base = drvdata->base;
unsigned long flags;
int ret = 0;
@@ -340,7 +340,7 @@ static bool filter(struct dma_chan *chan, void *param)
static int txx9aclc_dma_init(struct txx9aclc_soc_device *dev,
struct txx9aclc_dmadata *dmadata)
{
- struct txx9aclc_plat_drvdata *drvdata =txx9aclc_drvdata;
+ struct txx9aclc_plat_drvdata *drvdata = txx9aclc_drvdata;
struct txx9dmac_slave *ds = &dmadata->dma_slave;
dma_cap_mask_t mask;
@@ -392,6 +392,7 @@ static int txx9aclc_pcm_remove(struct snd_soc_platform *platform)
for (i = 0; i < 2; i++) {
struct txx9aclc_dmadata *dmadata = &dev->dmadata[i];
struct dma_chan *chan = dmadata->dma_chan;
+
if (chan) {
dmadata->frag_count = -1;
dmaengine_terminate_all(chan);
diff --git a/sound/soc/ux500/mop500.c b/sound/soc/ux500/mop500.c
index ba9fc099cf67..b50f68a439ce 100644
--- a/sound/soc/ux500/mop500.c
+++ b/sound/soc/ux500/mop500.c
@@ -33,7 +33,6 @@ static struct snd_soc_dai_link mop500_dai_links[] = {
.stream_name = "ab8500_0",
.cpu_dai_name = "ux500-msp-i2s.1",
.codec_dai_name = "ab8500-codec-dai.0",
- .platform_name = "ux500-msp-i2s.1",
.codec_name = "ab8500-codec.0",
.init = mop500_ab8500_machine_init,
.ops = mop500_ab8500_ops,
@@ -43,7 +42,6 @@ static struct snd_soc_dai_link mop500_dai_links[] = {
.stream_name = "ab8500_1",
.cpu_dai_name = "ux500-msp-i2s.3",
.codec_dai_name = "ab8500-codec-dai.1",
- .platform_name = "ux500-msp-i2s.3",
.codec_name = "ab8500-codec.0",
.init = NULL,
.ops = mop500_ab8500_ops,
@@ -87,8 +85,6 @@ static int mop500_of_probe(struct platform_device *pdev,
for (i = 0; i < 2; i++) {
mop500_dai_links[i].cpu_of_node = msp_np[i];
mop500_dai_links[i].cpu_dai_name = NULL;
- mop500_dai_links[i].platform_of_node = msp_np[i];
- mop500_dai_links[i].platform_name = NULL;
mop500_dai_links[i].codec_of_node = codec_np;
mop500_dai_links[i].codec_name = NULL;
}
diff --git a/sound/soc/ux500/ux500_msp_dai.c b/sound/soc/ux500/ux500_msp_dai.c
index b343efd9be5b..ec5152aa3f6e 100644
--- a/sound/soc/ux500/ux500_msp_dai.c
+++ b/sound/soc/ux500/ux500_msp_dai.c
@@ -133,6 +133,7 @@ static int setup_pcm_framing(struct snd_soc_dai *dai, unsigned int rate,
struct ux500_msp_i2s_drvdata *drvdata = dev_get_drvdata(dai->dev);
u32 frame_length = MSP_FRAME_LEN_1;
+
prot_desc->frame_width = 0;
switch (drvdata->slots) {
@@ -482,7 +483,8 @@ static int ux500_msp_dai_prepare(struct snd_pcm_substream *substream,
if ((drvdata->fmt & SND_SOC_DAIFMT_MASTER_MASK) &&
(drvdata->msp->f_bitclk > 19200000)) {
/* If the bit-clock is higher than 19.2MHz, Vape should be
- * run in 100% OPP. Only when bit-clock is used (MSP master) */
+ * run in 100% OPP. Only when bit-clock is used (MSP master)
+ */
prcmu_qos_update_requirement(PRCMU_QOS_APE_OPP,
"ux500-msp-i2s", 100);
drvdata->vape_opp_constraint = 1;
diff --git a/sound/soc/ux500/ux500_msp_i2s.c b/sound/soc/ux500/ux500_msp_i2s.c
index 959d7b4edf56..bd5266aca0f1 100644
--- a/sound/soc/ux500/ux500_msp_i2s.c
+++ b/sound/soc/ux500/ux500_msp_i2s.c
@@ -604,7 +604,6 @@ int ux500_msp_i2s_trigger(struct ux500_msp *msp, int cmd, int direction)
break;
default:
return -EINVAL;
- break;
}
return 0;
diff --git a/sound/soc/zte/Kconfig b/sound/soc/zte/Kconfig
index 6d8a90d36315..75f67a5d23ea 100644
--- a/sound/soc/zte/Kconfig
+++ b/sound/soc/zte/Kconfig
@@ -15,3 +15,11 @@ config ZX_I2S
help
Say Y or M if you want to add support for codecs attached to the
ZTE ZX I2S interface
+
+config ZX_TDM
+ tristate "ZTE ZX TDM Driver Support"
+ depends on COMMON_CLK
+ select SND_SOC_GENERIC_DMAENGINE_PCM
+ help
+ Say Y or M if you want to add support for codecs attached to the
+ ZTE ZX TDM interface
diff --git a/sound/soc/zte/Makefile b/sound/soc/zte/Makefile
index 77768f5fd10c..1fc841acdfdd 100644
--- a/sound/soc/zte/Makefile
+++ b/sound/soc/zte/Makefile
@@ -1,2 +1,3 @@
obj-$(CONFIG_ZX_SPDIF) += zx-spdif.o
obj-$(CONFIG_ZX_I2S) += zx-i2s.o
+obj-$(CONFIG_ZX_TDM) += zx-tdm.o
diff --git a/sound/soc/zte/zx-tdm.c b/sound/soc/zte/zx-tdm.c
new file mode 100644
index 000000000000..bd632cc503b3
--- /dev/null
+++ b/sound/soc/zte/zx-tdm.c
@@ -0,0 +1,461 @@
+/*
+ * ZTE's TDM driver
+ *
+ * Copyright (C) 2017 ZTE Ltd
+ *
+ * Author: Baoyou Xie <baoyou.xie@linaro.org>
+ *
+ * License terms: GNU General Public License (GPL) version 2
+ */
+
+#include <linux/clk.h>
+#include <linux/io.h>
+#include <linux/mfd/syscon.h>
+#include <linux/module.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+
+#define REG_TIMING_CTRL 0x04
+#define REG_TX_FIFO_CTRL 0x0C
+#define REG_RX_FIFO_CTRL 0x10
+#define REG_INT_EN 0x1C
+#define REG_INT_STATUS 0x20
+#define REG_DATABUF 0x24
+#define REG_TS_MASK0 0x44
+#define REG_PROCESS_CTRL 0x54
+
+#define FIFO_CTRL_TX_RST BIT(0)
+#define FIFO_CTRL_RX_RST BIT(0)
+#define DEAGULT_FIFO_THRES GENMASK(4, 2)
+
+#define FIFO_CTRL_TX_DMA_EN BIT(1)
+#define FIFO_CTRL_RX_DMA_EN BIT(1)
+
+#define TX_FIFO_RST_MASK BIT(0)
+#define RX_FIFO_RST_MASK BIT(0)
+
+#define FIFOCTRL_TX_FIFO_RST BIT(0)
+#define FIFOCTRL_RX_FIFO_RST BIT(0)
+
+#define TXTH_MASK GENMASK(5, 2)
+#define RXTH_MASK GENMASK(5, 2)
+
+#define FIFOCTRL_THRESHOLD(x) ((x) << 2)
+
+#define TIMING_MS_MASK BIT(1)
+/*
+ * 00: 8 clk cycles every timeslot
+ * 01: 16 clk cycles every timeslot
+ * 10: 32 clk cycles every timeslot
+ */
+#define TIMING_SYNC_WIDTH_MASK GENMASK(6, 5)
+#define TIMING_WIDTH_SHIFT 5
+#define TIMING_DEFAULT_WIDTH 0
+#define TIMING_TS_WIDTH(x) ((x) << TIMING_WIDTH_SHIFT)
+#define TIMING_WIDTH_FACTOR 8
+
+#define TIMING_MASTER_MODE BIT(21)
+#define TIMING_LSB_FIRST BIT(20)
+#define TIMING_TS_NUM(x) (((x) - 1) << 7)
+#define TIMING_CLK_SEL_MASK GENMASK(2, 0)
+#define TIMING_CLK_SEL_DEF BIT(2)
+
+#define PROCESS_TX_EN BIT(0)
+#define PROCESS_RX_EN BIT(1)
+#define PROCESS_TDM_EN BIT(2)
+#define PROCESS_DISABLE_ALL 0
+
+#define INT_DISABLE_ALL 0
+#define INT_STATUS_MASK GENMASK(6, 0)
+
+struct zx_tdm_info {
+ struct snd_dmaengine_dai_dma_data dma_playback;
+ struct snd_dmaengine_dai_dma_data dma_capture;
+ resource_size_t phy_addr;
+ void __iomem *regbase;
+ struct clk *dai_wclk;
+ struct clk *dai_pclk;
+ int master;
+ struct device *dev;
+};
+
+static inline u32 zx_tdm_readl(struct zx_tdm_info *tdm, u16 reg)
+{
+ return readl_relaxed(tdm->regbase + reg);
+}
+
+static inline void zx_tdm_writel(struct zx_tdm_info *tdm, u16 reg, u32 val)
+{
+ writel_relaxed(val, tdm->regbase + reg);
+}
+
+static void zx_tdm_tx_en(struct zx_tdm_info *tdm, bool on)
+{
+ unsigned long val;
+
+ val = zx_tdm_readl(tdm, REG_PROCESS_CTRL);
+ if (on)
+ val |= PROCESS_TX_EN | PROCESS_TDM_EN;
+ else
+ val &= ~(PROCESS_TX_EN | PROCESS_TDM_EN);
+ zx_tdm_writel(tdm, REG_PROCESS_CTRL, val);
+}
+
+static void zx_tdm_rx_en(struct zx_tdm_info *tdm, bool on)
+{
+ unsigned long val;
+
+ val = zx_tdm_readl(tdm, REG_PROCESS_CTRL);
+ if (on)
+ val |= PROCESS_RX_EN | PROCESS_TDM_EN;
+ else
+ val &= ~(PROCESS_RX_EN | PROCESS_TDM_EN);
+ zx_tdm_writel(tdm, REG_PROCESS_CTRL, val);
+}
+
+static void zx_tdm_tx_dma_en(struct zx_tdm_info *tdm, bool on)
+{
+ unsigned long val;
+
+ val = zx_tdm_readl(tdm, REG_TX_FIFO_CTRL);
+ val |= FIFO_CTRL_TX_RST | DEAGULT_FIFO_THRES;
+ if (on)
+ val |= FIFO_CTRL_TX_DMA_EN;
+ else
+ val &= ~FIFO_CTRL_TX_DMA_EN;
+ zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, val);
+}
+
+static void zx_tdm_rx_dma_en(struct zx_tdm_info *tdm, bool on)
+{
+ unsigned long val;
+
+ val = zx_tdm_readl(tdm, REG_RX_FIFO_CTRL);
+ val |= FIFO_CTRL_RX_RST | DEAGULT_FIFO_THRES;
+ if (on)
+ val |= FIFO_CTRL_RX_DMA_EN;
+ else
+ val &= ~FIFO_CTRL_RX_DMA_EN;
+ zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, val);
+}
+
+#define ZX_TDM_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000)
+
+#define ZX_TDM_FMTBIT \
+ (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FORMAT_MU_LAW | \
+ SNDRV_PCM_FORMAT_A_LAW)
+
+static int zx_tdm_dai_probe(struct snd_soc_dai *dai)
+{
+ struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
+
+ snd_soc_dai_set_drvdata(dai, zx_tdm);
+ zx_tdm->dma_playback.addr = zx_tdm->phy_addr + REG_DATABUF;
+ zx_tdm->dma_playback.maxburst = 16;
+ zx_tdm->dma_capture.addr = zx_tdm->phy_addr + REG_DATABUF;
+ zx_tdm->dma_capture.maxburst = 16;
+ snd_soc_dai_init_dma_data(dai, &zx_tdm->dma_playback,
+ &zx_tdm->dma_capture);
+ return 0;
+}
+
+static int zx_tdm_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+ struct zx_tdm_info *tdm = snd_soc_dai_get_drvdata(cpu_dai);
+ unsigned long val;
+
+ val = zx_tdm_readl(tdm, REG_TIMING_CTRL);
+ val &= ~(TIMING_SYNC_WIDTH_MASK | TIMING_MS_MASK);
+ val |= TIMING_DEFAULT_WIDTH << TIMING_WIDTH_SHIFT;
+
+ switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+ case SND_SOC_DAIFMT_CBM_CFM:
+ tdm->master = 1;
+ val |= TIMING_MASTER_MODE;
+ break;
+ case SND_SOC_DAIFMT_CBS_CFS:
+ tdm->master = 0;
+ val &= ~TIMING_MASTER_MODE;
+ break;
+ default:
+ dev_err(cpu_dai->dev, "Unknown master/slave format\n");
+ return -EINVAL;
+ }
+
+
+ zx_tdm_writel(tdm, REG_TIMING_CTRL, val);
+
+ return 0;
+}
+
+static int zx_tdm_hw_params(struct snd_pcm_substream *substream,
+ struct snd_pcm_hw_params *params,
+ struct snd_soc_dai *socdai)
+{
+ struct zx_tdm_info *tdm = snd_soc_dai_get_drvdata(socdai);
+ struct snd_dmaengine_dai_dma_data *dma_data;
+ unsigned int ts_width = TIMING_DEFAULT_WIDTH;
+ unsigned int ch_num = 32;
+ unsigned int mask = 0;
+ unsigned int ret = 0;
+ unsigned long val;
+
+ dma_data = snd_soc_dai_get_dma_data(socdai, substream);
+ dma_data->addr_width = ch_num >> 3;
+
+ switch (params_format(params)) {
+ case SNDRV_PCM_FORMAT_MU_LAW:
+ case SNDRV_PCM_FORMAT_A_LAW:
+ case SNDRV_PCM_FORMAT_S16_LE:
+ ts_width = 1;
+ break;
+ default:
+ ts_width = 0;
+ dev_err(socdai->dev, "Unknown data format\n");
+ return -EINVAL;
+ }
+
+ val = zx_tdm_readl(tdm, REG_TIMING_CTRL);
+ val |= TIMING_TS_WIDTH(ts_width) | TIMING_TS_NUM(1);
+ zx_tdm_writel(tdm, REG_TIMING_CTRL, val);
+ zx_tdm_writel(tdm, REG_TS_MASK0, mask);
+
+ if (tdm->master)
+ ret = clk_set_rate(tdm->dai_wclk,
+ params_rate(params) * TIMING_WIDTH_FACTOR * ch_num);
+
+ return ret;
+}
+
+static int zx_tdm_trigger(struct snd_pcm_substream *substream, int cmd,
+ struct snd_soc_dai *dai)
+{
+ int capture = (substream->stream == SNDRV_PCM_STREAM_CAPTURE);
+ struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
+ unsigned int val;
+ int ret = 0;
+
+ switch (cmd) {
+ case SNDRV_PCM_TRIGGER_START:
+ if (capture) {
+ val = zx_tdm_readl(zx_tdm, REG_RX_FIFO_CTRL);
+ val |= FIFOCTRL_RX_FIFO_RST;
+ zx_tdm_writel(zx_tdm, REG_RX_FIFO_CTRL, val);
+
+ zx_tdm_rx_dma_en(zx_tdm, true);
+ } else {
+ val = zx_tdm_readl(zx_tdm, REG_TX_FIFO_CTRL);
+ val |= FIFOCTRL_TX_FIFO_RST;
+ zx_tdm_writel(zx_tdm, REG_TX_FIFO_CTRL, val);
+
+ zx_tdm_tx_dma_en(zx_tdm, true);
+ }
+ break;
+ case SNDRV_PCM_TRIGGER_RESUME:
+ case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+ if (capture)
+ zx_tdm_rx_en(zx_tdm, true);
+ else
+ zx_tdm_tx_en(zx_tdm, true);
+ break;
+ case SNDRV_PCM_TRIGGER_STOP:
+ if (capture)
+ zx_tdm_rx_dma_en(zx_tdm, false);
+ else
+ zx_tdm_tx_dma_en(zx_tdm, false);
+ break;
+ case SNDRV_PCM_TRIGGER_SUSPEND:
+ case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+ if (capture)
+ zx_tdm_rx_en(zx_tdm, false);
+ else
+ zx_tdm_tx_en(zx_tdm, false);
+ break;
+ default:
+ ret = -EINVAL;
+ break;
+ }
+
+ return ret;
+}
+
+static int zx_tdm_startup(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
+ int ret;
+
+ ret = clk_prepare_enable(zx_tdm->dai_wclk);
+ if (ret)
+ return ret;
+
+ ret = clk_prepare_enable(zx_tdm->dai_pclk);
+ if (ret) {
+ clk_disable_unprepare(zx_tdm->dai_wclk);
+ return ret;
+ }
+
+ return 0;
+}
+
+static void zx_tdm_shutdown(struct snd_pcm_substream *substream,
+ struct snd_soc_dai *dai)
+{
+ struct zx_tdm_info *zx_tdm = dev_get_drvdata(dai->dev);
+
+ clk_disable_unprepare(zx_tdm->dai_pclk);
+ clk_disable_unprepare(zx_tdm->dai_wclk);
+}
+
+static struct snd_soc_dai_ops zx_tdm_dai_ops = {
+ .trigger = zx_tdm_trigger,
+ .hw_params = zx_tdm_hw_params,
+ .set_fmt = zx_tdm_set_fmt,
+ .startup = zx_tdm_startup,
+ .shutdown = zx_tdm_shutdown,
+};
+
+static const struct snd_soc_component_driver zx_tdm_component = {
+ .name = "zx-tdm",
+};
+
+static void zx_tdm_init_state(struct zx_tdm_info *tdm)
+{
+ unsigned int val;
+
+ zx_tdm_writel(tdm, REG_PROCESS_CTRL, PROCESS_DISABLE_ALL);
+
+ val = zx_tdm_readl(tdm, REG_TIMING_CTRL);
+ val |= TIMING_LSB_FIRST;
+ val &= ~TIMING_CLK_SEL_MASK;
+ val |= TIMING_CLK_SEL_DEF;
+ zx_tdm_writel(tdm, REG_TIMING_CTRL, val);
+
+ zx_tdm_writel(tdm, REG_INT_EN, INT_DISABLE_ALL);
+ /*
+ * write INT_STATUS register to clear it.
+ */
+ zx_tdm_writel(tdm, REG_INT_STATUS, INT_STATUS_MASK);
+ zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, FIFOCTRL_RX_FIFO_RST);
+ zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, FIFOCTRL_TX_FIFO_RST);
+
+ val = zx_tdm_readl(tdm, REG_RX_FIFO_CTRL);
+ val &= ~(RXTH_MASK | RX_FIFO_RST_MASK);
+ val |= FIFOCTRL_THRESHOLD(8);
+ zx_tdm_writel(tdm, REG_RX_FIFO_CTRL, val);
+
+ val = zx_tdm_readl(tdm, REG_TX_FIFO_CTRL);
+ val &= ~(TXTH_MASK | TX_FIFO_RST_MASK);
+ val |= FIFOCTRL_THRESHOLD(8);
+ zx_tdm_writel(tdm, REG_TX_FIFO_CTRL, val);
+}
+
+static struct snd_soc_dai_driver zx_tdm_dai = {
+ .name = "zx-tdm-dai",
+ .id = 0,
+ .probe = zx_tdm_dai_probe,
+ .playback = {
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = ZX_TDM_RATES,
+ .formats = ZX_TDM_FMTBIT,
+ },
+ .capture = {
+ .channels_min = 1,
+ .channels_max = 4,
+ .rates = ZX_TDM_RATES,
+ .formats = ZX_TDM_FMTBIT,
+ },
+ .ops = &zx_tdm_dai_ops,
+};
+
+static int zx_tdm_probe(struct platform_device *pdev)
+{
+ struct device *dev = &pdev->dev;
+ struct of_phandle_args out_args;
+ unsigned int dma_reg_offset;
+ struct zx_tdm_info *zx_tdm;
+ unsigned int dma_mask;
+ struct resource *res;
+ struct regmap *regmap_sysctrl;
+ int ret;
+
+ zx_tdm = devm_kzalloc(&pdev->dev, sizeof(*zx_tdm), GFP_KERNEL);
+ if (!zx_tdm)
+ return -ENOMEM;
+
+ zx_tdm->dev = dev;
+
+ zx_tdm->dai_wclk = devm_clk_get(&pdev->dev, "wclk");
+ if (IS_ERR(zx_tdm->dai_wclk)) {
+ dev_err(&pdev->dev, "Fail to get wclk\n");
+ return PTR_ERR(zx_tdm->dai_wclk);
+ }
+
+ zx_tdm->dai_pclk = devm_clk_get(&pdev->dev, "pclk");
+ if (IS_ERR(zx_tdm->dai_pclk)) {
+ dev_err(&pdev->dev, "Fail to get pclk\n");
+ return PTR_ERR(zx_tdm->dai_pclk);
+ }
+
+ res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+ zx_tdm->phy_addr = res->start;
+ zx_tdm->regbase = devm_ioremap_resource(&pdev->dev, res);
+ if (IS_ERR(zx_tdm->regbase))
+ return PTR_ERR(zx_tdm->regbase);
+
+ ret = of_parse_phandle_with_fixed_args(pdev->dev.of_node,
+ "zte,tdm-dma-sysctrl", 2, 0, &out_args);
+ if (ret) {
+ dev_err(&pdev->dev, "Fail to get zte,tdm-dma-sysctrl\n");
+ return ret;
+ }
+
+ dma_reg_offset = out_args.args[0];
+ dma_mask = out_args.args[1];
+ regmap_sysctrl = syscon_node_to_regmap(out_args.np);
+ if (IS_ERR(regmap_sysctrl)) {
+ of_node_put(out_args.np);
+ return PTR_ERR(regmap_sysctrl);
+ }
+
+ regmap_update_bits(regmap_sysctrl, dma_reg_offset, dma_mask, dma_mask);
+ of_node_put(out_args.np);
+
+ zx_tdm_init_state(zx_tdm);
+ platform_set_drvdata(pdev, zx_tdm);
+
+ ret = devm_snd_soc_register_component(&pdev->dev, &zx_tdm_component,
+ &zx_tdm_dai, 1);
+ if (ret) {
+ dev_err(&pdev->dev, "Register DAI failed: %d\n", ret);
+ return ret;
+ }
+
+ ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+ if (ret)
+ dev_err(&pdev->dev, "Register platform PCM failed: %d\n", ret);
+
+ return ret;
+}
+
+static const struct of_device_id zx_tdm_dt_ids[] = {
+ { .compatible = "zte,zx296718-tdm", },
+ {}
+};
+MODULE_DEVICE_TABLE(of, zx_tdm_dt_ids);
+
+static struct platform_driver tdm_driver = {
+ .probe = zx_tdm_probe,
+ .driver = {
+ .name = "zx-tdm",
+ .of_match_table = zx_tdm_dt_ids,
+ },
+};
+module_platform_driver(tdm_driver);
+
+MODULE_AUTHOR("Baoyou Xie <baoyou.xie@linaro.org>");
+MODULE_DESCRIPTION("ZTE TDM DAI driver");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/synth/emux/emux_oss.c b/sound/synth/emux/emux_oss.c
index ac75816ada7c..850fab4a8f3b 100644
--- a/sound/synth/emux/emux_oss.c
+++ b/sound/synth/emux/emux_oss.c
@@ -225,9 +225,9 @@ snd_emux_load_patch_seq_oss(struct snd_seq_oss_arg *arg, int format,
else if (format == SNDRV_OSS_SOUNDFONT_PATCH) {
struct soundfont_patch_info patch;
if (count < (int)sizeof(patch))
- rc = -EINVAL;
+ return -EINVAL;
if (copy_from_user(&patch, buf, sizeof(patch)))
- rc = -EFAULT;
+ return -EFAULT;
if (patch.type >= SNDRV_SFNT_LOAD_INFO &&
patch.type <= SNDRV_SFNT_PROBE_DATA)
rc = snd_soundfont_load(emu->sflist, buf, count, SF_CLIENT_NO(p->chset.port));
diff --git a/sound/usb/card.c b/sound/usb/card.c
index f36cb068dad3..6640277a725b 100644
--- a/sound/usb/card.c
+++ b/sound/usb/card.c
@@ -332,6 +332,7 @@ static int snd_usb_audio_dev_free(struct snd_device *device)
static int snd_usb_audio_create(struct usb_interface *intf,
struct usb_device *dev, int idx,
const struct snd_usb_audio_quirk *quirk,
+ unsigned int usb_id,
struct snd_usb_audio **rchip)
{
struct snd_card *card;
@@ -381,8 +382,7 @@ static int snd_usb_audio_create(struct usb_interface *intf,
atomic_set(&chip->usage_count, 0);
atomic_set(&chip->shutdown, 0);
- chip->usb_id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
- le16_to_cpu(dev->descriptor.idProduct));
+ chip->usb_id = usb_id;
INIT_LIST_HEAD(&chip->pcm_list);
INIT_LIST_HEAD(&chip->ep_list);
INIT_LIST_HEAD(&chip->midi_list);
@@ -569,7 +569,7 @@ static int usb_audio_probe(struct usb_interface *intf,
(vid[i] == -1 || vid[i] == USB_ID_VENDOR(id)) &&
(pid[i] == -1 || pid[i] == USB_ID_PRODUCT(id))) {
err = snd_usb_audio_create(intf, dev, i, quirk,
- &chip);
+ id, &chip);
if (err < 0)
goto __error;
chip->pm_intf = intf;
diff --git a/sound/usb/line6/pcm.c b/sound/usb/line6/pcm.c
index fab53f58d447..b3854f8c0c67 100644
--- a/sound/usb/line6/pcm.c
+++ b/sound/usb/line6/pcm.c
@@ -430,7 +430,7 @@ static int snd_line6_control_playback_put(struct snd_kcontrol *kcontrol,
}
/* control definition */
-static struct snd_kcontrol_new line6_controls[] = {
+static const struct snd_kcontrol_new line6_controls[] = {
{
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Playback Volume",
diff --git a/sound/usb/line6/pod.c b/sound/usb/line6/pod.c
index 17aa616e61f5..358224cc5638 100644
--- a/sound/usb/line6/pod.c
+++ b/sound/usb/line6/pod.c
@@ -380,7 +380,7 @@ static int snd_pod_control_monitor_put(struct snd_kcontrol *kcontrol,
}
/* control definition */
-static struct snd_kcontrol_new pod_control_monitor = {
+static const struct snd_kcontrol_new pod_control_monitor = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitor Playback Volume",
.index = 0,
diff --git a/sound/usb/line6/toneport.c b/sound/usb/line6/toneport.c
index 8e22f430d700..ba7975c0d03d 100644
--- a/sound/usb/line6/toneport.c
+++ b/sound/usb/line6/toneport.c
@@ -250,7 +250,7 @@ static void toneport_start_pcm(unsigned long arg)
}
/* control definition */
-static struct snd_kcontrol_new toneport_control_monitor = {
+static const struct snd_kcontrol_new toneport_control_monitor = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "Monitor Playback Volume",
.index = 0,
@@ -261,7 +261,7 @@ static struct snd_kcontrol_new toneport_control_monitor = {
};
/* source selector definition */
-static struct snd_kcontrol_new toneport_control_source = {
+static const struct snd_kcontrol_new toneport_control_source = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "PCM Capture Source",
.index = 0,
diff --git a/sound/usb/midi.c b/sound/usb/midi.c
index 6e763bc8d7db..a35f41467237 100644
--- a/sound/usb/midi.c
+++ b/sound/usb/midi.c
@@ -1922,7 +1922,7 @@ static int roland_load_put(struct snd_kcontrol *kcontrol,
return changed;
}
-static struct snd_kcontrol_new roland_load_ctl = {
+static const struct snd_kcontrol_new roland_load_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "MIDI Input Mode",
.info = roland_load_info,
diff --git a/sound/usb/mixer.c b/sound/usb/mixer.c
index 4703caea56b2..082736c539bc 100644
--- a/sound/usb/mixer.c
+++ b/sound/usb/mixer.c
@@ -1172,7 +1172,7 @@ static struct snd_kcontrol_new usb_feature_unit_ctl = {
};
/* the read-only variant */
-static struct snd_kcontrol_new usb_feature_unit_ctl_ro = {
+static const struct snd_kcontrol_new usb_feature_unit_ctl_ro = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later manually */
.info = mixer_ctl_feature_info,
@@ -1745,7 +1745,7 @@ static int mixer_ctl_procunit_put(struct snd_kcontrol *kcontrol,
}
/* alsa control interface for processing/extension unit */
-static struct snd_kcontrol_new mixer_procunit_ctl = {
+static const struct snd_kcontrol_new mixer_procunit_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later */
.info = mixer_ctl_feature_info,
@@ -2033,7 +2033,7 @@ static int mixer_ctl_selector_put(struct snd_kcontrol *kcontrol,
}
/* alsa control interface for selector unit */
-static struct snd_kcontrol_new mixer_selectunit_ctl = {
+static const struct snd_kcontrol_new mixer_selectunit_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "", /* will be filled later */
.info = mixer_ctl_selector_info,
diff --git a/sound/usb/mixer_scarlett.c b/sound/usb/mixer_scarlett.c
index 7438e7c4a842..c33e2378089d 100644
--- a/sound/usb/mixer_scarlett.c
+++ b/sound/usb/mixer_scarlett.c
@@ -477,7 +477,7 @@ static int scarlett_ctl_meter_get(struct snd_kcontrol *kctl,
return 0;
}
-static struct snd_kcontrol_new usb_scarlett_ctl_switch = {
+static const struct snd_kcontrol_new usb_scarlett_ctl_switch = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "",
.info = scarlett_ctl_switch_info,
@@ -487,7 +487,7 @@ static struct snd_kcontrol_new usb_scarlett_ctl_switch = {
static const DECLARE_TLV_DB_SCALE(db_scale_scarlett_gain, -12800, 100, 0);
-static struct snd_kcontrol_new usb_scarlett_ctl = {
+static const struct snd_kcontrol_new usb_scarlett_ctl = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -499,7 +499,7 @@ static struct snd_kcontrol_new usb_scarlett_ctl = {
.tlv = { .p = db_scale_scarlett_gain }
};
-static struct snd_kcontrol_new usb_scarlett_ctl_master = {
+static const struct snd_kcontrol_new usb_scarlett_ctl_master = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READWRITE |
SNDRV_CTL_ELEM_ACCESS_TLV_READ,
@@ -511,7 +511,7 @@ static struct snd_kcontrol_new usb_scarlett_ctl_master = {
.tlv = { .p = db_scale_scarlett_gain }
};
-static struct snd_kcontrol_new usb_scarlett_ctl_enum = {
+static const struct snd_kcontrol_new usb_scarlett_ctl_enum = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "",
.info = scarlett_ctl_enum_info,
@@ -519,7 +519,7 @@ static struct snd_kcontrol_new usb_scarlett_ctl_enum = {
.put = scarlett_ctl_enum_put,
};
-static struct snd_kcontrol_new usb_scarlett_ctl_dynamic_enum = {
+static const struct snd_kcontrol_new usb_scarlett_ctl_dynamic_enum = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.name = "",
.info = scarlett_ctl_enum_dynamic_info,
@@ -527,7 +527,7 @@ static struct snd_kcontrol_new usb_scarlett_ctl_dynamic_enum = {
.put = scarlett_ctl_enum_put,
};
-static struct snd_kcontrol_new usb_scarlett_ctl_sync = {
+static const struct snd_kcontrol_new usb_scarlett_ctl_sync = {
.iface = SNDRV_CTL_ELEM_IFACE_MIXER,
.access = SNDRV_CTL_ELEM_ACCESS_READ | SNDRV_CTL_ELEM_ACCESS_VOLATILE,
.name = "",
diff --git a/sound/usb/usx2y/us122l.c b/sound/usb/usx2y/us122l.c
index cf45bf1f7ee0..e118bdca983d 100644
--- a/sound/usb/usx2y/us122l.c
+++ b/sound/usb/usx2y/us122l.c
@@ -472,7 +472,7 @@ static int usb_stream_hwdep_new(struct snd_card *card)
hw->ops.mmap = usb_stream_hwdep_mmap;
hw->ops.poll = usb_stream_hwdep_poll;
- sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm",
+ sprintf(hw->name, "/dev/bus/usb/%03d/%03d/hwdeppcm",
dev->bus->busnum, dev->devnum);
return 0;
}
diff --git a/sound/usb/usx2y/usX2Yhwdep.c b/sound/usb/usx2y/usX2Yhwdep.c
index 605e1047c01d..f4b3cda412fc 100644
--- a/sound/usb/usx2y/usX2Yhwdep.c
+++ b/sound/usb/usx2y/usX2Yhwdep.c
@@ -258,7 +258,7 @@ int usX2Y_hwdep_new(struct snd_card *card, struct usb_device* device)
hw->ops.mmap = snd_us428ctls_mmap;
hw->ops.poll = snd_us428ctls_poll;
hw->exclusive = 1;
- sprintf(hw->name, "/proc/bus/usb/%03d/%03d", device->bus->busnum, device->devnum);
+ sprintf(hw->name, "/dev/bus/usb/%03d/%03d", device->bus->busnum, device->devnum);
return 0;
}
diff --git a/sound/usb/usx2y/usx2yhwdeppcm.c b/sound/usb/usx2y/usx2yhwdeppcm.c
index f95164b91152..d51c7fd7835b 100644
--- a/sound/usb/usx2y/usx2yhwdeppcm.c
+++ b/sound/usb/usx2y/usx2yhwdeppcm.c
@@ -723,7 +723,7 @@ int usX2Y_hwdep_pcm_new(struct snd_card *card)
hw->ops.release = snd_usX2Y_hwdep_pcm_release;
hw->ops.mmap = snd_usX2Y_hwdep_pcm_mmap;
hw->exclusive = 1;
- sprintf(hw->name, "/proc/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
+ sprintf(hw->name, "/dev/bus/usb/%03d/%03d/hwdeppcm", dev->bus->busnum, dev->devnum);
err = snd_pcm_new(card, NAME_ALLCAPS" hwdep Audio", 2, 1, 1, &pcm);
if (err < 0) {
diff --git a/sound/x86/intel_hdmi_audio.c b/sound/x86/intel_hdmi_audio.c
index 909391d5270c..c19efc9708d7 100644
--- a/sound/x86/intel_hdmi_audio.c
+++ b/sound/x86/intel_hdmi_audio.c
@@ -30,7 +30,7 @@
#include <linux/pm_runtime.h>
#include <linux/dma-mapping.h>
#include <linux/delay.h>
-#include <asm/cacheflush.h>
+#include <asm/set_memory.h>
#include <sound/core.h>
#include <sound/asoundef.h>
#include <sound/pcm.h>