whiterose

linux unikernel
Log | Files | Refs | README | LICENSE | git clone https://git.ne02ptzero.me/git/whiterose

commit da2577fe63f865cd9dc785a42c29c0071f567a35
parent 542d0e583b7b366527175b2b5fc0aad262fa33b0
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Wed,  6 Mar 2019 14:10:46 -0800

Merge tag 'sound-5.1-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound

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
  ...

Diffstat:
MDocumentation/devicetree/bindings/sound/adi,adau1977.txt | 13++++++++++---
MDocumentation/devicetree/bindings/sound/ak4458.txt | 2+-
DDocumentation/devicetree/bindings/sound/audio-graph-scu-card.txt | 123-------------------------------------------------------------------------------
ADocumentation/devicetree/bindings/sound/cs35l36.txt | 168+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
ADocumentation/devicetree/bindings/sound/cs4341.txt | 22++++++++++++++++++++++
MDocumentation/devicetree/bindings/sound/davinci-mcasp-audio.txt | 17+++++++++++++++++
ADocumentation/devicetree/bindings/sound/fsl,micfil.txt | 32++++++++++++++++++++++++++++++++
ADocumentation/devicetree/bindings/sound/google,cros-ec-codec.txt | 26++++++++++++++++++++++++++
ADocumentation/devicetree/bindings/sound/ingenic,jz4725b-codec.txt | 20++++++++++++++++++++
ADocumentation/devicetree/bindings/sound/ingenic,jz4740-codec.txt | 20++++++++++++++++++++
ADocumentation/devicetree/bindings/sound/mt6358.txt | 18++++++++++++++++++
ADocumentation/devicetree/bindings/sound/mt8183-afe-pcm.txt | 36++++++++++++++++++++++++++++++++++++
ADocumentation/devicetree/bindings/sound/mtk-btcvsd-snd.txt | 24++++++++++++++++++++++++
MDocumentation/devicetree/bindings/sound/nvidia,tegra30-hda.txt | 5+++++
MDocumentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt | 7++++---
MDocumentation/devicetree/bindings/sound/qcom,wcd9335.txt | 10+++++-----
ADocumentation/devicetree/bindings/sound/rockchip,rk3328-codec.txt | 23+++++++++++++++++++++++
MDocumentation/devicetree/bindings/sound/sgtl5000.txt | 9+++++++++
DDocumentation/devicetree/bindings/sound/simple-scu-card.txt | 94-------------------------------------------------------------------------------
ADocumentation/devicetree/bindings/sound/sprd-pcm.txt | 23+++++++++++++++++++++++
ADocumentation/devicetree/bindings/sound/xlnx,audio-formatter.txt | 29+++++++++++++++++++++++++++++
ADocumentation/devicetree/bindings/sound/xlnx,spdif.txt | 28++++++++++++++++++++++++++++
MDocumentation/sound/hd-audio/models.rst | 4+++-
MDocumentation/sound/kernel-api/writing-an-alsa-driver.rst | 35+++++++++++++----------------------
MDocumentation/sound/soc/dpcm.rst | 10++++------
MMAINTAINERS | 8++++++++
March/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts | 1+
March/arm64/boot/dts/nvidia/tegra194-p2972-0000.dts | 1+
March/arm64/boot/dts/nvidia/tegra210-p2597.dtsi | 1+
Mdrivers/gpio/gpiolib-acpi.c | 15+++++++++++----
Mdrivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c | 1-
Mdrivers/media/pci/solo6x10/solo6x10-g723.c | 4+---
Mdrivers/media/pci/tw686x/tw686x-audio.c | 3++-
Minclude/linux/acpi.h | 7+++++++
Minclude/sound/core.h | 1-
Ainclude/sound/cs35l36.h | 43+++++++++++++++++++++++++++++++++++++++++++
Minclude/sound/dmaengine_pcm.h | 4----
Minclude/sound/hda_register.h | 2++
Minclude/sound/hda_verbs.h | 2++
Minclude/sound/hdaudio.h | 3+++
Minclude/sound/info.h | 36+++++++++++++++++++++++++++++++++++-
Minclude/sound/memalloc.h | 1-
Minclude/sound/pcm.h | 29+++++++----------------------
Minclude/sound/simple_card_utils.h | 4++--
Minclude/sound/soc-acpi.h | 33+++++++++++++++++++++------------
Minclude/sound/soc-dapm.h | 27+++++++++++++++++++--------
Minclude/sound/soc-topology.h | 8+++++---
Minclude/sound/soc.h | 73++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msound/ac97/bus.c | 2+-
Msound/aoa/core/gpio-feature.c | 1+
Msound/aoa/soundbus/i2sbus/core.c | 4----
Msound/arm/aaci.c | 4++--
Msound/arm/pxa2xx-ac97.c | 1-
Msound/atmel/ac97c.c | 4+---
Msound/core/compress_offload.c | 11+----------
Msound/core/info.c | 131++++++++++++++++++++++++++++++++++++++++++++++++++++---------------------------
Msound/core/init.c | 31++++---------------------------
Msound/core/memalloc.c | 2++
Msound/core/oss/pcm_oss.c | 1-
Msound/core/pcm.c | 163++++++++++++++++++++++++++++---------------------------------------------------
Msound/core/pcm_lib.c | 9++++++---
Msound/core/pcm_local.h | 1+
Msound/core/pcm_memory.c | 62++++++++++++++++++--------------------------------------------
Msound/core/pcm_native.c | 315+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Msound/drivers/aloop.c | 14++------------
Msound/drivers/dummy.c | 12++----------
Msound/drivers/opl4/opl4_proc.c | 4----
Msound/drivers/pcsp/pcsp.c | 1-
Msound/drivers/vx/vx_core.c | 9+--------
Msound/firewire/Kconfig | 1+
Msound/firewire/bebob/bebob.c | 14+++++++++++++-
Msound/firewire/bebob/bebob_proc.c | 12++----------
Msound/firewire/dice/dice-proc.c | 12++----------
Msound/firewire/dice/dice.c | 12+++++++++++-
Msound/firewire/digi00x/digi00x-proc.c | 16++--------------
Msound/firewire/fireface/Makefile | 4++--
Msound/firewire/fireface/ff-midi.c | 2+-
Msound/firewire/fireface/ff-pcm.c | 2+-
Msound/firewire/fireface/ff-proc.c | 229+++++++------------------------------------------------------------------------
Dsound/firewire/fireface/ff-protocol-ff400.c | 161-------------------------------------------------------------------------------
Dsound/firewire/fireface/ff-protocol-ff800.c | 143-------------------------------------------------------------------------------
Asound/firewire/fireface/ff-protocol-former.c | 597+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/firewire/fireface/ff-protocol-latter.c | 430+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/firewire/fireface/ff-stream.c | 40+++-------------------------------------
Msound/firewire/fireface/ff-transaction.c | 143++++++++++++++-----------------------------------------------------------------
Msound/firewire/fireface/ff.c | 28++++++++++++++++++++++++++++
Msound/firewire/fireface/ff.h | 23++++++++++++++---------
Msound/firewire/fireworks/fireworks_proc.c | 12++----------
Msound/firewire/motu/amdtp-motu.c | 4+++-
Msound/firewire/motu/motu-proc.c | 12++----------
Msound/firewire/oxfw/oxfw-proc.c | 12++----------
Msound/firewire/tascam/tascam-proc.c | 12++----------
Msound/hda/hdac_controller.c | 8+++++---
Msound/hda/hdac_i915.c | 4++--
Msound/hda/hdac_stream.c | 44+++++++++++++++++++++++++++++++++++++++++++-
Msound/i2c/other/ak4113.c | 5++---
Msound/i2c/other/ak4114.c | 5++---
Msound/i2c/other/ak4xxx-adda.c | 8+-------
Msound/isa/ad1816a/ad1816a_lib.c | 3+--
Msound/isa/als100.c | 1-
Msound/isa/cmi8328.c | 1-
Msound/isa/cmi8330.c | 3+--
Msound/isa/es1688/es1688.c | 2--
Msound/isa/es1688/es1688_lib.c | 6++----
Msound/isa/es18xx.c | 4+---
Msound/isa/gus/gus_irq.c | 5+----
Msound/isa/gus/gus_main.c | 13+++++++++++--
Msound/isa/gus/gus_mem.c | 6+-----
Msound/isa/gus/gus_pcm.c | 4++--
Msound/isa/opti9xx/miro.c | 5+----
Msound/isa/sb/jazz16.c | 1-
Msound/isa/sb/sb16.c | 1-
Msound/isa/sb/sb16_csp.c | 5++---
Msound/isa/sb/sb16_main.c | 12++++++++----
Msound/isa/sb/sb8.c | 1-
Msound/isa/sb/sb8_main.c | 2+-
Msound/isa/sscape.c | 7++++---
Msound/isa/wss/wss_lib.c | 3+--
Msound/mips/hal2.c | 31+++++++++++++++++--------------
Msound/mips/sgio2audio.c | 7++++---
Msound/parisc/harmony.c | 10++--------
Msound/pci/ac97/ac97_proc.c | 26+++++++++-----------------
Msound/pci/ad1889.c | 13+++----------
Msound/pci/ak4531_codec.c | 5+----
Msound/pci/ali5451/ali5451.c | 8++------
Msound/pci/als300.c | 1-
Msound/pci/als4000.c | 1-
Msound/pci/asihpi/asihpi.c | 6++----
Msound/pci/atiixp.c | 24+++++++++---------------
Msound/pci/atiixp_modem.c | 8++------
Msound/pci/aw2/aw2-alsa.c | 40++++++++++++----------------------------
Msound/pci/azt3328.c | 4----
Msound/pci/bt87x.c | 10+++++-----
Msound/pci/ca0106/ca0106_main.c | 19++++++-------------
Msound/pci/ca0106/ca0106_proc.c | 40+++++++++++++++-------------------------
Msound/pci/cmipci.c | 9+--------
Msound/pci/cs4281.c | 5+----
Msound/pci/cs46xx/cs46xx_dsp_spos.h | 6------
Msound/pci/cs46xx/cs46xx_lib.c | 6------
Msound/pci/cs46xx/dsp_spos.c | 133++++++++++++++++++++++---------------------------------------------------------
Msound/pci/cs46xx/dsp_spos_scb_lib.c | 19+++++--------------
Msound/pci/cs5535audio/cs5535audio_pm.c | 1-
Msound/pci/ctxfi/ctatc.c | 8--------
Msound/pci/echoaudio/echoaudio.c | 19+++++++------------
Msound/pci/emu10k1/emu10k1.c | 6------
Msound/pci/emu10k1/emu10k1x.c | 12+++---------
Msound/pci/emu10k1/emupcm.c | 22+++++++++++++++-------
Msound/pci/emu10k1/emuproc.c | 81++++++++++++++++++++++++++++++-------------------------------------------------
Msound/pci/emu10k1/p16v.c | 17+++++++----------
Msound/pci/ens1370.c | 9++-------
Msound/pci/es1938.c | 1-
Msound/pci/es1968.c | 1-
Msound/pci/fm801.c | 1-
Msound/pci/hda/hda_beep.c | 151++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msound/pci/hda/hda_beep.h | 5-----
Msound/pci/hda/hda_codec.c | 56+++++++++++++++++++++++++++++++++++++++++---------------
Msound/pci/hda/hda_proc.c | 9+--------
Msound/pci/hda/hda_tegra.c | 132++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msound/pci/hda/patch_hdmi.c | 10+++++++++-
Msound/pci/hda/patch_realtek.c | 66+++++++++++++++++++++++++++++++++++++++++-------------------------
Msound/pci/ice1712/ews.c | 7++++++-
Msound/pci/ice1712/ice1712.c | 8+-------
Msound/pci/ice1712/ice1724.c | 8+-------
Msound/pci/ice1712/pontis.c | 12+++---------
Msound/pci/ice1712/prodigy192.c | 5++---
Msound/pci/ice1712/prodigy_hifi.c | 8++------
Msound/pci/ice1712/quartet.c | 4+---
Msound/pci/intel8x0.c | 8++------
Msound/pci/intel8x0m.c | 9++-------
Msound/pci/korg1212/korg1212.c | 6++----
Msound/pci/lola/lola_proc.c | 16+++++-----------
Msound/pci/lx6464es/lx6464es.c | 16++++------------
Msound/pci/maestro3.c | 1-
Msound/pci/mixart/mixart.c | 6++----
Msound/pci/nm256/nm256.c | 1-
Msound/pci/oxygen/oxygen_lib.c | 12+++---------
Msound/pci/oxygen/pcm1796.h | 1-
Msound/pci/oxygen/xonar_pcm179x.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msound/pci/pcxhr/pcxhr.c | 21+++++++--------------
Msound/pci/riptide/riptide.c | 7++-----
Msound/pci/rme32.c | 5+----
Msound/pci/rme96.c | 7+------
Msound/pci/rme9652/hdsp.c | 5+----
Msound/pci/rme9652/hdspm.c | 75+++++++++++++++++++++++++++------------------------------------------------
Msound/pci/rme9652/rme9652.c | 6++----
Msound/pci/sis7019.c | 1-
Msound/pci/sonicvibes.c | 6++----
Msound/pci/trident/trident_main.c | 8+-------
Msound/pci/via82xx.c | 8++------
Msound/pci/via82xx_modem.c | 16+++++-----------
Msound/pci/ymfpci/ymfpci_main.c | 10+---------
Msound/pcmcia/pdaudiocf/pdaudiocf_core.c | 6+-----
Msound/ppc/pmac.c | 1-
Msound/ppc/snd_ps3.c | 6+-----
Msound/ppc/tumbler.c | 1+
Msound/sh/aica.c | 14++++++--------
Msound/soc/Kconfig | 1+
Msound/soc/Makefile | 1+
Msound/soc/amd/acp-pcm-dma.c | 26+++++++++++---------------
Msound/soc/amd/raven/acp3x-pcm-dma.c | 8++++----
Msound/soc/codecs/Kconfig | 67+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--
Msound/soc/codecs/Makefile | 14++++++++++++++
Msound/soc/codecs/ad193x.c | 76++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msound/soc/codecs/ad193x.h | 8++++++++
Msound/soc/codecs/adau1977.c | 12+++++++-----
Msound/soc/codecs/adau7002.c | 45+++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/codecs/ak4118.c | 1+
Msound/soc/codecs/ak4458.c | 79++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---
Asound/soc/codecs/cros_ec_codec.c | 441+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/codecs/cs35l36.c | 1957+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/codecs/cs35l36.h | 446+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/codecs/cs4271.c | 4++--
Asound/soc/codecs/cs4341.c | 346+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/codecs/cs47l24.c | 52++++++++++++++++++++++++++--------------------------
Msound/soc/codecs/da7219-aad.c | 2+-
Msound/soc/codecs/da7219.c | 197++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msound/soc/codecs/da7219.h | 2++
Msound/soc/codecs/dmic.c | 4++--
Msound/soc/codecs/es8316.c | 200+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----
Msound/soc/codecs/es8316.h | 7+++++++
Msound/soc/codecs/hdac_hdmi.c | 87+++++++++++++++++--------------------------------------------------------------
Asound/soc/codecs/jz4725b.c | 598+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/codecs/jz4740.c | 26++++++++++++++------------
Msound/soc/codecs/max98090.c | 15---------------
Msound/soc/codecs/max98373.c | 6+++---
Msound/soc/codecs/max9860.c | 3++-
Msound/soc/codecs/max98927.c | 8++++----
Msound/soc/codecs/msm8916-wcd-analog.c | 3+++
Msound/soc/codecs/msm8916-wcd-digital.c | 23++---------------------
Msound/soc/codecs/mt6351.c | 4+---
Asound/soc/codecs/mt6358.c | 2336+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/codecs/mt6358.h | 2314+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/codecs/nau8824.c | 2+-
Msound/soc/codecs/nau8825.c | 1+
Msound/soc/codecs/pcm186x.c | 8++++----
Msound/soc/codecs/pcm3060.c | 35+++++++++++++++++++++++++++++++++++
Msound/soc/codecs/pcm3060.h | 5+++++
Msound/soc/codecs/pcm512x.c | 37+++++++++++++++++++++++++++++--------
Asound/soc/codecs/rk3328_codec.c | 519+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/codecs/rk3328_codec.h | 210+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/codecs/rl6347a.c | 4++--
Msound/soc/codecs/rt274.c | 19+++++++++++++------
Msound/soc/codecs/rt286.c | 8++++----
Msound/soc/codecs/rt298.c | 12++++++------
Msound/soc/codecs/rt5514.c | 1+
Msound/soc/codecs/rt5640.c | 6+++---
Msound/soc/codecs/rt5645.c | 66++++++++++++++++--------------------------------------------------
Msound/soc/codecs/rt5651.c | 67+++++++++++++++++++++++++++++++++++++++++++++++--------------------
Msound/soc/codecs/rt5651.h | 1+
Msound/soc/codecs/rt5670.c | 54------------------------------------------------------
Msound/soc/codecs/rt5677.c | 8++++----
Msound/soc/codecs/rt5682.c | 8++++++++
Msound/soc/codecs/sgtl5000.c | 19++++++++++++++++++-
Msound/soc/codecs/ssm2602.c | 71+++++++++++++++++++++++++++++++++++++++++------------------------------
Msound/soc/codecs/tlv320aic31xx.c | 16+++-------------
Msound/soc/codecs/tlv320aic32x4.c | 31++++++++++++++++++++++++++++++-
Msound/soc/codecs/tlv320aic32x4.h | 1+
Msound/soc/codecs/tlv320aic3x.c | 25++++---------------------
Msound/soc/codecs/tscs42xx.c | 2+-
Asound/soc/codecs/wcd-clsh-v2.c | 576+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/codecs/wcd-clsh-v2.h | 49+++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/codecs/wcd9335.c | 5244+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/codecs/wcd9335.h | 640+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/codecs/wm5102.c | 64++++++++++++++++++++++++++++++++--------------------------------
Msound/soc/codecs/wm5110.c | 80++++++++++++++++++++++++++++++++++++++++----------------------------------------
Msound/soc/codecs/wm8741.c | 22+++++++++++++++++++++-
Msound/soc/codecs/wm8770.c | 18+++---------------
Msound/soc/codecs/wm8904.c | 21++++++++++++---------
Msound/soc/codecs/wm8962.c | 9+++------
Msound/soc/codecs/wm8995.c | 29+++++++----------------------
Msound/soc/codecs/wm8996.c | 9+++------
Msound/soc/codecs/wm8997.c | 60++++++++++++++++++++++++++++++------------------------------
Msound/soc/codecs/wm8998.c | 60++++++++++++++++++++++++++++++------------------------------
Msound/soc/codecs/wm_adsp.c | 498+++++++++++++++++++++++++++++++++++++++++++++++--------------------------------
Msound/soc/codecs/wm_adsp.h | 4++--
Msound/soc/dwc/dwc-pcm.c | 3++-
Msound/soc/fsl/Kconfig | 9+++++++++
Msound/soc/fsl/Makefile | 2++
Msound/soc/fsl/fsl-asoc-card.c | 1+
Msound/soc/fsl/fsl_esai.c | 7++++---
Asound/soc/fsl/fsl_micfil.c | 826+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/fsl/fsl_micfil.h | 283+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/fsl/fsl_sai.c | 8++++----
Msound/soc/fsl/fsl_spdif.c | 12+++++++-----
Msound/soc/fsl/fsl_spdif.h | 2+-
Msound/soc/fsl/fsl_ssi.c | 6++++--
Msound/soc/fsl/imx-sgtl5000.c | 12++++++++----
Msound/soc/fsl/imx-spdif.c | 4+---
Msound/soc/generic/Kconfig | 17-----------------
Msound/soc/generic/Makefile | 4----
Msound/soc/generic/audio-graph-card.c | 481+++++++++++++++++++++++++++++++++++++++++++++----------------------------------
Dsound/soc/generic/audio-graph-scu-card.c | 501-------------------------------------------------------------------------------
Msound/soc/generic/simple-card-utils.c | 25+++++++++++++++----------
Msound/soc/generic/simple-card.c | 479++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Dsound/soc/generic/simple-scu-card.c | 474-------------------------------------------------------------------------------
Msound/soc/intel/atom/sst-atom-controls.c | 2+-
Msound/soc/intel/atom/sst-mfld-platform-pcm.c | 11+++--------
Msound/soc/intel/atom/sst/sst_acpi.c | 45++++++++++++++++++++++++++-------------------
Msound/soc/intel/atom/sst/sst_drv_interface.c | 2+-
Msound/soc/intel/atom/sst/sst_loader.c | 2+-
Msound/soc/intel/baytrail/sst-baytrail-ipc.c | 3+--
Msound/soc/intel/baytrail/sst-baytrail-pcm.c | 17+++++------------
Msound/soc/intel/boards/Kconfig | 1+
Msound/soc/intel/boards/bdw-rt5677.c | 14++++++++++++++
Msound/soc/intel/boards/broadwell.c | 16++++++++++++++++
Msound/soc/intel/boards/bxt_da7219_max98357a.c | 114++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msound/soc/intel/boards/bxt_rt298.c | 13+++++++++++++
Msound/soc/intel/boards/bytcht_da7213.c | 8++++++++
Msound/soc/intel/boards/bytcht_es8316.c | 336+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msound/soc/intel/boards/bytcr_rt5640.c | 23++++++++++++++++++++++-
Msound/soc/intel/boards/bytcr_rt5651.c | 180++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msound/soc/intel/boards/cht_bsw_max98090_ti.c | 12++++++++++++
Msound/soc/intel/boards/cht_bsw_nau8824.c | 12++++++++++++
Msound/soc/intel/boards/cht_bsw_rt5645.c | 9+++++++++
Msound/soc/intel/boards/cht_bsw_rt5672.c | 30++++++++++++++++++------------
Msound/soc/intel/boards/glk_rt5682_max98357a.c | 23++++++++++++++++++-----
Msound/soc/intel/boards/haswell.c | 15+++++++++++++++
Msound/soc/intel/boards/kbl_da7219_max98927.c | 207+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++----------
Msound/soc/intel/common/soc-acpi-intel-bxt-match.c | 25++++++++++---------------
Msound/soc/intel/common/soc-acpi-intel-byt-match.c | 92+++++++++++++++++++++++++++++++++++++++++++++++++++----------------------------
Msound/soc/intel/common/soc-acpi-intel-cht-match.c | 70++++++++++++++++++++++++++++------------------------------------------
Msound/soc/intel/common/soc-acpi-intel-cnl-match.c | 5++---
Msound/soc/intel/common/soc-acpi-intel-glk-match.c | 10++++------
Msound/soc/intel/common/soc-acpi-intel-hda-match.c | 2+-
Msound/soc/intel/common/soc-acpi-intel-hsw-bdw-match.c | 20++++++++------------
Msound/soc/intel/common/soc-acpi-intel-icl-match.c | 5++---
Msound/soc/intel/common/soc-acpi-intel-kbl-match.c | 19+++++++++++++++++++
Msound/soc/intel/haswell/sst-haswell-ipc.c | 2+-
Msound/soc/intel/haswell/sst-haswell-pcm.c | 14++++----------
Msound/soc/intel/skylake/skl-messages.c | 2+-
Msound/soc/intel/skylake/skl-pcm.c | 12++++--------
Msound/soc/intel/skylake/skl-topology.c | 2+-
Msound/soc/mediatek/Kconfig | 19+++++++++++++++++++
Msound/soc/mediatek/Makefile | 1+
Msound/soc/mediatek/common/Makefile | 3+++
Msound/soc/mediatek/common/mtk-afe-platform-driver.c | 6+++---
Asound/soc/mediatek/common/mtk-btcvsd.c | 1364+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/mediatek/mt8183/Makefile | 13+++++++++++++
Asound/soc/mediatek/mt8183/mt8183-afe-clk.c | 611+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/mediatek/mt8183/mt8183-afe-clk.h | 38++++++++++++++++++++++++++++++++++++++
Asound/soc/mediatek/mt8183/mt8183-afe-common.h | 108+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/mediatek/mt8183/mt8183-afe-pcm.c | 1237+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/mediatek/mt8183/mt8183-dai-adda.c | 501+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/mediatek/mt8183/mt8183-dai-hostless.c | 118+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/mediatek/mt8183/mt8183-dai-i2s.c | 1040+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/mediatek/mt8183/mt8183-dai-pcm.c | 318+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/mediatek/mt8183/mt8183-dai-tdm.c | 639+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/mediatek/mt8183/mt8183-interconnection.h | 33+++++++++++++++++++++++++++++++++
Asound/soc/mediatek/mt8183/mt8183-reg.h | 1666+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/meson/axg-fifo.c | 7++++---
Msound/soc/pxa/Makefile | 1-
Msound/soc/qcom/Kconfig | 4+++-
Msound/soc/qcom/apq8016_sbc.c | 21++++++++++++++++-----
Msound/soc/qcom/apq8096.c | 71++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-
Msound/soc/qcom/common.c | 10++++++++--
Msound/soc/qcom/sdm845.c | 2++
Msound/soc/samsung/dma.h | 3++-
Msound/soc/samsung/dmaengine.c | 16+++++++---------
Msound/soc/samsung/i2s.c | 731++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msound/soc/samsung/odroid.c | 206+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Msound/soc/samsung/pcm.c | 2+-
Msound/soc/samsung/s3c2412-i2s.c | 2+-
Msound/soc/samsung/s3c24xx-i2s.c | 2+-
Msound/soc/samsung/spdif.c | 2+-
Msound/soc/sh/fsi.c | 5+++--
Msound/soc/sh/rcar/core.c | 45++++++++++++++++++++-------------------------
Msound/soc/sh/rcar/gen.c | 24++++++++++++++++++++++++
Msound/soc/sh/rcar/rsnd.h | 27+++++++++++++++++++++++++++
Msound/soc/sh/rcar/src.c | 125++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msound/soc/sh/rcar/ssiu.c | 24+++++++++++-------------
Msound/soc/sh/siu_pcm.c | 15++-------------
Msound/soc/soc-compress.c | 177+++++++++++++++++++++++++++++++++++--------------------------------------------
Msound/soc/soc-core.c | 262++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Msound/soc/soc-dapm.c | 271++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------------------
Msound/soc/soc-generic-dmaengine-pcm.c | 18+++++-------------
Msound/soc/soc-pcm.c | 5+++++
Msound/soc/soc-topology.c | 166+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------------
Asound/soc/sprd/Kconfig | 6++++++
Asound/soc/sprd/Makefile | 4++++
Asound/soc/sprd/sprd-pcm-dma.c | 562+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/sprd/sprd-pcm-dma.h | 15+++++++++++++++
Msound/soc/stm/stm32_adfsdm.c | 5+++--
Msound/soc/stm/stm32_i2s.c | 136++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msound/soc/stm/stm32_sai.c | 11++++++++---
Msound/soc/stm/stm32_sai_sub.c | 8+++-----
Msound/soc/sunxi/sun4i-codec.c | 149+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++--------
Msound/soc/sunxi/sun50i-codec-analog.c | 4++--
Msound/soc/ti/davinci-mcasp.c | 175++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msound/soc/txx9/txx9aclc.c | 4+++-
Msound/soc/uniphier/aio-dma.c | 3++-
Msound/soc/xilinx/Kconfig | 14++++++++++++++
Msound/soc/xilinx/Makefile | 4++++
Asound/soc/xilinx/xlnx_formatter_pcm.c | 709+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/xilinx/xlnx_spdif.c | 339+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/xtensa/xtfpga-i2s.c | 6+++---
Msound/sparc/dbri.c | 19+++++--------------
Msound/spi/at73c213.c | 2+-
Msound/synth/emux/emux_proc.c | 4----
Msound/usb/card.c | 1-
Msound/usb/card.h | 1+
Msound/usb/format.c | 2++
Msound/usb/line6/driver.c | 4+---
Msound/usb/line6/pod.c | 3++-
Msound/usb/mixer.c | 13++++---------
Msound/usb/mixer_quirks.c | 6++----
Msound/usb/pcm.c | 4++++
Msound/usb/proc.c | 14++++++--------
Msound/usb/quirks-table.h | 65+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/usb/quirks.c | 119++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msound/usb/usx2y/usbusx2yaudio.c | 21++++++++++-----------
Msound/usb/usx2y/usx2yhwdeppcm.c | 19++++++++-----------
Msound/x86/intel_hdmi_audio.c | 15++-------------
412 files changed, 34394 insertions(+), 6007 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/adi,adau1977.txt b/Documentation/devicetree/bindings/sound/adi,adau1977.txt @@ -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>; }; diff --git a/Documentation/devicetree/bindings/sound/ak4458.txt b/Documentation/devicetree/bindings/sound/ak4458.txt @@ -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 @@ -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 @@ -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 @@ -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>; + }; diff --git a/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt b/Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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 @@ -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>; + }; diff --git a/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.txt b/Documentation/devicetree/bindings/sound/nvidia,tegra30-hda.txt @@ -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"; }; diff --git a/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt b/Documentation/devicetree/bindings/sound/qcom,msm8916-wcd-analog.txt @@ -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>; }; }; diff --git a/Documentation/devicetree/bindings/sound/qcom,wcd9335.txt b/Documentation/devicetree/bindings/sound/qcom,wcd9335.txt @@ -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 @@ -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"; +}; diff --git a/Documentation/devicetree/bindings/sound/sgtl5000.txt b/Documentation/devicetree/bindings/sound/sgtl5000.txt @@ -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 @@ -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 @@ -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 @@ -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 @@ -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>; + }; diff --git a/Documentation/sound/hd-audio/models.rst b/Documentation/sound/hd-audio/models.rst @@ -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 ============== diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst @@ -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 diff --git a/Documentation/sound/soc/dpcm.rst b/Documentation/sound/soc/dpcm.rst @@ -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. - - diff --git a/MAINTAINERS b/MAINTAINERS @@ -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> diff --git a/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts b/arch/arm64/boot/dts/nvidia/tegra186-p2771-0000.dts @@ -54,6 +54,7 @@ }; hda@3510000 { + nvidia,model = "jetson-tx2-hda"; status = "okay"; }; diff --git a/arch/arm64/boot/dts/nvidia/tegra194-p2972-0000.dts b/arch/arm64/boot/dts/nvidia/tegra194-p2972-0000.dts @@ -21,6 +21,7 @@ }; hda@3510000 { + nvidia,model = "jetson-xavier-hda"; status = "okay"; }; diff --git a/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi b/arch/arm64/boot/dts/nvidia/tegra210-p2597.dtsi @@ -1323,6 +1323,7 @@ }; hda@70030000 { + nvidia,model = "jetson-tx1-hda"; status = "okay"; }; diff --git a/drivers/gpio/gpiolib-acpi.c b/drivers/gpio/gpiolib-acpi.c @@ -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 diff --git a/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c b/drivers/gpu/drm/bridge/synopsys/dw-hdmi-ahb-audio.c @@ -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; } diff --git a/drivers/media/pci/solo6x10/solo6x10-g723.c b/drivers/media/pci/solo6x10/solo6x10-g723.c @@ -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; diff --git a/drivers/media/pci/tw686x/tw686x-audio.c b/drivers/media/pci/tw686x/tw686x-audio.c @@ -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, diff --git a/include/linux/acpi.h b/include/linux/acpi.h @@ -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; }; diff --git a/include/sound/core.h b/include/sound/core.h @@ -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 @@ -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 */ diff --git a/include/sound/dmaengine_pcm.h b/include/sound/dmaengine_pcm.h @@ -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 diff --git a/include/sound/hda_register.h b/include/sound/hda_register.h @@ -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 */ diff --git a/include/sound/hda_verbs.h b/include/sound/hda_verbs.h @@ -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 diff --git a/include/sound/hdaudio.h b/include/sound/hdaudio.h @@ -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 */ diff --git a/include/sound/info.h b/include/sound/info.h @@ -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 */ diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h @@ -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)) diff --git a/include/sound/pcm.h b/include/sound/pcm.h @@ -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); diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h @@ -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); diff --git a/include/sound/soc-acpi.h b/include/sound/soc-acpi.h @@ -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 diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h @@ -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 { diff --git a/include/sound/soc-topology.h b/include/sound/soc-topology.h @@ -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 */ diff --git a/include/sound/soc.h b/include/sound/soc.h @@ -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. @@ -919,13 +947,25 @@ struct snd_soc_dai_link { 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 * do not need a platform. */ 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 diff --git a/sound/ac97/bus.c b/sound/ac97/bus.c @@ -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; diff --git a/sound/aoa/core/gpio-feature.c b/sound/aoa/core/gpio-feature.c @@ -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; diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c @@ -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; diff --git a/sound/arm/aaci.c b/sound/arm/aaci.c @@ -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; diff --git a/sound/arm/pxa2xx-ac97.c b/sound/arm/pxa2xx-ac97.c @@ -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); diff --git a/sound/atmel/ac97c.c b/sound/atmel/ac97c.c @@ -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; diff --git a/sound/core/compress_offload.c b/sound/core/compress_offload.c @@ -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; diff --git a/sound/core/info.c b/sound/core/info.c @@ -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); + /* */ diff --git a/sound/core/init.c b/sound/core/init.c @@ -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); diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c @@ -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; diff --git a/sound/core/oss/pcm_oss.c b/sound/core/oss/pcm_oss.c @@ -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); } diff --git a/sound/core/pcm.c b/sound/core/pcm.c @@ -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; diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c @@ -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, diff --git a/sound/core/pcm_local.h b/sound/core/pcm_local.h @@ -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 */ diff --git a/sound/core/pcm_memory.c b/sound/core/pcm_memory.c @@ -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); diff --git a/sound/core/pcm_native.c b/sound/core/pcm_native.c @@ -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; diff --git a/sound/drivers/aloop.c b/sound/drivers/aloop.c @@ -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; } diff --git a/sound/drivers/dummy.c b/sound/drivers/dummy.c @@ -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; } diff --git a/sound/drivers/opl4/opl4_proc.c b/sound/drivers/opl4/opl4_proc.c @@ -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; diff --git a/sound/drivers/pcsp/pcsp.c b/sound/drivers/pcsp/pcsp.c @@ -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; } diff --git a/sound/drivers/vx/vx_core.c b/sound/drivers/vx/vx_core.c @@ -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; } diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig @@ -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 diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c @@ -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), diff --git a/sound/firewire/bebob/bebob_proc.c b/sound/firewire/bebob/bebob_proc.c @@ -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); diff --git a/sound/firewire/dice/dice-proc.c b/sound/firewire/dice/dice-proc.c @@ -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); diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c @@ -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, diff --git a/sound/firewire/digi00x/digi00x-proc.c b/sound/firewire/digi00x/digi00x-proc.c @@ -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); } diff --git a/sound/firewire/fireface/Makefile b/sound/firewire/fireface/Makefile @@ -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 diff --git a/sound/firewire/fireface/ff-midi.c b/sound/firewire/fireface/ff-midi.c @@ -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); diff --git a/sound/firewire/fireface/ff-pcm.c b/sound/firewire/fireface/ff-pcm.c @@ -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; diff --git a/sound/firewire/fireface/ff-proc.c b/sound/firewire/fireface/ff-proc.c @@ -8,209 +8,29 @@ #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 @@ -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 @@ -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 @@ -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 @@ -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_clock = latter_get_clock, + .switch_fetching_mode = latter_switch_fetching_mode, + .begin_session = latter_begin_session, + .finish_session = latter_finish_session, + .dump_status = latter_dump_status, +}; diff --git a/sound/firewire/fireface/ff-stream.c b/sound/firewire/fireface/ff-stream.c @@ -37,44 +37,10 @@ static void release_resources(struct snd_ff *ff) fw_iso_resources_free(&ff->rx_resources); } -static int 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, - SND_FF_REG_FETCH_PCM_FRAMES, reg, - sizeof(__le32) * count, 0); - kfree(reg); - return err; -} - static inline void finish_session(struct snd_ff *ff) { ff->spec->protocol->finish_session(ff); - switch_fetching_mode(ff, false); + ff->spec->protocol->switch_fetching_mode(ff, false); } static int init_stream(struct snd_ff *ff, enum amdtp_stream_direction dir) @@ -147,7 +113,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) if (ff->substreams_counter == 0) return 0; - err = snd_ff_transaction_get_clock(ff, &curr_rate, &src); + err = ff->spec->protocol->get_clock(ff, &curr_rate, &src); if (err < 0) return err; if (curr_rate != rate || @@ -206,7 +172,7 @@ int snd_ff_stream_start_duplex(struct snd_ff *ff, unsigned int rate) goto error; } - err = switch_fetching_mode(ff, true); + err = ff->spec->protocol->switch_fetching_mode(ff, true); if (err < 0) goto error; } diff --git a/sound/firewire/fireface/ff-transaction.c b/sound/firewire/fireface/ff-transaction.c @@ -8,72 +8,6 @@ #include "ff.h" -#define SND_FF_REG_MIDI_RX_PORT_0 0x000080180000ull -#define SND_FF_REG_MIDI_RX_PORT_1 0x000080190000ull - -int snd_ff_transaction_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, - SND_FF_REG_CLOCK_CONFIG, &reg, sizeof(reg), 0); - if (err < 0) - return err; - data = le32_to_cpu(reg); - - /* Calculate sampling rate. */ - switch ((data >> 1) & 0x03) { - case 0x01: - *rate = 32000; - break; - case 0x00: - *rate = 44100; - break; - case 0x03: - *rate = 48000; - break; - case 0x02: - default: - return -EIO; - } - - if (data & 0x08) - *rate *= 2; - else if (data & 0x10) - *rate *= 4; - - /* Calculate source of clock. */ - if (data & 0x01) { - *src = SND_FF_CLOCK_SRC_INTERNAL; - } else { - /* TODO: 0x02, 0x06, 0x07? */ - switch ((data >> 10) & 0x07) { - case 0x00: - *src = SND_FF_CLOCK_SRC_ADAT1; - break; - case 0x01: - *src = SND_FF_CLOCK_SRC_ADAT2; - break; - case 0x03: - *src = SND_FF_CLOCK_SRC_SPDIF; - break; - case 0x04: - *src = SND_FF_CLOCK_SRC_WORD; - break; - case 0x05: - *src = SND_FF_CLOCK_SRC_LTC; - break; - default: - return -EIO; - } - } - - return 0; -} - static void finish_transmit_midi_msg(struct snd_ff *ff, unsigned int port, int rcode) { @@ -117,23 +51,17 @@ static void finish_transmit_midi1_msg(struct fw_card *card, int rcode, finish_transmit_midi_msg(ff, 1, rcode); } -static inline void fill_midi_buf(struct snd_ff *ff, unsigned int port, - unsigned int index, u8 byte) -{ - ff->msg_buf[port][index] = cpu_to_le32(byte); -} - static void transmit_midi_msg(struct snd_ff *ff, unsigned int port) { struct snd_rawmidi_substream *substream = READ_ONCE(ff->rx_midi_substreams[port]); - u8 *buf = (u8 *)ff->msg_buf[port]; - int i, len; + int quad_count; struct fw_device *fw_dev = fw_parent_device(ff->unit); unsigned long long addr; int generation; fw_transaction_callback_t callback; + int tcode; if (substream == NULL || snd_rawmidi_transmit_empty(substream)) return; @@ -147,26 +75,26 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port) return; } - len = snd_rawmidi_transmit_peek(substream, buf, - SND_FF_MAXIMIM_MIDI_QUADS); - if (len <= 0) + quad_count = ff->spec->protocol->fill_midi_msg(ff, substream, port); + if (quad_count <= 0) return; - for (i = len - 1; i >= 0; i--) - fill_midi_buf(ff, port, i, buf[i]); - if (port == 0) { - addr = SND_FF_REG_MIDI_RX_PORT_0; + addr = ff->spec->midi_rx_addrs[0]; callback = finish_transmit_midi0_msg; } else { - addr = SND_FF_REG_MIDI_RX_PORT_1; + addr = ff->spec->midi_rx_addrs[1]; callback = finish_transmit_midi1_msg; } /* Set interval to next transaction. */ ff->next_ktime[port] = ktime_add_ns(ktime_get(), - len * 8 * NSEC_PER_SEC / 31250); - ff->rx_bytes[port] = len; + ff->rx_bytes[port] * 8 * NSEC_PER_SEC / 31250); + + if (quad_count == 1) + tcode = TCODE_WRITE_QUADLET_REQUEST; + else + tcode = TCODE_WRITE_BLOCK_REQUEST; /* * In Linux FireWire core, when generation is updated with memory @@ -178,10 +106,9 @@ static void transmit_midi_msg(struct snd_ff *ff, unsigned int port) */ generation = fw_dev->generation; smp_rmb(); - fw_send_request(fw_dev->card, &ff->transactions[port], - TCODE_WRITE_BLOCK_REQUEST, + fw_send_request(fw_dev->card, &ff->transactions[port], tcode, fw_dev->node_id, generation, fw_dev->max_speed, - addr, &ff->msg_buf[port], len * 4, + addr, &ff->msg_buf[port], quad_count * 4, callback, &ff->transactions[port]); } @@ -209,7 +136,9 @@ static void handle_midi_msg(struct fw_card *card, struct fw_request *request, fw_send_response(card, request, RCODE_COMPLETE); - ff->spec->protocol->handle_midi_msg(ff, buf, length); + offset -= ff->async_handler.offset; + ff->spec->protocol->handle_midi_msg(ff, (unsigned int)offset, buf, + length); } static int allocate_own_address(struct snd_ff *ff, int i) @@ -217,7 +146,7 @@ static int allocate_own_address(struct snd_ff *ff, int i) struct fw_address_region midi_msg_region; int err; - ff->async_handler.length = SND_FF_MAXIMIM_MIDI_QUADS * 4; + ff->async_handler.length = ff->spec->midi_addr_range; ff->async_handler.address_callback = handle_midi_msg; ff->async_handler.callback_data = ff; @@ -236,35 +165,13 @@ static int allocate_own_address(struct snd_ff *ff, int i) return err; } -/* - * Controllers are allowed to register higher 4 bytes of address to receive - * the transactions. Different models have different registers for this purpose; - * e.g. 0x'0000'8010'03f4 for Fireface 400. - * The controllers are not allowed to register lower 4 bytes of the address. - * They are forced to select one of 4 options for the part of address by writing - * corresponding bits to 0x'0000'8010'051f. - * - * The 3rd-6th bits of this register are flags to indicate lower 4 bytes of - * address to which the device transferrs the transactions. In short: - * - 0x20: 0x'....'....'0000'0180 - * - 0x10: 0x'....'....'0000'0100 - * - 0x08: 0x'....'....'0000'0080 - * - 0x04: 0x'....'....'0000'0000 - * - * This driver configure 0x'....'....'0000'0000 to receive MIDI messages from - * units. The 3rd bit of the register should be configured, however this driver - * deligates this task to userspace applications due to a restriction that this - * register is write-only and the other bits have own effects. - * - * Unlike Fireface 800, Fireface 400 cancels transferring asynchronous - * transactions when the 1st and 2nd of the register stand. These two bits have - * the same effect. - * - 0x02, 0x01: cancel transferring - * - * On the other hand, the bits have no effect on Fireface 800. This model - * cancels asynchronous transactions when the higher 4 bytes of address is - * overwritten with zero. - */ +// Controllers are allowed to register higher 4 bytes of destination address to +// receive asynchronous transactions for MIDI messages, while the way to +// register lower 4 bytes of address is different depending on protocols. For +// details, please refer to comments in protocol implementations. +// +// This driver expects userspace applications to configure registers for the +// lower address because in most cases such registers has the other settings. int snd_ff_transaction_reregister(struct snd_ff *ff) { struct fw_card *fw_card = fw_parent_device(ff->unit)->card; diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c @@ -153,6 +153,8 @@ static const struct snd_ff_spec spec_ff800 = { .midi_out_ports = 1, .protocol = &snd_ff_protocol_ff800, .midi_high_addr = 0x000200000320ull, + .midi_addr_range = 12, + .midi_rx_addrs = {0x000080180000ull, 0}, }; static const struct snd_ff_spec spec_ff400 = { @@ -163,6 +165,20 @@ static const struct snd_ff_spec spec_ff400 = { .midi_out_ports = 2, .protocol = &snd_ff_protocol_ff400, .midi_high_addr = 0x0000801003f4ull, + .midi_addr_range = SND_FF_MAXIMIM_MIDI_QUADS * 4, + .midi_rx_addrs = {0x000080180000ull, 0x000080190000ull}, +}; + +static const struct snd_ff_spec spec_ucx = { + .name = "FirefaceUCX", + .pcm_capture_channels = {18, 14, 12}, + .pcm_playback_channels = {18, 14, 12}, + .midi_in_ports = 2, + .midi_out_ports = 2, + .protocol = &snd_ff_protocol_latter, + .midi_high_addr = 0xffff00000034ull, + .midi_addr_range = 0x80, + .midi_rx_addrs = {0xffff00000030ull, 0xffff00000030ull}, }; static const struct ieee1394_device_id snd_ff_id_table[] = { @@ -190,6 +206,18 @@ static const struct ieee1394_device_id snd_ff_id_table[] = { .model_id = 0x101800, .driver_data = (kernel_ulong_t)&spec_ff400, }, + // Fireface UCX. + { + .match_flags = IEEE1394_MATCH_VENDOR_ID | + IEEE1394_MATCH_SPECIFIER_ID | + IEEE1394_MATCH_VERSION | + IEEE1394_MATCH_MODEL_ID, + .vendor_id = OUI_RME, + .specifier_id = OUI_RME, + .version = 0x000004, + .model_id = 0x101800, + .driver_data = (kernel_ulong_t)&spec_ucx, + }, {} }; MODULE_DEVICE_TABLE(ieee1394, snd_ff_id_table); diff --git a/sound/firewire/fireface/ff.h b/sound/firewire/fireface/ff.h @@ -35,11 +35,6 @@ #define SND_FF_IN_MIDI_PORTS 2 #define SND_FF_OUT_MIDI_PORTS 2 -#define SND_FF_REG_SYNC_STATUS 0x0000801c0000ull -/* For block write request. */ -#define SND_FF_REG_FETCH_PCM_FRAMES 0x0000801c0000ull -#define SND_FF_REG_CLOCK_CONFIG 0x0000801c0004ull - enum snd_ff_stream_mode { SND_FF_STREAM_MODE_LOW = 0, SND_FF_STREAM_MODE_MID, @@ -59,6 +54,8 @@ struct snd_ff_spec { const struct snd_ff_protocol *protocol; u64 midi_high_addr; + u8 midi_addr_range; + u64 midi_rx_addrs[SND_FF_OUT_MIDI_PORTS]; }; struct snd_ff { @@ -78,7 +75,7 @@ struct snd_ff { /* TO handle MIDI rx. */ struct snd_rawmidi_substream *rx_midi_substreams[SND_FF_OUT_MIDI_PORTS]; - u8 running_status[SND_FF_OUT_MIDI_PORTS]; + bool on_sysex[SND_FF_OUT_MIDI_PORTS]; __le32 msg_buf[SND_FF_OUT_MIDI_PORTS][SND_FF_MAXIMIM_MIDI_QUADS]; struct work_struct rx_midi_work[SND_FF_OUT_MIDI_PORTS]; struct fw_transaction transactions[SND_FF_OUT_MIDI_PORTS]; @@ -108,16 +105,23 @@ enum snd_ff_clock_src { }; struct snd_ff_protocol { - void (*handle_midi_msg)(struct snd_ff *ff, __le32 *buf, size_t length); + void (*handle_midi_msg)(struct snd_ff *ff, unsigned int offset, + __le32 *buf, size_t length); + int (*fill_midi_msg)(struct snd_ff *ff, + struct snd_rawmidi_substream *substream, + unsigned int port); + int (*get_clock)(struct snd_ff *ff, unsigned int *rate, + enum snd_ff_clock_src *src); + int (*switch_fetching_mode)(struct snd_ff *ff, bool enable); int (*begin_session)(struct snd_ff *ff, unsigned int rate); void (*finish_session)(struct snd_ff *ff); + void (*dump_status)(struct snd_ff *ff, struct snd_info_buffer *buffer); }; extern const struct snd_ff_protocol snd_ff_protocol_ff800; extern const struct snd_ff_protocol snd_ff_protocol_ff400; +extern const struct snd_ff_protocol snd_ff_protocol_latter; -int snd_ff_transaction_get_clock(struct snd_ff *ff, unsigned int *rate, - enum snd_ff_clock_src *src); int snd_ff_transaction_register(struct snd_ff *ff); int snd_ff_transaction_reregister(struct snd_ff *ff); void snd_ff_transaction_unregister(struct snd_ff *ff); @@ -142,6 +146,7 @@ int snd_ff_stream_lock_try(struct snd_ff *ff); void snd_ff_stream_lock_release(struct snd_ff *ff); void snd_ff_proc_init(struct snd_ff *ff); +const char *snd_ff_proc_get_clk_label(enum snd_ff_clock_src src); int snd_ff_create_midi_devices(struct snd_ff *ff); diff --git a/sound/firewire/fireworks/fireworks_proc.c b/sound/firewire/fireworks/fireworks_proc.c @@ -199,12 +199,8 @@ add_node(struct snd_efw *efw, struct snd_info_entry *root, const char *name, struct snd_info_entry *entry; entry = snd_info_create_card_entry(efw->card, name, root); - if (entry == NULL) - return; - - snd_info_set_text_ops(entry, efw, op); - if (snd_info_register(entry) < 0) - snd_info_free_entry(entry); + if (entry) + snd_info_set_text_ops(entry, efw, op); } void snd_efw_proc_init(struct snd_efw *efw) @@ -220,10 +216,6 @@ void snd_efw_proc_init(struct snd_efw *efw) if (root == NULL) return; root->mode = S_IFDIR | 0555; - if (snd_info_register(root) < 0) { - snd_info_free_entry(root); - return; - } add_node(efw, root, "clock", proc_read_clock); add_node(efw, root, "firmware", proc_read_hwinfo); diff --git a/sound/firewire/motu/amdtp-motu.c b/sound/firewire/motu/amdtp-motu.c @@ -136,7 +136,9 @@ static void read_pcm_s32(struct amdtp_stream *s, byte = (u8 *)buffer + p->pcm_byte_offset; for (c = 0; c < channels; ++c) { - *dst = (byte[0] << 24) | (byte[1] << 16) | byte[2]; + *dst = (byte[0] << 24) | + (byte[1] << 16) | + (byte[2] << 8); byte += 3; dst++; } diff --git a/sound/firewire/motu/motu-proc.c b/sound/firewire/motu/motu-proc.c @@ -87,12 +87,8 @@ static void add_node(struct snd_motu *motu, struct snd_info_entry *root, struct snd_info_entry *entry; entry = snd_info_create_card_entry(motu->card, name, root); - if (entry == NULL) - return; - - snd_info_set_text_ops(entry, motu, op); - if (snd_info_register(entry) < 0) - snd_info_free_entry(entry); + if (entry) + snd_info_set_text_ops(entry, motu, op); } void snd_motu_proc_init(struct snd_motu *motu) @@ -108,10 +104,6 @@ void snd_motu_proc_init(struct snd_motu *motu) if (root == NULL) return; root->mode = S_IFDIR | 0555; - if (snd_info_register(root) < 0) { - snd_info_free_entry(root); - return; - } add_node(motu, root, "clock", proc_read_clock); add_node(motu, root, "format", proc_read_format); diff --git a/sound/firewire/oxfw/oxfw-proc.c b/sound/firewire/oxfw/oxfw-proc.c @@ -83,12 +83,8 @@ static void add_node(struct snd_oxfw *oxfw, struct snd_info_entry *root, struct snd_info_entry *entry; entry = snd_info_create_card_entry(oxfw->card, name, root); - if (entry == NULL) - return; - - snd_info_set_text_ops(entry, oxfw, op); - if (snd_info_register(entry) < 0) - snd_info_free_entry(entry); + if (entry) + snd_info_set_text_ops(entry, oxfw, op); } void snd_oxfw_proc_init(struct snd_oxfw *oxfw) @@ -104,10 +100,6 @@ void snd_oxfw_proc_init(struct snd_oxfw *oxfw) if (root == NULL) return; root->mode = S_IFDIR | 0555; - if (snd_info_register(root) < 0) { - snd_info_free_entry(root); - return; - } add_node(oxfw, root, "formation", proc_read_formation); } diff --git a/sound/firewire/tascam/tascam-proc.c b/sound/firewire/tascam/tascam-proc.c @@ -58,12 +58,8 @@ static void add_node(struct snd_tscm *tscm, struct snd_info_entry *root, struct snd_info_entry *entry; entry = snd_info_create_card_entry(tscm->card, name, root); - if (entry == NULL) - return; - - snd_info_set_text_ops(entry, tscm, op); - if (snd_info_register(entry) < 0) - snd_info_free_entry(entry); + if (entry) + snd_info_set_text_ops(entry, tscm, op); } void snd_tscm_proc_init(struct snd_tscm *tscm) @@ -79,10 +75,6 @@ void snd_tscm_proc_init(struct snd_tscm *tscm) if (root == NULL) return; root->mode = S_IFDIR | 0555; - if (snd_info_register(root) < 0) { - snd_info_free_entry(root); - return; - } add_node(tscm, root, "firmware", proc_read_firmware); } diff --git a/sound/hda/hdac_controller.c b/sound/hda/hdac_controller.c @@ -376,7 +376,7 @@ void snd_hdac_bus_exit_link_reset(struct hdac_bus *bus) { unsigned long timeout; - snd_hdac_chip_updateb(bus, GCTL, 0, AZX_GCTL_RESET); + snd_hdac_chip_updateb(bus, GCTL, AZX_GCTL_RESET, AZX_GCTL_RESET); timeout = jiffies + msecs_to_jiffies(100); while (!snd_hdac_chip_readb(bus, GCTL) && time_before(jiffies, timeout)) @@ -415,7 +415,7 @@ int snd_hdac_bus_reset_link(struct hdac_bus *bus, bool full_reset) } /* Accept unsolicited responses */ - snd_hdac_chip_updatel(bus, GCTL, 0, AZX_GCTL_UNSOL); + snd_hdac_chip_updatel(bus, GCTL, AZX_GCTL_UNSOL, AZX_GCTL_UNSOL); /* detect codecs */ if (!bus->codec_mask) { @@ -431,7 +431,9 @@ EXPORT_SYMBOL_GPL(snd_hdac_bus_reset_link); static void azx_int_enable(struct hdac_bus *bus) { /* enable controller CIE and GIE */ - snd_hdac_chip_updatel(bus, INTCTL, 0, AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN); + snd_hdac_chip_updatel(bus, INTCTL, + AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN, + AZX_INT_CTRL_EN | AZX_INT_GLOBAL_EN); } /* disable interrupts */ diff --git a/sound/hda/hdac_i915.c b/sound/hda/hdac_i915.c @@ -144,9 +144,9 @@ int snd_hdac_i915_init(struct hdac_bus *bus) return -ENODEV; if (!acomp->ops) { request_module("i915"); - /* 10s timeout */ + /* 60s timeout */ wait_for_completion_timeout(&bind_complete, - msecs_to_jiffies(10 * 1000)); + msecs_to_jiffies(60 * 1000)); } if (!acomp->ops) { dev_info(bus->dev, "couldn't bind with audio component\n"); diff --git a/sound/hda/hdac_stream.c b/sound/hda/hdac_stream.c @@ -13,6 +13,40 @@ #include "trace.h" /** + * snd_hdac_get_stream_stripe_ctl - get stripe control value + * @bus: HD-audio core bus + * @substream: PCM substream + */ +int snd_hdac_get_stream_stripe_ctl(struct hdac_bus *bus, + struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + unsigned int channels = runtime->channels, + rate = runtime->rate, + bits_per_sample = runtime->sample_bits, + max_sdo_lines, value, sdo_line; + + /* T_AZA_GCAP_NSDO is 1:2 bitfields in GCAP */ + max_sdo_lines = snd_hdac_chip_readl(bus, GCAP) & AZX_GCAP_NSDO; + + /* following is from HD audio spec */ + for (sdo_line = max_sdo_lines; sdo_line > 0; sdo_line >>= 1) { + if (rate > 48000) + value = (channels * bits_per_sample * + (rate / 48000)) / sdo_line; + else + value = (channels * bits_per_sample) / sdo_line; + + if (value >= 8) + break; + } + + /* stripe value: 0 for 1SDO, 1 for 2SDO, 2 for 4SDO lines */ + return sdo_line >> 1; +} +EXPORT_SYMBOL_GPL(snd_hdac_get_stream_stripe_ctl); + +/** * snd_hdac_stream_init - initialize each stream (aka device) * @bus: HD-audio core bus * @azx_dev: HD-audio core stream object to initialize @@ -48,6 +82,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_stream_init); void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) { struct hdac_bus *bus = azx_dev->bus; + int stripe_ctl; trace_snd_hdac_stream_start(bus, azx_dev); @@ -56,7 +91,13 @@ void snd_hdac_stream_start(struct hdac_stream *azx_dev, bool fresh_start) azx_dev->start_wallclk -= azx_dev->period_wallclk; /* enable SIE */ - snd_hdac_chip_updatel(bus, INTCTL, 0, 1 << azx_dev->index); + snd_hdac_chip_updatel(bus, INTCTL, + 1 << azx_dev->index, + 1 << azx_dev->index); + /* set stripe control */ + stripe_ctl = snd_hdac_get_stream_stripe_ctl(bus, azx_dev->substream); + snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, + stripe_ctl); /* set DMA start and interrupt mask */ snd_hdac_stream_updateb(azx_dev, SD_CTL, 0, SD_CTL_DMA_START | SD_INT_MASK); @@ -73,6 +114,7 @@ void snd_hdac_stream_clear(struct hdac_stream *azx_dev) snd_hdac_stream_updateb(azx_dev, SD_CTL, SD_CTL_DMA_START | SD_INT_MASK, 0); snd_hdac_stream_writeb(azx_dev, SD_STS, SD_INT_MASK); /* to be sure */ + snd_hdac_stream_updateb(azx_dev, SD_CTL_3B, SD_CTL_STRIPE_MASK, 0); azx_dev->running = false; } EXPORT_SYMBOL_GPL(snd_hdac_stream_clear); diff --git a/sound/i2c/other/ak4113.c b/sound/i2c/other/ak4113.c @@ -492,9 +492,8 @@ static void snd_ak4113_proc_regs_read(struct snd_info_entry *entry, static void snd_ak4113_proc_init(struct ak4113 *ak4113) { - struct snd_info_entry *entry; - if (!snd_card_proc_new(ak4113->card, "ak4113", &entry)) - snd_info_set_text_ops(entry, ak4113, snd_ak4113_proc_regs_read); + snd_card_ro_proc_new(ak4113->card, "ak4113", ak4113, + snd_ak4113_proc_regs_read); } int snd_ak4113_build(struct ak4113 *ak4113, diff --git a/sound/i2c/other/ak4114.c b/sound/i2c/other/ak4114.c @@ -465,9 +465,8 @@ static void snd_ak4114_proc_regs_read(struct snd_info_entry *entry, static void snd_ak4114_proc_init(struct ak4114 *ak4114) { - struct snd_info_entry *entry; - if (!snd_card_proc_new(ak4114->card, "ak4114", &entry)) - snd_info_set_text_ops(entry, ak4114, snd_ak4114_proc_regs_read); + snd_card_ro_proc_new(ak4114->card, "ak4114", ak4114, + snd_ak4114_proc_regs_read); } int snd_ak4114_build(struct ak4114 *ak4114, diff --git a/sound/i2c/other/ak4xxx-adda.c b/sound/i2c/other/ak4xxx-adda.c @@ -875,13 +875,7 @@ static void proc_regs_read(struct snd_info_entry *entry, static int proc_init(struct snd_akm4xxx *ak) { - struct snd_info_entry *entry; - int err; - err = snd_card_proc_new(ak->card, ak->name, &entry); - if (err < 0) - return err; - snd_info_set_text_ops(entry, ak, proc_regs_read); - return 0; + return snd_card_ro_proc_new(ak->card, ak->name, ak, proc_regs_read); } int snd_akm4xxx_build_controls(struct snd_akm4xxx *ak) diff --git a/sound/isa/ad1816a/ad1816a_lib.c b/sound/isa/ad1816a/ad1816a_lib.c @@ -518,7 +518,6 @@ void snd_ad1816a_suspend(struct snd_ad1816a *chip) int reg; unsigned long flags; - snd_pcm_suspend_all(chip->pcm); spin_lock_irqsave(&chip->lock, flags); for (reg = 0; reg < 48; reg++) chip->image[reg] = snd_ad1816a_read(chip, reg); @@ -694,7 +693,7 @@ int snd_ad1816a_pcm(struct snd_ad1816a *chip, int device) snd_ad1816a_init(chip); snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_isa_data(), + chip->card->dev, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); chip->pcm = pcm; diff --git a/sound/isa/als100.c b/sound/isa/als100.c @@ -322,7 +322,6 @@ static int snd_als100_pnp_suspend(struct pnp_card_link *pcard, pm_message_t stat struct snd_sb *chip = acard->chip; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); snd_sbmixer_suspend(chip); return 0; } diff --git a/sound/isa/cmi8328.c b/sound/isa/cmi8328.c @@ -434,7 +434,6 @@ static int snd_cmi8328_suspend(struct device *pdev, unsigned int n, cmi = card->private_data; snd_cmi8328_cfg_save(cmi->port, cmi->cfg); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(cmi->wss->pcm); cmi->wss->suspend(cmi->wss); return 0; diff --git a/sound/isa/cmi8330.c b/sound/isa/cmi8330.c @@ -470,7 +470,7 @@ static int snd_cmi8330_pcm(struct snd_card *card, struct snd_cmi8330 *chip) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &chip->streams[SNDRV_PCM_STREAM_CAPTURE].ops); snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_isa_data(), + card->dev, 64*1024, 128*1024); chip->pcm = pcm; @@ -484,7 +484,6 @@ static int snd_cmi8330_suspend(struct snd_card *card) struct snd_cmi8330 *acard = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(acard->pcm); acard->wss->suspend(acard->wss); snd_sbmixer_suspend(acard->sb); return 0; diff --git a/sound/isa/es1688/es1688.c b/sound/isa/es1688/es1688.c @@ -301,10 +301,8 @@ static int snd_es968_pnp_suspend(struct pnp_card_link *pcard, pm_message_t state) { struct snd_card *card = pnp_get_card_drvdata(pcard); - struct snd_es1688 *chip = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); return 0; } diff --git a/sound/isa/es1688/es1688_lib.c b/sound/isa/es1688/es1688_lib.c @@ -121,7 +121,7 @@ EXPORT_SYMBOL(snd_es1688_reset); static int snd_es1688_probe(struct snd_es1688 *chip) { unsigned long flags; - unsigned short major, minor, hw; + unsigned short major, minor; int i; /* @@ -166,14 +166,12 @@ static int snd_es1688_probe(struct snd_es1688 *chip) if (!chip->version) return -ENODEV; /* probably SB */ - hw = ES1688_HW_AUTO; switch (chip->version & 0xfff0) { case 0x4880: snd_printk(KERN_ERR "[0x%lx] ESS: AudioDrive ES488 detected, " "but driver is in another place\n", chip->port); return -ENODEV; case 0x6880: - hw = (chip->version & 0x0f) >= 8 ? ES1688_HW_1688 : ES1688_HW_688; break; default: snd_printk(KERN_ERR "[0x%lx] ESS: unknown AudioDrive chip " @@ -746,7 +744,7 @@ int snd_es1688_pcm(struct snd_card *card, struct snd_es1688 *chip, int device) chip->pcm = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_isa_data(), + card->dev, 64*1024, 64*1024); return 0; } diff --git a/sound/isa/es18xx.c b/sound/isa/es18xx.c @@ -1717,7 +1717,7 @@ static int snd_es18xx_pcm(struct snd_card *card, int device) chip->pcm = pcm; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_isa_data(), + card->dev, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); return 0; @@ -1731,8 +1731,6 @@ static int snd_es18xx_suspend(struct snd_card *card, pm_message_t state) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); - /* power down */ chip->pm_reg = (unsigned char)snd_es18xx_read(chip, ES18XX_PM); chip->pm_reg |= (ES18XX_PM_FM | ES18XX_PM_SUS); diff --git a/sound/isa/gus/gus_irq.c b/sound/isa/gus/gus_irq.c @@ -140,10 +140,7 @@ static void snd_gus_irq_info_read(struct snd_info_entry *entry, void snd_gus_irq_profile_init(struct snd_gus_card *gus) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(gus->card, "gusirq", &entry)) - snd_info_set_text_ops(entry, gus, snd_gus_irq_info_read); + snd_card_ro_proc_new(gus->card, "gusirq", gus, snd_gus_irq_info_read); } #endif diff --git a/sound/isa/gus/gus_main.c b/sound/isa/gus/gus_main.c @@ -92,8 +92,17 @@ static const struct snd_kcontrol_new snd_gus_joystick_control = { static void snd_gus_init_control(struct snd_gus_card *gus) { - if (!gus->ace_flag) - snd_ctl_add(gus->card, snd_ctl_new1(&snd_gus_joystick_control, gus)); + int ret; + + if (!gus->ace_flag) { + ret = + snd_ctl_add(gus->card, + snd_ctl_new1(&snd_gus_joystick_control, + gus)); + if (ret) + snd_printk(KERN_ERR "gus: snd_ctl_add failed: %d\n", + ret); + } } /* diff --git a/sound/isa/gus/gus_mem.c b/sound/isa/gus/gus_mem.c @@ -238,9 +238,6 @@ int snd_gf1_mem_init(struct snd_gus_card * gus) { struct snd_gf1_mem *alloc; struct snd_gf1_mem_block block; -#ifdef CONFIG_SND_DEBUG - struct snd_info_entry *entry; -#endif alloc = &gus->gf1.mem_alloc; mutex_init(&alloc->memory_mutex); @@ -263,8 +260,7 @@ int snd_gf1_mem_init(struct snd_gus_card * gus) if (snd_gf1_mem_xalloc(alloc, &block) == NULL) return -ENOMEM; #ifdef CONFIG_SND_DEBUG - if (! snd_card_proc_new(gus->card, "gusmem", &entry)) - snd_info_set_text_ops(entry, gus, snd_gf1_mem_info_read); + snd_card_ro_proc_new(gus->card, "gusmem", gus, snd_gf1_mem_info_read); #endif return 0; } diff --git a/sound/isa/gus/gus_pcm.c b/sound/isa/gus/gus_pcm.c @@ -891,7 +891,7 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, - snd_dma_isa_data(), + card->dev, 64*1024, gus->gf1.dma1 > 3 ? 128*1024 : 64*1024); pcm->info_flags = 0; @@ -901,7 +901,7 @@ int snd_gf1_pcm_new(struct snd_gus_card *gus, int pcm_dev, int control_index) if (gus->gf1.dma2 == gus->gf1.dma1) pcm->info_flags |= SNDRV_PCM_INFO_HALF_DUPLEX; snd_pcm_lib_preallocate_pages(pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream, - SNDRV_DMA_TYPE_DEV, snd_dma_isa_data(), + SNDRV_DMA_TYPE_DEV, card->dev, 64*1024, gus->gf1.dma2 > 3 ? 128*1024 : 64*1024); } strcpy(pcm->name, pcm->id); diff --git a/sound/isa/opti9xx/miro.c b/sound/isa/opti9xx/miro.c @@ -997,10 +997,7 @@ static void snd_miro_proc_read(struct snd_info_entry * entry, static void snd_miro_proc_init(struct snd_card *card, struct snd_miro *miro) { - struct snd_info_entry *entry; - - if (!snd_card_proc_new(card, "miro", &entry)) - snd_info_set_text_ops(entry, miro, snd_miro_proc_read); + snd_card_ro_proc_new(card, "miro", miro, snd_miro_proc_read); } /* diff --git a/sound/isa/sb/jazz16.c b/sound/isa/sb/jazz16.c @@ -356,7 +356,6 @@ static int snd_jazz16_suspend(struct device *pdev, unsigned int n, struct snd_sb *chip = acard->chip; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); snd_sbmixer_suspend(chip); return 0; } diff --git a/sound/isa/sb/sb16.c b/sound/isa/sb/sb16.c @@ -471,7 +471,6 @@ static int snd_sb16_suspend(struct snd_card *card, pm_message_t state) struct snd_sb *chip = acard->chip; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); snd_sbmixer_suspend(chip); return 0; } diff --git a/sound/isa/sb/sb16_csp.c b/sound/isa/sb/sb16_csp.c @@ -1126,10 +1126,9 @@ static int snd_sb_csp_qsound_transfer(struct snd_sb_csp * p) static int init_proc_entry(struct snd_sb_csp * p, int device) { char name[16]; - struct snd_info_entry *entry; + sprintf(name, "cspD%d", device); - if (! snd_card_proc_new(p->chip->card, name, &entry)) - snd_info_set_text_ops(entry, p, info_read); + snd_card_ro_proc_new(p->chip->card, name, p, info_read); return 0; } diff --git a/sound/isa/sb/sb16_main.c b/sound/isa/sb/sb16_main.c @@ -879,13 +879,17 @@ int snd_sb16dsp_pcm(struct snd_sb *chip, int device) snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_sb16_playback_ops); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_sb16_capture_ops); - if (chip->dma16 >= 0 && chip->dma8 != chip->dma16) - snd_ctl_add(card, snd_ctl_new1(&snd_sb16_dma_control, chip)); - else + if (chip->dma16 >= 0 && chip->dma8 != chip->dma16) { + err = snd_ctl_add(card, snd_ctl_new1( + &snd_sb16_dma_control, chip)); + if (err) + return err; + } else { pcm->info_flags = SNDRV_PCM_INFO_HALF_DUPLEX; + } snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_isa_data(), + card->dev, 64*1024, 128*1024); return 0; } diff --git a/sound/isa/sb/sb8.c b/sound/isa/sb/sb8.c @@ -218,7 +218,6 @@ static int snd_sb8_suspend(struct device *dev, unsigned int n, struct snd_sb *chip = acard->chip; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); snd_sbmixer_suspend(chip); return 0; } diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c @@ -610,7 +610,7 @@ int snd_sb8dsp_pcm(struct snd_sb *chip, int device) if (chip->dma8 > 3 || chip->dma16 >= 0) max_prealloc = 128 * 1024; snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_isa_data(), + card->dev, 64*1024, max_prealloc); return 0; diff --git a/sound/isa/sscape.c b/sound/isa/sscape.c @@ -167,12 +167,13 @@ static inline struct soundscape *get_card_soundscape(struct snd_card *c) * I think this means that the memory has to map to * contiguous pages of physical memory. */ -static struct snd_dma_buffer *get_dmabuf(struct snd_dma_buffer *buf, +static struct snd_dma_buffer *get_dmabuf(struct soundscape *s, + struct snd_dma_buffer *buf, unsigned long size) { if (buf) { if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, - snd_dma_isa_data(), + s->chip->card->dev, size, buf) < 0) { snd_printk(KERN_ERR "sscape: Failed to allocate " "%lu bytes for DMA\n", @@ -443,7 +444,7 @@ static int upload_dma_data(struct soundscape *s, const unsigned char *data, int ret; unsigned char val; - if (!get_dmabuf(&dma, PAGE_ALIGN(32 * 1024))) + if (!get_dmabuf(s, &dma, PAGE_ALIGN(32 * 1024))) return -ENOMEM; spin_lock_irqsave(&s->lock, flags); diff --git a/sound/isa/wss/wss_lib.c b/sound/isa/wss/wss_lib.c @@ -1625,7 +1625,6 @@ static void snd_wss_suspend(struct snd_wss *chip) int reg; unsigned long flags; - snd_pcm_suspend_all(chip->pcm); spin_lock_irqsave(&chip->reg_lock, flags); for (reg = 0; reg < 32; reg++) chip->image[reg] = snd_wss_in(chip, reg); @@ -1943,7 +1942,7 @@ int snd_wss_pcm(struct snd_wss *chip, int device) strcpy(pcm->name, snd_wss_chip_id(chip)); snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_isa_data(), + chip->card->dev, 64*1024, chip->dma1 > 3 || chip->dma2 > 3 ? 128*1024 : 64*1024); chip->pcm = pcm; diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c @@ -454,21 +454,22 @@ static inline void hal2_stop_adc(struct snd_hal2 *hal2) hal2->adc.pbus.pbus->pbdma_ctrl = HPC3_PDMACTRL_LD; } -static int hal2_alloc_dmabuf(struct hal2_codec *codec) +static int hal2_alloc_dmabuf(struct snd_hal2 *hal2, struct hal2_codec *codec) { + struct device *dev = hal2->card->dev; struct hal2_desc *desc; dma_addr_t desc_dma, buffer_dma; int count = H2_BUF_SIZE / H2_BLOCK_SIZE; int i; - codec->buffer = dma_alloc_attrs(NULL, H2_BUF_SIZE, &buffer_dma, + codec->buffer = dma_alloc_attrs(dev, H2_BUF_SIZE, &buffer_dma, GFP_KERNEL, DMA_ATTR_NON_CONSISTENT); if (!codec->buffer) return -ENOMEM; - desc = dma_alloc_attrs(NULL, count * sizeof(struct hal2_desc), + desc = dma_alloc_attrs(dev, count * sizeof(struct hal2_desc), &desc_dma, GFP_KERNEL, DMA_ATTR_NON_CONSISTENT); if (!desc) { - dma_free_attrs(NULL, H2_BUF_SIZE, codec->buffer, buffer_dma, + dma_free_attrs(dev, H2_BUF_SIZE, codec->buffer, buffer_dma, DMA_ATTR_NON_CONSISTENT); return -ENOMEM; } @@ -482,17 +483,19 @@ static int hal2_alloc_dmabuf(struct hal2_codec *codec) desc_dma : desc_dma + (i + 1) * sizeof(struct hal2_desc); desc++; } - dma_cache_sync(NULL, codec->desc, count * sizeof(struct hal2_desc), + dma_cache_sync(dev, codec->desc, count * sizeof(struct hal2_desc), DMA_TO_DEVICE); codec->desc_count = count; return 0; } -static void hal2_free_dmabuf(struct hal2_codec *codec) +static void hal2_free_dmabuf(struct snd_hal2 *hal2, struct hal2_codec *codec) { - dma_free_attrs(NULL, codec->desc_count * sizeof(struct hal2_desc), + struct device *dev = hal2->card->dev; + + dma_free_attrs(dev, codec->desc_count * sizeof(struct hal2_desc), codec->desc, codec->desc_dma, DMA_ATTR_NON_CONSISTENT); - dma_free_attrs(NULL, H2_BUF_SIZE, codec->buffer, codec->buffer_dma, + dma_free_attrs(dev, H2_BUF_SIZE, codec->buffer, codec->buffer_dma, DMA_ATTR_NON_CONSISTENT); } @@ -540,7 +543,7 @@ static int hal2_playback_open(struct snd_pcm_substream *substream) runtime->hw = hal2_pcm_hw; - err = hal2_alloc_dmabuf(&hal2->dac); + err = hal2_alloc_dmabuf(hal2, &hal2->dac); if (err) return err; return 0; @@ -550,7 +553,7 @@ static int hal2_playback_close(struct snd_pcm_substream *substream) { struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream); - hal2_free_dmabuf(&hal2->dac); + hal2_free_dmabuf(hal2, &hal2->dac); return 0; } @@ -606,7 +609,7 @@ static void hal2_playback_transfer(struct snd_pcm_substream *substream, unsigned char *buf = hal2->dac.buffer + rec->hw_data; memcpy(buf, substream->runtime->dma_area + rec->sw_data, bytes); - dma_cache_sync(NULL, buf, bytes, DMA_TO_DEVICE); + dma_cache_sync(hal2->card->dev, buf, bytes, DMA_TO_DEVICE); } @@ -629,7 +632,7 @@ static int hal2_capture_open(struct snd_pcm_substream *substream) runtime->hw = hal2_pcm_hw; - err = hal2_alloc_dmabuf(adc); + err = hal2_alloc_dmabuf(hal2, adc); if (err) return err; return 0; @@ -639,7 +642,7 @@ static int hal2_capture_close(struct snd_pcm_substream *substream) { struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream); - hal2_free_dmabuf(&hal2->adc); + hal2_free_dmabuf(hal2, &hal2->adc); return 0; } @@ -694,7 +697,7 @@ static void hal2_capture_transfer(struct snd_pcm_substream *substream, struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream); unsigned char *buf = hal2->adc.buffer + rec->hw_data; - dma_cache_sync(NULL, buf, bytes, DMA_FROM_DEVICE); + dma_cache_sync(hal2->card->dev, buf, bytes, DMA_FROM_DEVICE); memcpy(substream->runtime->dma_area + rec->sw_data, buf, bytes); } diff --git a/sound/mips/sgio2audio.c b/sound/mips/sgio2audio.c @@ -805,7 +805,7 @@ static int snd_sgio2audio_free(struct snd_sgio2audio *chip) free_irq(snd_sgio2_isr_table[i].irq, &chip->channel[snd_sgio2_isr_table[i].idx]); - dma_free_coherent(NULL, MACEISA_RINGBUFFERS_SIZE, + dma_free_coherent(chip->card->dev, MACEISA_RINGBUFFERS_SIZE, chip->ring_base, chip->ring_base_dma); /* release card data */ @@ -843,8 +843,9 @@ static int snd_sgio2audio_create(struct snd_card *card, chip->card = card; - chip->ring_base = dma_alloc_coherent(NULL, MACEISA_RINGBUFFERS_SIZE, - &chip->ring_base_dma, GFP_USER); + chip->ring_base = dma_alloc_coherent(card->dev, + MACEISA_RINGBUFFERS_SIZE, + &chip->ring_base_dma, GFP_KERNEL); if (chip->ring_base == NULL) { printk(KERN_ERR "sgio2audio: could not allocate ring buffers\n"); diff --git a/sound/parisc/harmony.c b/sound/parisc/harmony.c @@ -669,14 +669,8 @@ snd_harmony_pcm_init(struct snd_harmony *h) } /* pre-allocate space for DMA */ - err = snd_pcm_lib_preallocate_pages_for_all(pcm, h->dma.type, - h->dma.dev, - MAX_BUF_SIZE, - MAX_BUF_SIZE); - if (err < 0) { - printk(KERN_ERR PFX "buffer allocation error: %d\n", err); - return err; - } + snd_pcm_lib_preallocate_pages_for_all(pcm, h->dma.type, h->dma.dev, + MAX_BUF_SIZE, MAX_BUF_SIZE); h->st.format = snd_harmony_set_data_format(h, SNDRV_PCM_FORMAT_S16_BE, 1); diff --git a/sound/pci/ac97/ac97_proc.c b/sound/pci/ac97/ac97_proc.c @@ -436,25 +436,20 @@ void snd_ac97_proc_init(struct snd_ac97 * ac97) return; prefix = ac97_is_audio(ac97) ? "ac97" : "mc97"; sprintf(name, "%s#%d-%d", prefix, ac97->addr, ac97->num); - if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) { + entry = snd_info_create_card_entry(ac97->bus->card, name, + ac97->bus->proc); + if (entry) snd_info_set_text_ops(entry, ac97, snd_ac97_proc_read); - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } ac97->proc = entry; sprintf(name, "%s#%d-%d+regs", prefix, ac97->addr, ac97->num); - if ((entry = snd_info_create_card_entry(ac97->bus->card, name, ac97->bus->proc)) != NULL) { + entry = snd_info_create_card_entry(ac97->bus->card, name, + ac97->bus->proc); + if (entry) { snd_info_set_text_ops(entry, ac97, snd_ac97_proc_regs_read); #ifdef CONFIG_SND_DEBUG entry->mode |= 0200; entry->c.text.write = snd_ac97_proc_regs_write; #endif - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } } ac97->proc_regs = entry; } @@ -473,13 +468,10 @@ void snd_ac97_bus_proc_init(struct snd_ac97_bus * bus) char name[32]; sprintf(name, "codec97#%d", bus->num); - if ((entry = snd_info_create_card_entry(bus->card, name, bus->card->proc_root)) != NULL) { + entry = snd_info_create_card_entry(bus->card, name, + bus->card->proc_root); + if (entry) entry->mode = S_IFDIR | 0555; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } bus->proc = entry; } diff --git a/sound/pci/ad1889.c b/sound/pci/ad1889.c @@ -644,16 +644,11 @@ snd_ad1889_pcm_init(struct snd_ad1889 *chip, int device) chip->psubs = NULL; chip->csubs = NULL; - err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(chip->pci), BUFFER_BYTES_MAX / 2, BUFFER_BYTES_MAX); - if (err < 0) { - dev_err(chip->card->dev, "buffer allocation error: %d\n", err); - return err; - } - return 0; } @@ -741,10 +736,8 @@ snd_ad1889_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffe static void snd_ad1889_proc_init(struct snd_ad1889 *chip) { - struct snd_info_entry *entry; - - if (!snd_card_proc_new(chip->card, chip->card->driver, &entry)) - snd_info_set_text_ops(entry, chip, snd_ad1889_proc_read); + snd_card_ro_proc_new(chip->card, chip->card->driver, + chip, snd_ad1889_proc_read); } static const struct ac97_quirk ac97_quirks[] = { diff --git a/sound/pci/ak4531_codec.c b/sound/pci/ak4531_codec.c @@ -481,8 +481,5 @@ static void snd_ak4531_proc_read(struct snd_info_entry *entry, static void snd_ak4531_proc_init(struct snd_card *card, struct snd_ak4531 *ak4531) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(card, "ak4531", &entry)) - snd_info_set_text_ops(entry, ak4531, snd_ak4531_proc_read); + snd_card_ro_proc_new(card, "ak4531", ak4531, snd_ak4531_proc_read); } diff --git a/sound/pci/ali5451/ali5451.c b/sound/pci/ali5451/ali5451.c @@ -1882,10 +1882,8 @@ static int ali_suspend(struct device *dev) return 0; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - for (i = 0; i < chip->num_of_codecs; i++) { - snd_pcm_suspend_all(chip->pcm[i]); + for (i = 0; i < chip->num_of_codecs; i++) snd_ac97_suspend(chip->ac97[i]); - } spin_lock_irq(&chip->reg_lock); @@ -2051,9 +2049,7 @@ static void snd_ali_proc_read(struct snd_info_entry *entry, static void snd_ali_proc_init(struct snd_ali *codec) { - struct snd_info_entry *entry; - if (!snd_card_proc_new(codec->card, "ali5451", &entry)) - snd_info_set_text_ops(entry, codec, snd_ali_proc_read); + snd_card_ro_proc_new(codec->card, "ali5451", codec, snd_ali_proc_read); } static int snd_ali_resources(struct snd_ali *codec) diff --git a/sound/pci/als300.c b/sound/pci/als300.c @@ -731,7 +731,6 @@ static int snd_als300_suspend(struct device *dev) struct snd_als300 *chip = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); return 0; } diff --git a/sound/pci/als4000.c b/sound/pci/als4000.c @@ -994,7 +994,6 @@ static int snd_als4000_suspend(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); snd_sbmixer_suspend(chip); return 0; } diff --git a/sound/pci/asihpi/asihpi.c b/sound/pci/asihpi/asihpi.c @@ -2782,10 +2782,8 @@ snd_asihpi_proc_read(struct snd_info_entry *entry, static void snd_asihpi_proc_init(struct snd_card_asihpi *asihpi) { - struct snd_info_entry *entry; - - if (!snd_card_proc_new(asihpi->card, "info", &entry)) - snd_info_set_text_ops(entry, asihpi, snd_asihpi_proc_read); + snd_card_ro_proc_new(asihpi->card, "info", asihpi, + snd_asihpi_proc_read); } /*------------------------------------------------------------ diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c @@ -733,6 +733,10 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: case SNDRV_PCM_TRIGGER_RESUME: + if (dma->running && dma->suspended && + cmd == SNDRV_PCM_TRIGGER_RESUME) + writel(dma->saved_curptr, chip->remap_addr + + dma->ops->dt_cur); dma->ops->enable_transfer(chip, 1); dma->running = 1; dma->suspended = 0; @@ -740,9 +744,12 @@ static int snd_atiixp_pcm_trigger(struct snd_pcm_substream *substream, int cmd) case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: case SNDRV_PCM_TRIGGER_SUSPEND: + dma->suspended = cmd == SNDRV_PCM_TRIGGER_SUSPEND; + if (dma->running && dma->suspended) + dma->saved_curptr = readl(chip->remap_addr + + dma->ops->dt_cur); dma->ops->enable_transfer(chip, 0); dma->running = 0; - dma->suspended = cmd == SNDRV_PCM_TRIGGER_SUSPEND; break; default: err = -EINVAL; @@ -1479,14 +1486,6 @@ static int snd_atiixp_suspend(struct device *dev) int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - for (i = 0; i < NUM_ATI_PCMDEVS; i++) - if (chip->pcmdevs[i]) { - struct atiixp_dma *dma = &chip->dmas[i]; - if (dma->substream && dma->running) - dma->saved_curptr = readl(chip->remap_addr + - dma->ops->dt_cur); - snd_pcm_suspend_all(chip->pcmdevs[i]); - } for (i = 0; i < NUM_ATI_CODECS; i++) snd_ac97_suspend(chip->ac97[i]); snd_atiixp_aclink_down(chip); @@ -1514,8 +1513,6 @@ static int snd_atiixp_resume(struct device *dev) dma->substream->ops->prepare(dma->substream); writel((u32)dma->desc_buf.addr | ATI_REG_LINKPTR_EN, chip->remap_addr + dma->ops->llp_offset); - writel(dma->saved_curptr, chip->remap_addr + - dma->ops->dt_cur); } } @@ -1546,10 +1543,7 @@ static void snd_atiixp_proc_read(struct snd_info_entry *entry, static void snd_atiixp_proc_init(struct atiixp *chip) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(chip->card, "atiixp", &entry)) - snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read); + snd_card_ro_proc_new(chip->card, "atiixp", chip, snd_atiixp_proc_read); } diff --git a/sound/pci/atiixp_modem.c b/sound/pci/atiixp_modem.c @@ -1125,8 +1125,6 @@ static int snd_atiixp_suspend(struct device *dev) int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - for (i = 0; i < NUM_ATI_PCMDEVS; i++) - snd_pcm_suspend_all(chip->pcmdevs[i]); for (i = 0; i < NUM_ATI_CODECS; i++) snd_ac97_suspend(chip->ac97[i]); snd_atiixp_aclink_down(chip); @@ -1172,10 +1170,8 @@ static void snd_atiixp_proc_read(struct snd_info_entry *entry, static void snd_atiixp_proc_init(struct atiixp_modem *chip) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(chip->card, "atiixp-modem", &entry)) - snd_info_set_text_ops(entry, chip, snd_atiixp_proc_read); + snd_card_ro_proc_new(chip->card, "atiixp-modem", chip, + snd_atiixp_proc_read); } diff --git a/sound/pci/aw2/aw2-alsa.c b/sound/pci/aw2/aw2-alsa.c @@ -624,15 +624,10 @@ static int snd_aw2_new_pcm(struct aw2 *chip) /* pre-allocation of buffers */ /* Preallocate continuous pages. */ - err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_ana, - SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data - (chip->pci), - 64 * 1024, 64 * 1024); - if (err) - dev_err(chip->card->dev, - "snd_pcm_lib_preallocate_pages_for_all error (0x%X)\n", - err); + snd_pcm_lib_preallocate_pages_for_all(pcm_playback_ana, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 64 * 1024, 64 * 1024); err = snd_pcm_new(chip->card, "Audiowerk2 digital playback", 1, 1, 0, &pcm_playback_num); @@ -661,15 +656,10 @@ static int snd_aw2_new_pcm(struct aw2 *chip) /* pre-allocation of buffers */ /* Preallocate continuous pages. */ - err = snd_pcm_lib_preallocate_pages_for_all(pcm_playback_num, - SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data - (chip->pci), - 64 * 1024, 64 * 1024); - if (err) - dev_err(chip->card->dev, - "snd_pcm_lib_preallocate_pages_for_all error (0x%X)\n", - err); + snd_pcm_lib_preallocate_pages_for_all(pcm_playback_num, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 64 * 1024, 64 * 1024); err = snd_pcm_new(chip->card, "Audiowerk2 capture", 2, 0, 1, &pcm_capture); @@ -699,16 +689,10 @@ static int snd_aw2_new_pcm(struct aw2 *chip) /* pre-allocation of buffers */ /* Preallocate continuous pages. */ - err = snd_pcm_lib_preallocate_pages_for_all(pcm_capture, - SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data - (chip->pci), - 64 * 1024, 64 * 1024); - if (err) - dev_err(chip->card->dev, - "snd_pcm_lib_preallocate_pages_for_all error (0x%X)\n", - err); - + snd_pcm_lib_preallocate_pages_for_all(pcm_capture, + SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + 64 * 1024, 64 * 1024); /* Create control */ err = snd_ctl_add(chip->card, snd_ctl_new1(&aw2_control, chip)); diff --git a/sound/pci/azt3328.c b/sound/pci/azt3328.c @@ -2699,10 +2699,6 @@ snd_azf3328_suspend(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - /* same pcm object for playback/capture */ - snd_pcm_suspend_all(chip->pcm[AZF_CODEC_PLAYBACK]); - snd_pcm_suspend_all(chip->pcm[AZF_CODEC_I2S_OUT]); - snd_azf3328_suspend_ac97(chip); snd_azf3328_suspend_regs(chip, chip->ctrl_io, diff --git a/sound/pci/bt87x.c b/sound/pci/bt87x.c @@ -714,11 +714,11 @@ static int snd_bt87x_pcm(struct snd_bt87x *chip, int device, char *name) pcm->private_data = chip; strcpy(pcm->name, name); snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &snd_bt87x_pcm_ops); - return snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_DEV_SG, - snd_dma_pci_data(chip->pci), - 128 * 1024, - ALIGN(255 * 4092, 1024)); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), + 128 * 1024, + ALIGN(255 * 4092, 1024)); + return 0; } static int snd_bt87x_create(struct snd_card *card, diff --git a/sound/pci/ca0106/ca0106_main.c b/sound/pci/ca0106/ca0106_main.c @@ -1402,21 +1402,17 @@ static int snd_ca0106_pcm(struct snd_ca0106 *emu, int device) for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) { - if ((err = snd_pcm_lib_preallocate_pages(substream, - SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(emu->pci), - 64*1024, 64*1024)) < 0) /* FIXME: 32*1024 for sound buffer, between 32and64 for Periods table. */ - return err; + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(emu->pci), + 64*1024, 64*1024); } for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { - if ((err = snd_pcm_lib_preallocate_pages(substream, - SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(emu->pci), - 64*1024, 64*1024)) < 0) - return err; + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(emu->pci), + 64*1024, 64*1024); } err = snd_pcm_add_chmap_ctls(pcm, SNDRV_PCM_STREAM_PLAYBACK, map, 2, @@ -1910,11 +1906,8 @@ static int snd_ca0106_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct snd_ca0106 *chip = card->private_data; - int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - for (i = 0; i < 4; i++) - snd_pcm_suspend_all(chip->pcm[i]); if (chip->details->ac97) snd_ac97_suspend(chip->ac97); snd_ca0106_mixer_suspend(chip); diff --git a/sound/pci/ca0106/ca0106_proc.c b/sound/pci/ca0106/ca0106_proc.c @@ -424,30 +424,20 @@ static void snd_ca0106_proc_i2c_write(struct snd_info_entry *entry, int snd_ca0106_proc_init(struct snd_ca0106 *emu) { - struct snd_info_entry *entry; - - if(! snd_card_proc_new(emu->card, "iec958", &entry)) - snd_info_set_text_ops(entry, emu, snd_ca0106_proc_iec958); - if(! snd_card_proc_new(emu->card, "ca0106_reg32", &entry)) { - snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read32); - entry->c.text.write = snd_ca0106_proc_reg_write32; - entry->mode |= 0200; - } - if(! snd_card_proc_new(emu->card, "ca0106_reg16", &entry)) - snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read16); - if(! snd_card_proc_new(emu->card, "ca0106_reg8", &entry)) - snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read8); - if(! snd_card_proc_new(emu->card, "ca0106_regs1", &entry)) { - snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read1); - entry->c.text.write = snd_ca0106_proc_reg_write; - entry->mode |= 0200; - } - if(! snd_card_proc_new(emu->card, "ca0106_i2c", &entry)) { - entry->c.text.write = snd_ca0106_proc_i2c_write; - entry->private_data = emu; - entry->mode |= 0200; - } - if(! snd_card_proc_new(emu->card, "ca0106_regs2", &entry)) - snd_info_set_text_ops(entry, emu, snd_ca0106_proc_reg_read2); + snd_card_ro_proc_new(emu->card, "iec958", emu, snd_ca0106_proc_iec958); + snd_card_rw_proc_new(emu->card, "ca0106_reg32", emu, + snd_ca0106_proc_reg_read32, + snd_ca0106_proc_reg_write32); + snd_card_ro_proc_new(emu->card, "ca0106_reg16", emu, + snd_ca0106_proc_reg_read16); + snd_card_ro_proc_new(emu->card, "ca0106_reg8", emu, + snd_ca0106_proc_reg_read8); + snd_card_rw_proc_new(emu->card, "ca0106_regs1", emu, + snd_ca0106_proc_reg_read1, + snd_ca0106_proc_reg_write); + snd_card_rw_proc_new(emu->card, "ca0106_i2c", emu, NULL, + snd_ca0106_proc_i2c_write); + snd_card_ro_proc_new(emu->card, "ca0106_regs2", emu, + snd_ca0106_proc_reg_read2); return 0; } diff --git a/sound/pci/cmipci.c b/sound/pci/cmipci.c @@ -2792,10 +2792,7 @@ static void snd_cmipci_proc_read(struct snd_info_entry *entry, static void snd_cmipci_proc_init(struct cmipci *cm) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(cm->card, "cmipci", &entry)) - snd_info_set_text_ops(entry, cm, snd_cmipci_proc_read); + snd_card_ro_proc_new(cm->card, "cmipci", cm, snd_cmipci_proc_read); } static const struct pci_device_id snd_cmipci_ids[] = { @@ -3351,10 +3348,6 @@ static int snd_cmipci_suspend(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(cm->pcm); - snd_pcm_suspend_all(cm->pcm2); - snd_pcm_suspend_all(cm->pcm_spdif); - /* save registers */ for (i = 0; i < ARRAY_SIZE(saved_regs); i++) cm->saved_regs[i] = snd_cmipci_read(cm, saved_regs[i]); diff --git a/sound/pci/cs4281.c b/sound/pci/cs4281.c @@ -1174,8 +1174,7 @@ static void snd_cs4281_proc_init(struct cs4281 *chip) { struct snd_info_entry *entry; - if (! snd_card_proc_new(chip->card, "cs4281", &entry)) - snd_info_set_text_ops(entry, chip, snd_cs4281_proc_read); + snd_card_ro_proc_new(chip->card, "cs4281", chip, snd_cs4281_proc_read); if (! snd_card_proc_new(chip->card, "cs4281_BA0", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; entry->private_data = chip; @@ -2002,8 +2001,6 @@ static int cs4281_suspend(struct device *dev) unsigned int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); - snd_ac97_suspend(chip->ac97); snd_ac97_suspend(chip->ac97_secondary); diff --git a/sound/pci/cs46xx/cs46xx_dsp_spos.h b/sound/pci/cs46xx/cs46xx_dsp_spos.h @@ -177,22 +177,16 @@ struct dsp_spos_instance { /* proc fs */ struct snd_card *snd_card; struct snd_info_entry * proc_dsp_dir; - struct snd_info_entry * proc_sym_info_entry; - struct snd_info_entry * proc_modules_info_entry; - struct snd_info_entry * proc_parameter_dump_info_entry; - struct snd_info_entry * proc_sample_dump_info_entry; /* SCB's descriptors */ int nscb; int scb_highest_frag_index; struct dsp_scb_descriptor scbs[DSP_MAX_SCB_DESC]; - struct snd_info_entry * proc_scb_info_entry; struct dsp_scb_descriptor * the_null_scb; /* Task's descriptors */ int ntask; struct dsp_task_descriptor tasks[DSP_MAX_TASK_DESC]; - struct snd_info_entry * proc_task_info_entry; /* SPDIF status */ int spdif_status_out; diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c @@ -3781,12 +3781,6 @@ static int snd_cs46xx_suspend(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); chip->in_suspend = 1; - snd_pcm_suspend_all(chip->pcm); -#ifdef CONFIG_SND_CS46XX_NEW_DSP - snd_pcm_suspend_all(chip->pcm_rear); - snd_pcm_suspend_all(chip->pcm_center_lfe); - snd_pcm_suspend_all(chip->pcm_iec958); -#endif // chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL); // chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE); diff --git a/sound/pci/cs46xx/dsp_spos.c b/sound/pci/cs46xx/dsp_spos.c @@ -799,92 +799,49 @@ int cs46xx_dsp_proc_init (struct snd_card *card, struct snd_cs46xx *chip) ins->snd_card = card; - if ((entry = snd_info_create_card_entry(card, "dsp", card->proc_root)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; + entry = snd_info_create_card_entry(card, "dsp", card->proc_root); + if (entry) entry->mode = S_IFDIR | 0555; - - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ins->proc_dsp_dir = entry; if (!ins->proc_dsp_dir) return -ENOMEM; - if ((entry = snd_info_create_card_entry(card, "spos_symbols", ins->proc_dsp_dir)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = chip; - entry->mode = S_IFREG | 0644; - entry->c.text.read = cs46xx_dsp_proc_symbol_table_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ins->proc_sym_info_entry = entry; + entry = snd_info_create_card_entry(card, "spos_symbols", + ins->proc_dsp_dir); + if (entry) + snd_info_set_text_ops(entry, chip, + cs46xx_dsp_proc_symbol_table_read); - if ((entry = snd_info_create_card_entry(card, "spos_modules", ins->proc_dsp_dir)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = chip; - entry->mode = S_IFREG | 0644; - entry->c.text.read = cs46xx_dsp_proc_modules_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ins->proc_modules_info_entry = entry; - - if ((entry = snd_info_create_card_entry(card, "parameter", ins->proc_dsp_dir)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = chip; - entry->mode = S_IFREG | 0644; - entry->c.text.read = cs46xx_dsp_proc_parameter_dump_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ins->proc_parameter_dump_info_entry = entry; - - if ((entry = snd_info_create_card_entry(card, "sample", ins->proc_dsp_dir)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = chip; - entry->mode = S_IFREG | 0644; - entry->c.text.read = cs46xx_dsp_proc_sample_dump_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ins->proc_sample_dump_info_entry = entry; - - if ((entry = snd_info_create_card_entry(card, "task_tree", ins->proc_dsp_dir)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = chip; - entry->mode = S_IFREG | 0644; - entry->c.text.read = cs46xx_dsp_proc_task_tree_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ins->proc_task_info_entry = entry; - - if ((entry = snd_info_create_card_entry(card, "scb_info", ins->proc_dsp_dir)) != NULL) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = chip; - entry->mode = S_IFREG | 0644; - entry->c.text.read = cs46xx_dsp_proc_scb_read; - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - entry = NULL; - } - } - ins->proc_scb_info_entry = entry; + entry = snd_info_create_card_entry(card, "spos_modules", + ins->proc_dsp_dir); + if (entry) + snd_info_set_text_ops(entry, chip, + cs46xx_dsp_proc_modules_read); + + entry = snd_info_create_card_entry(card, "parameter", + ins->proc_dsp_dir); + if (entry) + snd_info_set_text_ops(entry, chip, + cs46xx_dsp_proc_parameter_dump_read); + + entry = snd_info_create_card_entry(card, "sample", + ins->proc_dsp_dir); + if (entry) + snd_info_set_text_ops(entry, chip, + cs46xx_dsp_proc_sample_dump_read); + + entry = snd_info_create_card_entry(card, "task_tree", + ins->proc_dsp_dir); + if (entry) + snd_info_set_text_ops(entry, chip, + cs46xx_dsp_proc_task_tree_read); + + entry = snd_info_create_card_entry(card, "scb_info", + ins->proc_dsp_dir); + if (entry) + snd_info_set_text_ops(entry, chip, + cs46xx_dsp_proc_scb_read); mutex_lock(&chip->spos_mutex); /* register/update SCB's entries on proc */ @@ -906,24 +863,6 @@ int cs46xx_dsp_proc_done (struct snd_cs46xx *chip) if (!ins) return 0; - snd_info_free_entry(ins->proc_sym_info_entry); - ins->proc_sym_info_entry = NULL; - - snd_info_free_entry(ins->proc_modules_info_entry); - ins->proc_modules_info_entry = NULL; - - snd_info_free_entry(ins->proc_parameter_dump_info_entry); - ins->proc_parameter_dump_info_entry = NULL; - - snd_info_free_entry(ins->proc_sample_dump_info_entry); - ins->proc_sample_dump_info_entry = NULL; - - snd_info_free_entry(ins->proc_scb_info_entry); - ins->proc_scb_info_entry = NULL; - - snd_info_free_entry(ins->proc_task_info_entry); - ins->proc_task_info_entry = NULL; - mutex_lock(&chip->spos_mutex); for (i = 0; i < ins->nscb; ++i) { if (ins->scbs[i].deleted) continue; diff --git a/sound/pci/cs46xx/dsp_spos_scb_lib.c b/sound/pci/cs46xx/dsp_spos_scb_lib.c @@ -254,8 +254,9 @@ void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip, if (ins->snd_card != NULL && ins->proc_dsp_dir != NULL && scb->proc_info == NULL) { - if ((entry = snd_info_create_card_entry(ins->snd_card, scb->scb_name, - ins->proc_dsp_dir)) != NULL) { + entry = snd_info_create_card_entry(ins->snd_card, scb->scb_name, + ins->proc_dsp_dir); + if (entry) { scb_info = kmalloc(sizeof(struct proc_scb_info), GFP_KERNEL); if (!scb_info) { snd_info_free_entry(entry); @@ -265,18 +266,8 @@ void cs46xx_dsp_proc_register_scb_desc (struct snd_cs46xx *chip, scb_info->chip = chip; scb_info->scb_desc = scb; - - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = scb_info; - entry->mode = S_IFREG | 0644; - - entry->c.text.read = cs46xx_dsp_proc_scb_info_read; - - if (snd_info_register(entry) < 0) { - snd_info_free_entry(entry); - kfree (scb_info); - entry = NULL; - } + snd_info_set_text_ops(entry, scb_info, + cs46xx_dsp_proc_scb_info_read); } out: scb->proc_info = entry; diff --git a/sound/pci/cs5535audio/cs5535audio_pm.c b/sound/pci/cs5535audio/cs5535audio_pm.c @@ -62,7 +62,6 @@ static int __maybe_unused snd_cs5535audio_suspend(struct device *dev) int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(cs5535au->pcm); snd_ac97_suspend(cs5535au->ac97); for (i = 0; i < NUM_CS5535AUDIO_DMAS; i++) { struct cs5535audio_dma *dma = &cs5535au->dmas[i]; diff --git a/sound/pci/ctxfi/ctatc.c b/sound/pci/ctxfi/ctatc.c @@ -1548,18 +1548,10 @@ static void atc_connect_resources(struct ct_atc *atc) #ifdef CONFIG_PM_SLEEP static int atc_suspend(struct ct_atc *atc) { - int i; struct hw *hw = atc->hw; snd_power_change_state(atc->card, SNDRV_CTL_POWER_D3hot); - for (i = FRONT; i < NUM_PCMS; i++) { - if (!atc->pcms[i]) - continue; - - snd_pcm_suspend_all(atc->pcms[i]); - } - atc_release_resources(atc); hw->suspend(hw); diff --git a/sound/pci/echoaudio/echoaudio.c b/sound/pci/echoaudio/echoaudio.c @@ -884,17 +884,15 @@ static const struct snd_pcm_ops digital_capture_ops = { static int snd_echo_preallocate_pages(struct snd_pcm *pcm, struct device *dev) { struct snd_pcm_substream *ss; - int stream, err; + int stream; for (stream = 0; stream < 2; stream++) - for (ss = pcm->streams[stream].substream; ss; ss = ss->next) { - err = snd_pcm_lib_preallocate_pages(ss, SNDRV_DMA_TYPE_DEV_SG, - dev, - ss->number ? 0 : 128<<10, - 256<<10); - if (err < 0) - return err; - } + for (ss = pcm->streams[stream].substream; ss; ss = ss->next) + snd_pcm_lib_preallocate_pages(ss, SNDRV_DMA_TYPE_DEV_SG, + dev, + ss->number ? 0 : 128<<10, + 256<<10); + return 0; } @@ -2165,9 +2163,6 @@ static int snd_echo_suspend(struct device *dev) { struct echoaudio *chip = dev_get_drvdata(dev); - snd_pcm_suspend_all(chip->analog_pcm); - snd_pcm_suspend_all(chip->digital_pcm); - #ifdef ECHOCARD_HAS_MIDI /* This call can sleep */ if (chip->midi_out) diff --git a/sound/pci/emu10k1/emu10k1.c b/sound/pci/emu10k1/emu10k1.c @@ -224,12 +224,6 @@ static int snd_emu10k1_suspend(struct device *dev) cancel_delayed_work_sync(&emu->emu1010.firmware_work); - snd_pcm_suspend_all(emu->pcm); - snd_pcm_suspend_all(emu->pcm_mic); - snd_pcm_suspend_all(emu->pcm_efx); - snd_pcm_suspend_all(emu->pcm_multi); - snd_pcm_suspend_all(emu->pcm_p16v); - snd_ac97_suspend(emu->ac97); snd_emu10k1_efx_suspend(emu); diff --git a/sound/pci/emu10k1/emu10k1x.c b/sound/pci/emu10k1/emu10k1x.c @@ -1065,15 +1065,9 @@ static void snd_emu10k1x_proc_reg_write(struct snd_info_entry *entry, static int snd_emu10k1x_proc_init(struct emu10k1x *emu) { - struct snd_info_entry *entry; - - if(! snd_card_proc_new(emu->card, "emu10k1x_regs", &entry)) { - snd_info_set_text_ops(entry, emu, snd_emu10k1x_proc_reg_read); - entry->c.text.write = snd_emu10k1x_proc_reg_write; - entry->mode |= 0200; - entry->private_data = emu; - } - + snd_card_rw_proc_new(emu->card, "emu10k1x_regs", emu, + snd_emu10k1x_proc_reg_read, + snd_emu10k1x_proc_reg_write); return 0; } diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c @@ -1427,11 +1427,14 @@ int snd_emu10k1_pcm(struct snd_emu10k1 *emu, int device) emu->pcm = pcm; for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0) - return err; + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(emu->pci), + 64*1024, 64*1024); for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) - snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(emu->pci), + 64*1024, 64*1024); return 0; } @@ -1455,8 +1458,9 @@ int snd_emu10k1_pcm_multi(struct snd_emu10k1 *emu, int device) emu->pcm_multi = pcm; for (substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) - if ((err = snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, snd_dma_pci_data(emu->pci), 64*1024, 64*1024)) < 0) - return err; + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(emu->pci), + 64*1024, 64*1024); return 0; } @@ -1489,7 +1493,9 @@ int snd_emu10k1_pcm_mic(struct snd_emu10k1 *emu, int device) strcpy(pcm->name, "Mic Capture"); emu->pcm_mic = pcm; - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(emu->pci), + 64*1024, 64*1024); return 0; } @@ -1862,7 +1868,9 @@ int snd_emu10k1_pcm_efx(struct snd_emu10k1 *emu, int device) if (err < 0) return err; - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(emu->pci), 64*1024, 64*1024); + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(emu->pci), + 64*1024, 64*1024); return 0; } diff --git a/sound/pci/emu10k1/emuproc.c b/sound/pci/emu10k1/emuproc.c @@ -568,55 +568,40 @@ int snd_emu10k1_proc_init(struct snd_emu10k1 *emu) struct snd_info_entry *entry; #ifdef CONFIG_SND_DEBUG if (emu->card_capabilities->emu_model) { - if (! snd_card_proc_new(emu->card, "emu1010_regs", &entry)) - snd_info_set_text_ops(entry, emu, snd_emu_proc_emu1010_reg_read); - } - if (! snd_card_proc_new(emu->card, "io_regs", &entry)) { - snd_info_set_text_ops(entry, emu, snd_emu_proc_io_reg_read); - entry->c.text.write = snd_emu_proc_io_reg_write; - entry->mode |= 0200; - } - if (! snd_card_proc_new(emu->card, "ptr_regs00a", &entry)) { - snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00a); - entry->c.text.write = snd_emu_proc_ptr_reg_write00; - entry->mode |= 0200; - } - if (! snd_card_proc_new(emu->card, "ptr_regs00b", &entry)) { - snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read00b); - entry->c.text.write = snd_emu_proc_ptr_reg_write00; - entry->mode |= 0200; - } - if (! snd_card_proc_new(emu->card, "ptr_regs20a", &entry)) { - snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20a); - entry->c.text.write = snd_emu_proc_ptr_reg_write20; - entry->mode |= 0200; - } - if (! snd_card_proc_new(emu->card, "ptr_regs20b", &entry)) { - snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20b); - entry->c.text.write = snd_emu_proc_ptr_reg_write20; - entry->mode |= 0200; - } - if (! snd_card_proc_new(emu->card, "ptr_regs20c", &entry)) { - snd_info_set_text_ops(entry, emu, snd_emu_proc_ptr_reg_read20c); - entry->c.text.write = snd_emu_proc_ptr_reg_write20; - entry->mode |= 0200; + snd_card_ro_proc_new(emu->card, "emu1010_regs", + emu, snd_emu_proc_emu1010_reg_read); } + snd_card_rw_proc_new(emu->card, "io_regs", emu, + snd_emu_proc_io_reg_read, + snd_emu_proc_io_reg_write); + snd_card_rw_proc_new(emu->card, "ptr_regs00a", emu, + snd_emu_proc_ptr_reg_read00a, + snd_emu_proc_ptr_reg_write00); + snd_card_rw_proc_new(emu->card, "ptr_regs00b", emu, + snd_emu_proc_ptr_reg_read00b, + snd_emu_proc_ptr_reg_write00); + snd_card_rw_proc_new(emu->card, "ptr_regs20a", emu, + snd_emu_proc_ptr_reg_read20a, + snd_emu_proc_ptr_reg_write20); + snd_card_rw_proc_new(emu->card, "ptr_regs20b", emu, + snd_emu_proc_ptr_reg_read20b, + snd_emu_proc_ptr_reg_write20); + snd_card_rw_proc_new(emu->card, "ptr_regs20c", emu, + snd_emu_proc_ptr_reg_read20c, + snd_emu_proc_ptr_reg_write20); #endif - if (! snd_card_proc_new(emu->card, "emu10k1", &entry)) - snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_read); + snd_card_ro_proc_new(emu->card, "emu10k1", emu, snd_emu10k1_proc_read); - if (emu->card_capabilities->emu10k2_chip) { - if (! snd_card_proc_new(emu->card, "spdif-in", &entry)) - snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_spdif_read); - } - if (emu->card_capabilities->ca0151_chip) { - if (! snd_card_proc_new(emu->card, "capture-rates", &entry)) - snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_rates_read); - } + if (emu->card_capabilities->emu10k2_chip) + snd_card_ro_proc_new(emu->card, "spdif-in", emu, + snd_emu10k1_proc_spdif_read); + if (emu->card_capabilities->ca0151_chip) + snd_card_ro_proc_new(emu->card, "capture-rates", emu, + snd_emu10k1_proc_rates_read); - if (! snd_card_proc_new(emu->card, "voices", &entry)) - snd_info_set_text_ops(entry, emu, snd_emu10k1_proc_voices_read); + snd_card_ro_proc_new(emu->card, "voices", emu, + snd_emu10k1_proc_voices_read); if (! snd_card_proc_new(emu->card, "fx8010_gpr", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; @@ -646,11 +631,7 @@ int snd_emu10k1_proc_init(struct snd_emu10k1 *emu) entry->size = emu->audigy ? A_TOTAL_SIZE_CODE : TOTAL_SIZE_CODE; entry->c.ops = &snd_emu10k1_proc_ops_fx8010; } - if (! snd_card_proc_new(emu->card, "fx8010_acode", &entry)) { - entry->content = SNDRV_INFO_CONTENT_TEXT; - entry->private_data = emu; - entry->mode = S_IFREG | 0444 /*| S_IWUSR*/; - entry->c.text.read = snd_emu10k1_proc_acode_read; - } + snd_card_ro_proc_new(emu->card, "fx8010_acode", emu, + snd_emu10k1_proc_acode_read); return 0; } diff --git a/sound/pci/emu10k1/p16v.c b/sound/pci/emu10k1/p16v.c @@ -656,11 +656,10 @@ int snd_p16v_pcm(struct snd_emu10k1 *emu, int device) for(substream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream; substream; substream = substream->next) { - if ((err = snd_pcm_lib_preallocate_pages(substream, - SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(emu->pci), - ((65536 - 64) * 8), ((65536 - 64) * 8))) < 0) - return err; + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(emu->pci), + (65536 - 64) * 8, + (65536 - 64) * 8); /* dev_dbg(emu->card->dev, "preallocate playback substream: err=%d\n", err); @@ -670,11 +669,9 @@ int snd_p16v_pcm(struct snd_emu10k1 *emu, int device) for (substream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream; substream; substream = substream->next) { - if ((err = snd_pcm_lib_preallocate_pages(substream, - SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(emu->pci), - 65536 - 64, 65536 - 64)) < 0) - return err; + snd_pcm_lib_preallocate_pages(substream, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(emu->pci), + 65536 - 64, 65536 - 64); /* dev_dbg(emu->card->dev, "preallocate capture substream: err=%d\n", err); diff --git a/sound/pci/ens1370.c b/sound/pci/ens1370.c @@ -1902,10 +1902,8 @@ static void snd_ensoniq_proc_read(struct snd_info_entry *entry, static void snd_ensoniq_proc_init(struct ensoniq *ensoniq) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(ensoniq->card, "audiopci", &entry)) - snd_info_set_text_ops(entry, ensoniq, snd_ensoniq_proc_read); + snd_card_ro_proc_new(ensoniq->card, "audiopci", ensoniq, + snd_ensoniq_proc_read); } /* @@ -2037,9 +2035,6 @@ static int snd_ensoniq_suspend(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(ensoniq->pcm1); - snd_pcm_suspend_all(ensoniq->pcm2); - #ifdef CHIP1371 snd_ac97_suspend(ensoniq->u.es1371.ac97); #else diff --git a/sound/pci/es1938.c b/sound/pci/es1938.c @@ -1475,7 +1475,6 @@ static int es1938_suspend(struct device *dev) unsigned char *s, *d; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); /* save mixer-related registers */ for (s = saved_regs, d = chip->saved_regs; *s; s++, d++) diff --git a/sound/pci/es1968.c b/sound/pci/es1968.c @@ -2392,7 +2392,6 @@ static int es1968_suspend(struct device *dev) chip->in_suspend = 1; cancel_work_sync(&chip->hwvol_work); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); snd_es1968_bob_stop(chip); return 0; diff --git a/sound/pci/fm801.c b/sound/pci/fm801.c @@ -1408,7 +1408,6 @@ static int snd_fm801_suspend(struct device *dev) if (chip->tea575x_tuner & TUNER_ONLY) { /* FIXME: tea575x suspend */ } else { - snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); snd_ac97_suspend(chip->ac97_sec); } diff --git a/sound/pci/hda/hda_beep.c b/sound/pci/hda/hda_beep.c @@ -127,44 +127,6 @@ static void turn_off_beep(struct hda_beep *beep) } } -static void snd_hda_do_detach(struct hda_beep *beep) -{ - if (beep->registered) - input_unregister_device(beep->dev); - else - input_free_device(beep->dev); - beep->dev = NULL; - turn_off_beep(beep); -} - -static int snd_hda_do_attach(struct hda_beep *beep) -{ - struct input_dev *input_dev; - struct hda_codec *codec = beep->codec; - - input_dev = input_allocate_device(); - if (!input_dev) - return -ENOMEM; - - /* setup digital beep device */ - input_dev->name = "HDA Digital PCBeep"; - input_dev->phys = beep->phys; - input_dev->id.bustype = BUS_PCI; - input_dev->dev.parent = &codec->card->card_dev; - - input_dev->id.vendor = codec->core.vendor_id >> 16; - input_dev->id.product = codec->core.vendor_id & 0xffff; - input_dev->id.version = 0x01; - - input_dev->evbit[0] = BIT_MASK(EV_SND); - input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); - input_dev->event = snd_hda_beep_event; - input_set_drvdata(input_dev, beep); - - beep->dev = input_dev; - return 0; -} - /** * snd_hda_enable_beep_device - Turn on/off beep sound * @codec: the HDA codec @@ -186,6 +148,38 @@ int snd_hda_enable_beep_device(struct hda_codec *codec, int enable) } EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device); +static int beep_dev_register(struct snd_device *device) +{ + struct hda_beep *beep = device->device_data; + int err; + + err = input_register_device(beep->dev); + if (!err) + beep->registered = true; + return err; +} + +static int beep_dev_disconnect(struct snd_device *device) +{ + struct hda_beep *beep = device->device_data; + + if (beep->registered) + input_unregister_device(beep->dev); + else + input_free_device(beep->dev); + turn_off_beep(beep); + return 0; +} + +static int beep_dev_free(struct snd_device *device) +{ + struct hda_beep *beep = device->device_data; + + beep->codec->beep = NULL; + kfree(beep); + return 0; +} + /** * snd_hda_attach_beep_device - Attach a beep input device * @codec: the HDA codec @@ -194,14 +188,16 @@ EXPORT_SYMBOL_GPL(snd_hda_enable_beep_device); * Attach a beep object to the given widget. If beep hint is turned off * explicitly or beep_mode of the codec is turned off, this doesn't nothing. * - * The attached beep device has to be registered via - * snd_hda_register_beep_device() and released via snd_hda_detach_beep_device() - * appropriately. - * * Currently, only one beep device is allowed to each codec. */ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) { + static struct snd_device_ops ops = { + .dev_register = beep_dev_register, + .dev_disconnect = beep_dev_disconnect, + .dev_free = beep_dev_free, + }; + struct input_dev *input_dev; struct hda_beep *beep; int err; @@ -226,14 +222,41 @@ int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) INIT_WORK(&beep->beep_work, &snd_hda_generate_beep); mutex_init(&beep->mutex); - err = snd_hda_do_attach(beep); - if (err < 0) { - kfree(beep); - codec->beep = NULL; - return err; + input_dev = input_allocate_device(); + if (!input_dev) { + err = -ENOMEM; + goto err_free; } + /* setup digital beep device */ + input_dev->name = "HDA Digital PCBeep"; + input_dev->phys = beep->phys; + input_dev->id.bustype = BUS_PCI; + input_dev->dev.parent = &codec->card->card_dev; + + input_dev->id.vendor = codec->core.vendor_id >> 16; + input_dev->id.product = codec->core.vendor_id & 0xffff; + input_dev->id.version = 0x01; + + input_dev->evbit[0] = BIT_MASK(EV_SND); + input_dev->sndbit[0] = BIT_MASK(SND_BELL) | BIT_MASK(SND_TONE); + input_dev->event = snd_hda_beep_event; + input_set_drvdata(input_dev, beep); + + beep->dev = input_dev; + + err = snd_device_new(codec->card, SNDRV_DEV_JACK, beep, &ops); + if (err < 0) + goto err_input; + return 0; + + err_input: + input_free_device(beep->dev); + err_free: + kfree(beep); + codec->beep = NULL; + return err; } EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device); @@ -243,41 +266,11 @@ EXPORT_SYMBOL_GPL(snd_hda_attach_beep_device); */ void snd_hda_detach_beep_device(struct hda_codec *codec) { - struct hda_beep *beep = codec->beep; - if (beep) { - if (beep->dev) - snd_hda_do_detach(beep); - codec->beep = NULL; - kfree(beep); - } + if (!codec->bus->shutdown && codec->beep) + snd_device_free(codec->card, codec->beep); } EXPORT_SYMBOL_GPL(snd_hda_detach_beep_device); -/** - * snd_hda_register_beep_device - Register the beep device - * @codec: the HDA codec - */ -int snd_hda_register_beep_device(struct hda_codec *codec) -{ - struct hda_beep *beep = codec->beep; - int err; - - if (!beep || !beep->dev) - return 0; - - err = input_register_device(beep->dev); - if (err < 0) { - codec_err(codec, "hda_beep: unable to register input device\n"); - input_free_device(beep->dev); - codec->beep = NULL; - kfree(beep); - return err; - } - beep->registered = true; - return 0; -} -EXPORT_SYMBOL_GPL(snd_hda_register_beep_device); - static bool ctl_has_mute(struct snd_kcontrol *kcontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h @@ -34,7 +34,6 @@ struct hda_beep { int snd_hda_enable_beep_device(struct hda_codec *codec, int enable); int snd_hda_attach_beep_device(struct hda_codec *codec, int nid); void snd_hda_detach_beep_device(struct hda_codec *codec); -int snd_hda_register_beep_device(struct hda_codec *codec); #else static inline int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) { @@ -43,9 +42,5 @@ static inline int snd_hda_attach_beep_device(struct hda_codec *codec, int nid) static inline void snd_hda_detach_beep_device(struct hda_codec *codec) { } -static inline int snd_hda_register_beep_device(struct hda_codec *codec) -{ - return 0; -} #endif #endif diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c @@ -813,7 +813,6 @@ void snd_hda_codec_register(struct hda_codec *codec) if (codec->registered) return; if (device_is_registered(hda_codec_dev(codec))) { - snd_hda_register_beep_device(codec); codec_display_power(codec, true); pm_runtime_enable(hda_codec_dev(codec)); /* it was powered up in snd_hda_codec_new(), now all done */ @@ -828,14 +827,6 @@ static int snd_hda_codec_dev_register(struct snd_device *device) return 0; } -static int snd_hda_codec_dev_disconnect(struct snd_device *device) -{ - struct hda_codec *codec = device->device_data; - - snd_hda_detach_beep_device(codec); - return 0; -} - static int snd_hda_codec_dev_free(struct snd_device *device) { struct hda_codec *codec = device->device_data; @@ -921,7 +912,6 @@ int snd_hda_codec_device_new(struct hda_bus *bus, struct snd_card *card, int err; static struct snd_device_ops dev_ops = { .dev_register = snd_hda_codec_dev_register, - .dev_disconnect = snd_hda_codec_dev_disconnect, .dev_free = snd_hda_codec_dev_free, }; @@ -2917,18 +2907,16 @@ static void hda_call_codec_resume(struct hda_codec *codec) hda_jackpoll_work(&codec->jackpoll_work.work); else snd_hda_jack_report_sync(codec); + codec->core.dev.power.power_state = PMSG_ON; snd_hdac_leave_pm(&codec->core); } static int hda_codec_runtime_suspend(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); - struct hda_pcm *pcm; unsigned int state; cancel_delayed_work_sync(&codec->jackpoll_work); - list_for_each_entry(pcm, &codec->pcm_list_head, list) - snd_pcm_suspend_all(pcm->pcm); state = hda_call_codec_suspend(codec); if (codec->link_down_at_suspend || (codec_has_clkstop(codec) && codec_has_epss(codec) && @@ -2950,10 +2938,48 @@ static int hda_codec_runtime_resume(struct device *dev) } #endif /* CONFIG_PM */ +#ifdef CONFIG_PM_SLEEP +static int hda_codec_pm_suspend(struct device *dev) +{ + dev->power.power_state = PMSG_SUSPEND; + return pm_runtime_force_suspend(dev); +} + +static int hda_codec_pm_resume(struct device *dev) +{ + dev->power.power_state = PMSG_RESUME; + return pm_runtime_force_resume(dev); +} + +static int hda_codec_pm_freeze(struct device *dev) +{ + dev->power.power_state = PMSG_FREEZE; + return pm_runtime_force_suspend(dev); +} + +static int hda_codec_pm_thaw(struct device *dev) +{ + dev->power.power_state = PMSG_THAW; + return pm_runtime_force_resume(dev); +} + +static int hda_codec_pm_restore(struct device *dev) +{ + dev->power.power_state = PMSG_RESTORE; + return pm_runtime_force_resume(dev); +} +#endif /* CONFIG_PM_SLEEP */ + /* referred in hda_bind.c */ const struct dev_pm_ops hda_codec_driver_pm = { - SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, - pm_runtime_force_resume) +#ifdef CONFIG_PM_SLEEP + .suspend = hda_codec_pm_suspend, + .resume = hda_codec_pm_resume, + .freeze = hda_codec_pm_freeze, + .thaw = hda_codec_pm_thaw, + .poweroff = hda_codec_pm_suspend, + .restore = hda_codec_pm_restore, +#endif /* CONFIG_PM_SLEEP */ SET_RUNTIME_PM_OPS(hda_codec_runtime_suspend, hda_codec_runtime_resume, NULL) }; diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c @@ -919,15 +919,8 @@ static void print_codec_info(struct snd_info_entry *entry, int snd_hda_codec_proc_new(struct hda_codec *codec) { char name[32]; - struct snd_info_entry *entry; - int err; snprintf(name, sizeof(name), "codec#%d", codec->core.addr); - err = snd_card_proc_new(codec->card, name, &entry); - if (err < 0) - return err; - - snd_info_set_text_ops(entry, codec, print_codec_info); - return 0; + return snd_card_ro_proc_new(codec->card, name, codec, print_codec_info); } diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c @@ -32,6 +32,7 @@ #include <linux/slab.h> #include <linux/time.h> #include <linux/string.h> +#include <linux/pm_runtime.h> #include <sound/core.h> #include <sound/initval.h> @@ -232,40 +233,72 @@ static void hda_tegra_disable_clocks(struct hda_tegra *data) static int hda_tegra_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); - struct azx *chip = card->private_data; - struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); - struct hdac_bus *bus = azx_bus(chip); + int rc; + rc = pm_runtime_force_suspend(dev); + if (rc < 0) + return rc; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - azx_stop_chip(chip); - synchronize_irq(bus->irq); - azx_enter_link_reset(chip); - hda_tegra_disable_clocks(hda); - return 0; } static int hda_tegra_resume(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); + int rc; + + rc = pm_runtime_force_resume(dev); + if (rc < 0) + return rc; + snd_power_change_state(card, SNDRV_CTL_POWER_D0); + + return 0; +} +#endif /* CONFIG_PM_SLEEP */ + +#ifdef CONFIG_PM +static int hda_tegra_runtime_suspend(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); struct azx *chip = card->private_data; struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); + struct hdac_bus *bus = azx_bus(chip); - hda_tegra_enable_clocks(hda); + if (chip && chip->running) { + azx_stop_chip(chip); + synchronize_irq(bus->irq); + azx_enter_link_reset(chip); + } + hda_tegra_disable_clocks(hda); - hda_tegra_init(hda); + return 0; +} - azx_init_chip(chip, 1); +static int hda_tegra_runtime_resume(struct device *dev) +{ + struct snd_card *card = dev_get_drvdata(dev); + struct azx *chip = card->private_data; + struct hda_tegra *hda = container_of(chip, struct hda_tegra, chip); + int rc; - snd_power_change_state(card, SNDRV_CTL_POWER_D0); + rc = hda_tegra_enable_clocks(hda); + if (rc != 0) + return rc; + if (chip && chip->running) { + hda_tegra_init(hda); + azx_init_chip(chip, 1); + } return 0; } -#endif /* CONFIG_PM_SLEEP */ +#endif /* CONFIG_PM */ static const struct dev_pm_ops hda_tegra_pm = { SET_SYSTEM_SLEEP_PM_OPS(hda_tegra_suspend, hda_tegra_resume) + SET_RUNTIME_PM_OPS(hda_tegra_runtime_suspend, + hda_tegra_runtime_resume, + NULL) }; static int hda_tegra_dev_disconnect(struct snd_device *device) @@ -303,7 +336,23 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) struct hdac_bus *bus = azx_bus(chip); struct device *dev = hda->dev; struct resource *res; - int err; + + res = platform_get_resource(pdev, IORESOURCE_MEM, 0); + hda->regs = devm_ioremap_resource(dev, res); + if (IS_ERR(hda->regs)) + return PTR_ERR(hda->regs); + + bus->remap_addr = hda->regs + HDA_BAR0; + bus->addr = res->start + HDA_BAR0; + + hda_tegra_init(hda); + + return 0; +} + +static int hda_tegra_init_clk(struct hda_tegra *hda) +{ + struct device *dev = hda->dev; hda->hda_clk = devm_clk_get(dev, "hda"); if (IS_ERR(hda->hda_clk)) { @@ -321,22 +370,6 @@ static int hda_tegra_init_chip(struct azx *chip, struct platform_device *pdev) return PTR_ERR(hda->hda2hdmi_clk); } - res = platform_get_resource(pdev, IORESOURCE_MEM, 0); - hda->regs = devm_ioremap_resource(dev, res); - if (IS_ERR(hda->regs)) - return PTR_ERR(hda->regs); - - bus->remap_addr = hda->regs + HDA_BAR0; - bus->addr = res->start + HDA_BAR0; - - err = hda_tegra_enable_clocks(hda); - if (err) { - dev_err(dev, "failed to get enable clocks\n"); - return err; - } - - hda_tegra_init(hda); - return 0; } @@ -347,8 +380,8 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) int err; unsigned short gcap; int irq_id = platform_get_irq(pdev, 0); - const char *sname; - struct device_node *root; + const char *sname, *drv_name = "tegra-hda"; + struct device_node *np = pdev->dev.of_node; err = hda_tegra_init_chip(chip, pdev); if (err) @@ -407,17 +440,11 @@ static int hda_tegra_first_init(struct azx *chip, struct platform_device *pdev) } /* driver name */ - strcpy(card->driver, "tegra-hda"); - - root = of_find_node_by_path("/"); - sname = of_get_property(root, "compatible", NULL); - of_node_put(root); - if (!sname) { - dev_err(card->dev, - "failed to get compatible property from root node\n"); - return -ENODEV; - } + strncpy(card->driver, drv_name, sizeof(card->driver)); /* shortname for card */ + sname = of_get_property(np, "nvidia,model", NULL); + if (!sname) + sname = drv_name; if (strlen(sname) > sizeof(card->shortname)) dev_info(card->dev, "truncating shortname for card\n"); strncpy(card->shortname, sname, sizeof(card->shortname)); @@ -487,7 +514,8 @@ MODULE_DEVICE_TABLE(of, hda_tegra_match); static int hda_tegra_probe(struct platform_device *pdev) { - const unsigned int driver_flags = AZX_DCAPS_CORBRP_SELF_CLEAR; + const unsigned int driver_flags = AZX_DCAPS_CORBRP_SELF_CLEAR | + AZX_DCAPS_PM_RUNTIME; struct snd_card *card; struct azx *chip; struct hda_tegra *hda; @@ -506,12 +534,21 @@ static int hda_tegra_probe(struct platform_device *pdev) return err; } + err = hda_tegra_init_clk(hda); + if (err < 0) + goto out_free; + err = hda_tegra_create(card, driver_flags, hda); if (err < 0) goto out_free; card->private_data = chip; dev_set_drvdata(&pdev->dev, card); + + pm_runtime_enable(hda->dev); + if (!azx_has_pm_runtime(chip)) + pm_runtime_forbid(hda->dev); + schedule_work(&hda->probe_work); return 0; @@ -528,6 +565,7 @@ static void hda_tegra_probe_work(struct work_struct *work) struct platform_device *pdev = to_platform_device(hda->dev); int err; + pm_runtime_get_sync(hda->dev); err = hda_tegra_first_init(chip, pdev); if (err < 0) goto out_free; @@ -549,12 +587,18 @@ static void hda_tegra_probe_work(struct work_struct *work) snd_hda_set_power_save(&chip->bus, power_save * 1000); out_free: + pm_runtime_put(hda->dev); return; /* no error return from async probe */ } static int hda_tegra_remove(struct platform_device *pdev) { - return snd_card_free(dev_get_drvdata(&pdev->dev)); + int ret; + + ret = snd_card_free(dev_get_drvdata(&pdev->dev)); + pm_runtime_disable(&pdev->dev); + + return ret; } static void hda_tegra_shutdown(struct platform_device *pdev) diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c @@ -1865,7 +1865,7 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, hda_nid_t pin_nid; struct snd_pcm_runtime *runtime = substream->runtime; bool non_pcm; - int pinctl; + int pinctl, stripe; int err = 0; mutex_lock(&spec->pcm_lock); @@ -1909,6 +1909,14 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo, per_pin->channels = substream->runtime->channels; per_pin->setup = true; + if (get_wcaps(codec, cvt_nid) & AC_WCAP_STRIPE) { + stripe = snd_hdac_get_stream_stripe_ctl(&codec->bus->core, + substream); + snd_hda_codec_write(codec, cvt_nid, 0, + AC_VERB_SET_STRIPE_CONTROL, + stripe); + } + hdmi_setup_audio_infoframe(codec, per_pin, non_pcm); mutex_unlock(&per_pin->lock); if (spec->dyn_pin_out) { diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c @@ -118,6 +118,7 @@ struct alc_spec { unsigned int has_alc5505_dsp:1; unsigned int no_depop_delay:1; unsigned int done_hp_init:1; + unsigned int no_shutup_pins:1; /* for PLL fix */ hda_nid_t pll_nid; @@ -476,6 +477,14 @@ static void alc_auto_setup_eapd(struct hda_codec *codec, bool on) set_eapd(codec, *p, on); } +static void alc_shutup_pins(struct hda_codec *codec) +{ + struct alc_spec *spec = codec->spec; + + if (!spec->no_shutup_pins) + snd_hda_shutup_pins(codec); +} + /* generic shutup callback; * just turning off EAPD and a little pause for avoiding pop-noise */ @@ -486,7 +495,7 @@ static void alc_eapd_shutup(struct hda_codec *codec) alc_auto_setup_eapd(codec, false); if (!spec->no_depop_delay) msleep(200); - snd_hda_shutup_pins(codec); + alc_shutup_pins(codec); } /* generic EAPD initialization */ @@ -814,7 +823,7 @@ static inline void alc_shutup(struct hda_codec *codec) if (spec && spec->shutup) spec->shutup(codec); else - snd_hda_shutup_pins(codec); + alc_shutup_pins(codec); } static void alc_reboot_notify(struct hda_codec *codec) @@ -2950,7 +2959,7 @@ static void alc269_shutup(struct hda_codec *codec) (alc_get_coef0(codec) & 0x00ff) == 0x018) { msleep(150); } - snd_hda_shutup_pins(codec); + alc_shutup_pins(codec); } static struct coef_fw alc282_coefs[] = { @@ -3053,14 +3062,15 @@ static void alc282_shutup(struct hda_codec *codec) if (hp_pin_sense) msleep(85); - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); if (hp_pin_sense) msleep(100); alc_auto_setup_eapd(codec, false); - snd_hda_shutup_pins(codec); + alc_shutup_pins(codec); alc_write_coef_idx(codec, 0x78, coef78); } @@ -3166,15 +3176,16 @@ static void alc283_shutup(struct hda_codec *codec) if (hp_pin_sense) msleep(100); - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); alc_update_coef_idx(codec, 0x46, 0, 3 << 12); if (hp_pin_sense) msleep(100); alc_auto_setup_eapd(codec, false); - snd_hda_shutup_pins(codec); + alc_shutup_pins(codec); alc_write_coef_idx(codec, 0x43, 0x9614); } @@ -3240,14 +3251,15 @@ static void alc256_shutup(struct hda_codec *codec) /* NOTE: call this before clearing the pin, otherwise codec stalls */ alc_update_coef_idx(codec, 0x46, 0, 3 << 12); - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); if (hp_pin_sense) msleep(100); alc_auto_setup_eapd(codec, false); - snd_hda_shutup_pins(codec); + alc_shutup_pins(codec); } static void alc225_init(struct hda_codec *codec) @@ -3334,7 +3346,7 @@ static void alc225_shutup(struct hda_codec *codec) msleep(100); alc_auto_setup_eapd(codec, false); - snd_hda_shutup_pins(codec); + alc_shutup_pins(codec); } static void alc_default_init(struct hda_codec *codec) @@ -3388,14 +3400,15 @@ static void alc_default_shutup(struct hda_codec *codec) if (hp_pin_sense) msleep(85); - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); if (hp_pin_sense) msleep(100); alc_auto_setup_eapd(codec, false); - snd_hda_shutup_pins(codec); + alc_shutup_pins(codec); } static void alc294_hp_init(struct hda_codec *codec) @@ -3412,8 +3425,9 @@ static void alc294_hp_init(struct hda_codec *codec) msleep(100); - snd_hda_codec_write(codec, hp_pin, 0, - AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); + if (!spec->no_shutup_pins) + snd_hda_codec_write(codec, hp_pin, 0, + AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0); alc_update_coef_idx(codec, 0x6f, 0x000f, 0);/* Set HP depop to manual mode */ alc_update_coefex_idx(codec, 0x58, 0x00, 0x8000, 0x8000); /* HP depop procedure start */ @@ -3433,7 +3447,9 @@ static void alc294_init(struct hda_codec *codec) { struct alc_spec *spec = codec->spec; - if (!spec->done_hp_init) { + /* required only at boot or S4 resume time */ + if (!spec->done_hp_init || + codec->core.dev.power.power_state.event == PM_EVENT_RESTORE) { alc294_hp_init(codec); spec->done_hp_init = true; } @@ -5007,16 +5023,12 @@ static void alc_fixup_auto_mute_via_amp(struct hda_codec *codec, } } -static void alc_no_shutup(struct hda_codec *codec) -{ -} - static void alc_fixup_no_shutup(struct hda_codec *codec, const struct hda_fixup *fix, int action) { if (action == HDA_FIXUP_ACT_PRE_PROBE) { struct alc_spec *spec = codec->spec; - spec->shutup = alc_no_shutup; + spec->no_shutup_pins = 1; } } @@ -6677,6 +6689,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1028, 0x0704, "Dell XPS 13 9350", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE), SND_PCI_QUIRK(0x1028, 0x0706, "Dell Inspiron 7559", ALC256_FIXUP_DELL_INSPIRON_7559_SUBWOOFER), SND_PCI_QUIRK(0x1028, 0x0725, "Dell Inspiron 3162", ALC255_FIXUP_DELL_SPK_NOISE), + SND_PCI_QUIRK(0x1028, 0x0738, "Dell Precision 5820", ALC269_FIXUP_NO_SHUTUP), SND_PCI_QUIRK(0x1028, 0x075b, "Dell XPS 13 9360", ALC256_FIXUP_DELL_XPS_13_HEADPHONE_NOISE), SND_PCI_QUIRK(0x1028, 0x075c, "Dell XPS 27 7760", ALC298_FIXUP_SPK_VOLUME), SND_PCI_QUIRK(0x1028, 0x075d, "Dell AIO", ALC298_FIXUP_SPK_VOLUME), @@ -6771,7 +6784,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x12e0, "ASUS X541SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x13b0, "ASUS Z550SA", ALC256_FIXUP_ASUS_MIC), SND_PCI_QUIRK(0x1043, 0x1427, "Asus Zenbook UX31E", ALC269VB_FIXUP_ASUS_ZENBOOK), - SND_PCI_QUIRK(0x1043, 0x14a1, "ASUS UX533FD", ALC294_FIXUP_ASUS_SPK), SND_PCI_QUIRK(0x1043, 0x1517, "Asus Zenbook UX31A", ALC269VB_FIXUP_ASUS_ZENBOOK_UX31A), SND_PCI_QUIRK(0x1043, 0x16e3, "ASUS UX50", ALC269_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x1043, 0x1a13, "Asus G73Jw", ALC269_FIXUP_ASUS_G73JW), @@ -7391,6 +7403,10 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK, {0x12, 0x90a60130}, {0x17, 0x90170110}, + {0x21, 0x03211020}), + SND_HDA_PIN_QUIRK(0x10ec0294, 0x1043, "ASUS", ALC294_FIXUP_ASUS_SPK, + {0x12, 0x90a60130}, + {0x17, 0x90170110}, {0x21, 0x04211020}), SND_HDA_PIN_QUIRK(0x10ec0295, 0x1028, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE, ALC295_STANDARD_PINS, diff --git a/sound/pci/ice1712/ews.c b/sound/pci/ice1712/ews.c @@ -826,7 +826,12 @@ static int snd_ice1712_6fire_read_pca(struct snd_ice1712 *ice, unsigned char reg snd_i2c_lock(ice->i2c); byte = reg; - snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1); + if (snd_i2c_sendbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1)) { + snd_i2c_unlock(ice->i2c); + dev_err(ice->card->dev, "cannot send pca\n"); + return -EIO; + } + byte = 0; if (snd_i2c_readbytes(spec->i2cdevs[EWS_I2C_6FIRE], &byte, 1) != 1) { snd_i2c_unlock(ice->i2c); diff --git a/sound/pci/ice1712/ice1712.c b/sound/pci/ice1712/ice1712.c @@ -1603,10 +1603,7 @@ static void snd_ice1712_proc_read(struct snd_info_entry *entry, static void snd_ice1712_proc_init(struct snd_ice1712 *ice) { - struct snd_info_entry *entry; - - if (!snd_card_proc_new(ice->card, "ice1712", &entry)) - snd_info_set_text_ops(entry, ice, snd_ice1712_proc_read); + snd_card_ro_proc_new(ice->card, "ice1712", ice, snd_ice1712_proc_read); } /* @@ -2792,9 +2789,6 @@ static int snd_ice1712_suspend(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(ice->pcm); - snd_pcm_suspend_all(ice->pcm_pro); - snd_pcm_suspend_all(ice->pcm_ds); snd_ac97_suspend(ice->ac97); spin_lock_irq(&ice->reg_lock); diff --git a/sound/pci/ice1712/ice1724.c b/sound/pci/ice1712/ice1724.c @@ -1571,10 +1571,7 @@ static void snd_vt1724_proc_read(struct snd_info_entry *entry, static void snd_vt1724_proc_init(struct snd_ice1712 *ice) { - struct snd_info_entry *entry; - - if (!snd_card_proc_new(ice->card, "ice1724", &entry)) - snd_info_set_text_ops(entry, ice, snd_vt1724_proc_read); + snd_card_ro_proc_new(ice->card, "ice1724", ice, snd_vt1724_proc_read); } /* @@ -2804,9 +2801,6 @@ static int snd_vt1724_suspend(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(ice->pcm); - snd_pcm_suspend_all(ice->pcm_pro); - snd_pcm_suspend_all(ice->pcm_ds); snd_ac97_suspend(ice->ac97); spin_lock_irq(&ice->reg_lock); diff --git a/sound/pci/ice1712/pontis.c b/sound/pci/ice1712/pontis.c @@ -659,12 +659,8 @@ static void wm_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buff static void wm_proc_init(struct snd_ice1712 *ice) { - struct snd_info_entry *entry; - if (! snd_card_proc_new(ice->card, "wm_codec", &entry)) { - snd_info_set_text_ops(entry, ice, wm_proc_regs_read); - entry->mode |= 0200; - entry->c.text.write = wm_proc_regs_write; - } + snd_card_rw_proc_new(ice->card, "wm_codec", ice, wm_proc_regs_read, + wm_proc_regs_write); } static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) @@ -684,9 +680,7 @@ static void cs_proc_regs_read(struct snd_info_entry *entry, struct snd_info_buff static void cs_proc_init(struct snd_ice1712 *ice) { - struct snd_info_entry *entry; - if (! snd_card_proc_new(ice->card, "cs_codec", &entry)) - snd_info_set_text_ops(entry, ice, cs_proc_regs_read); + snd_card_ro_proc_new(ice->card, "cs_codec", ice, cs_proc_regs_read); } diff --git a/sound/pci/ice1712/prodigy192.c b/sound/pci/ice1712/prodigy192.c @@ -651,9 +651,8 @@ static void stac9460_proc_regs_read(struct snd_info_entry *entry, static void stac9460_proc_init(struct snd_ice1712 *ice) { - struct snd_info_entry *entry; - if (!snd_card_proc_new(ice->card, "stac9460_codec", &entry)) - snd_info_set_text_ops(entry, ice, stac9460_proc_regs_read); + snd_card_ro_proc_new(ice->card, "stac9460_codec", ice, + stac9460_proc_regs_read); } diff --git a/sound/pci/ice1712/prodigy_hifi.c b/sound/pci/ice1712/prodigy_hifi.c @@ -904,12 +904,8 @@ static void wm_proc_regs_read(struct snd_info_entry *entry, static void wm_proc_init(struct snd_ice1712 *ice) { - struct snd_info_entry *entry; - if (!snd_card_proc_new(ice->card, "wm_codec", &entry)) { - snd_info_set_text_ops(entry, ice, wm_proc_regs_read); - entry->mode |= 0200; - entry->c.text.write = wm_proc_regs_write; - } + snd_card_rw_proc_new(ice->card, "wm_codec", ice, wm_proc_regs_read, + wm_proc_regs_write); } static int prodigy_hifi_add_controls(struct snd_ice1712 *ice) diff --git a/sound/pci/ice1712/quartet.c b/sound/pci/ice1712/quartet.c @@ -502,9 +502,7 @@ static void proc_regs_read(struct snd_info_entry *entry, static void proc_init(struct snd_ice1712 *ice) { - struct snd_info_entry *entry; - if (!snd_card_proc_new(ice->card, "quartet", &entry)) - snd_info_set_text_ops(entry, ice, proc_regs_read); + snd_card_ro_proc_new(ice->card, "quartet", ice, proc_regs_read); } static int qtet_mute_get(struct snd_kcontrol *kcontrol, diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c @@ -2614,8 +2614,6 @@ static int intel8x0_suspend(struct device *dev) int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - for (i = 0; i < chip->pcm_devs; i++) - snd_pcm_suspend_all(chip->pcm[i]); for (i = 0; i < chip->ncodecs; i++) snd_ac97_suspend(chip->ac97[i]); if (chip->device_type == DEVICE_INTEL_ICH4) @@ -2865,10 +2863,8 @@ static void snd_intel8x0_proc_read(struct snd_info_entry * entry, static void snd_intel8x0_proc_init(struct intel8x0 *chip) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(chip->card, "intel8x0", &entry)) - snd_info_set_text_ops(entry, chip, snd_intel8x0_proc_read); + snd_card_ro_proc_new(chip->card, "intel8x0", chip, + snd_intel8x0_proc_read); } static int snd_intel8x0_dev_free(struct snd_device *device) diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c @@ -1025,11 +1025,8 @@ static int intel8x0m_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct intel8x0m *chip = card->private_data; - int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - for (i = 0; i < chip->pcm_devs; i++) - snd_pcm_suspend_all(chip->pcm[i]); snd_ac97_suspend(chip->ac97); if (chip->irq >= 0) { free_irq(chip->irq, chip); @@ -1087,10 +1084,8 @@ static void snd_intel8x0m_proc_read(struct snd_info_entry * entry, static void snd_intel8x0m_proc_init(struct intel8x0m *chip) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(chip->card, "intel8x0m", &entry)) - snd_info_set_text_ops(entry, chip, snd_intel8x0m_proc_read); + snd_card_ro_proc_new(chip->card, "intel8x0m", chip, + snd_intel8x0m_proc_read); } static int snd_intel8x0m_dev_free(struct snd_device *device) diff --git a/sound/pci/korg1212/korg1212.c b/sound/pci/korg1212/korg1212.c @@ -2090,10 +2090,8 @@ static void snd_korg1212_proc_read(struct snd_info_entry *entry, static void snd_korg1212_proc_init(struct snd_korg1212 *korg1212) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(korg1212->card, "korg1212", &entry)) - snd_info_set_text_ops(entry, korg1212, snd_korg1212_proc_read); + snd_card_ro_proc_new(korg1212->card, "korg1212", korg1212, + snd_korg1212_proc_read); } static int diff --git a/sound/pci/lola/lola_proc.c b/sound/pci/lola/lola_proc.c @@ -208,15 +208,9 @@ static void lola_proc_regs_read(struct snd_info_entry *entry, void lola_proc_debug_new(struct lola *chip) { - struct snd_info_entry *entry; - - if (!snd_card_proc_new(chip->card, "codec", &entry)) - snd_info_set_text_ops(entry, chip, lola_proc_codec_read); - if (!snd_card_proc_new(chip->card, "codec_rw", &entry)) { - snd_info_set_text_ops(entry, chip, lola_proc_codec_rw_read); - entry->mode |= 0200; - entry->c.text.write = lola_proc_codec_rw_write; - } - if (!snd_card_proc_new(chip->card, "regs", &entry)) - snd_info_set_text_ops(entry, chip, lola_proc_regs_read); + snd_card_ro_proc_new(chip->card, "codec", chip, lola_proc_codec_read); + snd_card_rw_proc_new(chip->card, "codec_rw", chip, + lola_proc_codec_rw_read, + lola_proc_codec_rw_write); + snd_card_ro_proc_new(chip->card, "regs", chip, lola_proc_regs_read); } diff --git a/sound/pci/lx6464es/lx6464es.c b/sound/pci/lx6464es/lx6464es.c @@ -854,11 +854,9 @@ static int lx_pcm_create(struct lx6464es *chip) pcm->nonatomic = true; strcpy(pcm->name, card_name); - err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, - snd_dma_pci_data(chip->pci), - size, size); - if (err < 0) - return err; + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_dma_pci_data(chip->pci), + size, size); chip->pcm = pcm; chip->capture_stream.is_capture = 1; @@ -948,13 +946,7 @@ static void lx_proc_levels_read(struct snd_info_entry *entry, static int lx_proc_create(struct snd_card *card, struct lx6464es *chip) { - struct snd_info_entry *entry; - int err = snd_card_proc_new(card, "levels", &entry); - if (err < 0) - return err; - - snd_info_set_text_ops(entry, chip, lx_proc_levels_read); - return 0; + return snd_card_ro_proc_new(card, "levels", chip, lx_proc_levels_read); } diff --git a/sound/pci/maestro3.c b/sound/pci/maestro3.c @@ -2422,7 +2422,6 @@ static int m3_suspend(struct device *dev) chip->in_suspend = 1; cancel_work_sync(&chip->hwvol_work); snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); msleep(10); /* give the assp a chance to idle.. */ diff --git a/sound/pci/mixart/mixart.c b/sound/pci/mixart/mixart.c @@ -1220,10 +1220,8 @@ static void snd_mixart_proc_init(struct snd_mixart *chip) struct snd_info_entry *entry; /* text interface to read perf and temp meters */ - if (! snd_card_proc_new(chip->card, "board_info", &entry)) { - entry->private_data = chip; - entry->c.text.read = snd_mixart_proc_read; - } + snd_card_ro_proc_new(chip->card, "board_info", chip, + snd_mixart_proc_read); if (! snd_card_proc_new(chip->card, "mixart_BA0", &entry)) { entry->content = SNDRV_INFO_CONTENT_DATA; diff --git a/sound/pci/nm256/nm256.c b/sound/pci/nm256/nm256.c @@ -1413,7 +1413,6 @@ static int nm256_suspend(struct device *dev) struct nm256 *chip = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); chip->coeffs_current = 0; return 0; diff --git a/sound/pci/oxygen/oxygen_lib.c b/sound/pci/oxygen/oxygen_lib.c @@ -244,10 +244,7 @@ static void oxygen_proc_read(struct snd_info_entry *entry, static void oxygen_proc_init(struct oxygen *chip) { - struct snd_info_entry *entry; - - if (!snd_card_proc_new(chip->card, "oxygen", &entry)) - snd_info_set_text_ops(entry, chip, oxygen_proc_read); + snd_card_ro_proc_new(chip->card, "oxygen", chip, oxygen_proc_read); } static const struct pci_device_id * @@ -373,7 +370,7 @@ static void oxygen_init(struct oxygen *chip) for (i = 0; i < 8; ++i) chip->dac_volume[i] = chip->model.dac_volume_min; chip->dac_mute = 1; - chip->spdif_playback_enable = 1; + chip->spdif_playback_enable = 0; chip->spdif_bits = OXYGEN_SPDIF_C | OXYGEN_SPDIF_ORIGINAL | (IEC958_AES1_CON_PCM_CODER << OXYGEN_SPDIF_CATEGORY_SHIFT); chip->spdif_pcm_bits = chip->spdif_bits; @@ -744,13 +741,10 @@ static int oxygen_pci_suspend(struct device *dev) { struct snd_card *card = dev_get_drvdata(dev); struct oxygen *chip = card->private_data; - unsigned int i, saved_interrupt_mask; + unsigned int saved_interrupt_mask; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - for (i = 0; i < PCM_COUNT; ++i) - snd_pcm_suspend(chip->streams[i]); - if (chip->model.suspend) chip->model.suspend(chip); diff --git a/sound/pci/oxygen/pcm1796.h b/sound/pci/oxygen/pcm1796.h @@ -10,7 +10,6 @@ #define PCM1796_MUTE 0x01 #define PCM1796_DME 0x02 #define PCM1796_DMF_MASK 0x0c -#define PCM1796_DMF_DISABLED 0x00 #define PCM1796_DMF_48 0x04 #define PCM1796_DMF_441 0x08 #define PCM1796_DMF_32 0x0c diff --git a/sound/pci/oxygen/xonar_pcm179x.c b/sound/pci/oxygen/xonar_pcm179x.c @@ -331,7 +331,7 @@ static void pcm1796_init(struct oxygen *chip) struct xonar_pcm179x *data = chip->model_data; data->pcm1796_regs[0][18 - PCM1796_REG_BASE] = - PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD; + PCM1796_FMT_24_I2S | PCM1796_ATLD; if (!data->broken_i2c) data->pcm1796_regs[0][18 - PCM1796_REG_BASE] |= PCM1796_MUTE; data->pcm1796_regs[0][19 - PCM1796_REG_BASE] = @@ -621,6 +621,23 @@ static void update_pcm1796_oversampling(struct oxygen *chip) pcm1796_write_cached(chip, i, 20, reg); } +static void update_pcm1796_deemph(struct oxygen *chip) +{ + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + u8 reg; + + reg = data->pcm1796_regs[0][18 - PCM1796_REG_BASE] & ~PCM1796_DMF_MASK; + if (data->current_rate == 48000) + reg |= PCM1796_DMF_48; + else if (data->current_rate == 44100) + reg |= PCM1796_DMF_441; + else if (data->current_rate == 32000) + reg |= PCM1796_DMF_32; + for (i = 0; i < data->dacs; ++i) + pcm1796_write_cached(chip, i, 18, reg); +} + static void set_pcm1796_params(struct oxygen *chip, struct snd_pcm_hw_params *params) { @@ -629,6 +646,7 @@ static void set_pcm1796_params(struct oxygen *chip, msleep(1); data->current_rate = params_rate(params); update_pcm1796_oversampling(chip); + update_pcm1796_deemph(chip); } static void update_pcm1796_volume(struct oxygen *chip) @@ -653,9 +671,11 @@ static void update_pcm1796_mute(struct oxygen *chip) unsigned int i; u8 value; - value = PCM1796_DMF_DISABLED | PCM1796_FMT_24_I2S | PCM1796_ATLD; + value = data->pcm1796_regs[0][18 - PCM1796_REG_BASE]; if (chip->dac_mute) value |= PCM1796_MUTE; + else + value &= ~PCM1796_MUTE; for (i = 0; i < data->dacs; ++i) pcm1796_write_cached(chip, i, 18, value); } @@ -777,6 +797,49 @@ static const struct snd_kcontrol_new rolloff_control = { .put = rolloff_put, }; +static int deemph_get(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + + value->value.integer.value[0] = + !!(data->pcm1796_regs[0][18 - PCM1796_REG_BASE] & PCM1796_DME); + return 0; +} + +static int deemph_put(struct snd_kcontrol *ctl, + struct snd_ctl_elem_value *value) +{ + struct oxygen *chip = ctl->private_data; + struct xonar_pcm179x *data = chip->model_data; + unsigned int i; + int changed; + u8 reg; + + mutex_lock(&chip->mutex); + reg = data->pcm1796_regs[0][18 - PCM1796_REG_BASE]; + if (!value->value.integer.value[0]) + reg &= ~PCM1796_DME; + else + reg |= PCM1796_DME; + changed = reg != data->pcm1796_regs[0][18 - PCM1796_REG_BASE]; + if (changed) { + for (i = 0; i < data->dacs; ++i) + pcm1796_write(chip, i, 18, reg); + } + mutex_unlock(&chip->mutex); + return changed; +} + +static const struct snd_kcontrol_new deemph_control = { + .iface = SNDRV_CTL_ELEM_IFACE_MIXER, + .name = "De-emphasis Playback Switch", + .info = snd_ctl_boolean_mono_info, + .get = deemph_get, + .put = deemph_put, +}; + static const struct snd_kcontrol_new hdav_hdmi_control = { .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = "HDMI Playback Switch", @@ -1011,6 +1074,10 @@ static int add_pcm1796_controls(struct oxygen *chip) snd_ctl_new1(&rolloff_control, chip)); if (err < 0) return err; + err = snd_ctl_add(chip->card, + snd_ctl_new1(&deemph_control, chip)); + if (err < 0) + return err; } return 0; } diff --git a/sound/pci/pcxhr/pcxhr.c b/sound/pci/pcxhr/pcxhr.c @@ -1454,21 +1454,14 @@ static void pcxhr_proc_ltc(struct snd_info_entry *entry, static void pcxhr_proc_init(struct snd_pcxhr *chip) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(chip->card, "info", &entry)) - snd_info_set_text_ops(entry, chip, pcxhr_proc_info); - if (! snd_card_proc_new(chip->card, "sync", &entry)) - snd_info_set_text_ops(entry, chip, pcxhr_proc_sync); + snd_card_ro_proc_new(chip->card, "info", chip, pcxhr_proc_info); + snd_card_ro_proc_new(chip->card, "sync", chip, pcxhr_proc_sync); /* gpio available on stereo sound cards only */ - if (chip->mgr->is_hr_stereo && - !snd_card_proc_new(chip->card, "gpio", &entry)) { - snd_info_set_text_ops(entry, chip, pcxhr_proc_gpio_read); - entry->c.text.write = pcxhr_proc_gpo_write; - entry->mode |= 0200; - } - if (!snd_card_proc_new(chip->card, "ltc", &entry)) - snd_info_set_text_ops(entry, chip, pcxhr_proc_ltc); + if (chip->mgr->is_hr_stereo) + snd_card_rw_proc_new(chip->card, "gpio", chip, + pcxhr_proc_gpio_read, + pcxhr_proc_gpo_write); + snd_card_ro_proc_new(chip->card, "ltc", chip, pcxhr_proc_ltc); } /* end of proc interface */ diff --git a/sound/pci/riptide/riptide.c b/sound/pci/riptide/riptide.c @@ -1158,7 +1158,6 @@ static int riptide_suspend(struct device *dev) chip->in_suspend = 1; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); snd_ac97_suspend(chip->ac97); return 0; } @@ -1974,10 +1973,8 @@ snd_riptide_proc_read(struct snd_info_entry *entry, static void snd_riptide_proc_init(struct snd_riptide *chip) { - struct snd_info_entry *entry; - - if (!snd_card_proc_new(chip->card, "riptide", &entry)) - snd_info_set_text_ops(entry, chip, snd_riptide_proc_read); + snd_card_ro_proc_new(chip->card, "riptide", chip, + snd_riptide_proc_read); } static int snd_riptide_mixer(struct snd_riptide *chip) diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c @@ -1568,10 +1568,7 @@ snd_rme32_proc_read(struct snd_info_entry * entry, struct snd_info_buffer *buffe static void snd_rme32_proc_init(struct rme32 *rme32) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(rme32->card, "rme32", &entry)) - snd_info_set_text_ops(entry, rme32, snd_rme32_proc_read); + snd_card_ro_proc_new(rme32->card, "rme32", rme32, snd_rme32_proc_read); } /* diff --git a/sound/pci/rme96.c b/sound/pci/rme96.c @@ -1868,10 +1868,7 @@ snd_rme96_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer static void snd_rme96_proc_init(struct rme96 *rme96) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(rme96->card, "rme96", &entry)) - snd_info_set_text_ops(entry, rme96, snd_rme96_proc_read); + snd_card_ro_proc_new(rme96->card, "rme96", rme96, snd_rme96_proc_read); } /* @@ -2388,8 +2385,6 @@ static int rme96_suspend(struct device *dev) struct rme96 *rme96 = card->private_data; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend(rme96->playback_substream); - snd_pcm_suspend(rme96->capture_substream); /* save capture & playback pointers */ rme96->playback_pointer = readl(rme96->iobase + RME96_IO_GET_PLAY_POS) diff --git a/sound/pci/rme9652/hdsp.c b/sound/pci/rme9652/hdsp.c @@ -3708,10 +3708,7 @@ snd_hdsp_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buffer) static void snd_hdsp_proc_init(struct hdsp *hdsp) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(hdsp->card, "hdsp", &entry)) - snd_info_set_text_ops(entry, hdsp, snd_hdsp_proc_read); + snd_card_ro_proc_new(hdsp->card, "hdsp", hdsp, snd_hdsp_proc_read); } static void snd_hdsp_free_buffers(struct hdsp *hdsp) diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c @@ -5287,44 +5287,35 @@ static void snd_hdspm_proc_ports_out(struct snd_info_entry *entry, static void snd_hdspm_proc_init(struct hdspm *hdspm) { - struct snd_info_entry *entry; + void (*read)(struct snd_info_entry *, struct snd_info_buffer *) = NULL; - if (!snd_card_proc_new(hdspm->card, "hdspm", &entry)) { - switch (hdspm->io_type) { - case AES32: - snd_info_set_text_ops(entry, hdspm, - snd_hdspm_proc_read_aes32); - break; - case MADI: - snd_info_set_text_ops(entry, hdspm, - snd_hdspm_proc_read_madi); - break; - case MADIface: - /* snd_info_set_text_ops(entry, hdspm, - snd_hdspm_proc_read_madiface); */ - break; - case RayDAT: - snd_info_set_text_ops(entry, hdspm, - snd_hdspm_proc_read_raydat); - break; - case AIO: - break; - } - } - - if (!snd_card_proc_new(hdspm->card, "ports.in", &entry)) { - snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_in); + switch (hdspm->io_type) { + case AES32: + read = snd_hdspm_proc_read_aes32; + break; + case MADI: + read = snd_hdspm_proc_read_madi; + break; + case MADIface: + /* read = snd_hdspm_proc_read_madiface; */ + break; + case RayDAT: + read = snd_hdspm_proc_read_raydat; + break; + case AIO: + break; } - if (!snd_card_proc_new(hdspm->card, "ports.out", &entry)) { - snd_info_set_text_ops(entry, hdspm, snd_hdspm_proc_ports_out); - } + snd_card_ro_proc_new(hdspm->card, "hdspm", hdspm, read); + snd_card_ro_proc_new(hdspm->card, "ports.in", hdspm, + snd_hdspm_proc_ports_in); + snd_card_ro_proc_new(hdspm->card, "ports.out", hdspm, + snd_hdspm_proc_ports_out); #ifdef CONFIG_SND_DEBUG /* debug file to read all hdspm registers */ - if (!snd_card_proc_new(hdspm->card, "debug", &entry)) - snd_info_set_text_ops(entry, hdspm, - snd_hdspm_proc_read_debug); + snd_card_ro_proc_new(hdspm->card, "debug", hdspm, + snd_hdspm_proc_read_debug); #endif } @@ -6411,7 +6402,6 @@ static int snd_hdspm_create_hwdep(struct snd_card *card, ------------------------------------------------------------*/ static int snd_hdspm_preallocate_memory(struct hdspm *hdspm) { - int err; struct snd_pcm *pcm; size_t wanted; @@ -6419,21 +6409,10 @@ static int snd_hdspm_preallocate_memory(struct hdspm *hdspm) wanted = HDSPM_DMA_AREA_BYTES; - err = - snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_DEV_SG, - snd_dma_pci_data(hdspm->pci), - wanted, - wanted); - if (err < 0) { - dev_dbg(hdspm->card->dev, - "Could not preallocate %zd Bytes\n", wanted); - - return err; - } else - dev_dbg(hdspm->card->dev, - " Preallocated %zd Bytes\n", wanted); - + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(hdspm->pci), + wanted, wanted); + dev_dbg(hdspm->card->dev, " Preallocated %zd Bytes\n", wanted); return 0; } diff --git a/sound/pci/rme9652/rme9652.c b/sound/pci/rme9652/rme9652.c @@ -1737,10 +1737,8 @@ snd_rme9652_proc_read(struct snd_info_entry *entry, struct snd_info_buffer *buff static void snd_rme9652_proc_init(struct snd_rme9652 *rme9652) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(rme9652->card, "rme9652", &entry)) - snd_info_set_text_ops(entry, rme9652, snd_rme9652_proc_read); + snd_card_ro_proc_new(rme9652->card, "rme9652", rme9652, + snd_rme9652_proc_read); } static void snd_rme9652_free_buffers(struct snd_rme9652 *rme9652) diff --git a/sound/pci/sis7019.c b/sound/pci/sis7019.c @@ -1214,7 +1214,6 @@ static int sis_suspend(struct device *dev) int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(sis->pcm); if (sis->codecs_present & SIS_PRIMARY_CODEC_PRESENT) snd_ac97_suspend(sis->ac97[0]); if (sis->codecs_present & SIS_SECONDARY_CODEC_PRESENT) diff --git a/sound/pci/sonicvibes.c b/sound/pci/sonicvibes.c @@ -1171,10 +1171,8 @@ static void snd_sonicvibes_proc_read(struct snd_info_entry *entry, static void snd_sonicvibes_proc_init(struct sonicvibes *sonic) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(sonic->card, "sonicvibes", &entry)) - snd_info_set_text_ops(entry, sonic, snd_sonicvibes_proc_read); + snd_card_ro_proc_new(sonic->card, "sonicvibes", sonic, + snd_sonicvibes_proc_read); } /* diff --git a/sound/pci/trident/trident_main.c b/sound/pci/trident/trident_main.c @@ -3320,13 +3320,11 @@ static void snd_trident_proc_read(struct snd_info_entry *entry, static void snd_trident_proc_init(struct snd_trident *trident) { - struct snd_info_entry *entry; const char *s = "trident"; if (trident->device == TRIDENT_DEVICE_ID_SI7018) s = "sis7018"; - if (! snd_card_proc_new(trident->card, s, &entry)) - snd_info_set_text_ops(entry, trident, snd_trident_proc_read); + snd_card_ro_proc_new(trident->card, s, trident, snd_trident_proc_read); } static int snd_trident_dev_free(struct snd_device *device) @@ -3915,10 +3913,6 @@ static int snd_trident_suspend(struct device *dev) trident->in_suspend = 1; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(trident->pcm); - snd_pcm_suspend_all(trident->foldback); - snd_pcm_suspend_all(trident->spdif); - snd_ac97_suspend(trident->ac97); snd_ac97_suspend(trident->ac97_sec); return 0; diff --git a/sound/pci/via82xx.c b/sound/pci/via82xx.c @@ -2144,10 +2144,8 @@ static void snd_via82xx_proc_read(struct snd_info_entry *entry, static void snd_via82xx_proc_init(struct via82xx *chip) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(chip->card, "via82xx", &entry)) - snd_info_set_text_ops(entry, chip, snd_via82xx_proc_read); + snd_card_ro_proc_new(chip->card, "via82xx", chip, + snd_via82xx_proc_read); } /* @@ -2278,8 +2276,6 @@ static int snd_via82xx_suspend(struct device *dev) int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - for (i = 0; i < 2; i++) - snd_pcm_suspend_all(chip->pcms[i]); for (i = 0; i < chip->num_devs; i++) snd_via82xx_channel_reset(chip, &chip->devs[i]); synchronize_irq(chip->irq); diff --git a/sound/pci/via82xx_modem.c b/sound/pci/via82xx_modem.c @@ -865,11 +865,9 @@ static int snd_via686_pcm_new(struct via82xx_modem *chip) init_viadev(chip, 0, VIA_REG_MO_STATUS, 0); init_viadev(chip, 1, VIA_REG_MI_STATUS, 1); - if ((err = snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, - snd_dma_pci_data(chip->pci), - 64*1024, 128*1024)) < 0) - return err; - + snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + snd_dma_pci_data(chip->pci), + 64*1024, 128*1024); return 0; } @@ -937,10 +935,8 @@ static void snd_via82xx_proc_read(struct snd_info_entry *entry, struct snd_info_ static void snd_via82xx_proc_init(struct via82xx_modem *chip) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(chip->card, "via82xx", &entry)) - snd_info_set_text_ops(entry, chip, snd_via82xx_proc_read); + snd_card_ro_proc_new(chip->card, "via82xx", chip, + snd_via82xx_proc_read); } /* @@ -1038,8 +1034,6 @@ static int snd_via82xx_suspend(struct device *dev) int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - for (i = 0; i < 2; i++) - snd_pcm_suspend_all(chip->pcms[i]); for (i = 0; i < chip->num_devs; i++) snd_via82xx_channel_reset(chip, &chip->devs[i]); synchronize_irq(chip->irq); diff --git a/sound/pci/ymfpci/ymfpci_main.c b/sound/pci/ymfpci/ymfpci_main.c @@ -1985,11 +1985,7 @@ static void snd_ymfpci_proc_read(struct snd_info_entry *entry, static int snd_ymfpci_proc_init(struct snd_card *card, struct snd_ymfpci *chip) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(card, "ymfpci", &entry)) - snd_info_set_text_ops(entry, chip, snd_ymfpci_proc_read); - return 0; + return snd_card_ro_proc_new(card, "ymfpci", chip, snd_ymfpci_proc_read); } /* @@ -2304,10 +2300,6 @@ static int snd_ymfpci_suspend(struct device *dev) unsigned int i; snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); - snd_pcm_suspend_all(chip->pcm2); - snd_pcm_suspend_all(chip->pcm_spdif); - snd_pcm_suspend_all(chip->pcm_4ch); snd_ac97_suspend(chip->ac97); for (i = 0; i < YDSXGR_NUM_SAVED_REGS; i++) chip->saved_regs[i] = snd_ymfpci_readl(chip, saved_regs_index[i]); diff --git a/sound/pcmcia/pdaudiocf/pdaudiocf_core.c b/sound/pcmcia/pdaudiocf/pdaudiocf_core.c @@ -148,10 +148,7 @@ static void pdacf_proc_read(struct snd_info_entry * entry, static void pdacf_proc_init(struct snd_pdacf *chip) { - struct snd_info_entry *entry; - - if (! snd_card_proc_new(chip->card, "pdaudiocf", &entry)) - snd_info_set_text_ops(entry, chip, pdacf_proc_read); + snd_card_ro_proc_new(chip->card, "pdaudiocf", chip, pdacf_proc_read); } struct snd_pdacf *snd_pdacf_create(struct snd_card *card) @@ -265,7 +262,6 @@ int snd_pdacf_suspend(struct snd_pdacf *chip) u16 val; snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); - snd_pcm_suspend_all(chip->pcm); /* disable interrupts, but use direct write to preserve old register value in chip->regmap */ val = inw(chip->port + PDAUDIOCF_REG_IER); val &= ~(PDAUDIOCF_IRQOVREN|PDAUDIOCF_IRQAKMEN|PDAUDIOCF_IRQLVLEN0|PDAUDIOCF_IRQLVLEN1); diff --git a/sound/ppc/pmac.c b/sound/ppc/pmac.c @@ -1365,7 +1365,6 @@ void snd_pmac_suspend(struct snd_pmac *chip) snd_power_change_state(chip->card, SNDRV_CTL_POWER_D3hot); if (chip->suspend) chip->suspend(chip); - snd_pcm_suspend_all(chip->pcm); spin_lock_irqsave(&chip->reg_lock, flags); snd_pmac_beep_stop(chip); spin_unlock_irqrestore(&chip->reg_lock, flags); diff --git a/sound/ppc/snd_ps3.c b/sound/ppc/snd_ps3.c @@ -1024,15 +1024,11 @@ static int snd_ps3_driver_probe(struct ps3_system_bus_device *dev) the_card.pcm->info_flags = SNDRV_PCM_INFO_NONINTERLEAVED; /* pre-alloc PCM DMA buffer*/ - ret = snd_pcm_lib_preallocate_pages_for_all(the_card.pcm, + snd_pcm_lib_preallocate_pages_for_all(the_card.pcm, SNDRV_DMA_TYPE_DEV, &dev->core, SND_PS3_PCM_PREALLOC_SIZE, SND_PS3_PCM_PREALLOC_SIZE); - if (ret < 0) { - pr_info("%s: prealloc failed\n", __func__); - goto clean_card; - } /* * allocate null buffer diff --git a/sound/ppc/tumbler.c b/sound/ppc/tumbler.c @@ -1371,6 +1371,7 @@ int snd_pmac_tumbler_init(struct snd_pmac *chip) mix->anded_reset = 1; if (of_get_property(np, "layout-id", NULL)) mix->reset_on_sleep = 0; + of_node_put(np); break; } } diff --git a/sound/sh/aica.c b/sound/sh/aica.c @@ -464,14 +464,12 @@ static int __init snd_aicapcmchip(struct snd_card_aica snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &snd_aicapcm_playback_ops); /* Allocate the DMA buffers */ - err = - snd_pcm_lib_preallocate_pages_for_all(pcm, - SNDRV_DMA_TYPE_CONTINUOUS, - snd_dma_continuous_data - (GFP_KERNEL), - AICA_BUFFER_SIZE, - AICA_BUFFER_SIZE); - return err; + snd_pcm_lib_preallocate_pages_for_all(pcm, + SNDRV_DMA_TYPE_CONTINUOUS, + snd_dma_continuous_data(GFP_KERNEL), + AICA_BUFFER_SIZE, + AICA_BUFFER_SIZE); + return 0; } /* Mixer controls */ diff --git a/sound/soc/Kconfig b/sound/soc/Kconfig @@ -64,6 +64,7 @@ source "sound/soc/samsung/Kconfig" source "sound/soc/sh/Kconfig" source "sound/soc/sirf/Kconfig" source "sound/soc/spear/Kconfig" +source "sound/soc/sprd/Kconfig" source "sound/soc/sti/Kconfig" source "sound/soc/stm/Kconfig" source "sound/soc/sunxi/Kconfig" diff --git a/sound/soc/Makefile b/sound/soc/Makefile @@ -48,6 +48,7 @@ obj-$(CONFIG_SND_SOC) += samsung/ obj-$(CONFIG_SND_SOC) += sh/ obj-$(CONFIG_SND_SOC) += sirf/ obj-$(CONFIG_SND_SOC) += spear/ +obj-$(CONFIG_SND_SOC) += sprd/ obj-$(CONFIG_SND_SOC) += sti/ obj-$(CONFIG_SND_SOC) += stm/ obj-$(CONFIG_SND_SOC) += sunxi/ diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c @@ -1142,7 +1142,6 @@ static int acp_dma_trigger(struct snd_pcm_substream *substream, int cmd) static int acp_dma_new(struct snd_soc_pcm_runtime *rtd) { - int ret; struct snd_soc_component *component = snd_soc_rtdcom_lookup(rtd, DRV_NAME); struct audio_drv_data *adata = dev_get_drvdata(component->dev); @@ -1150,24 +1149,21 @@ static int acp_dma_new(struct snd_soc_pcm_runtime *rtd) switch (adata->asic_type) { case CHIP_STONEY: - ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, - SNDRV_DMA_TYPE_DEV, - parent, - ST_MIN_BUFFER, - ST_MAX_BUFFER); + snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, + parent, + ST_MIN_BUFFER, + ST_MAX_BUFFER); break; default: - ret = snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, - SNDRV_DMA_TYPE_DEV, - parent, - MIN_BUFFER, - MAX_BUFFER); + snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, + SNDRV_DMA_TYPE_DEV, + parent, + MIN_BUFFER, + MAX_BUFFER); break; } - if (ret < 0) - dev_err(component->dev, - "buffer preallocation failure error:%d\n", ret); - return ret; + return 0; } static int acp_dma_close(struct snd_pcm_substream *substream) diff --git a/sound/soc/amd/raven/acp3x-pcm-dma.c b/sound/soc/amd/raven/acp3x-pcm-dma.c @@ -367,10 +367,10 @@ static snd_pcm_uframes_t acp3x_dma_pointer(struct snd_pcm_substream *substream) static int acp3x_dma_new(struct snd_soc_pcm_runtime *rtd) { - return snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, - SNDRV_DMA_TYPE_DEV, - NULL, MIN_BUFFER, - MAX_BUFFER); + snd_pcm_lib_preallocate_pages_for_all(rtd->pcm, SNDRV_DMA_TYPE_DEV, + rtd->pcm->card->dev, + MIN_BUFFER, MAX_BUFFER); + return 0; } static int acp3x_dma_hw_free(struct snd_pcm_substream *substream) diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig @@ -50,10 +50,12 @@ config SND_SOC_ALL_CODECS select SND_SOC_BT_SCO select SND_SOC_BD28623 select SND_SOC_CQ0093VC + select SND_SOC_CROS_EC_CODEC if MFD_CROS_EC select SND_SOC_CS35L32 if I2C select SND_SOC_CS35L33 if I2C select SND_SOC_CS35L34 if I2C select SND_SOC_CS35L35 if I2C + select SND_SOC_CS35L36 if I2C select SND_SOC_CS42L42 if I2C select SND_SOC_CS42L51_I2C if I2C select SND_SOC_CS42L52 if I2C && INPUT @@ -65,6 +67,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_CS4271_SPI if SPI_MASTER select SND_SOC_CS42XX8_I2C if I2C select SND_SOC_CS43130 if I2C + select SND_SOC_CS4341 if SND_SOC_I2C_AND_SPI select SND_SOC_CS4349 if I2C select SND_SOC_CS47L24 if MFD_CS47L24 select SND_SOC_CS53L30 if I2C @@ -88,6 +91,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_INNO_RK3036 select SND_SOC_ISABELLE if I2C select SND_SOC_JZ4740_CODEC + select SND_SOC_JZ4725B_CODEC select SND_SOC_LM4857 if I2C select SND_SOC_LM49453 if I2C select SND_SOC_MAX98088 if I2C @@ -109,6 +113,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MC13783 if MFD_MC13XXX select SND_SOC_ML26124 if I2C select SND_SOC_MT6351 if MTK_PMIC_WRAP + select SND_SOC_MT6358 if MTK_PMIC_WRAP select SND_SOC_NAU8540 if I2C select SND_SOC_NAU8810 if I2C select SND_SOC_NAU8822 if I2C @@ -129,6 +134,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM5102A select SND_SOC_PCM512x_I2C if I2C select SND_SOC_PCM512x_SPI if SPI_MASTER + select SND_SOC_RK3328 select SND_SOC_RT274 if I2C select SND_SOC_RT286 if I2C select SND_SOC_RT298 if I2C @@ -185,6 +191,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_TWL6040 if TWL6040_CORE select SND_SOC_UDA134X select SND_SOC_UDA1380 if I2C + select SND_SOC_WCD9335 if SLIMBUS select SND_SOC_WL1273 if MFD_WL1273_CORE select SND_SOC_WM0010 if SPI_MASTER select SND_SOC_WM1250_EV1 if I2C @@ -455,6 +462,13 @@ config SND_SOC_CPCAP config SND_SOC_CQ0093VC tristate +config SND_SOC_CROS_EC_CODEC + tristate "codec driver for ChromeOS EC" + depends on MFD_CROS_EC + help + If you say yes here you will get support for the + ChromeOS Embedded Controller's Audio Codec. + config SND_SOC_CS35L32 tristate "Cirrus Logic CS35L32 CODEC" depends on I2C @@ -471,6 +485,10 @@ config SND_SOC_CS35L35 tristate "Cirrus Logic CS35L35 CODEC" depends on I2C +config SND_SOC_CS35L36 + tristate "Cirrus Logic CS35L36 CODEC" + depends on I2C + config SND_SOC_CS42L42 tristate "Cirrus Logic CS42L42 CODEC" depends on I2C @@ -542,6 +560,12 @@ config SND_SOC_CS43130 tristate "Cirrus Logic CS43130 CODEC" depends on I2C +config SND_SOC_CS4341 + tristate "Cirrus Logic CS4341 CODEC" + depends on SND_SOC_I2C_AND_SPI + select REGMAP_I2C if I2C + select REGMAP_SPI if SPI_MASTER + # Cirrus Logic CS4349 HiFi DAC config SND_SOC_CS4349 tristate "Cirrus Logic CS4349 CODEC" @@ -560,8 +584,26 @@ config SND_SOC_CX20442 depends on TTY config SND_SOC_JZ4740_CODEC + depends on MIPS || COMPILE_TEST select REGMAP_MMIO - tristate + tristate "Ingenic JZ4740 internal CODEC" + help + Enable support for the internal CODEC found in the JZ4740 SoC + from Ingenic. + + This driver can also be built as a module. If so, the module + will be called snd-soc-jz4740-codec. + +config SND_SOC_JZ4725B_CODEC + depends on MIPS || COMPILE_TEST + select REGMAP + tristate "Ingenic JZ4725B internal CODEC" + help + Enable support for the internal CODEC found in the JZ4725B SoC + from Ingenic. + + This driver can also be built as a module. If so, the module + will be called snd-soc-jz4725b-codec. config SND_SOC_L3 tristate @@ -698,6 +740,7 @@ config SND_SOC_MSM8916_WCD_ANALOG config SND_SOC_MSM8916_WCD_DIGITAL tristate "Qualcomm MSM8916 WCD DIGITAL Codec" + select REGMAP_MMIO config SND_SOC_PCM1681 tristate "Texas Instruments PCM1681 CODEC" @@ -799,6 +842,10 @@ config SND_SOC_PCM512x_SPI select SND_SOC_PCM512x select REGMAP_SPI +config SND_SOC_RK3328 + tristate "Rockchip RK3328 audio CODEC" + select REGMAP_MMIO + config SND_SOC_RL6231 tristate default y if SND_SOC_RT5514=y @@ -1100,6 +1147,15 @@ config SND_SOC_UDA1380 tristate depends on I2C +config SND_SOC_WCD9335 + tristate "WCD9335 Codec" + depends on SLIMBUS + select REGMAP_SLIMBUS + help + The WCD9335 is a standalone Hi-Fi audio CODEC IC, supports + Qualcomm Technologies, Inc. (QTI) multimedia solutions, + including the MSM8996, MSM8976, and MSM8956 chipsets. + config SND_SOC_WL1273 tristate @@ -1211,7 +1267,8 @@ config SND_SOC_WM8903 depends on I2C config SND_SOC_WM8904 - tristate + tristate "Wolfson Microelectronics WM8904 CODEC" + depends on I2C config SND_SOC_WM8940 tristate @@ -1325,6 +1382,12 @@ config SND_SOC_ML26124 config SND_SOC_MT6351 tristate "MediaTek MT6351 Codec" +config SND_SOC_MT6358 + tristate "MediaTek MT6358 Codec" + help + Enable support for the platform which uses MT6358 as + external codec device. + config SND_SOC_NAU8540 tristate "Nuvoton Technology Corporation NAU85L40 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile @@ -42,10 +42,12 @@ snd-soc-bd28623-objs := bd28623.o snd-soc-bt-sco-objs := bt-sco.o snd-soc-cpcap-objs := cpcap.o snd-soc-cq93vc-objs := cq93vc.o +snd-soc-cros-ec-codec-objs := cros_ec_codec.o snd-soc-cs35l32-objs := cs35l32.o snd-soc-cs35l33-objs := cs35l33.o snd-soc-cs35l34-objs := cs35l34.o snd-soc-cs35l35-objs := cs35l35.o +snd-soc-cs35l36-objs := cs35l36.o snd-soc-cs42l42-objs := cs42l42.o snd-soc-cs42l51-objs := cs42l51.o snd-soc-cs42l51-i2c-objs := cs42l51-i2c.o @@ -60,6 +62,7 @@ snd-soc-cs4271-spi-objs := cs4271-spi.o snd-soc-cs42xx8-objs := cs42xx8.o snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o snd-soc-cs43130-objs := cs43130.o +snd-soc-cs4341-objs := cs4341.o snd-soc-cs4349-objs := cs4349.o snd-soc-cs47l24-objs := cs47l24.o snd-soc-cs53l30-objs := cs53l30.o @@ -84,6 +87,7 @@ snd-soc-ics43432-objs := ics43432.o snd-soc-inno-rk3036-objs := inno_rk3036.o snd-soc-isabelle-objs := isabelle.o snd-soc-jz4740-codec-objs := jz4740.o +snd-soc-jz4725b-codec-objs := jz4725b.o snd-soc-l3-objs := l3.o snd-soc-lm4857-objs := lm4857.o snd-soc-lm49453-objs := lm49453.o @@ -106,6 +110,7 @@ snd-soc-ml26124-objs := ml26124.o snd-soc-msm8916-analog-objs := msm8916-wcd-analog.o snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o snd-soc-mt6351-objs := mt6351.o +snd-soc-mt6358-objs := mt6358.o snd-soc-nau8540-objs := nau8540.o snd-soc-nau8810-objs := nau8810.o snd-soc-nau8822-objs := nau8822.o @@ -132,6 +137,7 @@ snd-soc-pcm5102a-objs := pcm5102a.o snd-soc-pcm512x-objs := pcm512x.o snd-soc-pcm512x-i2c-objs := pcm512x-i2c.o snd-soc-pcm512x-spi-objs := pcm512x-spi.o +snd-soc-rk3328-objs := rk3328_codec.o snd-soc-rl6231-objs := rl6231.o snd-soc-rl6347a-objs := rl6347a.o snd-soc-rt1305-objs := rt1305.o @@ -198,6 +204,7 @@ snd-soc-twl4030-objs := twl4030.o snd-soc-twl6040-objs := twl6040.o snd-soc-uda134x-objs := uda134x.o snd-soc-uda1380-objs := uda1380.o +snd-soc-wcd9335-objs := wcd-clsh-v2.o wcd9335.o snd-soc-wl1273-objs := wl1273.o snd-soc-wm-adsp-objs := wm_adsp.o snd-soc-wm0010-objs := wm0010.o @@ -308,10 +315,12 @@ obj-$(CONFIG_SND_SOC_BD28623) += snd-soc-bd28623.o obj-$(CONFIG_SND_SOC_BT_SCO) += snd-soc-bt-sco.o obj-$(CONFIG_SND_SOC_CQ0093VC) += snd-soc-cq93vc.o obj-$(CONFIG_SND_SOC_CPCAP) += snd-soc-cpcap.o +obj-$(CONFIG_SND_SOC_CROS_EC_CODEC) += snd-soc-cros-ec-codec.o obj-$(CONFIG_SND_SOC_CS35L32) += snd-soc-cs35l32.o obj-$(CONFIG_SND_SOC_CS35L33) += snd-soc-cs35l33.o obj-$(CONFIG_SND_SOC_CS35L34) += snd-soc-cs35l34.o obj-$(CONFIG_SND_SOC_CS35L35) += snd-soc-cs35l35.o +obj-$(CONFIG_SND_SOC_CS35L36) += snd-soc-cs35l36.o obj-$(CONFIG_SND_SOC_CS42L42) += snd-soc-cs42l42.o obj-$(CONFIG_SND_SOC_CS42L51) += snd-soc-cs42l51.o obj-$(CONFIG_SND_SOC_CS42L51_I2C) += snd-soc-cs42l51-i2c.o @@ -326,6 +335,7 @@ obj-$(CONFIG_SND_SOC_CS4271_SPI) += snd-soc-cs4271-spi.o obj-$(CONFIG_SND_SOC_CS42XX8) += snd-soc-cs42xx8.o obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o obj-$(CONFIG_SND_SOC_CS43130) += snd-soc-cs43130.o +obj-$(CONFIG_SND_SOC_CS4341) += snd-soc-cs4341.o obj-$(CONFIG_SND_SOC_CS4349) += snd-soc-cs4349.o obj-$(CONFIG_SND_SOC_CS47L24) += snd-soc-cs47l24.o obj-$(CONFIG_SND_SOC_CS53L30) += snd-soc-cs53l30.o @@ -350,6 +360,7 @@ obj-$(CONFIG_SND_SOC_ICS43432) += snd-soc-ics43432.o obj-$(CONFIG_SND_SOC_INNO_RK3036) += snd-soc-inno-rk3036.o obj-$(CONFIG_SND_SOC_ISABELLE) += snd-soc-isabelle.o obj-$(CONFIG_SND_SOC_JZ4740_CODEC) += snd-soc-jz4740-codec.o +obj-$(CONFIG_SND_SOC_JZ4725B_CODEC) += snd-soc-jz4725b-codec.o obj-$(CONFIG_SND_SOC_L3) += snd-soc-l3.o obj-$(CONFIG_SND_SOC_LM4857) += snd-soc-lm4857.o obj-$(CONFIG_SND_SOC_LM49453) += snd-soc-lm49453.o @@ -372,6 +383,7 @@ obj-$(CONFIG_SND_SOC_ML26124) += snd-soc-ml26124.o obj-$(CONFIG_SND_SOC_MSM8916_WCD_ANALOG) +=snd-soc-msm8916-analog.o obj-$(CONFIG_SND_SOC_MSM8916_WCD_DIGITAL) +=snd-soc-msm8916-digital.o obj-$(CONFIG_SND_SOC_MT6351) += snd-soc-mt6351.o +obj-$(CONFIG_SND_SOC_MT6358) += snd-soc-mt6358.o obj-$(CONFIG_SND_SOC_NAU8540) += snd-soc-nau8540.o obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o @@ -398,6 +410,7 @@ obj-$(CONFIG_SND_SOC_PCM5102A) += snd-soc-pcm5102a.o obj-$(CONFIG_SND_SOC_PCM512x) += snd-soc-pcm512x.o obj-$(CONFIG_SND_SOC_PCM512x_I2C) += snd-soc-pcm512x-i2c.o obj-$(CONFIG_SND_SOC_PCM512x_SPI) += snd-soc-pcm512x-spi.o +obj-$(CONFIG_SND_SOC_RK3328) += snd-soc-rk3328.o obj-$(CONFIG_SND_SOC_RL6231) += snd-soc-rl6231.o obj-$(CONFIG_SND_SOC_RL6347A) += snd-soc-rl6347a.o obj-$(CONFIG_SND_SOC_RT1305) += snd-soc-rt1305.o @@ -463,6 +476,7 @@ obj-$(CONFIG_SND_SOC_TWL4030) += snd-soc-twl4030.o obj-$(CONFIG_SND_SOC_TWL6040) += snd-soc-twl6040.o obj-$(CONFIG_SND_SOC_UDA134X) += snd-soc-uda134x.o obj-$(CONFIG_SND_SOC_UDA1380) += snd-soc-uda1380.o +obj-$(CONFIG_SND_SOC_WCD9335) += snd-soc-wcd9335.o obj-$(CONFIG_SND_SOC_WL1273) += snd-soc-wl1273.o obj-$(CONFIG_SND_SOC_WM0010) += snd-soc-wm0010.o obj-$(CONFIG_SND_SOC_WM1250_EV1) += snd-soc-wm1250-ev1.o diff --git a/sound/soc/codecs/ad193x.c b/sound/soc/codecs/ad193x.c @@ -37,6 +37,13 @@ static SOC_ENUM_SINGLE_DECL(ad193x_deemp_enum, AD193X_DAC_CTRL2, 1, static const DECLARE_TLV_DB_MINMAX(adau193x_tlv, -9563, 0); +static const unsigned int ad193x_sb[] = {32}; + +static struct snd_pcm_hw_constraint_list constr = { + .list = ad193x_sb, + .count = ARRAY_SIZE(ad193x_sb), +}; + static const struct snd_kcontrol_new ad193x_snd_controls[] = { /* DAC volume control */ SOC_DOUBLE_R_TLV("DAC1 Volume", AD193X_DAC_L1_VOL, @@ -93,6 +100,15 @@ static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = { SND_SOC_DAPM_INPUT("ADC2IN"), }; +static int ad193x_check_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = snd_soc_dapm_to_component(source->dapm); + struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); + + return !!ad193x->sysclk; +} + static const struct snd_soc_dapm_route audio_paths[] = { { "DAC", NULL, "SYSCLK" }, { "DAC Output", NULL, "DAC" }, @@ -101,7 +117,7 @@ static const struct snd_soc_dapm_route audio_paths[] = { { "DAC2OUT", NULL, "DAC Output" }, { "DAC3OUT", NULL, "DAC Output" }, { "DAC4OUT", NULL, "DAC Output" }, - { "SYSCLK", NULL, "PLL_PWR" }, + { "SYSCLK", NULL, "PLL_PWR", &ad193x_check_pll }, }; static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = { @@ -181,23 +197,26 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, { struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(codec_dai->component); unsigned int adc_serfmt = 0; + unsigned int dac_serfmt = 0; unsigned int adc_fmt = 0; unsigned int dac_fmt = 0; /* At present, the driver only support AUX ADC mode(SND_SOC_DAIFMT_I2S - * with TDM) and ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) + * with TDM), ADC&DAC TDM mode(SND_SOC_DAIFMT_DSP_A) and DAC I2S mode + * (SND_SOC_DAIFMT_I2S) */ switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { case SND_SOC_DAIFMT_I2S: adc_serfmt |= AD193X_ADC_SERFMT_TDM; + dac_serfmt |= AD193X_DAC_SERFMT_STEREO; break; case SND_SOC_DAIFMT_DSP_A: adc_serfmt |= AD193X_ADC_SERFMT_AUX; + dac_serfmt |= AD193X_DAC_SERFMT_TDM; break; default: if (ad193x_has_adc(ad193x)) return -EINVAL; - break; } switch (fmt & SND_SOC_DAIFMT_INV_MASK) { @@ -221,6 +240,12 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, return -EINVAL; } + /* For DSP_*, LRCLK's polarity must be inverted */ + if (fmt & SND_SOC_DAIFMT_DSP_A) { + change_bit(ffs(AD193X_DAC_LEFT_HIGH) - 1, + (unsigned long *)&dac_fmt); + } + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBM_CFM: /* codec clk & frm master */ adc_fmt |= AD193X_ADC_LCR_MASTER; @@ -248,6 +273,8 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai, regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2, AD193X_ADC_FMT_MASK, adc_fmt); } + regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL0, + AD193X_DAC_SERFMT_MASK, dac_serfmt); regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1, AD193X_DAC_FMT_MASK, dac_fmt); @@ -258,7 +285,22 @@ static int ad193x_set_dai_sysclk(struct snd_soc_dai *codec_dai, int clk_id, unsigned int freq, int dir) { struct snd_soc_component *component = codec_dai->component; + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); + + if (clk_id == AD193X_SYSCLK_MCLK) { + /* MCLK must be 512 x fs */ + if (dir == SND_SOC_CLOCK_OUT || freq != 24576000) + return -EINVAL; + + regmap_update_bits(ad193x->regmap, AD193X_PLL_CLK_CTRL1, + AD193X_PLL_SRC_MASK, + AD193X_PLL_DAC_SRC_MCLK | + AD193X_PLL_CLK_SRC_MCLK); + + snd_soc_dapm_sync(dapm); + return 0; + } switch (freq) { case 12288000: case 18432000: @@ -321,7 +363,16 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream, return 0; } +static int ad193x_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + &constr); +} + static const struct snd_soc_dai_ops ad193x_dai_ops = { + .startup = ad193x_startup, .hw_params = ad193x_hw_params, .digital_mute = ad193x_mute, .set_tdm_slot = ad193x_set_tdm_slot, @@ -351,6 +402,20 @@ static struct snd_soc_dai_driver ad193x_dai = { .ops = &ad193x_dai_ops, }; +/* codec DAI instance for DAC only */ +static struct snd_soc_dai_driver ad193x_no_adc_dai = { + .name = "ad193x-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S20_3LE | SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &ad193x_dai_ops, +}; + static int ad193x_component_probe(struct snd_soc_component *component) { struct ad193x_priv *ad193x = snd_soc_component_get_drvdata(component); @@ -444,8 +509,11 @@ int ad193x_probe(struct device *dev, struct regmap *regmap, dev_set_drvdata(dev, ad193x); + if (ad193x_has_adc(ad193x)) + return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x, + &ad193x_dai, 1); return devm_snd_soc_register_component(dev, &soc_component_dev_ad193x, - &ad193x_dai, 1); + &ad193x_no_adc_dai, 1); } EXPORT_SYMBOL_GPL(ad193x_probe); diff --git a/sound/soc/codecs/ad193x.h b/sound/soc/codecs/ad193x.h @@ -31,6 +31,11 @@ int ad193x_probe(struct device *dev, struct regmap *regmap, #define AD193X_PLL_INPUT_512 (2 << 1) #define AD193X_PLL_INPUT_768 (3 << 1) #define AD193X_PLL_CLK_CTRL1 0x01 +#define AD193X_PLL_SRC_MASK 0x03 +#define AD193X_PLL_DAC_SRC_PLL 0 +#define AD193X_PLL_DAC_SRC_MCLK 1 +#define AD193X_PLL_CLK_SRC_PLL (0 << 1) +#define AD193X_PLL_CLK_SRC_MCLK (1 << 1) #define AD193X_DAC_CTRL0 0x02 #define AD193X_DAC_POWERDOWN 0x01 #define AD193X_DAC_SERFMT_MASK 0xC0 @@ -96,4 +101,7 @@ int ad193x_probe(struct device *dev, struct regmap *regmap, #define AD193X_NUM_REGS 17 +#define AD193X_SYSCLK_PLL 0 +#define AD193X_SYSCLK_MCLK 1 + #endif diff --git a/sound/soc/codecs/adau1977.c b/sound/soc/codecs/adau1977.c @@ -885,13 +885,15 @@ static int adau1977_setup_micbias(struct adau1977 *adau1977) struct adau1977_platform_data *pdata = adau1977->dev->platform_data; unsigned int micbias; - if (pdata) { + if (pdata) micbias = pdata->micbias; - if (micbias > ADAU1977_MICBIAS_9V0) - return -EINVAL; - - } else { + else if (device_property_read_u32(adau1977->dev, "adi,micbias", + &micbias)) micbias = ADAU1977_MICBIAS_8V5; + + if (micbias > ADAU1977_MICBIAS_9V0) { + dev_err(adau1977->dev, "Invalid value for 'adi,micbias'\n"); + return -EINVAL; } return regmap_update_bits(adau1977->regmap, ADAU1977_REG_MICBIAS, diff --git a/sound/soc/codecs/adau7002.c b/sound/soc/codecs/adau7002.c @@ -8,6 +8,7 @@ */ #include <linux/acpi.h> +#include <linux/delay.h> #include <linux/init.h> #include <linux/module.h> #include <linux/of.h> @@ -15,12 +16,55 @@ #include <sound/soc.h> +struct adau7002_priv { + int wakeup_delay; +}; + +static int adau7002_aif_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct adau7002_priv *adau7002 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (adau7002->wakeup_delay) + msleep(adau7002->wakeup_delay); + break; + } + + return 0; +} + +static int adau7002_component_probe(struct snd_soc_component *component) +{ + struct adau7002_priv *adau7002; + + adau7002 = devm_kzalloc(component->dev, sizeof(*adau7002), + GFP_KERNEL); + if (!adau7002) + return -ENOMEM; + + device_property_read_u32(component->dev, "wakeup-delay-ms", + &adau7002->wakeup_delay); + + snd_soc_component_set_drvdata(component, adau7002); + + return 0; +} + static const struct snd_soc_dapm_widget adau7002_widgets[] = { + SND_SOC_DAPM_AIF_OUT_E("ADAU AIF", "Capture", 0, + SND_SOC_NOPM, 0, 0, adau7002_aif_event, + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_INPUT("PDM_DAT"), SND_SOC_DAPM_REGULATOR_SUPPLY("IOVDD", 0, 0), }; static const struct snd_soc_dapm_route adau7002_routes[] = { + { "ADAU AIF", NULL, "PDM_DAT"}, { "Capture", NULL, "PDM_DAT" }, { "Capture", NULL, "IOVDD" }, }; @@ -40,6 +84,7 @@ static struct snd_soc_dai_driver adau7002_dai = { }; static const struct snd_soc_component_driver adau7002_component_driver = { + .probe = adau7002_component_probe, .dapm_widgets = adau7002_widgets, .num_dapm_widgets = ARRAY_SIZE(adau7002_widgets), .dapm_routes = adau7002_routes, diff --git a/sound/soc/codecs/ak4118.c b/sound/soc/codecs/ak4118.c @@ -6,6 +6,7 @@ */ #include <linux/i2c.h> +#include <linux/gpio/consumer.h> #include <linux/module.h> #include <linux/of_device.h> #include <linux/of_gpio.h> diff --git a/sound/soc/codecs/ak4458.c b/sound/soc/codecs/ak4458.c @@ -21,6 +21,11 @@ #include "ak4458.h" +struct ak4458_drvdata { + struct snd_soc_dai_driver *dai_drv; + const struct snd_soc_component_driver *comp_drv; +}; + /* AK4458 Codec Private Data */ struct ak4458_priv { struct device *dev; @@ -258,6 +263,33 @@ static const struct snd_soc_dapm_route ak4458_intercon[] = { {"AK4458 AOUTD", NULL, "AK4458 DAC4"}, }; +/* ak4497 controls */ +static const struct snd_kcontrol_new ak4497_snd_controls[] = { + SOC_DOUBLE_R_TLV("DAC Playback Volume", AK4458_03_LCHATT, + AK4458_04_RCHATT, 0, 0xFF, 0, dac_tlv), + SOC_ENUM("AK4497 De-emphasis Response DAC", ak4458_dac1_dem_enum), + SOC_ENUM_EXT("AK4497 Digital Filter Setting", ak4458_digfil_enum, + get_digfil, set_digfil), + SOC_ENUM("AK4497 Inverting Enable of DZFB", ak4458_dzfb_enum), + SOC_ENUM("AK4497 Sound Mode", ak4458_sm_enum), + SOC_ENUM("AK4497 Attenuation transition Time Setting", + ak4458_ats_enum), +}; + +/* ak4497 dapm widgets */ +static const struct snd_soc_dapm_widget ak4497_dapm_widgets[] = { + SND_SOC_DAPM_DAC("AK4497 DAC", NULL, AK4458_0A_CONTROL6, 2, 0), + SND_SOC_DAPM_AIF_IN("AK4497 SDTI", "Playback", 0, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("AK4497 AOUT"), +}; + +/* ak4497 dapm routes */ +static const struct snd_soc_dapm_route ak4497_intercon[] = { + {"AK4497 DAC", NULL, "AK4497 SDTI"}, + {"AK4497 AOUT", NULL, "AK4497 DAC"}, + +}; + static int ak4458_rstn_control(struct snd_soc_component *component, int bit) { int ret; @@ -476,6 +508,18 @@ static struct snd_soc_dai_driver ak4458_dai = { .ops = &ak4458_dai_ops, }; +static struct snd_soc_dai_driver ak4497_dai = { + .name = "ak4497-aif", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = AK4458_FORMATS, + }, + .ops = &ak4458_dai_ops, +}; + static void ak4458_power_off(struct ak4458_priv *ak4458) { if (ak4458->reset_gpiod) { @@ -573,6 +617,21 @@ static const struct snd_soc_component_driver soc_codec_dev_ak4458 = { .non_legacy_dai_naming = 1, }; +static const struct snd_soc_component_driver soc_codec_dev_ak4497 = { + .probe = ak4458_probe, + .remove = ak4458_remove, + .controls = ak4497_snd_controls, + .num_controls = ARRAY_SIZE(ak4497_snd_controls), + .dapm_widgets = ak4497_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(ak4497_dapm_widgets), + .dapm_routes = ak4497_intercon, + .num_dapm_routes = ARRAY_SIZE(ak4497_intercon), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + static const struct regmap_config ak4458_regmap = { .reg_bits = 8, .val_bits = 8, @@ -583,6 +642,16 @@ static const struct regmap_config ak4458_regmap = { .cache_type = REGCACHE_RBTREE, }; +static const struct ak4458_drvdata ak4458_drvdata = { + .dai_drv = &ak4458_dai, + .comp_drv = &soc_codec_dev_ak4458, +}; + +static const struct ak4458_drvdata ak4497_drvdata = { + .dai_drv = &ak4497_dai, + .comp_drv = &soc_codec_dev_ak4497, +}; + static const struct dev_pm_ops ak4458_pm = { SET_RUNTIME_PM_OPS(ak4458_runtime_suspend, ak4458_runtime_resume, NULL) SET_SYSTEM_SLEEP_PM_OPS(pm_runtime_force_suspend, @@ -592,6 +661,7 @@ static const struct dev_pm_ops ak4458_pm = { static int ak4458_i2c_probe(struct i2c_client *i2c) { struct ak4458_priv *ak4458; + const struct ak4458_drvdata *drvdata; int ret; ak4458 = devm_kzalloc(&i2c->dev, sizeof(*ak4458), GFP_KERNEL); @@ -605,6 +675,8 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) i2c_set_clientdata(i2c, ak4458); ak4458->dev = &i2c->dev; + drvdata = of_device_get_match_data(&i2c->dev); + ak4458->reset_gpiod = devm_gpiod_get_optional(ak4458->dev, "reset", GPIOD_OUT_LOW); if (IS_ERR(ak4458->reset_gpiod)) @@ -615,8 +687,8 @@ static int ak4458_i2c_probe(struct i2c_client *i2c) if (IS_ERR(ak4458->mute_gpiod)) return PTR_ERR(ak4458->mute_gpiod); - ret = devm_snd_soc_register_component(ak4458->dev, &soc_codec_dev_ak4458, - &ak4458_dai, 1); + ret = devm_snd_soc_register_component(ak4458->dev, drvdata->comp_drv, + drvdata->dai_drv, 1); if (ret < 0) { dev_err(ak4458->dev, "Failed to register CODEC: %d\n", ret); return ret; @@ -635,7 +707,8 @@ static int ak4458_i2c_remove(struct i2c_client *i2c) } static const struct of_device_id ak4458_of_match[] = { - { .compatible = "asahi-kasei,ak4458", }, + { .compatible = "asahi-kasei,ak4458", .data = &ak4458_drvdata}, + { .compatible = "asahi-kasei,ak4497", .data = &ak4497_drvdata}, { }, }; diff --git a/sound/soc/codecs/cros_ec_codec.c b/sound/soc/codecs/cros_ec_codec.c @@ -0,0 +1,441 @@ +// SPDX-License-Identifier: GPL-2.0 +/* + * Driver for ChromeOS Embedded Controller codec. + * + * This driver uses the cros-ec interface to communicate with the ChromeOS + * EC for audio function. + */ + +#include <linux/delay.h> +#include <linux/device.h> +#include <linux/kernel.h> +#include <linux/mfd/cros_ec.h> +#include <linux/mfd/cros_ec_commands.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define DRV_NAME "cros-ec-codec" + +/** + * struct cros_ec_codec_data - ChromeOS EC codec driver data. + * @dev: Device structure used in sysfs. + * @ec_device: cros_ec_device structure to talk to the physical device. + * @component: Pointer to the component. + * @max_dmic_gain: Maximum gain in dB supported by EC codec. + */ +struct cros_ec_codec_data { + struct device *dev; + struct cros_ec_device *ec_device; + struct snd_soc_component *component; + unsigned int max_dmic_gain; +}; + +static const DECLARE_TLV_DB_SCALE(ec_mic_gain_tlv, 0, 100, 0); + +static int ec_command_get_gain(struct snd_soc_component *component, + struct ec_param_codec_i2s *param, + struct ec_response_codec_gain *resp) +{ + struct cros_ec_codec_data *codec_data = + snd_soc_component_get_drvdata(component); + struct cros_ec_device *ec_device = codec_data->ec_device; + u8 buffer[sizeof(struct cros_ec_command) + + max(sizeof(struct ec_param_codec_i2s), + sizeof(struct ec_response_codec_gain))]; + struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; + int ret; + + msg->version = 0; + msg->command = EC_CMD_CODEC_I2S; + msg->outsize = sizeof(struct ec_param_codec_i2s); + msg->insize = sizeof(struct ec_response_codec_gain); + + memcpy(msg->data, param, msg->outsize); + + ret = cros_ec_cmd_xfer_status(ec_device, msg); + if (ret > 0) + memcpy(resp, msg->data, msg->insize); + + return ret; +} + +/* + * Wrapper for EC command without response. + */ +static int ec_command_no_resp(struct snd_soc_component *component, + struct ec_param_codec_i2s *param) +{ + struct cros_ec_codec_data *codec_data = + snd_soc_component_get_drvdata(component); + struct cros_ec_device *ec_device = codec_data->ec_device; + u8 buffer[sizeof(struct cros_ec_command) + + sizeof(struct ec_param_codec_i2s)]; + struct cros_ec_command *msg = (struct cros_ec_command *)&buffer; + + msg->version = 0; + msg->command = EC_CMD_CODEC_I2S; + msg->outsize = sizeof(struct ec_param_codec_i2s); + msg->insize = 0; + + memcpy(msg->data, param, msg->outsize); + + return cros_ec_cmd_xfer_status(ec_device, msg); +} + +static int set_i2s_config(struct snd_soc_component *component, + enum ec_i2s_config i2s_config) +{ + struct ec_param_codec_i2s param; + + dev_dbg(component->dev, "%s set I2S format to %u\n", __func__, + i2s_config); + + param.cmd = EC_CODEC_I2S_SET_CONFIG; + param.i2s_config = i2s_config; + + return ec_command_no_resp(component, &param); +} + +static int cros_ec_i2s_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + enum ec_i2s_config i2s_config; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + i2s_config = EC_DAI_FMT_I2S; + break; + + case SND_SOC_DAIFMT_RIGHT_J: + i2s_config = EC_DAI_FMT_RIGHT_J; + break; + + case SND_SOC_DAIFMT_LEFT_J: + i2s_config = EC_DAI_FMT_LEFT_J; + break; + + case SND_SOC_DAIFMT_DSP_A: + i2s_config = EC_DAI_FMT_PCM_A; + break; + + case SND_SOC_DAIFMT_DSP_B: + i2s_config = EC_DAI_FMT_PCM_B; + break; + + default: + return -EINVAL; + } + + return set_i2s_config(component, i2s_config); +} + +static int set_i2s_sample_depth(struct snd_soc_component *component, + enum ec_sample_depth_value depth) +{ + struct ec_param_codec_i2s param; + + dev_dbg(component->dev, "%s set depth to %u\n", __func__, depth); + + param.cmd = EC_CODEC_SET_SAMPLE_DEPTH; + param.depth = depth; + + return ec_command_no_resp(component, &param); +} + +static int set_i2s_bclk(struct snd_soc_component *component, uint32_t bclk) +{ + struct ec_param_codec_i2s param; + + dev_dbg(component->dev, "%s set i2s bclk to %u\n", __func__, bclk); + + param.cmd = EC_CODEC_I2S_SET_BCLK; + param.bclk = bclk; + + return ec_command_no_resp(component, &param); +} + +static int cros_ec_i2s_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + unsigned int rate, bclk; + int ret; + + rate = params_rate(params); + if (rate != 48000) + return -EINVAL; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_16); + break; + case SNDRV_PCM_FORMAT_S24_LE: + ret = set_i2s_sample_depth(component, EC_CODEC_SAMPLE_DEPTH_24); + break; + default: + return -EINVAL; + } + if (ret < 0) + return ret; + + bclk = snd_soc_params_to_bclk(params); + return set_i2s_bclk(component, bclk); +} + +static const struct snd_soc_dai_ops cros_ec_i2s_dai_ops = { + .hw_params = cros_ec_i2s_hw_params, + .set_fmt = cros_ec_i2s_set_dai_fmt, +}; + +static struct snd_soc_dai_driver cros_ec_dai[] = { + { + .name = "cros_ec_codec I2S", + .id = 0, + .capture = { + .stream_name = "I2S Capture", + .channels_min = 2, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_48000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &cros_ec_i2s_dai_ops, + } +}; + +static int get_ec_mic_gain(struct snd_soc_component *component, + u8 *left, u8 *right) +{ + struct ec_param_codec_i2s param; + struct ec_response_codec_gain resp; + int ret; + + param.cmd = EC_CODEC_GET_GAIN; + + ret = ec_command_get_gain(component, &param, &resp); + if (ret < 0) + return ret; + + *left = resp.left; + *right = resp.right; + + return 0; +} + +static int mic_gain_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + u8 left, right; + int ret; + + ret = get_ec_mic_gain(component, &left, &right); + if (ret) + return ret; + + ucontrol->value.integer.value[0] = left; + ucontrol->value.integer.value[1] = right; + + return 0; +} + +static int set_ec_mic_gain(struct snd_soc_component *component, + u8 left, u8 right) +{ + struct ec_param_codec_i2s param; + + dev_dbg(component->dev, "%s set mic gain to %u, %u\n", + __func__, left, right); + + param.cmd = EC_CODEC_SET_GAIN; + param.gain.left = left; + param.gain.right = right; + + return ec_command_no_resp(component, &param); +} + +static int mic_gain_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cros_ec_codec_data *codec_data = + snd_soc_component_get_drvdata(component); + int left = ucontrol->value.integer.value[0]; + int right = ucontrol->value.integer.value[1]; + unsigned int max_dmic_gain = codec_data->max_dmic_gain; + + if (left > max_dmic_gain || right > max_dmic_gain) + return -EINVAL; + + return set_ec_mic_gain(component, (u8)left, (u8)right); +} + +static struct snd_kcontrol_new mic_gain_control = + SOC_DOUBLE_EXT_TLV("EC Mic Gain", SND_SOC_NOPM, SND_SOC_NOPM, 0, 0, 0, + mic_gain_get, mic_gain_put, ec_mic_gain_tlv); + +static int enable_i2s(struct snd_soc_component *component, int enable) +{ + struct ec_param_codec_i2s param; + + dev_dbg(component->dev, "%s set i2s to %u\n", __func__, enable); + + param.cmd = EC_CODEC_I2S_ENABLE; + param.i2s_enable = enable; + + return ec_command_no_resp(component, &param); +} + +static int cros_ec_i2s_enable_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + dev_dbg(component->dev, + "%s got SND_SOC_DAPM_PRE_PMU event\n", __func__); + return enable_i2s(component, 1); + + case SND_SOC_DAPM_PRE_PMD: + dev_dbg(component->dev, + "%s got SND_SOC_DAPM_PRE_PMD event\n", __func__); + return enable_i2s(component, 0); + } + + return 0; +} + +/* + * The goal of this DAPM route is to turn on/off I2S using EC + * host command when capture stream is started/stopped. + */ +static const struct snd_soc_dapm_widget cros_ec_codec_dapm_widgets[] = { + SND_SOC_DAPM_INPUT("DMIC"), + + /* + * Control EC to enable/disable I2S. + */ + SND_SOC_DAPM_SUPPLY("I2S Enable", SND_SOC_NOPM, + 0, 0, cros_ec_i2s_enable_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_AIF_OUT("I2STX", "I2S Capture", 0, SND_SOC_NOPM, 0, 0), +}; + +static const struct snd_soc_dapm_route cros_ec_codec_dapm_routes[] = { + { "I2STX", NULL, "DMIC" }, + { "I2STX", NULL, "I2S Enable" }, +}; + +/* + * Read maximum gain from device property and set it to mixer control. + */ +static int cros_ec_set_gain_range(struct device *dev) +{ + struct soc_mixer_control *control; + struct cros_ec_codec_data *codec_data = dev_get_drvdata(dev); + int rc; + + rc = device_property_read_u32(dev, "max-dmic-gain", + &codec_data->max_dmic_gain); + if (rc) + return rc; + + control = (struct soc_mixer_control *) + mic_gain_control.private_value; + control->max = codec_data->max_dmic_gain; + control->platform_max = codec_data->max_dmic_gain; + + return 0; +} + +static int cros_ec_codec_probe(struct snd_soc_component *component) +{ + int rc; + + struct cros_ec_codec_data *codec_data = + snd_soc_component_get_drvdata(component); + + rc = cros_ec_set_gain_range(codec_data->dev); + if (rc) + return rc; + + return snd_soc_add_component_controls(component, &mic_gain_control, 1); +} + +static const struct snd_soc_component_driver cros_ec_component_driver = { + .probe = cros_ec_codec_probe, + .dapm_widgets = cros_ec_codec_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cros_ec_codec_dapm_widgets), + .dapm_routes = cros_ec_codec_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(cros_ec_codec_dapm_routes), +}; + +/* + * Platform device and platform driver fro cros-ec-codec. + */ +static int cros_ec_codec_platform_probe(struct platform_device *pd) +{ + struct device *dev = &pd->dev; + struct cros_ec_device *ec_device = dev_get_drvdata(pd->dev.parent); + struct cros_ec_codec_data *codec_data; + + codec_data = devm_kzalloc(dev, sizeof(struct cros_ec_codec_data), + GFP_KERNEL); + if (!codec_data) + return -ENOMEM; + + codec_data->dev = dev; + codec_data->ec_device = ec_device; + + platform_set_drvdata(pd, codec_data); + + return snd_soc_register_component(dev, &cros_ec_component_driver, + cros_ec_dai, ARRAY_SIZE(cros_ec_dai)); +} + +#ifdef CONFIG_OF +static const struct of_device_id cros_ec_codec_of_match[] = { + { .compatible = "google,cros-ec-codec" }, + {}, +}; +MODULE_DEVICE_TABLE(of, cros_ec_codec_of_match); +#endif + +static struct platform_driver cros_ec_codec_platform_driver = { + .driver = { + .name = DRV_NAME, + .of_match_table = of_match_ptr(cros_ec_codec_of_match), + }, + .probe = cros_ec_codec_platform_probe, +}; + +module_platform_driver(cros_ec_codec_platform_driver); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ChromeOS EC codec driver"); +MODULE_AUTHOR("Cheng-Yi Chiang <cychiang@chromium.org>"); +MODULE_ALIAS("platform:" DRV_NAME); diff --git a/sound/soc/codecs/cs35l36.c b/sound/soc/codecs/cs35l36.c @@ -0,0 +1,1957 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// cs35l36.c -- CS35L36 ALSA SoC audio driver +// +// Copyright 2018 Cirrus Logic, Inc. +// +// Author: James Schulman <james.schulman@cirrus.com> + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/i2c.h> +#include <linux/slab.h> +#include <linux/workqueue.h> +#include <linux/platform_device.h> +#include <linux/regulator/consumer.h> +#include <linux/gpio/consumer.h> +#include <linux/of_device.h> +#include <linux/of_gpio.h> +#include <linux/regmap.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/soc-dapm.h> +#include <linux/gpio.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <sound/cs35l36.h> +#include <linux/of_irq.h> +#include <linux/completion.h> + +#include "cs35l36.h" + +/* + * Some fields take zero as a valid value so use a high bit flag that won't + * get written to the device to mark those. + */ +#define CS35L36_VALID_PDATA 0x80000000 + +static const char * const cs35l36_supplies[] = { + "VA", + "VP", +}; + +struct cs35l36_private { + struct device *dev; + struct cs35l36_platform_data pdata; + struct regmap *regmap; + struct regulator_bulk_data supplies[2]; + int num_supplies; + int clksrc; + int chip_version; + int rev_id; + int ldm_mode_sel; + struct gpio_desc *reset_gpio; +}; + +struct cs35l36_pll_config { + int freq; + int clk_cfg; + int fll_igain; +}; + +static const struct cs35l36_pll_config cs35l36_pll_sysclk[] = { + {32768, 0x00, 0x05}, + {8000, 0x01, 0x03}, + {11025, 0x02, 0x03}, + {12000, 0x03, 0x03}, + {16000, 0x04, 0x04}, + {22050, 0x05, 0x04}, + {24000, 0x06, 0x04}, + {32000, 0x07, 0x05}, + {44100, 0x08, 0x05}, + {48000, 0x09, 0x05}, + {88200, 0x0A, 0x06}, + {96000, 0x0B, 0x06}, + {128000, 0x0C, 0x07}, + {176400, 0x0D, 0x07}, + {192000, 0x0E, 0x07}, + {256000, 0x0F, 0x08}, + {352800, 0x10, 0x08}, + {384000, 0x11, 0x08}, + {512000, 0x12, 0x09}, + {705600, 0x13, 0x09}, + {750000, 0x14, 0x09}, + {768000, 0x15, 0x09}, + {1000000, 0x16, 0x0A}, + {1024000, 0x17, 0x0A}, + {1200000, 0x18, 0x0A}, + {1411200, 0x19, 0x0A}, + {1500000, 0x1A, 0x0A}, + {1536000, 0x1B, 0x0A}, + {2000000, 0x1C, 0x0A}, + {2048000, 0x1D, 0x0A}, + {2400000, 0x1E, 0x0A}, + {2822400, 0x1F, 0x0A}, + {3000000, 0x20, 0x0A}, + {3072000, 0x21, 0x0A}, + {3200000, 0x22, 0x0A}, + {4000000, 0x23, 0x0A}, + {4096000, 0x24, 0x0A}, + {4800000, 0x25, 0x0A}, + {5644800, 0x26, 0x0A}, + {6000000, 0x27, 0x0A}, + {6144000, 0x28, 0x0A}, + {6250000, 0x29, 0x08}, + {6400000, 0x2A, 0x0A}, + {6500000, 0x2B, 0x08}, + {6750000, 0x2C, 0x09}, + {7526400, 0x2D, 0x0A}, + {8000000, 0x2E, 0x0A}, + {8192000, 0x2F, 0x0A}, + {9600000, 0x30, 0x0A}, + {11289600, 0x31, 0x0A}, + {12000000, 0x32, 0x0A}, + {12288000, 0x33, 0x0A}, + {12500000, 0x34, 0x08}, + {12800000, 0x35, 0x0A}, + {13000000, 0x36, 0x0A}, + {13500000, 0x37, 0x0A}, + {19200000, 0x38, 0x0A}, + {22579200, 0x39, 0x0A}, + {24000000, 0x3A, 0x0A}, + {24576000, 0x3B, 0x0A}, + {25000000, 0x3C, 0x0A}, + {25600000, 0x3D, 0x0A}, + {26000000, 0x3E, 0x0A}, + {27000000, 0x3F, 0x0A}, +}; + +static struct reg_default cs35l36_reg[] = { + {CS35L36_TESTKEY_CTRL, 0x00000000}, + {CS35L36_USERKEY_CTL, 0x00000000}, + {CS35L36_OTP_CTRL1, 0x00002460}, + {CS35L36_OTP_CTRL2, 0x00000000}, + {CS35L36_OTP_CTRL3, 0x00000000}, + {CS35L36_OTP_CTRL4, 0x00000000}, + {CS35L36_OTP_CTRL5, 0x00000000}, + {CS35L36_PAC_CTL1, 0x00000004}, + {CS35L36_PAC_CTL2, 0x00000000}, + {CS35L36_PAC_CTL3, 0x00000000}, + {CS35L36_PWR_CTRL1, 0x00000000}, + {CS35L36_PWR_CTRL2, 0x00003321}, + {CS35L36_PWR_CTRL3, 0x01000010}, + {CS35L36_CTRL_OVRRIDE, 0x00000002}, + {CS35L36_AMP_OUT_MUTE, 0x00000000}, + {CS35L36_OTP_TRIM_STATUS, 0x00000000}, + {CS35L36_DISCH_FILT, 0x00000000}, + {CS35L36_PROTECT_REL_ERR, 0x00000000}, + {CS35L36_PAD_INTERFACE, 0x00000038}, + {CS35L36_PLL_CLK_CTRL, 0x00000010}, + {CS35L36_GLOBAL_CLK_CTRL, 0x00000003}, + {CS35L36_ADC_CLK_CTRL, 0x00000000}, + {CS35L36_SWIRE_CLK_CTRL, 0x00000000}, + {CS35L36_SP_SCLK_CLK_CTRL, 0x00000000}, + {CS35L36_MDSYNC_EN, 0x00000000}, + {CS35L36_MDSYNC_TX_ID, 0x00000000}, + {CS35L36_MDSYNC_PWR_CTRL, 0x00000000}, + {CS35L36_MDSYNC_DATA_TX, 0x00000000}, + {CS35L36_MDSYNC_TX_STATUS, 0x00000002}, + {CS35L36_MDSYNC_RX_STATUS, 0x00000000}, + {CS35L36_MDSYNC_ERR_STATUS, 0x00000000}, + {CS35L36_BSTCVRT_VCTRL1, 0x00000000}, + {CS35L36_BSTCVRT_VCTRL2, 0x00000001}, + {CS35L36_BSTCVRT_PEAK_CUR, 0x0000004A}, + {CS35L36_BSTCVRT_SFT_RAMP, 0x00000003}, + {CS35L36_BSTCVRT_COEFF, 0x00002424}, + {CS35L36_BSTCVRT_SLOPE_LBST, 0x00005800}, + {CS35L36_BSTCVRT_SW_FREQ, 0x00010000}, + {CS35L36_BSTCVRT_DCM_CTRL, 0x00002001}, + {CS35L36_BSTCVRT_DCM_MODE_FORCE, 0x00000000}, + {CS35L36_BSTCVRT_OVERVOLT_CTRL, 0x00000130}, + {CS35L36_VPI_LIMIT_MODE, 0x00000000}, + {CS35L36_VPI_LIMIT_MINMAX, 0x00003000}, + {CS35L36_VPI_VP_THLD, 0x00101010}, + {CS35L36_VPI_TRACK_CTRL, 0x00000000}, + {CS35L36_VPI_TRIG_MODE_CTRL, 0x00000000}, + {CS35L36_VPI_TRIG_STEPS, 0x00000000}, + {CS35L36_VI_SPKMON_FILT, 0x00000003}, + {CS35L36_VI_SPKMON_GAIN, 0x00000909}, + {CS35L36_VI_SPKMON_IP_SEL, 0x00000000}, + {CS35L36_DTEMP_WARN_THLD, 0x00000002}, + {CS35L36_DTEMP_STATUS, 0x00000000}, + {CS35L36_VPVBST_FS_SEL, 0x00000001}, + {CS35L36_VPVBST_VP_CTRL, 0x000001C0}, + {CS35L36_VPVBST_VBST_CTRL, 0x000001C0}, + {CS35L36_ASP_TX_PIN_CTRL, 0x00000028}, + {CS35L36_ASP_RATE_CTRL, 0x00090000}, + {CS35L36_ASP_FORMAT, 0x00000002}, + {CS35L36_ASP_FRAME_CTRL, 0x00180018}, + {CS35L36_ASP_TX1_TX2_SLOT, 0x00010000}, + {CS35L36_ASP_TX3_TX4_SLOT, 0x00030002}, + {CS35L36_ASP_TX5_TX6_SLOT, 0x00050004}, + {CS35L36_ASP_TX7_TX8_SLOT, 0x00070006}, + {CS35L36_ASP_RX1_SLOT, 0x00000000}, + {CS35L36_ASP_RX_TX_EN, 0x00000000}, + {CS35L36_ASP_RX1_SEL, 0x00000008}, + {CS35L36_ASP_TX1_SEL, 0x00000018}, + {CS35L36_ASP_TX2_SEL, 0x00000019}, + {CS35L36_ASP_TX3_SEL, 0x00000028}, + {CS35L36_ASP_TX4_SEL, 0x00000029}, + {CS35L36_ASP_TX5_SEL, 0x00000020}, + {CS35L36_ASP_TX6_SEL, 0x00000000}, + {CS35L36_SWIRE_P1_TX1_SEL, 0x00000018}, + {CS35L36_SWIRE_P1_TX2_SEL, 0x00000019}, + {CS35L36_SWIRE_P2_TX1_SEL, 0x00000028}, + {CS35L36_SWIRE_P2_TX2_SEL, 0x00000029}, + {CS35L36_SWIRE_P2_TX3_SEL, 0x00000020}, + {CS35L36_SWIRE_DP1_FIFO_CFG, 0x0000001B}, + {CS35L36_SWIRE_DP2_FIFO_CFG, 0x0000001B}, + {CS35L36_SWIRE_DP3_FIFO_CFG, 0x0000001B}, + {CS35L36_SWIRE_PCM_RX_DATA, 0x00000000}, + {CS35L36_SWIRE_FS_SEL, 0x00000001}, + {CS35L36_AMP_DIG_VOL_CTRL, 0x00008000}, + {CS35L36_VPBR_CFG, 0x02AA1905}, + {CS35L36_VBBR_CFG, 0x02AA1905}, + {CS35L36_VPBR_STATUS, 0x00000000}, + {CS35L36_VBBR_STATUS, 0x00000000}, + {CS35L36_OVERTEMP_CFG, 0x00000001}, + {CS35L36_AMP_ERR_VOL, 0x00000000}, + {CS35L36_CLASSH_CFG, 0x000B0405}, + {CS35L36_CLASSH_FET_DRV_CFG, 0x00000111}, + {CS35L36_NG_CFG, 0x00000033}, + {CS35L36_AMP_GAIN_CTRL, 0x00000273}, + {CS35L36_PWM_MOD_IO_CTRL, 0x00000000}, + {CS35L36_PWM_MOD_STATUS, 0x00000000}, + {CS35L36_DAC_MSM_CFG, 0x00000000}, + {CS35L36_AMP_SLOPE_CTRL, 0x00000B00}, + {CS35L36_AMP_PDM_VOLUME, 0x00000000}, + {CS35L36_AMP_PDM_RATE_CTRL, 0x00000000}, + {CS35L36_PDM_CH_SEL, 0x00000000}, + {CS35L36_AMP_NG_CTRL, 0x0000212F}, + {CS35L36_PDM_HIGHFILT_CTRL, 0x00000000}, + {CS35L36_PAC_INT0_CTRL, 0x00000001}, + {CS35L36_PAC_INT1_CTRL, 0x00000001}, + {CS35L36_PAC_INT2_CTRL, 0x00000001}, + {CS35L36_PAC_INT3_CTRL, 0x00000001}, + {CS35L36_PAC_INT4_CTRL, 0x00000001}, + {CS35L36_PAC_INT5_CTRL, 0x00000001}, + {CS35L36_PAC_INT6_CTRL, 0x00000001}, + {CS35L36_PAC_INT7_CTRL, 0x00000001}, +}; + +static bool cs35l36_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L36_SW_RESET: + case CS35L36_SW_REV: + case CS35L36_HW_REV: + case CS35L36_TESTKEY_CTRL: + case CS35L36_USERKEY_CTL: + case CS35L36_OTP_MEM30: + case CS35L36_OTP_CTRL1: + case CS35L36_OTP_CTRL2: + case CS35L36_OTP_CTRL3: + case CS35L36_OTP_CTRL4: + case CS35L36_OTP_CTRL5: + case CS35L36_PAC_CTL1: + case CS35L36_PAC_CTL2: + case CS35L36_PAC_CTL3: + case CS35L36_DEVICE_ID: + case CS35L36_FAB_ID: + case CS35L36_REV_ID: + case CS35L36_PWR_CTRL1: + case CS35L36_PWR_CTRL2: + case CS35L36_PWR_CTRL3: + case CS35L36_CTRL_OVRRIDE: + case CS35L36_AMP_OUT_MUTE: + case CS35L36_OTP_TRIM_STATUS: + case CS35L36_DISCH_FILT: + case CS35L36_PROTECT_REL_ERR: + case CS35L36_PAD_INTERFACE: + case CS35L36_PLL_CLK_CTRL: + case CS35L36_GLOBAL_CLK_CTRL: + case CS35L36_ADC_CLK_CTRL: + case CS35L36_SWIRE_CLK_CTRL: + case CS35L36_SP_SCLK_CLK_CTRL: + case CS35L36_TST_FS_MON0: + case CS35L36_MDSYNC_EN: + case CS35L36_MDSYNC_TX_ID: + case CS35L36_MDSYNC_PWR_CTRL: + case CS35L36_MDSYNC_DATA_TX: + case CS35L36_MDSYNC_TX_STATUS: + case CS35L36_MDSYNC_RX_STATUS: + case CS35L36_MDSYNC_ERR_STATUS: + case CS35L36_BSTCVRT_VCTRL1: + case CS35L36_BSTCVRT_VCTRL2: + case CS35L36_BSTCVRT_PEAK_CUR: + case CS35L36_BSTCVRT_SFT_RAMP: + case CS35L36_BSTCVRT_COEFF: + case CS35L36_BSTCVRT_SLOPE_LBST: + case CS35L36_BSTCVRT_SW_FREQ: + case CS35L36_BSTCVRT_DCM_CTRL: + case CS35L36_BSTCVRT_DCM_MODE_FORCE: + case CS35L36_BSTCVRT_OVERVOLT_CTRL: + case CS35L36_BST_TST_MANUAL: + case CS35L36_BST_ANA2_TEST: + case CS35L36_VPI_LIMIT_MODE: + case CS35L36_VPI_LIMIT_MINMAX: + case CS35L36_VPI_VP_THLD: + case CS35L36_VPI_TRACK_CTRL: + case CS35L36_VPI_TRIG_MODE_CTRL: + case CS35L36_VPI_TRIG_STEPS: + case CS35L36_VI_SPKMON_FILT: + case CS35L36_VI_SPKMON_GAIN: + case CS35L36_VI_SPKMON_IP_SEL: + case CS35L36_DTEMP_WARN_THLD: + case CS35L36_DTEMP_STATUS: + case CS35L36_VPVBST_FS_SEL: + case CS35L36_VPVBST_VP_CTRL: + case CS35L36_VPVBST_VBST_CTRL: + case CS35L36_ASP_TX_PIN_CTRL: + case CS35L36_ASP_RATE_CTRL: + case CS35L36_ASP_FORMAT: + case CS35L36_ASP_FRAME_CTRL: + case CS35L36_ASP_TX1_TX2_SLOT: + case CS35L36_ASP_TX3_TX4_SLOT: + case CS35L36_ASP_TX5_TX6_SLOT: + case CS35L36_ASP_TX7_TX8_SLOT: + case CS35L36_ASP_RX1_SLOT: + case CS35L36_ASP_RX_TX_EN: + case CS35L36_ASP_RX1_SEL: + case CS35L36_ASP_TX1_SEL: + case CS35L36_ASP_TX2_SEL: + case CS35L36_ASP_TX3_SEL: + case CS35L36_ASP_TX4_SEL: + case CS35L36_ASP_TX5_SEL: + case CS35L36_ASP_TX6_SEL: + case CS35L36_SWIRE_P1_TX1_SEL: + case CS35L36_SWIRE_P1_TX2_SEL: + case CS35L36_SWIRE_P2_TX1_SEL: + case CS35L36_SWIRE_P2_TX2_SEL: + case CS35L36_SWIRE_P2_TX3_SEL: + case CS35L36_SWIRE_DP1_FIFO_CFG: + case CS35L36_SWIRE_DP2_FIFO_CFG: + case CS35L36_SWIRE_DP3_FIFO_CFG: + case CS35L36_SWIRE_PCM_RX_DATA: + case CS35L36_SWIRE_FS_SEL: + case CS35L36_AMP_DIG_VOL_CTRL: + case CS35L36_VPBR_CFG: + case CS35L36_VBBR_CFG: + case CS35L36_VPBR_STATUS: + case CS35L36_VBBR_STATUS: + case CS35L36_OVERTEMP_CFG: + case CS35L36_AMP_ERR_VOL: + case CS35L36_CLASSH_CFG: + case CS35L36_CLASSH_FET_DRV_CFG: + case CS35L36_NG_CFG: + case CS35L36_AMP_GAIN_CTRL: + case CS35L36_PWM_MOD_IO_CTRL: + case CS35L36_PWM_MOD_STATUS: + case CS35L36_DAC_MSM_CFG: + case CS35L36_AMP_SLOPE_CTRL: + case CS35L36_AMP_PDM_VOLUME: + case CS35L36_AMP_PDM_RATE_CTRL: + case CS35L36_PDM_CH_SEL: + case CS35L36_AMP_NG_CTRL: + case CS35L36_PDM_HIGHFILT_CTRL: + case CS35L36_INT1_STATUS: + case CS35L36_INT2_STATUS: + case CS35L36_INT3_STATUS: + case CS35L36_INT4_STATUS: + case CS35L36_INT1_RAW_STATUS: + case CS35L36_INT2_RAW_STATUS: + case CS35L36_INT3_RAW_STATUS: + case CS35L36_INT4_RAW_STATUS: + case CS35L36_INT1_MASK: + case CS35L36_INT2_MASK: + case CS35L36_INT3_MASK: + case CS35L36_INT4_MASK: + case CS35L36_INT1_EDGE_LVL_CTRL: + case CS35L36_INT3_EDGE_LVL_CTRL: + case CS35L36_PAC_INT_STATUS: + case CS35L36_PAC_INT_RAW_STATUS: + case CS35L36_PAC_INT_FLUSH_CTRL: + case CS35L36_PAC_INT0_CTRL: + case CS35L36_PAC_INT1_CTRL: + case CS35L36_PAC_INT2_CTRL: + case CS35L36_PAC_INT3_CTRL: + case CS35L36_PAC_INT4_CTRL: + case CS35L36_PAC_INT5_CTRL: + case CS35L36_PAC_INT6_CTRL: + case CS35L36_PAC_INT7_CTRL: + return true; + default: + if (reg >= CS35L36_PAC_PMEM_WORD0 && + reg <= CS35L36_PAC_PMEM_WORD1023) + return true; + else + return false; + } +} + +static bool cs35l36_precious_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L36_TESTKEY_CTRL: + case CS35L36_USERKEY_CTL: + case CS35L36_TST_FS_MON0: + return true; + default: + return false; + } +} + +static bool cs35l36_volatile_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case CS35L36_SW_RESET: + case CS35L36_SW_REV: + case CS35L36_HW_REV: + case CS35L36_TESTKEY_CTRL: + case CS35L36_USERKEY_CTL: + case CS35L36_DEVICE_ID: + case CS35L36_FAB_ID: + case CS35L36_REV_ID: + case CS35L36_INT1_STATUS: + case CS35L36_INT2_STATUS: + case CS35L36_INT3_STATUS: + case CS35L36_INT4_STATUS: + case CS35L36_INT1_RAW_STATUS: + case CS35L36_INT2_RAW_STATUS: + case CS35L36_INT3_RAW_STATUS: + case CS35L36_INT4_RAW_STATUS: + case CS35L36_INT1_MASK: + case CS35L36_INT2_MASK: + case CS35L36_INT3_MASK: + case CS35L36_INT4_MASK: + case CS35L36_INT1_EDGE_LVL_CTRL: + case CS35L36_INT3_EDGE_LVL_CTRL: + case CS35L36_PAC_INT_STATUS: + case CS35L36_PAC_INT_RAW_STATUS: + case CS35L36_PAC_INT_FLUSH_CTRL: + return true; + default: + if (reg >= CS35L36_PAC_PMEM_WORD0 && + reg <= CS35L36_PAC_PMEM_WORD1023) + return true; + else + return false; + } +} + +static DECLARE_TLV_DB_SCALE(dig_vol_tlv, -10200, 25, 0); +static DECLARE_TLV_DB_SCALE(amp_gain_tlv, 0, 1, 1); + +static const char * const cs35l36_pcm_sftramp_text[] = { + "Off", ".5ms", "1ms", "2ms", "4ms", "8ms", "15ms", "30ms"}; + +static SOC_ENUM_SINGLE_DECL(pcm_sft_ramp, CS35L36_AMP_DIG_VOL_CTRL, 0, + cs35l36_pcm_sftramp_text); + +static int cs35l36_ldm_sel_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + + ucontrol->value.integer.value[0] = cs35l36->ldm_mode_sel; + + return 0; +} + +static int cs35l36_ldm_sel_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + int val = (ucontrol->value.integer.value[0]) ? CS35L36_NG_AMP_EN_MASK : + 0; + + cs35l36->ldm_mode_sel = val; + + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_AMP_EN_MASK, val); + + return 0; +} + +static const struct snd_kcontrol_new cs35l36_aud_controls[] = { + SOC_SINGLE_SX_TLV("Digital PCM Volume", CS35L36_AMP_DIG_VOL_CTRL, + 3, 0x4D0, 0x390, dig_vol_tlv), + SOC_SINGLE_TLV("Analog PCM Volume", CS35L36_AMP_GAIN_CTRL, 5, 0x13, 0, + amp_gain_tlv), + SOC_ENUM("PCM Soft Ramp", pcm_sft_ramp), + SOC_SINGLE("Amp Gain Zero-Cross Switch", CS35L36_AMP_GAIN_CTRL, + CS35L36_AMP_ZC_SHIFT, 1, 0), + SOC_SINGLE("PDM LDM Enter Ramp Switch", CS35L36_DAC_MSM_CFG, + CS35L36_PDM_LDM_ENTER_SHIFT, 1, 0), + SOC_SINGLE("PDM LDM Exit Ramp Switch", CS35L36_DAC_MSM_CFG, + CS35L36_PDM_LDM_EXIT_SHIFT, 1, 0), + SOC_SINGLE_BOOL_EXT("LDM Select Switch", 0, cs35l36_ldm_sel_get, + cs35l36_ldm_sel_put), +}; + +static int cs35l36_main_amp_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + u32 reg; + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL1, + CS35L36_GLOBAL_EN_MASK, + 1 << CS35L36_GLOBAL_EN_SHIFT); + + usleep_range(2000, 2100); + + regmap_read(cs35l36->regmap, CS35L36_INT4_RAW_STATUS, &reg); + + if (WARN_ON_ONCE(reg & CS35L36_PLL_UNLOCK_MASK)) + dev_crit(cs35l36->dev, "PLL Unlocked\n"); + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RX1_SEL, + CS35L36_PCM_RX_SEL_MASK, + CS35L36_PCM_RX_SEL_PCM); + regmap_update_bits(cs35l36->regmap, CS35L36_AMP_OUT_MUTE, + CS35L36_AMP_MUTE_MASK, + 0 << CS35L36_AMP_MUTE_SHIFT); + break; + case SND_SOC_DAPM_PRE_PMD: + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RX1_SEL, + CS35L36_PCM_RX_SEL_MASK, + CS35L36_PCM_RX_SEL_ZERO); + regmap_update_bits(cs35l36->regmap, CS35L36_AMP_OUT_MUTE, + CS35L36_AMP_MUTE_MASK, + 1 << CS35L36_AMP_MUTE_SHIFT); + break; + case SND_SOC_DAPM_POST_PMD: + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL1, + CS35L36_GLOBAL_EN_MASK, + 0 << CS35L36_GLOBAL_EN_SHIFT); + + usleep_range(2000, 2100); + break; + default: + dev_dbg(component->dev, "Invalid event = 0x%x\n", event); + return -EINVAL; + } + + return 0; +} + +static int cs35l36_boost_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + + switch (event) { + case SND_SOC_DAPM_POST_PMU: + if (!cs35l36->pdata.extern_boost) + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL2, + CS35L36_BST_EN_MASK, + CS35L36_BST_EN << + CS35L36_BST_EN_SHIFT); + break; + case SND_SOC_DAPM_POST_PMD: + if (!cs35l36->pdata.extern_boost) + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL2, + CS35L36_BST_EN_MASK, + CS35L36_BST_DIS_VP << + CS35L36_BST_EN_SHIFT); + break; + default: + dev_dbg(component->dev, "Invalid event = 0x%x\n", event); + return -EINVAL; + } + + return 0; +} + +static const char * const cs35l36_chan_text[] = { + "RX1", + "RX2", +}; + +static SOC_ENUM_SINGLE_DECL(chansel_enum, CS35L36_ASP_RX1_SLOT, 0, + cs35l36_chan_text); + +static const struct snd_kcontrol_new cs35l36_chan_mux = + SOC_DAPM_ENUM("Input Mux", chansel_enum); + +static const struct snd_kcontrol_new amp_enable_ctrl = + SOC_DAPM_SINGLE_AUTODISABLE("Switch", CS35L36_AMP_OUT_MUTE, + CS35L36_AMP_MUTE_SHIFT, 1, 1); + +static const struct snd_kcontrol_new boost_ctrl = + SOC_DAPM_SINGLE_VIRT("Switch", 1); + +static const char * const asp_tx_src_text[] = { + "Zero Fill", "ASPRX1", "VMON", "IMON", "ERRVOL", "VPMON", "VBSTMON" +}; + +static const unsigned int asp_tx_src_values[] = { + 0x00, 0x08, 0x18, 0x19, 0x20, 0x28, 0x29 +}; + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx1_src_enum, CS35L36_ASP_TX1_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx1_src = + SOC_DAPM_ENUM("ASPTX1SRC", asp_tx1_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx2_src_enum, CS35L36_ASP_TX2_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx2_src = + SOC_DAPM_ENUM("ASPTX2SRC", asp_tx2_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx3_src_enum, CS35L36_ASP_TX3_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx3_src = + SOC_DAPM_ENUM("ASPTX3SRC", asp_tx3_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx4_src_enum, CS35L36_ASP_TX4_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx4_src = + SOC_DAPM_ENUM("ASPTX4SRC", asp_tx4_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx5_src_enum, CS35L36_ASP_TX5_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx5_src = + SOC_DAPM_ENUM("ASPTX5SRC", asp_tx5_src_enum); + +static SOC_VALUE_ENUM_SINGLE_DECL(asp_tx6_src_enum, CS35L36_ASP_TX6_SEL, 0, + CS35L36_APS_TX_SEL_MASK, asp_tx_src_text, + asp_tx_src_values); + +static const struct snd_kcontrol_new asp_tx6_src = + SOC_DAPM_ENUM("ASPTX6SRC", asp_tx6_src_enum); + +static const struct snd_soc_dapm_widget cs35l36_dapm_widgets[] = { + SND_SOC_DAPM_MUX("Channel Mux", SND_SOC_NOPM, 0, 0, &cs35l36_chan_mux), + SND_SOC_DAPM_AIF_IN("SDIN", NULL, 0, CS35L36_ASP_RX_TX_EN, 16, 0), + + SND_SOC_DAPM_OUT_DRV_E("Main AMP", CS35L36_PWR_CTRL2, 0, 0, NULL, 0, + cs35l36_main_amp_event, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD), + + SND_SOC_DAPM_OUTPUT("SPK"), + SND_SOC_DAPM_SWITCH("AMP Enable", SND_SOC_NOPM, 0, 1, &amp_enable_ctrl), + SND_SOC_DAPM_MIXER("CLASS H", CS35L36_PWR_CTRL3, 4, 0, NULL, 0), + SND_SOC_DAPM_SWITCH_E("BOOST Enable", SND_SOC_NOPM, 0, 0, &boost_ctrl, + cs35l36_boost_event, SND_SOC_DAPM_POST_PMD | + SND_SOC_DAPM_POST_PMU), + + SND_SOC_DAPM_AIF_OUT("ASPTX1", NULL, 0, CS35L36_ASP_RX_TX_EN, 0, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX2", NULL, 1, CS35L36_ASP_RX_TX_EN, 1, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX3", NULL, 2, CS35L36_ASP_RX_TX_EN, 2, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX4", NULL, 3, CS35L36_ASP_RX_TX_EN, 3, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX5", NULL, 4, CS35L36_ASP_RX_TX_EN, 4, 0), + SND_SOC_DAPM_AIF_OUT("ASPTX6", NULL, 5, CS35L36_ASP_RX_TX_EN, 5, 0), + + SND_SOC_DAPM_MUX("ASPTX1SRC", SND_SOC_NOPM, 0, 0, &asp_tx1_src), + SND_SOC_DAPM_MUX("ASPTX2SRC", SND_SOC_NOPM, 0, 0, &asp_tx2_src), + SND_SOC_DAPM_MUX("ASPTX3SRC", SND_SOC_NOPM, 0, 0, &asp_tx3_src), + SND_SOC_DAPM_MUX("ASPTX4SRC", SND_SOC_NOPM, 0, 0, &asp_tx4_src), + SND_SOC_DAPM_MUX("ASPTX5SRC", SND_SOC_NOPM, 0, 0, &asp_tx5_src), + SND_SOC_DAPM_MUX("ASPTX6SRC", SND_SOC_NOPM, 0, 0, &asp_tx6_src), + + SND_SOC_DAPM_ADC("VMON ADC", NULL, CS35L36_PWR_CTRL2, 12, 0), + SND_SOC_DAPM_ADC("IMON ADC", NULL, CS35L36_PWR_CTRL2, 13, 0), + SND_SOC_DAPM_ADC("VPMON ADC", NULL, CS35L36_PWR_CTRL2, 8, 0), + SND_SOC_DAPM_ADC("VBSTMON ADC", NULL, CS35L36_PWR_CTRL2, 9, 0), + + SND_SOC_DAPM_INPUT("VP"), + SND_SOC_DAPM_INPUT("VBST"), + SND_SOC_DAPM_INPUT("VSENSE"), +}; + +static const struct snd_soc_dapm_route cs35l36_audio_map[] = { + {"VPMON ADC", NULL, "VP"}, + {"VBSTMON ADC", NULL, "VBST"}, + {"IMON ADC", NULL, "VSENSE"}, + {"VMON ADC", NULL, "VSENSE"}, + + {"ASPTX1SRC", "IMON", "IMON ADC"}, + {"ASPTX1SRC", "VMON", "VMON ADC"}, + {"ASPTX1SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX1SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX2SRC", "IMON", "IMON ADC"}, + {"ASPTX2SRC", "VMON", "VMON ADC"}, + {"ASPTX2SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX2SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX3SRC", "IMON", "IMON ADC"}, + {"ASPTX3SRC", "VMON", "VMON ADC"}, + {"ASPTX3SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX3SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX4SRC", "IMON", "IMON ADC"}, + {"ASPTX4SRC", "VMON", "VMON ADC"}, + {"ASPTX4SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX4SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX5SRC", "IMON", "IMON ADC"}, + {"ASPTX5SRC", "VMON", "VMON ADC"}, + {"ASPTX5SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX5SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX6SRC", "IMON", "IMON ADC"}, + {"ASPTX6SRC", "VMON", "VMON ADC"}, + {"ASPTX6SRC", "VBSTMON", "VBSTMON ADC"}, + {"ASPTX6SRC", "VPMON", "VPMON ADC"}, + + {"ASPTX1", NULL, "ASPTX1SRC"}, + {"ASPTX2", NULL, "ASPTX2SRC"}, + {"ASPTX3", NULL, "ASPTX3SRC"}, + {"ASPTX4", NULL, "ASPTX4SRC"}, + {"ASPTX5", NULL, "ASPTX5SRC"}, + {"ASPTX6", NULL, "ASPTX6SRC"}, + + {"AMP Capture", NULL, "ASPTX1"}, + {"AMP Capture", NULL, "ASPTX2"}, + {"AMP Capture", NULL, "ASPTX3"}, + {"AMP Capture", NULL, "ASPTX4"}, + {"AMP Capture", NULL, "ASPTX5"}, + {"AMP Capture", NULL, "ASPTX6"}, + + {"AMP Enable", "Switch", "AMP Playback"}, + {"SDIN", NULL, "AMP Enable"}, + {"Channel Mux", "RX1", "SDIN"}, + {"Channel Mux", "RX2", "SDIN"}, + {"BOOST Enable", "Switch", "Channel Mux"}, + {"CLASS H", NULL, "BOOST Enable"}, + {"Main AMP", NULL, "Channel Mux"}, + {"Main AMP", NULL, "CLASS H"}, + {"SPK", NULL, "Main AMP"}, +}; + +static int cs35l36_set_dai_fmt(struct snd_soc_dai *component_dai, + unsigned int fmt) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component_dai->component); + unsigned int asp_fmt, lrclk_fmt, sclk_fmt, slave_mode, clk_frc; + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + slave_mode = 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + slave_mode = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_SCLK_MSTR_MASK, + slave_mode << CS35L36_SCLK_MSTR_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL, + CS35L36_LRCLK_MSTR_MASK, + slave_mode << CS35L36_LRCLK_MSTR_SHIFT); + + switch (fmt & SND_SOC_DAIFMT_CLOCK_MASK) { + case SND_SOC_DAIFMT_CONT: + clk_frc = 1; + break; + case SND_SOC_DAIFMT_GATED: + clk_frc = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_SCLK_FRC_MASK, clk_frc << + CS35L36_SCLK_FRC_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL, + CS35L36_LRCLK_FRC_MASK, clk_frc << + CS35L36_LRCLK_FRC_SHIFT); + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_DSP_A: + asp_fmt = 0; + break; + case SND_SOC_DAIFMT_I2S: + asp_fmt = 2; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_IF: + lrclk_fmt = 1; + sclk_fmt = 0; + break; + case SND_SOC_DAIFMT_IB_NF: + lrclk_fmt = 0; + sclk_fmt = 1; + break; + case SND_SOC_DAIFMT_IB_IF: + lrclk_fmt = 1; + sclk_fmt = 1; + break; + case SND_SOC_DAIFMT_NB_NF: + lrclk_fmt = 0; + sclk_fmt = 0; + break; + default: + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_RATE_CTRL, + CS35L36_LRCLK_INV_MASK, + lrclk_fmt << CS35L36_LRCLK_INV_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_SCLK_INV_MASK, + sclk_fmt << CS35L36_SCLK_INV_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FORMAT, + CS35L36_ASP_FMT_MASK, asp_fmt); + + return 0; +} + +struct cs35l36_global_fs_config { + int rate; + int fs_cfg; +}; + +static const struct cs35l36_global_fs_config cs35l36_fs_rates[] = { + {12000, 0x01}, + {24000, 0x02}, + {48000, 0x03}, + {96000, 0x04}, + {192000, 0x05}, + {384000, 0x06}, + {11025, 0x09}, + {22050, 0x0A}, + {44100, 0x0B}, + {88200, 0x0C}, + {176400, 0x0D}, + {8000, 0x11}, + {16000, 0x12}, + {32000, 0x13}, +}; + +static int cs35l36_pcm_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(dai->component); + unsigned int asp_width, global_fs = params_rate(params); + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l36_fs_rates); i++) { + if (global_fs == cs35l36_fs_rates[i].rate) + regmap_update_bits(cs35l36->regmap, + CS35L36_GLOBAL_CLK_CTRL, + CS35L36_GLOBAL_FS_MASK, + cs35l36_fs_rates[i].fs_cfg << + CS35L36_GLOBAL_FS_SHIFT); + } + + switch (params_width(params)) { + case 16: + asp_width = CS35L36_ASP_WIDTH_16; + break; + case 24: + asp_width = CS35L36_ASP_WIDTH_24; + break; + case 32: + asp_width = CS35L36_ASP_WIDTH_32; + break; + default: + return -EINVAL; + } + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FRAME_CTRL, + CS35L36_ASP_RX_WIDTH_MASK, + asp_width << CS35L36_ASP_RX_WIDTH_SHIFT); + } else { + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_FRAME_CTRL, + CS35L36_ASP_TX_WIDTH_MASK, + asp_width << CS35L36_ASP_TX_WIDTH_SHIFT); + } + + return 0; +} + +static int cs35l36_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + int fs1, fs2; + + if (freq > CS35L36_FS_NOM_6MHZ) { + fs1 = CS35L36_FS1_DEFAULT_VAL; + fs2 = CS35L36_FS2_DEFAULT_VAL; + } else { + fs1 = 3 * ((CS35L36_FS_NOM_6MHZ * 4 + freq - 1) / freq) + 4; + fs2 = 5 * ((CS35L36_FS_NOM_6MHZ * 4 + freq - 1) / freq) + 4; + } + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_update_bits(cs35l36->regmap, CS35L36_TST_FS_MON0, + CS35L36_FS1_WINDOW_MASK | CS35L36_FS2_WINDOW_MASK, + fs1 | (fs2 << CS35L36_FS2_WINDOW_SHIFT)); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + return 0; +} + +static const struct cs35l36_pll_config *cs35l36_get_clk_config( + struct cs35l36_private *cs35l36, int freq) +{ + int i; + + for (i = 0; i < ARRAY_SIZE(cs35l36_pll_sysclk); i++) { + if (cs35l36_pll_sysclk[i].freq == freq) + return &cs35l36_pll_sysclk[i]; + } + + return NULL; +} + +static const unsigned int cs35l36_src_rates[] = { + 8000, 12000, 11025, 16000, 22050, 24000, 32000, + 44100, 48000, 88200, 96000, 176400, 192000, 384000 +}; + +static const struct snd_pcm_hw_constraint_list cs35l36_constraints = { + .count = ARRAY_SIZE(cs35l36_src_rates), + .list = cs35l36_src_rates, +}; + +static int cs35l36_pcm_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &cs35l36_constraints); + + return 0; +} + +static const struct snd_soc_dai_ops cs35l36_ops = { + .startup = cs35l36_pcm_startup, + .set_fmt = cs35l36_set_dai_fmt, + .hw_params = cs35l36_pcm_hw_params, + .set_sysclk = cs35l36_dai_set_sysclk, +}; + +static struct snd_soc_dai_driver cs35l36_dai[] = { + { + .name = "cs35l36-pcm", + .id = 0, + .playback = { + .stream_name = "AMP Playback", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS35L36_RX_FORMATS, + }, + .capture = { + .stream_name = "AMP Capture", + .channels_min = 1, + .channels_max = 8, + .rates = SNDRV_PCM_RATE_KNOT, + .formats = CS35L36_TX_FORMATS, + }, + .ops = &cs35l36_ops, + .symmetric_rates = 1, + }, +}; + +static int cs35l36_component_set_sysclk(struct snd_soc_component *component, + int clk_id, int source, unsigned int freq, + int dir) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + const struct cs35l36_pll_config *clk_cfg; + int prev_clksrc; + bool pdm_switch; + + prev_clksrc = cs35l36->clksrc; + + switch (clk_id) { + case 0: + cs35l36->clksrc = CS35L36_PLLSRC_SCLK; + break; + case 1: + cs35l36->clksrc = CS35L36_PLLSRC_LRCLK; + break; + case 2: + cs35l36->clksrc = CS35L36_PLLSRC_PDMCLK; + break; + case 3: + cs35l36->clksrc = CS35L36_PLLSRC_SELF; + break; + case 4: + cs35l36->clksrc = CS35L36_PLLSRC_MCLK; + break; + default: + return -EINVAL; + } + + clk_cfg = cs35l36_get_clk_config(cs35l36, freq); + if (clk_cfg == NULL) { + dev_err(component->dev, "Invalid CLK Config Freq: %d\n", freq); + return -EINVAL; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_OPENLOOP_MASK, + 1 << CS35L36_PLL_OPENLOOP_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_REFCLK_FREQ_MASK, + clk_cfg->clk_cfg << CS35L36_REFCLK_FREQ_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_REFCLK_EN_MASK, + 0 << CS35L36_PLL_REFCLK_EN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_CLK_SEL_MASK, + cs35l36->clksrc); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_OPENLOOP_MASK, + 0 << CS35L36_PLL_OPENLOOP_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_CLK_CTRL, + CS35L36_PLL_REFCLK_EN_MASK, + 1 << CS35L36_PLL_REFCLK_EN_SHIFT); + + if (cs35l36->rev_id == CS35L36_REV_A0) { + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_write(cs35l36->regmap, CS35L36_DCO_CTRL, 0x00036DA8); + regmap_write(cs35l36->regmap, CS35L36_MISC_CTRL, 0x0100EE0E); + + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_LOOP_PARAMS, + CS35L36_PLL_IGAIN_MASK, + CS35L36_PLL_IGAIN << + CS35L36_PLL_IGAIN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_PLL_LOOP_PARAMS, + CS35L36_PLL_FFL_IGAIN_MASK, + clk_cfg->fll_igain); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + } + + if (cs35l36->clksrc == CS35L36_PLLSRC_PDMCLK) { + pdm_switch = cs35l36->ldm_mode_sel && + (prev_clksrc != CS35L36_PLLSRC_PDMCLK); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 0 << CS35L36_NG_DELAY_SHIFT); + + regmap_update_bits(cs35l36->regmap, CS35L36_DAC_MSM_CFG, + CS35L36_PDM_MODE_MASK, + 1 << CS35L36_PDM_MODE_SHIFT); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 3 << CS35L36_NG_DELAY_SHIFT); + } else { + pdm_switch = cs35l36->ldm_mode_sel && + (prev_clksrc == CS35L36_PLLSRC_PDMCLK); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 0 << CS35L36_NG_DELAY_SHIFT); + + regmap_update_bits(cs35l36->regmap, CS35L36_DAC_MSM_CFG, + CS35L36_PDM_MODE_MASK, + 0 << CS35L36_PDM_MODE_SHIFT); + + if (pdm_switch) + regmap_update_bits(cs35l36->regmap, CS35L36_NG_CFG, + CS35L36_NG_DELAY_MASK, + 3 << CS35L36_NG_DELAY_SHIFT); + } + + return 0; +} + +static int cs35l36_boost_inductor(struct cs35l36_private *cs35l36, int inductor) +{ + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_COEFF, + CS35L36_BSTCVRT_K1_MASK, 0x3C); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_COEFF, + CS35L36_BSTCVRT_K2_MASK, + 0x3C << CS35L36_BSTCVRT_K2_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SW_FREQ, + CS35L36_BSTCVRT_CCMFREQ_MASK, 0x00); + + switch (inductor) { + case 1000: /* 1 uH */ + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_SLOPE_MASK, + 0x75 << CS35L36_BSTCVRT_SLOPE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_LBSTVAL_MASK, 0x00); + break; + case 1200: /* 1.2 uH */ + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_SLOPE_MASK, + 0x6B << CS35L36_BSTCVRT_SLOPE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_SLOPE_LBST, + CS35L36_BSTCVRT_LBSTVAL_MASK, 0x01); + break; + default: + dev_err(cs35l36->dev, "%s Invalid Inductor Value %d uH\n", + __func__, inductor); + return -EINVAL; + } + + return 0; +} + +static int cs35l36_component_probe(struct snd_soc_component *component) +{ + struct cs35l36_private *cs35l36 = + snd_soc_component_get_drvdata(component); + int ret = 0; + + if ((cs35l36->rev_id == CS35L36_REV_A0) && cs35l36->pdata.dcm_mode) { + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_DCM_CTRL, + CS35L36_DCM_AUTO_MASK, + CS35L36_DCM_AUTO_MASK); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_update_bits(cs35l36->regmap, CS35L36_BST_TST_MANUAL, + CS35L36_BST_MAN_IPKCOMP_MASK, + 0 << CS35L36_BST_MAN_IPKCOMP_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BST_TST_MANUAL, + CS35L36_BST_MAN_IPKCOMP_EN_MASK, + CS35L36_BST_MAN_IPKCOMP_EN_MASK); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + } + + if (cs35l36->pdata.amp_pcm_inv) + regmap_update_bits(cs35l36->regmap, CS35L36_AMP_DIG_VOL_CTRL, + CS35L36_AMP_PCM_INV_MASK, + CS35L36_AMP_PCM_INV_MASK); + + if (cs35l36->pdata.multi_amp_mode) + regmap_update_bits(cs35l36->regmap, CS35L36_ASP_TX_PIN_CTRL, + CS35L36_ASP_TX_HIZ_MASK, + CS35L36_ASP_TX_HIZ_MASK); + + if (cs35l36->pdata.imon_pol_inv) + regmap_update_bits(cs35l36->regmap, CS35L36_VI_SPKMON_FILT, + CS35L36_IMON_POL_MASK, 0); + + if (cs35l36->pdata.vmon_pol_inv) + regmap_update_bits(cs35l36->regmap, CS35L36_VI_SPKMON_FILT, + CS35L36_VMON_POL_MASK, 0); + + if (cs35l36->pdata.bst_vctl) + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL1, + CS35L35_BSTCVRT_CTL_MASK, + cs35l36->pdata.bst_vctl); + + if (cs35l36->pdata.bst_vctl_sel) + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL2, + CS35L35_BSTCVRT_CTL_SEL_MASK, + cs35l36->pdata.bst_vctl_sel); + + if (cs35l36->pdata.bst_ipk) + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_PEAK_CUR, + CS35L36_BST_IPK_MASK, + cs35l36->pdata.bst_ipk); + + if (cs35l36->pdata.boost_ind) { + ret = cs35l36_boost_inductor(cs35l36, cs35l36->pdata.boost_ind); + if (ret < 0) { + dev_err(cs35l36->dev, + "Boost inductor config failed(%d)\n", ret); + return ret; + } + } + + if (cs35l36->pdata.temp_warn_thld) + regmap_update_bits(cs35l36->regmap, CS35L36_DTEMP_WARN_THLD, + CS35L36_TEMP_THLD_MASK, + cs35l36->pdata.temp_warn_thld); + + if (cs35l36->pdata.irq_drv_sel) + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_DRV_SEL_MASK, + cs35l36->pdata.irq_drv_sel << + CS35L36_INT_DRV_SEL_SHIFT); + + if (cs35l36->pdata.irq_gpio_sel) + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_GPIO_SEL_MASK, + cs35l36->pdata.irq_gpio_sel << + CS35L36_INT_GPIO_SEL_SHIFT); + + /* + * Rev B0 has 2 versions + * L36 is 10V + * L37 is 12V + * If L36 we need to clamp some values for safety + * after probe has setup dt values. We want to make + * sure we dont miss any values set in probe + */ + if (cs35l36->chip_version == CS35L36_10V_L36) { + regmap_update_bits(cs35l36->regmap, + CS35L36_BSTCVRT_OVERVOLT_CTRL, + CS35L36_BST_OVP_THLD_MASK, + CS35L36_BST_OVP_THLD_11V); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + regmap_update_bits(cs35l36->regmap, CS35L36_BST_ANA2_TEST, + CS35L36_BST_OVP_TRIM_MASK, + CS35L36_BST_OVP_TRIM_11V << + CS35L36_BST_OVP_TRIM_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL2, + CS35L36_BST_CTRL_LIM_MASK, + 1 << CS35L36_BST_CTRL_LIM_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_BSTCVRT_VCTRL1, + CS35L35_BSTCVRT_CTL_MASK, + CS35L36_BST_CTRL_10V_CLAMP); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + } + + /* + * RevA and B require the disabling of + * SYNC_GLOBAL_OVR when GLOBAL_EN = 0. + * Just turn it off from default + */ + regmap_update_bits(cs35l36->regmap, CS35L36_CTRL_OVRRIDE, + CS35L36_SYNC_GLOBAL_OVR_MASK, + 0 << CS35L36_SYNC_GLOBAL_OVR_SHIFT); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_cs35l36 = { + .probe = &cs35l36_component_probe, + .set_sysclk = cs35l36_component_set_sysclk, + .dapm_widgets = cs35l36_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs35l36_dapm_widgets), + .dapm_routes = cs35l36_audio_map, + .num_dapm_routes = ARRAY_SIZE(cs35l36_audio_map), + .controls = cs35l36_aud_controls, + .num_controls = ARRAY_SIZE(cs35l36_aud_controls), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static struct regmap_config cs35l36_regmap = { + .reg_bits = 32, + .val_bits = 32, + .reg_stride = 4, + .max_register = CS35L36_PAC_PMEM_WORD1023, + .reg_defaults = cs35l36_reg, + .num_reg_defaults = ARRAY_SIZE(cs35l36_reg), + .precious_reg = cs35l36_precious_reg, + .volatile_reg = cs35l36_volatile_reg, + .readable_reg = cs35l36_readable_reg, + .cache_type = REGCACHE_RBTREE, +}; + +static irqreturn_t cs35l36_irq(int irq, void *data) +{ + struct cs35l36_private *cs35l36 = data; + unsigned int status[4]; + unsigned int masks[4]; + int ret = IRQ_NONE; + + /* ack the irq by reading all status registers */ + regmap_bulk_read(cs35l36->regmap, CS35L36_INT1_STATUS, status, + ARRAY_SIZE(status)); + + regmap_bulk_read(cs35l36->regmap, CS35L36_INT1_MASK, masks, + ARRAY_SIZE(masks)); + + /* Check to see if unmasked bits are active */ + if (!(status[0] & ~masks[0]) && !(status[1] & ~masks[1]) && + !(status[2] & ~masks[2]) && !(status[3] & ~masks[3])) { + return IRQ_NONE; + } + + /* + * The following interrupts require a + * protection release cycle to get the + * speaker out of Safe-Mode. + */ + if (status[2] & CS35L36_AMP_SHORT_ERR) { + dev_crit(cs35l36->dev, "Amp short error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_AMP_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_AMP_SHORT_ERR_RLS, + CS35L36_AMP_SHORT_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_AMP_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT3_STATUS, + CS35L36_AMP_SHORT_ERR, + CS35L36_AMP_SHORT_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_TEMP_WARN) { + dev_crit(cs35l36->dev, "Over temperature warning\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_WARN_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_WARN_ERR_RLS, + CS35L36_TEMP_WARN_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_WARN_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_TEMP_WARN, CS35L36_TEMP_WARN); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_TEMP_ERR) { + dev_crit(cs35l36->dev, "Over temperature error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, CS35L36_TEMP_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_TEMP_ERR, CS35L36_TEMP_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_BST_OVP_ERR) { + dev_crit(cs35l36->dev, "VBST Over Voltage error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, CS35L36_TEMP_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_TEMP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_BST_OVP_ERR, CS35L36_BST_OVP_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_BST_DCM_UVP_ERR) { + dev_crit(cs35l36->dev, "DCM VBST Under Voltage Error\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_UVP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_UVP_ERR_RLS, + CS35L36_BST_UVP_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_UVP_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_BST_DCM_UVP_ERR, + CS35L36_BST_DCM_UVP_ERR); + ret = IRQ_HANDLED; + } + + if (status[0] & CS35L36_BST_SHORT_ERR) { + dev_crit(cs35l36->dev, "LBST SHORT error!\n"); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_SHORT_ERR_RLS, + CS35L36_BST_SHORT_ERR_RLS); + regmap_update_bits(cs35l36->regmap, CS35L36_PROTECT_REL_ERR, + CS35L36_BST_SHORT_ERR_RLS, 0); + regmap_update_bits(cs35l36->regmap, CS35L36_INT1_STATUS, + CS35L36_BST_SHORT_ERR, + CS35L36_BST_SHORT_ERR); + ret = IRQ_HANDLED; + } + + return ret; +} + +static int cs35l36_handle_of_data(struct i2c_client *i2c_client, + struct cs35l36_platform_data *pdata) +{ + struct device_node *np = i2c_client->dev.of_node; + struct cs35l36_vpbr_cfg *vpbr_config = &pdata->vpbr_config; + struct device_node *vpbr_node; + unsigned int val; + int ret; + + if (!np) + return 0; + + ret = of_property_read_u32(np, "cirrus,boost-ctl-millivolt", &val); + if (!ret) { + if (val < 2550 || val > 12000) { + dev_err(&i2c_client->dev, + "Invalid Boost Voltage %d mV\n", val); + return -EINVAL; + } + pdata->bst_vctl = (((val - 2550) / 100) + 1) << 1; + } else { + dev_err(&i2c_client->dev, + "Unable to find required parameter 'cirrus,boost-ctl-millivolt'"); + return -EINVAL; + } + + ret = of_property_read_u32(np, "cirrus,boost-ctl-select", &val); + if (!ret) + pdata->bst_vctl_sel = val | CS35L36_VALID_PDATA; + + ret = of_property_read_u32(np, "cirrus,boost-peak-milliamp", &val); + if (!ret) { + if (val < 1600 || val > 4500) { + dev_err(&i2c_client->dev, + "Invalid Boost Peak Current %u mA\n", val); + return -EINVAL; + } + + pdata->bst_ipk = (val - 1600) / 50; + } else { + dev_err(&i2c_client->dev, + "Unable to find required parameter 'cirrus,boost-peak-milliamp'"); + return -EINVAL; + } + + pdata->multi_amp_mode = of_property_read_bool(np, + "cirrus,multi-amp-mode"); + + pdata->dcm_mode = of_property_read_bool(np, + "cirrus,dcm-mode-enable"); + + pdata->amp_pcm_inv = of_property_read_bool(np, + "cirrus,amp-pcm-inv"); + + pdata->imon_pol_inv = of_property_read_bool(np, + "cirrus,imon-pol-inv"); + + pdata->vmon_pol_inv = of_property_read_bool(np, + "cirrus,vmon-pol-inv"); + + if (of_property_read_u32(np, "cirrus,temp-warn-threshold", &val) >= 0) + pdata->temp_warn_thld = val | CS35L36_VALID_PDATA; + + if (of_property_read_u32(np, "cirrus,boost-ind-nanohenry", &val) >= 0) { + pdata->boost_ind = val; + } else { + dev_err(&i2c_client->dev, "Inductor not specified.\n"); + return -EINVAL; + } + + if (of_property_read_u32(np, "cirrus,irq-drive-select", &val) >= 0) + pdata->irq_drv_sel = val | CS35L36_VALID_PDATA; + + if (of_property_read_u32(np, "cirrus,irq-gpio-select", &val) >= 0) + pdata->irq_gpio_sel = val | CS35L36_VALID_PDATA; + + /* VPBR Config */ + vpbr_node = of_get_child_by_name(np, "cirrus,vpbr-config"); + vpbr_config->is_present = vpbr_node ? true : false; + if (vpbr_config->is_present) { + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-en", + &val) >= 0) + vpbr_config->vpbr_en = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-thld", + &val) >= 0) + vpbr_config->vpbr_thld = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-atk-rate", + &val) >= 0) + vpbr_config->vpbr_atk_rate = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-atk-vol", + &val) >= 0) + vpbr_config->vpbr_atk_vol = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-max-attn", + &val) >= 0) + vpbr_config->vpbr_max_attn = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-wait", + &val) >= 0) + vpbr_config->vpbr_wait = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-rel-rate", + &val) >= 0) + vpbr_config->vpbr_rel_rate = val; + if (of_property_read_u32(vpbr_node, "cirrus,vpbr-mute-en", + &val) >= 0) + vpbr_config->vpbr_mute_en = val; + } + of_node_put(vpbr_node); + + return 0; +} + +static int cs35l36_pac(struct cs35l36_private *cs35l36) +{ + int ret, count; + unsigned int val; + + if (cs35l36->rev_id != CS35L36_REV_B0) + return 0; + + /* + * Magic code for internal PAC + */ + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_UNLOCK2); + + usleep_range(9500, 10500); + + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL1, + CS35L36_PAC_RESET); + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL3, + CS35L36_PAC_MEM_ACCESS); + regmap_write(cs35l36->regmap, CS35L36_PAC_PMEM_WORD0, + CS35L36_B0_PAC_PATCH); + + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL3, + CS35L36_PAC_MEM_ACCESS_CLR); + regmap_write(cs35l36->regmap, CS35L36_PAC_CTL1, + CS35L36_PAC_ENABLE_MASK); + + usleep_range(9500, 10500); + + ret = regmap_read(cs35l36->regmap, CS35L36_INT4_STATUS, &val); + if (ret < 0) { + dev_err(cs35l36->dev, "Failed to read int4_status %d\n", ret); + return ret; + } + + count = 0; + while (!(val & CS35L36_MCU_CONFIG_CLR)) { + usleep_range(100, 200); + count++; + + ret = regmap_read(cs35l36->regmap, CS35L36_INT4_STATUS, + &val); + if (ret < 0) { + dev_err(cs35l36->dev, "Failed to read int4_status %d\n", + ret); + return ret; + } + + if (count >= 100) + return -EINVAL; + } + + regmap_write(cs35l36->regmap, CS35L36_INT4_STATUS, + CS35L36_MCU_CONFIG_CLR); + regmap_update_bits(cs35l36->regmap, CS35L36_PAC_CTL1, + CS35L36_PAC_ENABLE_MASK, 0); + + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK1); + regmap_write(cs35l36->regmap, CS35L36_TESTKEY_CTRL, + CS35L36_TEST_LOCK2); + + return 0; +} + +static void cs35l36_apply_vpbr_config(struct cs35l36_private *cs35l36) +{ + struct cs35l36_platform_data *pdata = &cs35l36->pdata; + struct cs35l36_vpbr_cfg *vpbr_config = &pdata->vpbr_config; + + regmap_update_bits(cs35l36->regmap, CS35L36_PWR_CTRL3, + CS35L36_VPBR_EN_MASK, + vpbr_config->vpbr_en << + CS35L36_VPBR_EN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_THLD_MASK, + vpbr_config->vpbr_thld << + CS35L36_VPBR_THLD_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_MAX_ATTN_MASK, + vpbr_config->vpbr_max_attn << + CS35L36_VPBR_MAX_ATTN_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_ATK_VOL_MASK, + vpbr_config->vpbr_atk_vol << + CS35L36_VPBR_ATK_VOL_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_ATK_RATE_MASK, + vpbr_config->vpbr_atk_rate << + CS35L36_VPBR_ATK_RATE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_WAIT_MASK, + vpbr_config->vpbr_wait << + CS35L36_VPBR_WAIT_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_REL_RATE_MASK, + vpbr_config->vpbr_rel_rate << + CS35L36_VPBR_REL_RATE_SHIFT); + regmap_update_bits(cs35l36->regmap, CS35L36_VPBR_CFG, + CS35L36_VPBR_MUTE_EN_MASK, + vpbr_config->vpbr_mute_en << + CS35L36_VPBR_MUTE_EN_SHIFT); +} + +static const struct reg_sequence cs35l36_reva0_errata_patch[] = { + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK2 }, + /* Errata Writes */ + { CS35L36_OTP_CTRL1, 0x00002060 }, + { CS35L36_OTP_CTRL2, 0x00000001 }, + { CS35L36_OTP_CTRL1, 0x00002460 }, + { CS35L36_OTP_CTRL2, 0x00000001 }, + { 0x00002088, 0x012A1838 }, + { 0x00003014, 0x0100EE0E }, + { 0x00003008, 0x0008184A }, + { 0x00007418, 0x509001C8 }, + { 0x00007064, 0x0929A800 }, + { 0x00002D10, 0x0002C01C }, + { 0x0000410C, 0x00000A11 }, + { 0x00006E08, 0x8B19140C }, + { 0x00006454, 0x0300000A }, + { CS35L36_AMP_NG_CTRL, 0x000020EF }, + { 0x00007E34, 0x0000000E }, + { 0x0000410C, 0x00000A11 }, + { 0x00007410, 0x20514B00 }, + /* PAC Config */ + { CS35L36_CTRL_OVRRIDE, 0x00000000 }, + { CS35L36_PAC_INT0_CTRL, 0x00860001 }, + { CS35L36_PAC_INT1_CTRL, 0x00860001 }, + { CS35L36_PAC_INT2_CTRL, 0x00860001 }, + { CS35L36_PAC_INT3_CTRL, 0x00860001 }, + { CS35L36_PAC_INT4_CTRL, 0x00860001 }, + { CS35L36_PAC_INT5_CTRL, 0x00860001 }, + { CS35L36_PAC_INT6_CTRL, 0x00860001 }, + { CS35L36_PAC_INT7_CTRL, 0x00860001 }, + { CS35L36_PAC_INT_FLUSH_CTRL, 0x000000FF }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 }, +}; + +static const struct reg_sequence cs35l36_revb0_errata_patch[] = { + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_UNLOCK2 }, + { 0x00007064, 0x0929A800 }, + { 0x00007850, 0x00002FA9 }, + { 0x00007854, 0x0003F1D5 }, + { 0x00007858, 0x0003F5E3 }, + { 0x0000785C, 0x00001137 }, + { 0x00007860, 0x0001A7A5 }, + { 0x00007864, 0x0002F16A }, + { 0x00007868, 0x00003E21 }, + { 0x00007848, 0x00000001 }, + { 0x00003854, 0x05180240 }, + { 0x00007418, 0x509001C8 }, + { 0x0000394C, 0x028764BD }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK1 }, + { CS35L36_TESTKEY_CTRL, CS35L36_TEST_LOCK2 }, +}; + +static int cs35l36_i2c_probe(struct i2c_client *i2c_client, + const struct i2c_device_id *id) +{ + struct cs35l36_private *cs35l36; + struct device *dev = &i2c_client->dev; + struct cs35l36_platform_data *pdata = dev_get_platdata(dev); + struct irq_data *irq_d; + int ret, irq_pol, chip_irq_pol, i; + u32 reg_id, reg_revid, l37_id_reg; + + cs35l36 = devm_kzalloc(dev, sizeof(struct cs35l36_private), GFP_KERNEL); + if (!cs35l36) + return -ENOMEM; + + cs35l36->dev = dev; + + i2c_set_clientdata(i2c_client, cs35l36); + cs35l36->regmap = devm_regmap_init_i2c(i2c_client, &cs35l36_regmap); + if (IS_ERR(cs35l36->regmap)) { + ret = PTR_ERR(cs35l36->regmap); + dev_err(dev, "regmap_init() failed: %d\n", ret); + goto err; + } + + cs35l36->num_supplies = ARRAY_SIZE(cs35l36_supplies); + for (i = 0; i < ARRAY_SIZE(cs35l36_supplies); i++) + cs35l36->supplies[i].supply = cs35l36_supplies[i]; + + ret = devm_regulator_bulk_get(dev, cs35l36->num_supplies, + cs35l36->supplies); + if (ret != 0) { + dev_err(dev, "Failed to request core supplies: %d\n", ret); + return ret; + } + + if (pdata) { + cs35l36->pdata = *pdata; + } else { + pdata = devm_kzalloc(dev, sizeof(struct cs35l36_platform_data), + GFP_KERNEL); + if (!pdata) + return -ENOMEM; + + if (i2c_client->dev.of_node) { + ret = cs35l36_handle_of_data(i2c_client, pdata); + if (ret != 0) + return ret; + + } + + cs35l36->pdata = *pdata; + } + + ret = regulator_bulk_enable(cs35l36->num_supplies, cs35l36->supplies); + if (ret != 0) { + dev_err(dev, "Failed to enable core supplies: %d\n", ret); + return ret; + } + + /* returning NULL can be an option if in stereo mode */ + cs35l36->reset_gpio = devm_gpiod_get_optional(dev, "reset", + GPIOD_OUT_LOW); + if (IS_ERR(cs35l36->reset_gpio)) { + ret = PTR_ERR(cs35l36->reset_gpio); + cs35l36->reset_gpio = NULL; + if (ret == -EBUSY) { + dev_info(dev, "Reset line busy, assuming shared reset\n"); + } else { + dev_err(dev, "Failed to get reset GPIO: %d\n", ret); + goto err_disable_regs; + } + } + + if (cs35l36->reset_gpio) + gpiod_set_value_cansleep(cs35l36->reset_gpio, 1); + + usleep_range(2000, 2100); + + /* initialize amplifier */ + ret = regmap_read(cs35l36->regmap, CS35L36_SW_RESET, &reg_id); + if (ret < 0) { + dev_err(dev, "Get Device ID failed %d\n", ret); + goto err; + } + + if (reg_id != CS35L36_CHIP_ID) { + dev_err(dev, "Device ID (%X). Expected ID %X\n", reg_id, + CS35L36_CHIP_ID); + ret = -ENODEV; + goto err; + } + + ret = regmap_read(cs35l36->regmap, CS35L36_REV_ID, &reg_revid); + if (ret < 0) { + dev_err(&i2c_client->dev, "Get Revision ID failed %d\n", ret); + goto err; + } + + cs35l36->rev_id = reg_revid >> 8; + + ret = regmap_read(cs35l36->regmap, CS35L36_OTP_MEM30, &l37_id_reg); + if (ret < 0) { + dev_err(&i2c_client->dev, "Failed to read otp_id Register %d\n", + ret); + return ret; + } + + if ((l37_id_reg & CS35L36_OTP_REV_MASK) == CS35L36_OTP_REV_L37) + cs35l36->chip_version = CS35L36_12V_L37; + else + cs35l36->chip_version = CS35L36_10V_L36; + + switch (cs35l36->rev_id) { + case CS35L36_REV_A0: + ret = regmap_register_patch(cs35l36->regmap, + cs35l36_reva0_errata_patch, + ARRAY_SIZE(cs35l36_reva0_errata_patch)); + if (ret < 0) { + dev_err(dev, "Failed to apply A0 errata patch %d\n", + ret); + goto err; + } + break; + case CS35L36_REV_B0: + ret = cs35l36_pac(cs35l36); + if (ret < 0) { + dev_err(dev, "Failed to Trim OTP %d\n", ret); + goto err; + } + + ret = regmap_register_patch(cs35l36->regmap, + cs35l36_revb0_errata_patch, + ARRAY_SIZE(cs35l36_revb0_errata_patch)); + if (ret < 0) { + dev_err(dev, "Failed to apply B0 errata patch %d\n", + ret); + goto err; + } + break; + } + + if (pdata->vpbr_config.is_present) + cs35l36_apply_vpbr_config(cs35l36); + + irq_d = irq_get_irq_data(i2c_client->irq); + if (!irq_d) { + dev_err(&i2c_client->dev, "Invalid IRQ: %d\n", i2c_client->irq); + ret = -ENODEV; + goto err; + } + + irq_pol = irqd_get_trigger_type(irq_d); + + switch (irq_pol) { + case IRQF_TRIGGER_FALLING: + case IRQF_TRIGGER_LOW: + chip_irq_pol = 0; + break; + case IRQF_TRIGGER_RISING: + case IRQF_TRIGGER_HIGH: + chip_irq_pol = 1; + break; + default: + dev_err(cs35l36->dev, "Invalid IRQ polarity: %d\n", irq_pol); + ret = -EINVAL; + goto err; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_POL_SEL_MASK, + chip_irq_pol << CS35L36_INT_POL_SEL_SHIFT); + + ret = devm_request_threaded_irq(dev, i2c_client->irq, NULL, cs35l36_irq, + IRQF_ONESHOT | irq_pol, "cs35l36", + cs35l36); + if (ret != 0) { + dev_err(dev, "Failed to request IRQ: %d\n", ret); + goto err; + } + + regmap_update_bits(cs35l36->regmap, CS35L36_PAD_INTERFACE, + CS35L36_INT_OUTPUT_EN_MASK, 1); + + /* Set interrupt masks for critical errors */ + regmap_write(cs35l36->regmap, CS35L36_INT1_MASK, + CS35L36_INT1_MASK_DEFAULT); + regmap_write(cs35l36->regmap, CS35L36_INT3_MASK, + CS35L36_INT3_MASK_DEFAULT); + + dev_info(&i2c_client->dev, "Cirrus Logic CS35L%d, Revision: %02X\n", + cs35l36->chip_version, reg_revid >> 8); + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_cs35l36, + cs35l36_dai, + ARRAY_SIZE(cs35l36_dai)); + if (ret < 0) { + dev_err(dev, "%s: Register component failed %d\n", __func__, + ret); + goto err; + } + + return 0; + +err: + gpiod_set_value_cansleep(cs35l36->reset_gpio, 0); + +err_disable_regs: + regulator_bulk_disable(cs35l36->num_supplies, cs35l36->supplies); + return ret; +} + +static int cs35l36_i2c_remove(struct i2c_client *client) +{ + struct cs35l36_private *cs35l36 = i2c_get_clientdata(client); + + /* Reset interrupt masks for device removal */ + regmap_write(cs35l36->regmap, CS35L36_INT1_MASK, + CS35L36_INT1_MASK_RESET); + regmap_write(cs35l36->regmap, CS35L36_INT3_MASK, + CS35L36_INT3_MASK_RESET); + + if (cs35l36->reset_gpio) + gpiod_set_value_cansleep(cs35l36->reset_gpio, 0); + + regulator_bulk_disable(cs35l36->num_supplies, cs35l36->supplies); + + return 0; +} +static const struct of_device_id cs35l36_of_match[] = { + {.compatible = "cirrus,cs35l36"}, + {}, +}; +MODULE_DEVICE_TABLE(of, cs35l36_of_match); + +static const struct i2c_device_id cs35l36_id[] = { + {"cs35l36", 0}, + {} +}; + +MODULE_DEVICE_TABLE(i2c, cs35l36_id); + +static struct i2c_driver cs35l36_i2c_driver = { + .driver = { + .name = "cs35l36", + .of_match_table = cs35l36_of_match, + }, + .id_table = cs35l36_id, + .probe = cs35l36_i2c_probe, + .remove = cs35l36_i2c_remove, +}; +module_i2c_driver(cs35l36_i2c_driver); + +MODULE_DESCRIPTION("ASoC CS35L36 driver"); +MODULE_AUTHOR("James Schulman, Cirrus Logic Inc, <james.schulman@cirrus.com>"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs35l36.h b/sound/soc/codecs/cs35l36.h @@ -0,0 +1,446 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * cs35l36.h -- CS35L36 ALSA SoC audio driver + * + * Copyright 2018 Cirrus Logic, Inc. + * + * Author: James Schulman <james.schulman@cirrus.com> + * + */ + +#ifndef __CS35L36_H__ +#define __CS35L36_H__ + +#include <linux/regmap.h> + +#define CS35L36_FIRSTREG 0x00000000 +#define CS35L36_LASTREG 0x00E037FC +#define CS35L36_SW_RESET 0x00000000 +#define CS35L36_SW_REV 0x00000004 +#define CS35L36_HW_REV 0x00000008 +#define CS35L36_TESTKEY_CTRL 0x00000020 +#define CS35L36_USERKEY_CTL 0x00000024 +#define CS35L36_OTP_MEM30 0x00000478 +#define CS35L36_OTP_CTRL1 0x00000500 +#define CS35L36_OTP_CTRL2 0x00000504 +#define CS35L36_OTP_CTRL3 0x00000508 +#define CS35L36_OTP_CTRL4 0x0000050C +#define CS35L36_OTP_CTRL5 0x00000510 +#define CS35L36_PAC_CTL1 0x00000C00 +#define CS35L36_PAC_CTL2 0x00000C04 +#define CS35L36_PAC_CTL3 0x00000C08 +#define CS35L36_DEVICE_ID 0x00002004 +#define CS35L36_FAB_ID 0x00002008 +#define CS35L36_REV_ID 0x0000200C +#define CS35L36_PWR_CTRL1 0x00002014 +#define CS35L36_PWR_CTRL2 0x00002018 +#define CS35L36_PWR_CTRL3 0x0000201C +#define CS35L36_CTRL_OVRRIDE 0x00002020 +#define CS35L36_AMP_OUT_MUTE 0x00002024 +#define CS35L36_OTP_TRIM_STATUS 0x00002028 +#define CS35L36_DISCH_FILT 0x0000202C +#define CS35L36_OSC_TRIM 0x00002030 +#define CS35L36_PROTECT_REL_ERR 0x00002034 +#define CS35L36_PAD_INTERFACE 0x00002400 +#define CS35L36_PLL_CLK_CTRL 0x00002C04 +#define CS35L36_GLOBAL_CLK_CTRL 0x00002C0C +#define CS35L36_ADC_CLK_CTRL 0x00002C10 +#define CS35L36_SWIRE_CLK_CTRL 0x00002C14 +#define CS35L36_SP_SCLK_CLK_CTRL 0x00002D00 +#define CS35L36_TST_FS_MON0 0x00002D10 +#define CS35L36_PLL_LOOP_PARAMS 0x00003008 +#define CS35L36_DCO_CTRL 0x00003010 +#define CS35L36_MISC_CTRL 0x00003014 +#define CS35L36_MDSYNC_EN 0x00003404 +#define CS35L36_MDSYNC_TX_ID 0x00003408 +#define CS35L36_MDSYNC_PWR_CTRL 0x0000340C +#define CS35L36_MDSYNC_DATA_TX 0x00003410 +#define CS35L36_MDSYNC_TX_STATUS 0x0000341C +#define CS35L36_MDSYNC_RX_STATUS 0x00003420 +#define CS35L36_MDSYNC_ERR_STATUS 0x00003424 +#define CS35L36_BSTCVRT_VCTRL1 0x00003800 +#define CS35L36_BSTCVRT_VCTRL2 0x00003804 +#define CS35L36_BSTCVRT_PEAK_CUR 0x00003808 +#define CS35L36_BSTCVRT_SFT_RAMP 0x0000380C +#define CS35L36_BSTCVRT_COEFF 0x00003810 +#define CS35L36_BSTCVRT_SLOPE_LBST 0x00003814 +#define CS35L36_BSTCVRT_SW_FREQ 0x00003818 +#define CS35L36_BSTCVRT_DCM_CTRL 0x0000381C +#define CS35L36_BSTCVRT_DCM_MODE_FORCE 0x00003820 +#define CS35L36_BSTCVRT_OVERVOLT_CTRL 0x00003830 +#define CS35L36_BST_TST_MANUAL 0x0000393C +#define CS35L36_BST_ANA2_TEST 0x0000394C +#define CS35L36_VPI_LIMIT_MODE 0x00003C04 +#define CS35L36_VPI_LIMIT_MINMAX 0x00003C08 +#define CS35L36_VPI_VP_THLD 0x00003C0C +#define CS35L36_VPI_TRACK_CTRL 0x00003C10 +#define CS35L36_VPI_TRIG_MODE_CTRL 0x00003C14 +#define CS35L36_VPI_TRIG_STEPS 0x00003C18 +#define CS35L36_VI_SPKMON_FILT 0x00004004 +#define CS35L36_VI_SPKMON_GAIN 0x00004008 +#define CS35L36_VI_SPKMON_IP_SEL 0x00004100 +#define CS35L36_DTEMP_WARN_THLD 0x00004220 +#define CS35L36_DTEMP_STATUS 0x00004300 +#define CS35L36_VPVBST_FS_SEL 0x00004400 +#define CS35L36_VPVBST_VP_CTRL 0x00004440 +#define CS35L36_VPVBST_VBST_CTRL 0x00004444 +#define CS35L36_ASP_TX_PIN_CTRL 0x00004800 +#define CS35L36_ASP_RATE_CTRL 0x00004804 +#define CS35L36_ASP_FORMAT 0x00004808 +#define CS35L36_ASP_FRAME_CTRL 0x00004818 +#define CS35L36_ASP_TX1_TX2_SLOT 0x0000481C +#define CS35L36_ASP_TX3_TX4_SLOT 0x00004820 +#define CS35L36_ASP_TX5_TX6_SLOT 0x00004824 +#define CS35L36_ASP_TX7_TX8_SLOT 0x00004828 +#define CS35L36_ASP_RX1_SLOT 0x0000482C +#define CS35L36_ASP_RX_TX_EN 0x0000483C +#define CS35L36_ASP_RX1_SEL 0x00004C00 +#define CS35L36_ASP_TX1_SEL 0x00004C20 +#define CS35L36_ASP_TX2_SEL 0x00004C24 +#define CS35L36_ASP_TX3_SEL 0x00004C28 +#define CS35L36_ASP_TX4_SEL 0x00004C2C +#define CS35L36_ASP_TX5_SEL 0x00004C30 +#define CS35L36_ASP_TX6_SEL 0x00004C34 +#define CS35L36_SWIRE_P1_TX1_SEL 0x00004C40 +#define CS35L36_SWIRE_P1_TX2_SEL 0x00004C44 +#define CS35L36_SWIRE_P2_TX1_SEL 0x00004C60 +#define CS35L36_SWIRE_P2_TX2_SEL 0x00004C64 +#define CS35L36_SWIRE_P2_TX3_SEL 0x00004C68 +#define CS35L36_SWIRE_DP1_FIFO_CFG 0x00005000 +#define CS35L36_SWIRE_DP2_FIFO_CFG 0x00005004 +#define CS35L36_SWIRE_DP3_FIFO_CFG 0x00005008 +#define CS35L36_SWIRE_PCM_RX_DATA 0x0000500C +#define CS35L36_SWIRE_FS_SEL 0x00005010 +#define CS35L36_SPARE_CP_BITS 0x00005C00 +#define CS35L36_AMP_DIG_VOL_CTRL 0x00006000 +#define CS35L36_VPBR_CFG 0x00006404 +#define CS35L36_VBBR_CFG 0x00006408 +#define CS35L36_VPBR_STATUS 0x0000640C +#define CS35L36_VBBR_STATUS 0x00006410 +#define CS35L36_OVERTEMP_CFG 0x00006414 +#define CS35L36_AMP_ERR_VOL 0x00006418 +#define CS35L36_CLASSH_CFG 0x00006800 +#define CS35L36_CLASSH_FET_DRV_CFG 0x00006804 +#define CS35L36_NG_CFG 0x00006808 +#define CS35L36_AMP_GAIN_CTRL 0x00006C04 +#define CS35L36_PWM_MOD_IO_CTRL 0x0000706C +#define CS35L36_PWM_MOD_STATUS 0x00007070 +#define CS35L36_DAC_MSM_CFG 0x00007400 +#define CS35L36_AMP_SLOPE_CTRL 0x00007410 +#define CS35L36_AMP_PDM_VOLUME 0x00007E04 +#define CS35L36_AMP_PDM_RATE_CTRL 0x00007E08 +#define CS35L36_PDM_CH_SEL 0x00007E10 +#define CS35L36_AMP_NG_CTRL 0x00007E14 +#define CS35L36_PDM_HIGHFILT_CTRL 0x00007E3C +#define CS35L36_INT1_STATUS 0x00D00000 +#define CS35L36_INT2_STATUS 0x00D00004 +#define CS35L36_INT3_STATUS 0x00D00008 +#define CS35L36_INT4_STATUS 0x00D0000C +#define CS35L36_INT1_RAW_STATUS 0x00D00020 +#define CS35L36_INT2_RAW_STATUS 0x00D00024 +#define CS35L36_INT3_RAW_STATUS 0x00D00028 +#define CS35L36_INT4_RAW_STATUS 0x00D0002C +#define CS35L36_INT1_MASK 0x00D00040 +#define CS35L36_INT2_MASK 0x00D00044 +#define CS35L36_INT3_MASK 0x00D00048 +#define CS35L36_INT4_MASK 0x00D0004C +#define CS35L36_INT1_EDGE_LVL_CTRL 0x00D00060 +#define CS35L36_INT3_EDGE_LVL_CTRL 0x00D00068 +#define CS35L36_PAC_INT_STATUS 0x00D00200 +#define CS35L36_PAC_INT_RAW_STATUS 0x00D00210 +#define CS35L36_PAC_INT_FLUSH_CTRL 0x00D00218 +#define CS35L36_PAC_INT0_CTRL 0x00D00220 +#define CS35L36_PAC_INT1_CTRL 0x00D00224 +#define CS35L36_PAC_INT2_CTRL 0x00D00228 +#define CS35L36_PAC_INT3_CTRL 0x00D0022C +#define CS35L36_PAC_INT4_CTRL 0x00D00230 +#define CS35L36_PAC_INT5_CTRL 0x00D00234 +#define CS35L36_PAC_INT6_CTRL 0x00D00238 +#define CS35L36_PAC_INT7_CTRL 0x00D0023C +#define CS35L36_PAC_PMEM_WORD0 0x00E02800 +#define CS35L36_PAC_PMEM_WORD1 0x00E02804 +#define CS35L36_PAC_PMEM_WORD1023 0x00E037FC + +#define CS35L36_INTPAC_REG_COUNT 25 +#define CS35L36_CHIP_ID 0x00035A36 + +#define CS35L36_INT_OUTPUT_EN_MASK 0x01 +#define CS35L36_INT_GPIO_SEL_MASK 0x02 +#define CS35L36_INT_GPIO_SEL_SHIFT 1 +#define CS35L36_INT_POL_SEL_MASK 0x04 +#define CS35L36_INT_POL_SEL_SHIFT 2 +#define CS35L36_INT_DRV_SEL_MASK 0x20 +#define CS35L36_INT_DRV_SEL_SHIFT 5 +#define CS35L36_IRQ_SRC_MASK 0x08 +#define CS35L36_IRQ_SRC_SHIFT 3 + +#define CS35L36_SCLK_MSTR_MASK 0x40 +#define CS35L36_SCLK_MSTR_SHIFT 6 +#define CS35L36_LRCLK_MSTR_MASK 0x01 +#define CS35L36_LRCLK_MSTR_SHIFT 0 +#define CS35L36_SCLK_INV_MASK 0x100 +#define CS35L36_SCLK_INV_SHIFT 8 +#define CS35L36_LRCLK_INV_MASK 0x04 +#define CS35L36_LRCLK_INV_SHIFT 2 +#define CS35L36_SCLK_FRC_MASK 0x80 +#define CS35L36_SCLK_FRC_SHIFT 7 +#define CS35L36_LRCLK_FRC_MASK 0x02 +#define CS35L36_LRCLK_FRC_SHIFT 1 + +#define CS35L36_PDM_MODE_MASK 0x01 +#define CS35L36_PDM_MODE_SHIFT 0 + +#define CS35L36_ASP_FMT_MASK 0x07 +#define CS35L36_ASP_FMT_SHIFT 0 + +#define CS35L36_ASP_RX_WIDTH_MASK 0xFF0000 +#define CS35L36_ASP_RX_WIDTH_SHIFT 16 +#define CS35L36_ASP_TX_WIDTH_MASK 0xFF +#define CS35L36_ASP_TX_WIDTH_SHIFT 0 +#define CS35L36_ASP_WIDTH_16 0x10 +#define CS35L36_ASP_WIDTH_24 0x18 +#define CS35L36_ASP_WIDTH_32 0x20 + +#define CS35L36_ASP_RX1_SLOT_MASK 0x3F +#define CS35L36_ASP_RX1_EN_MASK 0x00010000 +#define CS35L36_ASP_RX1_EN_SHIFT 16 + +#define CS35L36_ASP_TX1_SLOT_MASK 0x3F +#define CS35L36_ASP_TX2_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX2_SLOT_SHIFT 16 +#define CS35L36_ASP_TX3_SLOT_MASK 0x3F +#define CS35L36_ASP_TX4_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX4_SLOT_SHIFT 16 +#define CS35L36_ASP_TX5_SLOT_MASK 0x3F +#define CS35L36_ASP_TX6_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX6_SLOT_SHIFT 16 +#define CS35L36_ASP_TX7_SLOT_MASK 0x3F +#define CS35L36_ASP_TX8_SLOT_MASK 0x3F0000 +#define CS35L36_ASP_TX8_SLOT_SHIFT 16 +#define CS35L36_ASP_TX_HIZ_MASK 0x200000 + +#define CS35L36_APS_TX_SEL_MASK 0x7F + +#define CS35L36_ASP_TX1_EN_MASK 0x01 +#define CS35L36_ASP_TX2_EN_MASK 0x02 +#define CS35L36_ASP_TX2_EN_SHIFT 1 +#define CS35L36_ASP_TX3_EN_MASK 0x04 +#define CS35L36_ASP_TX3_EN_SHIFT 2 +#define CS35L36_ASP_TX4_EN_MASK 0x08 +#define CS35L36_ASP_TX4_EN_SHIFT 3 +#define CS35L36_ASP_TX5_EN_MASK 0x10 +#define CS35L36_ASP_TX5_EN_SHIFT 4 +#define CS35L36_ASP_TX6_EN_MASK 0x20 +#define CS35L36_ASP_TX6_EN_SHIFT 5 +#define CS35L36_ASP_TX7_EN_MASK 0x40 +#define CS35L36_ASP_TX7_EN_SHIFT 6 +#define CS35L36_ASP_TX8_EN_MASK 0x80 +#define CS35L36_ASP_TX8_EN_SHIFT 7 + + +#define CS35L36_PLL_CLK_SEL_MASK 0x07 +#define CS35L36_PLL_CLK_SEL_SHIFT 0 +#define CS35L36_PLLSRC_SCLK 0 +#define CS35L36_PLLSRC_LRCLK 1 +#define CS35L36_PLLSRC_SELF 3 +#define CS35L36_PLLSRC_PDMCLK 4 +#define CS35L36_PLLSRC_MCLK 5 +#define CS35L36_PLLSRC_SWIRE 7 +#define CS35L36_REFCLK_FREQ_MASK 0x7E0 +#define CS35L36_REFCLK_FREQ_SHIFT 5 +#define CS35L36_PLL_OPENLOOP_MASK 0x800 +#define CS35L36_PLL_OPENLOOP_SHIFT 11 +#define CS35L36_PLL_REFCLK_EN_MASK 0x10 +#define CS35L36_PLL_REFCLK_EN_SHIFT 4 + + +#define CS35L36_GLOBAL_FS_MASK 0x1F +#define CS35L36_GLOBAL_FS_SHIFT 0 + +#define CS35L36_HPF_PCM_EN_MASK 0x800 +#define CS35L36_HPF_PCM_EN_SHIFT 15 +#define CS35L36_PCM_RX_SEL_MASK 0x7F +#define CS35L36_PCM_RX_SEL_SHIFT 0 + +#define CS35L36_PCM_RX_SEL_ZERO 0x00 +#define CS35L36_PCM_RX_SEL_PCM 0x08 +#define CS35L36_PCM_RX_SEL_SWIRE 0x10 +#define CS35L36_PCM_RX_SEL_DIAG 0x04 + +#define CS35L36_GLOBAL_EN_MASK 0x01 +#define CS35L36_GLOBAL_EN_SHIFT 0x00 + +#define CS35L36_AMP_PCM_INV_MASK 0x4000 +#define CS35L36_AMP_PCM_INV_SHIFT 14 + +#define CS35L36_AMP_VOL_PCM_MASK 0x3FF8 +#define CS35L36_AMP_VOL_PCM_SHIFT 3 +#define CS35L36_DIGITAL_MUTE 0x04CF + +#define CS35L36_AMP_RAMP_MASK 0x0007 +#define CS35L36_AMP_RAMP_SHIFT 0 + +#define CS35L36_AMP_MUTE_MASK 0x0010 +#define CS35L36_AMP_MUTE_SHIFT 4 + +#define CS35L36_GLOBAL_RESYNC_FS1_MASK 0x00000200 +#define CS35L36_GLOBAL_RESYNC_FS2_MASK 0x00000400 +#define CS35L36_SYNC_GLOBAL_OVR_MASK 0x00000002 +#define CS35L36_SYNC_GLOBAL_OVR_SHIFT 1 + +#define CS35L36_REFCLK_IN_MASK 0x00100000 +#define CS35L36_PLL_UNLOCK_MASK 0x00002000 + +#define CS35L36_ASP_RX_UDF_MASK 0x00000040 +#define CS35L36_ASP_RX_OVF_MASK 0x00000080 + +#define CS35L36_IMON_POL_MASK 0x02 +#define CS35L36_IMON_POL_SHIFT 1 + +#define CS35L36_VMON_POL_MASK 0x01 +#define CS35L36_VMON_POL_SHIFT 0 + +#define CS35L36_PDN_DONE 0x40 +#define CS35L36_PDN_DONE_SHIFT 6 +#define CS35L36_PUP_DONE 0x80 +#define CS35L36_PUP_DONE_SHIFT 7 +#define CS35L36_GLOBAL_EN_ASSRT 0x20 +#define CS35L36_PUP_DONE_IRQ_UNMASK 0x7F +#define CS35L36_PUP_DONE_IRQ_MASK 0xBF + +#define CS35L36_FS1_WINDOW_MASK 0x000007FF +#define CS35L36_FS2_WINDOW_MASK 0x00FFF800 +#define CS35L36_FS2_WINDOW_SHIFT 12 + +#define CS35L36_PLL_FFL_IGAIN_MASK 0x0F +#define CS35L36_PLL_IGAIN_MASK 0x3F0 +#define CS35L36_PLL_IGAIN_SHIFT 4 +#define CS35L36_PLL_IGAIN 0x04 + +#define CS35L36_BST_EN_MASK 0x30 +#define CS35L36_BST_EN 0x02 +#define CS35L36_BST_DIS_VP 0x01 +#define CS35L36_BST_DIS_EXTN 0x00 +#define CS35L36_BST_EN_SHIFT 4 +#define CS35L36_BST_MAN_IPKCOMP_MASK 0x200 +#define CS35L36_BST_MAN_IPKCOMP_SHIFT 9 + +#define CS35L36_BST_MAN_IPKCOMP_EN_MASK 0x100 +#define CS35L36_BST_MAN_IPKCOMP_EN_SHIFT 8 + +#define CS35L36_BST_IPK_MASK 0x7F +#define CS35L36_BST_OVP_THLD_MASK 0x3F +#define CS35L36_BST_OVP_THLD_11V 0x10 +#define CS35L36_BST_OVP_TRIM_MASK 0x00078000 +#define CS35L36_BST_OVP_TRIM_SHIFT 15 +#define CS35L36_BST_OVP_TRIM_11V 0x0C +#define CS35L36_BST_CTRL_LIM_MASK 0x04 +#define CS35L36_BST_CTRL_LIM_SHIFT 2 +#define CS35L36_BST_CTRL_10V_CLAMP 0x96 + +#define CS35L36_NG_AMP_EN_MASK 0x3F00 +#define CS35L36_NG_DELAY_MASK 0x70 +#define CS35L36_NG_DELAY_SHIFT 4 +#define CS35L36_AMP_ZC_SHIFT 10 +#define CS35L36_PDM_LDM_ENTER_SHIFT 3 +#define CS35L36_PDM_LDM_EXIT_SHIFT 4 + +#define CS35L36_BSTCVRT_K1_MASK 0xFF +#define CS35L36_BSTCVRT_K2_MASK 0xFF00 +#define CS35L36_BSTCVRT_K2_SHIFT 8 +#define CS35L36_BSTCVRT_SLOPE_MASK 0xFF00 +#define CS35L36_BSTCVRT_SLOPE_SHIFT 8 +#define CS35L36_BSTCVRT_CCMFREQ_MASK 0x0F +#define CS35L36_BSTCVRT_LBSTVAL_MASK 0x03 +#define CS35L35_BSTCVRT_CTL_MASK 0xFF +#define CS35L35_BSTCVRT_CTL_SEL_MASK 0x03 +#define CS35L36_DCM_AUTO_MASK 0x01 + +#define CS35L36_INT1_MASK_DEFAULT 0xF9BA7FFF +#define CS35L36_INT1_MASK_RESET 0xFFFFFFFF +#define CS35L36_INT3_MASK_DEFAULT 0xFFFFEFFF +#define CS35L36_INT3_MASK_RESET 0xFFFFFFFF + + +#define CS35L36_AMP_SHORT_ERR 0x1000 +#define CS35L36_BST_SHORT_ERR 0x40000 +#define CS35L36_TEMP_WARN 0x2000000 +#define CS35L36_TEMP_ERR 0x4000000 +#define CS35L36_BST_OVP_ERR 0x10000 +#define CS35L36_BST_DCM_UVP_ERR 0x20000 + +#define CS35L36_AMP_SHORT_ERR_RLS 0x02 +#define CS35L36_BST_SHORT_ERR_RLS 0x04 +#define CS35L36_BST_OVP_ERR_RLS 0x08 +#define CS35L36_BST_UVP_ERR_RLS 0x10 +#define CS35L36_TEMP_WARN_ERR_RLS 0x20 +#define CS35L36_TEMP_ERR_RLS 0x40 +#define CS35L36_TEMP_THLD_MASK 0x03 + +#define CS35L36_REV_B0 0xb0 +#define CS35L36_REV_A0 0xa0 +#define CS35L36_B0_PAC_PATCH 0x00DD0102 + +#define CS35L36_OTP_ECC_EN_MASK 0x400 +#define CS35L36_OTP_ECC_EN_SHIFT 10 +#define CS35L36_OTP_RUN_BOOT_MASK 0x01 +#define CS35L36_OTP_BOOT_DONE 0x2000000 +#define CS35L36_PAC_RESET_MASK 0x04 +#define CS35L36_PAC_RESET_SHIFT 2 +#define CS35L36_PAC_STALL_MASK 0x02 +#define CS35L36_PAC_STALL_SHIFT 1 +#define CS35L36_PAC_ENABLE_MASK 0x00000001 +#define CS35L36_PAC_MEM_ACCESS 0x01 +#define CS35L36_PAC_MEM_ACCESS_CLR 0 +#define CS35L36_SOFT_RESET 0x5AAA +#define CS35L36_MCU_BOOT_COMPLETE 0x02 +#define CS35L36_MCU_CONFIG_UNMASK 0x00FEFFFF +#define CS35L36_MCU_CONFIG_CLR 0x00010000 +#define CS35L36_MCU_CONFIG_MASK 0x00FFFFFF +#define CS35L36_GPIO_INT_SEL_MASK 0x0000003B +#define CS35L36_GPIO_INT_SEL_UNMASK 0x0000003A +#define CS35L36_PAC_RESET 0x00000000 +#define CS35L36_OTP_REV_MASK 0x00FF0000 +#define CS35L36_OTP_REV_L37 0x00CC0000 +#define CS35L36_12V_L37 37 +#define CS35L36_10V_L36 36 + +#define CS35L36_VPBR_EN_MASK 0x00001000 +#define CS35L36_VPBR_EN_SHIFT 12 + +#define CS35L36_VPBR_THLD_MASK 0x0000001F +#define CS35L36_VPBR_THLD_SHIFT 0 +#define CS35L36_VPBR_MAX_ATTN_MASK 0x00000F00 +#define CS35L36_VPBR_MAX_ATTN_SHIFT 8 +#define CS35L36_VPBR_ATK_VOL_MASK 0x0000F000 +#define CS35L36_VPBR_ATK_VOL_SHIFT 12 +#define CS35L36_VPBR_ATK_RATE_MASK 0x00070000 +#define CS35L36_VPBR_ATK_RATE_SHIFT 16 +#define CS35L36_VPBR_WAIT_MASK 0x00180000 +#define CS35L36_VPBR_WAIT_SHIFT 19 +#define CS35L36_VPBR_REL_RATE_MASK 0x00E00000 +#define CS35L36_VPBR_REL_RATE_SHIFT 21 +#define CS35L36_VPBR_MUTE_EN_MASK 0x01000000 +#define CS35L36_VPBR_MUTE_EN_SHIFT 24 + +#define CS35L36_OSC_FREQ_TRIM_MASK 0x070 +#define CS35L36_OSC_TRIM_DONE 0x08 + +#define CS35L36_FS1_DEFAULT_VAL 16 +#define CS35L36_FS2_DEFAULT_VAL 36 +#define CS35L36_FS_NOM_6MHZ 6000000 + +#define CS35L36_TEST_UNLOCK1 0x00005555 +#define CS35L36_TEST_UNLOCK2 0x0000AAAA +#define CS35L36_TEST_LOCK1 0x0000CCCC +#define CS35L36_TEST_LOCK2 0x00003333 + +#define CS35L36_PAC_PROG_MEM 512 + +#define CS35L36_RX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE) +#define CS35L36_TX_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE \ + | SNDRV_PCM_FMTBIT_S32_LE) + +extern const int cs35l36_a0_pac_patch[CS35L36_PAC_PROG_MEM]; + +#endif diff --git a/sound/soc/codecs/cs4271.c b/sound/soc/codecs/cs4271.c @@ -223,10 +223,10 @@ static int cs4271_set_dai_fmt(struct snd_soc_dai *codec_dai, switch (format & SND_SOC_DAIFMT_MASTER_MASK) { case SND_SOC_DAIFMT_CBS_CFS: - cs4271->master = 0; + cs4271->master = false; break; case SND_SOC_DAIFMT_CBM_CFM: - cs4271->master = 1; + cs4271->master = true; val |= CS4271_MODE1_MASTER; break; default: diff --git a/sound/soc/codecs/cs4341.c b/sound/soc/codecs/cs4341.c @@ -0,0 +1,346 @@ +/* SPDX-License-Identifier: GPL-2.0+ */ +/* + * Cirrus Logic CS4341A ALSA SoC Codec Driver + * Author: Alexander Shiyan <shc_work@mail.ru> + */ + +#include <linux/i2c.h> +#include <linux/module.h> +#include <linux/of.h> +#include <linux/regmap.h> +#include <linux/spi/spi.h> + +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#define CS4341_REG_MODE1 0x00 +#define CS4341_REG_MODE2 0x01 +#define CS4341_REG_MIX 0x02 +#define CS4341_REG_VOLA 0x03 +#define CS4341_REG_VOLB 0x04 + +#define CS4341_MODE2_DIF (7 << 4) +#define CS4341_MODE2_DIF_I2S_24 (0 << 4) +#define CS4341_MODE2_DIF_I2S_16 (1 << 4) +#define CS4341_MODE2_DIF_LJ_24 (2 << 4) +#define CS4341_MODE2_DIF_RJ_24 (3 << 4) +#define CS4341_MODE2_DIF_RJ_16 (5 << 4) +#define CS4341_VOLX_MUTE (1 << 7) + +struct cs4341_priv { + unsigned int fmt; + struct regmap *regmap; + struct regmap_config regcfg; +}; + +static const struct reg_default cs4341_reg_defaults[] = { + { CS4341_REG_MODE1, 0x00 }, + { CS4341_REG_MODE2, 0x82 }, + { CS4341_REG_MIX, 0x49 }, + { CS4341_REG_VOLA, 0x80 }, + { CS4341_REG_VOLB, 0x80 }, +}; + +static int cs4341_set_fmt(struct snd_soc_dai *dai, unsigned int format) +{ + struct snd_soc_component *component = dai->component; + struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component); + + switch (format & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBS_CFS: + break; + default: + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + default: + return -EINVAL; + } + + switch (format & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + case SND_SOC_DAIFMT_LEFT_J: + case SND_SOC_DAIFMT_RIGHT_J: + cs4341->fmt = format & SND_SOC_DAIFMT_FORMAT_MASK; + break; + default: + return -EINVAL; + } + + return 0; +} + +static int cs4341_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct cs4341_priv *cs4341 = snd_soc_component_get_drvdata(component); + unsigned int mode = 0; + int b24 = 0; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S24_LE: + b24 = 1; + break; + case SNDRV_PCM_FORMAT_S16_LE: + break; + default: + dev_err(component->dev, "Unsupported PCM format 0x%08x.\n", + params_format(params)); + return -EINVAL; + } + + switch (cs4341->fmt) { + case SND_SOC_DAIFMT_I2S: + mode = b24 ? CS4341_MODE2_DIF_I2S_24 : CS4341_MODE2_DIF_I2S_16; + break; + case SND_SOC_DAIFMT_LEFT_J: + mode = CS4341_MODE2_DIF_LJ_24; + break; + case SND_SOC_DAIFMT_RIGHT_J: + mode = b24 ? CS4341_MODE2_DIF_RJ_24 : CS4341_MODE2_DIF_RJ_16; + break; + default: + dev_err(component->dev, "Unsupported DAI format 0x%08x.\n", + cs4341->fmt); + return -EINVAL; + } + + return snd_soc_component_update_bits(component, CS4341_REG_MODE2, + CS4341_MODE2_DIF, mode); +} + +static int cs4341_digital_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_component *component = dai->component; + int ret; + + ret = snd_soc_component_update_bits(component, CS4341_REG_VOLA, + CS4341_VOLX_MUTE, + mute ? CS4341_VOLX_MUTE : 0); + if (ret < 0) + return ret; + + return snd_soc_component_update_bits(component, CS4341_REG_VOLB, + CS4341_VOLX_MUTE, + mute ? CS4341_VOLX_MUTE : 0); +} + +static DECLARE_TLV_DB_SCALE(out_tlv, -9000, 100, 0); + +static const char * const deemph[] = { + "None", "44.1k", "48k", "32k", +}; + +static const struct soc_enum deemph_enum = + SOC_ENUM_SINGLE(CS4341_REG_MODE2, 2, 4, deemph); + +static const char * const srzc[] = { + "Immediate", "Zero Cross", "Soft Ramp", "SR on ZC", +}; + +static const struct soc_enum srzc_enum = + SOC_ENUM_SINGLE(CS4341_REG_MIX, 5, 4, srzc); + + +static const struct snd_soc_dapm_widget cs4341_dapm_widgets[] = { + SND_SOC_DAPM_DAC("HiFi DAC", NULL, SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_OUTPUT("OutA"), + SND_SOC_DAPM_OUTPUT("OutB"), +}; + +static const struct snd_soc_dapm_route cs4341_routes[] = { + { "OutA", NULL, "HiFi DAC" }, + { "OutB", NULL, "HiFi DAC" }, + { "DAC Playback", NULL, "OutA" }, + { "DAC Playback", NULL, "OutB" }, +}; + +static const struct snd_kcontrol_new cs4341_controls[] = { + SOC_DOUBLE_R_TLV("Master Playback Volume", + CS4341_REG_VOLA, CS4341_REG_VOLB, 0, 90, 1, out_tlv), + SOC_ENUM("De-Emphasis Control", deemph_enum), + SOC_ENUM("Soft Ramp Zero Cross Control", srzc_enum), + SOC_SINGLE("Auto-Mute Switch", CS4341_REG_MODE2, 7, 1, 0), + SOC_SINGLE("Popguard Transient Switch", CS4341_REG_MODE2, 1, 1, 0), +}; + +static const struct snd_soc_dai_ops cs4341_dai_ops = { + .set_fmt = cs4341_set_fmt, + .hw_params = cs4341_hw_params, + .digital_mute = cs4341_digital_mute, +}; + +static struct snd_soc_dai_driver cs4341_dai = { + .name = "cs4341a-hifi", + .playback = { + .stream_name = "DAC Playback", + .channels_min = 1, + .channels_max = 2, + .rates = SNDRV_PCM_RATE_8000_96000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | + SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &cs4341_dai_ops, + .symmetric_rates = 1, +}; + +static const struct snd_soc_component_driver soc_component_cs4341 = { + .controls = cs4341_controls, + .num_controls = ARRAY_SIZE(cs4341_controls), + .dapm_widgets = cs4341_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(cs4341_dapm_widgets), + .dapm_routes = cs4341_routes, + .num_dapm_routes = ARRAY_SIZE(cs4341_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct of_device_id __maybe_unused cs4341_dt_ids[] = { + { .compatible = "cirrus,cs4341a", }, + { } +}; +MODULE_DEVICE_TABLE(of, cs4341_dt_ids); + +static int cs4341_probe(struct device *dev) +{ + struct cs4341_priv *cs4341 = dev_get_drvdata(dev); + int i; + + for (i = 0; i < ARRAY_SIZE(cs4341_reg_defaults); i++) + regmap_write(cs4341->regmap, cs4341_reg_defaults[i].reg, + cs4341_reg_defaults[i].def); + + return devm_snd_soc_register_component(dev, &soc_component_cs4341, + &cs4341_dai, 1); +} + +#if IS_ENABLED(CONFIG_I2C) +static int cs4341_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct cs4341_priv *cs4341; + + cs4341 = devm_kzalloc(&i2c->dev, sizeof(*cs4341), GFP_KERNEL); + if (!cs4341) + return -ENOMEM; + + i2c_set_clientdata(i2c, cs4341); + + cs4341->regcfg.reg_bits = 8; + cs4341->regcfg.val_bits = 8; + cs4341->regcfg.max_register = CS4341_REG_VOLB; + cs4341->regcfg.cache_type = REGCACHE_FLAT; + cs4341->regcfg.reg_defaults = cs4341_reg_defaults; + cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults); + cs4341->regmap = devm_regmap_init_i2c(i2c, &cs4341->regcfg); + if (IS_ERR(cs4341->regmap)) + return PTR_ERR(cs4341->regmap); + + return cs4341_probe(&i2c->dev); +} + +static const struct i2c_device_id cs4341_i2c_id[] = { + { "cs4341", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, cs4341_i2c_id); + +static struct i2c_driver cs4341_i2c_driver = { + .driver = { + .name = "cs4341-i2c", + .of_match_table = of_match_ptr(cs4341_dt_ids), + }, + .probe = cs4341_i2c_probe, + .id_table = cs4341_i2c_id, +}; +#endif + +#if defined(CONFIG_SPI_MASTER) +static bool cs4341_reg_readable(struct device *dev, unsigned int reg) +{ + return false; +} + +static int cs4341_spi_probe(struct spi_device *spi) +{ + struct cs4341_priv *cs4341; + int ret; + + cs4341 = devm_kzalloc(&spi->dev, sizeof(*cs4341), GFP_KERNEL); + if (!cs4341) + return -ENOMEM; + + if (!spi->bits_per_word) + spi->bits_per_word = 8; + if (!spi->max_speed_hz) + spi->max_speed_hz = 6000000; + ret = spi_setup(spi); + if (ret) + return ret; + + spi_set_drvdata(spi, cs4341); + + cs4341->regcfg.reg_bits = 16; + cs4341->regcfg.val_bits = 8; + cs4341->regcfg.write_flag_mask = 0x20; + cs4341->regcfg.max_register = CS4341_REG_VOLB; + cs4341->regcfg.cache_type = REGCACHE_FLAT; + cs4341->regcfg.readable_reg = cs4341_reg_readable; + cs4341->regcfg.reg_defaults = cs4341_reg_defaults; + cs4341->regcfg.num_reg_defaults = ARRAY_SIZE(cs4341_reg_defaults); + cs4341->regmap = devm_regmap_init_spi(spi, &cs4341->regcfg); + if (IS_ERR(cs4341->regmap)) + return PTR_ERR(cs4341->regmap); + + return cs4341_probe(&spi->dev); +} + +static struct spi_driver cs4341_spi_driver = { + .driver = { + .name = "cs4341-spi", + .of_match_table = of_match_ptr(cs4341_dt_ids), + }, + .probe = cs4341_spi_probe, +}; +#endif + +static int __init cs4341_init(void) +{ + int ret = 0; + +#if IS_ENABLED(CONFIG_I2C) + ret = i2c_add_driver(&cs4341_i2c_driver); + if (ret) + return ret; +#endif +#if defined(CONFIG_SPI_MASTER) + ret = spi_register_driver(&cs4341_spi_driver); +#endif + + return ret; +} +module_init(cs4341_init); + +static void __exit cs4341_exit(void) +{ +#if IS_ENABLED(CONFIG_I2C) + i2c_del_driver(&cs4341_i2c_driver); +#endif +#if defined(CONFIG_SPI_MASTER) + spi_unregister_driver(&cs4341_spi_driver); +#endif +} +module_exit(cs4341_exit); + +MODULE_AUTHOR("Alexander Shiyan <shc_work@mail.ru>"); +MODULE_DESCRIPTION("Cirrus Logic CS4341 ALSA SoC Codec Driver"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/codecs/cs47l24.c b/sound/soc/codecs/cs47l24.c @@ -500,72 +500,72 @@ SND_SOC_DAPM_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1, SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 1, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 2, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 3, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 4, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 5, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX7", NULL, 6, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 7, ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 1, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 2, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 3, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 4, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 5, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX7", NULL, 6, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 7, ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 1, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 2, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 3, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 4, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 5, ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 1, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 2, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 3, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 4, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 5, ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0, ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0, +SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 1, ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0), SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0, ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0), -SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0, +SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 1, ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0), SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM, diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c @@ -117,7 +117,7 @@ static void da7219_aad_hptest_work(struct work_struct *work) struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - u16 tonegen_freq_hptest; + __le16 tonegen_freq_hptest; u8 pll_srm_sts, pll_ctrl, gain_ramp_ctrl, accdet_cfg8; int report = 0, ret = 0; diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c @@ -423,7 +423,7 @@ static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol, struct soc_mixer_control *mixer_ctrl = (struct soc_mixer_control *) kcontrol->private_value; unsigned int reg = mixer_ctrl->reg; - u16 val; + __le16 val; int ret; mutex_lock(&da7219->ctrl_lock); @@ -450,7 +450,7 @@ static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol, struct soc_mixer_control *mixer_ctrl = (struct soc_mixer_control *) kcontrol->private_value; unsigned int reg = mixer_ctrl->reg; - u16 val; + __le16 val; int ret; /* @@ -838,7 +838,7 @@ static int da7219_dai_event(struct snd_soc_dapm_widget *w, ++i; msleep(50); } - } while ((i < DA7219_SRM_CHECK_RETRIES) & (!srm_lock)); + } while ((i < DA7219_SRM_CHECK_RETRIES) && (!srm_lock)); if (!srm_lock) dev_warn(component->dev, "SRM failed to lock\n"); @@ -1376,11 +1376,7 @@ static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt) return -EINVAL; } - /* By default 64 BCLKs per WCLK is supported */ - dai_clk_mode |= DA7219_DAI_BCLKS_PER_WCLK_64; - snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, - DA7219_DAI_BCLKS_PER_WCLK_MASK | DA7219_DAI_CLK_POL_MASK | DA7219_DAI_WCLK_POL_MASK, dai_clk_mode); snd_soc_component_update_bits(component, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK, @@ -1395,69 +1391,83 @@ static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai, { struct snd_soc_component *component = dai->component; struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); - u8 dai_bclks_per_wclk; + unsigned int ch_mask; + u8 dai_bclks_per_wclk, slot_offset; u16 offset; + __le16 dai_offset; u32 frame_size; - /* No channels enabled so disable TDM, revert to 64-bit frames */ + /* No channels enabled so disable TDM */ if (!tx_mask) { snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL, DA7219_DAI_TDM_CH_EN_MASK | DA7219_DAI_TDM_MODE_EN_MASK, 0); - snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, - DA7219_DAI_BCLKS_PER_WCLK_MASK, - DA7219_DAI_BCLKS_PER_WCLK_64); + da7219->tdm_en = false; return 0; } /* Check we have valid slots */ - if (fls(tx_mask) > DA7219_DAI_TDM_MAX_SLOTS) { - dev_err(component->dev, "Invalid number of slots, max = %d\n", + slot_offset = ffs(tx_mask) - 1; + ch_mask = (tx_mask >> slot_offset); + if (fls(ch_mask) > DA7219_DAI_TDM_MAX_SLOTS) { + dev_err(component->dev, + "Invalid number of slots, max = %d\n", DA7219_DAI_TDM_MAX_SLOTS); return -EINVAL; } - /* Check we have a valid offset given */ - if (rx_mask > DA7219_DAI_OFFSET_MAX) { - dev_err(component->dev, "Invalid slot offset, max = %d\n", - DA7219_DAI_OFFSET_MAX); + /* + * Ensure we have a valid offset into the frame, based on slot width + * and slot offset of first slot we're interested in. + */ + offset = slot_offset * slot_width; + if (offset > DA7219_DAI_OFFSET_MAX) { + dev_err(component->dev, "Invalid frame offset %d\n", offset); return -EINVAL; } - /* Calculate & validate frame size based on slot info provided. */ - frame_size = slots * slot_width; - switch (frame_size) { - case 32: - dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32; - break; - case 64: - dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64; - break; - case 128: - dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128; - break; - case 256: - dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256; - break; - default: - dev_err(component->dev, "Invalid frame size %d\n", frame_size); - return -EINVAL; - } + /* + * If we're master, calculate & validate frame size based on slot info + * provided as we have a limited set of rates available. + */ + if (da7219->master) { + frame_size = slots * slot_width; + switch (frame_size) { + case 32: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32; + break; + case 64: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64; + break; + case 128: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128; + break; + case 256: + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256; + break; + default: + dev_err(component->dev, "Invalid frame size %d\n", + frame_size); + return -EINVAL; + } - snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, - DA7219_DAI_BCLKS_PER_WCLK_MASK, - dai_bclks_per_wclk); + snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK, + dai_bclks_per_wclk); + } - offset = cpu_to_le16(rx_mask); + dai_offset = cpu_to_le16(offset); regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER, - &offset, sizeof(offset)); + &dai_offset, sizeof(dai_offset)); snd_soc_component_update_bits(component, DA7219_DAI_TDM_CTRL, DA7219_DAI_TDM_CH_EN_MASK | DA7219_DAI_TDM_MODE_EN_MASK, - (tx_mask << DA7219_DAI_TDM_CH_EN_SHIFT) | + (ch_mask << DA7219_DAI_TDM_CH_EN_SHIFT) | DA7219_DAI_TDM_MODE_EN_MASK); + da7219->tdm_en = true; + return 0; } @@ -1466,10 +1476,13 @@ static int da7219_hw_params(struct snd_pcm_substream *substream, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; - u8 dai_ctrl = 0, fs; + struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); + u8 dai_ctrl = 0, dai_bclks_per_wclk = 0, fs; unsigned int channels; + int word_len = params_width(params); + int frame_size; - switch (params_width(params)) { + switch (word_len) { case 16: dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE; break; @@ -1533,6 +1546,23 @@ static int da7219_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* + * If we're master, then we have a limited set of BCLK rates we + * support. For slave mode this isn't the case and the codec can detect + * the BCLK rate automatically. + */ + if (da7219->master && !da7219->tdm_en) { + frame_size = word_len * 2; + if (frame_size <= 32) + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32; + else + dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64; + + snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, + DA7219_DAI_BCLKS_PER_WCLK_MASK, + dai_bclks_per_wclk); + } + snd_soc_component_update_bits(component, DA7219_DAI_CTRL, DA7219_DAI_WORD_LENGTH_MASK | DA7219_DAI_CH_NUM_MASK, @@ -1767,7 +1797,7 @@ static int da7219_dai_clks_prepare(struct clk_hw *hw) { struct da7219_priv *da7219 = container_of(hw, struct da7219_priv, dai_clks_hw); - struct snd_soc_component *component = da7219->aad->component; + struct snd_soc_component *component = da7219->component; snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, DA7219_DAI_CLK_EN_MASK, @@ -1780,7 +1810,7 @@ static void da7219_dai_clks_unprepare(struct clk_hw *hw) { struct da7219_priv *da7219 = container_of(hw, struct da7219_priv, dai_clks_hw); - struct snd_soc_component *component = da7219->aad->component; + struct snd_soc_component *component = da7219->component; snd_soc_component_update_bits(component, DA7219_DAI_CLK_MODE, DA7219_DAI_CLK_EN_MASK, 0); @@ -1790,7 +1820,7 @@ static int da7219_dai_clks_is_prepared(struct clk_hw *hw) { struct da7219_priv *da7219 = container_of(hw, struct da7219_priv, dai_clks_hw); - struct snd_soc_component *component = da7219->aad->component; + struct snd_soc_component *component = da7219->component; u8 clk_reg; clk_reg = snd_soc_component_read32(component, DA7219_DAI_CLK_MODE); @@ -1798,13 +1828,50 @@ static int da7219_dai_clks_is_prepared(struct clk_hw *hw) return !!(clk_reg & DA7219_DAI_CLK_EN_MASK); } +static unsigned long da7219_dai_clks_recalc_rate(struct clk_hw *hw, + unsigned long parent_rate) +{ + struct da7219_priv *da7219 = + container_of(hw, struct da7219_priv, dai_clks_hw); + struct snd_soc_component *component = da7219->component; + u8 fs = snd_soc_component_read32(component, DA7219_SR); + + switch (fs & DA7219_SR_MASK) { + case DA7219_SR_8000: + return 8000; + case DA7219_SR_11025: + return 11025; + case DA7219_SR_12000: + return 12000; + case DA7219_SR_16000: + return 16000; + case DA7219_SR_22050: + return 22050; + case DA7219_SR_24000: + return 24000; + case DA7219_SR_32000: + return 32000; + case DA7219_SR_44100: + return 44100; + case DA7219_SR_48000: + return 48000; + case DA7219_SR_88200: + return 88200; + case DA7219_SR_96000: + return 96000; + default: + return 0; + } +} + static const struct clk_ops da7219_dai_clks_ops = { .prepare = da7219_dai_clks_prepare, .unprepare = da7219_dai_clks_unprepare, .is_prepared = da7219_dai_clks_is_prepared, + .recalc_rate = da7219_dai_clks_recalc_rate, }; -static void da7219_register_dai_clks(struct snd_soc_component *component) +static int da7219_register_dai_clks(struct snd_soc_component *component) { struct device *dev = component->dev; struct da7219_priv *da7219 = snd_soc_component_get_drvdata(component); @@ -1812,18 +1879,27 @@ static void da7219_register_dai_clks(struct snd_soc_component *component) struct clk_init_data init = {}; struct clk *dai_clks; struct clk_lookup *dai_clks_lookup; + const char *parent_name; + + if (da7219->mclk) { + parent_name = __clk_get_name(da7219->mclk); + init.parent_names = &parent_name; + init.num_parents = 1; + } else { + init.parent_names = NULL; + init.num_parents = 0; + } - init.parent_names = NULL; - init.num_parents = 0; init.name = pdata->dai_clks_name; init.ops = &da7219_dai_clks_ops; + init.flags = CLK_GET_RATE_NOCACHE; da7219->dai_clks_hw.init = &init; dai_clks = devm_clk_register(dev, &da7219->dai_clks_hw); if (IS_ERR(dai_clks)) { dev_warn(dev, "Failed to register DAI clocks: %ld\n", PTR_ERR(dai_clks)); - return; + return PTR_ERR(dai_clks); } da7219->dai_clks = dai_clks; @@ -1835,13 +1911,18 @@ static void da7219_register_dai_clks(struct snd_soc_component *component) dai_clks_lookup = clkdev_create(dai_clks, pdata->dai_clks_name, "%s", dev_name(dev)); if (!dai_clks_lookup) - dev_warn(dev, "Failed to create DAI clkdev"); + return -ENOMEM; else da7219->dai_clks_lookup = dai_clks_lookup; } + + return 0; } #else -static inline void da7219_register_dai_clks(struct snd_soc_component *component) {} +static inline int da7219_register_dai_clks(struct snd_soc_component *component) +{ + return 0; +} #endif /* CONFIG_COMMON_CLK */ static void da7219_handle_pdata(struct snd_soc_component *component) @@ -1854,8 +1935,6 @@ static void da7219_handle_pdata(struct snd_soc_component *component) da7219->wakeup_source = pdata->wakeup_source; - da7219_register_dai_clks(component); - /* Mic Bias voltages */ switch (pdata->micbias_lvl) { case DA7219_MICBIAS_1_6V: @@ -1901,6 +1980,7 @@ static int da7219_probe(struct snd_soc_component *component) unsigned int rev; int ret; + da7219->component = component; mutex_init(&da7219->ctrl_lock); mutex_init(&da7219->pll_lock); @@ -1947,6 +2027,11 @@ static int da7219_probe(struct snd_soc_component *component) } } + /* Register CCF DAI clock control */ + ret = da7219_register_dai_clks(component); + if (ret) + return ret; + /* Default PC counter to free-running */ snd_soc_component_update_bits(component, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK, DA7219_PC_FREERUN_MASK); diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h @@ -809,6 +809,7 @@ struct da7219_aad_priv; /* Private data */ struct da7219_priv { + struct snd_soc_component *component; struct da7219_aad_priv *aad; struct da7219_pdata *pdata; @@ -829,6 +830,7 @@ struct da7219_priv { int clk_src; bool master; + bool tdm_en; bool alc_en; bool micbias_on_event; unsigned int mic_pga_delay; diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c @@ -44,8 +44,8 @@ struct dmic { int modeswitch_delay; }; -int dmic_daiops_trigger(struct snd_pcm_substream *substream, - int cmd, struct snd_soc_dai *dai) +static int dmic_daiops_trigger(struct snd_pcm_substream *substream, + int cmd, struct snd_soc_dai *dai) { struct snd_soc_component *component = dai->component; struct dmic *dmic = snd_soc_component_get_drvdata(component); diff --git a/sound/soc/codecs/es8316.c b/sound/soc/codecs/es8316.c @@ -15,12 +15,14 @@ #include <linux/delay.h> #include <linux/i2c.h> #include <linux/mod_devicetable.h> +#include <linux/mutex.h> #include <linux/regmap.h> #include <sound/pcm.h> #include <sound/pcm_params.h> #include <sound/soc.h> #include <sound/soc-dapm.h> #include <sound/tlv.h> +#include <sound/jack.h> #include "es8316.h" /* In slave mode at single speed, the codec is documented as accepting 5 @@ -33,6 +35,11 @@ static const unsigned int supported_mclk_lrck_ratios[] = { }; struct es8316_priv { + struct mutex lock; + struct regmap *regmap; + struct snd_soc_component *component; + struct snd_soc_jack *jack; + int irq; unsigned int sysclk; unsigned int allowed_rates[NR_SUPPORTED_MCLK_LRCK_RATIOS]; struct snd_pcm_hw_constraint_list sysclk_constraints; @@ -94,6 +101,7 @@ static const struct snd_kcontrol_new es8316_snd_controls[] = { SOC_SINGLE("DAC Notch Filter Switch", ES8316_DAC_SET2, 6, 1, 0), SOC_SINGLE("DAC Double Fs Switch", ES8316_DAC_SET2, 7, 1, 0), SOC_SINGLE("DAC Stereo Enhancement", ES8316_DAC_SET3, 0, 7, 0), + SOC_SINGLE("DAC Mono Mix Switch", ES8316_DAC_SET3, 3, 1, 0), SOC_ENUM("Capture Polarity", adcpol), SOC_SINGLE("Mic Boost Switch", ES8316_ADC_D2SEPGA, 0, 1, 0), @@ -159,8 +167,6 @@ static const char * const es8316_hpmux_texts[] = { "lin-rin with Boost and PGA" }; -static const unsigned int es8316_hpmux_values[] = { 0, 1, 2, 3 }; - static SOC_ENUM_SINGLE_DECL(es8316_left_hpmux_enum, ES8316_HPMIX_SEL, 4, es8316_hpmux_texts); @@ -191,8 +197,6 @@ static const char * const es8316_dacsrc_texts[] = { "RDATA TO LDAC, LDATA TO RDAC", }; -static const unsigned int es8316_dacsrc_values[] = { 0, 1, 2, 3 }; - static SOC_ENUM_SINGLE_DECL(es8316_dacsrc_mux_enum, ES8316_DAC_SET1, 6, es8316_dacsrc_texts); @@ -529,8 +533,162 @@ static struct snd_soc_dai_driver es8316_dai = { .symmetric_rates = 1, }; +static void es8316_enable_micbias_for_mic_gnd_short_detect( + struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Bias"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Analog power"); + snd_soc_dapm_force_enable_pin_unlocked(dapm, "Mic Bias"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); + + msleep(20); +} + +static void es8316_disable_micbias_for_mic_gnd_short_detect( + struct snd_soc_component *component) +{ + struct snd_soc_dapm_context *dapm = snd_soc_component_get_dapm(component); + + snd_soc_dapm_mutex_lock(dapm); + snd_soc_dapm_disable_pin_unlocked(dapm, "Mic Bias"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Analog power"); + snd_soc_dapm_disable_pin_unlocked(dapm, "Bias"); + snd_soc_dapm_sync_unlocked(dapm); + snd_soc_dapm_mutex_unlock(dapm); +} + +static irqreturn_t es8316_irq(int irq, void *data) +{ + struct es8316_priv *es8316 = data; + struct snd_soc_component *comp = es8316->component; + unsigned int flags; + + mutex_lock(&es8316->lock); + + regmap_read(es8316->regmap, ES8316_GPIO_FLAG, &flags); + if (flags == 0x00) + goto out; /* Powered-down / reset */ + + /* Catch spurious IRQ before set_jack is called */ + if (!es8316->jack) + goto out; + + dev_dbg(comp->dev, "gpio flags %#04x\n", flags); + if