Merge tag 'sound-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai...
authorLinus Torvalds <torvalds@linux-foundation.org>
Wed, 6 Mar 2019 22:10:46 +0000 (14:10 -0800)
committerLinus Torvalds <torvalds@linux-foundation.org>
Wed, 6 Mar 2019 22:10:46 +0000 (14:10 -0800)
Pull sound updates from Takashi Iwai:
 "We had again a busy development cycle with many new drivers as well as
  lots of core improvements / cleanups. Let's go for highlights:

  ALSA core:

   - PCM locking scheme was refactored for reducing a global rwlock

   - PCM suspend is handled in the device type PM ops now; lots of
     explicit calls were reduced by this action

   - Cleanups about PCM buffer preallocation calls

   - Kill NULL device object in memory allocations

   - Lots of procfs API cleanups

  ASoC core:

   - Support for only powering up channels that are actively being used

   - Cleanups / fixes of topology API

  ASoC drivers:

   - MediaTek BTCVSD for a Bluetooth radio chip, which is the first such
     driver we've had upstream!

   - Quite a few improvements to simplify the generic card drivers,
     especially the merge of the SCU cards into the main generic drivers

   - Lots of fixes for probing on Intel systems to follow more standard
     styles

   - A big refresh and cleanup of the Samsung drivers

   - New drivers: Asahi Kasei Microdevices AK4497, Cirrus Logic CS4341
     and CS35L26, Google ChromeOS embedded controllers, Ingenic JZ4725B,
     MediaTek BTCVSD, MT8183 and MT6358, NXP MICFIL, Rockchip RK3328,
     Spreadtrum DMA controllers, Qualcomm WCD9335, Xilinx S/PDIF and PCM
     formatters

  ALSA drivers:

   - Improvements of Tegra HD-audio controller driver for supporting new
     chips

   - HD-audio codec quirks for ALC294 S4 resume, ASUS laptop, Chrome
     headset button support and Dell workstations

   - Improved DSD support on USB-audio

   - Quirk for MOTU MicroBook II USB-audio

   - Support for Fireface UCX support and Solid State Logic Duende
     Classic/Mini"

* tag 'sound-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (461 commits)
  ALSA: usb-audio: Add quirk for MOTU MicroBook II
  ASoC: stm32: i2s: skip useless write in slave mode
  ASoC: stm32: i2s: fix race condition in irq handler
  ASoC: stm32: i2s: remove useless callback
  ASoC: stm32: i2s: fix dma configuration
  ASoC: stm32: i2s: fix stream count management
  ASoC: stm32: i2s: fix 16 bit format support
  ASoC: stm32: i2s: fix IRQ clearing
  ASoC: qcom: Kconfig: fix dependency for sdm845
  ASoC: Intel: Boards: Add Maxim98373 support
  ASoC: rsnd: gen: fix SSI9 4/5/6/7 busif related register address
  ALSA: firewire-motu: fix construction of PCM frame for capture direction
  ALSA: bebob: use more identical mod_alias for Saffire Pro 10 I/O against Liquid Saffire 56
  ALSA: hda: Extend i915 component bind timeout
  ASoC: wm_adsp: Improve logging messages
  ASoC: wm_adsp: Add support for multiple compressed buffers
  ASoC: wm_adsp: Refactor compress stream initialisation
  ASoC: wm_adsp: Reorder some functions for improved clarity
  ASoC: wm_adsp: Factor out stripping padding from ADSP data
  ASoC: cs35l36: Fix an IS_ERR() vs NULL checking bug
  ...

412 files changed:
Documentation/devicetree/bindings/sound/adi,adau1977.txt
Documentation/devicetree/bindings/sound/ak4458.txt
Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt [deleted file]
Documentation/devicetree/bindings/sound/cs35l36.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/cs4341.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt
Documentation/devicetree/bindings/sound/fsl,micfil.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/ingenic,jz4725b-codec.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/ingenic,jz4740-codec.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/mt6358.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.txt
Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt
Documentation/devicetree/bindings/sound/qcom,wcd9335.txt
Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/sgtl5000.txt
Documentation/devicetree/bindings/sound/simple-scu-card.txt [deleted file]
Documentation/devicetree/bindings/sound/sprd-pcm.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/xlnx,audio-formatter.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/xlnx,spdif.txt [new file with mode: 0644]
Documentation/sound/hd-audio/models.rst
Documentation/sound/kernel-api/writing-an-alsa-driver.rst
Documentation/sound/soc/dpcm.rst
MAINTAINERS
arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts
arch/arm64/boot/dts/nvidia/tegra194-p2972-0000.dts
arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi
drivers/gpio/gpiolib-acpi.c
drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c
drivers/media/pci/solo6x10/solo6x10-g723.c
drivers/media/pci/tw686x/tw686x-audio.c
include/linux/acpi.h
include/sound/core.h
include/sound/cs35l36.h [new file with mode: 0644]
include/sound/dmaengine_pcm.h
include/sound/hda_register.h
include/sound/hda_verbs.h
include/sound/hdaudio.h
include/sound/info.h
include/sound/memalloc.h
include/sound/pcm.h
include/sound/simple_card_utils.h
include/sound/soc-acpi.h
include/sound/soc-dapm.h
include/sound/soc-topology.h
include/sound/soc.h
sound/ac97/bus.c
sound/aoa/core/gpio-feature.c
sound/aoa/soundbus/i2sbus/core.c
sound/arm/aaci.c
sound/arm/pxa2xx-ac97.c
sound/atmel/ac97c.c
sound/core/compress_offload.c
sound/core/info.c
sound/core/init.c
sound/core/memalloc.c
sound/core/oss/pcm_oss.c
sound/core/pcm.c
sound/core/pcm_lib.c
sound/core/pcm_local.h
sound/core/pcm_memory.c
sound/core/pcm_native.c
sound/drivers/aloop.c
sound/drivers/dummy.c
sound/drivers/opl4/opl4_proc.c
sound/drivers/pcsp/pcsp.c
sound/drivers/vx/vx_core.c
sound/firewire/Kconfig
sound/firewire/bebob/bebob.c
sound/firewire/bebob/bebob_proc.c
sound/firewire/dice/dice-proc.c
sound/firewire/dice/dice.c
sound/firewire/digi00x/digi00x-proc.c
sound/firewire/fireface/Makefile
sound/firewire/fireface/ff-midi.c
sound/firewire/fireface/ff-pcm.c
sound/firewire/fireface/ff-proc.c
sound/firewire/fireface/ff-protocol-ff400.c [deleted file]
sound/firewire/fireface/ff-protocol-ff800.c [deleted file]
sound/firewire/fireface/ff-protocol-former.c [new file with mode: 0644]
sound/firewire/fireface/ff-protocol-latter.c [new file with mode: 0644]
sound/firewire/fireface/ff-stream.c
sound/firewire/fireface/ff-transaction.c
sound/firewire/fireface/ff.c
sound/firewire/fireface/ff.h
sound/firewire/fireworks/fireworks_proc.c
sound/firewire/motu/amdtp-motu.c
sound/firewire/motu/motu-proc.c
sound/firewire/oxfw/oxfw-proc.c
sound/firewire/tascam/tascam-proc.c
sound/hda/hdac_controller.c
sound/hda/hdac_i915.c
sound/hda/hdac_stream.c
sound/i2c/other/ak4113.c
sound/i2c/other/ak4114.c
sound/i2c/other/ak4xxx-adda.c
sound/isa/ad1816a/ad1816a_lib.c
sound/isa/als100.c
sound/isa/cmi8328.c
sound/isa/cmi8330.c
sound/isa/es1688/es1688.c
sound/isa/es1688/es1688_lib.c
sound/isa/es18xx.c
sound/isa/gus/gus_irq.c
sound/isa/gus/gus_main.c
sound/isa/gus/gus_mem.c
sound/isa/gus/gus_pcm.c
sound/isa/opti9xx/miro.c
sound/isa/sb/jazz16.c
sound/isa/sb/sb16.c
sound/isa/sb/sb16_csp.c
sound/isa/sb/sb16_main.c
sound/isa/sb/sb8.c
sound/isa/sb/sb8_main.c
sound/isa/sscape.c
sound/isa/wss/wss_lib.c
sound/mips/hal2.c
sound/mips/sgio2audio.c
sound/parisc/harmony.c
sound/pci/ac97/ac97_proc.c
sound/pci/ad1889.c
sound/pci/ak4531_codec.c
sound/pci/ali5451/ali5451.c
sound/pci/als300.c
sound/pci/als4000.c
sound/pci/asihpi/asihpi.c
sound/pci/atiixp.c
sound/pci/atiixp_modem.c
sound/pci/aw2/aw2-alsa.c
sound/pci/azt3328.c
sound/pci/bt87x.c
sound/pci/ca0106/ca0106_main.c
sound/pci/ca0106/ca0106_proc.c
sound/pci/cmipci.c
sound/pci/cs4281.c
sound/pci/cs46xx/cs46xx_dsp_spos.h
sound/pci/cs46xx/cs46xx_lib.c
sound/pci/cs46xx/dsp_spos.c
sound/pci/cs46xx/dsp_spos_scb_lib.c
sound/pci/cs5535audio/cs5535audio_pm.c
sound/pci/ctxfi/ctatc.c
sound/pci/echoaudio/echoaudio.c
sound/pci/emu10k1/emu10k1.c
sound/pci/emu10k1/emu10k1x.c
sound/pci/emu10k1/emupcm.c
sound/pci/emu10k1/emuproc.c
sound/pci/emu10k1/p16v.c
sound/pci/ens1370.c
sound/pci/es1938.c
sound/pci/es1968.c
sound/pci/fm801.c
sound/pci/hda/hda_beep.c
sound/pci/hda/hda_beep.h
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_proc.c
sound/pci/hda/hda_tegra.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/ice1712/ews.c
sound/pci/ice1712/ice1712.c
sound/pci/ice1712/ice1724.c
sound/pci/ice1712/pontis.c
sound/pci/ice1712/prodigy192.c
sound/pci/ice1712/prodigy_hifi.c
sound/pci/ice1712/quartet.c
sound/pci/intel8x0.c
sound/pci/intel8x0m.c
sound/pci/korg1212/korg1212.c
sound/pci/lola/lola_proc.c
sound/pci/lx6464es/lx6464es.c
sound/pci/maestro3.c
sound/pci/mixart/mixart.c
sound/pci/nm256/nm256.c
sound/pci/oxygen/oxygen_lib.c
sound/pci/oxygen/pcm1796.h
sound/pci/oxygen/xonar_pcm179x.c
sound/pci/pcxhr/pcxhr.c
sound/pci/riptide/riptide.c
sound/pci/rme32.c
sound/pci/rme96.c
sound/pci/rme9652/hdsp.c
sound/pci/rme9652/hdspm.c
sound/pci/rme9652/rme9652.c
sound/pci/sis7019.c
sound/pci/sonicvibes.c
sound/pci/trident/trident_main.c
sound/pci/via82xx.c
sound/pci/via82xx_modem.c
sound/pci/ymfpci/ymfpci_main.c
sound/pcmcia/pdaudiocf/pdaudiocf_core.c
sound/ppc/pmac.c
sound/ppc/snd_ps3.c
sound/ppc/tumbler.c
sound/sh/aica.c
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/amd/acp-pcm-dma.c
sound/soc/amd/raven/acp3x-pcm-dma.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ad193x.c
sound/soc/codecs/ad193x.h
sound/soc/codecs/adau1977.c
sound/soc/codecs/adau7002.c
sound/soc/codecs/ak4118.c
sound/soc/codecs/ak4458.c
sound/soc/codecs/cros_ec_codec.c [new file with mode: 0644]
sound/soc/codecs/cs35l36.c [new file with mode: 0644]
sound/soc/codecs/cs35l36.h [new file with mode: 0644]
sound/soc/codecs/cs4271.c
sound/soc/codecs/cs4341.c [new file with mode: 0644]
sound/soc/codecs/cs47l24.c
sound/soc/codecs/da7219-aad.c
sound/soc/codecs/da7219.c
sound/soc/codecs/da7219.h
sound/soc/codecs/dmic.c
sound/soc/codecs/es8316.c
sound/soc/codecs/es8316.h
sound/soc/codecs/hdac_hdmi.c
sound/soc/codecs/jz4725b.c [new file with mode: 0644]
sound/soc/codecs/jz4740.c
sound/soc/codecs/max98090.c
sound/soc/codecs/max98373.c
sound/soc/codecs/max9860.c
sound/soc/codecs/max98927.c
sound/soc/codecs/msm8916-wcd-analog.c
sound/soc/codecs/msm8916-wcd-digital.c
sound/soc/codecs/mt6351.c
sound/soc/codecs/mt6358.c [new file with mode: 0644]
sound/soc/codecs/mt6358.h [new file with mode: 0644]
sound/soc/codecs/nau8824.c
sound/soc/codecs/nau8825.c
sound/soc/codecs/pcm186x.c
sound/soc/codecs/pcm3060.c
sound/soc/codecs/pcm3060.h
sound/soc/codecs/pcm512x.c
sound/soc/codecs/rk3328_codec.c [new file with mode: 0644]
sound/soc/codecs/rk3328_codec.h [new file with mode: 0644]
sound/soc/codecs/rl6347a.c
sound/soc/codecs/rt274.c
sound/soc/codecs/rt286.c
sound/soc/codecs/rt298.c
sound/soc/codecs/rt5514.c
sound/soc/codecs/rt5640.c
sound/soc/codecs/rt5645.c
sound/soc/codecs/rt5651.c
sound/soc/codecs/rt5651.h
sound/soc/codecs/rt5670.c
sound/soc/codecs/rt5677.c
sound/soc/codecs/rt5682.c
sound/soc/codecs/sgtl5000.c
sound/soc/codecs/ssm2602.c
sound/soc/codecs/tlv320aic31xx.c
sound/soc/codecs/tlv320aic32x4.c
sound/soc/codecs/tlv320aic32x4.h
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/tscs42xx.c
sound/soc/codecs/wcd-clsh-v2.c [new file with mode: 0644]
sound/soc/codecs/wcd-clsh-v2.h [new file with mode: 0644]
sound/soc/codecs/wcd9335.c [new file with mode: 0644]
sound/soc/codecs/wcd9335.h [new file with mode: 0644]
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm8741.c
sound/soc/codecs/wm8770.c
sound/soc/codecs/wm8904.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8995.c
sound/soc/codecs/wm8996.c
sound/soc/codecs/wm8997.c
sound/soc/codecs/wm8998.c
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.h
sound/soc/dwc/dwc-pcm.c
sound/soc/fsl/Kconfig
sound/soc/fsl/Makefile
sound/soc/fsl/fsl-asoc-card.c
sound/soc/fsl/fsl_esai.c
sound/soc/fsl/fsl_micfil.c [new file with mode: 0644]
sound/soc/fsl/fsl_micfil.h [new file with mode: 0644]
sound/soc/fsl/fsl_sai.c
sound/soc/fsl/fsl_spdif.c
sound/soc/fsl/fsl_spdif.h
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/imx-sgtl5000.c
sound/soc/fsl/imx-spdif.c
sound/soc/generic/Kconfig
sound/soc/generic/Makefile
sound/soc/generic/audio-graph-card.c
sound/soc/generic/audio-graph-scu-card.c [deleted file]
sound/soc/generic/simple-card-utils.c
sound/soc/generic/simple-card.c
sound/soc/generic/simple-scu-card.c [deleted file]
sound/soc/intel/atom/sst-atom-controls.c
sound/soc/intel/atom/sst-mfld-platform-pcm.c
sound/soc/intel/atom/sst/sst_acpi.c
sound/soc/intel/atom/sst/sst_drv_interface.c
sound/soc/intel/atom/sst/sst_loader.c
sound/soc/intel/baytrail/sst-baytrail-ipc.c
sound/soc/intel/baytrail/sst-baytrail-pcm.c
sound/soc/intel/boards/Kconfig
sound/soc/intel/boards/bdw-rt5677.c
sound/soc/intel/boards/broadwell.c
sound/soc/intel/boards/bxt_da7219_max98357a.c
sound/soc/intel/boards/bxt_rt298.c
sound/soc/intel/boards/bytcht_da7213.c
sound/soc/intel/boards/bytcht_es8316.c
sound/soc/intel/boards/bytcr_rt5640.c
sound/soc/intel/boards/bytcr_rt5651.c
sound/soc/intel/boards/cht_bsw_max98090_ti.c
sound/soc/intel/boards/cht_bsw_nau8824.c
sound/soc/intel/boards/cht_bsw_rt5645.c
sound/soc/intel/boards/cht_bsw_rt5672.c
sound/soc/intel/boards/glk_rt5682_max98357a.c
sound/soc/intel/boards/haswell.c
sound/soc/intel/boards/kbl_da7219_max98927.c
sound/soc/intel/common/soc-acpi-intel-bxt-match.c
sound/soc/intel/common/soc-acpi-intel-byt-match.c
sound/soc/intel/common/soc-acpi-intel-cht-match.c
sound/soc/intel/common/soc-acpi-intel-cnl-match.c
sound/soc/intel/common/soc-acpi-intel-glk-match.c
sound/soc/intel/common/soc-acpi-intel-hda-match.c
sound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c
sound/soc/intel/common/soc-acpi-intel-icl-match.c
sound/soc/intel/common/soc-acpi-intel-kbl-match.c
sound/soc/intel/haswell/sst-haswell-ipc.c
sound/soc/intel/haswell/sst-haswell-pcm.c
sound/soc/intel/skylake/skl-messages.c
sound/soc/intel/skylake/skl-pcm.c
sound/soc/intel/skylake/skl-topology.c
sound/soc/mediatek/Kconfig
sound/soc/mediatek/Makefile
sound/soc/mediatek/common/Makefile
sound/soc/mediatek/common/mtk-afe-platform-driver.c
sound/soc/mediatek/common/mtk-btcvsd.c [new file with mode: 0644]
sound/soc/mediatek/mt8183/Makefile [new file with mode: 0644]
sound/soc/mediatek/mt8183/mt8183-afe-clk.c [new file with mode: 0644]
sound/soc/mediatek/mt8183/mt8183-afe-clk.h [new file with mode: 0644]
sound/soc/mediatek/mt8183/mt8183-afe-common.h [new file with mode: 0644]
sound/soc/mediatek/mt8183/mt8183-afe-pcm.c [new file with mode: 0644]
sound/soc/mediatek/mt8183/mt8183-dai-adda.c [new file with mode: 0644]
sound/soc/mediatek/mt8183/mt8183-dai-hostless.c [new file with mode: 0644]
sound/soc/mediatek/mt8183/mt8183-dai-i2s.c [new file with mode: 0644]
sound/soc/mediatek/mt8183/mt8183-dai-pcm.c [new file with mode: 0644]
sound/soc/mediatek/mt8183/mt8183-dai-tdm.c [new file with mode: 0644]
sound/soc/mediatek/mt8183/mt8183-interconnection.h [new file with mode: 0644]
sound/soc/mediatek/mt8183/mt8183-reg.h [new file with mode: 0644]
sound/soc/meson/axg-fifo.c
sound/soc/pxa/Makefile
sound/soc/qcom/Kconfig
sound/soc/qcom/apq8016_sbc.c
sound/soc/qcom/apq8096.c
sound/soc/qcom/common.c
sound/soc/qcom/sdm845.c
sound/soc/samsung/dma.h
sound/soc/samsung/dmaengine.c
sound/soc/samsung/i2s.c
sound/soc/samsung/odroid.c
sound/soc/samsung/pcm.c
sound/soc/samsung/s3c2412-i2s.c
sound/soc/samsung/s3c24xx-i2s.c
sound/soc/samsung/spdif.c
sound/soc/sh/fsi.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/src.c
sound/soc/sh/rcar/ssiu.c
sound/soc/sh/siu_pcm.c
sound/soc/soc-compress.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-generic-dmaengine-pcm.c
sound/soc/soc-pcm.c
sound/soc/soc-topology.c
sound/soc/sprd/Kconfig [new file with mode: 0644]
sound/soc/sprd/Makefile [new file with mode: 0644]
sound/soc/sprd/sprd-pcm-dma.c [new file with mode: 0644]
sound/soc/sprd/sprd-pcm-dma.h [new file with mode: 0644]
sound/soc/stm/stm32_adfsdm.c
sound/soc/stm/stm32_i2s.c
sound/soc/stm/stm32_sai.c
sound/soc/stm/stm32_sai_sub.c
sound/soc/sunxi/sun4i-codec.c
sound/soc/sunxi/sun50i-codec-analog.c
sound/soc/ti/davinci-mcasp.c
sound/soc/txx9/txx9aclc.c
sound/soc/uniphier/aio-dma.c
sound/soc/xilinx/Kconfig
sound/soc/xilinx/Makefile
sound/soc/xilinx/xlnx_formatter_pcm.c [new file with mode: 0644]
sound/soc/xilinx/xlnx_spdif.c [new file with mode: 0644]
sound/soc/xtensa/xtfpga-i2s.c
sound/sparc/dbri.c
sound/spi/at73c213.c
sound/synth/emux/emux_proc.c
sound/usb/card.c
sound/usb/card.h
sound/usb/format.c
sound/usb/line6/driver.c
sound/usb/line6/pod.c
sound/usb/mixer.c
sound/usb/mixer_quirks.c
sound/usb/pcm.c
sound/usb/proc.c
sound/usb/quirks-table.h
sound/usb/quirks.c
sound/usb/usx2y/usbusx2yaudio.c
sound/usb/usx2y/usx2yhwdeppcm.c
sound/x86/intel_hdmi_audio.c

index e79aeef73f286fc30cb0107ea3dfe72abc7e3453..9225472c80b4c4f2c0b049c896b7f34d240cbfa4 100644 (file)
@@ -17,12 +17,18 @@ Required properties:
                 Documentation/devicetree/bindings/regulator/regulator.txt
 
 Optional properties:
- - reset-gpio the reset pin for the chip, for more details consult
+ - reset-gpios: the reset pin for the chip, for more details consult
                 Documentation/devicetree/bindings/gpio/gpio.txt
 
  - DVDD-supply: supply voltage for the digital core, please consult
                 Documentation/devicetree/bindings/regulator/regulator.txt
 
+- adi,micbias: configures the voltage setting for the MICBIAS pin.
+               Select 0/1/2/3/4/5/6/7/8 to specify MICBIAS voltage
+               5V/5.5V/6V/6.5V/7V/7.5V/8V/8.5V/9V
+               If not specified the default value will be "7" meaning 8.5 Volts.
+               This property is only valid for the ADAU1977
+
 For required properties on SPI, please consult
 Documentation/devicetree/bindings/spi/spi-bus.txt
 
@@ -40,7 +46,8 @@ Examples:
                AVDD-supply = <&regulator>;
                DVDD-supply = <&regulator_digital>;
 
-               reset_gpio = <&gpio 10 GPIO_ACTIVE_LOW>;
+               adi,micbias = <3>;
+               reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
        };
 
        adau1977_i2c: adau1977@11 {
@@ -50,5 +57,5 @@ Examples:
                AVDD-supply = <&regulator>;
                DVDD-supply = <&regulator_digital>;
 
-               reset_gpio = <&gpio 10 GPIO_ACTIVE_LOW>;
+               reset-gpios = <&gpio 10 GPIO_ACTIVE_LOW>;
        };
index 7839be78448dcb7b0396ad8f6909cc1af9af937e..e5820235e0d5682052b35e0604053a094c4a3618 100644 (file)
@@ -4,7 +4,7 @@ This device supports I2C mode.
 
 Required properties:
 
-- compatible : "asahi-kasei,ak4458"
+- compatible : "asahi-kasei,ak4458" or "asahi-kasei,ak4497"
 - reg : The I2C address of the device for I2C
 
 Optional properties:
diff --git a/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt b/Documentation/devicetree/bindings/sound/audio-graph-scu-card.txt
deleted file mode 100644 (file)
index 62d4276..0000000
+++ /dev/null
@@ -1,123 +0,0 @@
-Audio-Graph-SCU-Card:
-
-Audio-Graph-SCU-Card is "Audio-Graph-Card" + "ALSA DPCM".
-
-It is based on common bindings for device graphs.
-see ${LINUX}/Documentation/devicetree/bindings/graph.txt
-
-Basically, Audio-Graph-SCU-Card property is same as
-Simple-Card / Simple-SCU-Card / Audio-Graph-Card.
-see ${LINUX}/Documentation/devicetree/bindings/sound/simple-card.txt
-    ${LINUX}/Documentation/devicetree/bindings/sound/simple-scu-card.txt
-    ${LINUX}/Documentation/devicetree/bindings/sound/audio-graph-card.txt
-
-Below are same as Simple-Card / Audio-Graph-Card.
-
-- label
-- dai-format
-- frame-master
-- bitclock-master
-- bitclock-inversion
-- frame-inversion
-- dai-tdm-slot-num
-- dai-tdm-slot-width
-- clocks / system-clock-frequency
-
-Below are same as Simple-SCU-Card.
-
-- convert-rate
-- convert-channels
-- prefix
-- routing
-
-Required properties:
-
-- compatible                           : "audio-graph-scu-card";
-- dais                                 : list of CPU DAI port{s}
-
-Example 1. Sampling Rate Conversion
-
-       sound_card {
-               compatible = "audio-graph-scu-card";
-
-               label = "sound-card";
-               prefix = "codec";
-               routing = "codec Playback", "DAI0 Playback",
-                         "DAI0 Capture",   "codec Capture";
-               convert-rate = <48000>;
-
-               dais = <&cpu_port>;
-       };
-
-       audio-codec {
-               ...
-
-               port {
-                       codec_endpoint: endpoint {
-                               remote-endpoint = <&cpu_endpoint>;
-                       };
-               };
-       };
-
-       dai-controller {
-               ...
-               cpu_port: port {
-                       cpu_endpoint: endpoint {
-                               remote-endpoint = <&codec_endpoint>;
-
-                               dai-format = "left_j";
-                               ...
-                       };
-               };
-       };
-
-Example 2. 2 CPU 1 Codec (Mixing)
-
-       sound_card {
-               compatible = "audio-graph-scu-card";
-
-               label = "sound-card";
-               routing = "codec Playback", "DAI0 Playback",
-                         "codec Playback", "DAI1 Playback",
-                         "DAI0 Capture",   "codec Capture";
-
-               dais = <&cpu_port0
-                       &cpu_port1>;
-       };
-
-       audio-codec {
-               ...
-
-               audio-graph-card,prefix = "codec";
-               audio-graph-card,convert-rate = <48000>;
-               port {
-                       codec_endpoint0: endpoint {
-                               remote-endpoint = <&cpu_endpoint0>;
-                       };
-                       codec_endpoint1: endpoint {
-                               remote-endpoint = <&cpu_endpoint1>;
-                       };
-               };
-       };
-
-       dai-controller {
-               ...
-               ports {
-                       cpu_port0: port {
-                               cpu_endpoint0: endpoint {
-                                       remote-endpoint = <&codec_endpoint0>;
-
-                                       dai-format = "left_j";
-                                       ...
-                               };
-                       };
-                       cpu_port1: port {
-                               cpu_endpoint1: endpoint {
-                                       remote-endpoint = <&codec_endpoint1>;
-
-                                       dai-format = "left_j";
-                                       ...
-                               };
-                       };
-               };
-       };
diff --git a/Documentation/devicetree/bindings/sound/cs35l36.txt b/Documentation/devicetree/bindings/sound/cs35l36.txt
new file mode 100644 (file)
index 0000000..912bd16
--- /dev/null
@@ -0,0 +1,168 @@
+CS35L36 Speaker Amplifier
+
+Required properties:
+
+  - compatible : "cirrus,cs35l36"
+
+  - reg : the I2C address of the device for I2C
+
+  - VA-supply, VP-supply : power supplies for the device,
+  as covered in
+  Documentation/devicetree/bindings/regulator/regulator.txt.
+
+  - cirrus,boost-ctl-millivolt : Boost Voltage Value.  Configures the boost
+  converter's output voltage in mV. The range is from 2550mV to 12000mV with
+  increments of 50mV.
+  (Default) VP
+
+  - cirrus,boost-peak-milliamp : Boost-converter peak current limit in mA.
+  Configures the peak current by monitoring the current through the boost FET.
+  Range starts at 1600mA and goes to a maximum of 4500mA with increments of
+  50mA.
+  (Default) 4.50 Amps
+
+  - cirrus,boost-ind-nanohenry : Inductor estimation LBST reference value.
+  Seeds the digital boost converter's inductor estimation block with the initial
+  inductance value to reference.
+
+  1000 = 1uH (Default)
+  1200 = 1.2uH
+
+Optional properties:
+  - cirrus,multi-amp-mode : Boolean to determine if there are more than
+  one amplifier in the system. If more than one it is best to Hi-Z the ASP
+  port to prevent bus contention on the output signal
+
+  - cirrus,boost-ctl-select : Boost conerter control source selection.
+  Selects the source of the BST_CTL target VBST voltage for the boost
+  converter to generate.
+  0x00 - Control Port Value
+  0x01 - Class H Tracking (Default)
+  0x10 - MultiDevice Sync Value
+
+  - cirrus,amp-pcm-inv : Boolean to determine Amplifier will invert incoming
+  PCM data
+
+  - cirrus,imon-pol-inv : Boolean to determine Amplifier will invert the
+  polarity of outbound IMON feedback data
+
+  - cirrus,vmon-pol-inv : Boolean to determine Amplifier will invert the
+  polarity of outbound VMON feedback data
+
+  - cirrus,dcm-mode-enable : Boost converter automatic DCM Mode enable.
+  This enables the digital boost converter to operate in a low power
+  (Discontinuous Conduction) mode during low loading conditions.
+
+  - cirrus,weak-fet-disable : Boolean : The strength of the output drivers is
+  reduced when operating in a Weak-FET Drive Mode and must not be used to drive
+  a large load.
+
+  - cirrus,classh-wk-fet-delay :  Weak-FET entry delay. Controls the delay
+  (in ms) before the Class H algorithm switches to the weak-FET voltage
+  (after the audio falls and remains below the value specified in WKFET_AMP_THLD).
+
+  0 = 0ms
+  1 = 5ms
+  2 = 10ms
+  3 = 50ms
+  4 = 100ms (Default)
+  5 = 200ms
+  6 = 500ms
+  7 = 1000ms
+
+  - cirrus,classh-weak-fet-thld-millivolt : Weak-FET amplifier drive threshold.
+  Configures the signal threshold at which the PWM output stage enters
+  weak-FET operation. The range is 50mV to 700mV in 50mV increments.
+
+  - cirrus,temp-warn-threshold :  Amplifier overtemperature warning threshold.
+  Configures the threshold at which the overtemperature warning condition occurs.
+  When the threshold is met, the overtemperature warning attenuation is applied
+  and the TEMP_WARN_EINT interrupt status bit is set.
+  If TEMP_WARN_MASK = 0, INTb is asserted.
+
+  0 = 105C
+  1 = 115C
+  2 = 125C (Default)
+  3 = 135C
+
+  - cirrus,irq-drive-select : Selects the driver type of the selected interrupt
+  output.
+
+  0 = Open-drain
+  1 = Push-pull (Default)
+
+  - cirrus,irq-gpio-select : Selects the pin to serve as the programmable
+  interrupt output.
+
+  0 = PDM_DATA / SWIRE_SD / INT (Default)
+  1 = GPIO
+
+Optional properties for the "cirrus,vpbr-config" Sub-node
+
+  - cirrus,vpbr-en : VBST brownout prevention enable. Configures whether the
+  VBST brownout prevention algorithm is enabled or disabled.
+
+  0 = VBST brownout prevention disabled (default)
+  1 = VBST brownout prevention enabled
+
+  See Section 7.31.1 VPBR Config for configuration options & further details
+
+  - cirrus,vpbr-thld : Initial VPBR threshold. Configures the VP brownout
+  threshold voltage
+
+  - cirrus,cirrus,vpbr-atk-rate : Attenuation attack step rate. Configures the
+  amount delay between consecutive volume attenuation steps when a brownout
+  condition is present and the VP brownout condition is in an attacking state.
+
+  - cirrus,vpbr-atk-vol : VP brownout prevention step size. Configures the VP
+  brownout prevention attacking attenuation step size when operating in either
+  digital volume or analog gain modes.
+
+  - cirrus,vpbr-max-attn : Maximum attenuation that the VP brownout prevention
+  can apply to the audio signal.
+
+  - cirrus,vpbr-wait : Configures the delay time between a brownout condition
+  no longer being present and the VP brownout prevention entering an attenuation
+  release state.
+
+  - cirrus,vpbr-rel-rate : Attenuation release step rate. Configures the delay
+  between consecutive volume attenuation release steps when a brownout condition
+  is not longer present and the VP brownout is in an attenuation release state.
+
+  - cirrus,vpbr-mute-en : During the attack state, if the vpbr-max-attn value
+  is reached, the error condition still remains, and this bit is set, the audio
+  is muted.
+
+Example:
+
+cs35l36: cs35l36@40 {
+       compatible = "cirrus,cs35l36";
+       reg = <0x40>;
+       VA-supply = <&dummy_vreg>;
+       VP-supply = <&dummy_vreg>;
+       reset-gpios = <&gpio0 54 0>;
+       interrupt-parent = <&gpio8>;
+       interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+
+       cirrus,boost-ind-nanohenry = <1000>;
+       cirrus,boost-ctl-millivolt = <10000>;
+       cirrus,boost-peak-milliamp = <4500>;
+       cirrus,boost-ctl-select = <0x00>;
+       cirrus,weak-fet-delay = <0x04>;
+       cirrus,weak-fet-thld = <0x01>;
+       cirrus,temp-warn-threshold = <0x01>;
+       cirrus,multi-amp-mode;
+       cirrus,irq-drive-select = <0x01>;
+       cirrus,irq-gpio-select = <0x01>;
+
+       cirrus,vpbr-config {
+               cirrus,vpbr-en = <0x00>;
+               cirrus,vpbr-thld = <0x05>;
+               cirrus,vpbr-atk-rate = <0x02>;
+               cirrus,vpbr-atk-vol = <0x01>;
+               cirrus,vpbr-max-attn = <0x09>;
+               cirrus,vpbr-wait = <0x01>;
+               cirrus,vpbr-rel-rate = <0x05>;
+               cirrus,vpbr-mute-en = <0x00>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/sound/cs4341.txt b/Documentation/devicetree/bindings/sound/cs4341.txt
new file mode 100644 (file)
index 0000000..12b4aa8
--- /dev/null
@@ -0,0 +1,22 @@
+Cirrus Logic CS4341 audio DAC
+
+This device supports both I2C and SPI (configured with pin strapping
+on the board).
+
+Required properties:
+  - compatible: "cirrus,cs4341a"
+  - reg : the I2C address of the device for I2C, the chip select
+          number for SPI.
+
+For required properties on I2C-bus, please consult
+Documentation/devicetree/bindings/i2c/i2c.txt
+For required properties on SPI-bus, please consult
+Documentation/devicetree/bindings/spi/spi-bus.txt
+
+Example:
+       codec: cs4341@0 {
+               #sound-dai-cells = <0>;
+               compatible = "cirrus,cs4341a";
+               reg = <0>;
+               spi-max-frequency = <6000000>;
+       };
index b279b6072bd549b4c9bfc108c9654a4d1484db51..a58f79f5345cd75aff5bc82acf28342e2ca6b555 100644 (file)
@@ -45,6 +45,23 @@ Optional properties:
 - fck_parent : Should contain a valid clock name which will be used as parent
               for the McASP fck
 
+Optional GPIO support:
+If any McASP pin need to be used as GPIO then the McASP node must have:
+...
+  gpio-controller
+  #gpio-cells = <2>;
+...
+
+When requesting a GPIO, the first parameter is the PIN index in McASP_P*
+registers.
+For example to request the AXR2 pin of mcasp8:
+function-gpios = <&mcasp8 2 0>;
+
+Or to request the ACLKR pin of mcasp8:
+function-gpios = <&mcasp8 29 0>;
+
+For generic gpio information, please refer to bindings/gpio/gpio.txt
+
 Example:
 
 mcasp0: mcasp0@1d00000 {
diff --git a/Documentation/devicetree/bindings/sound/fsl,micfil.txt b/Documentation/devicetree/bindings/sound/fsl,micfil.txt
new file mode 100644 (file)
index 0000000..53e227b
--- /dev/null
@@ -0,0 +1,32 @@
+NXP MICFIL Digital Audio Interface (MICFIL).
+
+The MICFIL digital interface provides a 16-bit audio signal from a PDM
+microphone bitstream in a configurable output sampling rate.
+
+Required properties:
+
+  - compatible         : Compatible list, contains "fsl,imx8mm-micfil"
+
+  - reg                        : Offset and length of the register set for the device.
+
+  - interrupts         : Contains the micfil interrupts.
+
+  - clocks             : Must contain an entry for each entry in clock-names.
+
+  - clock-names                : Must include the "ipg_clk" for register access and
+                         "ipg_clk_app" for internal micfil clock.
+
+  - dmas               : Generic dma devicetree binding as described in
+                         Documentation/devicetree/bindings/dma/dma.txt.
+
+Example:
+micfil: micfil@30080000 {
+       compatible = "fsl,imx8mm-micfil";
+       reg = <0x0 0x30080000 0x0 0x10000>;
+       interrupts = <GIC_SPI 109 IRQ_TYPE_LEVEL_HIGH>,
+                    <GIC_SPI 110 IRQ_TYPE_LEVEL_HIGH>;
+       clocks = <&clk IMX8MM_CLK_PDM_IPG>,
+                <&clk IMX8MM_CLK_PDM_ROOT>;
+       clock-names = "ipg_clk", "ipg_clk_app";
+       dmas = <&sdma2 24 26 0x80000000>;
+};
diff --git a/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt b/Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt
new file mode 100644 (file)
index 0000000..1084f7f
--- /dev/null
@@ -0,0 +1,26 @@
+* Audio codec controlled by ChromeOS EC
+
+Google's ChromeOS EC codec is a digital mic codec provided by the
+Embedded Controller (EC) and is controlled via a host-command interface.
+
+An EC codec node should only be found as a sub-node of the EC node (see
+Documentation/devicetree/bindings/mfd/cros-ec.txt).
+
+Required properties:
+- compatible: Must contain "google,cros-ec-codec"
+- #sound-dai-cells: Should be 1. The cell specifies number of DAIs.
+- max-dmic-gain: A number for maximum gain in dB on digital microphone.
+
+Example:
+
+cros-ec@0 {
+       compatible = "google,cros-ec-spi";
+
+       ...
+
+       cros_ec_codec: ec-codec {
+               compatible = "google,cros-ec-codec";
+               #sound-dai-cells = <1>;
+               max-dmic-gain = <43>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/sound/ingenic,jz4725b-codec.txt b/Documentation/devicetree/bindings/sound/ingenic,jz4725b-codec.txt
new file mode 100644 (file)
index 0000000..05adc0d
--- /dev/null
@@ -0,0 +1,20 @@
+Ingenic JZ4725B codec controller
+
+Required properties:
+- compatible : "ingenic,jz4725b-codec"
+- reg : codec registers location and length
+- clocks : phandle to the AIC clock.
+- clock-names: must be set to "aic".
+- #sound-dai-cells: Must be set to 0.
+
+Example:
+
+codec: audio-codec@100200a4 {
+       compatible = "ingenic,jz4725b-codec";
+       reg = <0x100200a4 0x8>;
+
+       #sound-dai-cells = <0>;
+
+       clocks = <&cgu JZ4725B_CLK_AIC>;
+       clock-names = "aic";
+};
diff --git a/Documentation/devicetree/bindings/sound/ingenic,jz4740-codec.txt b/Documentation/devicetree/bindings/sound/ingenic,jz4740-codec.txt
new file mode 100644 (file)
index 0000000..1ffcade
--- /dev/null
@@ -0,0 +1,20 @@
+Ingenic JZ4740 codec controller
+
+Required properties:
+- compatible : "ingenic,jz4740-codec"
+- reg : codec registers location and length
+- clocks : phandle to the AIC clock.
+- clock-names: must be set to "aic".
+- #sound-dai-cells: Must be set to 0.
+
+Example:
+
+codec: audio-codec@10020080 {
+       compatible = "ingenic,jz4740-codec";
+       reg = <0x10020080 0x8>;
+
+       #sound-dai-cells = <0>;
+
+       clocks = <&cgu JZ4740_CLK_AIC>;
+       clock-names = "aic";
+};
diff --git a/Documentation/devicetree/bindings/sound/mt6358.txt b/Documentation/devicetree/bindings/sound/mt6358.txt
new file mode 100644 (file)
index 0000000..5465730
--- /dev/null
@@ -0,0 +1,18 @@
+Mediatek MT6358 Audio Codec
+
+The communication between MT6358 and SoC is through Mediatek PMIC wrapper.
+For more detail, please visit Mediatek PMIC wrapper documentation.
+
+Must be a child node of PMIC wrapper.
+
+Required properties:
+
+- compatible : "mediatek,mt6358-sound".
+- Avdd-supply : power source of AVDD
+
+Example:
+
+mt6358_snd {
+       compatible = "mediatek,mt6358-sound";
+       Avdd-supply = <&mt6358_vaud28_reg>;
+};
diff --git a/Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt b/Documentation/devicetree/bindings/sound/mt8183-afe-pcm.txt
new file mode 100644 (file)
index 0000000..396ba38
--- /dev/null
@@ -0,0 +1,36 @@
+Mediatek AFE PCM controller for mt8183
+
+Required properties:
+- compatible = "mediatek,mt68183-audio";
+- reg: register location and size
+- interrupts: should contain AFE interrupt
+- power-domains: should define the power domain
+- clocks: Must contain an entry for each entry in clock-names
+- clock-names: should have these clock names:
+               "infra_sys_audio_clk",
+               "mtkaif_26m_clk",
+               "top_mux_audio",
+               "top_mux_aud_intbus",
+               "top_sys_pll3_d4",
+               "top_clk26m_clk";
+
+Example:
+
+       afe: mt8183-afe-pcm@11220000  {
+               compatible = "mediatek,mt8183-audio";
+               reg = <0 0x11220000 0 0x1000>;
+               interrupts = <GIC_SPI 161 IRQ_TYPE_LEVEL_LOW>;
+               power-domains = <&scpsys MT8183_POWER_DOMAIN_AUDIO>;
+               clocks = <&infrasys CLK_INFRA_AUDIO>,
+                        <&infrasys CLK_INFRA_AUDIO_26M_BCLK>,
+                        <&topckgen CLK_TOP_MUX_AUDIO>,
+                        <&topckgen CLK_TOP_MUX_AUD_INTBUS>,
+                        <&topckgen CLK_TOP_SYSPLL_D2_D4>,
+                        <&clk26m>;
+               clock-names = "infra_sys_audio_clk",
+                             "mtkaif_26m_clk",
+                             "top_mux_audio",
+                             "top_mux_aud_intbus",
+                             "top_sys_pll_d2_d4",
+                             "top_clk26m_clk";
+       };
diff --git a/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt b/Documentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt
new file mode 100644 (file)
index 0000000..679e448
--- /dev/null
@@ -0,0 +1,24 @@
+Mediatek ALSA BT SCO CVSD/MSBC Driver
+
+Required properties:
+- compatible = "mediatek,mtk-btcvsd-snd";
+- reg: register location and size of PKV and SRAM_BANK2
+- interrupts: should contain BTSCO interrupt
+- mediatek,infracfg: the phandles of INFRASYS
+- mediatek,offset: Array contains of register offset and mask
+    infra_misc_offset,
+    infra_conn_bt_cvsd_mask,
+    cvsd_mcu_read_offset,
+    cvsd_mcu_write_offset,
+    cvsd_packet_indicator_offset
+
+Example:
+
+       mtk-btcvsd-snd@18000000 {
+               compatible = "mediatek,mtk-btcvsd-snd";
+               reg=<0 0x18000000 0 0x1000>,
+                   <0 0x18080000 0 0x8000>;
+               interrupts = <GIC_SPI 286 IRQ_TYPE_LEVEL_LOW>;
+               mediatek,infracfg = <&infrasys>;
+               mediatek,offset = <0xf00 0x800 0xfd0 0xfd4 0xfd8>;
+       };
index 44d27456e8a4862a11e67785b459fd5ef721cc82..21cd310963b1243d974ae3ca20ba498925737fed 100644 (file)
@@ -13,6 +13,10 @@ Required properties:
   See ../reset/reset.txt for details.
 - reset-names : Must include the following entries: hda, hda2hdmi, hda2codec_2x
 
+Optional properties:
+- nvidia,model : The user-visible name of this sound complex. Since the property
+  is optional, legacy boards can use default name provided in hda driver.
+
 Example:
 
 hda@70030000 {
@@ -27,4 +31,5 @@ hda@70030000 {
                 <&tegra_car 128>, /* hda2hdmi */
                 <&tegra_car 111>; /* hda2codec_2x */
        reset-names = "hda", "hda2hdmi", "hda2codec_2x";
+       nvidia,model = "jetson-tk1-hda";
 };
index fdcea3d12ee531fc02df2bdae7e79f4856deb600..e7d17dda55db295b54a7358212e26a432dfe7378 100644 (file)
@@ -30,6 +30,7 @@ Required properties
  - vdd-cdc-io-supply: phandle to VDD_CDC_IO regulator DT node.
  - vdd-cdc-tx-rx-cx-supply: phandle to VDD_CDC_TX/RX/CX regulator DT node.
  - vdd-micbias-supply: phandle of VDD_MICBIAS supply's regulator DT node.
+
 Optional Properties:
  - qcom,mbhc-vthreshold-low: Array of 5 threshold voltages in mV for 5 buttons
                             detection on headset when the mbhc is powered up
@@ -92,9 +93,9 @@ spmi_bus {
                                  "cdc_ear_cnp_int",
                                  "cdc_hphr_cnp_int",
                                  "cdc_hphl_cnp_int";
-                      VDD-CDC-IO-supply = <&pm8916_l5>;
-                      VDD-CDC-TX-RX-CX-supply = <&pm8916_l5>;
-                      VDD-MICBIAS-supply = <&pm8916_l13>;
+                      vdd-cdc-io-supply = <&pm8916_l5>;
+                      vdd-cdc-tx-rx-cx-supply = <&pm8916_l5>;
+                      vdd-micbias-supply = <&pm8916_l13>;
                       #sound-dai-cells = <1>;
        };
 };
index 1d8d49e30af70fce524ffad5b2b6553435571b52..5d6ea66a863fe2e46c2f8ca292d941dcded61a65 100644 (file)
@@ -34,12 +34,12 @@ Required properties with SLIMbus Interface:
        Definition: Interrupt names of WCD INTR1 and INTR2
        Should be: "intr1", "intr2"
 
-- reset-gpio:
+- reset-gpios:
        Usage: required
        Value type: <String Array>
        Definition: Reset gpio line
 
-- qcom,ifd:
+- slim-ifc-dev:
        Usage: required
        Value type: <phandle>
        Definition: SLIM interface device
@@ -104,13 +104,13 @@ Required properties with SLIMbus Interface:
        Value type: <u32>
        Definition: Must be 1
 
-codec@1{
+audio-codec@1{
        compatible = "slim217,1a0";
        reg  = <1 0>;
        interrupts = <&msmgpio 54 IRQ_TYPE_LEVEL_HIGH>;
        interrupt-names = "intr2"
-       reset-gpio = <&msmgpio 64 0>;
-       qcom,ifd  = <&wc9335_ifd>;
+       reset-gpios = <&msmgpio 64 0>;
+       slim-ifc-dev  = <&wc9335_ifd>;
        clock-names = "mclk", "native";
        clocks = <&rpmcc RPM_SMD_DIV_CLK1>,
                 <&rpmcc RPM_SMD_BB_CLK1>;
diff --git a/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt b/Documentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt
new file mode 100644 (file)
index 0000000..2469588
--- /dev/null
@@ -0,0 +1,23 @@
+* Rockchip Rk3328 internal codec
+
+Required properties:
+
+- compatible: "rockchip,rk3328-codec"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- rockchip,grf: the phandle of the syscon node for GRF register.
+- clocks: a list of phandle + clock-specifer pairs, one for each entry in clock-names.
+- clock-names: should be "pclk".
+- spk-depop-time-ms: speak depop time msec.
+
+Example for rk3328 internal codec:
+
+codec: codec@ff410000 {
+       compatible = "rockchip,rk3328-codec";
+       reg = <0x0 0xff410000 0x0 0x1000>;
+       rockchip,grf = <&grf>;
+       clocks = <&cru PCLK_ACODEC>;
+       clock-names = "pclk";
+       spk-depop-time-ms = 100;
+       status = "disabled";
+};
index 9c58f724396ab0881e851eaef1ed3a5902c14115..9d9ff5184939615668ed32164fc92e8dc32873c8 100644 (file)
@@ -37,6 +37,15 @@ VDDIO                1.8V            2.5V            3.3V
 2 =            3.33 mA         5.74 mA         8.03  mA
 3 =            4.99 mA         8.61 mA         12.05 mA
 
+- sclk-strength: the SCLK pad strength. Possible values are:
+0, 1, 2 and 3 as per the table below:
+
+VDDIO          1.8V            2.5V            3.3V
+0 =            Disable
+1 =            1.66 mA         2.87 mA         4.02  mA
+2 =            3.33 mA         5.74 mA         8.03  mA
+3 =            4.99 mA         8.61 mA         12.05 mA
+
 Example:
 
 sgtl5000: codec@a {
diff --git a/Documentation/devicetree/bindings/sound/simple-scu-card.txt b/Documentation/devicetree/bindings/sound/simple-scu-card.txt
deleted file mode 100644 (file)
index 3a2f716..0000000
+++ /dev/null
@@ -1,94 +0,0 @@
-ASoC Simple SCU Sound Card
-
-Simple SCU Sound Card is "Simple Sound Card" + "ALSA DPCM".
-For example, you can use this driver if you want to exchange sampling rate convert,
-Mixing, etc...
-
-Required properties:
-
-- compatible                           : "simple-scu-audio-card"
-                                         "renesas,rsrc-card"
-Optional properties:
-
-- simple-audio-card,name               : see simple-audio-card.txt
-- simple-audio-card,cpu                        : see simple-audio-card.txt
-- simple-audio-card,codec              : see simple-audio-card.txt
-
-Optional subnode properties:
-
-- simple-audio-card,format             : see simple-audio-card.txt
-- simple-audio-card,frame-master       : see simple-audio-card.txt
-- simple-audio-card,bitclock-master    : see simple-audio-card.txt
-- simple-audio-card,bitclock-inversion : see simple-audio-card.txt
-- simple-audio-card,frame-inversion    : see simple-audio-card.txt
-- simple-audio-card,convert-rate       : platform specified sampling rate convert
-- simple-audio-card,convert-channels   : platform specified converted channel size (2 - 8 ch)
-- simple-audio-card,prefix             : see routing
-- simple-audio-card,widgets            : Please refer to widgets.txt.
-- simple-audio-card,routing            : A list of the connections between audio components.
-                                         Each entry is a pair of strings, the first being the connection's sink,
-                                         the second being the connection's source. Valid names for sources.
-                                         use audio-prefix if some components is using same sink/sources naming.
-                                         it can be used if compatible was "renesas,rsrc-card";
-
-Required CPU/CODEC subnodes properties:
-
-- sound-dai                            : see simple-audio-card.txt
-
-Optional CPU/CODEC subnodes properties:
-
-- clocks / system-clock-frequency      : see simple-audio-card.txt
-
-Example 1. Sampling Rate Conversion
-
-sound {
-       compatible = "simple-scu-audio-card";
-
-       simple-audio-card,name = "rsnd-ak4643";
-       simple-audio-card,format = "left_j";
-       simple-audio-card,bitclock-master = <&sndcodec>;
-       simple-audio-card,frame-master = <&sndcodec>;
-
-       simple-audio-card,convert-rate = <48000>;
-
-       simple-audio-card,prefix = "ak4642";
-       simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
-                       "DAI0 Capture", "ak4642 Capture";
-
-       sndcpu: simple-audio-card,cpu {
-               sound-dai = <&rcar_sound>;
-       };
-
-       sndcodec: simple-audio-card,codec {
-               sound-dai = <&ak4643>;
-               system-clock-frequency = <11289600>;
-       };
-};
-
-Example 2. 2 CPU 1 Codec (Mixing)
-
-sound {
-       compatible = "simple-scu-audio-card";
-
-       simple-audio-card,name = "rsnd-ak4643";
-       simple-audio-card,format = "left_j";
-       simple-audio-card,bitclock-master = <&dpcmcpu>;
-       simple-audio-card,frame-master = <&dpcmcpu>;
-
-       simple-audio-card,routing = "ak4642 Playback", "DAI0 Playback",
-                       "ak4642 Playback", "DAI1 Playback";
-
-       dpcmcpu: cpu@0 {
-               sound-dai = <&rcar_sound 0>;
-       };
-
-       cpu@1 {
-               sound-dai = <&rcar_sound 1>;
-       };
-
-       codec {
-               prefix = "ak4642";
-               sound-dai = <&ak4643>;
-               clocks = <&audio_clock>;
-       };
-};
diff --git a/Documentation/devicetree/bindings/sound/sprd-pcm.txt b/Documentation/devicetree/bindings/sound/sprd-pcm.txt
new file mode 100644 (file)
index 0000000..4b23e84
--- /dev/null
@@ -0,0 +1,23 @@
+* Spreadtrum DMA platfrom bindings
+
+Required properties:
+- compatible: Should be "sprd,pcm-platform".
+- dmas: Specify the list of DMA controller phandle and DMA request line ordered pairs.
+- dma-names: Identifier string for each DMA request line in the dmas property.
+  These strings correspond 1:1 with the ordered pairs in dmas.
+
+Example:
+
+       audio_platform:platform@0 {
+               compatible = "sprd,pcm-platform";
+               dmas = <&agcp_dma 1 1>, <&agcp_dma 2 2>,
+                    <&agcp_dma 3 3>, <&agcp_dma 4 4>,
+                    <&agcp_dma 5 5>, <&agcp_dma 6 6>,
+                    <&agcp_dma 7 7>, <&agcp_dma 8 8>,
+                    <&agcp_dma 9 9>, <&agcp_dma 10 10>;
+               dma-names = "normal_p_l", "normal_p_r",
+                       "normal_c_l", "normal_c_r",
+                       "voice_c", "fast_p",
+                       "loop_c", "loop_p",
+                       "voip_c", "voip_p";
+       };
diff --git a/Documentation/devicetree/bindings/sound/xlnx,audio-formatter.txt b/Documentation/devicetree/bindings/sound/xlnx,audio-formatter.txt
new file mode 100644 (file)
index 0000000..cbc93c8
--- /dev/null
@@ -0,0 +1,29 @@
+Device-Tree bindings for Xilinx PL audio formatter
+
+The IP core supports DMA, data formatting(AES<->PCM conversion)
+of audio samples.
+
+Required properties:
+ - compatible: "xlnx,audio-formatter-1.0"
+ - interrupt-names: Names specified to list of interrupts in same
+                   order mentioned under "interrupts".
+                   List of supported interrupt names are:
+                   "irq_mm2s" : interrupt from MM2S block
+                   "irq_s2mm" : interrupt from S2MM block
+ - interrupts-parent: Phandle for interrupt controller.
+ - interrupts: List of Interrupt numbers.
+ - reg: Base address and size of the IP core instance.
+ - clock-names: List of input clocks.
+   Required elements: "s_axi_lite_aclk", "aud_mclk"
+ - clocks: Input clock specifier. Refer to common clock bindings.
+
+Example:
+       audio_ss_0_audio_formatter_0: audio_formatter@80010000 {
+               compatible = "xlnx,audio-formatter-1.0";
+               interrupt-names = "irq_mm2s", "irq_s2mm";
+               interrupt-parent = <&gic>;
+               interrupts = <0 104 4>, <0 105 4>;
+               reg = <0x0 0x80010000 0x0 0x1000>;
+               clock-names = "s_axi_lite_aclk", "aud_mclk";
+               clocks = <&clk 71>, <&clk_wiz_1 0>;
+       };
diff --git a/Documentation/devicetree/bindings/sound/xlnx,spdif.txt b/Documentation/devicetree/bindings/sound/xlnx,spdif.txt
new file mode 100644 (file)
index 0000000..15c2d64
--- /dev/null
@@ -0,0 +1,28 @@
+Device-Tree bindings for Xilinx SPDIF IP
+
+The IP supports playback and capture of SPDIF audio
+
+Required properties:
+ - compatible: "xlnx,spdif-2.0"
+ - clock-names: List of input clocks.
+   Required elements: "s_axi_aclk", "aud_clk_i"
+ - clocks: Input clock specifier. Refer to common clock bindings.
+ - reg: Base address and address length of the IP core instance.
+ - interrupts-parent: Phandle for interrupt controller.
+ - interrupts: List of Interrupt numbers.
+ - xlnx,spdif-mode: 0 :- receiver mode
+                   1 :- transmitter mode
+ - xlnx,aud_clk_i: input audio clock value.
+
+Example:
+       spdif_0: spdif@80010000 {
+               clock-names = "aud_clk_i", "s_axi_aclk";
+               clocks = <&misc_clk_0>, <&clk 71>;
+               compatible = "xlnx,spdif-2.0";
+               interrupt-names = "spdif_interrupt";
+               interrupt-parent = <&gic>;
+               interrupts = <0 91 4>;
+               reg = <0x0 0x80010000 0x0 0x10000>;
+               xlnx,spdif-mode = <1>;
+               xlnx,aud_clk_i = <49152913>;
+       };
index 368a07a165f5a7d4a1c5e47213b9d7dd61516be6..7d7c191102a73baf4eb1e72b01119458a2d3b53f 100644 (file)
@@ -254,10 +254,12 @@ alc274-dell-aio
     ALC274 fixups on Dell AIO machines
 alc255-dummy-lineout
     Dell Precision 3930 fixups
-alc255-dell-headset"},
+alc255-dell-headset
     Dell Precision 3630 fixups
 alc295-hp-x360
     HP Spectre X360 fixups
+alc-sense-combo
+    Headset button support for Chrome platform
 
 ALC66x/67x/892
 ==============
index b37234afdfa1cdf9f6966480e859dd501cf3fc3e..6b154dbb02cc76eb991c809af4bec52528114c84 100644 (file)
@@ -3520,14 +3520,14 @@ allocator will try to get an area as large as possible within the
 given size.
 
 The second argument (type) and the third argument (device pointer) are
-dependent on the bus. In the case of the ISA bus, pass
-:c:func:`snd_dma_isa_data()` as the third argument with
+dependent on the bus. For normal devices, pass the device pointer
+(typically identical as ``card->dev``) to the third argument with
 ``SNDRV_DMA_TYPE_DEV`` type. For the continuous buffer unrelated to the
 bus can be pre-allocated with ``SNDRV_DMA_TYPE_CONTINUOUS`` type and the
 ``snd_dma_continuous_data(GFP_KERNEL)`` device pointer, where
-``GFP_KERNEL`` is the kernel allocation flag to use. For the PCI
-scatter-gather buffers, use ``SNDRV_DMA_TYPE_DEV_SG`` with
-``snd_dma_pci_data(pci)`` (see the `Non-Contiguous Buffers`_
+``GFP_KERNEL`` is the kernel allocation flag to use. For the
+scatter-gather buffers, use ``SNDRV_DMA_TYPE_DEV_SG`` with the device
+pointer (see the `Non-Contiguous Buffers`_
 section).
 
 Once the buffer is pre-allocated, you can use the allocator in the
@@ -3924,15 +3924,12 @@ The scheme of the real suspend job is as follows.
 2. Call :c:func:`snd_power_change_state()` with
    ``SNDRV_CTL_POWER_D3hot`` to change the power status.
 
-3. Call :c:func:`snd_pcm_suspend_all()` to suspend the running
-   PCM streams.
-
-4. If AC97 codecs are used, call :c:func:`snd_ac97_suspend()` for
+3. If AC97 codecs are used, call :c:func:`snd_ac97_suspend()` for
    each codec.
 
-5. Save the register values if necessary.
+4. Save the register values if necessary.
 
-6. Stop the hardware if necessary.
+5. Stop the hardware if necessary.
 
 A typical code would be like:
 
@@ -3946,12 +3943,10 @@ A typical code would be like:
           /* (2) */
           snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
           /* (3) */
-          snd_pcm_suspend_all(chip->pcm);
-          /* (4) */
           snd_ac97_suspend(chip->ac97);
-          /* (5) */
+          /* (4) */
           snd_mychip_save_registers(chip);
-          /* (6) */
+          /* (5) */
           snd_mychip_stop_hardware(chip);
           return 0;
   }
@@ -3994,13 +3989,9 @@ A typical code would be like:
           return 0;
   }
 
-As shown in the above, it's better to save registers after suspending
-the PCM operations via :c:func:`snd_pcm_suspend_all()` or
-:c:func:`snd_pcm_suspend()`. It means that the PCM streams are
-already stopped when the register snapshot is taken. But, remember that
-you don't have to restart the PCM stream in the resume callback. It'll
-be restarted via trigger call with ``SNDRV_PCM_TRIGGER_RESUME`` when
-necessary.
+Note that, at the time this callback gets called, the PCM stream has
+been already suspended via its own PM ops calling
+:c:func:`snd_pcm_suspend_all()` internally.
 
 OK, we have all callbacks now. Let's set them up. In the initialization
 of the card, make sure that you can get the chip data from the card
index f6845b2278eafcb570d563da6dd6eaaee37fa51b..77f67ded53de892364149c8cb80be9a53bea3915 100644 (file)
@@ -13,7 +13,7 @@ drivers that expose several ALSA PCMs and can route to multiple DAIs.
 The DPCM runtime routing is determined by the ALSA mixer settings in the same
 way as the analog signal is routed in an ASoC codec driver. DPCM uses a DAPM
 graph representing the DSP internal audio paths and uses the mixer settings to
-determine the patch used by each ALSA PCM.
+determine the path used by each ALSA PCM.
 
 DPCM re-uses all the existing component codec, platform and DAI drivers without
 any modifications.
@@ -101,7 +101,7 @@ The audio driver processes this as follows :-
 
 4. Machine driver or audio HAL enables the speaker path.
 
-5. DPCM runs the PCM ops for startup(), hw_params(), prepapre() and
+5. DPCM runs the PCM ops for startup(), hw_params(), prepare() and
    trigger(start) for DAI1 Speakers since the path is enabled.
 
 In this example, the machine driver or userspace audio HAL can alter the routing
@@ -221,7 +221,7 @@ like a BT phone call :-
 This allows the host CPU to sleep while the DSP, MODEM DAI and the BT DAI are
 still in operation.
 
-A BE DAI link can also set the codec to a dummy device if the code is a device
+A BE DAI link can also set the codec to a dummy device if the codec is a device
 that is managed externally.
 
 Likewise a BE DAI can also set a dummy cpu DAI if the CPU DAI is managed by the
@@ -249,7 +249,7 @@ configuration.
        struct snd_interval *channels = hw_param_interval(params,
                                                SNDRV_PCM_HW_PARAM_CHANNELS);
 
-       /* The DSP will covert the FE rate to 48k, stereo */
+       /* The DSP will convert the FE rate to 48k, stereo */
        rate->min = rate->max = 48000;
        channels->min = channels->max = 2;
 
@@ -386,5 +386,3 @@ This means creating a new FE that is connected with a virtual path to both
 DAI links. The DAI links will be started when the FE PCM is started and stopped
 when the FE PCM is stopped. Note that the FE PCM cannot read or write data in
 this configuration.
-
-
index a419acaa01c507f23ece5978a4f1b065949fb36b..d1559363898fde80991dfa45bc248a05b55b7552 100644 (file)
@@ -3727,6 +3727,14 @@ N:       cros_ec
 N:     cros-ec
 F:     drivers/power/supply/cros_usbpd-charger.c
 
+CHROMEOS EC CODEC DRIVER
+M:     Cheng-Yi Chiang <cychiang@chromium.org>
+S:     Maintained
+R:     Enric Balletbo i Serra <enric.balletbo@collabora.com>
+R:     Guenter Roeck <groeck@chromium.org>
+F:     Documentation/devicetree/bindings/sound/google,cros-ec-codec.txt
+F:     sound/soc/codecs/cros_ec_codec.*
+
 CIRRUS LOGIC AUDIO CODEC DRIVERS
 M:     Brian Austin <brian.austin@cirrus.com>
 M:     Paul Handrigan <Paul.Handrigan@cirrus.com>
index 0f3c6cebb049c7801f28cf489f19164415d3eee4..31457f32e4d08aaafffabf15bc17520070af3090 100644 (file)
@@ -54,6 +54,7 @@
        };
 
        hda@3510000 {
+               nvidia,model = "jetson-tx2-hda";
                status = "okay";
        };
 
index 34a7044927fdc3058557e745ef8de7563486de55..b62e969458464e562cf04099b13455273b6b8183 100644 (file)
@@ -21,6 +21,7 @@
                };
 
                hda@3510000 {
+                       nvidia,model = "jetson-xavier-hda";
                        status = "okay";
                };
 
index 8780b5b3d2b96a6ea07960ccfd37df3a26a72fa2..95e890d8a1192ec0f3f546029df15bb1b6c81edf 100644 (file)
        };
 
        hda@70030000 {
+               nvidia,model = "jetson-tx1-hda";
                status = "okay";
        };
 
index 667f472fdbbd2ed8a910bfdd1054d84aec8b94e7..3e8e2d3ce7a8020a8f89cf5d187f32c8d7a539c8 100644 (file)
@@ -530,17 +530,24 @@ static int acpi_populate_gpio_lookup(struct acpi_resource *ares, void *data)
        if (ares->type != ACPI_RESOURCE_TYPE_GPIO)
                return 1;
 
-       if (lookup->n++ == lookup->index && !lookup->desc) {
+       if (!lookup->desc) {
                const struct acpi_resource_gpio *agpio = &ares->data.gpio;
-               int pin_index = lookup->pin_index;
+               bool gpioint = agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT;
+               int pin_index;
 
+               if (lookup->info.quirks & ACPI_GPIO_QUIRK_ONLY_GPIOIO && gpioint)
+                       lookup->index++;
+
+               if (lookup->n++ != lookup->index)
+                       return 1;
+
+               pin_index = lookup->pin_index;
                if (pin_index >= agpio->pin_table_length)
                        return 1;
 
                lookup->desc = acpi_get_gpiod(agpio->resource_source.string_ptr,
                                              agpio->pin_table[pin_index]);
-               lookup->info.gpioint =
-                       agpio->connection_type == ACPI_RESOURCE_GPIO_TYPE_INT;
+               lookup->info.gpioint = gpioint;
 
                /*
                 * Polarity and triggering are only specified for GpioInt
index cf3f0caf9c632fe47dd5f48999031100e12cf68c..ed7af7518b52be0d08c290bfdd5495fdbc782832 100644 (file)
@@ -614,7 +614,6 @@ static int snd_dw_hdmi_suspend(struct device *dev)
        struct snd_dw_hdmi *dw = dev_get_drvdata(dev);
 
        snd_power_change_state(dw->card, SNDRV_CTL_POWER_D3cold);
-       snd_pcm_suspend_all(dw->pcm);
 
        return 0;
 }
index 2cc05a9d57ac17e854533c1a8d68e5c6e5de1e04..a16242a9206fb6a99f474b11bb194d17e94f4451 100644 (file)
@@ -360,13 +360,11 @@ static int solo_snd_pcm_init(struct solo_dev *solo_dev)
             ss; ss = ss->next, i++)
                sprintf(ss->name, "Camera #%d Audio", i);
 
-       ret = snd_pcm_lib_preallocate_pages_for_all(pcm,
+       snd_pcm_lib_preallocate_pages_for_all(pcm,
                                        SNDRV_DMA_TYPE_CONTINUOUS,
                                        snd_dma_continuous_data(GFP_KERNEL),
                                        G723_PERIOD_BYTES * PERIODS,
                                        G723_PERIOD_BYTES * PERIODS);
-       if (ret < 0)
-               return ret;
 
        solo_dev->snd_pcm = pcm;
 
index a28329698e205a86720b519b8e92612abe9e99a3..fb0e7573b5aea3929a047214d61a1dadd02048d9 100644 (file)
@@ -301,11 +301,12 @@ static int tw686x_snd_pcm_init(struct tw686x_dev *dev)
             ss; ss = ss->next, i++)
                snprintf(ss->name, sizeof(ss->name), "vch%u audio", i);
 
-       return snd_pcm_lib_preallocate_pages_for_all(pcm,
+       snd_pcm_lib_preallocate_pages_for_all(pcm,
                                SNDRV_DMA_TYPE_DEV,
                                snd_dma_pci_data(dev->pci_dev),
                                TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX,
                                TW686X_AUDIO_PAGE_MAX * AUDIO_DMA_SIZE_MAX);
+       return 0;
 }
 
 static void tw686x_audio_dma_free(struct tw686x_dev *dev,
index 87715f20b69a075893b47491b29c4270f67757ee..03b4c4f225d00241eb6ffba3793ccfefa88f9a6d 100644 (file)
@@ -1014,6 +1014,13 @@ struct acpi_gpio_mapping {
 
 /* Ignore IoRestriction field */
 #define ACPI_GPIO_QUIRK_NO_IO_RESTRICTION      BIT(0)
+/*
+ * When ACPI GPIO mapping table is in use the index parameter inside it
+ * refers to the GPIO resource in _CRS method. That index has no
+ * distinction of actual type of the resource. When consumer wants to
+ * get GpioIo type explicitly, this quirk may be used.
+ */
+#define ACPI_GPIO_QUIRK_ONLY_GPIOIO            BIT(1)
 
        unsigned int quirks;
 };
index 36a5934cf4b10fc5083cc08f420a091bb9c24075..e923c23e05dd779bd7b40b35f4b51bc0b569b6d1 100644 (file)
@@ -120,7 +120,6 @@ struct snd_card {
        struct list_head ctl_files;     /* active control files */
 
        struct snd_info_entry *proc_root;       /* root for soundcard specific files */
-       struct snd_info_entry *proc_id; /* the card id */
        struct proc_dir_entry *proc_root_link;  /* number link to real id */
 
        struct list_head files_list;    /* all files associated to this card */
diff --git a/include/sound/cs35l36.h b/include/sound/cs35l36.h
new file mode 100644 (file)
index 0000000..8f8049d
--- /dev/null
@@ -0,0 +1,43 @@
+/* SPDX-License-Identifier: GPL-2.0 */
+/*
+ * linux/sound/cs35l36.h -- Platform data for CS35L36
+ *
+ * Copyright 2018 Cirrus Logic, Inc.
+ *
+ * Author: James Schulman <james.schulman@cirrus.com>
+ *
+ */
+
+#ifndef __CS35L36_H
+#define __CS35L36_H
+
+struct cs35l36_vpbr_cfg {
+       bool is_present;
+       bool vpbr_en;
+       int vpbr_thld;
+       int vpbr_atk_rate;
+       int vpbr_atk_vol;
+       int vpbr_max_attn;
+       int vpbr_wait;
+       int vpbr_rel_rate;
+       int vpbr_mute_en;
+};
+
+struct cs35l36_platform_data {
+       bool multi_amp_mode;
+       bool dcm_mode;
+       bool amp_pcm_inv;
+       bool imon_pol_inv;
+       bool vmon_pol_inv;
+       int boost_ind;
+       int bst_vctl;
+       int bst_vctl_sel;
+       int bst_ipk;
+       bool extern_boost;
+       int temp_warn_thld;
+       int irq_drv_sel;
+       int irq_gpio_sel;
+       struct cs35l36_vpbr_cfg vpbr_config;
+};
+
+#endif /* __CS35L36_H */
index 2c4cfaa135a62eae880e7d7fd78eb611055a7bb5..c679f611658022599a574d62b70f1157eb725b7a 100644 (file)
@@ -99,10 +99,6 @@ void snd_dmaengine_pcm_set_config_from_dai_data(
  * playback.
  */
 #define SND_DMAENGINE_PCM_FLAG_HALF_DUPLEX BIT(3)
-/*
- * The PCM streams have custom channel names specified.
- */
-#define SND_DMAENGINE_PCM_FLAG_CUSTOM_CHANNEL_NAME BIT(4)
 
 /**
  * struct snd_dmaengine_pcm_config - Configuration data for dmaengine based PCM
index 2ab39fb52d7a122b622a033361c647fbac4f14b0..0fd39295b426b0306e62bd7fd5e837c578f87f1c 100644 (file)
@@ -79,6 +79,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 
 /* stream register offsets from stream base */
 #define AZX_REG_SD_CTL                 0x00
+#define AZX_REG_SD_CTL_3B              0x02 /* 3rd byte of SD_CTL register */
 #define AZX_REG_SD_STS                 0x03
 #define AZX_REG_SD_LPIB                        0x04
 #define AZX_REG_SD_CBL                 0x08
@@ -165,6 +166,7 @@ enum { SDI0, SDI1, SDI2, SDI3, SDO0, SDO1, SDO2, SDO3 };
 #define SD_INT_COMPLETE                0x04    /* completion interrupt */
 #define SD_INT_MASK            (SD_INT_DESC_ERR|SD_INT_FIFO_ERR|\
                                 SD_INT_COMPLETE)
+#define SD_CTL_STRIPE_MASK     0x3     /* stripe control mask */
 
 /* SD_STS */
 #define SD_STS_FIFO_READY      0x20    /* FIFO ready */
index 2a8573a00ea60d4b1b5606e7504b100f53757e46..e36b77531c5c38692f99dca0f218a92cd08b6ba0 100644 (file)
@@ -66,6 +66,7 @@ enum {
 #define AC_VERB_GET_CONFIG_DEFAULT             0x0f1c
 /* f20: AFG/MFG */
 #define AC_VERB_GET_SUBSYSTEM_ID               0x0f20
+#define AC_VERB_GET_STRIPE_CONTROL             0x0f24
 #define AC_VERB_GET_CVT_CHAN_COUNT             0x0f2d
 #define AC_VERB_GET_HDMI_DIP_SIZE              0x0f2e
 #define AC_VERB_GET_HDMI_ELDD                  0x0f2f
@@ -110,6 +111,7 @@ enum {
 #define AC_VERB_SET_CONFIG_DEFAULT_BYTES_3     0x71f
 #define AC_VERB_SET_EAPD                               0x788
 #define AC_VERB_SET_CODEC_RESET                        0x7ff
+#define AC_VERB_SET_STRIPE_CONTROL             0x724
 #define AC_VERB_SET_CVT_CHAN_COUNT             0x72d
 #define AC_VERB_SET_HDMI_DIP_INDEX             0x730
 #define AC_VERB_SET_HDMI_DIP_DATA              0x731
index b4fa1c7752510b5216a92f6d663d4fc0d1da3b39..45f944d57982800cc176ed782aedbf9b1d30f1fe 100644 (file)
@@ -539,6 +539,9 @@ void snd_hdac_stream_sync(struct hdac_stream *azx_dev, bool start,
                          unsigned int streams);
 void snd_hdac_stream_timecounter_init(struct hdac_stream *azx_dev,
                                      unsigned int streams);
+int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus,
+                               struct snd_pcm_substream *substream);
+
 /*
  * macros for easy use
  */
index becdf66d2825e9c5a65ff5cbcf63868bf29eac9d..97fdda41e0763ac57f5aa4e9cc1300d77c71702b 100644 (file)
@@ -82,7 +82,6 @@ struct snd_info_entry {
                struct snd_info_entry_ops *ops;
        } c;
        struct snd_info_entry *parent;
-       struct snd_card *card;
        struct module *module;
        void *private_data;
        void (*private_free)(struct snd_info_entry *entry);
@@ -160,6 +159,13 @@ static inline void snd_info_set_text_ops(struct snd_info_entry *entry,
        entry->c.text.read = read;
 }
 
+int snd_card_rw_proc_new(struct snd_card *card, const char *name,
+                        void *private_data,
+                        void (*read)(struct snd_info_entry *,
+                                     struct snd_info_buffer *),
+                        void (*write)(struct snd_info_entry *entry,
+                                      struct snd_info_buffer *buffer));
+
 int snd_info_check_reserved_words(const char *str);
 
 #else
@@ -189,10 +195,38 @@ static inline int snd_card_proc_new(struct snd_card *card, const char *name,
 static inline void snd_info_set_text_ops(struct snd_info_entry *entry __attribute__((unused)),
                                         void *private_data,
                                         void (*read)(struct snd_info_entry *, struct snd_info_buffer *)) {}
+static inline int snd_card_rw_proc_new(struct snd_card *card, const char *name,
+                                      void *private_data,
+                                      void (*read)(struct snd_info_entry *,
+                                                   struct snd_info_buffer *),
+                                      void (*write)(struct snd_info_entry *entry,
+                                                    struct snd_info_buffer *buffer))
+{
+       return 0;
+}
 static inline int snd_info_check_reserved_words(const char *str) { return 1; }
 
 #endif
 
+/**
+ * snd_card_ro_proc_new - Create a read-only text proc file entry for the card
+ * @card: the card instance
+ * @name: the file name
+ * @private_data: the arbitrary private data
+ * @read: the read callback
+ *
+ * This proc file entry will be registered via snd_card_register() call, and
+ * it will be removed automatically at the card removal, too.
+ */
+static inline int
+snd_card_ro_proc_new(struct snd_card *card, const char *name,
+                    void *private_data,
+                    void (*read)(struct snd_info_entry *,
+                                 struct snd_info_buffer *))
+{
+       return snd_card_rw_proc_new(card, name, private_data, read, NULL);
+}
+
 /*
  * OSS info part
  */
index af3fa577fa066ae3aa0ed712e12d7c45eeac0b50..1ac0dd82a9163847dc5280aa55efbead4f671bdb 100644 (file)
@@ -37,7 +37,6 @@ struct snd_dma_device {
 };
 
 #define snd_dma_pci_data(pci)  (&(pci)->dev)
-#define snd_dma_isa_data()     NULL
 #define snd_dma_continuous_data(x)     ((struct device *)(__force unsigned long)(x))
 
 
index d6bd3caf68787490074cd859f980dcac057abf28..465d7d033c4c61446bb288bc76f844304bb285d3 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/mm.h>
 #include <linux/bitops.h>
 #include <linux/pm_qos.h>
+#include <linux/refcount.h>
 
 #define snd_pcm_substream_chip(substream) ((substream)->private_data)
 #define snd_pcm_chip(pcm) ((pcm)->private_data)
@@ -439,7 +440,7 @@ struct snd_pcm_group {              /* keep linked substreams */
        spinlock_t lock;
        struct mutex mutex;
        struct list_head substreams;
-       int count;
+       refcount_t refs;
 };
 
 struct pid;
@@ -470,7 +471,6 @@ struct snd_pcm_substream {
        struct snd_pcm_group self_group;        /* fake group for non linked substream (with substream lock inside) */
        struct snd_pcm_group *group;            /* pointer to current group */
        /* -- assigned files -- */
-       void *file;
        int ref_count;
        atomic_t mmap_count;
        unsigned int f_flags;
@@ -482,15 +482,6 @@ struct snd_pcm_substream {
 #endif
 #ifdef CONFIG_SND_VERBOSE_PROCFS
        struct snd_info_entry *proc_root;
-       struct snd_info_entry *proc_info_entry;
-       struct snd_info_entry *proc_hw_params_entry;
-       struct snd_info_entry *proc_sw_params_entry;
-       struct snd_info_entry *proc_status_entry;
-       struct snd_info_entry *proc_prealloc_entry;
-       struct snd_info_entry *proc_prealloc_max_entry;
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
-       struct snd_info_entry *proc_xrun_injection_entry;
-#endif
 #endif /* CONFIG_SND_VERBOSE_PROCFS */
        /* misc flags */
        unsigned int hw_opened: 1;
@@ -512,10 +503,8 @@ struct snd_pcm_str {
 #endif
 #ifdef CONFIG_SND_VERBOSE_PROCFS
        struct snd_info_entry *proc_root;
-       struct snd_info_entry *proc_info_entry;
 #ifdef CONFIG_SND_PCM_XRUN_DEBUG
        unsigned int xrun_debug;        /* 0 = disabled, 1 = verbose, 2 = stacktrace */
-       struct snd_info_entry *proc_xrun_debug_entry;
 #endif
 #endif
        struct snd_kcontrol *chmap_kctl; /* channel-mapping controls */
@@ -538,6 +527,7 @@ struct snd_pcm {
        void (*private_free) (struct snd_pcm *pcm);
        bool internal; /* pcm is for internal use only */
        bool nonatomic; /* whole PCM operations are in non-atomic context */
+       bool no_device_suspend; /* don't invoke device PM suspend */
 #if IS_ENABLED(CONFIG_SND_PCM_OSS)
        struct snd_pcm_oss oss;
 #endif
@@ -581,13 +571,8 @@ int snd_pcm_stop(struct snd_pcm_substream *substream, snd_pcm_state_t status);
 int snd_pcm_drain_done(struct snd_pcm_substream *substream);
 int snd_pcm_stop_xrun(struct snd_pcm_substream *substream);
 #ifdef CONFIG_PM
-int snd_pcm_suspend(struct snd_pcm_substream *substream);
 int snd_pcm_suspend_all(struct snd_pcm *pcm);
 #else
-static inline int snd_pcm_suspend(struct snd_pcm_substream *substream)
-{
-       return 0;
-}
 static inline int snd_pcm_suspend_all(struct snd_pcm *pcm)
 {
        return 0;
@@ -1200,12 +1185,12 @@ static inline void snd_pcm_gettime(struct snd_pcm_runtime *runtime,
  *  Memory
  */
 
-int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream);
-int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm);
-int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
+void snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream);
+void snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm);
+void snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
                                  int type, struct device *data,
                                  size_t size, size_t max);
-int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
+void snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
                                          int type, void *data,
                                          size_t size, size_t max);
 int snd_pcm_lib_malloc_pages(struct snd_pcm_substream *substream, size_t size);
index 6d69ed2bd7b1115a2f83fdb511466a6ebe8068b8..7afe45389972d49b192c2dfc0c905587c35fd393 100644 (file)
@@ -75,7 +75,7 @@ void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai);
                                   &dai_link->codec_dai_name,                   \
                                   list_name, cells_name, NULL)
 #define asoc_simple_card_parse_platform(node, dai_link, list_name, cells_name) \
-       asoc_simple_card_parse_dai(node, dai_link->platform,                                    \
+       asoc_simple_card_parse_dai(node, dai_link->platforms,                   \
                &dai_link->platform_of_node,                                    \
                NULL, list_name, cells_name, NULL)
 int asoc_simple_card_parse_dai(struct device_node *node,
@@ -108,7 +108,7 @@ int asoc_simple_card_parse_graph_dai(struct device_node *ep,
 int asoc_simple_card_init_dai(struct snd_soc_dai *dai,
                              struct asoc_simple_dai *simple_dai);
 
-int asoc_simple_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link);
+void asoc_simple_card_canonicalize_platform(struct snd_soc_dai_link *dai_link);
 void asoc_simple_card_canonicalize_cpu(struct snd_soc_dai_link *dai_link,
                                      int is_single_links);
 
index 266e64e3c24c4fad654af09c6c70383d844166e7..35b38e41e5b29639c029f24cece34033fc6a7d76 100644 (file)
@@ -22,20 +22,37 @@ struct snd_soc_acpi_package_context {
 #define SND_ACPI_I2C_ID_LEN (4 + ACPI_ID_LEN + 3 + 1)
 
 #if IS_ENABLED(CONFIG_ACPI)
+/* acpi match */
+struct snd_soc_acpi_mach *
+snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines);
+
 bool snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
                                    struct snd_soc_acpi_package_context *ctx);
+
+/* check all codecs */
+struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg);
+
 #else
+/* acpi match */
+static inline struct snd_soc_acpi_mach *
+snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines)
+{
+       return NULL;
+}
+
 static inline bool
 snd_soc_acpi_find_package_from_hid(const u8 hid[ACPI_ID_LEN],
                                   struct snd_soc_acpi_package_context *ctx)
 {
        return false;
 }
-#endif
 
-/* acpi match */
-struct snd_soc_acpi_mach *
-snd_soc_acpi_find_machine(struct snd_soc_acpi_mach *machines);
+/* check all codecs */
+static inline struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg)
+{
+       return NULL;
+}
+#endif
 
 /**
  * snd_soc_acpi_mach_params: interface for machine driver configuration
@@ -69,9 +86,6 @@ struct snd_soc_acpi_mach_params {
  *  is not constant since this field may be updated at run-time
  * @sof_fw_filename: Sound Open Firmware file name, if enabled
  * @sof_tplg_filename: Sound Open Firmware topology file name, if enabled
- * @asoc_plat_name: ASoC platform name, used for binding machine drivers
- * if non NULL
- * @new_mach_data: machine driver private data fixup
  */
 /* Descriptor for SST ASoC machine driver */
 struct snd_soc_acpi_mach {
@@ -85,8 +99,6 @@ struct snd_soc_acpi_mach {
        struct snd_soc_acpi_mach_params mach_params;
        const char *sof_fw_filename;
        const char *sof_tplg_filename;
-       const char *asoc_plat_name;
-       struct platform_device * (*new_mach_data)(void *pdata);
 };
 
 #define SND_SOC_ACPI_MAX_CODECS 3
@@ -105,7 +117,4 @@ struct snd_soc_acpi_codecs {
        u8 codecs[SND_SOC_ACPI_MAX_CODECS][ACPI_ID_LEN];
 };
 
-/* check all codecs */
-struct snd_soc_acpi_mach *snd_soc_acpi_codec_list(void *arg);
-
 #endif
index bd8163f151cb855d5e0c334c47aefde0c9128588..c00a0b8ade08613572babd9eac02ebdc7bf09ebc 100644 (file)
@@ -214,21 +214,21 @@ struct device;
        .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD}
 
 /* stream domain */
-#define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \
+#define SND_SOC_DAPM_AIF_IN(wname, stname, wchan, wreg, wshift, winvert) \
 {      .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
-       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), }
-#define SND_SOC_DAPM_AIF_IN_E(wname, stname, wslot, wreg, wshift, winvert, \
+       .channel = wchan, SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), }
+#define SND_SOC_DAPM_AIF_IN_E(wname, stname, wchan, wreg, wshift, winvert, \
                              wevent, wflags)                           \
 {      .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
-       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .channel = wchan, SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
        .event = wevent, .event_flags = wflags }
-#define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \
+#define SND_SOC_DAPM_AIF_OUT(wname, stname, wchan, wreg, wshift, winvert) \
 {      .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
-       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), }
-#define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wslot, wreg, wshift, winvert, \
+       .channel = wchan, SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), }
+#define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wchan, wreg, wshift, winvert, \
                             wevent, wflags)                            \
 {      .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
-       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .channel = wchan, SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
        .event = wevent, .event_flags = wflags }
 #define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
 {      .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \
@@ -407,6 +407,10 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm,
 int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card);
 void snd_soc_dapm_connect_dai_link_widgets(struct snd_soc_card *card);
 
+int snd_soc_dapm_update_dai(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai);
+
 /* dapm path setup */
 int snd_soc_dapm_new_widgets(struct snd_soc_card *card);
 void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm);
@@ -519,6 +523,9 @@ enum snd_soc_dapm_type {
        snd_soc_dapm_asrc,              /* DSP/CODEC ASRC component */
        snd_soc_dapm_encoder,           /* FW/SW audio encoder component */
        snd_soc_dapm_decoder,           /* FW/SW audio decoder component */
+
+       /* Don't edit below this line */
+       SND_SOC_DAPM_TYPE_COUNT
 };
 
 enum snd_soc_dapm_subclass {
@@ -540,6 +547,8 @@ struct snd_soc_dapm_route {
        /* Note: currently only supported for links where source is a supply */
        int (*connected)(struct snd_soc_dapm_widget *source,
                         struct snd_soc_dapm_widget *sink);
+
+       struct snd_soc_dobj dobj;
 };
 
 /* dapm audio path between two widgets */
@@ -625,6 +634,8 @@ struct snd_soc_dapm_widget {
        int endpoints[2];
 
        struct clk *clk;
+
+       int channel;
 };
 
 struct snd_soc_dapm_update {
index fa4b8413d2e222dd51e884f69e563c010b854980..5223896de26f51740af0061dfd71f9fff57034c7 100644 (file)
@@ -38,12 +38,14 @@ struct snd_soc_dapm_route;
 enum snd_soc_dobj_type {
        SND_SOC_DOBJ_NONE               = 0,    /* object is not dynamic */
        SND_SOC_DOBJ_MIXER,
-       SND_SOC_DOBJ_ENUM,
        SND_SOC_DOBJ_BYTES,
-       SND_SOC_DOBJ_PCM,
+       SND_SOC_DOBJ_ENUM,
+       SND_SOC_DOBJ_GRAPH,
+       SND_SOC_DOBJ_WIDGET,
        SND_SOC_DOBJ_DAI_LINK,
+       SND_SOC_DOBJ_PCM,
        SND_SOC_DOBJ_CODEC_LINK,
-       SND_SOC_DOBJ_WIDGET,
+       SND_SOC_DOBJ_BACKEND_LINK,
 };
 
 /* dynamic control object */
index e665f111b0d27548015b2b35e8f4c7bfab098df1..eb7db605955b852a469d11b62a93817f7726cf88 100644 (file)
@@ -802,6 +802,9 @@ struct snd_soc_component_driver {
        int probe_order;
        int remove_order;
 
+       /* signal if the module handling the component cannot be removed */
+       unsigned int ignore_module_refcount:1;
+
        /* bits */
        unsigned int idle_bias_on:1;
        unsigned int suspend_bias_off:1;
@@ -891,6 +894,18 @@ struct snd_soc_dai_link {
        /* config - must be set by machine driver */
        const char *name;                       /* Codec name */
        const char *stream_name;                /* Stream name */
+
+       /*
+        *      cpu_name
+        *      cpu_of_node
+        *      cpu_dai_name
+        *
+        * These are legacy style, and will be replaced to
+        * modern style (= snd_soc_dai_link_component) in the future,
+        * but, not yet supported so far.
+        * If modern style was supported for CPU, all driver will switch
+        * to use it, and, legacy style code will be removed from ALSA SoC.
+        */
        /*
         * You MAY specify the link's CPU-side device, either by device name,
         * or by DT/OF node, but not both. If this information is omitted,
@@ -906,6 +921,19 @@ struct snd_soc_dai_link {
         * only, which only works well when that device exposes a single DAI.
         */
        const char *cpu_dai_name;
+
+       /*
+        *      codec_name
+        *      codec_of_node
+        *      codec_dai_name
+        *
+        * These are legacy style, it will be converted to modern style
+        * (= snd_soc_dai_link_component) automatically in soc-core
+        * if driver is using legacy style.
+        * Driver shouldn't use both legacy and modern style in the same time.
+        * If modern style was supported for CPU, all driver will switch
+        * to use it, and, legacy style code will be removed from ALSA SoC.
+        */
        /*
         * You MUST specify the link's codec, either by device name, or by
         * DT/OF node, but not both.
@@ -918,6 +946,17 @@ struct snd_soc_dai_link {
        struct snd_soc_dai_link_component *codecs;
        unsigned int num_codecs;
 
+       /*
+        *      platform_name
+        *      platform_of_node
+        *
+        * These are legacy style, it will be converted to modern style
+        * (= snd_soc_dai_link_component) automatically in soc-core
+        * if driver is using legacy style.
+        * Driver shouldn't use both legacy and modern style in the same time.
+        * If modern style was supported for CPU, all driver will switch
+        * to use it, and, legacy style code will be removed from ALSA SoC.
+        */
        /*
         * You MAY specify the link's platform/PCM/DMA driver, either by
         * device name, or by DT/OF node, but not both. Some forms of link
@@ -925,7 +964,8 @@ struct snd_soc_dai_link {
         */
        const char *platform_name;
        struct device_node *platform_of_node;
-       struct snd_soc_dai_link_component *platform;
+       struct snd_soc_dai_link_component *platforms;
+       unsigned int num_platforms;
 
        int id; /* optional ID for machine driver link identification */
 
@@ -1543,6 +1583,37 @@ struct snd_soc_dai *snd_soc_card_get_codec_dai(struct snd_soc_card *card,
        return NULL;
 }
 
+static inline
+int snd_soc_fixup_dai_links_platform_name(struct snd_soc_card *card,
+                                         const char *platform_name)
+{
+       struct snd_soc_dai_link *dai_link;
+       const char *name;
+       int i;
+
+       if (!platform_name) /* nothing to do */
+               return 0;
+
+       /* set platform name for each dailink */
+       for_each_card_prelinks(card, i, dai_link) {
+               name = devm_kstrdup(card->dev, platform_name, GFP_KERNEL);
+               if (!name)
+                       return -ENOMEM;
+
+               if (dai_link->platforms)
+                       /* only single platform is supported for now */
+                       dai_link->platforms->name = name;
+               else
+                       /*
+                        * legacy mode, this case will be removed when all
+                        * derivers are switched to modern style dai_link.
+                        */
+                       dai_link->platform_name = name;
+       }
+
+       return 0;
+}
+
 #ifdef CONFIG_DEBUG_FS
 extern struct dentry *snd_soc_debugfs_root;
 #endif
index 9f0c480489ef04384445b35f8786c020dfc9d7fa..9cbf6927abe96a735f0e188cfd735710c1d989ad 100644 (file)
@@ -84,7 +84,7 @@ ac97_of_get_child_device(struct ac97_controller *ac97_ctrl, int idx,
                if ((idx != of_property_read_u32(node, "reg", &reg)) ||
                    !of_device_is_compatible(node, compat))
                        continue;
-               return of_node_get(node);
+               return node;
        }
 
        return NULL;
index 65557421fe0bad1c02d7975c545828d7e5bb4ec4..c3ff721e46607183f21afe6526db6cbcf0cd3956 100644 (file)
@@ -82,6 +82,7 @@ static struct device_node *get_gpio(char *name,
                        if (altname && (strcmp(audio_gpio, altname) == 0))
                                break;
                }
+               of_node_put(gpio);
                /* still not found, assume not there */
                if (!np)
                        return NULL;
index 40ebde2e1ab1bb2eee05a5c902fe18ea051f37df..904659d149888e00ef6f2e6278b202f879f7cee4 100644 (file)
@@ -380,10 +380,6 @@ static int i2sbus_suspend(struct macio_dev* dev, pm_message_t state)
        int err, ret = 0;
 
        list_for_each_entry(i2sdev, &control->list, item) {
-               /* Notify Alsa */
-               /* Suspend PCM streams */
-               snd_pcm_suspend_all(i2sdev->sound.pcm);
-
                /* Notify codecs */
                list_for_each_entry(cii, &i2sdev->sound.codec_list, list) {
                        err = 0;
index 0114ffed56dd4d2655d89e950774869dc2780d05..a2d4b41096e019583c920c023678d782fe4db5d3 100644 (file)
@@ -757,7 +757,6 @@ static int aaci_do_suspend(struct snd_card *card)
 {
        struct aaci *aaci = card->private_data;
        snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
-       snd_pcm_suspend_all(aaci->pcm);
        return 0;
 }
 
@@ -942,7 +941,8 @@ static int aaci_init_pcm(struct aaci *aaci)
                snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &aaci_playback_ops);
                snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &aaci_capture_ops);
                snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
-                                                     NULL, 0, 64 * 1024);
+                                                     aaci->card->dev,
+                                                     0, 64 * 1024);
        }
 
        return ret;
index 1f72672262d06406d21ea4fc00c400fac24fd94b..68fe5bb11eea69f68b5609fcb293615644fd64ae 100644 (file)
@@ -124,7 +124,6 @@ static int pxa2xx_ac97_do_suspend(struct snd_card *card)
        pxa2xx_audio_ops_t *platform_ops = card->dev->platform_data;
 
        snd_power_change_state(card, SNDRV_CTL_POWER_D3cold);
-       snd_pcm_suspend_all(pxa2xx_ac97_pcm);
        snd_ac97_suspend(pxa2xx_ac97_ac97);
        if (platform_ops && platform_ops->suspend)
                platform_ops->suspend(platform_ops->priv);
index 380025887aef61e2aa87a7845d12a7d1ba9b85ae..33c87a0547a96e9dd034287067ab4f748fe818bc 100644 (file)
@@ -603,11 +603,9 @@ static int atmel_ac97c_pcm_new(struct atmel_ac97c *chip)
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &atmel_ac97_capture_ops);
        snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &atmel_ac97_playback_ops);
 
-       retval = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
+       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
                        &chip->pdev->dev, hw.periods_min * hw.period_bytes_min,
                        hw.buffer_bytes_max);
-       if (retval)
-               return retval;
 
        pcm->private_data = chip;
        pcm->info_flags = 0;
index f7d2b373da0aaebd869b06773c24b7994c0cb472..a1a6fd75cfe50003b7ee2cf8af80cdf83d042ef3 100644 (file)
@@ -1015,22 +1015,13 @@ static int snd_compress_proc_init(struct snd_compr *compr)
        if (!entry)
                return -ENOMEM;
        entry->mode = S_IFDIR | 0555;
-       if (snd_info_register(entry) < 0) {
-               snd_info_free_entry(entry);
-               return -ENOMEM;
-       }
        compr->proc_root = entry;
 
        entry = snd_info_create_card_entry(compr->card, "info",
                                           compr->proc_root);
-       if (entry) {
+       if (entry)
                snd_info_set_text_ops(entry, compr,
                                      snd_compress_proc_info_read);
-               if (snd_info_register(entry) < 0) {
-                       snd_info_free_entry(entry);
-                       entry = NULL;
-               }
-       }
        compr->proc_info_entry = entry;
 
        return 0;
index fe502bc5e6d20e8d800739bbca65a16b6085f155..96a074019c33c28b5587d7d833110eced98669c0 100644 (file)
@@ -463,11 +463,12 @@ static struct snd_info_entry *create_subdir(struct module *mod,
 }
 
 static struct snd_info_entry *
-snd_info_create_entry(const char *name, struct snd_info_entry *parent);
+snd_info_create_entry(const char *name, struct snd_info_entry *parent,
+                     struct module *module);
 
 int __init snd_info_init(void)
 {
-       snd_proc_root = snd_info_create_entry("asound", NULL);
+       snd_proc_root = snd_info_create_entry("asound", NULL, THIS_MODULE);
        if (!snd_proc_root)
                return -ENOMEM;
        snd_proc_root->mode = S_IFDIR | 0555;
@@ -503,6 +504,14 @@ int __exit snd_info_done(void)
        return 0;
 }
 
+static void snd_card_id_read(struct snd_info_entry *entry,
+                            struct snd_info_buffer *buffer)
+{
+       struct snd_card *card = entry->private_data;
+
+       snd_iprintf(buffer, "%s\n", card->id);
+}
+
 /*
  * create a card proc file
  * called from init.c
@@ -520,28 +529,8 @@ int snd_info_card_create(struct snd_card *card)
        if (!entry)
                return -ENOMEM;
        card->proc_root = entry;
-       return 0;
-}
-
-/* register all pending info entries */
-static int snd_info_register_recursive(struct snd_info_entry *entry)
-{
-       struct snd_info_entry *p;
-       int err;
 
-       if (!entry->p) {
-               err = snd_info_register(entry);
-               if (err < 0)
-                       return err;
-       }
-
-       list_for_each_entry(p, &entry->children, list) {
-               err = snd_info_register_recursive(p);
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
+       return snd_card_ro_proc_new(card, "id", card, snd_card_id_read);
 }
 
 /*
@@ -557,7 +546,7 @@ int snd_info_card_register(struct snd_card *card)
        if (snd_BUG_ON(!card))
                return -ENXIO;
 
-       err = snd_info_register_recursive(card->proc_root);
+       err = snd_info_register(card->proc_root);
        if (err < 0)
                return err;
 
@@ -705,7 +694,8 @@ EXPORT_SYMBOL(snd_info_get_str);
  * Return: The pointer of the new instance, or %NULL on failure.
  */
 static struct snd_info_entry *
-snd_info_create_entry(const char *name, struct snd_info_entry *parent)
+snd_info_create_entry(const char *name, struct snd_info_entry *parent,
+                     struct module *module)
 {
        struct snd_info_entry *entry;
        entry = kzalloc(sizeof(*entry), GFP_KERNEL);
@@ -722,6 +712,7 @@ snd_info_create_entry(const char *name, struct snd_info_entry *parent)
        INIT_LIST_HEAD(&entry->children);
        INIT_LIST_HEAD(&entry->list);
        entry->parent = parent;
+       entry->module = module;
        if (parent)
                list_add_tail(&entry->list, &parent->children);
        return entry;
@@ -741,10 +732,9 @@ struct snd_info_entry *snd_info_create_module_entry(struct module * module,
                                               const char *name,
                                               struct snd_info_entry *parent)
 {
-       struct snd_info_entry *entry = snd_info_create_entry(name, parent);
-       if (entry)
-               entry->module = module;
-       return entry;
+       if (!parent)
+               parent = snd_proc_root;
+       return snd_info_create_entry(name, parent, module);
 }
 EXPORT_SYMBOL(snd_info_create_module_entry);
 
@@ -762,12 +752,9 @@ struct snd_info_entry *snd_info_create_card_entry(struct snd_card *card,
                                             const char *name,
                                             struct snd_info_entry * parent)
 {
-       struct snd_info_entry *entry = snd_info_create_entry(name, parent);
-       if (entry) {
-               entry->module = card->module;
-               entry->card = card;
-       }
-       return entry;
+       if (!parent)
+               parent = card->proc_root;
+       return snd_info_create_entry(name, parent, card->module);
 }
 EXPORT_SYMBOL(snd_info_create_card_entry);
 
@@ -813,15 +800,7 @@ void snd_info_free_entry(struct snd_info_entry * entry)
 }
 EXPORT_SYMBOL(snd_info_free_entry);
 
-/**
- * snd_info_register - register the info entry
- * @entry: the info entry
- *
- * Registers the proc info entry.
- *
- * Return: Zero if successful, or a negative error code on failure.
- */
-int snd_info_register(struct snd_info_entry * entry)
+static int __snd_info_register(struct snd_info_entry *entry)
 {
        struct proc_dir_entry *root, *p = NULL;
 
@@ -829,6 +808,8 @@ int snd_info_register(struct snd_info_entry * entry)
                return -ENXIO;
        root = entry->parent == NULL ? snd_proc_root->p : entry->parent->p;
        mutex_lock(&info_mutex);
+       if (entry->p || !root)
+               goto unlock;
        if (S_ISDIR(entry->mode)) {
                p = proc_mkdir_mode(entry->name, entry->mode, root);
                if (!p) {
@@ -850,11 +831,73 @@ int snd_info_register(struct snd_info_entry * entry)
                proc_set_size(p, entry->size);
        }
        entry->p = p;
+ unlock:
        mutex_unlock(&info_mutex);
        return 0;
 }
+
+/**
+ * snd_info_register - register the info entry
+ * @entry: the info entry
+ *
+ * Registers the proc info entry.
+ * The all children entries are registered recursively.
+ *
+ * Return: Zero if successful, or a negative error code on failure.
+ */
+int snd_info_register(struct snd_info_entry *entry)
+{
+       struct snd_info_entry *p;
+       int err;
+
+       if (!entry->p) {
+               err = __snd_info_register(entry);
+               if (err < 0)
+                       return err;
+       }
+
+       list_for_each_entry(p, &entry->children, list) {
+               err = snd_info_register(p);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
 EXPORT_SYMBOL(snd_info_register);
 
+/**
+ * snd_card_rw_proc_new - Create a read/write text proc file entry for the card
+ * @card: the card instance
+ * @name: the file name
+ * @private_data: the arbitrary private data
+ * @read: the read callback
+ * @write: the write callback, NULL for read-only
+ *
+ * This proc file entry will be registered via snd_card_register() call, and
+ * it will be removed automatically at the card removal, too.
+ */
+int snd_card_rw_proc_new(struct snd_card *card, const char *name,
+                        void *private_data,
+                        void (*read)(struct snd_info_entry *,
+                                     struct snd_info_buffer *),
+                        void (*write)(struct snd_info_entry *entry,
+                                      struct snd_info_buffer *buffer))
+{
+       struct snd_info_entry *entry;
+
+       entry = snd_info_create_card_entry(card, name, card->proc_root);
+       if (!entry)
+               return -ENOMEM;
+       snd_info_set_text_ops(entry, private_data, read);
+       if (write) {
+               entry->mode |= 0200;
+               entry->c.text.write = write;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_card_rw_proc_new);
+
 /*
 
  */
index 4849c611c0fe3a94d98aa3eef778f3da5538575f..0c4dc40376a709ff2e8aabd2f9ac4d25660389be 100644 (file)
@@ -100,31 +100,6 @@ int (*snd_mixer_oss_notify_callback)(struct snd_card *card, int free_flag);
 EXPORT_SYMBOL(snd_mixer_oss_notify_callback);
 #endif
 
-#ifdef CONFIG_SND_PROC_FS
-static void snd_card_id_read(struct snd_info_entry *entry,
-                            struct snd_info_buffer *buffer)
-{
-       snd_iprintf(buffer, "%s\n", entry->card->id);
-}
-
-static int init_info_for_card(struct snd_card *card)
-{
-       struct snd_info_entry *entry;
-
-       entry = snd_info_create_card_entry(card, "id", card->proc_root);
-       if (!entry) {
-               dev_dbg(card->dev, "unable to create card entry\n");
-               return -ENOMEM;
-       }
-       entry->c.text.read = snd_card_id_read;
-       card->proc_id = entry;
-
-       return snd_info_card_register(card);
-}
-#else /* !CONFIG_SND_PROC_FS */
-#define init_info_for_card(card)
-#endif
-
 static int check_empty_slot(struct module *module, int slot)
 {
        return !slots[slot] || !*slots[slot];
@@ -491,7 +466,6 @@ static int snd_card_do_free(struct snd_card *card)
        snd_device_free_all(card);
        if (card->private_free)
                card->private_free(card);
-       snd_info_free_entry(card->proc_id);
        if (snd_info_card_free(card) < 0) {
                dev_warn(card->dev, "unable to free card info\n");
                /* Not fatal error */
@@ -795,7 +769,10 @@ int snd_card_register(struct snd_card *card)
        }
        snd_cards[card->number] = card;
        mutex_unlock(&snd_card_mutex);
-       init_info_for_card(card);
+       err = snd_info_card_register(card);
+       if (err < 0)
+               return err;
+
 #if IS_ENABLED(CONFIG_SND_MIXER_OSS)
        if (snd_mixer_oss_notify_callback)
                snd_mixer_oss_notify_callback(card, SND_MIXER_OSS_NOTIFY_REGISTER);
index 59a4adc286ed77bb647788706a6ebd9323a1b640..eb974235c92bdfba19d706735a88a9cf95897664 100644 (file)
@@ -182,6 +182,8 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size,
                return -ENXIO;
        if (WARN_ON(!dmab))
                return -ENXIO;
+       if (WARN_ON(!device))
+               return -EINVAL;
 
        dmab->dev.type = type;
        dmab->dev.dev = device;
index 467039b342b511c7c4fb95f0d7c316f7715d7c70..d5b0d7ba83c4204db42df492a5e35f54a67c470c 100644 (file)
@@ -2427,7 +2427,6 @@ static int snd_pcm_oss_open_file(struct file *file,
                }
 
                pcm_oss_file->streams[idx] = substream;
-               substream->file = pcm_oss_file;
                snd_pcm_oss_init_substream(substream, &setup[idx], minor);
        }
        
index 01b9d62eef14db9f22bd1fb67bcaf5456f791d38..7b63aee124af3e3394c3f9646747f61f97cb141c 100644 (file)
@@ -528,52 +528,44 @@ static int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr)
        if (!entry)
                return -ENOMEM;
        entry->mode = S_IFDIR | 0555;
-       if (snd_info_register(entry) < 0) {
-               snd_info_free_entry(entry);
-               return -ENOMEM;
-       }
        pstr->proc_root = entry;
        entry = snd_info_create_card_entry(pcm->card, "info", pstr->proc_root);
-       if (entry) {
+       if (entry)
                snd_info_set_text_ops(entry, pstr, snd_pcm_stream_proc_info_read);
-               if (snd_info_register(entry) < 0) {
-                       snd_info_free_entry(entry);
-                       entry = NULL;
-               }
-       }
-       pstr->proc_info_entry = entry;
-
 #ifdef CONFIG_SND_PCM_XRUN_DEBUG
        entry = snd_info_create_card_entry(pcm->card, "xrun_debug",
                                           pstr->proc_root);
        if (entry) {
-               entry->c.text.read = snd_pcm_xrun_debug_read;
+               snd_info_set_text_ops(entry, pstr, snd_pcm_xrun_debug_read);
                entry->c.text.write = snd_pcm_xrun_debug_write;
                entry->mode |= 0200;
-               entry->private_data = pstr;
-               if (snd_info_register(entry) < 0) {
-                       snd_info_free_entry(entry);
-                       entry = NULL;
-               }
        }
-       pstr->proc_xrun_debug_entry = entry;
 #endif
        return 0;
 }
 
 static int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr)
 {
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
-       snd_info_free_entry(pstr->proc_xrun_debug_entry);
-       pstr->proc_xrun_debug_entry = NULL;
-#endif
-       snd_info_free_entry(pstr->proc_info_entry);
-       pstr->proc_info_entry = NULL;
        snd_info_free_entry(pstr->proc_root);
        pstr->proc_root = NULL;
        return 0;
 }
 
+static struct snd_info_entry *
+create_substream_info_entry(struct snd_pcm_substream *substream,
+                           const char *name,
+                           void (*read)(struct snd_info_entry *,
+                                        struct snd_info_buffer *))
+{
+       struct snd_info_entry *entry;
+
+       entry = snd_info_create_card_entry(substream->pcm->card, name,
+                                          substream->proc_root);
+       if (entry)
+               snd_info_set_text_ops(entry, substream, read);
+       return entry;
+}
+
 static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
 {
        struct snd_info_entry *entry;
@@ -588,101 +580,61 @@ static int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream)
        if (!entry)
                return -ENOMEM;
        entry->mode = S_IFDIR | 0555;
-       if (snd_info_register(entry) < 0) {
-               snd_info_free_entry(entry);
-               return -ENOMEM;
-       }
        substream->proc_root = entry;
-       entry = snd_info_create_card_entry(card, "info", substream->proc_root);
-       if (entry) {
-               snd_info_set_text_ops(entry, substream,
-                                     snd_pcm_substream_proc_info_read);
-               if (snd_info_register(entry) < 0) {
-                       snd_info_free_entry(entry);
-                       entry = NULL;
-               }
-       }
-       substream->proc_info_entry = entry;
-       entry = snd_info_create_card_entry(card, "hw_params",
-                                          substream->proc_root);
-       if (entry) {
-               snd_info_set_text_ops(entry, substream,
-                                     snd_pcm_substream_proc_hw_params_read);
-               if (snd_info_register(entry) < 0) {
-                       snd_info_free_entry(entry);
-                       entry = NULL;
-               }
-       }
-       substream->proc_hw_params_entry = entry;
-       entry = snd_info_create_card_entry(card, "sw_params",
-                                          substream->proc_root);
-       if (entry) {
-               snd_info_set_text_ops(entry, substream,
-                                     snd_pcm_substream_proc_sw_params_read);
-               if (snd_info_register(entry) < 0) {
-                       snd_info_free_entry(entry);
-                       entry = NULL;
-               }
-       }
-       substream->proc_sw_params_entry = entry;
-       entry = snd_info_create_card_entry(card, "status",
-                                          substream->proc_root);
-       if (entry) {
-               snd_info_set_text_ops(entry, substream,
-                                     snd_pcm_substream_proc_status_read);
-               if (snd_info_register(entry) < 0) {
-                       snd_info_free_entry(entry);
-                       entry = NULL;
-               }
-       }
-       substream->proc_status_entry = entry;
+
+       create_substream_info_entry(substream, "info",
+                                   snd_pcm_substream_proc_info_read);
+       create_substream_info_entry(substream, "hw_params",
+                                   snd_pcm_substream_proc_hw_params_read);
+       create_substream_info_entry(substream, "sw_params",
+                                   snd_pcm_substream_proc_sw_params_read);
+       create_substream_info_entry(substream, "status",
+                                   snd_pcm_substream_proc_status_read);
 
 #ifdef CONFIG_SND_PCM_XRUN_DEBUG
-       entry = snd_info_create_card_entry(card, "xrun_injection",
-                                          substream->proc_root);
+       entry = create_substream_info_entry(substream, "xrun_injection", NULL);
        if (entry) {
-               entry->private_data = substream;
-               entry->c.text.read = NULL;
                entry->c.text.write = snd_pcm_xrun_injection_write;
                entry->mode = S_IFREG | 0200;
-               if (snd_info_register(entry) < 0) {
-                       snd_info_free_entry(entry);
-                       entry = NULL;
-               }
        }
-       substream->proc_xrun_injection_entry = entry;
 #endif /* CONFIG_SND_PCM_XRUN_DEBUG */
 
        return 0;
 }
 
-static int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream)
-{
-       snd_info_free_entry(substream->proc_info_entry);
-       substream->proc_info_entry = NULL;
-       snd_info_free_entry(substream->proc_hw_params_entry);
-       substream->proc_hw_params_entry = NULL;
-       snd_info_free_entry(substream->proc_sw_params_entry);
-       substream->proc_sw_params_entry = NULL;
-       snd_info_free_entry(substream->proc_status_entry);
-       substream->proc_status_entry = NULL;
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
-       snd_info_free_entry(substream->proc_xrun_injection_entry);
-       substream->proc_xrun_injection_entry = NULL;
-#endif
-       snd_info_free_entry(substream->proc_root);
-       substream->proc_root = NULL;
-       return 0;
-}
 #else /* !CONFIG_SND_VERBOSE_PROCFS */
 static inline int snd_pcm_stream_proc_init(struct snd_pcm_str *pstr) { return 0; }
 static inline int snd_pcm_stream_proc_done(struct snd_pcm_str *pstr) { return 0; }
 static inline int snd_pcm_substream_proc_init(struct snd_pcm_substream *substream) { return 0; }
-static inline int snd_pcm_substream_proc_done(struct snd_pcm_substream *substream) { return 0; }
 #endif /* CONFIG_SND_VERBOSE_PROCFS */
 
 static const struct attribute_group *pcm_dev_attr_groups[];
 
+/*
+ * PM callbacks: we need to deal only with suspend here, as the resume is
+ * triggered either from user-space or the driver's resume callback
+ */
+#ifdef CONFIG_PM_SLEEP
+static int do_pcm_suspend(struct device *dev)
+{
+       struct snd_pcm_str *pstr = container_of(dev, struct snd_pcm_str, dev);
+
+       if (!pstr->pcm->no_device_suspend)
+               snd_pcm_suspend_all(pstr->pcm);
+       return 0;
+}
+#endif
+
+static const struct dev_pm_ops pcm_dev_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(do_pcm_suspend, NULL)
+};
+
+/* device type for PCM -- basically only for passing PM callbacks */
+static const struct device_type pcm_dev_type = {
+       .name = "pcm",
+       .pm = &pcm_dev_pm_ops,
+};
+
 /**
  * snd_pcm_new_stream - create a new PCM stream
  * @pcm: the pcm instance
@@ -713,6 +665,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
 
        snd_device_initialize(&pstr->dev, pcm->card);
        pstr->dev.groups = pcm_dev_attr_groups;
+       pstr->dev.type = &pcm_dev_type;
        dev_set_name(&pstr->dev, "pcmC%iD%i%c", pcm->card->number, pcm->device,
                     stream == SNDRV_PCM_STREAM_PLAYBACK ? 'p' : 'c');
 
@@ -753,9 +706,7 @@ int snd_pcm_new_stream(struct snd_pcm *pcm, int stream, int substream_count)
                        }
                }
                substream->group = &substream->self_group;
-               spin_lock_init(&substream->self_group.lock);
-               mutex_init(&substream->self_group.mutex);
-               INIT_LIST_HEAD(&substream->self_group.substreams);
+               snd_pcm_group_init(&substream->self_group);
                list_add_tail(&substream->link_list, &substream->self_group.substreams);
                atomic_set(&substream->mmap_count, 0);
                prev = substream;
@@ -885,15 +836,17 @@ static void snd_pcm_free_stream(struct snd_pcm_str * pstr)
 #if IS_ENABLED(CONFIG_SND_PCM_OSS)
        struct snd_pcm_oss_setup *setup, *setupn;
 #endif
+
+       /* free all proc files under the stream */
+       snd_pcm_stream_proc_done(pstr);
+
        substream = pstr->substream;
        while (substream) {
                substream_next = substream->next;
                snd_pcm_timer_done(substream);
-               snd_pcm_substream_proc_done(substream);
                kfree(substream);
                substream = substream_next;
        }
-       snd_pcm_stream_proc_done(pstr);
 #if IS_ENABLED(CONFIG_SND_PCM_OSS)
        for (setup = pstr->oss.setup_list; setup; setup = setupn) {
                setupn = setup->next;
index 6c0b30391ba99ced198ee7f7c943083899c4b93d..345ab1ab2cac49f2ee639858de03a33a684a73b6 100644 (file)
@@ -2176,6 +2176,10 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
        if (runtime->status->state == SNDRV_PCM_STATE_RUNNING)
                snd_pcm_update_hw_ptr(substream);
 
+       /*
+        * If size < start_threshold, wait indefinitely. Another
+        * thread may start capture
+        */
        if (!is_playback &&
            runtime->status->state == SNDRV_PCM_STATE_PREPARED &&
            size >= runtime->start_threshold) {
@@ -2214,9 +2218,8 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream,
                if (frames > cont)
                        frames = cont;
                if (snd_BUG_ON(!frames)) {
-                       runtime->twake = 0;
-                       snd_pcm_stream_unlock_irq(substream);
-                       return -EINVAL;
+                       err = -EINVAL;
+                       goto _end_unlock;
                }
                snd_pcm_stream_unlock_irq(substream);
                err = writer(substream, appl_ofs, data, offset, frames,
index c515612969a474485b2a843591a15a42605e8a0a..0b4b5dfaec181924423eff390866ab95ec8044c3 100644 (file)
@@ -66,5 +66,6 @@ static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {}
 #endif
 
 void __snd_pcm_xrun(struct snd_pcm_substream *substream);
+void snd_pcm_group_init(struct snd_pcm_group *group);
 
 #endif /* __SOUND_CORE_PCM_LOCAL_H */
index 4b5356a1031599afb2158f4f703dbdc1998d68cb..ed73be80bd297157009b5c86656447f7dcedcf24 100644 (file)
@@ -87,19 +87,10 @@ static void snd_pcm_lib_preallocate_dma_free(struct snd_pcm_substream *substream
  * @substream: the pcm substream instance
  *
  * Releases the pre-allocated buffer of the given substream.
- *
- * Return: Zero if successful, or a negative error code on failure.
  */
-int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
+void snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
 {
        snd_pcm_lib_preallocate_dma_free(substream);
-#ifdef CONFIG_SND_VERBOSE_PROCFS
-       snd_info_free_entry(substream->proc_prealloc_max_entry);
-       substream->proc_prealloc_max_entry = NULL;
-       snd_info_free_entry(substream->proc_prealloc_entry);
-       substream->proc_prealloc_entry = NULL;
-#endif
-       return 0;
 }
 
 /**
@@ -107,10 +98,8 @@ int snd_pcm_lib_preallocate_free(struct snd_pcm_substream *substream)
  * @pcm: the pcm instance
  *
  * Releases all the pre-allocated buffers on the given pcm.
- *
- * Return: Zero if successful, or a negative error code on failure.
  */
-int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
+void snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
 {
        struct snd_pcm_substream *substream;
        int stream;
@@ -118,7 +107,6 @@ int snd_pcm_lib_preallocate_free_for_all(struct snd_pcm *pcm)
        for (stream = 0; stream < 2; stream++)
                for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
                        snd_pcm_lib_preallocate_free(substream);
-       return 0;
 }
 EXPORT_SYMBOL(snd_pcm_lib_preallocate_free_for_all);
 
@@ -198,26 +186,19 @@ static inline void preallocate_info_init(struct snd_pcm_substream *substream)
 {
        struct snd_info_entry *entry;
 
-       if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc", substream->proc_root)) != NULL) {
-               entry->c.text.read = snd_pcm_lib_preallocate_proc_read;
+       entry = snd_info_create_card_entry(substream->pcm->card, "prealloc",
+                                          substream->proc_root);
+       if (entry) {
+               snd_info_set_text_ops(entry, substream,
+                                     snd_pcm_lib_preallocate_proc_read);
                entry->c.text.write = snd_pcm_lib_preallocate_proc_write;
                entry->mode |= 0200;
-               entry->private_data = substream;
-               if (snd_info_register(entry) < 0) {
-                       snd_info_free_entry(entry);
-                       entry = NULL;
-               }
        }
-       substream->proc_prealloc_entry = entry;
-       if ((entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max", substream->proc_root)) != NULL) {
-               entry->c.text.read = snd_pcm_lib_preallocate_max_proc_read;
-               entry->private_data = substream;
-               if (snd_info_register(entry) < 0) {
-                       snd_info_free_entry(entry);
-                       entry = NULL;
-               }
-       }
-       substream->proc_prealloc_max_entry = entry;
+       entry = snd_info_create_card_entry(substream->pcm->card, "prealloc_max",
+                                          substream->proc_root);
+       if (entry)
+               snd_info_set_text_ops(entry, substream,
+                                     snd_pcm_lib_preallocate_max_proc_read);
 }
 
 #else /* !CONFIG_SND_VERBOSE_PROCFS */
@@ -227,7 +208,7 @@ static inline void preallocate_info_init(struct snd_pcm_substream *substream)
 /*
  * pre-allocate the buffer and create a proc file for the substream
  */
-static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
+static void snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
                                          size_t size, size_t max)
 {
 
@@ -238,7 +219,6 @@ static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
                substream->buffer_bytes_max = substream->dma_buffer.bytes;
        substream->dma_max = max;
        preallocate_info_init(substream);
-       return 0;
 }
 
 
@@ -251,16 +231,14 @@ static int snd_pcm_lib_preallocate_pages1(struct snd_pcm_substream *substream,
  * @max: the max. allowed pre-allocation size
  *
  * Do pre-allocation for the given DMA buffer type.
- *
- * Return: Zero if successful, or a negative error code on failure.
  */
-int snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
+void snd_pcm_lib_preallocate_pages(struct snd_pcm_substream *substream,
                                  int type, struct device *data,
                                  size_t size, size_t max)
 {
        substream->dma_buffer.dev.type = type;
        substream->dma_buffer.dev.dev = data;
-       return snd_pcm_lib_preallocate_pages1(substream, size, max);
+       snd_pcm_lib_preallocate_pages1(substream, size, max);
 }
 EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
 
@@ -274,21 +252,17 @@ EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages);
  *
  * Do pre-allocation to all substreams of the given pcm for the
  * specified DMA type.
- *
- * Return: Zero if successful, or a negative error code on failure.
  */
-int snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
+void snd_pcm_lib_preallocate_pages_for_all(struct snd_pcm *pcm,
                                          int type, void *data,
                                          size_t size, size_t max)
 {
        struct snd_pcm_substream *substream;
-       int stream, err;
+       int stream;
 
        for (stream = 0; stream < 2; stream++)
                for (substream = pcm->streams[stream].substream; substream; substream = substream->next)
-                       if ((err = snd_pcm_lib_preallocate_pages(substream, type, data, size, max)) < 0)
-                               return err;
-       return 0;
+                       snd_pcm_lib_preallocate_pages(substream, type, data, size, max);
 }
 EXPORT_SYMBOL(snd_pcm_lib_preallocate_pages_for_all);
 
index 818dff1de545fad25669adf1144cbd49e5934067..f731f904e8ccb4e9671523e3b68e7825c779d8d8 100644 (file)
@@ -85,71 +85,30 @@ static int snd_pcm_open(struct file *file, struct snd_pcm *pcm, int stream);
  *
  */
 
-static DEFINE_RWLOCK(snd_pcm_link_rwlock);
 static DECLARE_RWSEM(snd_pcm_link_rwsem);
 
-/* Writer in rwsem may block readers even during its waiting in queue,
- * and this may lead to a deadlock when the code path takes read sem
- * twice (e.g. one in snd_pcm_action_nonatomic() and another in
- * snd_pcm_stream_lock()).  As a (suboptimal) workaround, let writer to
- * sleep until all the readers are completed without blocking by writer.
- */
-static inline void down_write_nonfifo(struct rw_semaphore *lock)
+void snd_pcm_group_init(struct snd_pcm_group *group)
 {
-       while (!down_write_trylock(lock))
-               msleep(1);
+       spin_lock_init(&group->lock);
+       mutex_init(&group->mutex);
+       INIT_LIST_HEAD(&group->substreams);
+       refcount_set(&group->refs, 0);
 }
 
-#define PCM_LOCK_DEFAULT       0
-#define PCM_LOCK_IRQ   1
-#define PCM_LOCK_IRQSAVE       2
-
-static unsigned long __snd_pcm_stream_lock_mode(struct snd_pcm_substream *substream,
-                                               unsigned int mode)
-{
-       unsigned long flags = 0;
-       if (substream->pcm->nonatomic) {
-               down_read_nested(&snd_pcm_link_rwsem, SINGLE_DEPTH_NESTING);
-               mutex_lock(&substream->self_group.mutex);
-       } else {
-               switch (mode) {
-               case PCM_LOCK_DEFAULT:
-                       read_lock(&snd_pcm_link_rwlock);
-                       break;
-               case PCM_LOCK_IRQ:
-                       read_lock_irq(&snd_pcm_link_rwlock);
-                       break;
-               case PCM_LOCK_IRQSAVE:
-                       read_lock_irqsave(&snd_pcm_link_rwlock, flags);
-                       break;
-               }
-               spin_lock(&substream->self_group.lock);
-       }
-       return flags;
+/* define group lock helpers */
+#define DEFINE_PCM_GROUP_LOCK(action, mutex_action) \
+static void snd_pcm_group_ ## action(struct snd_pcm_group *group, bool nonatomic) \
+{ \
+       if (nonatomic) \
+               mutex_ ## mutex_action(&group->mutex); \
+       else \
+               spin_ ## action(&group->lock); \
 }
 
-static void __snd_pcm_stream_unlock_mode(struct snd_pcm_substream *substream,
-                                        unsigned int mode, unsigned long flags)
-{
-       if (substream->pcm->nonatomic) {
-               mutex_unlock(&substream->self_group.mutex);
-               up_read(&snd_pcm_link_rwsem);
-       } else {
-               spin_unlock(&substream->self_group.lock);
-
-               switch (mode) {
-               case PCM_LOCK_DEFAULT:
-                       read_unlock(&snd_pcm_link_rwlock);
-                       break;
-               case PCM_LOCK_IRQ:
-                       read_unlock_irq(&snd_pcm_link_rwlock);
-                       break;
-               case PCM_LOCK_IRQSAVE:
-                       read_unlock_irqrestore(&snd_pcm_link_rwlock, flags);
-                       break;
-               }
-       }
-}
+DEFINE_PCM_GROUP_LOCK(lock, lock);
+DEFINE_PCM_GROUP_LOCK(unlock, unlock);
+DEFINE_PCM_GROUP_LOCK(lock_irq, lock);
+DEFINE_PCM_GROUP_LOCK(unlock_irq, unlock);
 
 /**
  * snd_pcm_stream_lock - Lock the PCM stream
@@ -161,7 +120,7 @@ static void __snd_pcm_stream_unlock_mode(struct snd_pcm_substream *substream,
  */
 void snd_pcm_stream_lock(struct snd_pcm_substream *substream)
 {
-       __snd_pcm_stream_lock_mode(substream, PCM_LOCK_DEFAULT);
+       snd_pcm_group_lock(&substream->self_group, substream->pcm->nonatomic);
 }
 EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
 
@@ -173,7 +132,7 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_lock);
  */
 void snd_pcm_stream_unlock(struct snd_pcm_substream *substream)
 {
-       __snd_pcm_stream_unlock_mode(substream, PCM_LOCK_DEFAULT, 0);
+       snd_pcm_group_unlock(&substream->self_group, substream->pcm->nonatomic);
 }
 EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
 
@@ -187,7 +146,8 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock);
  */
 void snd_pcm_stream_lock_irq(struct snd_pcm_substream *substream)
 {
-       __snd_pcm_stream_lock_mode(substream, PCM_LOCK_IRQ);
+       snd_pcm_group_lock_irq(&substream->self_group,
+                              substream->pcm->nonatomic);
 }
 EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
 
@@ -199,13 +159,19 @@ EXPORT_SYMBOL_GPL(snd_pcm_stream_lock_irq);
  */
 void snd_pcm_stream_unlock_irq(struct snd_pcm_substream *substream)
 {
-       __snd_pcm_stream_unlock_mode(substream, PCM_LOCK_IRQ, 0);
+       snd_pcm_group_unlock_irq(&substream->self_group,
+                                substream->pcm->nonatomic);
 }
 EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irq);
 
 unsigned long _snd_pcm_stream_lock_irqsave(struct snd_pcm_substream *substream)
 {
-       return __snd_pcm_stream_lock_mode(substream, PCM_LOCK_IRQSAVE);
+       unsigned long flags = 0;
+       if (substream->pcm->nonatomic)
+               mutex_lock(&substream->self_group.mutex);
+       else
+               spin_lock_irqsave(&substream->self_group.lock, flags);
+       return flags;
 }
 EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
 
@@ -219,7 +185,10 @@ EXPORT_SYMBOL_GPL(_snd_pcm_stream_lock_irqsave);
 void snd_pcm_stream_unlock_irqrestore(struct snd_pcm_substream *substream,
                                      unsigned long flags)
 {
-       __snd_pcm_stream_unlock_mode(substream, PCM_LOCK_IRQSAVE, flags);
+       if (substream->pcm->nonatomic)
+               mutex_unlock(&substream->self_group.mutex);
+       else
+               spin_unlock_irqrestore(&substream->self_group.lock, flags);
 }
 EXPORT_SYMBOL_GPL(snd_pcm_stream_unlock_irqrestore);
 
@@ -1124,6 +1093,68 @@ static int snd_pcm_action_single(const struct action_ops *ops,
        return res;
 }
 
+static void snd_pcm_group_assign(struct snd_pcm_substream *substream,
+                                struct snd_pcm_group *new_group)
+{
+       substream->group = new_group;
+       list_move(&substream->link_list, &new_group->substreams);
+}
+
+/*
+ * Unref and unlock the group, but keep the stream lock;
+ * when the group becomes empty and no longer referred, destroy itself
+ */
+static void snd_pcm_group_unref(struct snd_pcm_group *group,
+                               struct snd_pcm_substream *substream)
+{
+       bool do_free;
+
+       if (!group)
+               return;
+       do_free = refcount_dec_and_test(&group->refs) &&
+               list_empty(&group->substreams);
+       snd_pcm_group_unlock(group, substream->pcm->nonatomic);
+       if (do_free)
+               kfree(group);
+}
+
+/*
+ * Lock the group inside a stream lock and reference it;
+ * return the locked group object, or NULL if not linked
+ */
+static struct snd_pcm_group *
+snd_pcm_stream_group_ref(struct snd_pcm_substream *substream)
+{
+       bool nonatomic = substream->pcm->nonatomic;
+       struct snd_pcm_group *group;
+       bool trylock;
+
+       for (;;) {
+               if (!snd_pcm_stream_linked(substream))
+                       return NULL;
+               group = substream->group;
+               /* block freeing the group object */
+               refcount_inc(&group->refs);
+
+               trylock = nonatomic ? mutex_trylock(&group->mutex) :
+                       spin_trylock(&group->lock);
+               if (trylock)
+                       break; /* OK */
+
+               /* re-lock for avoiding ABBA deadlock */
+               snd_pcm_stream_unlock(substream);
+               snd_pcm_group_lock(group, nonatomic);
+               snd_pcm_stream_lock(substream);
+
+               /* check the group again; the above opens a small race window */
+               if (substream->group == group)
+                       break; /* OK */
+               /* group changed, try again */
+               snd_pcm_group_unref(group, substream);
+       }
+       return group;
+}
+
 /*
  *  Note: call with stream lock
  */
@@ -1131,28 +1162,15 @@ static int snd_pcm_action(const struct action_ops *ops,
                          struct snd_pcm_substream *substream,
                          int state)
 {
+       struct snd_pcm_group *group;
        int res;
 
-       if (!snd_pcm_stream_linked(substream))
-               return snd_pcm_action_single(ops, substream, state);
-
-       if (substream->pcm->nonatomic) {
-               if (!mutex_trylock(&substream->group->mutex)) {
-                       mutex_unlock(&substream->self_group.mutex);
-                       mutex_lock(&substream->group->mutex);
-                       mutex_lock(&substream->self_group.mutex);
-               }
+       group = snd_pcm_stream_group_ref(substream);
+       if (group)
                res = snd_pcm_action_group(ops, substream, state, 1);
-               mutex_unlock(&substream->group->mutex);
-       } else {
-               if (!spin_trylock(&substream->group->lock)) {
-                       spin_unlock(&substream->self_group.lock);
-                       spin_lock(&substream->group->lock);
-                       spin_lock(&substream->self_group.lock);
-               }
-               res = snd_pcm_action_group(ops, substream, state, 1);
-               spin_unlock(&substream->group->lock);
-       }
+       else
+               res = snd_pcm_action_single(ops, substream, state);
+       snd_pcm_group_unref(group, substream);
        return res;
 }
 
@@ -1179,6 +1197,7 @@ static int snd_pcm_action_nonatomic(const struct action_ops *ops,
 {
        int res;
 
+       /* Guarantee the group members won't change during non-atomic action */
        down_read(&snd_pcm_link_rwsem);
        if (snd_pcm_stream_linked(substream))
                res = snd_pcm_action_group(ops, substream, state, 0);
@@ -1460,29 +1479,24 @@ static const struct action_ops snd_pcm_action_suspend = {
        .post_action = snd_pcm_post_suspend
 };
 
-/**
+/*
  * snd_pcm_suspend - trigger SUSPEND to all linked streams
  * @substream: the PCM substream
  *
  * After this call, all streams are changed to SUSPENDED state.
  *
- * Return: Zero if successful (or @substream is %NULL), or a negative error
- * code.
+ * Return: Zero if successful, or a negative error code.
  */
-int snd_pcm_suspend(struct snd_pcm_substream *substream)
+static int snd_pcm_suspend(struct snd_pcm_substream *substream)
 {
        int err;
        unsigned long flags;
 
-       if (! substream)
-               return 0;
-
        snd_pcm_stream_lock_irqsave(substream, flags);
        err = snd_pcm_action(&snd_pcm_action_suspend, substream, 0);
        snd_pcm_stream_unlock_irqrestore(substream, flags);
        return err;
 }
-EXPORT_SYMBOL(snd_pcm_suspend);
 
 /**
  * snd_pcm_suspend_all - trigger SUSPEND to all substreams in the given pcm
@@ -1506,6 +1520,14 @@ int snd_pcm_suspend_all(struct snd_pcm *pcm)
                        /* FIXME: the open/close code should lock this as well */
                        if (substream->runtime == NULL)
                                continue;
+
+                       /*
+                        * Skip BE dai link PCM's that are internal and may
+                        * not have their substream ops set.
+                        */
+                       if (!substream->ops)
+                               continue;
+
                        err = snd_pcm_suspend(substream);
                        if (err < 0 && err != -EBUSY)
                                return err;
@@ -1792,8 +1814,6 @@ static const struct action_ops snd_pcm_action_drain_init = {
        .post_action = snd_pcm_post_drain_init
 };
 
-static int snd_pcm_drop(struct snd_pcm_substream *substream);
-
 /*
  * Drain the stream(s).
  * When the substream is linked, sync until the draining of all playback streams
@@ -1807,6 +1827,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
        struct snd_card *card;
        struct snd_pcm_runtime *runtime;
        struct snd_pcm_substream *s;
+       struct snd_pcm_group *group;
        wait_queue_entry_t wait;
        int result = 0;
        int nonblock = 0;
@@ -1823,7 +1844,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
        } else if (substream->f_flags & O_NONBLOCK)
                nonblock = 1;
 
-       down_read(&snd_pcm_link_rwsem);
        snd_pcm_stream_lock_irq(substream);
        /* resume pause */
        if (runtime->status->state == SNDRV_PCM_STATE_PAUSED)
@@ -1848,6 +1868,7 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
                }
                /* find a substream to drain */
                to_check = NULL;
+               group = snd_pcm_stream_group_ref(substream);
                snd_pcm_group_for_each_entry(s, substream) {
                        if (s->stream != SNDRV_PCM_STREAM_PLAYBACK)
                                continue;
@@ -1857,12 +1878,12 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
                                break;
                        }
                }
+               snd_pcm_group_unref(group, substream);
                if (!to_check)
                        break; /* all drained */
                init_waitqueue_entry(&wait, current);
                add_wait_queue(&to_check->sleep, &wait);
                snd_pcm_stream_unlock_irq(substream);
-               up_read(&snd_pcm_link_rwsem);
                if (runtime->no_period_wakeup)
                        tout = MAX_SCHEDULE_TIMEOUT;
                else {
@@ -1874,9 +1895,17 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
                        tout = msecs_to_jiffies(tout * 1000);
                }
                tout = schedule_timeout_interruptible(tout);
-               down_read(&snd_pcm_link_rwsem);
+
                snd_pcm_stream_lock_irq(substream);
-               remove_wait_queue(&to_check->sleep, &wait);
+               group = snd_pcm_stream_group_ref(substream);
+               snd_pcm_group_for_each_entry(s, substream) {
+                       if (s->runtime == to_check) {
+                               remove_wait_queue(&to_check->sleep, &wait);
+                               break;
+                       }
+               }
+               snd_pcm_group_unref(group, substream);
+
                if (card->shutdown) {
                        result = -ENODEV;
                        break;
@@ -1896,7 +1925,6 @@ static int snd_pcm_drain(struct snd_pcm_substream *substream,
 
  unlock:
        snd_pcm_stream_unlock_irq(substream);
-       up_read(&snd_pcm_link_rwsem);
 
        return result;
 }
@@ -1935,13 +1963,19 @@ static int snd_pcm_drop(struct snd_pcm_substream *substream)
 static bool is_pcm_file(struct file *file)
 {
        struct inode *inode = file_inode(file);
+       struct snd_pcm *pcm;
        unsigned int minor;
 
        if (!S_ISCHR(inode->i_mode) || imajor(inode) != snd_major)
                return false;
        minor = iminor(inode);
-       return snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK) ||
-               snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE);
+       pcm = snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_PLAYBACK);
+       if (!pcm)
+               pcm = snd_lookup_minor_data(minor, SNDRV_DEVICE_TYPE_PCM_CAPTURE);
+       if (!pcm)
+               return false;
+       snd_card_unref(pcm->card);
+       return true;
 }
 
 /*
@@ -1952,7 +1986,8 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
        int res = 0;
        struct snd_pcm_file *pcm_file;
        struct snd_pcm_substream *substream1;
-       struct snd_pcm_group *group;
+       struct snd_pcm_group *group, *target_group;
+       bool nonatomic = substream->pcm->nonatomic;
        struct fd f = fdget(fd);
 
        if (!f.file)
@@ -1963,13 +1998,14 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
        }
        pcm_file = f.file->private_data;
        substream1 = pcm_file->substream;
-       group = kmalloc(sizeof(*group), GFP_KERNEL);
+       group = kzalloc(sizeof(*group), GFP_KERNEL);
        if (!group) {
                res = -ENOMEM;
                goto _nolock;
        }
-       down_write_nonfifo(&snd_pcm_link_rwsem);
-       write_lock_irq(&snd_pcm_link_rwlock);
+       snd_pcm_group_init(group);
+
+       down_write(&snd_pcm_link_rwsem);
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN ||
            substream->runtime->status->state != substream1->runtime->status->state ||
            substream->pcm->nonatomic != substream1->pcm->nonatomic) {
@@ -1980,23 +2016,23 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
                res = -EALREADY;
                goto _end;
        }
+
+       snd_pcm_stream_lock_irq(substream);
        if (!snd_pcm_stream_linked(substream)) {
-               substream->group = group;
-               group = NULL;
-               spin_lock_init(&substream->group->lock);
-               mutex_init(&substream->group->mutex);
-               INIT_LIST_HEAD(&substream->group->substreams);
-               list_add_tail(&substream->link_list, &substream->group->substreams);
-               substream->group->count = 1;
-       }
-       list_add_tail(&substream1->link_list, &substream->group->substreams);
-       substream->group->count++;
-       substream1->group = substream->group;
+               snd_pcm_group_assign(substream, group);
+               group = NULL; /* assigned, don't free this one below */
+       }
+       target_group = substream->group;
+       snd_pcm_stream_unlock_irq(substream);
+
+       snd_pcm_group_lock_irq(target_group, nonatomic);
+       snd_pcm_stream_lock(substream1);
+       snd_pcm_group_assign(substream1, target_group);
+       snd_pcm_stream_unlock(substream1);
+       snd_pcm_group_unlock_irq(target_group, nonatomic);
  _end:
-       write_unlock_irq(&snd_pcm_link_rwlock);
        up_write(&snd_pcm_link_rwsem);
  _nolock:
-       snd_card_unref(substream1->pcm->card);
        kfree(group);
  _badf:
        fdput(f);
@@ -2005,34 +2041,43 @@ static int snd_pcm_link(struct snd_pcm_substream *substream, int fd)
 
 static void relink_to_local(struct snd_pcm_substream *substream)
 {
-       substream->group = &substream->self_group;
-       INIT_LIST_HEAD(&substream->self_group.substreams);
-       list_add_tail(&substream->link_list, &substream->self_group.substreams);
+       snd_pcm_stream_lock(substream);
+       snd_pcm_group_assign(substream, &substream->self_group);
+       snd_pcm_stream_unlock(substream);
 }
 
 static int snd_pcm_unlink(struct snd_pcm_substream *substream)
 {
-       struct snd_pcm_substream *s;
+       struct snd_pcm_group *group;
+       bool nonatomic = substream->pcm->nonatomic;
+       bool do_free = false;
        int res = 0;
 
-       down_write_nonfifo(&snd_pcm_link_rwsem);
-       write_lock_irq(&snd_pcm_link_rwlock);
+       down_write(&snd_pcm_link_rwsem);
+
        if (!snd_pcm_stream_linked(substream)) {
                res = -EALREADY;
                goto _end;
        }
-       list_del(&substream->link_list);
-       substream->group->count--;
-       if (substream->group->count == 1) {     /* detach the last stream, too */
-               snd_pcm_group_for_each_entry(s, substream) {
-                       relink_to_local(s);
-                       break;
-               }
-               kfree(substream->group);
-       }
+
+       group = substream->group;
+       snd_pcm_group_lock_irq(group, nonatomic);
+
        relink_to_local(substream);
+
+       /* detach the last stream, too */
+       if (list_is_singular(&group->substreams)) {
+               relink_to_local(list_first_entry(&group->substreams,
+                                                struct snd_pcm_substream,
+                                                link_list));
+               do_free = !refcount_read(&group->refs);
+       }
+
+       snd_pcm_group_unlock_irq(group, nonatomic);
+       if (do_free)
+               kfree(group);
+
        _end:
-       write_unlock_irq(&snd_pcm_link_rwlock);
        up_write(&snd_pcm_link_rwsem);
        return res;
 }
@@ -2457,10 +2502,8 @@ static int snd_pcm_open_file(struct file *file,
                return -ENOMEM;
        }
        pcm_file->substream = substream;
-       if (substream->ref_count == 1) {
-               substream->file = pcm_file;
+       if (substream->ref_count == 1)
                substream->pcm_release = pcm_release_private;
-       }
        file->private_data = pcm_file;
 
        return 0;
index 1e34e6381baad1f8aec5789418e3aef6207176bd..8c3fbe1276be141c79af15acba98287ac8ebf46b 100644 (file)
@@ -1133,16 +1133,10 @@ static void print_cable_info(struct snd_info_entry *entry,
 static int loopback_proc_new(struct loopback *loopback, int cidx)
 {
        char name[32];
-       struct snd_info_entry *entry;
-       int err;
 
        snprintf(name, sizeof(name), "cable#%d", cidx);
-       err = snd_card_proc_new(loopback->card, name, &entry);
-       if (err < 0)
-               return err;
-
-       snd_info_set_text_ops(entry, loopback, print_cable_info);
-       return 0;
+       return snd_card_ro_proc_new(loopback->card, name, loopback,
+                                   print_cable_info);
 }
 
 static int loopback_probe(struct platform_device *devptr)
@@ -1200,12 +1194,8 @@ static int loopback_remove(struct platform_device *devptr)
 static int loopback_suspend(struct device *pdev)
 {
        struct snd_card *card = dev_get_drvdata(pdev);
-       struct loopback *loopback = card->private_data;
 
        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-
-       snd_pcm_suspend_all(loopback->pcm[0]);
-       snd_pcm_suspend_all(loopback->pcm[1]);
        return 0;
 }
        
index 9af154db530a803943c9dc420d06e5a180d0789f..2672c2e13334dd99e959bf74fce5dd0a9692c3ae 100644 (file)
@@ -1037,14 +1037,8 @@ static void dummy_proc_write(struct snd_info_entry *entry,
 
 static void dummy_proc_init(struct snd_dummy *chip)
 {
-       struct snd_info_entry *entry;
-
-       if (!snd_card_proc_new(chip->card, "dummy_pcm", &entry)) {
-               snd_info_set_text_ops(entry, chip, dummy_proc_read);
-               entry->c.text.write = dummy_proc_write;
-               entry->mode |= 0200;
-               entry->private_data = chip;
-       }
+       snd_card_rw_proc_new(chip->card, "dummy_pcm", chip,
+                            dummy_proc_read, dummy_proc_write);
 }
 #else
 #define dummy_proc_init(x)
@@ -1138,10 +1132,8 @@ static int snd_dummy_remove(struct platform_device *devptr)
 static int snd_dummy_suspend(struct device *pdev)
 {
        struct snd_card *card = dev_get_drvdata(pdev);
-       struct snd_dummy *dummy = card->private_data;
 
        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
-       snd_pcm_suspend_all(dummy->pcm);
        return 0;
 }
        
index 16b24091d79932bc6c21093a6259790dc3ef5e0e..f1b839a0e7b7d6bb1c75880a68b80afe403e45fc 100644 (file)
@@ -114,10 +114,6 @@ int snd_opl4_create_proc(struct snd_opl4 *opl4)
                entry->c.ops = &snd_opl4_mem_proc_ops;
                entry->module = THIS_MODULE;
                entry->private_data = opl4;
-               if (snd_info_register(entry) < 0) {
-                       snd_info_free_entry(entry);
-                       entry = NULL;
-               }
        }
        opl4->proc_entry = entry;
        return 0;
index 0dd3f46eb03e804da8f8af663d3e270a96dc6aa1..d83ad3820f02a29b81d56afe559bb35bf89ef8d2 100644 (file)
@@ -197,7 +197,6 @@ static int pcsp_suspend(struct device *dev)
 {
        struct snd_pcsp *chip = dev_get_drvdata(dev);
        pcsp_stop_beep(chip);
-       snd_pcm_suspend_all(chip->pcm);
        return 0;
 }
 
index 04368dd59a4c8dd80503bae67ca3acfa12cc5a0d..543945643a76604faddbb81cb18ef2c469271271 100644 (file)
@@ -643,10 +643,7 @@ static void vx_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *b
 
 static void vx_proc_init(struct vx_core *chip)
 {
-       struct snd_info_entry *entry;
-
-       if (! snd_card_proc_new(chip->card, "vx-status", &entry))
-               snd_info_set_text_ops(entry, chip, vx_proc_read);
+       snd_card_ro_proc_new(chip->card, "vx-status", chip, vx_proc_read);
 }
 
 
@@ -732,12 +729,8 @@ EXPORT_SYMBOL(snd_vx_dsp_load);
  */
 int snd_vx_suspend(struct vx_core *chip)
 {
-       unsigned int i;
-
        snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot);
        chip->chip_status |= VX_STAT_IN_SUSPEND;
-       for (i = 0; i < chip->hw->num_codecs; i++)
-               snd_pcm_suspend_all(chip->pcm[i]);
 
        return 0;
 }
index 052e005902590c41247bff33f415b430e0c8744d..b9e96d0b3a0a232f20661eaf04bf533351741d56 100644 (file)
@@ -163,5 +163,6 @@ config SND_FIREFACE
         Say Y here to include support for RME fireface series.
          * Fireface 400
          * Fireface 800
+         * Fireface UCX
 
 endif # SND_FIREWIRE
index d91874275d2c371f078f1601c2c2627e875627f3..5b46e8dcc2dd334151b67de70a707df2f587b269 100644 (file)
@@ -448,7 +448,19 @@ static const struct ieee1394_device_id bebob_id_table[] = {
        /* Focusrite, SaffirePro 26 I/O */
        SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000003, &saffirepro_26_spec),
        /* Focusrite, SaffirePro 10 I/O */
-       SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, 0x00000006, &saffirepro_10_spec),
+       {
+               // The combination of vendor_id and model_id is the same as the
+               // same as the one of Liquid Saffire 56.
+               .match_flags    = IEEE1394_MATCH_VENDOR_ID |
+                                 IEEE1394_MATCH_MODEL_ID |
+                                 IEEE1394_MATCH_SPECIFIER_ID |
+                                 IEEE1394_MATCH_VERSION,
+               .vendor_id      = VEN_FOCUSRITE,
+               .model_id       = 0x000006,
+               .specifier_id   = 0x00a02d,
+               .version        = 0x010001,
+               .driver_data    = (kernel_ulong_t)&saffirepro_10_spec,
+       },
        /* Focusrite, Saffire(no label and LE) */
        SND_BEBOB_DEV_ENTRY(VEN_FOCUSRITE, MODEL_FOCUSRITE_SAFFIRE_BOTH,
                            &saffire_spec),
index 8096891af9137a509e415cea4242a639a21c329b..05e2a1c6326cb597af0d3061f47db5c019a1246c 100644 (file)
@@ -163,12 +163,8 @@ add_node(struct snd_bebob *bebob, struct snd_info_entry *root, const char *name,
        struct snd_info_entry *entry;
 
        entry = snd_info_create_card_entry(bebob->card, name, root);
-       if (entry == NULL)
-               return;
-
-       snd_info_set_text_ops(entry, bebob, op);
-       if (snd_info_register(entry) < 0)
-               snd_info_free_entry(entry);
+       if (entry)
+               snd_info_set_text_ops(entry, bebob, op);
 }
 
 void snd_bebob_proc_init(struct snd_bebob *bebob)
@@ -184,10 +180,6 @@ void snd_bebob_proc_init(struct snd_bebob *bebob)
        if (root == NULL)
                return;
        root->mode = S_IFDIR | 0555;
-       if (snd_info_register(root) < 0) {
-               snd_info_free_entry(root);
-               return;
-       }
 
        add_node(bebob, root, "clock", proc_read_clock);
        add_node(bebob, root, "firmware", proc_read_hw_info);
index bb870fc73f99f08ac34d4a921c88c8f26a63b4b7..9b1d509c6320c528f76520432409ad7f300b4be3 100644 (file)
@@ -285,12 +285,8 @@ static void add_node(struct snd_dice *dice, struct snd_info_entry *root,
        struct snd_info_entry *entry;
 
        entry = snd_info_create_card_entry(dice->card, name, root);
-       if (!entry)
-               return;
-
-       snd_info_set_text_ops(entry, dice, op);
-       if (snd_info_register(entry) < 0)
-               snd_info_free_entry(entry);
+       if (entry)
+               snd_info_set_text_ops(entry, dice, op);
 }
 
 void snd_dice_create_proc(struct snd_dice *dice)
@@ -306,10 +302,6 @@ void snd_dice_create_proc(struct snd_dice *dice)
        if (!root)
                return;
        root->mode = S_IFDIR | 0555;
-       if (snd_info_register(root) < 0) {
-               snd_info_free_entry(root);
-               return;
-       }
 
        add_node(dice, root, "dice", dice_proc_read);
        add_node(dice, root, "formation", dice_proc_read_formation);
index ed50b222d36ea0d4dd7ddd4440ca8f62ce0e7d8a..eee184b05d937f093a641137bce9ad2b1f6eec69 100644 (file)
@@ -18,6 +18,7 @@ MODULE_LICENSE("GPL v2");
 #define OUI_ALESIS             0x000595
 #define OUI_MAUDIO             0x000d6c
 #define OUI_MYTEK              0x001ee8
+#define OUI_SSL                        0x0050c2        // Actually ID reserved by IEEE.
 
 #define DICE_CATEGORY_ID       0x04
 #define WEISS_CATEGORY_ID      0x00
@@ -196,7 +197,7 @@ static int dice_probe(struct fw_unit *unit,
        struct snd_dice *dice;
        int err;
 
-       if (!entry->driver_data) {
+       if (!entry->driver_data && entry->vendor_id != OUI_SSL) {
                err = check_dice_category(unit);
                if (err < 0)
                        return -ENODEV;
@@ -361,6 +362,15 @@ static const struct ieee1394_device_id dice_id_table[] = {
                .model_id       = 0x000002,
                .driver_data = (kernel_ulong_t)snd_dice_detect_mytek_formats,
        },
+       // Solid State Logic, Duende Classic and Mini.
+       // NOTE: each field of GUID in config ROM is not compliant to standard
+       // DICE scheme.
+       {
+               .match_flags    = IEEE1394_MATCH_VENDOR_ID |
+                                 IEEE1394_MATCH_MODEL_ID,
+               .vendor_id      = OUI_SSL,
+               .model_id       = 0x000070,
+       },
        {
                .match_flags = IEEE1394_MATCH_VERSION,
                .version     = DICE_INTERFACE,
index 6996d5a6ff5f7ce61cb97a803a18313cd46bc3f9..d22e8675b10fa02012b5a33689d1866615fb880e 100644 (file)
@@ -80,20 +80,8 @@ void snd_dg00x_proc_init(struct snd_dg00x *dg00x)
                return;
 
        root->mode = S_IFDIR | 0555;
-       if (snd_info_register(root) < 0) {
-               snd_info_free_entry(root);
-               return;
-       }
 
        entry = snd_info_create_card_entry(dg00x->card, "clock", root);
-       if (entry == NULL) {
-               snd_info_free_entry(root);
-               return;
-       }
-
-       snd_info_set_text_ops(entry, dg00x, proc_read_clock);
-       if (snd_info_register(entry) < 0) {
-               snd_info_free_entry(entry);
-               snd_info_free_entry(root);
-       }
+       if (entry)
+               snd_info_set_text_ops(entry, dg00x, proc_read_clock);
 }
index 79a7d6d99d7287b0735a0aaf1d690429ae7c2cad..d64f4e2a10966eb7310ba40bd436ea5497e2bf44 100644 (file)
@@ -1,4 +1,4 @@
 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 \
-                    ff-protocol-ff800.o
+                    ff-stream.o ff-pcm.o ff-hwdep.o ff-protocol-former.o \
+                    ff-protocol-latter.o
 obj-$(CONFIG_SND_FIREFACE) += snd-fireface.o
index 6a49611ee46250a0534c4cd1455dc9a15972fc18..5b44e1c4569a334357ef734ce3d22bdfdf16fc6b 100644 (file)
@@ -19,7 +19,7 @@ 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->on_sysex[substream->number] = 0;
        ff->rx_midi_error[substream->number] = false;
 
        WRITE_ONCE(ff->rx_midi_substreams[substream->number], substream);
index d0bc96b20a65d45e10b12010d220745b80c3bc7b..5adf04b95c04cf27091edbe200ac85781f314dca 100644 (file)
@@ -152,7 +152,7 @@ static int pcm_open(struct snd_pcm_substream *substream)
        if (err < 0)
                goto release_lock;
 
-       err = snd_ff_transaction_get_clock(ff, &rate, &src);
+       err = ff->spec->protocol->get_clock(ff, &rate, &src);
        if (err < 0)
                goto release_lock;
 
index a0c550dabe9ace95a4186f71cbad32eaf5e84c57..b886b541c94baaa5e16e8515990510e0662f9bae 100644 (file)
 
 #include "./ff.h"
 
-static void proc_dump_clock_config(struct snd_info_entry *entry,
-                                  struct snd_info_buffer *buffer)
+const char *snd_ff_proc_get_clk_label(enum snd_ff_clock_src src)
 {
-       struct snd_ff *ff = entry->private_data;
-       __le32 reg;
-       u32 data;
-       unsigned int rate;
-       const char *src;
-       int err;
-
-       err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
-                                SND_FF_REG_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 = "ADAT1";
-                       break;
-               case 0x01:
-                       src = "ADAT2";
-                       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);
+       static const char *const labels[] = {
+               "Internal",
+               "S/PDIF",
+               "ADAT1",
+               "ADAT2",
+               "Word",
+               "LTC",
+       };
+
+       if (src >= ARRAY_SIZE(labels))
+               return NULL;
+
+       return labels[src];
 }
 
-static void proc_dump_sync_status(struct snd_info_entry *entry,
-                                 struct snd_info_buffer *buffer)
+static void proc_dump_status(struct snd_info_entry *entry,
+                            struct snd_info_buffer *buffer)
 {
        struct snd_ff *ff = entry->private_data;
-       __le32 reg;
-       u32 data;
-       int err;
 
-       err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
-                                SND_FF_REG_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, "ADAT1:");
-       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, "ADAT2:");
-       if ((data >> 8) & 0x08) {
-               if ((data >> 8) & 0x20)
-                       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, "ADAT1:");
-                       break;
-               case 0x01:
-                       snd_iprintf(buffer, "ADAT2:");
-                       break;
-               case 0x03:
-                       snd_iprintf(buffer, "S/PDIF:");
-                       break;
-               case 0x04:
-                       snd_iprintf(buffer, "Word:");
-                       break;
-               case 0x07:
-                       snd_iprintf(buffer, "Nothing:");
-                       break;
-               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);
+       ff->spec->protocol->dump_status(ff, buffer);
 }
 
 static void add_node(struct snd_ff *ff, struct snd_info_entry *root,
@@ -221,12 +41,8 @@ static void add_node(struct snd_ff *ff, struct snd_info_entry *root,
        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);
+       if (entry)
+               snd_info_set_text_ops(entry, ff, op);
 }
 
 void snd_ff_proc_init(struct snd_ff *ff)
@@ -242,11 +58,6 @@ void snd_ff_proc_init(struct snd_ff *ff)
        if (root == NULL)
                return;
        root->mode = S_IFDIR | 0555;
-       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);
+       add_node(ff, root, "status", proc_dump_status);
 }
diff --git a/sound/firewire/fireface/ff-protocol-ff400.c b/sound/firewire/fireface/ff-protocol-ff400.c
deleted file mode 100644 (file)
index 2280fab..0000000
+++ /dev/null
@@ -1,161 +0,0 @@
-/*
- * 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
-
-/*
- * 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)
-{
-       enum snd_ff_stream_mode mode;
-       int i;
-       int 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;
-
-       err = snd_ff_stream_get_multiplier_mode(i, &mode);
-       if (err < 0)
-               return err;
-
-       /* Keep resources for in-stream. */
-       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 int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
-{
-       __le32 reg;
-       int err;
-
-       err = keep_resources(ff, rate);
-       if (err < 0)
-               return err;
-
-       /* 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 void ff400_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length)
-{
-       int i;
-
-       for (i = 0; i < length / 4; i++) {
-               u32 quad = le32_to_cpu(buf[i]);
-               u8 byte;
-               unsigned int index;
-               struct snd_rawmidi_substream *substream;
-
-               /* 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 = READ_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 = READ_ONCE(ff->tx_midi_substreams[1]);
-                       if (substream != NULL) {
-                               byte = (quad >> 16) & 0xff;
-                               snd_rawmidi_receive(substream, &byte, 1);
-                       }
-               }
-       }
-}
-
-const struct snd_ff_protocol snd_ff_protocol_ff400 = {
-       .handle_midi_msg        = ff400_handle_midi_msg,
-       .begin_session          = ff400_begin_session,
-       .finish_session         = ff400_finish_session,
-};
diff --git a/sound/firewire/fireface/ff-protocol-ff800.c b/sound/firewire/fireface/ff-protocol-ff800.c
deleted file mode 100644 (file)
index 2acbf60..0000000
+++ /dev/null
@@ -1,143 +0,0 @@
-/*
- * ff-protocol-ff800.c - a part of driver for RME Fireface series
- *
- * Copyright (c) 2018 Takashi Sakamoto
- *
- * Licensed under the terms of the GNU General Public License, version 2.
- */
-
-#include <linux/delay.h>
-
-#include "ff.h"
-
-#define FF800_STF              0x0000fc88f000
-#define FF800_RX_PACKET_FORMAT 0x0000fc88f004
-#define FF800_ALLOC_TX_STREAM  0x0000fc88f008
-#define FF800_ISOC_COMM_START  0x0000fc88f00c
-#define   FF800_TX_S800_FLAG   0x00000800
-#define FF800_ISOC_COMM_STOP   0x0000fc88f010
-
-#define FF800_TX_PACKET_ISOC_CH        0x0000801c0008
-
-static int allocate_rx_resources(struct snd_ff *ff)
-{
-       u32 data;
-       __le32 reg;
-       int err;
-
-       // Controllers should allocate isochronous resources for rx stream.
-       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)
-               return err;
-
-       // Set isochronous channel and the number of quadlets of rx packets.
-       data = ff->rx_stream.data_block_quadlets << 3;
-       data = (data << 8) | ff->rx_resources.channel;
-       reg = cpu_to_le32(data);
-       return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
-                               FF800_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
-}
-
-static int allocate_tx_resources(struct snd_ff *ff)
-{
-       __le32 reg;
-       unsigned int count;
-       unsigned int tx_isoc_channel;
-       int err;
-
-       reg = cpu_to_le32(ff->tx_stream.data_block_quadlets);
-       err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
-                                FF800_ALLOC_TX_STREAM, &reg, sizeof(reg), 0);
-       if (err < 0)
-               return err;
-
-       // Wait till the format of tx packet is available.
-       count = 0;
-       while (count++ < 10) {
-               u32 data;
-               err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
-                               FF800_TX_PACKET_ISOC_CH, &reg, sizeof(reg), 0);
-               if (err < 0)
-                       return err;
-
-               data = le32_to_cpu(reg);
-               if (data != 0xffffffff) {
-                       tx_isoc_channel = data;
-                       break;
-               }
-
-               msleep(50);
-       }
-       if (count >= 10)
-               return -ETIMEDOUT;
-
-       // NOTE: this is a makeshift to start OHCI 1394 IR context in the
-       // channel. On the other hand, 'struct fw_iso_resources.allocated' is
-       // not true and it's not deallocated at stop.
-       ff->tx_resources.channel = tx_isoc_channel;
-
-       return 0;
-}
-
-static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
-{
-       __le32 reg;
-       int err;
-
-       reg = cpu_to_le32(rate);
-       err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
-                                FF800_STF, &reg, sizeof(reg), 0);
-       if (err < 0)
-               return err;
-
-       // If starting isochronous communication immediately, change of STF has
-       // no effect. In this case, the communication runs based on former STF.
-       // Let's sleep for a bit.
-       msleep(100);
-
-       err = allocate_rx_resources(ff);
-       if (err < 0)
-               return err;
-
-       err = allocate_tx_resources(ff);
-       if (err < 0)
-               return err;
-
-       reg = cpu_to_le32(0x80000000);
-       reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets);
-       if (fw_parent_device(ff->unit)->max_speed == SCODE_800)
-               reg |= cpu_to_le32(FF800_TX_S800_FLAG);
-       return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
-                                FF800_ISOC_COMM_START, &reg, sizeof(reg), 0);
-}
-
-static void ff800_finish_session(struct snd_ff *ff)
-{
-       __le32 reg;
-
-       reg = cpu_to_le32(0x80000000);
-       snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
-                          FF800_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
-}
-
-static void ff800_handle_midi_msg(struct snd_ff *ff, __le32 *buf, size_t length)
-{
-       int i;
-
-       for (i = 0; i < length / 4; i++) {
-               u8 byte = le32_to_cpu(buf[i]) & 0xff;
-               struct snd_rawmidi_substream *substream;
-
-               substream = READ_ONCE(ff->tx_midi_substreams[0]);
-               if (substream)
-                       snd_rawmidi_receive(substream, &byte, 1);
-       }
-}
-
-const struct snd_ff_protocol snd_ff_protocol_ff800 = {
-       .handle_midi_msg        = ff800_handle_midi_msg,
-       .begin_session          = ff800_begin_session,
-       .finish_session         = ff800_finish_session,
-};
diff --git a/sound/firewire/fireface/ff-protocol-former.c b/sound/firewire/fireface/ff-protocol-former.c
new file mode 100644 (file)
index 0000000..8d1c2c6
--- /dev/null
@@ -0,0 +1,597 @@
+// SPDX-License-Identifier: GPL-2.0
+// ff-protocol-former.c - a part of driver for RME Fireface series
+//
+// Copyright (c) 2019 Takashi Sakamoto
+//
+// Licensed under the terms of the GNU General Public License, version 2.
+
+#include <linux/delay.h>
+
+#include "ff.h"
+
+#define FORMER_REG_SYNC_STATUS         0x0000801c0000ull
+/* For block write request. */
+#define FORMER_REG_FETCH_PCM_FRAMES    0x0000801c0000ull
+#define FORMER_REG_CLOCK_CONFIG                0x0000801c0004ull
+
+static int parse_clock_bits(u32 data, unsigned int *rate,
+                           enum snd_ff_clock_src *src)
+{
+       static const struct {
+               unsigned int rate;
+               u32 mask;
+       } *rate_entry, rate_entries[] = {
+               {  32000, 0x00000002, },
+               {  44100, 0x00000000, },
+               {  48000, 0x00000006, },
+               {  64000, 0x0000000a, },
+               {  88200, 0x00000008, },
+               {  96000, 0x0000000e, },
+               { 128000, 0x00000012, },
+               { 176400, 0x00000010, },
+               { 192000, 0x00000016, },
+       };
+       static const struct {
+               enum snd_ff_clock_src src;
+               u32 mask;
+       } *clk_entry, clk_entries[] = {
+               { SND_FF_CLOCK_SRC_ADAT1,       0x00000000, },
+               { SND_FF_CLOCK_SRC_ADAT2,       0x00000400, },
+               { SND_FF_CLOCK_SRC_SPDIF,       0x00000c00, },
+               { SND_FF_CLOCK_SRC_WORD,        0x00001000, },
+               { SND_FF_CLOCK_SRC_LTC,         0x00001800, },
+       };
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
+               rate_entry = rate_entries + i;
+               if ((data & 0x0000001e) == rate_entry->mask) {
+                       *rate = rate_entry->rate;
+                       break;
+               }
+       }
+       if (i == ARRAY_SIZE(rate_entries))
+               return -EIO;
+
+       if (data & 0x00000001) {
+               *src = SND_FF_CLOCK_SRC_INTERNAL;
+       } else {
+               for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
+                       clk_entry = clk_entries + i;
+                       if ((data & 0x00001c00) == clk_entry->mask) {
+                               *src = clk_entry->src;
+                               break;
+                       }
+               }
+               if (i == ARRAY_SIZE(clk_entries))
+                       return -EIO;
+       }
+
+       return 0;
+}
+
+static int former_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,
+                                FORMER_REG_CLOCK_CONFIG, &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+       data = le32_to_cpu(reg);
+
+       return parse_clock_bits(data, rate, src);
+}
+
+static int former_switch_fetching_mode(struct snd_ff *ff, bool enable)
+{
+       unsigned int count;
+       __le32 *reg;
+       int i;
+       int err;
+
+       count = 0;
+       for (i = 0; i < SND_FF_STREAM_MODE_COUNT; ++i)
+               count = max(count, ff->spec->pcm_playback_channels[i]);
+
+       reg = kcalloc(count, sizeof(__le32), GFP_KERNEL);
+       if (!reg)
+               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 < count; ++i)
+                       reg[i] = cpu_to_le32(0x00000001);
+       }
+
+       err = snd_fw_transaction(ff->unit, TCODE_WRITE_BLOCK_REQUEST,
+                                FORMER_REG_FETCH_PCM_FRAMES, reg,
+                                sizeof(__le32) * count, 0);
+       kfree(reg);
+       return err;
+}
+
+static void dump_clock_config(struct snd_ff *ff, struct snd_info_buffer *buffer)
+{
+       __le32 reg;
+       u32 data;
+       unsigned int rate;
+       enum snd_ff_clock_src src;
+       const char *label;
+       int err;
+
+       err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
+                                FORMER_REG_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 & 0x00000020) ? "Professional" : "Consumer",
+                   (data & 0x00000040) ? "on" : "off");
+
+       snd_iprintf(buffer, "Optical output interface format: %s\n",
+                   (data & 0x00000100) ? "S/PDIF" : "ADAT");
+
+       snd_iprintf(buffer, "Word output single speed: %s\n",
+                   (data & 0x00002000) ? "on" : "off");
+
+       snd_iprintf(buffer, "S/PDIF input interface: %s\n",
+                   (data & 0x00000200) ? "Optical" : "Coaxial");
+
+       err = parse_clock_bits(data, &rate, &src);
+       if (err < 0)
+               return;
+       label = snd_ff_proc_get_clk_label(src);
+       if (!label)
+               return;
+
+       snd_iprintf(buffer, "Clock configuration: %d %s\n", rate, label);
+}
+
+static void dump_sync_status(struct snd_ff *ff, struct snd_info_buffer *buffer)
+{
+       static const struct {
+               char *const label;
+               u32 locked_mask;
+               u32 synced_mask;
+       } *clk_entry, clk_entries[] = {
+               { "WDClk",      0x40000000, 0x20000000, },
+               { "S/PDIF",     0x00080000, 0x00040000, },
+               { "ADAT1",      0x00000400, 0x00001000, },
+               { "ADAT2",      0x00000800, 0x00002000, },
+       };
+       static const struct {
+               char *const label;
+               u32 mask;
+       } *referred_entry, referred_entries[] = {
+               { "ADAT1",      0x00000000, },
+               { "ADAT2",      0x00400000, },
+               { "S/PDIF",     0x00c00000, },
+               { "WDclk",      0x01000000, },
+               { "TCO",        0x01400000, },
+       };
+       static const struct {
+               unsigned int rate;
+               u32 mask;
+       } *rate_entry, rate_entries[] = {
+               { 32000,        0x02000000, },
+               { 44100,        0x04000000, },
+               { 48000,        0x06000000, },
+               { 64000,        0x08000000, },
+               { 88200,        0x0a000000, },
+               { 96000,        0x0c000000, },
+               { 128000,       0x0e000000, },
+               { 176400,       0x10000000, },
+               { 192000,       0x12000000, },
+       };
+       __le32 reg[2];
+       u32 data[2];
+       int i;
+       int err;
+
+       err = snd_fw_transaction(ff->unit, TCODE_READ_BLOCK_REQUEST,
+                                FORMER_REG_SYNC_STATUS, reg, sizeof(reg), 0);
+       if (err < 0)
+               return;
+       data[0] = le32_to_cpu(reg[0]);
+       data[1] = le32_to_cpu(reg[1]);
+
+       snd_iprintf(buffer, "External source detection:\n");
+
+       for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
+               const char *state;
+
+               clk_entry = clk_entries + i;
+               if (data[0] & clk_entry->locked_mask) {
+                       if (data[0] & clk_entry->synced_mask)
+                               state = "sync";
+                       else
+                               state = "lock";
+               } else {
+                       state = "none";
+               }
+
+               snd_iprintf(buffer, "%s: %s\n", clk_entry->label, state);
+       }
+
+       snd_iprintf(buffer, "Referred clock:\n");
+
+       if (data[1] & 0x00000001) {
+               snd_iprintf(buffer, "Internal\n");
+       } else {
+               unsigned int rate;
+               const char *label;
+
+               for (i = 0; i < ARRAY_SIZE(referred_entries); ++i) {
+                       referred_entry = referred_entries + i;
+                       if ((data[0] & 0x1e0000) == referred_entry->mask) {
+                               label = referred_entry->label;
+                               break;
+                       }
+               }
+               if (i == ARRAY_SIZE(referred_entries))
+                       label = "none";
+
+               for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
+                       rate_entry = rate_entries + i;
+                       if ((data[0] & 0x1e000000) == rate_entry->mask) {
+                               rate = rate_entry->rate;
+                               break;
+                       }
+               }
+               if (i == ARRAY_SIZE(rate_entries))
+                       rate = 0;
+
+               snd_iprintf(buffer, "%s %d\n", label, rate);
+       }
+}
+
+static void former_dump_status(struct snd_ff *ff,
+                              struct snd_info_buffer *buffer)
+{
+       dump_clock_config(ff, buffer);
+       dump_sync_status(ff, buffer);
+}
+
+static int former_fill_midi_msg(struct snd_ff *ff,
+                               struct snd_rawmidi_substream *substream,
+                               unsigned int port)
+{
+       u8 *buf = (u8 *)ff->msg_buf[port];
+       int len;
+       int i;
+
+       len = snd_rawmidi_transmit_peek(substream, buf,
+                                       SND_FF_MAXIMIM_MIDI_QUADS);
+       if (len <= 0)
+               return len;
+
+       // One quadlet includes one byte.
+       for (i = len - 1; i >= 0; --i)
+               ff->msg_buf[port][i] = cpu_to_le32(buf[i]);
+       ff->rx_bytes[port] = len;
+
+       return len;
+}
+
+#define FF800_STF              0x0000fc88f000
+#define FF800_RX_PACKET_FORMAT 0x0000fc88f004
+#define FF800_ALLOC_TX_STREAM  0x0000fc88f008
+#define FF800_ISOC_COMM_START  0x0000fc88f00c
+#define   FF800_TX_S800_FLAG   0x00000800
+#define FF800_ISOC_COMM_STOP   0x0000fc88f010
+
+#define FF800_TX_PACKET_ISOC_CH        0x0000801c0008
+
+static int allocate_rx_resources(struct snd_ff *ff)
+{
+       u32 data;
+       __le32 reg;
+       int err;
+
+       // Controllers should allocate isochronous resources for rx stream.
+       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)
+               return err;
+
+       // Set isochronous channel and the number of quadlets of rx packets.
+       data = ff->rx_stream.data_block_quadlets << 3;
+       data = (data << 8) | ff->rx_resources.channel;
+       reg = cpu_to_le32(data);
+       return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+                               FF800_RX_PACKET_FORMAT, &reg, sizeof(reg), 0);
+}
+
+static int allocate_tx_resources(struct snd_ff *ff)
+{
+       __le32 reg;
+       unsigned int count;
+       unsigned int tx_isoc_channel;
+       int err;
+
+       reg = cpu_to_le32(ff->tx_stream.data_block_quadlets);
+       err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                FF800_ALLOC_TX_STREAM, &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       // Wait till the format of tx packet is available.
+       count = 0;
+       while (count++ < 10) {
+               u32 data;
+               err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+                               FF800_TX_PACKET_ISOC_CH, &reg, sizeof(reg), 0);
+               if (err < 0)
+                       return err;
+
+               data = le32_to_cpu(reg);
+               if (data != 0xffffffff) {
+                       tx_isoc_channel = data;
+                       break;
+               }
+
+               msleep(50);
+       }
+       if (count >= 10)
+               return -ETIMEDOUT;
+
+       // NOTE: this is a makeshift to start OHCI 1394 IR context in the
+       // channel. On the other hand, 'struct fw_iso_resources.allocated' is
+       // not true and it's not deallocated at stop.
+       ff->tx_resources.channel = tx_isoc_channel;
+
+       return 0;
+}
+
+static int ff800_begin_session(struct snd_ff *ff, unsigned int rate)
+{
+       __le32 reg;
+       int err;
+
+       reg = cpu_to_le32(rate);
+       err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                FF800_STF, &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       // If starting isochronous communication immediately, change of STF has
+       // no effect. In this case, the communication runs based on former STF.
+       // Let's sleep for a bit.
+       msleep(100);
+
+       err = allocate_rx_resources(ff);
+       if (err < 0)
+               return err;
+
+       err = allocate_tx_resources(ff);
+       if (err < 0)
+               return err;
+
+       reg = cpu_to_le32(0x80000000);
+       reg |= cpu_to_le32(ff->tx_stream.data_block_quadlets);
+       if (fw_parent_device(ff->unit)->max_speed == SCODE_800)
+               reg |= cpu_to_le32(FF800_TX_S800_FLAG);
+       return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                FF800_ISOC_COMM_START, &reg, sizeof(reg), 0);
+}
+
+static void ff800_finish_session(struct snd_ff *ff)
+{
+       __le32 reg;
+
+       reg = cpu_to_le32(0x80000000);
+       snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          FF800_ISOC_COMM_STOP, &reg, sizeof(reg), 0);
+}
+
+// Fireface 800 doesn't allow drivers to register lower 4 bytes of destination
+// address.
+// A write transaction to clear registered higher 4 bytes of destination address
+// has an effect to suppress asynchronous transaction from device.
+static void ff800_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
+                                 __le32 *buf, size_t length)
+{
+       int i;
+
+       for (i = 0; i < length / 4; i++) {
+               u8 byte = le32_to_cpu(buf[i]) & 0xff;
+               struct snd_rawmidi_substream *substream;
+
+               substream = READ_ONCE(ff->tx_midi_substreams[0]);
+               if (substream)
+                       snd_rawmidi_receive(substream, &byte, 1);
+       }
+}
+
+const struct snd_ff_protocol snd_ff_protocol_ff800 = {
+       .handle_midi_msg        = ff800_handle_midi_msg,
+       .fill_midi_msg          = former_fill_midi_msg,
+       .get_clock              = former_get_clock,
+       .switch_fetching_mode   = former_switch_fetching_mode,
+       .begin_session          = ff800_begin_session,
+       .finish_session         = ff800_finish_session,
+       .dump_status            = former_dump_status,
+};
+
+#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
+
+/*
+ * 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)
+{
+       enum snd_ff_stream_mode mode;
+       int i;
+       int 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;
+
+       err = snd_ff_stream_get_multiplier_mode(i, &mode);
+       if (err < 0)
+               return err;
+
+       /* Keep resources for in-stream. */
+       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. */
+       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 int ff400_begin_session(struct snd_ff *ff, unsigned int rate)
+{
+       __le32 reg;
+       int err;
+
+       err = keep_resources(ff, rate);
+       if (err < 0)
+               return err;
+
+       /* 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);
+}
+
+// For Fireface 400, lower 4 bytes of destination address is configured by bit
+// flag in quadlet register (little endian) at 0x'0000'801'0051c. Drivers can
+// select one of 4 options:
+//
+// bit flags: offset of destination address
+//  - 0x04000000: 0x'....'....'0000'0000
+//  - 0x08000000: 0x'....'....'0000'0080
+//  - 0x10000000: 0x'....'....'0000'0100
+//  - 0x20000000: 0x'....'....'0000'0180
+//
+// Drivers can suppress the device to transfer asynchronous transactions by
+// using below 2 bits.
+//  - 0x01000000: suppress transmission
+//  - 0x02000000: suppress transmission
+//
+// Actually, the register is write-only and includes the other options such as
+// input attenuation. This driver allocates destination address with '0000'0000
+// in its lower offset and expects userspace application to configure the
+// register for it.
+static void ff400_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
+                                 __le32 *buf, size_t length)
+{
+       int i;
+
+       for (i = 0; i < length / 4; i++) {
+               u32 quad = le32_to_cpu(buf[i]);
+               u8 byte;
+               unsigned int index;
+               struct snd_rawmidi_substream *substream;
+
+               /* 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 = READ_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 = READ_ONCE(ff->tx_midi_substreams[1]);
+                       if (substream != NULL) {
+                               byte = (quad >> 16) & 0xff;
+                               snd_rawmidi_receive(substream, &byte, 1);
+                       }
+               }
+       }
+}
+
+const struct snd_ff_protocol snd_ff_protocol_ff400 = {
+       .handle_midi_msg        = ff400_handle_midi_msg,
+       .fill_midi_msg          = former_fill_midi_msg,
+       .get_clock              = former_get_clock,
+       .switch_fetching_mode   = former_switch_fetching_mode,
+       .begin_session          = ff400_begin_session,
+       .finish_session         = ff400_finish_session,
+       .dump_status            = former_dump_status,
+};
diff --git a/sound/firewire/fireface/ff-protocol-latter.c b/sound/firewire/fireface/ff-protocol-latter.c
new file mode 100644 (file)
index 0000000..c8236ff
--- /dev/null
@@ -0,0 +1,430 @@
+// SPDX-License-Identifier: GPL-2.0
+// ff-protocol-latter - a part of driver for RME Fireface series
+//
+// Copyright (c) 2019 Takashi Sakamoto
+//
+// Licensed under the terms of the GNU General Public License, version 2.
+
+#include <linux/delay.h>
+
+#include "ff.h"
+
+#define LATTER_STF             0xffff00000004
+#define LATTER_ISOC_CHANNELS   0xffff00000008
+#define LATTER_ISOC_START      0xffff0000000c
+#define LATTER_FETCH_MODE      0xffff00000010
+#define LATTER_SYNC_STATUS     0x0000801c0000
+
+static int parse_clock_bits(u32 data, unsigned int *rate,
+                           enum snd_ff_clock_src *src)
+{
+       static const struct {
+               unsigned int rate;
+               u32 flag;
+       } *rate_entry, rate_entries[] = {
+               { 32000,        0x00000000, },
+               { 44100,        0x01000000, },
+               { 48000,        0x02000000, },
+               { 64000,        0x04000000, },
+               { 88200,        0x05000000, },
+               { 96000,        0x06000000, },
+               { 128000,       0x08000000, },
+               { 176400,       0x09000000, },
+               { 192000,       0x0a000000, },
+       };
+       static const struct {
+               enum snd_ff_clock_src src;
+               u32 flag;
+       } *clk_entry, clk_entries[] = {
+               { SND_FF_CLOCK_SRC_SPDIF,       0x00000200, },
+               { SND_FF_CLOCK_SRC_ADAT1,       0x00000400, },
+               { SND_FF_CLOCK_SRC_WORD,        0x00000600, },
+               { SND_FF_CLOCK_SRC_INTERNAL,    0x00000e00, },
+       };
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(rate_entries); ++i) {
+               rate_entry = rate_entries + i;
+               if ((data & 0x0f000000) == rate_entry->flag) {
+                       *rate = rate_entry->rate;
+                       break;
+               }
+       }
+       if (i == ARRAY_SIZE(rate_entries))
+               return -EIO;
+
+       for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
+               clk_entry = clk_entries + i;
+               if ((data & 0x000e00) == clk_entry->flag) {
+                       *src = clk_entry->src;
+                       break;
+               }
+       }
+       if (i == ARRAY_SIZE(clk_entries))
+               return -EIO;
+
+       return 0;
+}
+
+static int latter_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,
+                                LATTER_SYNC_STATUS, &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+       data = le32_to_cpu(reg);
+
+       return parse_clock_bits(data, rate, src);
+}
+
+static int latter_switch_fetching_mode(struct snd_ff *ff, bool enable)
+{
+       u32 data;
+       __le32 reg;
+
+       if (enable)
+               data = 0x00000000;
+       else
+               data = 0xffffffff;
+       reg = cpu_to_le32(data);
+
+       return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                 LATTER_FETCH_MODE, &reg, sizeof(reg), 0);
+}
+
+static int keep_resources(struct snd_ff *ff, unsigned int rate)
+{
+       enum snd_ff_stream_mode mode;
+       int i;
+       int 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;
+
+       err = snd_ff_stream_get_multiplier_mode(i, &mode);
+       if (err < 0)
+               return err;
+
+       /* Keep resources for in-stream. */
+       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. */
+       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 int latter_begin_session(struct snd_ff *ff, unsigned int rate)
+{
+       static const struct {
+               unsigned int stf;
+               unsigned int code;
+               unsigned int flag;
+       } *entry, rate_table[] = {
+               { 32000,  0x00, 0x92, },
+               { 44100,  0x02, 0x92, },
+               { 48000,  0x04, 0x92, },
+               { 64000,  0x08, 0x8e, },
+               { 88200,  0x0a, 0x8e, },
+               { 96000,  0x0c, 0x8e, },
+               { 128000, 0x10, 0x8c, },
+               { 176400, 0x12, 0x8c, },
+               { 192000, 0x14, 0x8c, },
+       };
+       u32 data;
+       __le32 reg;
+       unsigned int count;
+       int i;
+       int err;
+
+       for (i = 0; i < ARRAY_SIZE(rate_table); ++i) {
+               entry = rate_table + i;
+               if (entry->stf == rate)
+                       break;
+       }
+       if (i == ARRAY_SIZE(rate_table))
+               return -EINVAL;
+
+       reg = cpu_to_le32(entry->code);
+       err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                LATTER_STF, &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       // Confirm to shift transmission clock.
+       count = 0;
+       while (count++ < 10) {
+               unsigned int curr_rate;
+               enum snd_ff_clock_src src;
+
+               err = latter_get_clock(ff, &curr_rate, &src);
+               if (err < 0)
+                       return err;
+
+               if (curr_rate == rate)
+                       break;
+       }
+       if (count == 10)
+               return -ETIMEDOUT;
+
+       err = keep_resources(ff, rate);
+       if (err < 0)
+               return err;
+
+       data = (ff->tx_resources.channel << 8) | ff->rx_resources.channel;
+       reg = cpu_to_le32(data);
+       err = snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                LATTER_ISOC_CHANNELS, &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       // Always use the maximum number of data channels in data block of
+       // packet.
+       reg = cpu_to_le32(entry->flag);
+       return snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                 LATTER_ISOC_START, &reg, sizeof(reg), 0);
+}
+
+static void latter_finish_session(struct snd_ff *ff)
+{
+       __le32 reg;
+
+       reg = cpu_to_le32(0x00000000);
+       snd_fw_transaction(ff->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          LATTER_ISOC_START, &reg, sizeof(reg), 0);
+}
+
+static void latter_dump_status(struct snd_ff *ff, struct snd_info_buffer *buffer)
+{
+       static const struct {
+               char *const label;
+               u32 locked_mask;
+               u32 synced_mask;
+       } *clk_entry, clk_entries[] = {
+               { "S/PDIF",     0x00000001, 0x00000010, },
+               { "ADAT",       0x00000002, 0x00000020, },
+               { "WDClk",      0x00000004, 0x00000040, },
+       };
+       __le32 reg;
+       u32 data;
+       unsigned int rate;
+       enum snd_ff_clock_src src;
+       const char *label;
+       int i;
+       int err;
+
+       err = snd_fw_transaction(ff->unit, TCODE_READ_QUADLET_REQUEST,
+                                LATTER_SYNC_STATUS, &reg, sizeof(reg), 0);
+       if (err < 0)
+               return;
+       data = le32_to_cpu(reg);
+
+       snd_iprintf(buffer, "External source detection:\n");
+
+       for (i = 0; i < ARRAY_SIZE(clk_entries); ++i) {
+               clk_entry = clk_entries + i;
+               snd_iprintf(buffer, "%s: ", clk_entry->label);
+               if (data & clk_entry->locked_mask) {
+                       if (data & clk_entry->synced_mask)
+                               snd_iprintf(buffer, "sync\n");
+                       else
+                               snd_iprintf(buffer, "lock\n");
+               } else {
+                       snd_iprintf(buffer, "none\n");
+               }
+       }
+
+       err = parse_clock_bits(data, &rate, &src);
+       if (err < 0)
+               return;
+       label = snd_ff_proc_get_clk_label(src);
+       if (!label)
+               return;
+
+       snd_iprintf(buffer, "Referred clock: %s %d\n", label, rate);
+}
+
+// NOTE: transactions are transferred within 0x00-0x7f in allocated range of
+// address. This seems to be for check of discontinuity in receiver side.
+//
+// Like Fireface 400, drivers can select one of 4 options for lower 4 bytes of
+// destination address by bit flags in quadlet register (little endian) at
+// 0x'ffff'0000'0014:
+//
+// bit flags: offset of destination address
+// - 0x00002000: 0x'....'....'0000'0000
+// - 0x00004000: 0x'....'....'0000'0080
+// - 0x00008000: 0x'....'....'0000'0100
+// - 0x00010000: 0x'....'....'0000'0180
+//
+// Drivers can suppress the device to transfer asynchronous transactions by
+// clear these bit flags.
+//
+// Actually, the register is write-only and includes the other settings such as
+// input attenuation. This driver allocates for the first option
+// (0x'....'....'0000'0000) and expects userspace application to configure the
+// register for it.
+static void latter_handle_midi_msg(struct snd_ff *ff, unsigned int offset,
+                                  __le32 *buf, size_t length)
+{
+       u32 data = le32_to_cpu(*buf);
+       unsigned int index = (data & 0x000000f0) >> 4;
+       u8 byte[3];
+       struct snd_rawmidi_substream *substream;
+       unsigned int len;
+
+       if (index >= ff->spec->midi_in_ports)
+               return;
+
+       switch (data & 0x0000000f) {
+       case 0x00000008:
+       case 0x00000009:
+       case 0x0000000a:
+       case 0x0000000b:
+       case 0x0000000e:
+               len = 3;
+               break;
+       case 0x0000000c:
+       case 0x0000000d:
+               len = 2;
+               break;
+       default:
+               len = data & 0x00000003;
+               if (len == 0)
+                       len = 3;
+               break;
+       }
+
+       byte[0] = (data & 0x0000ff00) >> 8;
+       byte[1] = (data & 0x00ff0000) >> 16;
+       byte[2] = (data & 0xff000000) >> 24;
+
+       substream = READ_ONCE(ff->tx_midi_substreams[index]);
+       if (substream)
+               snd_rawmidi_receive(substream, byte, len);
+}
+
+/*
+ * When return minus value, given argument is not MIDI status.
+ * When return 0, given argument is a beginning of system exclusive.
+ * When return the others, given argument is MIDI data.
+ */
+static inline int calculate_message_bytes(u8 status)
+{
+       switch (status) {
+       case 0xf6:      /* Tune request. */
+       case 0xf8:      /* Timing clock. */
+       case 0xfa:      /* Start. */
+       case 0xfb:      /* Continue. */
+       case 0xfc:      /* Stop. */
+       case 0xfe:      /* Active sensing. */
+       case 0xff:      /* System reset. */
+               return 1;
+       case 0xf1:      /* MIDI time code quarter frame. */
+       case 0xf3:      /* Song select. */
+               return 2;
+       case 0xf2:      /* Song position pointer. */
+               return 3;
+       case 0xf0:      /* Exclusive. */
+               return 0;
+       case 0xf7:      /* End of exclusive. */
+               break;
+       case 0xf4:      /* Undefined. */
+       case 0xf5:      /* Undefined. */
+       case 0xf9:      /* Undefined. */
+       case 0xfd:      /* Undefined. */
+               break;
+       default:
+               switch (status & 0xf0) {
+               case 0x80:      /* Note on. */
+               case 0x90:      /* Note off. */
+               case 0xa0:      /* Polyphonic key pressure. */
+               case 0xb0:      /* Control change and Mode change. */
+               case 0xe0:      /* Pitch bend change. */
+                       return 3;
+               case 0xc0:      /* Program change. */
+               case 0xd0:      /* Channel pressure. */
+                       return 2;
+               default:
+               break;
+               }
+       break;
+       }
+
+       return -EINVAL;
+}
+
+static int latter_fill_midi_msg(struct snd_ff *ff,
+                               struct snd_rawmidi_substream *substream,
+                               unsigned int port)
+{
+       u32 data = {0};
+       u8 *buf = (u8 *)&data;
+       int consumed;
+
+       buf[0] = port << 4;
+       consumed = snd_rawmidi_transmit_peek(substream, buf + 1, 3);
+       if (consumed <= 0)
+               return consumed;
+
+       if (!ff->on_sysex[port]) {
+               if (buf[1] != 0xf0) {
+                       if (consumed < calculate_message_bytes(buf[1]))
+                               return 0;
+               } else {
+                       // The beginning of exclusives.
+                       ff->on_sysex[port] = true;
+               }
+
+               buf[0] |= consumed;
+       } else {
+               if (buf[1] != 0xf7) {
+                       if (buf[2] == 0xf7 || buf[3] == 0xf7) {
+                               // Transfer end code at next time.
+                               consumed -= 1;
+                       }
+
+                       buf[0] |= consumed;
+               } else {
+                       // The end of exclusives.
+                       ff->on_sysex[port] = false;
+                       consumed = 1;
+                       buf[0] |= 0x0f;
+               }
+       }
+
+       ff->msg_buf[port][0] = cpu_to_le32(data);
+       ff->rx_bytes[port] = consumed;
+
+       return 1;
+}
+
+const struct snd_ff_protocol snd_ff_protocol_latter = {
+       .handle_midi_msg        = latter_handle_midi_msg,
+       .fill_midi_msg          = latter_fill_midi_msg,
+       .get_cloc