whiterose

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

commit 3acbd2de6bc3af215c6ed7732dfc097d1e238503
parent d49f8a52b15bf35db778035340d8a673149f9f93
Author: Linus Torvalds <torvalds@linux-foundation.org>
Date:   Thu, 25 Oct 2018 09:00:15 -0700

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

Pull sound updates from Takashi Iwai:
 "There have been little changes in ALSA core stuff, but ASoC core still
  kept rolling for the continued restructuring. The rest are lots of
  small driver-specific changes and some minor API updates. Here are
  highlights:

  General:
  - Appropriate fall-through annotations everywhere
  - Some code cleanup in memalloc code, handling non-cacahed pages more
    commonly in the helper
  - Deployment of SNDRV_PCM_INFO_SYNC_APPLPTR flag consistently

  Drivers:
  - More HD-audio CA0132 codec improvement for supporting other Creative
    boards
  - Plumbing legacy HD-audio codecs as ASoC BE on Intel SST; this will
    give move support of existing HD-audio devices with DSP
  - A few device-specific HD-audio quirks as usual
  - New quirk for RME CC devices and correction for B&W PX for USB-audio
  - FireWire: code refactoring including devres usages

  ASoC Core:
  - Continued componentization works; it's almost done!
  - A bunch of new for_each_foo macros
  - Cleanups and fixes in DAPM code

  ASoC Drivers:
  - MCLK support for several different devices, including CS42L51, STM32
    SAI, and MAX98373
  - Support for Allwinner A64 CODEC analog, Intel boards with DA7219 and
    MAX98927, Meson AXG PDM inputs, Nuvoton NAU8822, Renesas R8A7744 and
    TI PCM3060"

* tag 'sound-4.20-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (299 commits)
  ASoC: stm32: sai: fix master clock naming
  ASoC: stm32: add clock dependency for sai
  ALSA: hda/ca0132 - Actually fix microphone issue
  ASoC: sun4i-i2s: move code from startup/shutdown hooks into pm_runtime hooks
  ASoC: wm2000: Remove wm2000_read helper function
  ASoC: cs42l51: fix mclk support
  ASoC: wm_adsp: Log addresses as 8 digits in wm_adsp_buffer_populate
  ASoC: wm_adsp: Rename memory fields in wm_adsp_buffer
  ASoC: cs42l51: add mclk support
  ASoC: stm32: sai: set sai as mclk clock provider
  ASoC: dt-bindings: add mclk support to cs42l51
  ASoC: dt-bindings: add mclk provider support to stm32 sai
  ASoC: soc-core: fix trivial checkpatch issues
  ASoC: dapm: Add support for hw_free on CODEC to CODEC links
  ASoC: Intel: kbl_da7219_max98927: minor white space clean up
  ALSA: i2c/cs8427: Fix int to char conversion
  ALSA: doc: Brush up the old writing-an-alsa-driver
  ASoC: rsnd: tidyup SSICR::SWSP for TDM
  ASoC: rsnd: enable TDM settings for SSI parent
  ASoC: pcm3168a: add hw constraint for capture channel
  ...

Diffstat:
ADocumentation/devicetree/bindings/sound/adi,adau1977.txt | 54++++++++++++++++++++++++++++++++++++++++++++++++++++++
ADocumentation/devicetree/bindings/sound/amlogic,axg-pdm.txt | 24++++++++++++++++++++++++
ADocumentation/devicetree/bindings/sound/cs42l51.txt | 17+++++++++++++++++
ADocumentation/devicetree/bindings/sound/maxim,max98088.txt | 23+++++++++++++++++++++++
ADocumentation/devicetree/bindings/sound/mikroe,mikroe-proto.txt | 23+++++++++++++++++++++++
ADocumentation/devicetree/bindings/sound/nau8822.txt | 16++++++++++++++++
ADocumentation/devicetree/bindings/sound/pcm3060.txt | 17+++++++++++++++++
MDocumentation/devicetree/bindings/sound/qcom,q6afe.txt | 18+++++++++---------
MDocumentation/devicetree/bindings/sound/renesas,rsnd.txt | 5++++-
MDocumentation/devicetree/bindings/sound/st,sta32x.txt | 9+++++++++
MDocumentation/devicetree/bindings/sound/st,stm32-sai.txt | 7+++++++
MDocumentation/devicetree/bindings/sound/sun4i-i2s.txt | 2++
ADocumentation/devicetree/bindings/sound/sun50i-codec-analog.txt | 12++++++++++++
MDocumentation/devicetree/bindings/sound/ts3a227e.txt | 2+-
ADocumentation/devicetree/bindings/sound/wm8782.txt | 17+++++++++++++++++
MDocumentation/devicetree/bindings/trivial-devices.txt | 1-
MDocumentation/devicetree/bindings/vendor-prefixes.txt | 1+
MDocumentation/sound/hd-audio/models.rst | 2++
MDocumentation/sound/kernel-api/writing-an-alsa-driver.rst | 307++++++++++++++++++++++++++++++++++++++-----------------------------------------
MMAINTAINERS | 7+++++++
Rsound/pci/hda/hda_codec.h -> include/sound/hda_codec.h | 0
Minclude/sound/memalloc.h | 3+++
Minclude/sound/rawmidi.h | 1+
Minclude/sound/simple_card_utils.h | 27++++++++++++++++++---------
Minclude/sound/soc-acpi-intel-match.h | 6++++++
Minclude/sound/soc-dapm.h | 9---------
Minclude/sound/soc-dpcm.h | 10++++++++++
Minclude/sound/soc.h | 45++++++++++++++++++++++++++++++++++++++++++++-
Minclude/uapi/sound/asound.h | 2+-
Msound/aoa/soundbus/i2sbus/core.c | 15++++++++-------
Msound/arm/Kconfig | 1-
Msound/core/memalloc.c | 41+++++++++++++++++++++++++----------------
Msound/core/oss/pcm_plugin.c | 4++--
Msound/core/pcm_lib.c | 21++++++++++++++-------
Msound/core/rawmidi.c | 22++++++++++++++++++++++
Msound/core/seq/oss/seq_oss_timer.c | 2+-
Msound/core/seq/seq_system.c | 22+++++++++++++++++++---
Msound/core/seq/seq_virmidi.c | 4+---
Msound/core/sgbuf.c | 15+++++++++++++--
Msound/firewire/Kconfig | 2++
Msound/firewire/amdtp-stream.c | 78+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msound/firewire/bebob/bebob.c | 58++++++++++++++++------------------------------------------
Msound/firewire/bebob/bebob_maudio.c | 5+++--
Msound/firewire/dice/dice.c | 41++++++++++-------------------------------
Msound/firewire/digi00x/digi00x.c | 35+++++++++++++----------------------
Msound/firewire/fireface/ff.c | 36++++++++++++------------------------
Msound/firewire/fireworks/fireworks.c | 69++++++++++++++++++++-------------------------------------------------
Msound/firewire/isight.c | 18++++++++++--------
Msound/firewire/motu/motu.c | 47+++++++++++++----------------------------------
Msound/firewire/oxfw/oxfw-scs1x.c | 5+++--
Msound/firewire/oxfw/oxfw-spkr.c | 5+++--
Msound/firewire/oxfw/oxfw-stream.c | 13++++++++-----
Msound/firewire/oxfw/oxfw.c | 63++++++++++++---------------------------------------------------
Msound/firewire/tascam/tascam.c | 40++++++++++++----------------------------
Msound/hda/ext/hdac_ext_controller.c | 22++++++++++++++--------
Msound/i2c/cs8427.c | 2+-
Msound/isa/opti9xx/opti92x-ad1848.c | 6++++--
Msound/isa/sb/sb8_main.c | 10+++++-----
Msound/mips/hal2.c | 13+++++--------
Msound/pci/asihpi/hpios.c | 2+-
Msound/pci/atiixp.c | 6+++---
Msound/pci/au88x0/au88x0_core.c | 6++++++
Msound/pci/cs46xx/cs46xx_lib.c | 6++++--
Msound/pci/emu10k1/emupcm.c | 3++-
Msound/pci/hda/hda_auto_parser.c | 2+-
Msound/pci/hda/hda_beep.h | 2+-
Msound/pci/hda/hda_bind.c | 14+++++++++++++-
Msound/pci/hda/hda_codec.c | 2+-
Msound/pci/hda/hda_controller.c | 36+++++++++---------------------------
Msound/pci/hda/hda_controller.h | 20+++++++-------------
Msound/pci/hda/hda_eld.c | 2+-
Msound/pci/hda/hda_generic.c | 2+-
Msound/pci/hda/hda_hwdep.c | 2+-
Msound/pci/hda/hda_intel.c | 112++++++++++++-------------------------------------------------------------------
Msound/pci/hda/hda_jack.c | 2+-
Msound/pci/hda/hda_proc.c | 2+-
Msound/pci/hda/hda_sysfs.c | 2+-
Msound/pci/hda/hda_tegra.c | 20++------------------
Msound/pci/hda/patch_analog.c | 2+-
Msound/pci/hda/patch_ca0110.c | 2+-
Msound/pci/hda/patch_ca0132.c | 1675++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------------
Msound/pci/hda/patch_cirrus.c | 2+-
Msound/pci/hda/patch_cmedia.c | 2+-
Msound/pci/hda/patch_conexant.c | 3++-
Msound/pci/hda/patch_hdmi.c | 2+-
Msound/pci/hda/patch_realtek.c | 29++++++++++++++++++++++++++++-
Msound/pci/hda/patch_si3054.c | 2+-
Msound/pci/hda/patch_sigmatel.c | 22+++++++++++++++++++++-
Msound/pci/hda/patch_via.c | 2+-
Msound/pci/intel8x0.c | 97+++++++++++++------------------------------------------------------------------
Msound/pci/intel8x0m.c | 20++++++++++----------
Msound/pci/rme32.c | 22++++++++--------------
Msound/pci/rme9652/hdspm.c | 2+-
Msound/soc/amd/acp-da7219-max98357a.c | 77++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msound/soc/amd/acp-pcm-dma.c | 30++++++++++++++++++++----------
Msound/soc/amd/acp.h | 3++-
Msound/soc/atmel/Kconfig | 12++++++++++++
Msound/soc/atmel/Makefile | 2++
Msound/soc/atmel/atmel_ssc_dai.c | 13+++----------
Asound/soc/atmel/mikroe-proto.c | 165+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/atmel/tse850-pcm5142.c | 78++++++++++++++++++++++++++++++++++++------------------------------------------
Msound/soc/bcm/cygnus-ssp.c | 13++++---------
Msound/soc/codecs/Kconfig | 36++++++++++++++++++++++++++++++++++--
Msound/soc/codecs/Makefile | 10++++++++++
Msound/soc/codecs/adau1761.c | 3++-
Msound/soc/codecs/adau17x1.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------
Msound/soc/codecs/adau17x1.h | 4----
Msound/soc/codecs/cs4265.c | 12+++++++-----
Msound/soc/codecs/cs42l51.c | 21+++++++++++++++++++++
Msound/soc/codecs/dmic.c | 1+
Msound/soc/codecs/es8328.c | 4++--
Asound/soc/codecs/hdac_hda.c | 483+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/codecs/hdac_hda.h | 24++++++++++++++++++++++++
Msound/soc/codecs/hdac_hdmi.c | 11+++++++----
Msound/soc/codecs/max98088.c | 36++++++++++++++++++++++++++++++++++++
Msound/soc/codecs/max98373.c | 47++++++++++++++++++++++++-----------------------
Asound/soc/codecs/nau8822.c | 1136+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/codecs/nau8822.h | 204+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/codecs/pcm186x.c | 3++-
Asound/soc/codecs/pcm3060-i2c.c | 60++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/codecs/pcm3060-spi.c | 59+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/codecs/pcm3060.c | 295+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/codecs/pcm3060.h | 88+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/codecs/pcm3168a.c | 82+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/codecs/rt274.c | 2++
Msound/soc/codecs/rt5514-spi.c | 14++++++++------
Msound/soc/codecs/rt5651.c | 1-
Msound/soc/codecs/rt5663.c | 7++++++-
Msound/soc/codecs/rt5668.c | 10+---------
Msound/soc/codecs/rt5670.c | 12++++++++++++
Msound/soc/codecs/rt5677-spi.c | 1-
Msound/soc/codecs/rt5682.c | 86+++++++++++++++++++++++++++++++++++++++++++++++++++++--------------------------
Msound/soc/codecs/rt5682.h | 14++++++++++++++
Msound/soc/codecs/sgtl5000.c | 2+-
Msound/soc/codecs/sta32x.c | 30++++++++++++++++++++++++++++++
Msound/soc/codecs/tas5720.c | 103++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-------
Msound/soc/codecs/tas6424.c | 58+++++++++++++++++++++++++++++++++++++++++++++++++---------
Msound/soc/codecs/tas6424.h | 10++++++++++
Msound/soc/codecs/tlv320aic31xx.c | 85+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/codecs/tlv320aic31xx.h | 23+++++++++++++++++++++++
Msound/soc/codecs/tscs454.c | 2+-
Msound/soc/codecs/wm2000.c | 54++++++++++++++++++++++++++++++------------------------
Msound/soc/codecs/wm8782.c | 63+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/codecs/wm8904.c | 1-
Msound/soc/codecs/wm8974.c | 1-
Msound/soc/codecs/wm9712.c | 3++-
Msound/soc/codecs/wm_adsp.c | 26+++++++++++++-------------
Msound/soc/davinci/davinci-mcasp.c | 37+++++++++++++++++++++++++++++++++++++
Msound/soc/fsl/fsl_asrc_dma.c | 2+-
Msound/soc/fsl/fsl_esai.c | 2+-
Msound/soc/fsl/fsl_utils.c | 4++--
Msound/soc/fsl/pcm030-audio-fabric.c | 5+++--
Msound/soc/generic/audio-graph-card.c | 21++++++++++++++++++---
Msound/soc/generic/audio-graph-scu-card.c | 55+++++++++++++++++++++++++++++++++++++------------------
Msound/soc/generic/simple-card-utils.c | 53++++++++++++++++++++++++++++++++++++++++++++---------
Msound/soc/generic/simple-card.c | 30+++++++++++++++++++++++++-----
Msound/soc/generic/simple-scu-card.c | 54++++++++++++++++++++++++++++++++++++------------------
Msound/soc/hisilicon/hi6210-i2s.c | 4++--
Msound/soc/intel/atom/sst-mfld-platform-pcm.c | 4++--
Msound/soc/intel/boards/Kconfig | 22++++++++++++++++++++++
Msound/soc/intel/boards/Makefile | 4++++
Msound/soc/intel/boards/broadwell.c | 4++--
Msound/soc/intel/boards/bytcr_rt5640.c | 4++--
Msound/soc/intel/boards/bytcr_rt5651.c | 4++--
Msound/soc/intel/boards/cht_bsw_rt5672.c | 9+++++++--
Asound/soc/intel/boards/kbl_da7219_max98927.c | 983+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/intel/boards/kbl_rt5663_max98927.c | 5++---
Msound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c | 5++---
Asound/soc/intel/boards/skl_hda_dsp_common.c | 127+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/intel/boards/skl_hda_dsp_common.h | 38++++++++++++++++++++++++++++++++++++++
Asound/soc/intel/boards/skl_hda_dsp_generic.c | 183+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/intel/common/Makefile | 3++-
Msound/soc/intel/common/soc-acpi-intel-byt-match.c | 7+++++++
Asound/soc/intel/common/soc-acpi-intel-hda-match.c | 40++++++++++++++++++++++++++++++++++++++++
Msound/soc/intel/common/soc-acpi-intel-kbl-match.c | 13+++++++++++++
Msound/soc/intel/common/sst-firmware.c | 2+-
Msound/soc/intel/skylake/skl-pcm.c | 71+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msound/soc/intel/skylake/skl-topology.c | 4++--
Msound/soc/intel/skylake/skl.c | 96++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++---------
Msound/soc/intel/skylake/skl.h | 12+++++++++---
Msound/soc/mediatek/mt2701/mt2701-cs42448.c | 13+++++++------
Msound/soc/mediatek/mt2701/mt2701-wm8960.c | 14+++++++-------
Msound/soc/mediatek/mt6797/mt6797-mt6351.c | 14+++++++-------
Msound/soc/mediatek/mt8173/mt8173-max98090.c | 13+++++++------
Msound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c | 12++++++------
Msound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c | 12++++++------
Msound/soc/mediatek/mt8173/mt8173-rt5650.c | 12++++++------
Msound/soc/meson/Kconfig | 13+++++++++++++
Msound/soc/meson/Makefile | 2++
Msound/soc/meson/axg-card.c | 16+++++++---------
Msound/soc/meson/axg-fifo.c | 2++
Asound/soc/meson/axg-pdm.c | 654+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/soc/meson/axg-tdm-interface.c | 50+++++++++++++++++++++++++++++---------------------
Msound/soc/nuc900/nuc900-ac97.c | 4+---
Msound/soc/omap/omap-hdmi-audio.c | 4+---
Msound/soc/pxa/Kconfig | 13++++++++++---
Msound/soc/pxa/pxa-ssp.c | 6++++++
Msound/soc/pxa/pxa2xx-ac97.c | 48+++++++++++++++++++++++++-----------------------
Msound/soc/qcom/apq8096.c | 7+++----
Msound/soc/qcom/qdsp6/q6adm.c | 17++++++++---------
Msound/soc/qcom/qdsp6/q6asm-dai.c | 8++++----
Msound/soc/qcom/qdsp6/q6asm.c | 1-
Msound/soc/qcom/qdsp6/q6core.c | 9++-------
Msound/soc/qcom/sdm845.c | 7+++----
Msound/soc/rockchip/rk3288_hdmi_analog.c | 1-
Msound/soc/rockchip/rockchip_pcm.c | 3++-
Msound/soc/samsung/tm2_wm5110.c | 13+++++++------
Msound/soc/sh/hac.c | 3+--
Msound/soc/sh/rcar/adg.c | 4++--
Msound/soc/sh/rcar/core.c | 124++++++++++++++++++++++++++++++++++++++-----------------------------------------
Msound/soc/sh/rcar/ctu.c | 2+-
Msound/soc/sh/rcar/dma.c | 109++++++++++++++++++++++++++++++++++++++++++++++++-------------------------------
Msound/soc/sh/rcar/gen.c | 33+++++++++++++++++++++++++++------
Msound/soc/sh/rcar/rsnd.h | 63+++++++++++++++++++++++++++++++++++++++++++--------------------
Msound/soc/sh/rcar/src.c | 2+-
Msound/soc/sh/rcar/ssi.c | 112++++++++++++++++++++++++++++++++++++++++++++++---------------------------------
Msound/soc/sh/rcar/ssiu.c | 92++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++-----------
Msound/soc/soc-compress.c | 4++--
Msound/soc/soc-core.c | 582++++++++++++++++++++++++++++++++++++++++++-------------------------------------
Msound/soc/soc-dapm.c | 437+++++++++++++++++++++++++++++++++++++++----------------------------------------
Msound/soc/soc-ops.c | 4++--
Msound/soc/soc-pcm.c | 253+++++++++++++++++++++++++++++++++++++------------------------------------------
Msound/soc/soc-topology.c | 15++-------------
Msound/soc/soc-utils.c | 4++--
Msound/soc/stm/Kconfig | 1+
Msound/soc/stm/stm32_sai.c | 2+-
Msound/soc/stm/stm32_sai.h | 3+++
Msound/soc/stm/stm32_sai_sub.c | 281+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++------------
Msound/soc/sunxi/Kconfig | 17+++++++++++++++--
Msound/soc/sunxi/Makefile | 2++
Msound/soc/sunxi/sun4i-i2s.c | 82++++++++++++++++++++++++++++++++++++++++++++-----------------------------------
Asound/soc/sunxi/sun50i-codec-analog.c | 444+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/sunxi/sun8i-adda-pr-regmap.c | 102+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Asound/soc/sunxi/sun8i-adda-pr-regmap.h | 7+++++++
Msound/soc/sunxi/sun8i-codec-analog.c | 79+++----------------------------------------------------------------------------
Msound/soc/sunxi/sun8i-codec.c | 22+++++++++++++++++++---
Msound/soc/tegra/tegra_sgtl5000.c | 17+++++++++++++++--
Msound/soc/txx9/txx9aclc-ac97.c | 3+--
Msound/usb/caiaq/device.c | 1+
Msound/usb/midi.c | 3+--
Msound/usb/mixer_quirks.c | 381+++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++++
Msound/usb/quirks-table.h | 9++-------
Msound/x86/intel_hdmi_audio.c | 29+++--------------------------
Msound/xen/xen_snd_front_alsa.c | 46+++++++++++++++++++++++-----------------------
244 files changed, 10808 insertions(+), 2656 deletions(-)

diff --git a/Documentation/devicetree/bindings/sound/adi,adau1977.txt b/Documentation/devicetree/bindings/sound/adi,adau1977.txt @@ -0,0 +1,54 @@ +Analog Devices ADAU1977/ADAU1978/ADAU1979 + +Datasheets: +http://www.analog.com/media/en/technical-documentation/data-sheets/ADAU1977.pdf +http://www.analog.com/media/en/technical-documentation/data-sheets/ADAU1978.pdf +http://www.analog.com/media/en/technical-documentation/data-sheets/ADAU1979.pdf + +This driver supports both the I2C and SPI bus. + +Required properties: + - compatible: Should contain one of the following: + "adi,adau1977" + "adi,adau1978" + "adi,adau1979" + + - AVDD-supply: analog power supply for the device, please consult + Documentation/devicetree/bindings/regulator/regulator.txt + +Optional properties: + - reset-gpio: 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 + +For required properties on SPI, please consult +Documentation/devicetree/bindings/spi/spi-bus.txt + +Required properties on I2C: + + - reg: The i2c address. Value depends on the state of ADDR0 + and ADDR1, as wired in hardware. + +Examples: + + adau1977_spi: adau1977@0 { + compatible = "adi,adau1977"; + spi-max-frequency = <600000>; + + AVDD-supply = <&regulator>; + DVDD-supply = <&regulator_digital>; + + reset_gpio = <&gpio 10 GPIO_ACTIVE_LOW>; + }; + + adau1977_i2c: adau1977@11 { + compatible = "adi,adau1977"; + reg = <0x11>; + + AVDD-supply = <&regulator>; + DVDD-supply = <&regulator_digital>; + + reset_gpio = <&gpio 10 GPIO_ACTIVE_LOW>; + }; diff --git a/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.txt b/Documentation/devicetree/bindings/sound/amlogic,axg-pdm.txt @@ -0,0 +1,24 @@ +* Amlogic Audio PDM input + +Required properties: +- compatible: 'amlogic,axg-pdm' +- reg: physical base address of the controller and length of memory + mapped region. +- clocks: list of clock phandle, one for each entry clock-names. +- clock-names: should contain the following: + * "pclk" : peripheral clock. + * "dclk" : pdm digital clock + * "sysclk" : dsp system clock +- #sound-dai-cells: must be 0. + +Example of PDM on the A113 SoC: + +pdm: audio-controller@ff632000 { + compatible = "amlogic,axg-pdm"; + reg = <0x0 0xff632000 0x0 0x34>; + #sound-dai-cells = <0>; + clocks = <&clkc_audio AUD_CLKID_PDM>, + <&clkc_audio AUD_CLKID_PDM_DCLK>, + <&clkc_audio AUD_CLKID_PDM_SYSCLK>; + clock-names = "pclk", "dclk", "sysclk"; +}; diff --git a/Documentation/devicetree/bindings/sound/cs42l51.txt b/Documentation/devicetree/bindings/sound/cs42l51.txt @@ -0,0 +1,17 @@ +CS42L51 audio CODEC + +Optional properties: + + - clocks : a list of phandles + clock-specifiers, one for each entry in + clock-names + + - clock-names : must contain "MCLK" + +Example: + +cs42l51: cs42l51@4a { + compatible = "cirrus,cs42l51"; + reg = <0x4a>; + clocks = <&mclk_prov>; + clock-names = "MCLK"; +}; diff --git a/Documentation/devicetree/bindings/sound/maxim,max98088.txt b/Documentation/devicetree/bindings/sound/maxim,max98088.txt @@ -0,0 +1,23 @@ +MAX98088 audio CODEC + +This device supports I2C only. + +Required properties: + +- compatible: "maxim,max98088" or "maxim,max98089". +- reg: The I2C address of the device. + +Optional properties: + +- clocks: the clock provider of MCLK, see ../clock/clock-bindings.txt section + "consumer" for more information. +- clock-names: must be set to "mclk" + +Example: + +max98089: codec@10 { + compatible = "maxim,max98089"; + reg = <0x10>; + clocks = <&clks IMX6QDL_CLK_CKO2>; + clock-names = "mclk"; +}; diff --git a/Documentation/devicetree/bindings/sound/mikroe,mikroe-proto.txt b/Documentation/devicetree/bindings/sound/mikroe,mikroe-proto.txt @@ -0,0 +1,23 @@ +Mikroe-PROTO audio board + +Required properties: + - compatible: "mikroe,mikroe-proto" + - dai-format: Must be "i2s". + - i2s-controller: The phandle of the I2S controller. + - audio-codec: The phandle of the WM8731 audio codec. +Optional properties: + - model: The user-visible name of this sound complex. + - bitclock-master: Indicates dai-link bit clock master; for details see simple-card.txt (1). + - frame-master: Indicates dai-link frame master; for details see simple-card.txt (1). + +(1) : There must be the same master for both bit and frame clocks. + +Example: + sound { + compatible = "mikroe,mikroe-proto"; + model = "wm8731 @ sama5d2_xplained"; + i2s-controller = <&i2s0>; + audio-codec = <&wm8731>; + dai-format = "i2s"; + }; +}; diff --git a/Documentation/devicetree/bindings/sound/nau8822.txt b/Documentation/devicetree/bindings/sound/nau8822.txt @@ -0,0 +1,16 @@ +NAU8822 audio CODEC + +This device supports I2C only. + +Required properties: + + - compatible : "nuvoton,nau8822" + + - reg : the I2C address of the device. + +Example: + +codec: nau8822@1a { + compatible = "nuvoton,nau8822"; + reg = <0x1a>; +}; diff --git a/Documentation/devicetree/bindings/sound/pcm3060.txt b/Documentation/devicetree/bindings/sound/pcm3060.txt @@ -0,0 +1,17 @@ +PCM3060 audio CODEC + +This driver supports both I2C and SPI. + +Required properties: + +- compatible: "ti,pcm3060" + +- reg : the I2C address of the device for I2C, the chip select + number for SPI. + +Examples: + + pcm3060: pcm3060@46 { + compatible = "ti,pcm3060"; + reg = <0x46>; + }; diff --git a/Documentation/devicetree/bindings/sound/qcom,q6afe.txt b/Documentation/devicetree/bindings/sound/qcom,q6afe.txt @@ -49,7 +49,7 @@ configuration of each dai. Must contain the following properties. Usage: required for mi2s interface Value type: <prop-encoded-array> Definition: Must be list of serial data lines used by this dai. - should be one or more of the 1-4 sd lines. + should be one or more of the 0-3 sd lines. - qcom,tdm-sync-mode: Usage: required for tdm interface @@ -137,42 +137,42 @@ q6afe@4 { prim-mi2s-rx@16 { reg = <16>; - qcom,sd-lines = <1 3>; + qcom,sd-lines = <0 2>; }; prim-mi2s-tx@17 { reg = <17>; - qcom,sd-lines = <2>; + qcom,sd-lines = <1>; }; sec-mi2s-rx@18 { reg = <18>; - qcom,sd-lines = <1 4>; + qcom,sd-lines = <0 3>; }; sec-mi2s-tx@19 { reg = <19>; - qcom,sd-lines = <2>; + qcom,sd-lines = <1>; }; tert-mi2s-rx@20 { reg = <20>; - qcom,sd-lines = <2 4>; + qcom,sd-lines = <1 3>; }; tert-mi2s-tx@21 { reg = <21>; - qcom,sd-lines = <1>; + qcom,sd-lines = <0>; }; quat-mi2s-rx@22 { reg = <22>; - qcom,sd-lines = <1>; + qcom,sd-lines = <0>; }; quat-mi2s-tx@23 { reg = <23>; - qcom,sd-lines = <2>; + qcom,sd-lines = <1>; }; }; }; diff --git a/Documentation/devicetree/bindings/sound/renesas,rsnd.txt b/Documentation/devicetree/bindings/sound/renesas,rsnd.txt @@ -340,10 +340,12 @@ Required properties: - compatible : "renesas,rcar_sound-<soctype>", fallbacks "renesas,rcar_sound-gen1" if generation1, and "renesas,rcar_sound-gen2" if generation2 (or RZ/G1) - "renesas,rcar_sound-gen3" if generation3 + "renesas,rcar_sound-gen3" if generation3 (or RZ/G2) Examples with soctypes are: - "renesas,rcar_sound-r8a7743" (RZ/G1M) + - "renesas,rcar_sound-r8a7744" (RZ/G1N) - "renesas,rcar_sound-r8a7745" (RZ/G1E) + - "renesas,rcar_sound-r8a774a1" (RZ/G2M) - "renesas,rcar_sound-r8a7778" (R-Car M1A) - "renesas,rcar_sound-r8a7779" (R-Car H1) - "renesas,rcar_sound-r8a7790" (R-Car H2) @@ -353,6 +355,7 @@ Required properties: - "renesas,rcar_sound-r8a7795" (R-Car H3) - "renesas,rcar_sound-r8a7796" (R-Car M3-W) - "renesas,rcar_sound-r8a77965" (R-Car M3-N) + - "renesas,rcar_sound-r8a77990" (R-Car E3) - reg : Should contain the register physical address. required register is SRU/ADG/SSI if generation1 diff --git a/Documentation/devicetree/bindings/sound/st,sta32x.txt b/Documentation/devicetree/bindings/sound/st,sta32x.txt @@ -19,6 +19,10 @@ Required properties: Optional properties: + - clocks, clock-names: Clock specifier for XTI input clock. + If specified, the clock will be enabled when the codec is probed, + and disabled when it is removed. The 'clock-names' must be set to 'xti'. + - st,output-conf: number, Selects the output configuration: 0: 2-channel (full-bridge) power, 2-channel data-out 1: 2 (half-bridge). 1 (full-bridge) on-board power @@ -39,6 +43,9 @@ Optional properties: - st,thermal-warning-recover: If present, thermal warning recovery is enabled. + - st,fault-detect-recovery: + If present, fault detect recovery is enabled. + - st,thermal-warning-adjustment: If present, thermal warning adjustment is enabled. @@ -76,6 +83,8 @@ Example: codec: sta32x@38 { compatible = "st,sta32x"; reg = <0x1c>; + clocks = <&clock>; + clock-names = "xti"; reset-gpios = <&gpio1 19 0>; power-down-gpios = <&gpio1 16 0>; st,output-conf = /bits/ 8 <0x3>; // set output to 2-channel diff --git a/Documentation/devicetree/bindings/sound/st,stm32-sai.txt b/Documentation/devicetree/bindings/sound/st,stm32-sai.txt @@ -31,7 +31,11 @@ SAI subnodes required properties: - reg: Base address and size of SAI sub-block register set. - clocks: Must contain one phandle and clock specifier pair for sai_ck which feeds the internal clock generator. + If the SAI shares a master clock, with another SAI set as MCLK + clock provider, SAI provider phandle must be specified here. - clock-names: Must contain "sai_ck". + Must also contain "MCLK", if SAI shares a master clock, + with a SAI set as MCLK clock provider. - dmas: see Documentation/devicetree/bindings/dma/stm32-dma.txt - dma-names: identifier string for each DMA request line "tx": if sai sub-block is configured as playback DAI @@ -51,6 +55,9 @@ SAI subnodes Optional properties: configured according to protocol defined in related DAI link node, such as i2s, left justified, right justified, dsp and pdm protocols. Note: ac97 protocol is not supported by SAI driver + - #clock-cells: should be 0. This property must be present if the SAI device + is a master clock provider, according to clocks bindings, described in + Documentation/devicetree/bindings/clock/clock-bindings.txt. The device node should contain one 'port' child node with one child 'endpoint' node, according to the bindings defined in Documentation/devicetree/bindings/ diff --git a/Documentation/devicetree/bindings/sound/sun4i-i2s.txt b/Documentation/devicetree/bindings/sound/sun4i-i2s.txt @@ -10,6 +10,7 @@ Required properties: - "allwinner,sun6i-a31-i2s" - "allwinner,sun8i-a83t-i2s" - "allwinner,sun8i-h3-i2s" + - "allwinner,sun50i-a64-codec-i2s" - reg: physical base address of the controller and length of memory mapped region. - interrupts: should contain the I2S interrupt. @@ -26,6 +27,7 @@ Required properties for the following compatibles: - "allwinner,sun6i-a31-i2s" - "allwinner,sun8i-a83t-i2s" - "allwinner,sun8i-h3-i2s" + - "allwinner,sun50i-a64-codec-i2s" - resets: phandle to the reset line for this codec Example: diff --git a/Documentation/devicetree/bindings/sound/sun50i-codec-analog.txt b/Documentation/devicetree/bindings/sound/sun50i-codec-analog.txt @@ -0,0 +1,12 @@ +* Allwinner A64 Codec Analog Controls + +Required properties: +- compatible: must be one of the following compatibles: + - "allwinner,sun50i-a64-codec-analog" +- reg: must contain the registers location and length + +Example: + codec_analog: codec-analog@1f015c0 { + compatible = "allwinner,sun50i-a64-codec-analog"; + reg = <0x01f015c0 0x4>; + }; diff --git a/Documentation/devicetree/bindings/sound/ts3a227e.txt b/Documentation/devicetree/bindings/sound/ts3a227e.txt @@ -14,7 +14,7 @@ Required properties: Optional properies: - ti,micbias: Intended MICBIAS voltage (datasheet section 9.6.7). - Select 0/1/2/3/4/5/6/7 to specify MACBIAS voltage + Select 0/1/2/3/4/5/6/7 to specify MICBIAS voltage 2.1V/2.2V/2.3V/2.4V/2.5V/2.6V/2.7V/2.8V Default value is "1" (2.2V). diff --git a/Documentation/devicetree/bindings/sound/wm8782.txt b/Documentation/devicetree/bindings/sound/wm8782.txt @@ -0,0 +1,17 @@ +WM8782 stereo ADC + +This device does not have any control interface or reset pins. + +Required properties: + + - compatible : "wlf,wm8782" + - Vdda-supply : phandle to a regulator for the analog power supply (2.7V - 5.5V) + - Vdd-supply : phandle to a regulator for the digital power supply (2.7V - 3.6V) + +Example: + +wm8782: stereo-adc { + compatible = "wlf,wm8782"; + Vdda-supply = <&vdda_supply>; + Vdd-supply = <&vdd_supply>; +}; diff --git a/Documentation/devicetree/bindings/trivial-devices.txt b/Documentation/devicetree/bindings/trivial-devices.txt @@ -35,7 +35,6 @@ at,24c08 i2c serial eeprom (24cxx) atmel,at97sc3204t i2c trusted platform module (TPM) capella,cm32181 CM32181: Ambient Light Sensor capella,cm3232 CM3232: Ambient Light Sensor -cirrus,cs42l51 Cirrus Logic CS42L51 audio codec dallas,ds1374 I2C, 32-Bit Binary Counter Watchdog RTC with Trickle Charger and Reset Input/Output dallas,ds1631 High-Precision Digital Thermometer dallas,ds1672 Dallas DS1672 Real-time Clock diff --git a/Documentation/devicetree/bindings/vendor-prefixes.txt b/Documentation/devicetree/bindings/vendor-prefixes.txt @@ -235,6 +235,7 @@ micrel Micrel Inc. microchip Microchip Technology Inc. microcrystal Micro Crystal AG micron Micron Technology Inc. +mikroe MikroElektronika d.o.o. minix MINIX Technology Ltd. miramems MiraMEMS Sensing Technology Co., Ltd. mitsubishi Mitsubishi Electric Corporation diff --git a/Documentation/sound/hd-audio/models.rst b/Documentation/sound/hd-audio/models.rst @@ -309,6 +309,8 @@ asus-nx50 ASUS Nx50 fixups asus-nx51 ASUS Nx51 fixups +asus-g751 + ASUS G751 fixups alc891-headset Headset mode support on ALC891 alc891-headset-multi diff --git a/Documentation/sound/kernel-api/writing-an-alsa-driver.rst b/Documentation/sound/kernel-api/writing-an-alsa-driver.rst @@ -3,8 +3,6 @@ Writing an ALSA Driver ====================== :Author: Takashi Iwai <tiwai@suse.de> -:Date: Oct 15, 2007 -:Edition: 0.3.7 Preface ======= @@ -21,11 +19,6 @@ explain the general topic of linux kernel coding and doesn't cover low-level driver implementation details. It only describes the standard way to write a PCI sound driver on ALSA. -If you are already familiar with the older ALSA ver.0.5.x API, you can -check the drivers such as ``sound/pci/es1938.c`` or -``sound/pci/maestro3.c`` which have also almost the same code-base in -the ALSA 0.5.x tree, so you can compare the differences. - This document is still a draft version. Any feedback and corrections, please!! @@ -35,24 +28,7 @@ File Tree Structure General ------- -The ALSA drivers are provided in two ways. - -One is the trees provided as a tarball or via cvs from the ALSA's ftp -site, and another is the 2.6 (or later) Linux kernel tree. To -synchronize both, the ALSA driver tree is split into two different -trees: alsa-kernel and alsa-driver. The former contains purely the -source code for the Linux 2.6 (or later) tree. This tree is designed -only for compilation on 2.6 or later environment. The latter, -alsa-driver, contains many subtle files for compiling ALSA drivers -outside of the Linux kernel tree, wrapper functions for older 2.2 and -2.4 kernels, to adapt the latest kernel API, and additional drivers -which are still in development or in tests. The drivers in alsa-driver -tree will be moved to alsa-kernel (and eventually to the 2.6 kernel -tree) when they are finished and confirmed to work fine. - -The file tree structure of ALSA driver is depicted below. Both -alsa-kernel and alsa-driver have almost the same file structure, except -for “core” directory. It's named as “acore” in alsa-driver tree. +The file tree structure of ALSA driver is depicted below. :: @@ -61,14 +37,11 @@ for “core” directory. It's named as “acore” in alsa-driver tree. /oss /seq /oss - /instr - /ioctl32 /include /drivers /mpu401 /opl3 /i2c - /l3 /synth /emux /pci @@ -80,6 +53,7 @@ for “core” directory. It's named as “acore” in alsa-driver tree. /sparc /usb /pcmcia /(cards) + /soc /oss @@ -99,13 +73,6 @@ directory. The rawmidi OSS emulation is included in the ALSA rawmidi code since it's quite small. The sequencer code is stored in ``core/seq/oss`` directory (see `below <#core-seq-oss>`__). -core/ioctl32 -~~~~~~~~~~~~ - -This directory contains the 32bit-ioctl wrappers for 64bit architectures -such like x86-64, ppc64 and sparc64. For 32bit and alpha architectures, -these are not compiled. - core/seq ~~~~~~~~ @@ -119,11 +86,6 @@ core/seq/oss This contains the OSS sequencer emulation codes. -core/seq/instr -~~~~~~~~~~~~~~ - -This directory contains the modules for the sequencer instrument layer. - include directory ----------------- @@ -161,11 +123,6 @@ Although there is a standard i2c layer on Linux, ALSA has its own i2c code for some cards, because the soundcard needs only a simple operation and the standard i2c API is too complicated for such a purpose. -i2c/l3 -~~~~~~ - -This is a sub-directory for ARM L3 i2c. - synth directory --------------- @@ -209,11 +166,19 @@ The PCMCIA, especially PCCard drivers will go here. CardBus drivers will be in the pci directory, because their API is identical to that of standard PCI cards. +soc directory +------------- + +This directory contains the codes for ASoC (ALSA System on Chip) +layer including ASoC core, codec and machine drivers. + oss directory ------------- -The OSS/Lite source files are stored here in Linux 2.6 (or later) tree. -In the ALSA driver tarball, this directory is empty, of course :) +Here contains OSS/Lite codes. +All codes have been deprecated except for dmasound on m68k as of +writing this. + Basic Flow for PCI Drivers ========================== @@ -352,10 +317,8 @@ to details explained in the following section. /* (3) */ err = snd_mychip_create(card, pci, &chip); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto error; /* (4) */ strcpy(card->driver, "My Chip"); @@ -368,22 +331,23 @@ to details explained in the following section. /* (6) */ err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto error; /* (7) */ pci_set_drvdata(pci, card); dev++; return 0; + + error: + snd_card_free(card); + return err; } /* destructor -- see the "Destructor" sub-section */ static void snd_mychip_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } @@ -445,14 +409,26 @@ In this part, the PCI resources are allocated. struct mychip *chip; .... err = snd_mychip_create(card, pci, &chip); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto error; The details will be explained in the section `PCI Resource Management`_. +When something goes wrong, the probe function needs to deal with the +error. In this example, we have a single error handling path placed +at the end of the function. + +:: + + error: + snd_card_free(card); + return err; + +Since each component can be properly freed, the single +:c:func:`snd_card_free()` call should suffice in most cases. + + 4) Set the driver ID and name strings. ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~ @@ -486,10 +462,8 @@ too. :: err = snd_card_register(card); - if (err < 0) { - snd_card_free(card); - return err; - } + if (err < 0) + goto error; Will be explained in the section `Management of Cards and Components`_, too. @@ -513,14 +487,13 @@ The destructor, remove callback, simply releases the card instance. Then the ALSA middle layer will release all the attached components automatically. -It would be typically like the following: +It would be typically just :c:func:`calling snd_card_free()`: :: static void snd_mychip_remove(struct pci_dev *pci) { snd_card_free(pci_get_drvdata(pci)); - pci_set_drvdata(pci, NULL); } @@ -546,7 +519,7 @@ in the source file. If the code is split into several files, the files without module options don't need them. In addition to these headers, you'll need ``<linux/interrupt.h>`` for -interrupt handling, and ``<asm/io.h>`` for I/O access. If you use the +interrupt handling, and ``<linux/io.h>`` for I/O access. If you use the :c:func:`mdelay()` or :c:func:`udelay()` functions, you'll need to include ``<linux/delay.h>`` too. @@ -720,6 +693,13 @@ function, which will call the real destructor. where :c:func:`snd_mychip_free()` is the real destructor. +The demerit of this method is the obviously more amount of codes. +The merit is, however, you can trigger the own callback at registering +and disconnecting the card via setting in snd_device_ops. +About the registering and disconnecting the card, see the subsections +below. + + Registration and Release ------------------------ @@ -905,10 +885,8 @@ Resource Allocation ------------------- The allocation of I/O ports and irqs is done via standard kernel -functions. Unlike ALSA ver.0.5.x., there are no helpers for that. And -these resources must be released in the destructor function (see below). -Also, on ALSA 0.9.x, you don't need to allocate (pseudo-)DMA for PCI -like in ALSA 0.5.x. +functions. These resources must be released in the destructor +function (see below). Now assume that the PCI device has an I/O port with 8 bytes and an interrupt. Then :c:type:`struct mychip <mychip>` will have the @@ -1064,7 +1042,8 @@ and the allocation would be like below: :: - if ((err = pci_request_regions(pci, "My Chip")) < 0) { + err = pci_request_regions(pci, "My Chip"); + if (err < 0) { kfree(chip); return err; } @@ -1086,6 +1065,21 @@ and the corresponding destructor would be: .... } +Of course, a modern way with :c:func:`pci_iomap()` will make things a +bit easier, too. + +:: + + err = pci_request_regions(pci, "My Chip"); + if (err < 0) { + kfree(chip); + return err; + } + chip->iobase_virt = pci_iomap(pci, 0, 0); + +which is paired with :c:func:`pci_iounmap()` at destructor. + + PCI Entries ----------- @@ -1154,13 +1148,6 @@ And at last, the module entries: Note that these module entries are tagged with ``__init`` and ``__exit`` prefixes. -Oh, one thing was forgotten. If you have no exported symbols, you need -to declare it in 2.2 or 2.4 kernels (it's not necessary in 2.6 kernels). - -:: - - EXPORT_NO_SYMBOLS; - That's all! PCM Interface @@ -2113,6 +2100,16 @@ non-contiguous buffers. The mmap calls this callback to get the page address. Some examples will be explained in the later section `Buffer and Memory Management`_, too. +mmap calllback +~~~~~~~~~~~~~~ + +This is another optional callback for controlling mmap behavior. +Once when defined, PCM core calls this callback when a page is +memory-mapped instead of dealing via the standard helper. +If you need special handling (due to some architecture or +device-specific issues), implement everything here as you like. + + PCM Interrupt Handler --------------------- @@ -2370,6 +2367,27 @@ to define the inverse rule: hw_rule_format_by_channels, NULL, SNDRV_PCM_HW_PARAM_CHANNELS, -1); +One typical usage of the hw constraints is to align the buffer size +with the period size. As default, ALSA PCM core doesn't enforce the +buffer size to be aligned with the period size. For example, it'd be +possible to have a combination like 256 period bytes with 999 buffer +bytes. + +Many device chips, however, require the buffer to be a multiple of +periods. In such a case, call +:c:func:`snd_pcm_hw_constraint_integer()` for +``SNDRV_PCM_HW_PARAM_PERIODS``. + +:: + + snd_pcm_hw_constraint_integer(substream->runtime, + SNDRV_PCM_HW_PARAM_PERIODS); + +This assures that the number of periods is integer, hence the buffer +size is aligned with the period size. + +The hw constraint is a very much powerful mechanism to define the +preferred PCM configuration, and there are relevant helpers. I won't give more details here, rather I would like to say, “Luke, use the source.” @@ -3712,7 +3730,14 @@ example, for an intermediate buffer. Since the allocated pages are not contiguous, you need to set the ``page`` callback to obtain the physical address at every offset. -The implementation of ``page`` callback would be like this: +The easiest way to achieve it would be to use +:c:func:`snd_pcm_lib_alloc_vmalloc_buffer()` for allocating the buffer +via :c:func:`vmalloc()`, and set :c:func:`snd_pcm_sgbuf_ops_page()` to +the ``page`` callback. At release, you need to call +:c:func:`snd_pcm_lib_free_vmalloc_buffer()`. + +If you want to implementation the ``page`` manually, it would be like +this: :: @@ -3848,7 +3873,9 @@ Power Management If the chip is supposed to work with suspend/resume functions, you need to add power-management code to the driver. The additional code for -power-management should be ifdef-ed with ``CONFIG_PM``. +power-management should be ifdef-ed with ``CONFIG_PM``, or annotated +with __maybe_unused attribute; otherwise the compiler will complain +you. If the driver *fully* supports suspend/resume that is, the device can be properly resumed to its state when suspend was called, you can set the @@ -3879,18 +3906,16 @@ the case of PCI drivers, the callbacks look like below: :: - #ifdef CONFIG_PM - static int snd_my_suspend(struct pci_dev *pci, pm_message_t state) + static int __maybe_unused snd_my_suspend(struct device *dev) { .... /* do things for suspend */ return 0; } - static int snd_my_resume(struct pci_dev *pci) + static int __maybe_unused snd_my_resume(struct device *dev) { .... /* do things for suspend */ return 0; } - #endif The scheme of the real suspend job is as follows. @@ -3909,18 +3934,14 @@ The scheme of the real suspend job is as follows. 6. Stop the hardware if necessary. -7. Disable the PCI device by calling - :c:func:`pci_disable_device()`. Then, call - :c:func:`pci_save_state()` at last. - A typical code would be like: :: - static int mychip_suspend(struct pci_dev *pci, pm_message_t state) + static int __maybe_unused mychip_suspend(struct device *dev) { /* (1) */ - struct snd_card *card = pci_get_drvdata(pci); + struct snd_card *card = dev_get_drvdata(dev); struct mychip *chip = card->private_data; /* (2) */ snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); @@ -3932,9 +3953,6 @@ A typical code would be like: snd_mychip_save_registers(chip); /* (6) */ snd_mychip_stop_hardware(chip); - /* (7) */ - pci_disable_device(pci); - pci_save_state(pci); return 0; } @@ -3943,44 +3961,35 @@ The scheme of the real resume job is as follows. 1. Retrieve the card and the chip data. -2. Set up PCI. First, call :c:func:`pci_restore_state()`. Then - enable the pci device again by calling - :c:func:`pci_enable_device()`. Call - :c:func:`pci_set_master()` if necessary, too. +2. Re-initialize the chip. -3. Re-initialize the chip. +3. Restore the saved registers if necessary. -4. Restore the saved registers if necessary. +4. Resume the mixer, e.g. calling :c:func:`snd_ac97_resume()`. -5. Resume the mixer, e.g. calling :c:func:`snd_ac97_resume()`. +5. Restart the hardware (if any). -6. Restart the hardware (if any). - -7. Call :c:func:`snd_power_change_state()` with +6. Call :c:func:`snd_power_change_state()` with ``SNDRV_CTL_POWER_D0`` to notify the processes. A typical code would be like: :: - static int mychip_resume(struct pci_dev *pci) + static int __maybe_unused mychip_resume(struct pci_dev *pci) { /* (1) */ - struct snd_card *card = pci_get_drvdata(pci); + struct snd_card *card = dev_get_drvdata(dev); struct mychip *chip = card->private_data; /* (2) */ - pci_restore_state(pci); - pci_enable_device(pci); - pci_set_master(pci); - /* (3) */ snd_mychip_reinit_chip(chip); - /* (4) */ + /* (3) */ snd_mychip_restore_registers(chip); - /* (5) */ + /* (4) */ snd_ac97_resume(chip->ac97); - /* (6) */ + /* (5) */ snd_mychip_restart_chip(chip); - /* (7) */ + /* (6) */ snd_power_change_state(card, SNDRV_CTL_POWER_D0); return 0; } @@ -4046,15 +4055,14 @@ And next, set suspend/resume callbacks to the pci_driver. :: + static SIMPLE_DEV_PM_OPS(snd_my_pm_ops, mychip_suspend, mychip_resume); + static struct pci_driver driver = { .name = KBUILD_MODNAME, .id_table = snd_my_ids, .probe = snd_my_probe, .remove = snd_my_remove, - #ifdef CONFIG_PM - .suspend = snd_my_suspend, - .resume = snd_my_resume, - #endif + .driver.pm = &snd_my_pm_ops, }; Module Parameters @@ -4078,7 +4086,7 @@ variables, instead. ``enable`` option is not always necessary in this case, but it would be better to have a dummy option for compatibility. The module parameters must be declared with the standard -``module_param()()``, ``module_param_array()()`` and +``module_param()``, ``module_param_array()`` and :c:func:`MODULE_PARM_DESC()` macros. The typical coding would be like below: @@ -4094,15 +4102,14 @@ The typical coding would be like below: module_param_array(enable, bool, NULL, 0444); MODULE_PARM_DESC(enable, "Enable " CARD_NAME " soundcard."); -Also, don't forget to define the module description, classes, license -and devices. Especially, the recent modprobe requires to define the +Also, don't forget to define the module description and the license. +Especially, the recent modprobe requires to define the module license as GPL, etc., otherwise the system is shown as “tainted”. :: - MODULE_DESCRIPTION("My Chip"); + MODULE_DESCRIPTION("Sound driver for My Chip"); MODULE_LICENSE("GPL"); - MODULE_SUPPORTED_DEVICE("{{Vendor,My Chip Name}}"); How To Put Your Driver Into ALSA Tree @@ -4117,21 +4124,17 @@ a question now: how to put my own driver into the ALSA driver tree? Here Suppose that you create a new PCI driver for the card “xyz”. The card module name would be snd-xyz. The new driver is usually put into the -alsa-driver tree, ``alsa-driver/pci`` directory in the case of PCI -cards. Then the driver is evaluated, audited and tested by developers -and users. After a certain time, the driver will go to the alsa-kernel -tree (to the corresponding directory, such as ``alsa-kernel/pci``) and -eventually will be integrated into the Linux 2.6 tree (the directory -would be ``linux/sound/pci``). +alsa-driver tree, ``sound/pci`` directory in the case of PCI +cards. In the following sections, the driver code is supposed to be put into -alsa-driver tree. The two cases are covered: a driver consisting of a +Linux kernel tree. The two cases are covered: a driver consisting of a single source file and one consisting of several source files. Driver with A Single Source File -------------------------------- -1. Modify alsa-driver/pci/Makefile +1. Modify sound/pci/Makefile Suppose you have a file xyz.c. Add the following two lines @@ -4160,52 +4163,43 @@ Driver with A Single Source File For the details of Kconfig script, refer to the kbuild documentation. -3. Run cvscompile script to re-generate the configure script and build - the whole stuff again. - Drivers with Several Source Files --------------------------------- Suppose that the driver snd-xyz have several source files. They are -located in the new subdirectory, pci/xyz. +located in the new subdirectory, sound/pci/xyz. -1. Add a new directory (``xyz``) in ``alsa-driver/pci/Makefile`` as - below +1. Add a new directory (``sound/pci/xyz``) in ``sound/pci/Makefile`` + as below :: - obj-$(CONFIG_SND) += xyz/ + obj-$(CONFIG_SND) += sound/pci/xyz/ -2. Under the directory ``xyz``, create a Makefile +2. Under the directory ``sound/pci/xyz``, create a Makefile :: - ifndef SND_TOPDIR - SND_TOPDIR=../.. - endif - - include $(SND_TOPDIR)/toplevel.config - include $(SND_TOPDIR)/Makefile.conf - snd-xyz-objs := xyz.o abc.o def.o - obj-$(CONFIG_SND_XYZ) += snd-xyz.o - include $(SND_TOPDIR)/Rules.make - 3. Create the Kconfig entry This procedure is as same as in the last section. -4. Run cvscompile script to re-generate the configure script and build - the whole stuff again. Useful Functions ================ :c:func:`snd_printk()` and friends ---------------------------------------- +---------------------------------- + +.. note:: This subsection describes a few helper functions for + decorating a bit more on the standard :c:func:`printk()` & co. + However, in general, the use of such helpers is no longer recommended. + If possible, try to stick with the standard functions like + :c:func:`dev_err()` or :c:func:`pr_err()`. ALSA provides a verbose version of the :c:func:`printk()` function. If a kernel config ``CONFIG_SND_VERBOSE_PRINTK`` is set, this function @@ -4221,13 +4215,10 @@ just like :c:func:`snd_printk()`. If the ALSA is compiled without the debugging flag, it's ignored. :c:func:`snd_printdd()` is compiled in only when -``CONFIG_SND_DEBUG_VERBOSE`` is set. Please note that -``CONFIG_SND_DEBUG_VERBOSE`` is not set as default even if you configure -the alsa-driver with ``--with-debug=full`` option. You need to give -explicitly ``--with-debug=detect`` option instead. +``CONFIG_SND_DEBUG_VERBOSE`` is set. :c:func:`snd_BUG()` ------------------------- +------------------- It shows the ``BUG?`` message and stack trace as well as :c:func:`snd_BUG_ON()` at the point. It's useful to show that a @@ -4236,7 +4227,7 @@ fatal error happens there. When no debug flag is set, this macro is ignored. :c:func:`snd_BUG_ON()` ----------------------------- +---------------------- :c:func:`snd_BUG_ON()` macro is similar with :c:func:`WARN_ON()` macro. For example, snd_BUG_ON(!pointer); or diff --git a/MAINTAINERS b/MAINTAINERS @@ -14759,6 +14759,13 @@ L: netdev@vger.kernel.org S: Maintained F: drivers/net/ethernet/ti/netcp* +TI PCM3060 ASoC CODEC DRIVER +M: Kirill Marinushkin <kmarinushkin@birdec.tech> +L: alsa-devel@alsa-project.org (moderated for non-subscribers) +S: Maintained +F: Documentation/devicetree/bindings/sound/pcm3060.txt +F: sound/soc/codecs/pcm3060* + TI TAS571X FAMILY ASoC CODEC DRIVER M: Kevin Cernekee <cernekee@chromium.org> L: alsa-devel@alsa-project.org (moderated for non-subscribers) diff --git a/sound/pci/hda/hda_codec.h b/include/sound/hda_codec.h diff --git a/include/sound/memalloc.h b/include/sound/memalloc.h @@ -47,10 +47,13 @@ struct snd_dma_device { #define SNDRV_DMA_TYPE_UNKNOWN 0 /* not defined */ #define SNDRV_DMA_TYPE_CONTINUOUS 1 /* continuous no-DMA memory */ #define SNDRV_DMA_TYPE_DEV 2 /* generic device continuous */ +#define SNDRV_DMA_TYPE_DEV_UC 5 /* continuous non-cahced */ #ifdef CONFIG_SND_DMA_SGBUF #define SNDRV_DMA_TYPE_DEV_SG 3 /* generic device SG-buffer */ +#define SNDRV_DMA_TYPE_DEV_UC_SG 6 /* SG non-cached */ #else #define SNDRV_DMA_TYPE_DEV_SG SNDRV_DMA_TYPE_DEV /* no SG-buf support */ +#define SNDRV_DMA_TYPE_DEV_UC_SG SNDRV_DMA_TYPE_DEV_UC #endif #ifdef CONFIG_GENERIC_ALLOCATOR #define SNDRV_DMA_TYPE_DEV_IRAM 4 /* generic device iram-buffer */ diff --git a/include/sound/rawmidi.h b/include/sound/rawmidi.h @@ -171,6 +171,7 @@ int __snd_rawmidi_transmit_peek(struct snd_rawmidi_substream *substream, unsigned char *buffer, int count); int __snd_rawmidi_transmit_ack(struct snd_rawmidi_substream *substream, int count); +int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream); /* main midi functions */ diff --git a/include/sound/simple_card_utils.h b/include/sound/simple_card_utils.h @@ -51,29 +51,35 @@ int asoc_simple_card_parse_card_name(struct snd_soc_card *card, #define asoc_simple_card_parse_clk_cpu(dev, node, dai_link, simple_dai) \ asoc_simple_card_parse_clk(dev, node, dai_link->cpu_of_node, simple_dai, \ - dai_link->cpu_dai_name) + dai_link->cpu_dai_name, NULL) #define asoc_simple_card_parse_clk_codec(dev, node, dai_link, simple_dai) \ asoc_simple_card_parse_clk(dev, node, dai_link->codec_of_node, simple_dai,\ - dai_link->codec_dai_name) + dai_link->codec_dai_name, dai_link->codecs) int asoc_simple_card_parse_clk(struct device *dev, struct device_node *node, struct device_node *dai_of_node, struct asoc_simple_dai *simple_dai, - const char *name); + const char *dai_name, + struct snd_soc_dai_link_component *dlc); int asoc_simple_card_clk_enable(struct asoc_simple_dai *dai); void asoc_simple_card_clk_disable(struct asoc_simple_dai *dai); #define asoc_simple_card_parse_cpu(node, dai_link, \ list_name, cells_name, is_single_link) \ - asoc_simple_card_parse_dai(node, &dai_link->cpu_of_node, \ + asoc_simple_card_parse_dai(node, NULL, \ + &dai_link->cpu_of_node, \ &dai_link->cpu_dai_name, list_name, cells_name, is_single_link) #define asoc_simple_card_parse_codec(node, dai_link, list_name, cells_name) \ - asoc_simple_card_parse_dai(node, &dai_link->codec_of_node, \ - &dai_link->codec_dai_name, list_name, cells_name, NULL) + asoc_simple_card_parse_dai(node, dai_link->codecs, \ + &dai_link->codec_of_node, \ + &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_of_node, \ + asoc_simple_card_parse_dai(node, dai_link->platform, \ + &dai_link->platform_of_node, \ NULL, list_name, cells_name, NULL) int asoc_simple_card_parse_dai(struct device_node *node, + struct snd_soc_dai_link_component *dlc, struct device_node **endpoint_np, const char **dai_name, const char *list_name, @@ -81,12 +87,15 @@ int asoc_simple_card_parse_dai(struct device_node *node, int *is_single_links); #define asoc_simple_card_parse_graph_cpu(ep, dai_link) \ - asoc_simple_card_parse_graph_dai(ep, &dai_link->cpu_of_node, \ + asoc_simple_card_parse_graph_dai(ep, NULL, \ + &dai_link->cpu_of_node, \ &dai_link->cpu_dai_name) #define asoc_simple_card_parse_graph_codec(ep, dai_link) \ - asoc_simple_card_parse_graph_dai(ep, &dai_link->codec_of_node, \ + asoc_simple_card_parse_graph_dai(ep, dai_link->codecs, \ + &dai_link->codec_of_node, \ &dai_link->codec_dai_name) int asoc_simple_card_parse_graph_dai(struct device_node *ep, + struct snd_soc_dai_link_component *dlc, struct device_node **endpoint_np, const char **dai_name); diff --git a/include/sound/soc-acpi-intel-match.h b/include/sound/soc-acpi-intel-match.h @@ -25,4 +25,10 @@ extern struct snd_soc_acpi_mach snd_soc_acpi_intel_bxt_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_glk_machines[]; extern struct snd_soc_acpi_mach snd_soc_acpi_intel_cnl_machines[]; +/* + * generic table used for HDA codec-based platforms, possibly with + * additional ACPI-enumerated codecs + */ +extern struct snd_soc_acpi_mach snd_soc_acpi_intel_hda_machines[]; + #endif diff --git a/include/sound/soc-dapm.h b/include/sound/soc-dapm.h @@ -406,12 +406,6 @@ int snd_soc_dapm_new_dai_widgets(struct snd_soc_dapm_context *dapm, struct snd_soc_dai *dai); 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_new_pcm(struct snd_soc_card *card, - struct snd_soc_pcm_runtime *rtd, - const struct snd_soc_pcm_stream *params, - unsigned int num_params, - struct snd_soc_dapm_widget *source, - struct snd_soc_dapm_widget *sink); /* dapm path setup */ int snd_soc_dapm_new_widgets(struct snd_soc_card *card); @@ -590,9 +584,6 @@ struct snd_soc_dapm_widget { void *priv; /* widget specific data */ struct regulator *regulator; /* attached regulator */ struct pinctrl *pinctrl; /* attached pinctrl */ - const struct snd_soc_pcm_stream *params; /* params for dai links */ - unsigned int num_params; /* number of params for dai links */ - unsigned int params_select; /* currently selected param for dai link */ /* dapm control */ int reg; /* negative reg = no direct dapm */ diff --git a/include/sound/soc-dpcm.h b/include/sound/soc-dpcm.h @@ -103,6 +103,16 @@ struct snd_soc_dpcm_runtime { int trigger_pending; /* trigger cmd + 1 if pending, 0 if not */ }; +#define for_each_dpcm_fe(be, stream, dpcm) \ + list_for_each_entry(dpcm, &(be)->dpcm[stream].fe_clients, list_fe) + +#define for_each_dpcm_be(fe, stream, dpcm) \ + list_for_each_entry(dpcm, &(fe)->dpcm[stream].be_clients, list_be) +#define for_each_dpcm_be_safe(fe, stream, dpcm, _dpcm) \ + list_for_each_entry_safe(dpcm, _dpcm, &(fe)->dpcm[stream].be_clients, list_be) +#define for_each_dpcm_be_rollback(fe, stream, dpcm) \ + list_for_each_entry_continue_reverse(dpcm, &(fe)->dpcm[stream].be_clients, list_be) + /* can this BE stop and free */ int snd_soc_dpcm_can_be_free_stop(struct snd_soc_pcm_runtime *fe, struct snd_soc_pcm_runtime *be, int stream); diff --git a/include/sound/soc.h b/include/sound/soc.h @@ -372,6 +372,11 @@ #define SND_SOC_COMP_ORDER_LATE 1 #define SND_SOC_COMP_ORDER_LAST 2 +#define for_each_comp_order(order) \ + for (order = SND_SOC_COMP_ORDER_FIRST; \ + order <= SND_SOC_COMP_ORDER_LAST; \ + order++) + /* * Bias levels * @@ -859,6 +864,11 @@ struct snd_soc_component { #endif }; +#define for_each_component_dais(component, dai)\ + list_for_each_entry(dai, &(component)->dai_list, list) +#define for_each_component_dais_safe(component, dai, _dai)\ + list_for_each_entry_safe(dai, _dai, &(component)->dai_list, list) + struct snd_soc_rtdcom_list { struct snd_soc_component *component; struct list_head list; /* rtd::component_list */ @@ -915,6 +925,8 @@ struct snd_soc_dai_link { */ const char *platform_name; struct device_node *platform_of_node; + struct snd_soc_dai_link_component *platform; + int id; /* optional ID for machine driver link identification */ const struct snd_soc_pcm_stream *params; @@ -976,6 +988,10 @@ struct snd_soc_dai_link { struct list_head list; /* DAI link list of the soc card */ struct snd_soc_dobj dobj; /* For topology */ }; +#define for_each_link_codecs(link, i, codec) \ + for ((i) = 0; \ + ((i) < link->num_codecs) && ((codec) = &link->codecs[i]); \ + (i)++) struct snd_soc_codec_conf { /* @@ -1054,7 +1070,6 @@ struct snd_soc_card { struct snd_soc_dai_link *dai_link; /* predefined links only */ int num_links; /* predefined links only */ struct list_head dai_link_list; /* all links */ - int num_dai_links; struct list_head rtd_list; int num_rtd; @@ -1092,6 +1107,7 @@ struct snd_soc_card { /* lists of probed devices belonging to this card */ struct list_head component_dev_list; + struct list_head list; struct list_head widgets; struct list_head paths; @@ -1114,6 +1130,23 @@ struct snd_soc_card { void *drvdata; }; +#define for_each_card_prelinks(card, i, link) \ + for ((i) = 0; \ + ((i) < (card)->num_links) && ((link) = &(card)->dai_link[i]); \ + (i)++) + +#define for_each_card_links(card, link) \ + list_for_each_entry(dai_link, &(card)->dai_link_list, list) +#define for_each_card_links_safe(card, link, _link) \ + list_for_each_entry_safe(link, _link, &(card)->dai_link_list, list) + +#define for_each_card_rtds(card, rtd) \ + list_for_each_entry(rtd, &(card)->rtd_list, list) +#define for_each_card_rtds_safe(card, rtd, _rtd) \ + list_for_each_entry_safe(rtd, _rtd, &(card)->rtd_list, list) + +#define for_each_card_components(card, component) \ + list_for_each_entry(component, &(card)->component_dev_list, card_list) /* SoC machine DAI configuration, glues a codec and cpu DAI together */ struct snd_soc_pcm_runtime { @@ -1124,6 +1157,8 @@ struct snd_soc_pcm_runtime { enum snd_soc_pcm_subclass pcm_subclass; struct snd_pcm_ops ops; + unsigned int params_select; /* currently selected param for dai link */ + /* Dynamic PCM BE runtime data */ struct snd_soc_dpcm_runtime dpcm[2]; int fe_compr; @@ -1152,6 +1187,13 @@ struct snd_soc_pcm_runtime { unsigned int dev_registered:1; unsigned int pop_wait:1; }; +#define for_each_rtd_codec_dai(rtd, i, dai)\ + for ((i) = 0; \ + ((i) < rtd->num_codecs) && ((dai) = rtd->codec_dais[i]); \ + (i)++) +#define for_each_rtd_codec_dai_rollback(rtd, i, dai) \ + for (; ((i--) >= 0) && ((dai) = rtd->codec_dais[i]);) + /* mixer control */ struct soc_mixer_control { @@ -1359,6 +1401,7 @@ static inline void snd_soc_initialize_card_lists(struct snd_soc_card *card) INIT_LIST_HEAD(&card->dapm_list); INIT_LIST_HEAD(&card->aux_comp_list); INIT_LIST_HEAD(&card->component_dev_list); + INIT_LIST_HEAD(&card->list); } static inline bool snd_soc_volsw_is_stereo(struct soc_mixer_control *mc) diff --git a/include/uapi/sound/asound.h b/include/uapi/sound/asound.h @@ -752,7 +752,7 @@ struct snd_timer_info { #define SNDRV_TIMER_PSFLG_EARLY_EVENT (1<<2) /* write early event to the poll queue */ struct snd_timer_params { - unsigned int flags; /* flags - SNDRV_MIXER_PSFLG_* */ + unsigned int flags; /* flags - SNDRV_TIMER_PSFLG_* */ unsigned int ticks; /* requested resolution in ticks */ unsigned int queue_size; /* total size of queue (32-1024) */ unsigned int reserved0; /* reserved, was: failure locations */ diff --git a/sound/aoa/soundbus/i2sbus/core.c b/sound/aoa/soundbus/i2sbus/core.c @@ -157,18 +157,19 @@ static int i2sbus_add_dev(struct macio_dev *macio, struct device_node *child = NULL, *sound = NULL; struct resource *r; int i, layout = 0, rlen, ok = force; - static const char *rnames[] = { "i2sbus: %s (control)", - "i2sbus: %s (tx)", - "i2sbus: %s (rx)" }; + char node_name[6]; + static const char *rnames[] = { "i2sbus: %pOFn (control)", + "i2sbus: %pOFn (tx)", + "i2sbus: %pOFn (rx)" }; static irq_handler_t ints[] = { i2sbus_bus_intr, i2sbus_tx_intr, i2sbus_rx_intr }; - if (strlen(np->name) != 5) + if (snprintf(node_name, sizeof(node_name), "%pOFn", np) != 5) return 0; - if (strncmp(np->name, "i2s-", 4)) + if (strncmp(node_name, "i2s-", 4)) return 0; dev = kzalloc(sizeof(struct i2sbus_dev), GFP_KERNEL); @@ -228,13 +229,13 @@ static int i2sbus_add_dev(struct macio_dev *macio, dev->sound.pcmid = -1; dev->macio = macio; dev->control = control; - dev->bus_number = np->name[4] - 'a'; + dev->bus_number = node_name[4] - 'a'; INIT_LIST_HEAD(&dev->sound.codec_list); for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) { dev->interrupts[i] = -1; snprintf(dev->rnames[i], sizeof(dev->rnames[i]), - rnames[i], np->name); + rnames[i], np); } for (i = aoa_resource_i2smmio; i <= aoa_resource_rxdbdma; i++) { int irq = irq_of_parse_and_map(np, i); diff --git a/sound/arm/Kconfig b/sound/arm/Kconfig @@ -31,7 +31,6 @@ endif # SND_ARM config SND_PXA2XX_LIB tristate - select SND_AC97_CODEC if SND_PXA2XX_LIB_AC97 select SND_DMAENGINE_PCM config SND_PXA2XX_LIB_AC97 diff --git a/sound/core/memalloc.c b/sound/core/memalloc.c @@ -25,6 +25,9 @@ #include <linux/mm.h> #include <linux/dma-mapping.h> #include <linux/genalloc.h> +#ifdef CONFIG_X86 +#include <asm/set_memory.h> +#endif #include <sound/memalloc.h> /* @@ -82,31 +85,32 @@ EXPORT_SYMBOL(snd_free_pages); #ifdef CONFIG_HAS_DMA /* allocate the coherent DMA pages */ -static void *snd_malloc_dev_pages(struct device *dev, size_t size, dma_addr_t *dma) +static void snd_malloc_dev_pages(struct snd_dma_buffer *dmab, size_t size) { - int pg; gfp_t gfp_flags; - if (WARN_ON(!dma)) - return NULL; - pg = get_order(size); gfp_flags = GFP_KERNEL | __GFP_COMP /* compound page lets parts be mapped */ | __GFP_NORETRY /* don't trigger OOM-killer */ | __GFP_NOWARN; /* no stack trace print - this call is non-critical */ - return dma_alloc_coherent(dev, PAGE_SIZE << pg, dma, gfp_flags); + dmab->area = dma_alloc_coherent(dmab->dev.dev, size, &dmab->addr, + gfp_flags); +#ifdef CONFIG_X86 + if (dmab->area && dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC) + set_memory_wc((unsigned long)dmab->area, + PAGE_ALIGN(size) >> PAGE_SHIFT); +#endif } /* free the coherent DMA pages */ -static void snd_free_dev_pages(struct device *dev, size_t size, void *ptr, - dma_addr_t dma) +static void snd_free_dev_pages(struct snd_dma_buffer *dmab) { - int pg; - - if (ptr == NULL) - return; - pg = get_order(size); - dma_free_coherent(dev, PAGE_SIZE << pg, ptr, dma); +#ifdef CONFIG_X86 + if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC) + set_memory_wb((unsigned long)dmab->area, + PAGE_ALIGN(dmab->bytes) >> PAGE_SHIFT); +#endif + dma_free_coherent(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); } #ifdef CONFIG_GENERIC_ALLOCATOR @@ -199,12 +203,15 @@ int snd_dma_alloc_pages(int type, struct device *device, size_t size, */ dmab->dev.type = SNDRV_DMA_TYPE_DEV; #endif /* CONFIG_GENERIC_ALLOCATOR */ + /* fall through */ case SNDRV_DMA_TYPE_DEV: - dmab->area = snd_malloc_dev_pages(device, size, &dmab->addr); + case SNDRV_DMA_TYPE_DEV_UC: + snd_malloc_dev_pages(dmab, size); break; #endif #ifdef CONFIG_SND_DMA_SGBUF case SNDRV_DMA_TYPE_DEV_SG: + case SNDRV_DMA_TYPE_DEV_UC_SG: snd_malloc_sgbuf_pages(device, size, dmab, NULL); break; #endif @@ -275,11 +282,13 @@ void snd_dma_free_pages(struct snd_dma_buffer *dmab) break; #endif /* CONFIG_GENERIC_ALLOCATOR */ case SNDRV_DMA_TYPE_DEV: - snd_free_dev_pages(dmab->dev.dev, dmab->bytes, dmab->area, dmab->addr); + case SNDRV_DMA_TYPE_DEV_UC: + snd_free_dev_pages(dmab); break; #endif #ifdef CONFIG_SND_DMA_SGBUF case SNDRV_DMA_TYPE_DEV_SG: + case SNDRV_DMA_TYPE_DEV_UC_SG: snd_free_sgbuf_pages(dmab); break; #endif diff --git a/sound/core/oss/pcm_plugin.c b/sound/core/oss/pcm_plugin.c @@ -111,7 +111,7 @@ int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames) while (plugin->next) { if (plugin->dst_frames) frames = plugin->dst_frames(plugin, frames); - if (snd_BUG_ON(frames <= 0)) + if (snd_BUG_ON((snd_pcm_sframes_t)frames <= 0)) return -ENXIO; plugin = plugin->next; err = snd_pcm_plugin_alloc(plugin, frames); @@ -123,7 +123,7 @@ int snd_pcm_plug_alloc(struct snd_pcm_substream *plug, snd_pcm_uframes_t frames) while (plugin->prev) { if (plugin->src_frames) frames = plugin->src_frames(plugin, frames); - if (snd_BUG_ON(frames <= 0)) + if (snd_BUG_ON((snd_pcm_sframes_t)frames <= 0)) return -ENXIO; plugin = plugin->prev; err = snd_pcm_plugin_alloc(plugin, frames); diff --git a/sound/core/pcm_lib.c b/sound/core/pcm_lib.c @@ -2172,18 +2172,25 @@ snd_pcm_sframes_t __snd_pcm_lib_xfer(struct snd_pcm_substream *substream, if (err < 0) goto _end_unlock; + runtime->twake = runtime->control->avail_min ? : 1; + if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) + snd_pcm_update_hw_ptr(substream); + if (!is_playback && - runtime->status->state == SNDRV_PCM_STATE_PREPARED && - size >= runtime->start_threshold) { - err = snd_pcm_start(substream); - if (err < 0) + runtime->status->state == SNDRV_PCM_STATE_PREPARED) { + if (size >= runtime->start_threshold) { + err = snd_pcm_start(substream); + if (err < 0) + goto _end_unlock; + } else { + /* nothing to do */ + err = 0; goto _end_unlock; + } } - runtime->twake = runtime->control->avail_min ? : 1; - if (runtime->status->state == SNDRV_PCM_STATE_RUNNING) - snd_pcm_update_hw_ptr(substream); avail = snd_pcm_avail(substream); + while (size > 0) { snd_pcm_uframes_t frames, appl_ptr, appl_ofs; snd_pcm_uframes_t cont; diff --git a/sound/core/rawmidi.c b/sound/core/rawmidi.c @@ -1236,6 +1236,28 @@ int snd_rawmidi_transmit(struct snd_rawmidi_substream *substream, } EXPORT_SYMBOL(snd_rawmidi_transmit); +/** + * snd_rawmidi_proceed - Discard the all pending bytes and proceed + * @substream: rawmidi substream + * + * Return: the number of discarded bytes + */ +int snd_rawmidi_proceed(struct snd_rawmidi_substream *substream) +{ + struct snd_rawmidi_runtime *runtime = substream->runtime; + unsigned long flags; + int count = 0; + + spin_lock_irqsave(&runtime->lock, flags); + if (runtime->avail < runtime->buffer_size) { + count = runtime->buffer_size - runtime->avail; + __snd_rawmidi_transmit_ack(substream, count); + } + spin_unlock_irqrestore(&runtime->lock, flags); + return count; +} +EXPORT_SYMBOL(snd_rawmidi_proceed); + static long snd_rawmidi_kernel_write1(struct snd_rawmidi_substream *substream, const unsigned char __user *userbuf, const unsigned char *kernelbuf, diff --git a/sound/core/seq/oss/seq_oss_timer.c b/sound/core/seq/oss/seq_oss_timer.c @@ -92,7 +92,7 @@ snd_seq_oss_process_timer_event(struct seq_oss_timer *rec, union evrec *ev) case TMR_WAIT_REL: parm += rec->cur_tick; rec->realtime = 0; - /* fall through and continue to next */ + /* fall through */ case TMR_WAIT_ABS: if (parm == 0) { rec->realtime = 1; diff --git a/sound/core/seq/seq_system.c b/sound/core/seq/seq_system.c @@ -123,6 +123,7 @@ int __init snd_seq_system_client_init(void) { struct snd_seq_port_callback pcallbacks; struct snd_seq_port_info *port; + int err; port = kzalloc(sizeof(*port), GFP_KERNEL); if (!port) @@ -134,6 +135,10 @@ int __init snd_seq_system_client_init(void) /* register client */ sysclient = snd_seq_create_kernel_client(NULL, 0, "System"); + if (sysclient < 0) { + kfree(port); + return sysclient; + } /* register timer */ strcpy(port->name, "Timer"); @@ -144,7 +149,10 @@ int __init snd_seq_system_client_init(void) port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; port->addr.client = sysclient; port->addr.port = SNDRV_SEQ_PORT_SYSTEM_TIMER; - snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port); + err = snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, + port); + if (err < 0) + goto error_port; /* register announcement port */ strcpy(port->name, "Announce"); @@ -154,16 +162,24 @@ int __init snd_seq_system_client_init(void) port->flags = SNDRV_SEQ_PORT_FLG_GIVEN_PORT; port->addr.client = sysclient; port->addr.port = SNDRV_SEQ_PORT_SYSTEM_ANNOUNCE; - snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, port); + err = snd_seq_kernel_client_ctl(sysclient, SNDRV_SEQ_IOCTL_CREATE_PORT, + port); + if (err < 0) + goto error_port; announce_port = port->addr.port; kfree(port); return 0; + + error_port: + snd_seq_system_client_done(); + kfree(port); + return err; } /* unregister our internal client */ -void __exit snd_seq_system_client_done(void) +void snd_seq_system_client_done(void) { int oldsysclient = sysclient; diff --git a/sound/core/seq/seq_virmidi.c b/sound/core/seq/seq_virmidi.c @@ -149,9 +149,7 @@ static void snd_vmidi_output_work(struct work_struct *work) /* discard the outputs in dispatch mode unless subscribed */ if (vmidi->seq_mode == SNDRV_VIRMIDI_SEQ_DISPATCH && !(vmidi->rdev->flags & SNDRV_VIRMIDI_SUBSCRIBE)) { - char buf[32]; - while (snd_rawmidi_transmit(substream, buf, sizeof(buf)) > 0) - ; /* ignored */ + snd_rawmidi_proceed(substream); return; } diff --git a/sound/core/sgbuf.c b/sound/core/sgbuf.c @@ -23,6 +23,7 @@ #include <linux/mm.h> #include <linux/vmalloc.h> #include <linux/export.h> +#include <asm/pgtable.h> #include <sound/memalloc.h> @@ -43,6 +44,8 @@ int snd_free_sgbuf_pages(struct snd_dma_buffer *dmab) dmab->area = NULL; tmpb.dev.type = SNDRV_DMA_TYPE_DEV; + if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC_SG) + tmpb.dev.type = SNDRV_DMA_TYPE_DEV_UC; tmpb.dev.dev = sgbuf->dev; for (i = 0; i < sgbuf->pages; i++) { if (!(sgbuf->table[i].addr & ~PAGE_MASK)) @@ -72,12 +75,20 @@ void *snd_malloc_sgbuf_pages(struct device *device, struct snd_dma_buffer tmpb; struct snd_sg_page *table; struct page **pgtable; + int type = SNDRV_DMA_TYPE_DEV; + pgprot_t prot = PAGE_KERNEL; dmab->area = NULL; dmab->addr = 0; dmab->private_data = sgbuf = kzalloc(sizeof(*sgbuf), GFP_KERNEL); if (! sgbuf) return NULL; + if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_UC_SG) { + type = SNDRV_DMA_TYPE_DEV_UC; +#ifdef pgprot_noncached + prot = pgprot_noncached(PAGE_KERNEL); +#endif + } sgbuf->dev = device; pages = snd_sgbuf_aligned_pages(size); sgbuf->tblsize = sgbuf_align_table(pages); @@ -98,7 +109,7 @@ void *snd_malloc_sgbuf_pages(struct device *device, if (chunk > maxpages) chunk = maxpages; chunk <<= PAGE_SHIFT; - if (snd_dma_alloc_pages_fallback(SNDRV_DMA_TYPE_DEV, device, + if (snd_dma_alloc_pages_fallback(type, device, chunk, &tmpb) < 0) { if (!sgbuf->pages) goto _failed; @@ -125,7 +136,7 @@ void *snd_malloc_sgbuf_pages(struct device *device, } sgbuf->size = size; - dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, PAGE_KERNEL); + dmab->area = vmap(sgbuf->page_table, sgbuf->pages, VM_MAP, prot); if (! dmab->area) goto _failed; if (res_size) diff --git a/sound/firewire/Kconfig b/sound/firewire/Kconfig @@ -147,7 +147,9 @@ config SND_FIREWIRE_MOTU help Say Y here to enable support for FireWire devices which MOTU produced: * 828mk2 + * Traveler * 828mk3 + * Audio Express To compile this driver as a module, choose M here: the module will be called snd-firewire-motu. diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c @@ -140,6 +140,59 @@ const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = { }; EXPORT_SYMBOL(amdtp_rate_table); +static int apply_constraint_to_size(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *s = hw_param_interval(params, rule->var); + const struct snd_interval *r = + hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval t = { + .min = s->min, .max = s->max, .integer = 1, + }; + int i; + + for (i = 0; i < CIP_SFC_COUNT; ++i) { + unsigned int rate = amdtp_rate_table[i]; + unsigned int step = amdtp_syt_intervals[i]; + + if (!snd_interval_test(r, rate)) + continue; + + t.min = roundup(t.min, step); + t.max = rounddown(t.max, step); + } + + if (snd_interval_checkempty(&t)) + return -EINVAL; + + return snd_interval_refine(s, &t); +} + +static int apply_constraint_to_rate(struct snd_pcm_hw_params *params, + struct snd_pcm_hw_rule *rule) +{ + struct snd_interval *r = + hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE); + const struct snd_interval *s = hw_param_interval_c(params, rule->deps[0]); + struct snd_interval t = { + .min = UINT_MAX, .max = 0, .integer = 1, + }; + int i; + + for (i = 0; i < CIP_SFC_COUNT; ++i) { + unsigned int step = amdtp_syt_intervals[i]; + unsigned int rate = amdtp_rate_table[i]; + + if (s->min % step || s->max % step) + continue; + + t.min = min(t.min, rate); + t.max = max(t.max, rate); + } + + return snd_interval_refine(r, &t); +} + /** * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream * @s: the AMDTP stream, which must be initialized. @@ -194,16 +247,27 @@ int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s, * number equals to SYT_INTERVAL. So the number is 8, 16 or 32, * depending on its sampling rate. For accurate period interrupt, it's * preferrable to align period/buffer sizes to current SYT_INTERVAL. - * - * TODO: These constraints can be improved with proper rules. - * Currently apply LCM of SYT_INTERVALs. */ - err = snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32); + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, + apply_constraint_to_size, NULL, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + goto end; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + apply_constraint_to_rate, NULL, + SNDRV_PCM_HW_PARAM_PERIOD_SIZE, -1); + if (err < 0) + goto end; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_BUFFER_SIZE, + apply_constraint_to_size, NULL, + SNDRV_PCM_HW_PARAM_RATE, -1); + if (err < 0) + goto end; + err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + apply_constraint_to_rate, NULL, + SNDRV_PCM_HW_PARAM_BUFFER_SIZE, -1); if (err < 0) goto end; - err = snd_pcm_hw_constraint_step(runtime, 0, - SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32); end: return err; } diff --git a/sound/firewire/bebob/bebob.c b/sound/firewire/bebob/bebob.c @@ -126,23 +126,6 @@ end: return err; } -static void bebob_free(struct snd_bebob *bebob) -{ - snd_bebob_stream_destroy_duplex(bebob); - fw_unit_put(bebob->unit); - - kfree(bebob->maudio_special_quirk); - - mutex_destroy(&bebob->mutex); - kfree(bebob); -} - -/* - * This module releases the FireWire unit data after all ALSA character devices - * are released by applications. This is for releasing stream data or finishing - * transactions safely. Thus at returning from .remove(), this module still keep - * references for the unit. - */ static void bebob_card_free(struct snd_card *card) { @@ -152,7 +135,7 @@ bebob_card_free(struct snd_card *card) clear_bit(bebob->card_index, devices_used); mutex_unlock(&devices_mutex); - bebob_free(card->private_data); + snd_bebob_stream_destroy_duplex(bebob); } static const struct snd_bebob_spec * @@ -192,7 +175,6 @@ do_registration(struct work_struct *work) return; mutex_lock(&devices_mutex); - for (card_index = 0; card_index < SNDRV_CARDS; card_index++) { if (!test_bit(card_index, devices_used) && enable[card_index]) break; @@ -208,6 +190,11 @@ do_registration(struct work_struct *work) mutex_unlock(&devices_mutex); return; } + set_bit(card_index, devices_used); + mutex_unlock(&devices_mutex); + + bebob->card->private_free = bebob_card_free; + bebob->card->private_data = bebob; err = name_device(bebob); if (err < 0) @@ -248,23 +235,10 @@ do_registration(struct work_struct *work) if (err < 0) goto error; - set_bit(card_index, devices_used); - mutex_unlock(&devices_mutex); - - /* - * After registered, bebob instance can be released corresponding to - * releasing the sound card instance. - */ - bebob->card->private_free = bebob_card_free; - bebob->card->private_data = bebob; bebob->registered = true; return; error: - mutex_unlock(&devices_mutex); - snd_bebob_stream_destroy_duplex(bebob); - kfree(bebob->maudio_special_quirk); - bebob->maudio_special_quirk = NULL; snd_card_free(bebob->card); dev_info(&bebob->unit->device, "Sound card registration failed: %d\n", err); @@ -295,15 +269,15 @@ bebob_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry) } /* Allocate this independent of sound card instance. */ - bebob = kzalloc(sizeof(struct snd_bebob), GFP_KERNEL); - if (bebob == NULL) + bebob = devm_kzalloc(&unit->device, sizeof(struct snd_bebob), + GFP_KERNEL); + if (!bebob) return -ENOMEM; - bebob->unit = fw_unit_get(unit); - bebob->entry = entry; - bebob->spec = spec; dev_set_drvdata(&unit->device, bebob); + bebob->entry = entry; + bebob->spec = spec; mutex_init(&bebob->mutex); spin_lock_init(&bebob->lock); init_waitqueue_head(&bebob->hwdep_wait); @@ -379,12 +353,12 @@ static void bebob_remove(struct fw_unit *unit) cancel_delayed_work_sync(&bebob->dwork); if (bebob->registered) { - /* No need to wait for releasing card object in this context. */ - snd_card_free_when_closed(bebob->card); - } else { - /* Don't forget this case. */ - bebob_free(bebob); + // Block till all of ALSA character devices are released. + snd_card_free(bebob->card); } + + mutex_destroy(&bebob->mutex); + fw_unit_put(bebob->unit); } static const struct snd_bebob_rate_spec normal_rate_spec = { diff --git a/sound/firewire/bebob/bebob_maudio.c b/sound/firewire/bebob/bebob_maudio.c @@ -261,8 +261,9 @@ snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814) struct special_params *params; int err; - params = kzalloc(sizeof(struct special_params), GFP_KERNEL); - if (params == NULL) + params = devm_kzalloc(&bebob->card->card_dev, + sizeof(struct special_params), GFP_KERNEL); + if (!params) return -ENOMEM; mutex_lock(&bebob->mutex); diff --git a/sound/firewire/dice/dice.c b/sound/firewire/dice/dice.c @@ -122,25 +122,12 @@ static void dice_card_strings(struct snd_dice *dice) strcpy(card->mixername, "DICE"); } -static void dice_free(struct snd_dice *dice) +static void dice_card_free(struct snd_card *card) { + struct snd_dice *dice = card->private_data; + snd_dice_stream_destroy_duplex(dice); snd_dice_transaction_destroy(dice); - fw_unit_put(dice->unit); - - mutex_destroy(&dice->mutex); - kfree(dice); -} - -/* - * This module releases the FireWire unit data after all ALSA character devices - * are released by applications. This is for releasing stream data or finishing - * transactions safely. Thus at returning from .remove(), this module still keep - * references for the unit. - */ -static void dice_card_free(struct snd_card *card) -{ - dice_free(card->private_data); } static void do_registration(struct work_struct *work) @@ -155,6 +142,8 @@ static void do_registration(struct work_struct *work) &dice->card); if (err < 0) return; + dice->card->private_free = dice_card_free; + dice->card->private_data = dice; err = snd_dice_transaction_init(dice); if (err < 0) @@ -192,19 +181,10 @@ static void do_registration(struct work_struct *work) if (err < 0) goto error; - /* - * After registered, dice instance can be released corresponding to - * releasing the sound card instance. - */ - dice->card->private_free = dice_card_free; - dice->card->private_data = dice; dice->registered = true; return; error: - snd_dice_stream_destroy_duplex(dice); - snd_dice_transaction_destroy(dice); - snd_dice_stream_destroy_duplex(dice); snd_card_free(dice->card); dev_info(&dice->unit->device, "Sound card registration failed: %d\n", err); @@ -223,10 +203,9 @@ static int dice_probe(struct fw_unit *unit, } /* Allocate this independent of sound card instance. */ - dice = kzalloc(sizeof(struct snd_dice), GFP_KERNEL); - if (dice == NULL) + dice = devm_kzalloc(&unit->device, sizeof(struct snd_dice), GFP_KERNEL); + if (!dice) return -ENOMEM; - dice->unit = fw_unit_get(unit); dev_set_drvdata(&unit->device, dice); @@ -263,10 +242,10 @@ static void dice_remove(struct fw_unit *unit) if (dice->registered) { /* No need to wait for releasing card object in this context. */ snd_card_free_when_closed(dice->card); - } else { - /* Don't forget this case. */ - dice_free(dice); } + + mutex_destroy(&dice->mutex); + fw_unit_put(dice->unit); } static void dice_bus_reset(struct fw_unit *unit) diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c @@ -41,20 +41,12 @@ static int name_card(struct snd_dg00x *dg00x) return 0; } -static void dg00x_free(struct snd_dg00x *dg00x) +static void dg00x_card_free(struct snd_card *card) { + struct snd_dg00x *dg00x = card->private_data; + snd_dg00x_stream_destroy_duplex(dg00x); snd_dg00x_transaction_unregister(dg00x); - - fw_unit_put(dg00x->unit); - - mutex_destroy(&dg00x->mutex); - kfree(dg00x); -} - -static void dg00x_card_free(struct snd_card *card) -{ - dg00x_free(card->private_data); } static void do_registration(struct work_struct *work) @@ -70,6 +62,8 @@ static void do_registration(struct work_struct *work) &dg00x->card); if (err < 0) return; + dg00x->card->private_free = dg00x_card_free; + dg00x->card->private_data = dg00x; err = name_card(dg00x); if (err < 0) @@ -101,14 +95,10 @@ static void do_registration(struct work_struct *work) if (err < 0) goto error; - dg00x->card->private_free = dg00x_card_free; - dg00x->card->private_data = dg00x; dg00x->registered = true; return; error: - snd_dg00x_transaction_unregister(dg00x); - snd_dg00x_stream_destroy_duplex(dg00x); snd_card_free(dg00x->card); dev_info(&dg00x->unit->device, "Sound card registration failed: %d\n", err); @@ -120,8 +110,9 @@ static int snd_dg00x_probe(struct fw_unit *unit, struct snd_dg00x *dg00x; /* Allocate this independent of sound card instance. */ - dg00x = kzalloc(sizeof(struct snd_dg00x), GFP_KERNEL); - if (dg00x == NULL) + dg00x = devm_kzalloc(&unit->device, sizeof(struct snd_dg00x), + GFP_KERNEL); + if (!dg00x) return -ENOMEM; dg00x->unit = fw_unit_get(unit); @@ -173,12 +164,12 @@ static void snd_dg00x_remove(struct fw_unit *unit) cancel_delayed_work_sync(&dg00x->dwork); if (dg00x->registered) { - /* No need to wait for releasing card object in this context. */ - snd_card_free_when_closed(dg00x->card); - } else { - /* Don't forget this case. */ - dg00x_free(dg00x); + // Block till all of ALSA character devices are released. + snd_card_free(dg00x->card); } + + mutex_destroy(&dg00x->mutex); + fw_unit_put(dg00x->unit); } static const struct ieee1394_device_id snd_dg00x_id_table[] = { diff --git a/sound/firewire/fireface/ff.c b/sound/firewire/fireface/ff.c @@ -27,20 +27,12 @@ static void name_card(struct snd_ff *ff) dev_name(&ff->unit->device), 100 << fw_dev->max_speed); } -static void ff_free(struct snd_ff *ff) +static void ff_card_free(struct snd_card *card) { + struct snd_ff *ff = card->private_data; + snd_ff_stream_destroy_duplex(ff); snd_ff_transaction_unregister(ff); - - fw_unit_put(ff->unit); - - mutex_destroy(&ff->mutex); - kfree(ff); -} - -static void ff_card_free(struct snd_card *card) -{ - ff_free(card->private_data); } static void do_registration(struct work_struct *work) @@ -55,6 +47,8 @@ static void do_registration(struct work_struct *work) &ff->card); if (err < 0) return; + ff->card->private_free = ff_card_free; + ff->card->private_data = ff; err = snd_ff_transaction_register(ff); if (err < 0) @@ -84,14 +78,10 @@ static void do_registration(struct work_struct *work) if (err < 0) goto error; - ff->card->private_free = ff_card_free; - ff->card->private_data = ff; ff->registered = true; return; error: - snd_ff_transaction_unregister(ff); - snd_ff_stream_destroy_duplex(ff); snd_card_free(ff->card); dev_info(&ff->unit->device, "Sound card registration failed: %d\n", err); @@ -102,11 +92,9 @@ static int snd_ff_probe(struct fw_unit *unit, { struct snd_ff *ff; - ff = kzalloc(sizeof(struct snd_ff), GFP_KERNEL); - if (ff == NULL) + ff = devm_kzalloc(&unit->device, sizeof(struct snd_ff), GFP_KERNEL); + if (!ff) return -ENOMEM; - - /* initialize myself */ ff->unit = fw_unit_get(unit); dev_set_drvdata(&unit->device, ff); @@ -149,12 +137,12 @@ static void snd_ff_remove(struct fw_unit *unit) cancel_work_sync(&ff->dwork.work); if (ff->registered) { - /* No need to wait for releasing card object in this context. */ - snd_card_free_when_closed(ff->card); - } else { - /* Don't forget this case. */ - ff_free(ff); + // Block till all of ALSA character devices are released. + snd_card_free(ff->card); } + + mutex_destroy(&ff->mutex); + fw_unit_put(ff->unit); } static const struct snd_ff_spec spec_ff400 = { diff --git a/sound/firewire/fireworks/fireworks.c b/sound/firewire/fireworks/fireworks.c @@ -184,36 +184,17 @@ end: return err; } -static void efw_free(struct snd_efw *efw) -{ - snd_efw_stream_destroy_duplex(efw); - snd_efw_transaction_remove_instance(efw); - fw_unit_put(efw->unit); - - kfree(efw->resp_buf); - - mutex_destroy(&efw->mutex); - kfree(efw); -} - -/* - * This module releases the FireWire unit data after all ALSA character devices - * are released by applications. This is for releasing stream data or finishing - * transactions safely. Thus at returning from .remove(), this module still keep - * references for the unit. - */ static void efw_card_free(struct snd_card *card) { struct snd_efw *efw = card->private_data; - if (efw->card_index >= 0) { - mutex_lock(&devices_mutex); - clear_bit(efw->card_index, devices_used); - mutex_unlock(&devices_mutex); - } + mutex_lock(&devices_mutex); + clear_bit(efw->card_index, devices_used); + mutex_unlock(&devices_mutex); - efw_free(card->private_data); + snd_efw_stream_destroy_duplex(efw); + snd_efw_transaction_remove_instance(efw); } static void @@ -226,9 +207,8 @@ do_registration(struct work_struct *work) if (efw->registered) return; - mutex_lock(&devices_mutex); - /* check registered cards */ + mutex_lock(&devices_mutex); for (card_index = 0; card_index < SNDRV_CARDS; ++card_index) { if (!test_bit(card_index, devices_used) && enable[card_index]) break; @@ -244,12 +224,18 @@ do_registration(struct work_struct *work) mutex_unlock(&devices_mutex); return; } + set_bit(card_index, devices_used); + mutex_unlock(&devices_mutex); + + efw->card->private_free = efw_card_free; + efw->card->private_data = efw; /* prepare response buffer */ snd_efw_resp_buf_size = clamp(snd_efw_resp_buf_size, SND_EFW_RESPONSE_MAXIMUM_BYTES, 4096U); - efw->resp_buf = kzalloc(snd_efw_resp_buf_size, GFP_KERNEL); - if (efw->resp_buf == NULL) { + efw->resp_buf = devm_kzalloc(&efw->card->card_dev, + snd_efw_resp_buf_size, GFP_KERNEL); + if (!efw->resp_buf) { err = -ENOMEM; goto error; } @@ -284,25 +270,11 @@ do_registration(struct work_struct *work) if (err < 0) goto error; - set_bit(card_index, devices_used); - mutex_unlock(&devices_mutex); - - /* - * After registered, efw instance can be released corresponding to - * releasing the sound card instance. - */ - efw->card->private_free = efw_card_free; - efw->card->private_data = efw; efw->registered = true; return; error: - mutex_unlock(&devices_mutex); - snd_efw_transaction_remove_instance(efw); - snd_efw_stream_destroy_duplex(efw); snd_card_free(efw->card); - kfree(efw->resp_buf); - efw->resp_buf = NULL; dev_info(&efw->unit->device, "Sound card registration failed: %d\n", err); } @@ -312,10 +284,9 @@ efw_probe(struct fw_unit *unit, const struct ieee1394_device_id *entry) { struct snd_efw *efw; - efw = kzalloc(sizeof(struct snd_efw), GFP_KERNEL); + efw = devm_kzalloc(&unit->device, sizeof(struct snd_efw), GFP_KERNEL); if (efw == NULL) return -ENOMEM; - efw->unit = fw_unit_get(unit); dev_set_drvdata(&unit->device, efw); @@ -363,12 +334,12 @@ static void efw_remove(struct fw_unit *unit) cancel_delayed_work_sync(&efw->dwork); if (efw->registered) { - /* No need to wait for releasing card object in this context. */ - snd_card_free_when_closed(efw->card); - } else { - /* Don't forget this case. */ - efw_free(efw); + // Block till all of ALSA character devices are released. + snd_card_free(efw->card); } + + mutex_destroy(&efw->mutex); + fw_unit_put(efw->unit); } static const struct ieee1394_device_id efw_id_table[] = { diff --git a/sound/firewire/isight.c b/sound/firewire/isight.c @@ -602,8 +602,6 @@ static void isight_card_free(struct snd_card *card) struct isight *isight = card->private_data; fw_iso_resources_destroy(&isight->resources); - fw_unit_put(isight->unit); - mutex_destroy(&isight->mutex); } static u64 get_unit_base(struct fw_unit *unit) @@ -640,7 +638,7 @@ static int isight_probe(struct fw_unit *unit, if (!isight->audio_base) { dev_err(&unit->device, "audio unit base not found\n"); err = -ENXIO; - goto err_unit; + goto error; } fw_iso_resources_init(&isight->resources, unit); @@ -669,12 +667,12 @@ static int isight_probe(struct fw_unit *unit, dev_set_drvdata(&unit->device, isight); return 0; - -err_unit: - fw_unit_put(isight->unit); - mutex_destroy(&isight->mutex); error: snd_card_free(card); + + mutex_destroy(&isight->mutex); + fw_unit_put(isight->unit); + return err; } @@ -703,7 +701,11 @@ static void isight_remove(struct fw_unit *unit) isight_stop_streaming(isight); mutex_unlock(&isight->mutex); - snd_card_free_when_closed(isight->card); + // Block till all of ALSA character devices are released. + snd_card_free(isight->card); + + mutex_destroy(&isight->mutex); + fw_unit_put(isight->unit); } static const struct ieee1394_device_id isight_id_table[] = { diff --git a/sound/firewire/motu/motu.c b/sound/firewire/motu/motu.c @@ -52,26 +52,12 @@ static void name_card(struct snd_motu *motu) dev_name(&motu->unit->device), 100 << fw_dev->max_speed); } -static void motu_free(struct snd_motu *motu) +static void motu_card_free(struct snd_card *card) { - snd_motu_transaction_unregister(motu); + struct snd_motu *motu = card->private_data; + snd_motu_transaction_unregister(motu); snd_motu_stream_destroy_duplex(motu); - fw_unit_put(motu->unit); - - mutex_destroy(&motu->mutex); - kfree(motu); -} - -/* - * This module releases the FireWire unit data after all ALSA character devices - * are released by applications. This is for releasing stream data or finishing - * transactions safely. Thus at returning from .remove(), this module still keep - * references for the unit. - */ -static void motu_card_free(struct snd_card *card) -{ - motu_free(card->private_data); } static void do_registration(struct work_struct *work) @@ -86,6 +72,8 @@ static void do_registration(struct work_struct *work) &motu->card); if (err < 0) return; + motu->card->private_free = motu_card_free; + motu->card->private_data = motu; name_card(motu); @@ -120,18 +108,10 @@ static void do_registration(struct work_struct *work) if (err < 0) goto error; - /* - * After registered, motu instance can be released corresponding to - * releasing the sound card instance. - */ - motu->card->private_free = motu_card_free; - motu->card->private_data = motu; motu->registered = true; return; error: - snd_motu_transaction_unregister(motu); - snd_motu_stream_destroy_duplex(motu); snd_card_free(motu->card); dev_info(&motu->unit->device, "Sound card registration failed: %d\n", err); @@ -143,14 +123,13 @@ static int motu_probe(struct fw_unit *unit, struct snd_motu *motu; /* Allocate this independently of sound card instance. */ - motu = kzalloc(sizeof(struct snd_motu), GFP_KERNEL); - if (motu == NULL) + motu = devm_kzalloc(&unit->device, sizeof(struct snd_motu), GFP_KERNEL); + if (!motu) return -ENOMEM; - - motu->spec = (const struct snd_motu_spec *)entry->driver_data; motu->unit = fw_unit_get(unit); dev_set_drvdata(&unit->device, motu); + motu->spec = (const struct snd_motu_spec *)entry->driver_data; mutex_init(&motu->mutex); spin_lock_init(&motu->lock); init_waitqueue_head(&motu->hwdep_wait); @@ -174,12 +153,12 @@ static void motu_remove(struct fw_unit *unit) cancel_delayed_work_sync(&motu->dwork); if (motu->registered) { - /* No need to wait for releasing card object in this context. */ - snd_card_free_when_closed(motu->card); - } else { - /* Don't forget this case. */ - motu_free(motu); + // Block till all of ALSA character devices are released. + snd_card_free(motu->card); } + + mutex_destroy(&motu->mutex); + fw_unit_put(motu->unit); } static void motu_bus_update(struct fw_unit *unit) diff --git a/sound/firewire/oxfw/oxfw-scs1x.c b/sound/firewire/oxfw/oxfw-scs1x.c @@ -372,8 +372,9 @@ int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw) struct fw_scs1x *scs; int err; - scs = kzalloc(sizeof(struct fw_scs1x), GFP_KERNEL); - if (scs == NULL) + scs = devm_kzalloc(&oxfw->card->card_dev, sizeof(struct fw_scs1x), + GFP_KERNEL); + if (!scs) return -ENOMEM; scs->fw_dev = fw_parent_device(oxfw->unit); oxfw->spec = scs; diff --git a/sound/firewire/oxfw/oxfw-spkr.c b/sound/firewire/oxfw/oxfw-spkr.c @@ -270,8 +270,9 @@ int snd_oxfw_add_spkr(struct snd_oxfw *oxfw, bool is_lacie) unsigned int i, first_ch; int err; - spkr = kzalloc(sizeof(struct fw_spkr), GFP_KERNEL); - if (spkr == NULL) + spkr = devm_kzalloc(&oxfw->card->card_dev, sizeof(struct fw_spkr), + GFP_KERNEL); + if (!spkr) return -ENOMEM; oxfw->spec = spkr; diff --git a/sound/firewire/oxfw/oxfw-stream.c b/sound/firewire/oxfw/oxfw-stream.c @@ -517,8 +517,9 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, if (err < 0) goto end; - formats[eid] = kmemdup(buf, *len, GFP_KERNEL); - if (formats[eid] == NULL) { + formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len, + GFP_KERNEL); + if (!formats[eid]) { err = -ENOMEM; goto end; } @@ -535,7 +536,8 @@ assume_stream_formats(struct snd_oxfw *oxfw, enum avc_general_plug_dir dir, continue; eid++; - formats[eid] = kmemdup(buf, *len, GFP_KERNEL); + formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, *len, + GFP_KERNEL); if (formats[eid] == NULL) { err = -ENOMEM; goto end; @@ -597,8 +599,9 @@ static int fill_stream_formats(struct snd_oxfw *oxfw, if (err < 0) break; - formats[eid] = kmemdup(buf, len, GFP_KERNEL); - if (formats[eid] == NULL) { + formats[eid] = devm_kmemdup(&oxfw->card->card_dev, buf, len, + GFP_KERNEL); + if (!formats[eid]) { err = -ENOMEM; break; } diff --git a/sound/firewire/oxfw/oxfw.c b/sound/firewire/oxfw/oxfw.c @@ -113,35 +113,13 @@ end: return err; } -static void oxfw_free(struct snd_oxfw *oxfw) +static void oxfw_card_free(struct snd_card *card) { - unsigned int i; + struct snd_oxfw *oxfw = card->private_data; snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); if (oxfw->has_output) snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); - - fw_unit_put(oxfw->unit); - - for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) { - kfree(oxfw->tx_stream_formats[i]); - kfree(oxfw->rx_stream_formats[i]); - } - - kfree(oxfw->spec); - mutex_destroy(&oxfw->mutex); - kfree(oxfw); -} - -/* - * This module releases the FireWire unit data after all ALSA character devices - * are released by applications. This is for releasing stream data or finishing - * transactions safely. Thus at returning from .remove(), this module still keep - * references for the unit. - */ -static void oxfw_card_free(struct snd_card *card) -{ - oxfw_free(card->private_data); } static int detect_quirks(struct snd_oxfw *oxfw) @@ -208,7 +186,6 @@ static int detect_quirks(struct snd_oxfw *oxfw) static void do_registration(struct work_struct *work) { struct snd_oxfw *oxfw = container_of(work, struct snd_oxfw, dwork.work); - int i; int err; if (oxfw->registered) @@ -218,6 +195,8 @@ static void do_registration(struct work_struct *work) &oxfw->card); if (err < 0) return; + oxfw->card->private_free = oxfw_card_free; + oxfw->card->private_data = oxfw; err = name_card(oxfw); if (err < 0) @@ -258,28 +237,11 @@ static void do_registration(struct work_struct *work) if (err < 0) goto error; - /* - * After registered, oxfw instance can be released corresponding to - * releasing the sound card instance. - */ - oxfw->card->private_free = oxfw_card_free; - oxfw->card->private_data = oxfw; oxfw->registered = true; return; error: - snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->rx_stream); - if (oxfw->has_output) - snd_oxfw_stream_destroy_simplex(oxfw, &oxfw->tx_stream); - for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; ++i) { - kfree(oxfw->tx_stream_formats[i]); - oxfw->tx_stream_formats[i] = NULL; - kfree(oxfw->rx_stream_formats[i]); - oxfw->rx_stream_formats[i] = NULL; - } snd_card_free(oxfw->card); - kfree(oxfw->spec); - oxfw->spec = NULL; dev_info(&oxfw->unit->device, "Sound card registration failed: %d\n", err); } @@ -293,14 +255,13 @@ static int oxfw_probe(struct fw_unit *unit, return -ENODEV; /* Allocate this independent of sound card instance. */ - oxfw = kzalloc(sizeof(struct snd_oxfw), GFP_KERNEL); - if (oxfw == NULL) + oxfw = devm_kzalloc(&unit->device, sizeof(struct snd_oxfw), GFP_KERNEL); + if (!oxfw) return -ENOMEM; - - oxfw->entry = entry; oxfw->unit = fw_unit_get(unit); dev_set_drvdata(&unit->device, oxfw); + oxfw->entry = entry; mutex_init(&oxfw->mutex); spin_lock_init(&oxfw->lock); init_waitqueue_head(&oxfw->hwdep_wait); @@ -347,12 +308,12 @@ static void oxfw_remove(struct fw_unit *unit) cancel_delayed_work_sync(&oxfw->dwork); if (oxfw->registered) { - /* No need to wait for releasing card object in this context. */ - snd_card_free_when_closed(oxfw->card); - } else { - /* Don't forget this case. */ - oxfw_free(oxfw); + // Block till all of ALSA character devices are released. + snd_card_free(oxfw->card); } + + mutex_destroy(&oxfw->mutex); + fw_unit_put(oxfw->unit); } static const struct compat_info griffin_firewave = { diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c @@ -85,20 +85,12 @@ static int identify_model(struct snd_tscm *tscm) return 0; } -static void tscm_free(struct snd_tscm *tscm) +static void tscm_card_free(struct snd_card *card) { + struct snd_tscm *tscm = card->private_data; + snd_tscm_transaction_unregister(tscm); snd_tscm_stream_destroy_duplex(tscm); - - fw_unit_put(tscm->unit); - - mutex_destroy(&tscm->mutex); - kfree(tscm); -} - -static void tscm_card_free(struct snd_card *card) -{ - tscm_free(card->private_data); } static void do_registration(struct work_struct *work) @@ -110,6 +102,8 @@ static void do_registration(struct work_struct *work) &tscm->card); if (err < 0) return; + tscm->card->private_free = tscm_card_free; + tscm->card->private_data = tscm; err = identify_model(tscm); if (err < 0) @@ -141,18 +135,10 @@ static void do_registration(struct work_struct *work) if (err < 0) goto error; - /* - * After registered, tscm instance can be released corresponding to - * releasing the sound card instance. - */ - tscm->card->private_free = tscm_card_free; - tscm->card->private_data = tscm; tscm->registered = true; return; error: - snd_tscm_transaction_unregister(tscm); - snd_tscm_stream_destroy_duplex(tscm); snd_card_free(tscm->card); dev_info(&tscm->unit->device, "Sound card registration failed: %d\n", err); @@ -164,11 +150,9 @@ static int snd_tscm_probe(struct fw_unit *unit, struct snd_tscm *tscm; /* Allocate this independent of sound card instance. */ - tscm = kzalloc(sizeof(struct snd_tscm), GFP_KERNEL); - if (tscm == NULL) + tscm = devm_kzalloc(&unit->device, sizeof(struct snd_tscm), GFP_KERNEL); + if (!tscm) return -ENOMEM; - - /* initialize myself */ tscm->unit = fw_unit_get(unit); dev_set_drvdata(&unit->device, tscm); @@ -216,12 +200,12 @@ static void snd_tscm_remove(struct fw_unit *unit) cancel_delayed_work_sync(&tscm->dwork); if (tscm->registered) { - /* No need to wait for releasing card object in this context. */ - snd_card_free_when_closed(tscm->card); - } else { - /* Don't forget this case. */ - tscm_free(tscm); + // Block till all of ALSA character devices are released. + snd_card_free(tscm->card); } + + mutex_destroy(&tscm->mutex); + fw_unit_put(tscm->unit); } static const struct ieee1394_device_id snd_tscm_id_table[] = { diff --git a/sound/hda/ext/hdac_ext_controller.c b/sound/hda/ext/hdac_ext_controller.c @@ -48,9 +48,11 @@ void snd_hdac_ext_bus_ppcap_enable(struct hdac_bus *bus, bool enable) } if (enable) - snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_GPROCEN); + snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, + AZX_PPCTL_GPROCEN, AZX_PPCTL_GPROCEN); else - snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_GPROCEN, 0); + snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, + AZX_PPCTL_GPROCEN, 0); } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_enable); @@ -68,9 +70,11 @@ void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_bus *bus, bool enable) } if (enable) - snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, 0, AZX_PPCTL_PIE); + snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, + AZX_PPCTL_PIE, AZX_PPCTL_PIE); else - snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, AZX_PPCTL_PIE, 0); + snd_hdac_updatel(bus->ppcap, AZX_REG_PP_PPCTL, + AZX_PPCTL_PIE, 0); } EXPORT_SYMBOL_GPL(snd_hdac_ext_bus_ppcap_int_enable); @@ -194,7 +198,8 @@ static int check_hdac_link_power_active(struct hdac_ext_link *link, bool enable) */ int snd_hdac_ext_bus_link_power_up(struct hdac_ext_link *link) { - snd_hdac_updatel(link->ml_addr, AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA); + snd_hdac_updatel(link->ml_addr, AZX_REG_ML_LCTL, + AZX_MLCTL_SPA, AZX_MLCTL_SPA); return check_hdac_link_power_active(link, true); } @@ -222,8 +227,8 @@ int snd_hdac_ext_bus_link_power_up_all(struct hdac_bus *bus) int ret; list_for_each_entry(hlink, &bus->hlink_list, list) { - snd_hdac_updatel(hlink->ml_addr, - AZX_REG_ML_LCTL, 0, AZX_MLCTL_SPA); + snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, + AZX_MLCTL_SPA, AZX_MLCTL_SPA); ret = check_hdac_link_power_active(hlink, true); if (ret < 0) return ret; @@ -243,7 +248,8 @@ int snd_hdac_ext_bus_link_power_down_all(struct hdac_bus *bus) int ret; list_for_each_entry(hlink, &bus->hlink_list, list) { - snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, AZX_MLCTL_SPA, 0); + snd_hdac_updatel(hlink->ml_addr, AZX_REG_ML_LCTL, + AZX_MLCTL_SPA, 0); ret = check_hdac_link_power_active(hlink, false); if (ret < 0) return ret; diff --git a/sound/i2c/cs8427.c b/sound/i2c/cs8427.c @@ -118,7 +118,7 @@ static int snd_cs8427_send_corudata(struct snd_i2c_device *device, struct cs8427 *chip = device->private_data; char *hw_data = udata ? chip->playback.hw_udata : chip->playback.hw_status; - char data[32]; + unsigned char data[32]; int err, idx; if (!memcmp(hw_data, ndata, count)) diff --git a/sound/isa/opti9xx/opti92x-ad1848.c b/sound/isa/opti9xx/opti92x-ad1848.c @@ -389,7 +389,8 @@ static int snd_opti9xx_configure(struct snd_opti9xx *chip, case OPTi9XX_HW_82C931: /* disable 3D sound (set GPIO1 as output, low) */ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(20), 0x04, 0x0c); - case OPTi9XX_HW_82C933: /* FALL THROUGH */ + /* fall through */ + case OPTi9XX_HW_82C933: /* * The BTC 1817DW has QS1000 wavetable which is connected * to the serial digital input of the OPTI931. @@ -400,7 +401,8 @@ static int snd_opti9xx_configure(struct snd_opti9xx *chip, * or digital input signal. */ snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(26), 0x01, 0x01); - case OPTi9XX_HW_82C930: /* FALL THROUGH */ + /* fall through */ + case OPTi9XX_HW_82C930: snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(6), 0x02, 0x03); snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(3), 0x00, 0xff); snd_opti9xx_write_mask(chip, OPTi9XX_MC_REG(4), 0x10 | diff --git a/sound/isa/sb/sb8_main.c b/sound/isa/sb/sb8_main.c @@ -130,13 +130,13 @@ static int snd_sb8_playback_prepare(struct snd_pcm_substream *substream) chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; break; } - /* fallthru */ + /* fall through */ case SB_HW_201: if (rate > 23000) { chip->playback_format = SB_DSP_HI_OUTPUT_AUTO; break; } - /* fallthru */ + /* fall through */ case SB_HW_20: chip->playback_format = SB_DSP_LO_OUTPUT_AUTO; break; @@ -287,7 +287,7 @@ static int snd_sb8_capture_prepare(struct snd_pcm_substream *substream) chip->capture_format = SB_DSP_HI_INPUT_AUTO; break; } - /* fallthru */ + /* fall through */ case SB_HW_20: chip->capture_format = SB_DSP_LO_INPUT_AUTO; break; @@ -387,7 +387,7 @@ irqreturn_t snd_sb8dsp_interrupt(struct snd_sb *chip) case SB_MODE_PLAYBACK_16: /* ok.. playback is active */ if (chip->hardware != SB_HW_JAZZ16) break; - /* fallthru */ + /* fall through */ case SB_MODE_PLAYBACK_8: substream = chip->playback_substream; if (chip->playback_format == SB_DSP_OUTPUT) @@ -397,7 +397,7 @@ irqreturn_t snd_sb8dsp_interrupt(struct snd_sb *chip) case SB_MODE_CAPTURE_16: if (chip->hardware != SB_HW_JAZZ16) break; - /* fallthru */ + /* fall through */ case SB_MODE_CAPTURE_8: substream = chip->capture_substream; if (chip->capture_format == SB_DSP_INPUT) diff --git a/sound/mips/hal2.c b/sound/mips/hal2.c @@ -500,7 +500,8 @@ static const struct snd_pcm_hardware hal2_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | - SNDRV_PCM_INFO_BLOCK_TRANSFER), + SNDRV_PCM_INFO_BLOCK_TRANSFER | + SNDRV_PCM_INFO_SYNC_APPLPTR), .formats = SNDRV_PCM_FMTBIT_S16_BE, .rates = SNDRV_PCM_RATE_8000_48000, .rate_min = 8000, @@ -563,6 +564,8 @@ static int hal2_playback_prepare(struct snd_pcm_substream *substream) dac->sample_rate = hal2_compute_rate(dac, runtime->rate); memset(&dac->pcm_indirect, 0, sizeof(dac->pcm_indirect)); dac->pcm_indirect.hw_buffer_size = H2_BUF_SIZE; + dac->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2; + dac->pcm_indirect.hw_io = dac->buffer_dma; dac->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); dac->substream = substream; hal2_setup_dac(hal2); @@ -575,9 +578,6 @@ static int hal2_playback_trigger(struct snd_pcm_substream *substream, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_START: - hal2->dac.pcm_indirect.hw_io = hal2->dac.buffer_dma; - hal2->dac.pcm_indirect.hw_data = 0; - substream->ops->ack(substream); hal2_start_dac(hal2); break; case SNDRV_PCM_TRIGGER_STOP: @@ -615,7 +615,6 @@ static int hal2_playback_ack(struct snd_pcm_substream *substream) struct snd_hal2 *hal2 = snd_pcm_substream_chip(substream); struct hal2_codec *dac = &hal2->dac; - dac->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2; return snd_pcm_indirect_playback_transfer(substream, &dac->pcm_indirect, hal2_playback_transfer); @@ -655,6 +654,7 @@ static int hal2_capture_prepare(struct snd_pcm_substream *substream) memset(&adc->pcm_indirect, 0, sizeof(adc->pcm_indirect)); adc->pcm_indirect.hw_buffer_size = H2_BUF_SIZE; adc->pcm_indirect.hw_queue_size = H2_BUF_SIZE / 2; + adc->pcm_indirect.hw_io = adc->buffer_dma; adc->pcm_indirect.sw_buffer_size = snd_pcm_lib_buffer_bytes(substream); adc->substream = substream; hal2_setup_adc(hal2); @@ -667,9 +667,6 @@ static int hal2_capture_trigger(struct snd_pcm_substream *substream, int cmd) switch (cmd) { case SNDRV_PCM_TRIGGER_START: - hal2->adc.pcm_indirect.hw_io = hal2->adc.buffer_dma; - hal2->adc.pcm_indirect.hw_data = 0; - printk(KERN_DEBUG "buffer_dma %x\n", hal2->adc.buffer_dma); hal2_start_adc(hal2); break; case SNDRV_PCM_TRIGGER_STOP: diff --git a/sound/pci/asihpi/hpios.c b/sound/pci/asihpi/hpios.c @@ -49,7 +49,7 @@ u16 hpios_locked_mem_alloc(struct consistent_dma_area *p_mem_area, u32 size, /*?? any benefit in using managed dmam_alloc_coherent? */ p_mem_area->vaddr = dma_alloc_coherent(&pdev->dev, size, &p_mem_area->dma_handle, - GFP_DMA32 | GFP_KERNEL); + GFP_KERNEL); if (p_mem_area->vaddr) { HPI_DEBUG_LOG(DEBUG, "allocated %d bytes, dma 0x%x vma %p\n", diff --git a/sound/pci/atiixp.c b/sound/pci/atiixp.c @@ -903,15 +903,15 @@ static int snd_atiixp_playback_prepare(struct snd_pcm_substream *substream) case 8: data |= ATI_REG_OUT_DMA_SLOT_BIT(10) | ATI_REG_OUT_DMA_SLOT_BIT(11); - /* fallthru */ + /* fall through */ case 6: data |= ATI_REG_OUT_DMA_SLOT_BIT(7) | ATI_REG_OUT_DMA_SLOT_BIT(8); - /* fallthru */ + /* fall through */ case 4: data |= ATI_REG_OUT_DMA_SLOT_BIT(6) | ATI_REG_OUT_DMA_SLOT_BIT(9); - /* fallthru */ + /* fall through */ default: data |= ATI_REG_OUT_DMA_SLOT_BIT(3) | ATI_REG_OUT_DMA_SLOT_BIT(4); diff --git a/sound/pci/au88x0/au88x0_core.c b/sound/pci/au88x0/au88x0_core.c @@ -1115,6 +1115,7 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma, hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0xc, snd_pcm_sgbuf_get_addr(dma->substream, psize * 3)); + /* fall through */ /* 3 pages */ case 3: dma->cfg0 |= 0x12000000; @@ -1122,12 +1123,14 @@ vortex_adbdma_setbuffers(vortex_t * vortex, int adbdma, hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x8, snd_pcm_sgbuf_get_addr(dma->substream, psize * 2)); + /* fall through */ /* 2 pages */ case 2: dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize - 1); hwwrite(vortex->mmio, VORTEX_ADBDMA_BUFBASE + (adbdma << 4) + 0x4, snd_pcm_sgbuf_get_addr(dma->substream, psize)); + /* fall through */ /* 1 page */ case 1: dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize - 1) << 0xc); @@ -1390,17 +1393,20 @@ vortex_wtdma_setbuffers(vortex_t * vortex, int wtdma, dma->cfg1 |= 0x88000000 | 0x44000000 | 0x30000000 | (psize-1); hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0xc, snd_pcm_sgbuf_get_addr(dma->substream, psize * 3)); + /* fall through */ /* 3 pages */ case 3: dma->cfg0 |= 0x12000000; dma->cfg1 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc); hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x8, snd_pcm_sgbuf_get_addr(dma->substream, psize * 2)); + /* fall through */ /* 2 pages */ case 2: dma->cfg0 |= 0x88000000 | 0x44000000 | 0x10000000 | (psize-1); hwwrite(vortex->mmio, VORTEX_WTDMA_BUFBASE + (wtdma << 4) + 0x4, snd_pcm_sgbuf_get_addr(dma->substream, psize)); + /* fall through */ /* 1 page */ case 1: dma->cfg0 |= 0x80000000 | 0x40000000 | ((psize-1) << 0xc); diff --git a/sound/pci/cs46xx/cs46xx_lib.c b/sound/pci/cs46xx/cs46xx_lib.c @@ -1443,7 +1443,8 @@ static const struct snd_pcm_hardware snd_cs46xx_playback = .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER /*|*/ - /*SNDRV_PCM_INFO_RESUME*/), + /*SNDRV_PCM_INFO_RESUME*/ | + SNDRV_PCM_INFO_SYNC_APPLPTR), .formats = (SNDRV_PCM_FMTBIT_S8 | SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S16_BE | SNDRV_PCM_FMTBIT_U16_LE | SNDRV_PCM_FMTBIT_U16_BE), @@ -1465,7 +1466,8 @@ static const struct snd_pcm_hardware snd_cs46xx_capture = .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_BLOCK_TRANSFER /*|*/ - /*SNDRV_PCM_INFO_RESUME*/), + /*SNDRV_PCM_INFO_RESUME*/ | + SNDRV_PCM_INFO_SYNC_APPLPTR), .formats = SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_CONTINUOUS | SNDRV_PCM_RATE_8000_48000, .rate_min = 5500, diff --git a/sound/pci/emu10k1/emupcm.c b/sound/pci/emu10k1/emupcm.c @@ -1753,7 +1753,8 @@ static const struct snd_pcm_hardware snd_emu10k1_fx8010_playback = { .info = (SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_RESUME | - /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE), + /* SNDRV_PCM_INFO_MMAP_VALID | */ SNDRV_PCM_INFO_PAUSE | + SNDRV_PCM_INFO_SYNC_APPLPTR), .formats = SNDRV_PCM_FMTBIT_U8 | SNDRV_PCM_FMTBIT_S16_LE, .rates = SNDRV_PCM_RATE_48000, .rate_min = 48000, diff --git a/sound/pci/hda/hda_auto_parser.c b/sound/pci/hda/hda_auto_parser.c @@ -13,7 +13,7 @@ #include <linux/export.h> #include <linux/sort.h> #include <sound/core.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" diff --git a/sound/pci/hda/hda_beep.h b/sound/pci/hda/hda_beep.h @@ -9,7 +9,7 @@ #ifndef __SOUND_HDA_BEEP_H #define __SOUND_HDA_BEEP_H -#include "hda_codec.h" +#include <sound/hda_codec.h> #define HDA_BEEP_MODE_OFF 0 #define HDA_BEEP_MODE_ON 1 diff --git a/sound/pci/hda/hda_bind.c b/sound/pci/hda/hda_bind.c @@ -11,7 +11,7 @@ #include <linux/pm.h> #include <linux/pm_runtime.h> #include <sound/core.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" /* @@ -81,6 +81,12 @@ static int hda_codec_driver_probe(struct device *dev) hda_codec_patch_t patch; int err; + if (codec->bus->core.ext_ops) { + if (WARN_ON(!codec->bus->core.ext_ops->hdev_attach)) + return -EINVAL; + return codec->bus->core.ext_ops->hdev_attach(&codec->core); + } + if (WARN_ON(!codec->preset)) return -EINVAL; @@ -134,6 +140,12 @@ static int hda_codec_driver_remove(struct device *dev) { struct hda_codec *codec = dev_to_hda_codec(dev); + if (codec->bus->core.ext_ops) { + if (WARN_ON(!codec->bus->core.ext_ops->hdev_detach)) + return -EINVAL; + return codec->bus->core.ext_ops->hdev_detach(&codec->core); + } + if (codec->patch_ops.free) codec->patch_ops.free(codec); snd_hda_codec_cleanup_for_unbind(codec); diff --git a/sound/pci/hda/hda_codec.c b/sound/pci/hda/hda_codec.c @@ -27,7 +27,7 @@ #include <linux/pm.h> #include <linux/pm_runtime.h> #include <sound/core.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include <sound/asoundef.h> #include <sound/tlv.h> #include <sound/initval.h> diff --git a/sound/pci/hda/hda_controller.c b/sound/pci/hda/hda_controller.c @@ -130,8 +130,9 @@ static int azx_pcm_hw_params(struct snd_pcm_substream *substream, azx_dev->core.bufsize = 0; azx_dev->core.period_bytes = 0; azx_dev->core.format_val = 0; - ret = chip->ops->substream_alloc_pages(chip, substream, - params_buffer_bytes(hw_params)); + ret = snd_pcm_lib_malloc_pages(substream, + params_buffer_bytes(hw_params)); + unlock: dsp_unlock(azx_dev); return ret; @@ -141,7 +142,6 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream) { struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx_dev *azx_dev = get_azx_dev(substream); - struct azx *chip = apcm->chip; struct hda_pcm_stream *hinfo = to_hda_pcm_stream(substream); int err; @@ -152,7 +152,7 @@ static int azx_pcm_hw_free(struct snd_pcm_substream *substream) snd_hda_codec_cleanup(apcm->codec, hinfo, substream); - err = chip->ops->substream_free_pages(chip, substream); + err = snd_pcm_lib_free_pages(substream); azx_stream(azx_dev)->prepared = 0; dsp_unlock(azx_dev); return err; @@ -732,6 +732,7 @@ int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec, int pcm_dev = cpcm->device; unsigned int size; int s, err; + int type = SNDRV_DMA_TYPE_DEV_SG; list_for_each_entry(apcm, &chip->pcm_list, list) { if (apcm->pcm->device == pcm_dev) { @@ -770,7 +771,9 @@ int snd_hda_attach_pcm_stream(struct hda_bus *_bus, struct hda_codec *codec, size = CONFIG_SND_HDA_PREALLOC_SIZE * 1024; if (size > MAX_PREALLOC_SIZE) size = MAX_PREALLOC_SIZE; - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV_SG, + if (chip->uc_buffer) + type = SNDRV_DMA_TYPE_DEV_UC_SG; + snd_pcm_lib_preallocate_pages_for_all(pcm, type, chip->card->dev, size, MAX_PREALLOC_SIZE); return 0; @@ -1220,27 +1223,6 @@ void snd_hda_bus_reset(struct hda_bus *bus) bus->in_reset = 0; } -static int get_jackpoll_interval(struct azx *chip) -{ - int i; - unsigned int j; - - if (!chip->jackpoll_ms) - return 0; - - i = chip->jackpoll_ms[chip->dev_index]; - if (i == 0) - return 0; - if (i < 50 || i > 60000) - j = 0; - else - j = msecs_to_jiffies(i); - if (j == 0) - dev_warn(chip->card->dev, - "jackpoll_ms value out of range: %d\n", i); - return j; -} - /* HD-audio bus initialization */ int azx_bus_init(struct azx *chip, const char *model, const struct hdac_io_ops *io_ops) @@ -1323,7 +1305,7 @@ int azx_probe_codecs(struct azx *chip, unsigned int max_slots) err = snd_hda_codec_new(&chip->bus, chip->card, c, &codec); if (err < 0) continue; - codec->jackpoll_interval = get_jackpoll_interval(chip); + codec->jackpoll_interval = chip->jackpoll_interval; codec->beep_mode = chip->beep_mode; codecs++; } diff --git a/sound/pci/hda/hda_controller.h b/sound/pci/hda/hda_controller.h @@ -20,7 +20,7 @@ #include <sound/core.h> #include <sound/pcm.h> #include <sound/initval.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include <sound/hda_register.h> #define AZX_MAX_CODECS HDA_MAX_CODECS @@ -76,7 +76,6 @@ struct azx_dev { * when link position is not greater than FIFO size */ unsigned int insufficient:1; - unsigned int wc_marked:1; }; #define azx_stream(dev) (&(dev)->core) @@ -88,11 +87,6 @@ struct azx; struct hda_controller_ops { /* Disable msi if supported, PCI only */ int (*disable_msi_reset_irq)(struct azx *); - int (*substream_alloc_pages)(struct azx *chip, - struct snd_pcm_substream *substream, - size_t size); - int (*substream_free_pages)(struct azx *chip, - struct snd_pcm_substream *substream); void (*pcm_mmap_prepare)(struct snd_pcm_substream *substream, struct vm_area_struct *area); /* Check if current position is acceptable */ @@ -127,7 +121,7 @@ struct azx { int capture_streams; int capture_index_offset; int num_streams; - const int *jackpoll_ms; /* per-card jack poll interval */ + int jackpoll_interval; /* jack poll interval in jiffies */ /* Register interaction. */ const struct hda_controller_ops *ops; @@ -160,6 +154,7 @@ struct azx { unsigned int msi:1; unsigned int probing:1; /* codec probing phase */ unsigned int snoop:1; + unsigned int uc_buffer:1; /* non-cached pages for stream buffers */ unsigned int align_buffer_size:1; unsigned int region_requested:1; unsigned int disabled:1; /* disabled by vga_switcheroo */ @@ -175,11 +170,10 @@ struct azx { #define azx_bus(chip) (&(chip)->bus.core) #define bus_to_azx(_bus) container_of(_bus, struct azx, bus.core) -#ifdef CONFIG_X86 -#define azx_snoop(chip) ((chip)->snoop) -#else -#define azx_snoop(chip) true -#endif +static inline bool azx_snoop(struct azx *chip) +{ + return !IS_ENABLED(CONFIG_X86) || chip->snoop; +} /* * macros for easy use diff --git a/sound/pci/hda/hda_eld.c b/sound/pci/hda/hda_eld.c @@ -27,7 +27,7 @@ #include <sound/core.h> #include <asm/unaligned.h> #include <sound/hda_chmap.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" enum eld_versions { diff --git a/sound/pci/hda/hda_generic.c b/sound/pci/hda/hda_generic.c @@ -32,7 +32,7 @@ #include <sound/core.h> #include <sound/jack.h> #include <sound/tlv.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" diff --git a/sound/pci/hda/hda_hwdep.c b/sound/pci/hda/hda_hwdep.c @@ -23,7 +23,7 @@ #include <linux/compat.h> #include <linux/nospec.h> #include <sound/core.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include <sound/hda_hwdep.h> #include <sound/minors.h> diff --git a/sound/pci/hda/hda_intel.c b/sound/pci/hda/hda_intel.c @@ -63,7 +63,7 @@ #include <linux/vgaarb.h> #include <linux/vga_switcheroo.h> #include <linux/firmware.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_controller.h" #include "hda_intel.h" @@ -399,61 +399,6 @@ static char *driver_short_names[] = { [AZX_DRIVER_GENERIC] = "HD-Audio Generic", }; -#ifdef CONFIG_X86 -static void __mark_pages_wc(struct azx *chip, struct snd_dma_buffer *dmab, bool on) -{ - int pages; - - if (azx_snoop(chip)) - return; - if (!dmab || !dmab->area || !dmab->bytes) - return; - -#ifdef CONFIG_SND_DMA_SGBUF - if (dmab->dev.type == SNDRV_DMA_TYPE_DEV_SG) { - struct snd_sg_buf *sgbuf = dmab->private_data; - if (chip->driver_type == AZX_DRIVER_CMEDIA) - return; /* deal with only CORB/RIRB buffers */ - if (on) - set_pages_array_wc(sgbuf->page_table, sgbuf->pages); - else - set_pages_array_wb(sgbuf->page_table, sgbuf->pages); - return; - } -#endif - - pages = (dmab->bytes + PAGE_SIZE - 1) >> PAGE_SHIFT; - if (on) - set_memory_wc((unsigned long)dmab->area, pages); - else - set_memory_wb((unsigned long)dmab->area, pages); -} - -static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf, - bool on) -{ - __mark_pages_wc(chip, buf, on); -} -static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev, - struct snd_pcm_substream *substream, bool on) -{ - if (azx_dev->wc_marked != on) { - __mark_pages_wc(chip, snd_pcm_get_dma_buf(substream), on); - azx_dev->wc_marked = on; - } -} -#else -/* NOP for other archs */ -static inline void mark_pages_wc(struct azx *chip, struct snd_dma_buffer *buf, - bool on) -{ -} -static inline void mark_runtime_wc(struct azx *chip, struct azx_dev *azx_dev, - struct snd_pcm_substream *substream, bool on) -{ -} -#endif - static int azx_acquire_irq(struct azx *chip, int do_disconnect); static void set_default_power_save(struct azx *chip); @@ -1678,6 +1623,7 @@ static void azx_check_snoop_available(struct azx *chip) dev_info(chip->card->dev, "Force to %s mode by module option\n", snoop ? "snoop" : "non-snoop"); chip->snoop = snoop; + chip->uc_buffer = !snoop; return; } @@ -1698,8 +1644,12 @@ static void azx_check_snoop_available(struct azx *chip) snoop = false; chip->snoop = snoop; - if (!snoop) + if (!snoop) { dev_info(chip->card->dev, "Force to non-snoop mode\n"); + /* C-Media requires non-cached pages only for CORB/RIRB */ + if (chip->driver_type != AZX_DRIVER_CMEDIA) + chip->uc_buffer = true; + } } static void azx_probe_work(struct work_struct *work) @@ -1767,7 +1717,8 @@ static int azx_create(struct snd_card *card, struct pci_dev *pci, chip->driver_type = driver_caps & 0xff; check_msi(chip); chip->dev_index = dev; - chip->jackpoll_ms = jackpoll_ms; + if (jackpoll_ms[dev] >= 50 && jackpoll_ms[dev] <= 60000) + chip->jackpoll_interval = msecs_to_jiffies(jackpoll_ms[dev]); INIT_LIST_HEAD(&chip->pcm_list); INIT_WORK(&hda->irq_pending_work, azx_irq_pending_work); INIT_LIST_HEAD(&hda->list); @@ -2090,55 +2041,24 @@ static int dma_alloc_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) { struct azx *chip = bus_to_azx(bus); - int err; - err = snd_dma_alloc_pages(type, - bus->dev, - size, buf); - if (err < 0) - return err; - mark_pages_wc(chip, buf, true); - return 0; + if (!azx_snoop(chip) && type == SNDRV_DMA_TYPE_DEV) + type = SNDRV_DMA_TYPE_DEV_UC; + return snd_dma_alloc_pages(type, bus->dev, size, buf); } static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) { - struct azx *chip = bus_to_azx(bus); - - mark_pages_wc(chip, buf, false); snd_dma_free_pages(buf); } -static int substream_alloc_pages(struct azx *chip, - struct snd_pcm_substream *substream, - size_t size) -{ - struct azx_dev *azx_dev = get_azx_dev(substream); - int ret; - - mark_runtime_wc(chip, azx_dev, substream, false); - ret = snd_pcm_lib_malloc_pages(substream, size); - if (ret < 0) - return ret; - mark_runtime_wc(chip, azx_dev, substream, true); - return 0; -} - -static int substream_free_pages(struct azx *chip, - struct snd_pcm_substream *substream) -{ - struct azx_dev *azx_dev = get_azx_dev(substream); - mark_runtime_wc(chip, azx_dev, substream, false); - return snd_pcm_lib_free_pages(substream); -} - static void pcm_mmap_prepare(struct snd_pcm_substream *substream, struct vm_area_struct *area) { #ifdef CONFIG_X86 struct azx_pcm *apcm = snd_pcm_substream_chip(substream); struct azx *chip = apcm->chip; - if (!azx_snoop(chip) && chip->driver_type != AZX_DRIVER_CMEDIA) + if (chip->uc_buffer) area->vm_page_prot = pgprot_writecombine(area->vm_page_prot); #endif } @@ -2156,8 +2076,6 @@ static const struct hdac_io_ops pci_hda_io_ops = { static const struct hda_controller_ops pci_hda_ops = { .disable_msi_reset_irq = disable_msi_reset_irq, - .substream_alloc_pages = substream_alloc_pages, - .substream_free_pages = substream_free_pages, .pcm_mmap_prepare = pcm_mmap_prepare, .position_check = azx_position_check, .link_power = azx_intel_link_power, @@ -2257,8 +2175,12 @@ static struct snd_pci_quirk power_save_blacklist[] = { /* https://bugzilla.redhat.com/show_bug.cgi?id=1581607 */ SND_PCI_QUIRK(0x1558, 0x3501, "Clevo W35xSS_370SS", 0), /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ + SND_PCI_QUIRK(0x1028, 0x0497, "Dell Precision T3600", 0), + /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ /* Note the P55A-UD3 and Z87-D3HP share the subsys id for the HDA dev */ SND_PCI_QUIRK(0x1458, 0xa002, "Gigabyte P55A-UD3 / Z87-D3HP", 0), + /* https://bugzilla.redhat.com/show_bug.cgi?id=1525104 */ + SND_PCI_QUIRK(0x8086, 0x2040, "Intel DZ77BH-55K", 0), /* https://bugzilla.kernel.org/show_bug.cgi?id=199607 */ SND_PCI_QUIRK(0x8086, 0x2057, "Intel NUC5i7RYB", 0), /* https://bugzilla.redhat.com/show_bug.cgi?id=1520902 */ diff --git a/sound/pci/hda/hda_jack.c b/sound/pci/hda/hda_jack.c @@ -15,7 +15,7 @@ #include <sound/core.h> #include <sound/control.h> #include <sound/jack.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" diff --git a/sound/pci/hda/hda_proc.c b/sound/pci/hda/hda_proc.c @@ -25,7 +25,7 @@ #include <linux/slab.h> #include <sound/core.h> #include <linux/module.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" static int dump_coef = -1; diff --git a/sound/pci/hda/hda_sysfs.c b/sound/pci/hda/hda_sysfs.c @@ -14,7 +14,7 @@ #include <linux/string.h> #include <linux/export.h> #include <sound/core.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include <sound/hda_hwdep.h> #include <sound/minors.h> diff --git a/sound/pci/hda/hda_tegra.c b/sound/pci/hda/hda_tegra.c @@ -35,7 +35,7 @@ #include <sound/core.h> #include <sound/initval.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_controller.h" /* Defines for Nvidia Tegra HDA support */ @@ -99,19 +99,6 @@ static void dma_free_pages(struct hdac_bus *bus, struct snd_dma_buffer *buf) snd_dma_free_pages(buf); } -static int substream_alloc_pages(struct azx *chip, - struct snd_pcm_substream *substream, - size_t size) -{ - return snd_pcm_lib_malloc_pages(substream, size); -} - -static int substream_free_pages(struct azx *chip, - struct snd_pcm_substream *substream) -{ - return snd_pcm_lib_free_pages(substream); -} - /* * Register access ops. Tegra HDA register access is DWORD only. */ @@ -180,10 +167,7 @@ static const struct hdac_io_ops hda_tegra_io_ops = { .dma_free_pages = dma_free_pages, }; -static const struct hda_controller_ops hda_tegra_ops = { - .substream_alloc_pages = substream_alloc_pages, - .substream_free_pages = substream_free_pages, -}; +static const struct hda_controller_ops hda_tegra_ops; /* nothing special */ static void hda_tegra_init(struct hda_tegra *hda) { diff --git a/sound/pci/hda/patch_analog.c b/sound/pci/hda/patch_analog.c @@ -24,7 +24,7 @@ #include <linux/module.h> #include <sound/core.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_beep.h" diff --git a/sound/pci/hda/patch_ca0110.c b/sound/pci/hda/patch_ca0110.c @@ -22,7 +22,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <sound/core.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" diff --git a/sound/pci/hda/patch_ca0132.c b/sound/pci/hda/patch_ca0132.c @@ -31,8 +31,9 @@ #include <linux/types.h> #include <linux/io.h> #include <linux/pci.h> +#include <asm/io.h> #include <sound/core.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" @@ -81,12 +82,12 @@ #define SCP_GET 1 #define EFX_FILE "ctefx.bin" -#define SBZ_EFX_FILE "ctefx-sbz.bin" +#define DESKTOP_EFX_FILE "ctefx-desktop.bin" #define R3DI_EFX_FILE "ctefx-r3di.bin" #ifdef CONFIG_SND_HDA_CODEC_CA0132_DSP MODULE_FIRMWARE(EFX_FILE); -MODULE_FIRMWARE(SBZ_EFX_FILE); +MODULE_FIRMWARE(DESKTOP_EFX_FILE); MODULE_FIRMWARE(R3DI_EFX_FILE); #endif @@ -152,7 +153,10 @@ enum { XBASS_XOVER, EQ_PRESET_ENUM, SMART_VOLUME_ENUM, - MIC_BOOST_ENUM + MIC_BOOST_ENUM, + AE5_HEADPHONE_GAIN_ENUM, + AE5_SOUND_FILTER_ENUM, + ZXR_HEADPHONE_GAIN #define EFFECTS_COUNT (EFFECT_END_NID - EFFECT_START_NID) }; @@ -666,6 +670,65 @@ static const struct ct_dsp_volume_ctl ca0132_alt_vol_ctls[] = { } }; +/* Values for ca0113_mmio_command_set for selecting output. */ +#define AE5_CA0113_OUT_SET_COMMANDS 6 +struct ae5_ca0113_output_set { + unsigned int group[AE5_CA0113_OUT_SET_COMMANDS]; + unsigned int target[AE5_CA0113_OUT_SET_COMMANDS]; + unsigned int vals[AE5_CA0113_OUT_SET_COMMANDS]; +}; + +static const struct ae5_ca0113_output_set ae5_ca0113_output_presets[] = { + { .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, + .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, + .vals = { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f } + }, + { .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, + .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, + .vals = { 0x3f, 0x3f, 0x00, 0x00, 0x00, 0x00 } + }, + { .group = { 0x30, 0x30, 0x48, 0x48, 0x48, 0x30 }, + .target = { 0x2e, 0x30, 0x0d, 0x17, 0x19, 0x32 }, + .vals = { 0x00, 0x00, 0x40, 0x00, 0x00, 0x3f } + } +}; + +/* ae5 ca0113 command sequences to set headphone gain levels. */ +#define AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS 4 +struct ae5_headphone_gain_set { + char *name; + unsigned int vals[AE5_HEADPHONE_GAIN_PRESET_MAX_COMMANDS]; +}; + +static const struct ae5_headphone_gain_set ae5_headphone_gain_presets[] = { + { .name = "Low (16-31", + .vals = { 0xff, 0x2c, 0xf5, 0x32 } + }, + { .name = "Medium (32-149", + .vals = { 0x38, 0xa8, 0x3e, 0x4c } + }, + { .name = "High (150-600", + .vals = { 0xff, 0xff, 0xff, 0x7f } + } +}; + +struct ae5_filter_set { + char *name; + unsigned int val; +}; + +static const struct ae5_filter_set ae5_filter_presets[] = { + { .name = "Slow Roll Off", + .val = 0xa0 + }, + { .name = "Minimum Phase", + .val = 0xc0 + }, + { .name = "Fast Roll Off", + .val = 0x80 + } +}; + enum hda_cmd_vendor_io { /* for DspIO node */ VENDOR_DSPIO_SCP_WRITE_DATA_LOW = 0x000, @@ -685,6 +748,9 @@ enum hda_cmd_vendor_io { VENDOR_CHIPIO_DATA_LOW = 0x300, VENDOR_CHIPIO_DATA_HIGH = 0x400, + VENDOR_CHIPIO_8051_WRITE_DIRECT = 0x500, + VENDOR_CHIPIO_8051_READ_DIRECT = 0xD00, + VENDOR_CHIPIO_GET_PARAMETER = 0xF00, VENDOR_CHIPIO_STATUS = 0xF01, VENDOR_CHIPIO_HIC_POST_READ = 0x702, @@ -692,6 +758,9 @@ enum hda_cmd_vendor_io { VENDOR_CHIPIO_8051_DATA_WRITE = 0x707, VENDOR_CHIPIO_8051_DATA_READ = 0xF07, + VENDOR_CHIPIO_8051_PMEM_READ = 0xF08, + VENDOR_CHIPIO_8051_IRAM_WRITE = 0x709, + VENDOR_CHIPIO_8051_IRAM_READ = 0xF09, VENDOR_CHIPIO_CT_EXTENSIONS_ENABLE = 0x70A, VENDOR_CHIPIO_CT_EXTENSIONS_GET = 0xF0A, @@ -798,6 +867,12 @@ enum control_param_id { * impedance is selected*/ CONTROL_PARAM_PORTD_160OHM_GAIN = 10, + /* + * This control param name was found in the 8051 memory, and makes + * sense given the fact the AE-5 uses it and has the ASI flag set. + */ + CONTROL_PARAM_ASI = 23, + /* Stream Control */ /* Select stream with the given ID */ @@ -955,7 +1030,11 @@ struct ca0132_spec { long eq_preset_val; unsigned int tlv[4]; struct hda_vmaster_mute_hook vmaster_mute; - + /* AE-5 Control values */ + unsigned char ae5_headphone_gain_val; + unsigned char ae5_filter_val; + /* ZxR Control Values */ + unsigned char zxr_gain_set; struct hda_codec *codec; struct delayed_work unsol_hp_work; @@ -995,8 +1074,11 @@ enum { QUIRK_ALIENWARE, QUIRK_ALIENWARE_M17XR4, QUIRK_SBZ, + QUIRK_ZXR, + QUIRK_ZXR_DBPRO, QUIRK_R3DI, QUIRK_R3D, + QUIRK_AE5, }; static const struct hda_pintbl alienware_pincfgs[] = { @@ -1028,6 +1110,21 @@ static const struct hda_pintbl sbz_pincfgs[] = { {} }; +/* Sound Blaster ZxR pin configs taken from Windows Driver */ +static const struct hda_pintbl zxr_pincfgs[] = { + { 0x0b, 0x01047110 }, /* Port G -- Lineout FRONT L/R */ + { 0x0c, 0x414510f0 }, /* SPDIF Out 1 - Disabled*/ + { 0x0d, 0x014510f0 }, /* Digital Out */ + { 0x0e, 0x41c520f0 }, /* SPDIF In - Disabled*/ + { 0x0f, 0x0122711f }, /* Port A -- BackPanel HP */ + { 0x10, 0x01017111 }, /* Port D -- Center/LFE */ + { 0x11, 0x01017114 }, /* Port B -- LineMicIn2 / Rear L/R */ + { 0x12, 0x01a271f0 }, /* Port C -- LineIn1 */ + { 0x13, 0x908700f0 }, /* What U Hear In*/ + { 0x18, 0x50d000f0 }, /* N/A */ + {} +}; + /* Recon3D pin configs taken from Windows Driver */ static const struct hda_pintbl r3d_pincfgs[] = { { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */ @@ -1043,6 +1140,21 @@ static const struct hda_pintbl r3d_pincfgs[] = { {} }; +/* Sound Blaster AE-5 pin configs taken from Windows Driver */ +static const struct hda_pintbl ae5_pincfgs[] = { + { 0x0b, 0x01017010 }, /* Port G -- Lineout FRONT L/R */ + { 0x0c, 0x014510f0 }, /* SPDIF Out 1 */ + { 0x0d, 0x014510f0 }, /* Digital Out */ + { 0x0e, 0x01c510f0 }, /* SPDIF In */ + { 0x0f, 0x01017114 }, /* Port A -- Rear L/R. */ + { 0x10, 0x01017012 }, /* Port D -- Center/LFE or FP Hp */ + { 0x11, 0x01a170ff }, /* Port B -- LineMicIn2 / Rear Headphone */ + { 0x12, 0x01a170f0 }, /* Port C -- LineIn1 */ + { 0x13, 0x908700f0 }, /* What U Hear In*/ + { 0x18, 0x50d000f0 }, /* N/A */ + {} +}; + /* Recon3D integrated pin configs taken from Windows Driver */ static const struct hda_pintbl r3di_pincfgs[] = { { 0x0b, 0x01014110 }, /* Port G -- Lineout FRONT L/R */ @@ -1069,6 +1181,7 @@ static const struct snd_pci_quirk ca0132_quirks[] = { SND_PCI_QUIRK(0x1458, 0xA026, "Gigabyte G1.Sniper Z97", QUIRK_R3DI), SND_PCI_QUIRK(0x1458, 0xA036, "Gigabyte GA-Z170X-Gaming 7", QUIRK_R3DI), SND_PCI_QUIRK(0x1102, 0x0013, "Recon3D", QUIRK_R3D), + SND_PCI_QUIRK(0x1102, 0x0051, "Sound Blaster AE-5", QUIRK_AE5), {} }; @@ -1454,6 +1567,20 @@ static void chipio_set_conn_rate(struct hda_codec *codec, } /* + * Writes to the 8051's internal address space directly instead of indirectly, + * giving access to the special function registers located at addresses + * 0x80-0xFF. + */ +static void chipio_8051_write_direct(struct hda_codec *codec, + unsigned int addr, unsigned int data) +{ + unsigned int verb; + + verb = VENDOR_CHIPIO_8051_WRITE_DIRECT | data; + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, verb, addr); +} + +/* * Enable clocks. */ static void chipio_enable_clocks(struct hda_codec *codec) @@ -3088,7 +3215,9 @@ static bool dspload_wait_loaded(struct hda_codec *codec) } /* - * Setup GPIO for the other variants of Core3D. + * ca0113 related functions. The ca0113 acts as the HDA bus for the pci-e + * based cards, and has a second mmio region, region2, that's used for special + * commands. */ /* @@ -3096,8 +3225,11 @@ static bool dspload_wait_loaded(struct hda_codec *codec) * the mmio address 0x320 is used to set GPIO pins. The format for the data * The first eight bits are just the number of the pin. So far, I've only seen * this number go to 7. + * AE-5 note: The AE-5 seems to use pins 2 and 3 to somehow set the color value + * of the on-card LED. It seems to use pin 2 for data, then toggles 3 to on and + * then off to send that bit. */ -static void ca0132_mmio_gpio_set(struct hda_codec *codec, unsigned int gpio_pin, +static void ca0113_mmio_gpio_set(struct hda_codec *codec, unsigned int gpio_pin, bool enable) { struct ca0132_spec *spec = codec->spec; @@ -3110,6 +3242,89 @@ static void ca0132_mmio_gpio_set(struct hda_codec *codec, unsigned int gpio_pin, } /* + * Special pci region2 commands that are only used by the AE-5. They follow + * a set format, and require reads at certain points to seemingly 'clear' + * the response data. My first tests didn't do these reads, and would cause + * the card to get locked up until the memory was read. These commands + * seem to work with three distinct values that I've taken to calling group, + * target-id, and value. + */ +static void ca0113_mmio_command_set(struct hda_codec *codec, unsigned int group, + unsigned int target, unsigned int value) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int write_val; + + writel(0x0000007e, spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + writel(0x0000005a, spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + + writel(0x00800005, spec->mem_base + 0x20c); + writel(group, spec->mem_base + 0x804); + + writel(0x00800005, spec->mem_base + 0x20c); + write_val = (target & 0xff); + write_val |= (value << 8); + + + writel(write_val, spec->mem_base + 0x204); + /* + * Need delay here or else it goes too fast and works inconsistently. + */ + msleep(20); + + readl(spec->mem_base + 0x860); + readl(spec->mem_base + 0x854); + readl(spec->mem_base + 0x840); + + writel(0x00800004, spec->mem_base + 0x20c); + writel(0x00000000, spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); +} + +/* + * This second type of command is used for setting the sound filter type. + */ +static void ca0113_mmio_command_set_type2(struct hda_codec *codec, + unsigned int group, unsigned int target, unsigned int value) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int write_val; + + writel(0x0000007e, spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + writel(0x0000005a, spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + + writel(0x00800003, spec->mem_base + 0x20c); + writel(group, spec->mem_base + 0x804); + + writel(0x00800005, spec->mem_base + 0x20c); + write_val = (target & 0xff); + write_val |= (value << 8); + + + writel(write_val, spec->mem_base + 0x204); + msleep(20); + readl(spec->mem_base + 0x860); + readl(spec->mem_base + 0x854); + readl(spec->mem_base + 0x840); + + writel(0x00800004, spec->mem_base + 0x20c); + writel(0x00000000, spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); + readl(spec->mem_base + 0x210); +} + +/* + * Setup GPIO for the other variants of Core3D. + */ + +/* * Sets up the GPIO pins so that they are discoverable. If this isn't done, * the card shows as having no GPIO pins. */ @@ -3119,6 +3334,7 @@ static void ca0132_gpio_init(struct hda_codec *codec) switch (spec->quirk) { case QUIRK_SBZ: + case QUIRK_AE5: snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); snd_hda_codec_write(codec, 0x01, 0, 0x790, 0x23); @@ -3928,6 +4144,138 @@ exit: return err < 0 ? err : 0; } +static int ae5_headphone_gain_set(struct hda_codec *codec, long val); +static int zxr_headphone_gain_set(struct hda_codec *codec, long val); +static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val); + +static void ae5_mmio_select_out(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int i; + + for (i = 0; i < AE5_CA0113_OUT_SET_COMMANDS; i++) + ca0113_mmio_command_set(codec, + ae5_ca0113_output_presets[spec->cur_out_type].group[i], + ae5_ca0113_output_presets[spec->cur_out_type].target[i], + ae5_ca0113_output_presets[spec->cur_out_type].vals[i]); +} + +/* + * These are the commands needed to setup output on each of the different card + * types. + */ +static void ca0132_alt_select_out_quirk_handler(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + + switch (spec->cur_out_type) { + case SPEAKER_OUT: + switch (spec->quirk) { + case QUIRK_SBZ: + ca0113_mmio_gpio_set(codec, 7, false); + ca0113_mmio_gpio_set(codec, 4, true); + ca0113_mmio_gpio_set(codec, 1, true); + chipio_set_control_param(codec, 0x0d, 0x18); + break; + case QUIRK_ZXR: + ca0113_mmio_gpio_set(codec, 2, true); + ca0113_mmio_gpio_set(codec, 3, true); + ca0113_mmio_gpio_set(codec, 5, false); + zxr_headphone_gain_set(codec, 0); + chipio_set_control_param(codec, 0x0d, 0x24); + break; + case QUIRK_R3DI: + chipio_set_control_param(codec, 0x0d, 0x24); + r3di_gpio_out_set(codec, R3DI_LINE_OUT); + break; + case QUIRK_R3D: + chipio_set_control_param(codec, 0x0d, 0x24); + ca0113_mmio_gpio_set(codec, 1, true); + break; + case QUIRK_AE5: + ae5_mmio_select_out(codec); + ae5_headphone_gain_set(codec, 2); + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, 0x29, tmp); + dspio_set_uint_param(codec, 0x96, 0x2a, tmp); + chipio_set_control_param(codec, 0x0d, 0xa4); + chipio_write(codec, 0x18b03c, 0x00000012); + break; + } + break; + case HEADPHONE_OUT: + switch (spec->quirk) { + case QUIRK_SBZ: + ca0113_mmio_gpio_set(codec, 7, true); + ca0113_mmio_gpio_set(codec, 4, true); + ca0113_mmio_gpio_set(codec, 1, false); + chipio_set_control_param(codec, 0x0d, 0x12); + break; + case QUIRK_ZXR: + ca0113_mmio_gpio_set(codec, 2, false); + ca0113_mmio_gpio_set(codec, 3, false); + ca0113_mmio_gpio_set(codec, 5, true); + zxr_headphone_gain_set(codec, spec->zxr_gain_set); + chipio_set_control_param(codec, 0x0d, 0x21); + break; + case QUIRK_R3DI: + chipio_set_control_param(codec, 0x0d, 0x21); + r3di_gpio_out_set(codec, R3DI_HEADPHONE_OUT); + break; + case QUIRK_R3D: + chipio_set_control_param(codec, 0x0d, 0x21); + ca0113_mmio_gpio_set(codec, 0x1, false); + break; + case QUIRK_AE5: + ae5_mmio_select_out(codec); + ae5_headphone_gain_set(codec, + spec->ae5_headphone_gain_val); + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x96, 0x29, tmp); + dspio_set_uint_param(codec, 0x96, 0x2a, tmp); + chipio_set_control_param(codec, 0x0d, 0xa1); + chipio_write(codec, 0x18b03c, 0x00000012); + break; + } + break; + case SURROUND_OUT: + switch (spec->quirk) { + case QUIRK_SBZ: + ca0113_mmio_gpio_set(codec, 7, false); + ca0113_mmio_gpio_set(codec, 4, true); + ca0113_mmio_gpio_set(codec, 1, true); + chipio_set_control_param(codec, 0x0d, 0x18); + break; + case QUIRK_ZXR: + ca0113_mmio_gpio_set(codec, 2, true); + ca0113_mmio_gpio_set(codec, 3, true); + ca0113_mmio_gpio_set(codec, 5, false); + zxr_headphone_gain_set(codec, 0); + chipio_set_control_param(codec, 0x0d, 0x24); + break; + case QUIRK_R3DI: + chipio_set_control_param(codec, 0x0d, 0x24); + r3di_gpio_out_set(codec, R3DI_LINE_OUT); + break; + case QUIRK_R3D: + ca0113_mmio_gpio_set(codec, 1, true); + chipio_set_control_param(codec, 0x0d, 0x24); + break; + case QUIRK_AE5: + ae5_mmio_select_out(codec); + ae5_headphone_gain_set(codec, 2); + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, 0x29, tmp); + dspio_set_uint_param(codec, 0x96, 0x2a, tmp); + chipio_set_control_param(codec, 0x0d, 0xa4); + chipio_write(codec, 0x18b03c, 0x00000012); + break; + } + break; + } +} + /* * This function behaves similarly to the ca0132_select_out funciton above, * except with a few differences. It adds the ability to select the current @@ -3978,26 +4326,11 @@ static int ca0132_alt_select_out(struct hda_codec *codec) if (err < 0) goto exit; + ca0132_alt_select_out_quirk_handler(codec); + switch (spec->cur_out_type) { case SPEAKER_OUT: codec_dbg(codec, "%s speaker\n", __func__); - /*speaker out config*/ - switch (spec->quirk) { - case QUIRK_SBZ: - ca0132_mmio_gpio_set(codec, 7, false); - ca0132_mmio_gpio_set(codec, 4, true); - ca0132_mmio_gpio_set(codec, 1, true); - chipio_set_control_param(codec, 0x0D, 0x18); - break; - case QUIRK_R3DI: - chipio_set_control_param(codec, 0x0D, 0x24); - r3di_gpio_out_set(codec, R3DI_LINE_OUT); - break; - case QUIRK_R3D: - chipio_set_control_param(codec, 0x0D, 0x24); - ca0132_mmio_gpio_set(codec, 1, true); - break; - } /* disable headphone node */ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[1], 0, @@ -4021,23 +4354,6 @@ static int ca0132_alt_select_out(struct hda_codec *codec) break; case HEADPHONE_OUT: codec_dbg(codec, "%s hp\n", __func__); - /* Headphone out config*/ - switch (spec->quirk) { - case QUIRK_SBZ: - ca0132_mmio_gpio_set(codec, 7, true); - ca0132_mmio_gpio_set(codec, 4, true); - ca0132_mmio_gpio_set(codec, 1, false); - chipio_set_control_param(codec, 0x0D, 0x12); - break; - case QUIRK_R3DI: - chipio_set_control_param(codec, 0x0D, 0x21); - r3di_gpio_out_set(codec, R3DI_HEADPHONE_OUT); - break; - case QUIRK_R3D: - chipio_set_control_param(codec, 0x0D, 0x21); - ca0132_mmio_gpio_set(codec, 0x1, false); - break; - } snd_hda_codec_write(codec, spec->out_pins[0], 0, AC_VERB_SET_EAPD_BTLENABLE, 0x00); @@ -4067,23 +4383,7 @@ static int ca0132_alt_select_out(struct hda_codec *codec) break; case SURROUND_OUT: codec_dbg(codec, "%s surround\n", __func__); - /* Surround out config*/ - switch (spec->quirk) { - case QUIRK_SBZ: - ca0132_mmio_gpio_set(codec, 7, false); - ca0132_mmio_gpio_set(codec, 4, true); - ca0132_mmio_gpio_set(codec, 1, true); - chipio_set_control_param(codec, 0x0D, 0x18); - break; - case QUIRK_R3DI: - chipio_set_control_param(codec, 0x0D, 0x24); - r3di_gpio_out_set(codec, R3DI_LINE_OUT); - break; - case QUIRK_R3D: - ca0132_mmio_gpio_set(codec, 1, true); - chipio_set_control_param(codec, 0x0D, 0x24); - break; - } + /* enable line out node */ pin_ctl = snd_hda_codec_read(codec, spec->out_pins[0], 0, AC_VERB_GET_PIN_WIDGET_CONTROL, 0); @@ -4108,14 +4408,21 @@ static int ca0132_alt_select_out(struct hda_codec *codec) snd_hda_set_pin_ctl(codec, spec->out_pins[3], pin_ctl | PIN_OUT); - if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) - dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_ONE); - else - dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_EIGHT); + dspio_set_uint_param(codec, 0x80, 0x04, FLOAT_EIGHT); break; } + /* + * Surround always sets it's scp command to req 0x04 to FLOAT_EIGHT. + * With this set though, X_BASS cannot be enabled. So, if we have OutFX + * enabled, we need to make sure X_BASS is off, otherwise everything + * sounds all muffled. Running ca0132_effects_set with X_BASS as the + * effect should sort this out. + */ + if (spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) + ca0132_effects_set(codec, X_BASS, + spec->effects_switch[X_BASS - EFFECT_START_NID]); - /* run through the output dsp commands for line-out */ + /* run through the output dsp commands for the selected output. */ for (i = 0; i < alt_out_presets[spec->cur_out_type].commands; i++) { err = dspio_set_uint_param(codec, alt_out_presets[spec->cur_out_type].mids[i], @@ -4152,7 +4459,6 @@ static void ca0132_unsol_hp_delayed(struct work_struct *work) static void ca0132_set_dmic(struct hda_codec *codec, int enable); static int ca0132_mic_boost_set(struct hda_codec *codec, long val); -static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val); static void resume_mic1(struct hda_codec *codec, unsigned int oldval); static int stop_mic1(struct hda_codec *codec); static int ca0132_cvoice_switch_set(struct hda_codec *codec); @@ -4341,13 +4647,20 @@ static int ca0132_alt_select_in(struct hda_codec *codec) switch (spec->quirk) { case QUIRK_SBZ: case QUIRK_R3D: - ca0132_mmio_gpio_set(codec, 0, false); + ca0113_mmio_gpio_set(codec, 0, false); + tmp = FLOAT_THREE; + break; + case QUIRK_ZXR: tmp = FLOAT_THREE; break; case QUIRK_R3DI: r3di_gpio_mic_set(codec, R3DI_REAR_MIC); tmp = FLOAT_ONE; break; + case QUIRK_AE5: + ca0113_mmio_command_set(codec, 0x48, 0x28, 0x00); + tmp = FLOAT_THREE; + break; default: tmp = FLOAT_ONE; break; @@ -4362,10 +4675,19 @@ static int ca0132_alt_select_in(struct hda_codec *codec) chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); - - if (spec->quirk == QUIRK_SBZ) { + switch (spec->quirk) { + case QUIRK_SBZ: chipio_write(codec, 0x18B098, 0x0000000C); chipio_write(codec, 0x18B09C, 0x0000000C); + break; + case QUIRK_ZXR: + chipio_write(codec, 0x18B098, 0x0000000C); + chipio_write(codec, 0x18B09C, 0x000000CC); + break; + case QUIRK_AE5: + chipio_write(codec, 0x18B098, 0x0000000C); + chipio_write(codec, 0x18B09C, 0x0000004C); + break; } ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); break; @@ -4374,11 +4696,14 @@ static int ca0132_alt_select_in(struct hda_codec *codec) switch (spec->quirk) { case QUIRK_SBZ: case QUIRK_R3D: - ca0132_mmio_gpio_set(codec, 0, false); + ca0113_mmio_gpio_set(codec, 0, false); break; case QUIRK_R3DI: r3di_gpio_mic_set(codec, R3DI_REAR_MIC); break; + case QUIRK_AE5: + ca0113_mmio_command_set(codec, 0x48, 0x28, 0x00); + break; } chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); @@ -4389,11 +4714,13 @@ static int ca0132_alt_select_in(struct hda_codec *codec) tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x80, 0x00, tmp); - if (spec->quirk == QUIRK_SBZ) { + switch (spec->quirk) { + case QUIRK_SBZ: + case QUIRK_AE5: chipio_write(codec, 0x18B098, 0x00000000); chipio_write(codec, 0x18B09C, 0x00000000); + break; } - chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); break; @@ -4401,14 +4728,18 @@ static int ca0132_alt_select_in(struct hda_codec *codec) switch (spec->quirk) { case QUIRK_SBZ: case QUIRK_R3D: - ca0132_mmio_gpio_set(codec, 0, true); - ca0132_mmio_gpio_set(codec, 5, false); + ca0113_mmio_gpio_set(codec, 0, true); + ca0113_mmio_gpio_set(codec, 5, false); tmp = FLOAT_THREE; break; case QUIRK_R3DI: r3di_gpio_mic_set(codec, R3DI_FRONT_MIC); tmp = FLOAT_ONE; break; + case QUIRK_AE5: + ca0113_mmio_command_set(codec, 0x48, 0x28, 0x3f); + tmp = FLOAT_THREE; + break; default: tmp = FLOAT_ONE; break; @@ -4424,9 +4755,15 @@ static int ca0132_alt_select_in(struct hda_codec *codec) chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); - if (spec->quirk == QUIRK_SBZ) { + switch (spec->quirk) { + case QUIRK_SBZ: chipio_write(codec, 0x18B098, 0x0000000C); chipio_write(codec, 0x18B09C, 0x000000CC); + break; + case QUIRK_AE5: + chipio_write(codec, 0x18B098, 0x0000000C); + chipio_write(codec, 0x18B09C, 0x0000004C); + break; } ca0132_alt_mic_boost_set(codec, spec->mic_boost_enum_val); break; @@ -4435,7 +4772,6 @@ static int ca0132_alt_select_in(struct hda_codec *codec) snd_hda_power_down_pm(codec); return 0; - } /* @@ -4507,6 +4843,8 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) /* if PE if off, turn off out effects. */ if (!spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID]) val = 0; + if (spec->cur_out_type == SURROUND_OUT && nid == X_BASS) + val = 0; } /* for in effect, qualify with CrystalVoice */ @@ -4520,7 +4858,7 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) val = 0; /* If Voice Focus on SBZ, set to two channel. */ - if ((nid == VOICE_FOCUS) && (spec->quirk == QUIRK_SBZ) + if ((nid == VOICE_FOCUS) && (spec->use_pci_mmio) && (spec->cur_mic_type != REAR_LINE_IN)) { if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]) { @@ -4539,7 +4877,7 @@ static int ca0132_effects_set(struct hda_codec *codec, hda_nid_t nid, long val) * For SBZ noise reduction, there's an extra command * to module ID 0x47. No clue why. */ - if ((nid == NOISE_REDUCTION) && (spec->quirk == QUIRK_SBZ) + if ((nid == NOISE_REDUCTION) && (spec->use_pci_mmio) && (spec->cur_mic_type != REAR_LINE_IN)) { if (spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID]) { @@ -4678,6 +5016,27 @@ static int ca0132_alt_mic_boost_set(struct hda_codec *codec, long val) return ret; } +static int ae5_headphone_gain_set(struct hda_codec *codec, long val) +{ + unsigned int i; + + for (i = 0; i < 4; i++) + ca0113_mmio_command_set(codec, 0x48, 0x11 + i, + ae5_headphone_gain_presets[val].vals[i]); + return 0; +} + +/* + * gpio pin 1 is a relay that switches on/off, apparently setting the headphone + * amplifier to handle a 600 ohm load. + */ +static int zxr_headphone_gain_set(struct hda_codec *codec, long val) +{ + ca0113_mmio_gpio_set(codec, 1, val); + + return 0; +} + static int ca0132_vnode_switch_set(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { @@ -4942,66 +5301,172 @@ static int ca0132_alt_mic_boost_put(struct snd_kcontrol *kcontrol, return 1; } - /* - * Input Select Control for alternative ca0132 codecs. This exists because - * front microphone has no auto-detect, and we need a way to set the rear - * as line-in + * Sound BlasterX AE-5 Headphone Gain Controls. */ -static int ca0132_alt_input_source_info(struct snd_kcontrol *kcontrol, +#define AE5_HEADPHONE_GAIN_MAX 3 +static int ae5_headphone_gain_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { + char *sfx = " Ohms)"; + char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = IN_SRC_NUM_OF_INPUTS; - if (uinfo->value.enumerated.item >= IN_SRC_NUM_OF_INPUTS) - uinfo->value.enumerated.item = IN_SRC_NUM_OF_INPUTS - 1; - strcpy(uinfo->value.enumerated.name, - in_src_str[uinfo->value.enumerated.item]); + uinfo->value.enumerated.items = AE5_HEADPHONE_GAIN_MAX; + if (uinfo->value.enumerated.item >= AE5_HEADPHONE_GAIN_MAX) + uinfo->value.enumerated.item = AE5_HEADPHONE_GAIN_MAX - 1; + sprintf(namestr, "%s %s", + ae5_headphone_gain_presets[uinfo->value.enumerated.item].name, + sfx); + strcpy(uinfo->value.enumerated.name, namestr); return 0; } -static int ca0132_alt_input_source_get(struct snd_kcontrol *kcontrol, +static int ae5_headphone_gain_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; - ucontrol->value.enumerated.item[0] = spec->in_enum_val; + ucontrol->value.enumerated.item[0] = spec->ae5_headphone_gain_val; return 0; } -static int ca0132_alt_input_source_put(struct snd_kcontrol *kcontrol, +static int ae5_headphone_gain_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol) { struct hda_codec *codec = snd_kcontrol_chip(kcontrol); struct ca0132_spec *spec = codec->spec; int sel = ucontrol->value.enumerated.item[0]; - unsigned int items = IN_SRC_NUM_OF_INPUTS; + unsigned int items = AE5_HEADPHONE_GAIN_MAX; if (sel >= items) return 0; - codec_dbg(codec, "ca0132_alt_input_select: sel=%d, preset=%s\n", - sel, in_src_str[sel]); + codec_dbg(codec, "ae5_headphone_gain: boost=%d\n", + sel); - spec->in_enum_val = sel; + spec->ae5_headphone_gain_val = sel; - ca0132_alt_select_in(codec); + if (spec->out_enum_val == HEADPHONE_OUT) + ae5_headphone_gain_set(codec, spec->ae5_headphone_gain_val); return 1; } -/* Sound Blaster Z Output Select Control */ -static int ca0132_alt_output_select_get_info(struct snd_kcontrol *kcontrol, +/* + * Sound BlasterX AE-5 sound filter enumerated control. + */ +#define AE5_SOUND_FILTER_MAX 3 + +static int ae5_sound_filter_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo) { + char namestr[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; uinfo->count = 1; - uinfo->value.enumerated.items = NUM_OF_OUTPUTS; - if (uinfo->value.enumerated.item >= NUM_OF_OUTPUTS) - uinfo->value.enumerated.item = NUM_OF_OUTPUTS - 1; - strcpy(uinfo->value.enumerated.name, + uinfo->value.enumerated.items = AE5_SOUND_FILTER_MAX; + if (uinfo->value.enumerated.item >= AE5_SOUND_FILTER_MAX) + uinfo->value.enumerated.item = AE5_SOUND_FILTER_MAX - 1; + sprintf(namestr, "%s", + ae5_filter_presets[uinfo->value.enumerated.item].name); + strcpy(uinfo->value.enumerated.name, namestr); + return 0; +} + +static int ae5_sound_filter_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->ae5_filter_val; + return 0; +} + +static int ae5_sound_filter_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = AE5_SOUND_FILTER_MAX; + + if (sel >= items) + return 0; + + codec_dbg(codec, "ae5_sound_filter: %s\n", + ae5_filter_presets[sel].name); + + spec->ae5_filter_val = sel; + + ca0113_mmio_command_set_type2(codec, 0x48, 0x07, + ae5_filter_presets[sel].val); + + return 1; +} + +/* + * Input Select Control for alternative ca0132 codecs. This exists because + * front microphone has no auto-detect, and we need a way to set the rear + * as line-in + */ +static int ca0132_alt_input_source_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = IN_SRC_NUM_OF_INPUTS; + if (uinfo->value.enumerated.item >= IN_SRC_NUM_OF_INPUTS) + uinfo->value.enumerated.item = IN_SRC_NUM_OF_INPUTS - 1; + strcpy(uinfo->value.enumerated.name, + in_src_str[uinfo->value.enumerated.item]); + return 0; +} + +static int ca0132_alt_input_source_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + + ucontrol->value.enumerated.item[0] = spec->in_enum_val; + return 0; +} + +static int ca0132_alt_input_source_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct hda_codec *codec = snd_kcontrol_chip(kcontrol); + struct ca0132_spec *spec = codec->spec; + int sel = ucontrol->value.enumerated.item[0]; + unsigned int items = IN_SRC_NUM_OF_INPUTS; + + if (sel >= items) + return 0; + + codec_dbg(codec, "ca0132_alt_input_select: sel=%d, preset=%s\n", + sel, in_src_str[sel]); + + spec->in_enum_val = sel; + + ca0132_alt_select_in(codec); + + return 1; +} + +/* Sound Blaster Z Output Select Control */ +static int ca0132_alt_output_select_get_info(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_info *uinfo) +{ + uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; + uinfo->count = 1; + uinfo->value.enumerated.items = NUM_OF_OUTPUTS; + if (uinfo->value.enumerated.item >= NUM_OF_OUTPUTS) + uinfo->value.enumerated.item = NUM_OF_OUTPUTS - 1; + strcpy(uinfo->value.enumerated.name, alt_out_presets[uinfo->value.enumerated.item].name); return 0; } @@ -5330,6 +5795,16 @@ static int ca0132_switch_put(struct snd_kcontrol *kcontrol, goto exit; } + if (nid == ZXR_HEADPHONE_GAIN) { + spec->zxr_gain_set = *valp; + if (spec->cur_out_type == HEADPHONE_OUT) + changed = zxr_headphone_gain_set(codec, *valp); + else + changed = 0; + + goto exit; + } + exit: snd_hda_power_down(codec); return changed; @@ -5705,6 +6180,50 @@ static int ca0132_alt_add_mic_boost_enum(struct hda_codec *codec) } /* + * Add headphone gain enumerated control for the AE-5. This switches between + * three modes, low, medium, and high. When non-headphone outputs are selected, + * it is automatically set to high. This is the same behavior as Windows. + */ +static int ae5_add_headphone_gain_enum(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("AE-5: Headphone Gain", + AE5_HEADPHONE_GAIN_ENUM, 1, 0, HDA_OUTPUT); + knew.info = ae5_headphone_gain_info; + knew.get = ae5_headphone_gain_get; + knew.put = ae5_headphone_gain_put; + return snd_hda_ctl_add(codec, AE5_HEADPHONE_GAIN_ENUM, + snd_ctl_new1(&knew, codec)); +} + +/* + * Add sound filter enumerated control for the AE-5. This adds three different + * settings: Slow Roll Off, Minimum Phase, and Fast Roll Off. From what I've + * read into it, it changes the DAC's interpolation filter. + */ +static int ae5_add_sound_filter_enum(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + HDA_CODEC_MUTE_MONO("AE-5: Sound Filter", + AE5_SOUND_FILTER_ENUM, 1, 0, HDA_OUTPUT); + knew.info = ae5_sound_filter_info; + knew.get = ae5_sound_filter_get; + knew.put = ae5_sound_filter_put; + return snd_hda_ctl_add(codec, AE5_SOUND_FILTER_ENUM, + snd_ctl_new1(&knew, codec)); +} + +static int zxr_add_headphone_gain_switch(struct hda_codec *codec) +{ + struct snd_kcontrol_new knew = + CA0132_CODEC_MUTE_MONO("ZxR: 600 Ohm Gain", + ZXR_HEADPHONE_GAIN, 1, HDA_OUTPUT); + + return snd_hda_ctl_add(codec, ZXR_HEADPHONE_GAIN, + snd_ctl_new1(&knew, codec)); +} + +/* * Need to create slave controls for the alternate codecs that have surround * capabilities. */ @@ -5847,7 +6366,8 @@ static int ca0132_build_controls(struct hda_codec *codec) NULL, ca0132_alt_slave_pfxs, "Playback Switch", true, &spec->vmaster_mute.sw_kctl); - + if (err < 0) + return err; } /* Add in and out effects controls. @@ -5855,8 +6375,8 @@ static int ca0132_build_controls(struct hda_codec *codec) */ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT; for (i = 0; i < num_fx; i++) { - /* SBZ and R3D break if Echo Cancellation is used. */ - if (spec->quirk == QUIRK_SBZ || spec->quirk == QUIRK_R3D) { + /* Desktop cards break if Echo Cancellation is used. */ + if (spec->use_pci_mmio) { if (i == (ECHO_CANCELLATION - IN_EFFECT_START_NID + OUT_EFFECTS_COUNT)) continue; @@ -5874,8 +6394,14 @@ static int ca0132_build_controls(struct hda_codec *codec) * prefix, and change PlayEnhancement and CrystalVoice to match. */ if (spec->use_alt_controls) { - ca0132_alt_add_svm_enum(codec); - add_ca0132_alt_eq_presets(codec); + err = ca0132_alt_add_svm_enum(codec); + if (err < 0) + return err; + + err = add_ca0132_alt_eq_presets(codec); + if (err < 0) + return err; + err = add_fx_switch(codec, PLAY_ENHANCEMENT, "Enable OutFX", 0); if (err < 0) @@ -5912,7 +6438,9 @@ static int ca0132_build_controls(struct hda_codec *codec) if (err < 0) return err; } - add_voicefx(codec); + err = add_voicefx(codec); + if (err < 0) + return err; /* * If the codec uses alt_functions, you need the enumerated controls @@ -5920,9 +6448,36 @@ static int ca0132_build_controls(struct hda_codec *codec) * setting control. */ if (spec->use_alt_functions) { - ca0132_alt_add_output_enum(codec); - ca0132_alt_add_input_enum(codec); - ca0132_alt_add_mic_boost_enum(codec); + err = ca0132_alt_add_output_enum(codec); + if (err < 0) + return err; + err = ca0132_alt_add_mic_boost_enum(codec); + if (err < 0) + return err; + /* + * ZxR only has microphone input, there is no front panel + * header on the card, and aux-in is handled by the DBPro board. + */ + if (spec->quirk != QUIRK_ZXR) { + err = ca0132_alt_add_input_enum(codec); + if (err < 0) + return err; + } + } + + if (spec->quirk == QUIRK_AE5) { + err = ae5_add_headphone_gain_enum(codec); + if (err < 0) + return err; + err = ae5_add_sound_filter_enum(codec); + if (err < 0) + return err; + } + + if (spec->quirk == QUIRK_ZXR) { + err = zxr_add_headphone_gain_switch(codec); + if (err < 0) + return err; } #ifdef ENABLE_TUNING_CONTROLS add_tuning_ctls(codec); @@ -5955,6 +6510,27 @@ static int ca0132_build_controls(struct hda_codec *codec) return 0; } +static int dbpro_build_controls(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + int err = 0; + + if (spec->dig_out) { + err = snd_hda_create_spdif_out_ctls(codec, spec->dig_out, + spec->dig_out); + if (err < 0) + return err; + } + + if (spec->dig_in) { + err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in); + if (err < 0) + return err; + } + + return 0; +} + /* * PCM */ @@ -6058,6 +6634,40 @@ static int ca0132_build_pcms(struct hda_codec *codec) return 0; } +static int dbpro_build_pcms(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct hda_pcm *info; + + info = snd_hda_codec_pcm_new(codec, "CA0132 Alt Analog"); + if (!info) + return -ENOMEM; + info->stream[SNDRV_PCM_STREAM_CAPTURE] = ca0132_pcm_analog_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = 1; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adcs[0]; + + + if (!spec->dig_out && !spec->dig_in) + return 0; + + info = snd_hda_codec_pcm_new(codec, "CA0132 Digital"); + if (!info) + return -ENOMEM; + info->pcm_type = HDA_PCM_TYPE_SPDIF; + if (spec->dig_out) { + info->stream[SNDRV_PCM_STREAM_PLAYBACK] = + ca0132_pcm_digital_playback; + info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->dig_out; + } + if (spec->dig_in) { + info->stream[SNDRV_PCM_STREAM_CAPTURE] = + ca0132_pcm_digital_capture; + info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in; + } + + return 0; +} + static void init_output(struct hda_codec *codec, hda_nid_t pin, hda_nid_t dac) { if (pin) { @@ -6238,69 +6848,48 @@ static void ca0132_refresh_widget_caps(struct hda_codec *codec) } /* - * Recon3D r3d_setup_defaults sub functions. + * Creates a dummy stream to bind the output to. This seems to have to be done + * after changing the main outputs source and destination streams. */ - -static void r3d_dsp_scp_startup(struct hda_codec *codec) +static void ca0132_alt_create_dummy_stream(struct hda_codec *codec) { - unsigned int tmp; - - tmp = 0x00000000; - dspio_set_uint_param_no_source(codec, 0x80, 0x0A, tmp); - - tmp = 0x00000001; - dspio_set_uint_param_no_source(codec, 0x80, 0x0B, tmp); - - tmp = 0x00000004; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); - - tmp = 0x00000005; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); - - tmp = 0x00000000; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); - -} + struct ca0132_spec *spec = codec->spec; + unsigned int stream_format; -static void r3d_dsp_initial_mic_setup(struct hda_codec *codec) -{ - unsigned int tmp; + stream_format = snd_hdac_calc_stream_format(48000, 2, + SNDRV_PCM_FORMAT_S32_LE, 32, 0); - /* Mic 1 Setup */ - chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - /* This ConnPointID is unique to Recon3Di. Haven't seen it elsewhere */ - chipio_set_conn_rate(codec, 0x0F, SR_96_000); - tmp = FLOAT_ONE; - dspio_set_uint_param(codec, 0x80, 0x00, tmp); + snd_hda_codec_setup_stream(codec, spec->dacs[0], spec->dsp_stream_id, + 0, stream_format); - /* Mic 2 Setup, even though it isn't connected on SBZ */ - chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000); - chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, SR_96_000); - chipio_set_conn_rate(codec, 0x0F, SR_96_000); - tmp = FLOAT_ZERO; - dspio_set_uint_param(codec, 0x80, 0x01, tmp); + snd_hda_codec_cleanup_stream(codec, spec->dacs[0]); } /* - * Initialize Sound Blaster Z analog microphones. + * Initialize mic for non-chromebook ca0132 implementations. */ -static void sbz_init_analog_mics(struct hda_codec *codec) +static void ca0132_alt_init_analog_mics(struct hda_codec *codec) { + struct ca0132_spec *spec = codec->spec; unsigned int tmp; /* Mic 1 Setup */ chipio_set_conn_rate(codec, MEM_CONNID_MICIN1, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT1, SR_96_000); - tmp = FLOAT_THREE; + if (spec->quirk == QUIRK_R3DI) { + chipio_set_conn_rate(codec, 0x0F, SR_96_000); + tmp = FLOAT_ONE; + } else + tmp = FLOAT_THREE; dspio_set_uint_param(codec, 0x80, 0x00, tmp); - /* Mic 2 Setup, even though it isn't connected on SBZ */ + /* Mic 2 setup (not present on desktop cards) */ chipio_set_conn_rate(codec, MEM_CONNID_MICIN2, SR_96_000); chipio_set_conn_rate(codec, MEM_CONNID_MICOUT2, SR_96_000); + if (spec->quirk == QUIRK_R3DI) + chipio_set_conn_rate(codec, 0x0F, SR_96_000); tmp = FLOAT_ZERO; dspio_set_uint_param(codec, 0x80, 0x01, tmp); - } /* @@ -6333,7 +6922,6 @@ static void sbz_connect_streams(struct hda_codec *codec) codec_dbg(codec, "Connect Streams exited, mutex released.\n"); mutex_unlock(&spec->chipio_mutex); - } /* @@ -6360,19 +6948,29 @@ static void sbz_chipio_startup_data(struct hda_codec *codec) chipio_set_stream_channels(codec, 0x0C, 6); chipio_set_stream_control(codec, 0x0C, 1); /* No clue what these control */ - chipio_write_no_mutex(codec, 0x190030, 0x0001e0c0); - chipio_write_no_mutex(codec, 0x190034, 0x0001e1c1); - chipio_write_no_mutex(codec, 0x190038, 0x0001e4c2); - chipio_write_no_mutex(codec, 0x19003c, 0x0001e5c3); - chipio_write_no_mutex(codec, 0x190040, 0x0001e2c4); - chipio_write_no_mutex(codec, 0x190044, 0x0001e3c5); - chipio_write_no_mutex(codec, 0x190048, 0x0001e8c6); - chipio_write_no_mutex(codec, 0x19004c, 0x0001e9c7); - chipio_write_no_mutex(codec, 0x190050, 0x0001ecc8); - chipio_write_no_mutex(codec, 0x190054, 0x0001edc9); - chipio_write_no_mutex(codec, 0x190058, 0x0001eaca); - chipio_write_no_mutex(codec, 0x19005c, 0x0001ebcb); - + if (spec->quirk == QUIRK_SBZ) { + chipio_write_no_mutex(codec, 0x190030, 0x0001e0c0); + chipio_write_no_mutex(codec, 0x190034, 0x0001e1c1); + chipio_write_no_mutex(codec, 0x190038, 0x0001e4c2); + chipio_write_no_mutex(codec, 0x19003c, 0x0001e5c3); + chipio_write_no_mutex(codec, 0x190040, 0x0001e2c4); + chipio_write_no_mutex(codec, 0x190044, 0x0001e3c5); + chipio_write_no_mutex(codec, 0x190048, 0x0001e8c6); + chipio_write_no_mutex(codec, 0x19004c, 0x0001e9c7); + chipio_write_no_mutex(codec, 0x190050, 0x0001ecc8); + chipio_write_no_mutex(codec, 0x190054, 0x0001edc9); + chipio_write_no_mutex(codec, 0x190058, 0x0001eaca); + chipio_write_no_mutex(codec, 0x19005c, 0x0001ebcb); + } else if (spec->quirk == QUIRK_ZXR) { + chipio_write_no_mutex(codec, 0x190038, 0x000140c2); + chipio_write_no_mutex(codec, 0x19003c, 0x000141c3); + chipio_write_no_mutex(codec, 0x190040, 0x000150c4); + chipio_write_no_mutex(codec, 0x190044, 0x000151c5); + chipio_write_no_mutex(codec, 0x190050, 0x000142c8); + chipio_write_no_mutex(codec, 0x190054, 0x000143c9); + chipio_write_no_mutex(codec, 0x190058, 0x000152ca); + chipio_write_no_mutex(codec, 0x19005c, 0x000153cb); + } chipio_write_no_mutex(codec, 0x19042c, 0x00000001); codec_dbg(codec, "Startup Data exited, mutex released.\n"); @@ -6380,35 +6978,56 @@ static void sbz_chipio_startup_data(struct hda_codec *codec) } /* - * Sound Blaster Z uses these after DSP is loaded. Weird SCP commands - * without a 0x20 source like normal. + * Custom DSP SCP commands where the src value is 0x00 instead of 0x20. This is + * done after the DSP is loaded. */ -static void sbz_dsp_scp_startup(struct hda_codec *codec) +static void ca0132_alt_dsp_scp_startup(struct hda_codec *codec) { - unsigned int tmp; - - tmp = 0x00000003; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); - - tmp = 0x00000000; - dspio_set_uint_param_no_source(codec, 0x80, 0x0A, tmp); - - tmp = 0x00000001; - dspio_set_uint_param_no_source(codec, 0x80, 0x0B, tmp); - - tmp = 0x00000004; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); - - tmp = 0x00000005; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); - - tmp = 0x00000000; - dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + struct ca0132_spec *spec = codec->spec; + unsigned int tmp, i; + /* + * Gotta run these twice, or else mic works inconsistently. Not clear + * why this is, but multiple tests have confirmed it. + */ + for (i = 0; i < 2; i++) { + switch (spec->quirk) { + case QUIRK_SBZ: + case QUIRK_AE5: + tmp = 0x00000003; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + tmp = 0x00000000; + dspio_set_uint_param_no_source(codec, 0x80, 0x0A, tmp); + tmp = 0x00000001; + dspio_set_uint_param_no_source(codec, 0x80, 0x0B, tmp); + tmp = 0x00000004; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + tmp = 0x00000005; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + tmp = 0x00000000; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + break; + case QUIRK_R3D: + case QUIRK_R3DI: + tmp = 0x00000000; + dspio_set_uint_param_no_source(codec, 0x80, 0x0A, tmp); + tmp = 0x00000001; + dspio_set_uint_param_no_source(codec, 0x80, 0x0B, tmp); + tmp = 0x00000004; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + tmp = 0x00000005; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + tmp = 0x00000000; + dspio_set_uint_param_no_source(codec, 0x80, 0x0C, tmp); + break; + } + msleep(100); + } } -static void sbz_dsp_initial_mic_setup(struct hda_codec *codec) +static void ca0132_alt_dsp_initial_mic_setup(struct hda_codec *codec) { + struct ca0132_spec *spec = codec->spec; unsigned int tmp; chipio_set_stream_control(codec, 0x03, 0); @@ -6423,17 +7042,170 @@ static void sbz_dsp_initial_mic_setup(struct hda_codec *codec) chipio_set_stream_control(codec, 0x03, 1); chipio_set_stream_control(codec, 0x04, 1); - chipio_write(codec, 0x18b098, 0x0000000c); - chipio_write(codec, 0x18b09C, 0x0000000c); + switch (spec->quirk) { + case QUIRK_SBZ: + chipio_write(codec, 0x18b098, 0x0000000c); + chipio_write(codec, 0x18b09C, 0x0000000c); + break; + case QUIRK_AE5: + chipio_write(codec, 0x18b098, 0x0000000c); + chipio_write(codec, 0x18b09c, 0x0000004c); + break; + } } -/* - * Setup default parameters for DSP - */ -static void ca0132_setup_defaults(struct hda_codec *codec) +static void ae5_post_dsp_register_set(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - unsigned int tmp; + + chipio_8051_write_direct(codec, 0x93, 0x10); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x44); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc2); + + writeb(0xff, spec->mem_base + 0x304); + writeb(0xff, spec->mem_base + 0x304); + writeb(0xff, spec->mem_base + 0x304); + writeb(0xff, spec->mem_base + 0x304); + writeb(0x00, spec->mem_base + 0x100); + writeb(0xff, spec->mem_base + 0x304); + writeb(0x00, spec->mem_base + 0x100); + writeb(0xff, spec->mem_base + 0x304); + writeb(0x00, spec->mem_base + 0x100); + writeb(0xff, spec->mem_base + 0x304); + writeb(0x00, spec->mem_base + 0x100); + writeb(0xff, spec->mem_base + 0x304); + + ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x3f); + ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x3f); + ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); +} + +static void ae5_post_dsp_param_setup(struct hda_codec *codec) +{ + /* + * Param3 in the 8051's memory is represented by the ascii string 'mch' + * which seems to be 'multichannel'. This is also mentioned in the + * AE-5's registry values in Windows. + */ + chipio_set_control_param(codec, 3, 0); + /* + * I believe ASI is 'audio serial interface' and that it's used to + * change colors on the external LED strip connected to the AE-5. + */ + chipio_set_control_flag(codec, CONTROL_FLAG_ASI_96KHZ, 1); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x724, 0x83); + chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x92); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_HIGH, 0xfa); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_DATA_WRITE, 0x22); +} + +static void ae5_post_dsp_pll_setup(struct hda_codec *codec) +{ + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x41); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc8); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x45); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xcc); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x40); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xcb); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x43); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc7); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x51); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0x8d); +} + +static void ae5_post_dsp_stream_setup(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, 0x725, 0x81); + + chipio_set_conn_rate_no_mutex(codec, 0x70, SR_96_000); + + chipio_set_stream_channels(codec, 0x0C, 6); + chipio_set_stream_control(codec, 0x0C, 1); + + chipio_set_stream_source_dest(codec, 0x5, 0x43, 0x0); + + chipio_set_stream_source_dest(codec, 0x18, 0x9, 0xd0); + chipio_set_conn_rate_no_mutex(codec, 0xd0, SR_96_000); + chipio_set_stream_channels(codec, 0x18, 6); + chipio_set_stream_control(codec, 0x18, 1); + + chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 4); + + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x43); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc7); + + ca0113_mmio_command_set(codec, 0x48, 0x01, 0x80); + + mutex_unlock(&spec->chipio_mutex); +} + +static void ae5_post_dsp_startup_data(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + mutex_lock(&spec->chipio_mutex); + + chipio_write_no_mutex(codec, 0x189000, 0x0001f101); + chipio_write_no_mutex(codec, 0x189004, 0x0001f101); + chipio_write_no_mutex(codec, 0x189024, 0x00014004); + chipio_write_no_mutex(codec, 0x189028, 0x0002000f); + + ca0113_mmio_command_set(codec, 0x48, 0x0a, 0x05); + chipio_set_control_param_no_mutex(codec, CONTROL_PARAM_ASI, 7); + ca0113_mmio_command_set(codec, 0x48, 0x0b, 0x12); + ca0113_mmio_command_set(codec, 0x48, 0x04, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x06, 0x48); + ca0113_mmio_command_set(codec, 0x48, 0x0a, 0x05); + ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00); + ca0113_mmio_gpio_set(codec, 0, true); + ca0113_mmio_gpio_set(codec, 1, true); + ca0113_mmio_command_set(codec, 0x48, 0x07, 0x80); + + chipio_write_no_mutex(codec, 0x18b03c, 0x00000012); + + ca0113_mmio_command_set(codec, 0x48, 0x0f, 0x00); + ca0113_mmio_command_set(codec, 0x48, 0x10, 0x00); + + mutex_unlock(&spec->chipio_mutex); +} + +/* + * Setup default parameters for DSP + */ +static void ca0132_setup_defaults(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; int num_fx; int idx, i; @@ -6485,9 +7257,8 @@ static void r3d_setup_defaults(struct hda_codec *codec) if (spec->dsp_state != DSP_DOWNLOADED) return; - r3d_dsp_scp_startup(codec); - - r3d_dsp_initial_mic_setup(codec); + ca0132_alt_dsp_scp_startup(codec); + ca0132_alt_init_analog_mics(codec); /*remove DSP headroom*/ tmp = FLOAT_ZERO; @@ -6523,19 +7294,16 @@ static void r3d_setup_defaults(struct hda_codec *codec) static void sbz_setup_defaults(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - unsigned int tmp, stream_format; + unsigned int tmp; int num_fx; int idx, i; if (spec->dsp_state != DSP_DOWNLOADED) return; - sbz_dsp_scp_startup(codec); - - sbz_init_analog_mics(codec); - + ca0132_alt_dsp_scp_startup(codec); + ca0132_alt_init_analog_mics(codec); sbz_connect_streams(codec); - sbz_chipio_startup_data(codec); chipio_set_stream_control(codec, 0x03, 1); @@ -6561,8 +7329,7 @@ static void sbz_setup_defaults(struct hda_codec *codec) /* Set speaker source? */ dspio_set_uint_param(codec, 0x32, 0x00, tmp); - sbz_dsp_initial_mic_setup(codec); - + ca0132_alt_dsp_initial_mic_setup(codec); /* out, in effects + voicefx */ num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; @@ -6575,23 +7342,74 @@ static void sbz_setup_defaults(struct hda_codec *codec) } } - /* - * Have to make a stream to bind the sound output to, otherwise - * you'll get dead audio. Before I did this, it would bind to an - * audio input, and would never work - */ - stream_format = snd_hdac_calc_stream_format(48000, 2, - SNDRV_PCM_FORMAT_S32_LE, 32, 0); + ca0132_alt_create_dummy_stream(codec); +} - snd_hda_codec_setup_stream(codec, spec->dacs[0], spec->dsp_stream_id, - 0, stream_format); +/* + * Setup default parameters for the Sound BlasterX AE-5 DSP. + */ +static void ae5_setup_defaults(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + unsigned int tmp; + int num_fx; + int idx, i; - snd_hda_codec_cleanup_stream(codec, spec->dacs[0]); + if (spec->dsp_state != DSP_DOWNLOADED) + return; - snd_hda_codec_setup_stream(codec, spec->dacs[0], spec->dsp_stream_id, - 0, stream_format); + ca0132_alt_dsp_scp_startup(codec); + ca0132_alt_init_analog_mics(codec); + chipio_set_stream_control(codec, 0x03, 1); + chipio_set_stream_control(codec, 0x04, 1); - snd_hda_codec_cleanup_stream(codec, spec->dacs[0]); + /* New, unknown SCP req's */ + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, 0x29, tmp); + dspio_set_uint_param(codec, 0x96, 0x2a, tmp); + dspio_set_uint_param(codec, 0x80, 0x0d, tmp); + dspio_set_uint_param(codec, 0x80, 0x0e, tmp); + + ca0113_mmio_command_set(codec, 0x30, 0x2e, 0x3f); + ca0113_mmio_gpio_set(codec, 0, false); + ca0113_mmio_command_set(codec, 0x30, 0x28, 0x00); + + /* Internal loopback off */ + tmp = FLOAT_ONE; + dspio_set_uint_param(codec, 0x37, 0x08, tmp); + dspio_set_uint_param(codec, 0x37, 0x10, tmp); + + /*remove DSP headroom*/ + tmp = FLOAT_ZERO; + dspio_set_uint_param(codec, 0x96, 0x3C, tmp); + + /* set WUH source */ + tmp = FLOAT_TWO; + dspio_set_uint_param(codec, 0x31, 0x00, tmp); + chipio_set_conn_rate(codec, MEM_CONNID_WUH, SR_48_000); + + /* Set speaker source? */ + dspio_set_uint_param(codec, 0x32, 0x00, tmp); + + ca0132_alt_dsp_initial_mic_setup(codec); + ae5_post_dsp_register_set(codec); + ae5_post_dsp_param_setup(codec); + ae5_post_dsp_pll_setup(codec); + ae5_post_dsp_stream_setup(codec); + ae5_post_dsp_startup_data(codec); + + /* out, in effects + voicefx */ + num_fx = OUT_EFFECTS_COUNT + IN_EFFECTS_COUNT + 1; + for (idx = 0; idx < num_fx; idx++) { + for (i = 0; i <= ca0132_effects[idx].params; i++) { + dspio_set_uint_param(codec, + ca0132_effects[idx].mid, + ca0132_effects[idx].reqs[i], + ca0132_effects[idx].def_vals[i]); + } + } + + ca0132_alt_create_dummy_stream(codec); } /* @@ -6673,12 +7491,14 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec) */ switch (spec->quirk) { case QUIRK_SBZ: - if (request_firmware(&fw_entry, SBZ_EFX_FILE, + case QUIRK_R3D: + case QUIRK_AE5: + if (request_firmware(&fw_entry, DESKTOP_EFX_FILE, codec->card->dev) != 0) { - codec_dbg(codec, "SBZ alt firmware not detected. "); + codec_dbg(codec, "Desktop firmware not found."); spec->alt_firmware_present = false; } else { - codec_dbg(codec, "Sound Blaster Z firmware selected."); + codec_dbg(codec, "Desktop firmware selected."); spec->alt_firmware_present = true; } break; @@ -6921,6 +7741,14 @@ static void ca0132_init_chip(struct hda_codec *codec) spec->effects_switch[PLAY_ENHANCEMENT - EFFECT_START_NID] = 1; spec->effects_switch[CRYSTAL_VOICE - EFFECT_START_NID] = 0; + /* + * The ZxR doesn't have a front panel header, and it's line-in is on + * the daughter board. So, there is no input enum control, and we need + * to make sure that spec->in_enum_val is set properly. + */ + if (spec->quirk == QUIRK_ZXR) + spec->in_enum_val = REAR_MIC; + #ifdef ENABLE_TUNING_CONTROLS ca0132_init_tuning_defaults(codec); #endif @@ -6948,11 +7776,11 @@ static void sbz_region2_exit(struct hda_codec *codec) for (i = 0; i < 8; i++) writeb(0xb3, spec->mem_base + 0x304); - ca0132_mmio_gpio_set(codec, 0, false); - ca0132_mmio_gpio_set(codec, 1, false); - ca0132_mmio_gpio_set(codec, 4, true); - ca0132_mmio_gpio_set(codec, 5, false); - ca0132_mmio_gpio_set(codec, 7, false); + ca0113_mmio_gpio_set(codec, 0, false); + ca0113_mmio_gpio_set(codec, 1, false); + ca0113_mmio_gpio_set(codec, 4, true); + ca0113_mmio_gpio_set(codec, 5, false); + ca0113_mmio_gpio_set(codec, 7, false); } static void sbz_set_pin_ctl_default(struct hda_codec *codec) @@ -6995,6 +7823,16 @@ static void sbz_gpio_shutdown_commands(struct hda_codec *codec, int dir, AC_VERB_SET_GPIO_DATA, data); } +static void zxr_dbpro_power_state_shutdown(struct hda_codec *codec) +{ + hda_nid_t pins[7] = {0x05, 0x0c, 0x09, 0x0e, 0x08, 0x11, 0x01}; + unsigned int i; + + for (i = 0; i < 7; i++) + snd_hda_codec_write(codec, pins[i], 0, + AC_VERB_SET_POWER_STATE, 0x03); +} + static void sbz_exit_chip(struct hda_codec *codec) { chipio_set_stream_control(codec, 0x03, 0); @@ -7037,6 +7875,61 @@ static void r3d_exit_chip(struct hda_codec *codec) snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x5b); } +static void ae5_exit_chip(struct hda_codec *codec) +{ + chipio_set_stream_control(codec, 0x03, 0); + chipio_set_stream_control(codec, 0x04, 0); + + ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); + ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); + ca0113_mmio_command_set(codec, 0x30, 0x30, 0x00); + ca0113_mmio_command_set(codec, 0x30, 0x2b, 0x00); + ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x00); + ca0113_mmio_gpio_set(codec, 0, false); + ca0113_mmio_gpio_set(codec, 1, false); + + snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); + snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); + + chipio_set_control_param(codec, CONTROL_PARAM_ASI, 0); + + chipio_set_stream_control(codec, 0x18, 0); + chipio_set_stream_control(codec, 0x0c, 0); + + snd_hda_codec_write(codec, 0x01, 0, 0x724, 0x83); +} + +static void zxr_exit_chip(struct hda_codec *codec) +{ + chipio_set_stream_control(codec, 0x03, 0); + chipio_set_stream_control(codec, 0x04, 0); + chipio_set_stream_control(codec, 0x14, 0); + chipio_set_stream_control(codec, 0x0C, 0); + + chipio_set_conn_rate(codec, 0x41, SR_192_000); + chipio_set_conn_rate(codec, 0x91, SR_192_000); + + chipio_write(codec, 0x18a020, 0x00000083); + + snd_hda_codec_write(codec, 0x01, 0, 0x793, 0x00); + snd_hda_codec_write(codec, 0x01, 0, 0x794, 0x53); + + ca0132_clear_unsolicited(codec); + sbz_set_pin_ctl_default(codec); + snd_hda_codec_write(codec, 0x0B, 0, AC_VERB_SET_EAPD_BTLENABLE, 0x00); + + ca0113_mmio_gpio_set(codec, 5, false); + ca0113_mmio_gpio_set(codec, 2, false); + ca0113_mmio_gpio_set(codec, 3, false); + ca0113_mmio_gpio_set(codec, 0, false); + ca0113_mmio_gpio_set(codec, 4, true); + ca0113_mmio_gpio_set(codec, 0, true); + ca0113_mmio_gpio_set(codec, 5, true); + ca0113_mmio_gpio_set(codec, 2, false); + ca0113_mmio_gpio_set(codec, 3, false); +} + static void ca0132_exit_chip(struct hda_codec *codec) { /* put any chip cleanup stuffs here. */ @@ -7140,11 +8033,6 @@ static void sbz_pre_dsp_setup(struct hda_codec *codec) writel(0x00820680, spec->mem_base + 0x01C); writel(0x00820680, spec->mem_base + 0x01C); - snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfc); - snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfd); - snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfe); - snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xff); - chipio_write(codec, 0x18b0a4, 0x000000c2); snd_hda_codec_write(codec, 0x11, 0, @@ -7153,12 +8041,6 @@ static void sbz_pre_dsp_setup(struct hda_codec *codec) static void r3d_pre_dsp_setup(struct hda_codec *codec) { - - snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfc); - snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfd); - snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xfe); - snd_hda_codec_write(codec, 0x15, 0, 0xd00, 0xff); - chipio_write(codec, 0x18b0a4, 0x000000c2); snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, @@ -7205,23 +8087,116 @@ static void ca0132_mmio_init(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; - writel(0x00000000, spec->mem_base + 0x400); - writel(0x00000000, spec->mem_base + 0x408); - writel(0x00000000, spec->mem_base + 0x40C); - writel(0x00880680, spec->mem_base + 0x01C); - writel(0x00000083, spec->mem_base + 0xC0C); + if (spec->quirk == QUIRK_AE5) + writel(0x00000001, spec->mem_base + 0x400); + else + writel(0x00000000, spec->mem_base + 0x400); + + if (spec->quirk == QUIRK_AE5) + writel(0x00000001, spec->mem_base + 0x408); + else + writel(0x00000000, spec->mem_base + 0x408); + + if (spec->quirk == QUIRK_AE5) + writel(0x00000001, spec->mem_base + 0x40c); + else + writel(0x00000000, spec->mem_base + 0x40C); + + if (spec->quirk == QUIRK_ZXR) + writel(0x00880640, spec->mem_base + 0x01C); + else + writel(0x00880680, spec->mem_base + 0x01C); + + if (spec->quirk == QUIRK_AE5) + writel(0x00000080, spec->mem_base + 0xC0C); + else + writel(0x00000083, spec->mem_base + 0xC0C); + writel(0x00000030, spec->mem_base + 0xC00); writel(0x00000000, spec->mem_base + 0xC04); + + if (spec->quirk == QUIRK_AE5) + writel(0x00000000, spec->mem_base + 0xC0C); + else + writel(0x00000003, spec->mem_base + 0xC0C); + writel(0x00000003, spec->mem_base + 0xC0C); writel(0x00000003, spec->mem_base + 0xC0C); writel(0x00000003, spec->mem_base + 0xC0C); - writel(0x00000003, spec->mem_base + 0xC0C); - writel(0x000000C1, spec->mem_base + 0xC08); + + if (spec->quirk == QUIRK_AE5) + writel(0x00000001, spec->mem_base + 0xC08); + else + writel(0x000000C1, spec->mem_base + 0xC08); + writel(0x000000F1, spec->mem_base + 0xC08); writel(0x00000001, spec->mem_base + 0xC08); writel(0x000000C7, spec->mem_base + 0xC08); writel(0x000000C1, spec->mem_base + 0xC08); writel(0x00000080, spec->mem_base + 0xC04); + + if (spec->quirk == QUIRK_AE5) { + writel(0x00000000, spec->mem_base + 0x42c); + writel(0x00000000, spec->mem_base + 0x46c); + writel(0x00000000, spec->mem_base + 0x4ac); + writel(0x00000000, spec->mem_base + 0x4ec); + writel(0x00000000, spec->mem_base + 0x43c); + writel(0x00000000, spec->mem_base + 0x47c); + writel(0x00000000, spec->mem_base + 0x4bc); + writel(0x00000000, spec->mem_base + 0x4fc); + writel(0x00000600, spec->mem_base + 0x100); + writel(0x00000014, spec->mem_base + 0x410); + writel(0x0000060f, spec->mem_base + 0x100); + writel(0x0000070f, spec->mem_base + 0x100); + writel(0x00000aff, spec->mem_base + 0x830); + writel(0x00000000, spec->mem_base + 0x86c); + writel(0x0000006b, spec->mem_base + 0x800); + writel(0x00000001, spec->mem_base + 0x86c); + writel(0x0000006b, spec->mem_base + 0x800); + writel(0x00000057, spec->mem_base + 0x804); + writel(0x00800000, spec->mem_base + 0x20c); + } +} + +/* + * This function writes to some SFR's, does some region2 writes, and then + * eventually resets the codec with the 0x7ff verb. Not quite sure why it does + * what it does. + */ +static void ae5_register_set(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + chipio_8051_write_direct(codec, 0x93, 0x10); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x44); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0xc2); + + writeb(0x0f, spec->mem_base + 0x304); + writeb(0x0f, spec->mem_base + 0x304); + writeb(0x0f, spec->mem_base + 0x304); + writeb(0x0f, spec->mem_base + 0x304); + writeb(0x0e, spec->mem_base + 0x100); + writeb(0x1f, spec->mem_base + 0x304); + writeb(0x0c, spec->mem_base + 0x100); + writeb(0x3f, spec->mem_base + 0x304); + writeb(0x08, spec->mem_base + 0x100); + writeb(0x7f, spec->mem_base + 0x304); + writeb(0x00, spec->mem_base + 0x100); + writeb(0xff, spec->mem_base + 0x304); + + ca0113_mmio_command_set(codec, 0x30, 0x2d, 0x3f); + + chipio_8051_write_direct(codec, 0x90, 0x00); + chipio_8051_write_direct(codec, 0x90, 0x10); + + ca0113_mmio_command_set(codec, 0x48, 0x07, 0x83); + + chipio_write(codec, 0x18b0a4, 0x000000c2); + + snd_hda_codec_write(codec, 0x01, 0, 0x7ff, 0x00); + snd_hda_codec_write(codec, 0x01, 0, 0x7ff, 0x00); } /* @@ -7257,6 +8232,21 @@ static void ca0132_alt_init(struct hda_codec *codec) snd_hda_sequence_write(codec, spec->chip_init_verbs); snd_hda_sequence_write(codec, spec->desktop_init_verbs); break; + case QUIRK_AE5: + ca0132_gpio_init(codec); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_8051_ADDRESS_LOW, 0x49); + snd_hda_codec_write(codec, WIDGET_CHIP_CTRL, 0, + VENDOR_CHIPIO_PLL_PMU_WRITE, 0x88); + chipio_write(codec, 0x18b030, 0x00000020); + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_sequence_write(codec, spec->desktop_init_verbs); + ca0113_mmio_command_set(codec, 0x30, 0x32, 0x3f); + break; + case QUIRK_ZXR: + snd_hda_sequence_write(codec, spec->chip_init_verbs); + snd_hda_sequence_write(codec, spec->desktop_init_verbs); + break; } } @@ -7298,6 +8288,9 @@ static int ca0132_init(struct hda_codec *codec) snd_hda_power_up_pm(codec); + if (spec->quirk == QUIRK_AE5) + ae5_register_set(codec); + ca0132_init_unsol(codec); ca0132_init_params(codec); ca0132_init_flags(codec); @@ -7317,8 +8310,12 @@ static int ca0132_init(struct hda_codec *codec) r3d_setup_defaults(codec); break; case QUIRK_SBZ: + case QUIRK_ZXR: sbz_setup_defaults(codec); break; + case QUIRK_AE5: + ae5_setup_defaults(codec); + break; default: ca0132_setup_defaults(codec); ca0132_init_analog_mic2(codec); @@ -7372,6 +8369,21 @@ static int ca0132_init(struct hda_codec *codec) return 0; } +static int dbpro_init(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + struct auto_pin_cfg *cfg = &spec->autocfg; + unsigned int i; + + init_output(codec, cfg->dig_out_pins[0], spec->dig_out); + init_input(codec, cfg->dig_in_pin, spec->dig_in); + + for (i = 0; i < spec->num_inputs; i++) + init_input(codec, spec->input_pins[i], spec->adcs[i]); + + return 0; +} + static void ca0132_free(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -7382,9 +8394,15 @@ static void ca0132_free(struct hda_codec *codec) case QUIRK_SBZ: sbz_exit_chip(codec); break; + case QUIRK_ZXR: + zxr_exit_chip(codec); + break; case QUIRK_R3D: r3d_exit_chip(codec); break; + case QUIRK_AE5: + ae5_exit_chip(codec); + break; case QUIRK_R3DI: r3di_gpio_shutdown(codec); break; @@ -7400,6 +8418,16 @@ static void ca0132_free(struct hda_codec *codec) kfree(codec->spec); } +static void dbpro_free(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + zxr_dbpro_power_state_shutdown(codec); + + kfree(spec->spec_init_verbs); + kfree(codec->spec); +} + static void ca0132_reboot_notify(struct hda_codec *codec) { codec->patch_ops.free(codec); @@ -7414,6 +8442,13 @@ static const struct hda_codec_ops ca0132_patch_ops = { .reboot_notify = ca0132_reboot_notify, }; +static const struct hda_codec_ops dbpro_patch_ops = { + .build_controls = dbpro_build_controls, + .build_pcms = dbpro_build_pcms, + .init = dbpro_init, + .free = dbpro_free, +}; + static void ca0132_config(struct hda_codec *codec) { struct ca0132_spec *spec = codec->spec; @@ -7432,9 +8467,33 @@ static void ca0132_config(struct hda_codec *codec) switch (spec->quirk) { case QUIRK_ALIENWARE: - codec_dbg(codec, "ca0132_config: QUIRK_ALIENWARE applied.\n"); + codec_dbg(codec, "%s: QUIRK_ALIENWARE applied.\n", __func__); snd_hda_apply_pincfgs(codec, alienware_pincfgs); + break; + case QUIRK_SBZ: + codec_dbg(codec, "%s: QUIRK_SBZ applied.\n", __func__); + snd_hda_apply_pincfgs(codec, sbz_pincfgs); + break; + case QUIRK_ZXR: + codec_dbg(codec, "%s: QUIRK_ZXR applied.\n", __func__); + snd_hda_apply_pincfgs(codec, zxr_pincfgs); + break; + case QUIRK_R3D: + codec_dbg(codec, "%s: QUIRK_R3D applied.\n", __func__); + snd_hda_apply_pincfgs(codec, r3d_pincfgs); + break; + case QUIRK_R3DI: + codec_dbg(codec, "%s: QUIRK_R3DI applied.\n", __func__); + snd_hda_apply_pincfgs(codec, r3di_pincfgs); + break; + case QUIRK_AE5: + codec_dbg(codec, "%s: QUIRK_AE5 applied.\n", __func__); + snd_hda_apply_pincfgs(codec, r3di_pincfgs); + break; + } + switch (spec->quirk) { + case QUIRK_ALIENWARE: spec->num_outputs = 2; spec->out_pins[0] = 0x0b; /* speaker out */ spec->out_pins[1] = 0x0f; @@ -7454,15 +8513,6 @@ static void ca0132_config(struct hda_codec *codec) break; case QUIRK_SBZ: case QUIRK_R3D: - if (spec->quirk == QUIRK_SBZ) { - codec_dbg(codec, "%s: QUIRK_SBZ applied.\n", __func__); - snd_hda_apply_pincfgs(codec, sbz_pincfgs); - } - if (spec->quirk == QUIRK_R3D) { - codec_dbg(codec, "%s: QUIRK_R3D applied.\n", __func__); - snd_hda_apply_pincfgs(codec, r3d_pincfgs); - } - spec->num_outputs = 2; spec->out_pins[0] = 0x0B; /* Line out */ spec->out_pins[1] = 0x0F; /* Rear headphone out */ @@ -7487,10 +8537,62 @@ static void ca0132_config(struct hda_codec *codec) spec->multiout.dig_out_nid = spec->dig_out; spec->dig_in = 0x09; break; - case QUIRK_R3DI: - codec_dbg(codec, "%s: QUIRK_R3DI applied.\n", __func__); - snd_hda_apply_pincfgs(codec, r3di_pincfgs); + case QUIRK_ZXR: + spec->num_outputs = 2; + spec->out_pins[0] = 0x0B; /* Line out */ + spec->out_pins[1] = 0x0F; /* Rear headphone out */ + spec->out_pins[2] = 0x10; /* Center/LFE */ + spec->out_pins[3] = 0x11; /* Rear surround */ + spec->shared_out_nid = 0x2; + spec->unsol_tag_hp = spec->out_pins[1]; + spec->unsol_tag_front_hp = spec->out_pins[2]; + spec->adcs[0] = 0x7; /* Rear Mic / Line-in */ + spec->adcs[1] = 0x8; /* Not connected, no front mic */ + spec->adcs[2] = 0xa; /* what u hear */ + + spec->num_inputs = 2; + spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */ + spec->input_pins[1] = 0x13; /* What U Hear */ + spec->shared_mic_nid = 0x7; + spec->unsol_tag_amic1 = spec->input_pins[0]; + break; + case QUIRK_ZXR_DBPRO: + spec->adcs[0] = 0x8; /* ZxR DBPro Aux In */ + + spec->num_inputs = 1; + spec->input_pins[0] = 0x11; /* RCA Line-in */ + + spec->dig_out = 0x05; + spec->multiout.dig_out_nid = spec->dig_out; + + spec->dig_in = 0x09; + break; + case QUIRK_AE5: + spec->num_outputs = 2; + spec->out_pins[0] = 0x0B; /* Line out */ + spec->out_pins[1] = 0x11; /* Rear headphone out */ + spec->out_pins[2] = 0x10; /* Front Headphone / Center/LFE*/ + spec->out_pins[3] = 0x0F; /* Rear surround */ + spec->shared_out_nid = 0x2; + spec->unsol_tag_hp = spec->out_pins[1]; + spec->unsol_tag_front_hp = spec->out_pins[2]; + + spec->adcs[0] = 0x7; /* Rear Mic / Line-in */ + spec->adcs[1] = 0x8; /* Front Mic, but only if no DSP */ + spec->adcs[2] = 0xa; /* what u hear */ + + spec->num_inputs = 2; + spec->input_pins[0] = 0x12; /* Rear Mic / Line-in */ + spec->input_pins[1] = 0x13; /* What U Hear */ + spec->shared_mic_nid = 0x7; + spec->unsol_tag_amic1 = spec->input_pins[0]; + + /* SPDIF I/O */ + spec->dig_out = 0x05; + spec->multiout.dig_out_nid = spec->dig_out; + break; + case QUIRK_R3DI: spec->num_outputs = 2; spec->out_pins[0] = 0x0B; /* Line out */ spec->out_pins[1] = 0x0F; /* Rear headphone out */ @@ -7547,7 +8649,11 @@ static int ca0132_prepare_verbs(struct hda_codec *codec) struct ca0132_spec *spec = codec->spec; spec->chip_init_verbs = ca0132_init_verbs0; - if (spec->quirk == QUIRK_SBZ || spec->quirk == QUIRK_R3D) + /* + * Since desktop cards use pci_mmio, this can be used to determine + * whether or not to use these verbs instead of a separate bool. + */ + if (spec->use_pci_mmio) spec->desktop_init_verbs = ca0132_init_verbs1; spec->spec_init_verbs = kcalloc(NUM_SPEC_VERBS, sizeof(struct hda_verb), @@ -7579,6 +8685,29 @@ static int ca0132_prepare_verbs(struct hda_codec *codec) return 0; } +/* + * The Sound Blaster ZxR shares the same PCI subsystem ID as some regular + * Sound Blaster Z cards. However, they have different HDA codec subsystem + * ID's. So, we check for the ZxR's subsystem ID, as well as the DBPro + * daughter boards ID. + */ +static void sbz_detect_quirk(struct hda_codec *codec) +{ + struct ca0132_spec *spec = codec->spec; + + switch (codec->core.subsystem_id) { + case 0x11020033: + spec->quirk = QUIRK_ZXR; + break; + case 0x1102003f: + spec->quirk = QUIRK_ZXR_DBPRO; + break; + default: + spec->quirk = QUIRK_SBZ; + break; + } +} + static int patch_ca0132(struct hda_codec *codec) { struct ca0132_spec *spec; @@ -7593,10 +8722,6 @@ static int patch_ca0132(struct hda_codec *codec) codec->spec = spec; spec->codec = codec; - codec->patch_ops = ca0132_patch_ops; - codec->pcm_format_first = 1; - codec->no_sticky_stream = 1; - /* Detect codec quirk */ quirk = snd_pci_quirk_lookup(codec->bus->pci, ca0132_quirks); if (quirk) @@ -7604,6 +8729,18 @@ static int patch_ca0132(struct hda_codec *codec) else spec->quirk = QUIRK_NONE; + if (spec->quirk == QUIRK_SBZ) + sbz_detect_quirk(codec); + + if (spec->quirk == QUIRK_ZXR_DBPRO) + codec->patch_ops = dbpro_patch_ops; + else + codec->patch_ops = ca0132_patch_ops; + + codec->pcm_format_first = 1; + codec->no_sticky_stream = 1; + + spec->dsp_state = DSP_DOWNLOAD_INIT; spec->num_mixers = 1; @@ -7613,6 +8750,12 @@ static int patch_ca0132(struct hda_codec *codec) spec->mixers[0] = desktop_mixer; snd_hda_codec_set_name(codec, "Sound Blaster Z"); break; + case QUIRK_ZXR: + spec->mixers[0] = desktop_mixer; + snd_hda_codec_set_name(codec, "Sound Blaster ZxR"); + break; + case QUIRK_ZXR_DBPRO: + break; case QUIRK_R3D: spec->mixers[0] = desktop_mixer; snd_hda_codec_set_name(codec, "Recon3D"); @@ -7621,6 +8764,10 @@ static int patch_ca0132(struct hda_codec *codec) spec->mixers[0] = r3di_mixer; snd_hda_codec_set_name(codec, "Recon3Di"); break; + case QUIRK_AE5: + spec->mixers[0] = desktop_mixer; + snd_hda_codec_set_name(codec, "Sound BlasterX AE-5"); + break; default: spec->mixers[0] = ca0132_mixer; break; @@ -7630,6 +8777,8 @@ static int patch_ca0132(struct hda_codec *codec) switch (spec->quirk) { case QUIRK_SBZ: case QUIRK_R3D: + case QUIRK_AE5: + case QUIRK_ZXR: spec->use_alt_controls = true; spec->use_alt_functions = true; spec->use_pci_mmio = true; diff --git a/sound/pci/hda/patch_cirrus.c b/sound/pci/hda/patch_cirrus.c @@ -23,7 +23,7 @@ #include <linux/module.h> #include <sound/core.h> #include <sound/tlv.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" diff --git a/sound/pci/hda/patch_cmedia.c b/sound/pci/hda/patch_cmedia.c @@ -25,7 +25,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <sound/core.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" diff --git a/sound/pci/hda/patch_conexant.c b/sound/pci/hda/patch_conexant.c @@ -27,7 +27,7 @@ #include <sound/core.h> #include <sound/jack.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_beep.h" @@ -943,6 +943,7 @@ static const struct snd_pci_quirk cxt5066_fixups[] = { SND_PCI_QUIRK(0x17aa, 0x21da, "Lenovo X220", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x21db, "Lenovo X220-tablet", CXT_PINCFG_LENOVO_TP410), SND_PCI_QUIRK(0x17aa, 0x38af, "Lenovo IdeaPad Z560", CXT_FIXUP_MUTE_LED_EAPD), + SND_PCI_QUIRK(0x17aa, 0x3905, "Lenovo G50-30", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x390b, "Lenovo G50-80", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x3975, "Lenovo U300s", CXT_FIXUP_STEREO_DMIC), SND_PCI_QUIRK(0x17aa, 0x3977, "Lenovo IdeaPad U310", CXT_FIXUP_STEREO_DMIC), diff --git a/sound/pci/hda/patch_hdmi.c b/sound/pci/hda/patch_hdmi.c @@ -41,7 +41,7 @@ #include <sound/hdaudio.h> #include <sound/hda_i915.h> #include <sound/hda_chmap.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_jack.h" diff --git a/sound/pci/hda/patch_realtek.c b/sound/pci/hda/patch_realtek.c @@ -32,7 +32,7 @@ #include <linux/input.h> #include <sound/core.h> #include <sound/jack.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" @@ -6843,6 +6843,12 @@ static const struct snd_hda_pin_quirk alc269_pin_fixup_tbl[] = { {0x21, 0x0221101f}), SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION, {0x14, 0x90170110}, + {0x19, 0x02a11030}, + {0x1a, 0x02a11040}, + {0x1b, 0x01011020}, + {0x21, 0x0221101f}), + SND_HDA_PIN_QUIRK(0x10ec0235, 0x17aa, "Lenovo", ALC294_FIXUP_LENOVO_MIC_LOCATION, + {0x14, 0x90170110}, {0x19, 0x02a11020}, {0x1a, 0x02a11030}, {0x21, 0x0221101f}), @@ -7738,6 +7744,8 @@ enum { ALC662_FIXUP_ASUS_Nx50, ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE, ALC668_FIXUP_ASUS_Nx51, + ALC668_FIXUP_MIC_COEF, + ALC668_FIXUP_ASUS_G751, ALC891_FIXUP_HEADSET_MODE, ALC891_FIXUP_DELL_MIC_NO_PRESENCE, ALC662_FIXUP_ACER_VERITON, @@ -8007,6 +8015,23 @@ static const struct hda_fixup alc662_fixups[] = { .chained = true, .chain_id = ALC668_FIXUP_ASUS_Nx51_HEADSET_MODE, }, + [ALC668_FIXUP_MIC_COEF] = { + .type = HDA_FIXUP_VERBS, + .v.verbs = (const struct hda_verb[]) { + { 0x20, AC_VERB_SET_COEF_INDEX, 0xc3 }, + { 0x20, AC_VERB_SET_PROC_COEF, 0x4000 }, + {} + }, + }, + [ALC668_FIXUP_ASUS_G751] = { + .type = HDA_FIXUP_PINS, + .v.pins = (const struct hda_pintbl[]) { + { 0x16, 0x0421101f }, /* HP */ + {} + }, + .chained = true, + .chain_id = ALC668_FIXUP_MIC_COEF + }, [ALC891_FIXUP_HEADSET_MODE] = { .type = HDA_FIXUP_FUNC, .v.func = alc_fixup_headset_mode, @@ -8080,6 +8105,7 @@ static const struct snd_pci_quirk alc662_fixup_tbl[] = { SND_PCI_QUIRK(0x1043, 0x11cd, "Asus N550", ALC662_FIXUP_ASUS_Nx50), SND_PCI_QUIRK(0x1043, 0x13df, "Asus N550JX", ALC662_FIXUP_BASS_1A), SND_PCI_QUIRK(0x1043, 0x129d, "Asus N750", ALC662_FIXUP_ASUS_Nx50), + SND_PCI_QUIRK(0x1043, 0x12ff, "ASUS G751", ALC668_FIXUP_ASUS_G751), SND_PCI_QUIRK(0x1043, 0x1477, "ASUS N56VZ", ALC662_FIXUP_BASS_MODE4_CHMAP), SND_PCI_QUIRK(0x1043, 0x15a7, "ASUS UX51VZH", ALC662_FIXUP_BASS_16), SND_PCI_QUIRK(0x1043, 0x177d, "ASUS N551", ALC668_FIXUP_ASUS_Nx51), @@ -8184,6 +8210,7 @@ static const struct hda_model_fixup alc662_fixup_models[] = { {.id = ALC668_FIXUP_DELL_XPS13, .name = "dell-xps13"}, {.id = ALC662_FIXUP_ASUS_Nx50, .name = "asus-nx50"}, {.id = ALC668_FIXUP_ASUS_Nx51, .name = "asus-nx51"}, + {.id = ALC668_FIXUP_ASUS_G751, .name = "asus-g751"}, {.id = ALC891_FIXUP_HEADSET_MODE, .name = "alc891-headset"}, {.id = ALC891_FIXUP_DELL_MIC_NO_PRESENCE, .name = "alc891-headset-multi"}, {.id = ALC662_FIXUP_ACER_VERITON, .name = "acer-veriton"}, diff --git a/sound/pci/hda/patch_si3054.c b/sound/pci/hda/patch_si3054.c @@ -27,7 +27,7 @@ #include <linux/slab.h> #include <linux/module.h> #include <sound/core.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" /* si3054 verbs */ diff --git a/sound/pci/hda/patch_sigmatel.c b/sound/pci/hda/patch_sigmatel.c @@ -32,7 +32,7 @@ #include <linux/module.h> #include <sound/core.h> #include <sound/jack.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_beep.h" @@ -77,6 +77,7 @@ enum { STAC_DELL_M6_BOTH, STAC_DELL_EQ, STAC_ALIENWARE_M17X, + STAC_ELO_VUPOINT_15MX, STAC_92HD89XX_HP_FRONT_JACK, STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK, STAC_92HD73XX_ASUS_MOBO, @@ -1879,6 +1880,18 @@ static void stac92hd73xx_fixup_no_jd(struct hda_codec *codec, codec->no_jack_detect = 1; } + +static void stac92hd73xx_disable_automute(struct hda_codec *codec, + const struct hda_fixup *fix, int action) +{ + struct sigmatel_spec *spec = codec->spec; + + if (action != HDA_FIXUP_ACT_PRE_PROBE) + return; + + spec->gen.suppress_auto_mute = 1; +} + static const struct hda_fixup stac92hd73xx_fixups[] = { [STAC_92HD73XX_REF] = { .type = HDA_FIXUP_FUNC, @@ -1904,6 +1917,10 @@ static const struct hda_fixup stac92hd73xx_fixups[] = { .type = HDA_FIXUP_FUNC, .v.func = stac92hd73xx_fixup_alienware_m17x, }, + [STAC_ELO_VUPOINT_15MX] = { + .type = HDA_FIXUP_FUNC, + .v.func = stac92hd73xx_disable_automute, + }, [STAC_92HD73XX_INTEL] = { .type = HDA_FIXUP_PINS, .v.pins = intel_dg45id_pin_configs, @@ -1942,6 +1959,7 @@ static const struct hda_model_fixup stac92hd73xx_models[] = { { .id = STAC_DELL_M6_BOTH, .name = "dell-m6" }, { .id = STAC_DELL_EQ, .name = "dell-eq" }, { .id = STAC_ALIENWARE_M17X, .name = "alienware" }, + { .id = STAC_ELO_VUPOINT_15MX, .name = "elo-vupoint-15mx" }, { .id = STAC_92HD73XX_ASUS_MOBO, .name = "asus-mobo" }, {} }; @@ -1991,6 +2009,8 @@ static const struct snd_pci_quirk stac92hd73xx_fixup_tbl[] = { "Alienware M17x", STAC_ALIENWARE_M17X), SND_PCI_QUIRK(PCI_VENDOR_ID_DELL, 0x0490, "Alienware M17x R3", STAC_DELL_EQ), + SND_PCI_QUIRK(0x1059, 0x1011, + "ELO VuPoint 15MX", STAC_ELO_VUPOINT_15MX), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x1927, "HP Z1 G2", STAC_92HD89XX_HP_Z1_G2_RIGHT_MIC_JACK), SND_PCI_QUIRK(PCI_VENDOR_ID_HP, 0x2b17, diff --git a/sound/pci/hda/patch_via.c b/sound/pci/hda/patch_via.c @@ -52,7 +52,7 @@ #include <linux/module.h> #include <sound/core.h> #include <sound/asoundef.h> -#include "hda_codec.h" +#include <sound/hda_codec.h> #include "hda_local.h" #include "hda_auto_parser.h" #include "hda_jack.h" diff --git a/sound/pci/intel8x0.c b/sound/pci/intel8x0.c @@ -38,11 +38,6 @@ #include <sound/ac97_codec.h> #include <sound/info.h> #include <sound/initval.h> -/* for 440MX workaround */ -#include <asm/pgtable.h> -#ifdef CONFIG_X86 -#include <asm/set_memory.h> -#endif MODULE_AUTHOR("Jaroslav Kysela <perex@perex.cz>"); MODULE_DESCRIPTION("Intel 82801AA,82901AB,i810,i820,i830,i840,i845,MX440; SiS 7012; Ali 5455"); @@ -374,7 +369,6 @@ struct ichdev { unsigned int ali_slot; /* ALI DMA slot */ struct ac97_pcm *pcm; int pcm_open_flag; - unsigned int page_attr_changed: 1; unsigned int suspended: 1; }; @@ -724,25 +718,6 @@ static void snd_intel8x0_setup_periods(struct intel8x0 *chip, struct ichdev *ich iputbyte(chip, port + ichdev->roff_sr, ICH_FIFOE | ICH_BCIS | ICH_LVBCI); } -#ifdef __i386__ -/* - * Intel 82443MX running a 100MHz processor system bus has a hardware bug, - * which aborts PCI busmaster for audio transfer. A workaround is to set - * the pages as non-cached. For details, see the errata in - * http://download.intel.com/design/chipsets/specupdt/24505108.pdf - */ -static void fill_nocache(void *buf, int size, int nocache) -{ - size = (size + PAGE_SIZE - 1) >> PAGE_SHIFT; - if (nocache) - set_pages_uc(virt_to_page(buf), size); - else - set_pages_wb(virt_to_page(buf), size); -} -#else -#define fill_nocache(buf, size, nocache) do { ; } while (0) -#endif - /* * Interrupt handler */ @@ -850,7 +825,7 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: ichdev->suspended = 0; - /* fallthru */ + /* fall through */ case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: val = ICH_IOCE | ICH_STARTBM; @@ -858,7 +833,7 @@ static int snd_intel8x0_pcm_trigger(struct snd_pcm_substream *substream, int cmd break; case SNDRV_PCM_TRIGGER_SUSPEND: ichdev->suspended = 1; - /* fallthru */ + /* fall through */ case SNDRV_PCM_TRIGGER_STOP: val = 0; break; @@ -892,7 +867,7 @@ static int snd_intel8x0_ali_trigger(struct snd_pcm_substream *substream, int cmd switch (cmd) { case SNDRV_PCM_TRIGGER_RESUME: ichdev->suspended = 0; - /* fallthru */ + /* fall through */ case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { @@ -909,7 +884,7 @@ static int snd_intel8x0_ali_trigger(struct snd_pcm_substream *substream, int cmd break; case SNDRV_PCM_TRIGGER_SUSPEND: ichdev->suspended = 1; - /* fallthru */ + /* fall through */ case SNDRV_PCM_TRIGGER_STOP: case SNDRV_PCM_TRIGGER_PAUSE_PUSH: /* pause */ @@ -938,23 +913,12 @@ static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream, { struct intel8x0 *chip = snd_pcm_substream_chip(substream); struct ichdev *ichdev = get_ichdev(substream); - struct snd_pcm_runtime *runtime = substream->runtime; int dbl = params_rate(hw_params) > 48000; int err; - if (chip->fix_nocache && ichdev->page_attr_changed) { - fill_nocache(runtime->dma_area, runtime->dma_bytes, 0); /* clear */ - ichdev->page_attr_changed = 0; - } err = snd_pcm_lib_malloc_pages(substream, params_buffer_bytes(hw_params)); if (err < 0) return err; - if (chip->fix_nocache) { - if (runtime->dma_area && ! ichdev->page_attr_changed) { - fill_nocache(runtime->dma_area, runtime->dma_bytes, 1); - ichdev->page_attr_changed = 1; - } - } if (ichdev->pcm_open_flag) { snd_ac97_pcm_close(ichdev->pcm); ichdev->pcm_open_flag = 0; @@ -974,17 +938,12 @@ static int snd_intel8x0_hw_params(struct snd_pcm_substream *substream, static int snd_intel8x0_hw_free(struct snd_pcm_substream *substream) { - struct intel8x0 *chip = snd_pcm_substream_chip(substream); struct ichdev *ichdev = get_ichdev(substream); if (ichdev->pcm_open_flag) { snd_ac97_pcm_close(ichdev->pcm); ichdev->pcm_open_flag = 0; } - if (chip->fix_nocache && ichdev->page_attr_changed) { - fill_nocache(substream->runtime->dma_area, substream->runtime->dma_bytes, 0); - ichdev->page_attr_changed = 0; - } return snd_pcm_lib_free_pages(substream); } @@ -1510,6 +1469,9 @@ struct ich_pcm_table { int ac97_idx; }; +#define intel8x0_dma_type(chip) \ + ((chip)->fix_nocache ? SNDRV_DMA_TYPE_DEV_UC : SNDRV_DMA_TYPE_DEV) + static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device, struct ich_pcm_table *rec) { @@ -1540,7 +1502,7 @@ static int snd_intel8x0_pcm1(struct intel8x0 *chip, int device, strcpy(pcm->name, chip->card->shortname); chip->pcm[device] = pcm; - snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV, + snd_pcm_lib_preallocate_pages_for_all(pcm, intel8x0_dma_type(chip), snd_dma_pci_data(chip->pci), rec->prealloc_size, rec->prealloc_max_size); @@ -2629,11 +2591,8 @@ static int snd_intel8x0_free(struct intel8x0 *chip) __hw_end: if (chip->irq >= 0) free_irq(chip->irq, chip); - if (chip->bdbars.area) { - if (chip->fix_nocache) - fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 0); + if (chip->bdbars.area) snd_dma_free_pages(&chip->bdbars); - } if (chip->addr) pci_iounmap(chip->pci, chip->addr); if (chip->bmaddr) @@ -2657,17 +2616,6 @@ static int intel8x0_suspend(struct device *dev) snd_power_change_state(card, SNDRV_CTL_POWER_D3hot); for (i = 0; i < chip->pcm_devs; i++) snd_pcm_suspend_all(chip->pcm[i]); - /* clear nocache */ - if (chip->fix_nocache) { - for (i = 0; i < chip->bdbars_count; i++) { - struct ichdev *ichdev = &chip->ichd[i]; - if (ichdev->substream && ichdev->page_attr_changed) { - struct snd_pcm_runtime *runtime = ichdev->substream->runtime; - if (runtime->dma_area) - fill_nocache(runtime->dma_area, runtime->dma_bytes, 0); - } - } - } for (i = 0; i < chip->ncodecs; i++) snd_ac97_suspend(chip->ac97[i]); if (chip->device_type == DEVICE_INTEL_ICH4) @@ -2708,25 +2656,9 @@ static int intel8x0_resume(struct device *dev) ICH_PCM_SPDIF_1011); } - /* refill nocache */ - if (chip->fix_nocache) - fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 1); - for (i = 0; i < chip->ncodecs; i++) snd_ac97_resume(chip->ac97[i]); - /* refill nocache */ - if (chip->fix_nocache) { - for (i = 0; i < chip->bdbars_count; i++) { - struct ichdev *ichdev = &chip->ichd[i]; - if (ichdev->substream && ichdev->page_attr_changed) { - struct snd_pcm_runtime *runtime = ichdev->substream->runtime; - if (runtime->dma_area) - fill_nocache(runtime->dma_area, runtime->dma_bytes, 1); - } - } - } - /* resume status */ for (i = 0; i < chip->bdbars_count; i++) { struct ichdev *ichdev = &chip->ichd[i]; @@ -3057,6 +2989,12 @@ static int snd_intel8x0_create(struct snd_card *card, chip->inside_vm = snd_intel8x0_inside_vm(pci); + /* + * Intel 82443MX running a 100MHz processor system bus has a hardware + * bug, which aborts PCI busmaster for audio transfer. A workaround + * is to set the pages as non-cached. For details, see the errata in + * http://download.intel.com/design/chipsets/specupdt/24505108.pdf + */ if (pci->vendor == PCI_VENDOR_ID_INTEL && pci->device == PCI_DEVICE_ID_INTEL_440MX) chip->fix_nocache = 1; /* enable workaround */ @@ -3128,7 +3066,7 @@ static int snd_intel8x0_create(struct snd_card *card, /* allocate buffer descriptor lists */ /* the start of each lists must be aligned to 8 bytes */ - if (snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV, snd_dma_pci_data(pci), + if (snd_dma_alloc_pages(intel8x0_dma_type(chip), snd_dma_pci_data(pci), chip->bdbars_count * sizeof(u32) * ICH_MAX_FRAGS * 2, &chip->bdbars) < 0) { snd_intel8x0_free(chip); @@ -3137,9 +3075,6 @@ static int snd_intel8x0_create(struct snd_card *card, } /* tables must be aligned to 8 bytes here, but the kernel pages are much bigger, so we don't care (on i386) */ - /* workaround for 440MX */ - if (chip->fix_nocache) - fill_nocache(chip->bdbars.area, chip->bdbars.bytes, 1); int_sta_masks = 0; for (i = 0; i < chip->bdbars_count; i++) { ichdev = &chip->ichd[i]; diff --git a/sound/pci/intel8x0m.c b/sound/pci/intel8x0m.c @@ -1171,16 +1171,6 @@ static int snd_intel8x0m_create(struct snd_card *card, } port_inited: - if (request_irq(pci->irq, snd_intel8x0m_interrupt, IRQF_SHARED, - KBUILD_MODNAME, chip)) { - dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); - snd_intel8x0m_free(chip); - return -EBUSY; - } - chip->irq = pci->irq; - pci_set_master(pci); - synchronize_irq(chip->irq); - /* initialize offsets */ chip->bdbars_count = 2; tbl = intel_regs; @@ -1224,11 +1214,21 @@ static int snd_intel8x0m_create(struct snd_card *card, chip->int_sta_reg = ICH_REG_GLOB_STA; chip->int_sta_mask = int_sta_masks; + pci_set_master(pci); + if ((err = snd_intel8x0m_chip_init(chip, 1)) < 0) { snd_intel8x0m_free(chip); return err; } + if (request_irq(pci->irq, snd_intel8x0m_interrupt, IRQF_SHARED, + KBUILD_MODNAME, chip)) { + dev_err(card->dev, "unable to grab IRQ %d\n", pci->irq); + snd_intel8x0m_free(chip); + return -EBUSY; + } + chip->irq = pci->irq; + if ((err = snd_device_new(card, SNDRV_DEV_LOWLEVEL, chip, &ops)) < 0) { snd_intel8x0m_free(chip); return err; diff --git a/sound/pci/rme32.c b/sound/pci/rme32.c @@ -319,7 +319,8 @@ static const struct snd_pcm_hardware snd_rme32_spdif_info = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_SYNC_START), + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_SYNC_APPLPTR), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE), .rates = (SNDRV_PCM_RATE_32000 | @@ -346,7 +347,8 @@ static const struct snd_pcm_hardware snd_rme32_adat_info = SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_SYNC_START), + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_SYNC_APPLPTR), .formats= SNDRV_PCM_FMTBIT_S16_LE, .rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000), @@ -370,7 +372,8 @@ static const struct snd_pcm_hardware snd_rme32_spdif_fd_info = { SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_SYNC_START), + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_SYNC_APPLPTR), .formats = (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE), .rates = (SNDRV_PCM_RATE_32000 | @@ -397,7 +400,8 @@ static const struct snd_pcm_hardware snd_rme32_adat_fd_info = SNDRV_PCM_INFO_MMAP_VALID | SNDRV_PCM_INFO_INTERLEAVED | SNDRV_PCM_INFO_PAUSE | - SNDRV_PCM_INFO_SYNC_START), + SNDRV_PCM_INFO_SYNC_START | + SNDRV_PCM_INFO_SYNC_APPLPTR), .formats= SNDRV_PCM_FMTBIT_S16_LE, .rates = (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000), @@ -1104,16 +1108,6 @@ snd_rme32_pcm_trigger(struct snd_pcm_substream *substream, int cmd) snd_pcm_trigger_done(s, substream); } - /* prefill playback buffer */ - if (cmd == SNDRV_PCM_TRIGGER_START && rme32->fullduplex_mode) { - snd_pcm_group_for_each_entry(s, substream) { - if (s == rme32->playback_substream) { - s->ops->ack(s); - break; - } - } - } - switch (cmd) { case SNDRV_PCM_TRIGGER_START: if (rme32->running && ! RME32_ISWORKING(rme32)) diff --git a/sound/pci/rme9652/hdspm.c b/sound/pci/rme9652/hdspm.c @@ -6534,7 +6534,7 @@ static int snd_hdspm_create_alsa_devices(struct snd_card *card, dev_dbg(card->dev, "Update mixer controls...\n"); hdspm_update_simple_mixer_controls(hdspm); - dev_dbg(card->dev, "Initializeing complete ???\n"); + dev_dbg(card->dev, "Initializing complete?\n"); err = snd_card_register(card); if (err < 0) { diff --git a/sound/soc/amd/acp-da7219-max98357a.c b/sound/soc/amd/acp-da7219-max98357a.c @@ -42,7 +42,7 @@ #include "../codecs/da7219.h" #include "../codecs/da7219-aad.h" -#define CZ_PLAT_CLK 25000000 +#define CZ_PLAT_CLK 48000000 #define DUAL_CHANNEL 2 static struct snd_soc_jack cz_jack; @@ -75,7 +75,7 @@ static int cz_da7219_init(struct snd_soc_pcm_runtime *rtd) da7219_dai_clk = clk_get(component->dev, "da7219-dai-clks"); ret = snd_soc_card_jack_new(card, "Headset Jack", - SND_JACK_HEADPHONE | SND_JACK_MICROPHONE | + SND_JACK_HEADSET | SND_JACK_LINEOUT | SND_JACK_BTN_0 | SND_JACK_BTN_1 | SND_JACK_BTN_2 | SND_JACK_BTN_3, &cz_jack, NULL, 0); @@ -133,7 +133,7 @@ static const struct snd_pcm_hw_constraint_list constraints_channels = { .mask = 0, }; -static int cz_da7219_startup(struct snd_pcm_substream *substream) +static int cz_da7219_play_startup(struct snd_pcm_substream *substream) { struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; @@ -150,7 +150,28 @@ static int cz_da7219_startup(struct snd_pcm_substream *substream) snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); - machine->i2s_instance = I2S_SP_INSTANCE; + machine->play_i2s_instance = I2S_SP_INSTANCE; + return da7219_clk_enable(substream); +} + +static int cz_da7219_cap_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_card *card = rtd->card; + struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); + + /* + * On this platform for PCM device we support stereo + */ + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + + machine->cap_i2s_instance = I2S_SP_INSTANCE; machine->capture_channel = CAP_CHANNEL1; return da7219_clk_enable(substream); } @@ -162,11 +183,22 @@ static void cz_da7219_shutdown(struct snd_pcm_substream *substream) static int cz_max_startup(struct snd_pcm_substream *substream) { + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); - machine->i2s_instance = I2S_BT_INSTANCE; + /* + * On this platform for PCM device we support stereo + */ + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + + machine->play_i2s_instance = I2S_BT_INSTANCE; return da7219_clk_enable(substream); } @@ -177,21 +209,43 @@ static void cz_max_shutdown(struct snd_pcm_substream *substream) static int cz_dmic0_startup(struct snd_pcm_substream *substream) { + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); - machine->i2s_instance = I2S_BT_INSTANCE; + /* + * On this platform for PCM device we support stereo + */ + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + + machine->cap_i2s_instance = I2S_BT_INSTANCE; return da7219_clk_enable(substream); } static int cz_dmic1_startup(struct snd_pcm_substream *substream) { + struct snd_pcm_runtime *runtime = substream->runtime; struct snd_soc_pcm_runtime *rtd = substream->private_data; struct snd_soc_card *card = rtd->card; struct acp_platform_info *machine = snd_soc_card_get_drvdata(card); - machine->i2s_instance = I2S_SP_INSTANCE; + /* + * On this platform for PCM device we support stereo + */ + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_RATE, + &constraints_rates); + + machine->cap_i2s_instance = I2S_SP_INSTANCE; machine->capture_channel = CAP_CHANNEL0; return da7219_clk_enable(substream); } @@ -201,8 +255,13 @@ static void cz_dmic_shutdown(struct snd_pcm_substream *substream) da7219_clk_disable(); } +static const struct snd_soc_ops cz_da7219_play_ops = { + .startup = cz_da7219_play_startup, + .shutdown = cz_da7219_shutdown, +}; + static const struct snd_soc_ops cz_da7219_cap_ops = { - .startup = cz_da7219_startup, + .startup = cz_da7219_cap_startup, .shutdown = cz_da7219_shutdown, }; @@ -233,7 +292,7 @@ static struct snd_soc_dai_link cz_dai_7219_98357[] = { | SND_SOC_DAIFMT_CBM_CFM, .init = cz_da7219_init, .dpcm_playback = 1, - .ops = &cz_da7219_cap_ops, + .ops = &cz_da7219_play_ops, }, { .name = "amd-da7219-cap", diff --git a/sound/soc/amd/acp-pcm-dma.c b/sound/soc/amd/acp-pcm-dma.c @@ -867,8 +867,12 @@ static int acp_dma_hw_params(struct snd_pcm_substream *substream, return -EINVAL; if (pinfo) { - rtd->i2s_instance = pinfo->i2s_instance; - rtd->capture_channel = pinfo->capture_channel; + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) { + rtd->i2s_instance = pinfo->play_i2s_instance; + } else { + rtd->i2s_instance = pinfo->cap_i2s_instance; + rtd->capture_channel = pinfo->capture_channel; + } } if (adata->asic_type == CHIP_STONEY) { val = acp_reg_read(adata->acp_mmio, @@ -1036,16 +1040,22 @@ static snd_pcm_uframes_t acp_dma_pointer(struct snd_pcm_substream *substream) if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) { period_bytes = frames_to_bytes(runtime, runtime->period_size); - dscr = acp_reg_read(rtd->acp_mmio, rtd->dma_curr_dscr); - if (dscr == rtd->dma_dscr_idx_1) - pos = period_bytes; - else - pos = 0; bytescount = acp_get_byte_count(rtd); - if (bytescount > rtd->bytescount) + if (bytescount >= rtd->bytescount) bytescount -= rtd->bytescount; - delay = do_div(bytescount, period_bytes); - runtime->delay = bytes_to_frames(runtime, delay); + if (bytescount < period_bytes) { + pos = 0; + } else { + dscr = acp_reg_read(rtd->acp_mmio, rtd->dma_curr_dscr); + if (dscr == rtd->dma_dscr_idx_1) + pos = period_bytes; + else + pos = 0; + } + if (bytescount > 0) { + delay = do_div(bytescount, period_bytes); + runtime->delay = bytes_to_frames(runtime, delay); + } } else { buffersize = frames_to_bytes(runtime, runtime->buffer_size); bytescount = acp_get_byte_count(rtd); diff --git a/sound/soc/amd/acp.h b/sound/soc/amd/acp.h @@ -158,7 +158,8 @@ struct audio_drv_data { * and dma driver */ struct acp_platform_info { - u16 i2s_instance; + u16 play_i2s_instance; + u16 cap_i2s_instance; u16 capture_channel; }; diff --git a/sound/soc/atmel/Kconfig b/sound/soc/atmel/Kconfig @@ -97,4 +97,16 @@ config SND_ATMEL_SOC_I2S help Say Y or M if you want to add support for Atmel ASoc driver for boards using I2S. + +config SND_SOC_MIKROE_PROTO + tristate "Support for Mikroe-PROTO board" + depends on OF + depends on SND_SOC_I2C_AND_SPI + select SND_SOC_WM8731 + help + Say Y or M if you want to add support for MikroElektronika PROTO Audio + Board. This board contains the WM8731 codec, which can be configured + using I2C over SDA (MPU Data Input) and SCL (MPU Clock Input) pins. + Both playback and capture are supported. + endif diff --git a/sound/soc/atmel/Makefile b/sound/soc/atmel/Makefile @@ -17,6 +17,7 @@ snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o snd-atmel-soc-classd-objs := atmel-classd.o snd-atmel-soc-pdmic-objs := atmel-pdmic.o snd-atmel-soc-tse850-pcm5142-objs := tse850-pcm5142.o +snd-soc-mikroe-proto-objs := mikroe-proto.o obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o @@ -24,3 +25,4 @@ obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o obj-$(CONFIG_SND_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o obj-$(CONFIG_SND_ATMEL_SOC_PDMIC) += snd-atmel-soc-pdmic.o obj-$(CONFIG_SND_ATMEL_SOC_TSE850_PCM5142) += snd-atmel-soc-tse850-pcm5142.o +obj-$(CONFIG_SND_SOC_MIKROE_PROTO) += snd-soc-mikroe-proto.o diff --git a/sound/soc/atmel/atmel_ssc_dai.c b/sound/soc/atmel/atmel_ssc_dai.c @@ -1005,11 +1005,11 @@ static int asoc_ssc_init(struct device *dev) struct ssc_device *ssc = dev_get_drvdata(dev); int ret; - ret = snd_soc_register_component(dev, &atmel_ssc_component, + ret = devm_snd_soc_register_component(dev, &atmel_ssc_component, &atmel_ssc_dai, 1); if (ret) { dev_err(dev, "Could not register DAI: %d\n", ret); - goto err; + return ret; } if (ssc->pdata->use_dma) @@ -1019,15 +1019,10 @@ static int asoc_ssc_init(struct device *dev) if (ret) { dev_err(dev, "Could not register PCM: %d\n", ret); - goto err_unregister_dai; + return ret; } return 0; - -err_unregister_dai: - snd_soc_unregister_component(dev); -err: - return ret; } static void asoc_ssc_exit(struct device *dev) @@ -1038,8 +1033,6 @@ static void asoc_ssc_exit(struct device *dev) atmel_pcm_dma_platform_unregister(dev); else atmel_pcm_pdc_platform_unregister(dev); - - snd_soc_unregister_component(dev); } /** diff --git a/sound/soc/atmel/mikroe-proto.c b/sound/soc/atmel/mikroe-proto.c @@ -0,0 +1,165 @@ +/* + * ASoC driver for PROTO AudioCODEC (with a WM8731) + * + * Author: Florian Meier, <koalo@koalo.de> + * Copyright 2013 + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/platform_device.h> + +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/soc.h> +#include <sound/jack.h> + +#include "../codecs/wm8731.h" + +#define XTAL_RATE 12288000 /* This is fixed on this board */ + +static int snd_proto_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_card *card = rtd->card; + struct snd_soc_dai *codec_dai = rtd->codec_dai; + + /* Set proto sysclk */ + int ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL, + XTAL_RATE, SND_SOC_CLOCK_IN); + if (ret < 0) { + dev_err(card->dev, "Failed to set WM8731 SYSCLK: %d\n", + ret); + return ret; + } + + return 0; +} + +static const struct snd_soc_dapm_widget snd_proto_widget[] = { + SND_SOC_DAPM_MIC("Microphone Jack", NULL), + SND_SOC_DAPM_HP("Headphone Jack", NULL), +}; + +static const struct snd_soc_dapm_route snd_proto_route[] = { + /* speaker connected to LHPOUT/RHPOUT */ + {"Headphone Jack", NULL, "LHPOUT"}, + {"Headphone Jack", NULL, "RHPOUT"}, + + /* mic is connected to Mic Jack, with WM8731 Mic Bias */ + {"MICIN", NULL, "Mic Bias"}, + {"Mic Bias", NULL, "Microphone Jack"}, +}; + +/* audio machine driver */ +static struct snd_soc_card snd_proto = { + .name = "snd_mikroe_proto", + .owner = THIS_MODULE, + .dapm_widgets = snd_proto_widget, + .num_dapm_widgets = ARRAY_SIZE(snd_proto_widget), + .dapm_routes = snd_proto_route, + .num_dapm_routes = ARRAY_SIZE(snd_proto_route), +}; + +static int snd_proto_probe(struct platform_device *pdev) +{ + struct snd_soc_dai_link *dai; + struct device_node *np = pdev->dev.of_node; + struct device_node *codec_np, *cpu_np; + struct device_node *bitclkmaster = NULL; + struct device_node *framemaster = NULL; + unsigned int dai_fmt; + int ret = 0; + + if (!np) { + dev_err(&pdev->dev, "No device node supplied\n"); + return -EINVAL; + } + + snd_proto.dev = &pdev->dev; + ret = snd_soc_of_parse_card_name(&snd_proto, "model"); + if (ret) + return ret; + + dai = devm_kzalloc(&pdev->dev, sizeof(*dai), GFP_KERNEL); + if (!dai) + return -ENOMEM; + + snd_proto.dai_link = dai; + snd_proto.num_links = 1; + + dai->name = "WM8731"; + dai->stream_name = "WM8731 HiFi"; + dai->codec_dai_name = "wm8731-hifi"; + dai->init = &snd_proto_init; + + codec_np = of_parse_phandle(np, "audio-codec", 0); + if (!codec_np) { + dev_err(&pdev->dev, "audio-codec node missing\n"); + return -EINVAL; + } + dai->codec_of_node = codec_np; + + cpu_np = of_parse_phandle(np, "i2s-controller", 0); + if (!cpu_np) { + dev_err(&pdev->dev, "i2s-controller missing\n"); + return -EINVAL; + } + dai->cpu_of_node = cpu_np; + dai->platform_of_node = cpu_np; + + dai_fmt = snd_soc_of_parse_daifmt(np, NULL, + &bitclkmaster, &framemaster); + if (bitclkmaster != framemaster) { + dev_err(&pdev->dev, "Must be the same bitclock and frame master\n"); + return -EINVAL; + } + if (bitclkmaster) { + dai_fmt &= ~SND_SOC_DAIFMT_MASTER_MASK; + if (codec_np == bitclkmaster) + dai_fmt |= SND_SOC_DAIFMT_CBM_CFM; + else + dai_fmt |= SND_SOC_DAIFMT_CBS_CFS; + } + of_node_put(bitclkmaster); + of_node_put(framemaster); + dai->dai_fmt = dai_fmt; + + of_node_put(codec_np); + of_node_put(cpu_np); + + ret = snd_soc_register_card(&snd_proto); + if (ret && ret != -EPROBE_DEFER) + dev_err(&pdev->dev, + "snd_soc_register_card() failed: %d\n", ret); + + return ret; +} + +static int snd_proto_remove(struct platform_device *pdev) +{ + return snd_soc_unregister_card(&snd_proto); +} + +static const struct of_device_id snd_proto_of_match[] = { + { .compatible = "mikroe,mikroe-proto", }, + {}, +}; +MODULE_DEVICE_TABLE(of, snd_proto_of_match); + +static struct platform_driver snd_proto_driver = { + .driver = { + .name = "snd-mikroe-proto", + .of_match_table = snd_proto_of_match, + }, + .probe = snd_proto_probe, + .remove = snd_proto_remove, +}; + +module_platform_driver(snd_proto_driver); + +MODULE_AUTHOR("Florian Meier"); +MODULE_DESCRIPTION("ASoC Driver for PROTO board (WM8731)"); +MODULE_LICENSE("GPL"); diff --git a/sound/soc/atmel/tse850-pcm5142.c b/sound/soc/atmel/tse850-pcm5142.c @@ -1,44 +1,38 @@ -/* - * TSE-850 audio - ASoC driver for the Axentia TSE-850 with a PCM5142 codec - * - * Copyright (C) 2016 Axentia Technologies AB - * - * Author: Peter Rosin <peda@axentia.se> - * - * This program is free software; you can redistribute it and/or modify - * it under the terms of the GNU General Public License version 2 as - * published by the Free Software Foundation. - */ - -/* - * loop1 relays - * IN1 +---o +------------+ o---+ OUT1 - * \ / - * + + - * | / | - * +--o +--. | - * | add | | - * | V | - * | .---. | - * DAC +----------->|Sum|---+ - * | '---' | - * | | - * + + - * - * IN2 +---o--+------------+--o---+ OUT2 - * loop2 relays - * - * The 'loop1' gpio pin controlls two relays, which are either in loop - * position, meaning that input and output are directly connected, or - * they are in mixer position, meaning that the signal is passed through - * the 'Sum' mixer. Similarly for 'loop2'. - * - * In the above, the 'loop1' relays are inactive, thus feeding IN1 to the - * mixer (if 'add' is active) and feeding the mixer output to OUT1. The - * 'loop2' relays are active, short-cutting the TSE-850 from channel 2. - * IN1, IN2, OUT1 and OUT2 are TSE-850 connectors and DAC is the PCB name - * of the (filtered) output from the PCM5142 codec. - */ +// SPDX-License-Identifier: GPL-2.0 +// +// TSE-850 audio - ASoC driver for the Axentia TSE-850 with a PCM5142 codec +// +// Copyright (C) 2016 Axentia Technologies AB +// +// Author: Peter Rosin <peda@axentia.se> +// +// loop1 relays +// IN1 +---o +------------+ o---+ OUT1 +// \ / +// + + +// | / | +// +--o +--. | +// | add | | +// | V | +// | .---. | +// DAC +----------->|Sum|---+ +// | '---' | +// | | +// + + +// +// IN2 +---o--+------------+--o---+ OUT2 +// loop2 relays +// +// The 'loop1' gpio pin controlls two relays, which are either in loop +// position, meaning that input and output are directly connected, or +// they are in mixer position, meaning that the signal is passed through +// the 'Sum' mixer. Similarly for 'loop2'. +// +// In the above, the 'loop1' relays are inactive, thus feeding IN1 to the +// mixer (if 'add' is active) and feeding the mixer output to OUT1. The +// 'loop2' relays are active, short-cutting the TSE-850 from channel 2. +// IN1, IN2, OUT1 and OUT2 are TSE-850 connectors and DAC is the PCB name +// of the (filtered) output from the PCM5142 codec. #include <linux/clk.h> #include <linux/gpio.h> @@ -452,4 +446,4 @@ module_platform_driver(tse850_driver); /* Module information */ MODULE_AUTHOR("Peter Rosin <peda@axentia.se>"); MODULE_DESCRIPTION("ALSA SoC driver for TSE-850 with PCM5142 codec"); -MODULE_LICENSE("GPL"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/bcm/cygnus-ssp.c b/sound/soc/bcm/cygnus-ssp.c @@ -1334,7 +1334,7 @@ static int cygnus_ssp_probe(struct platform_device *pdev) cygaud->active_ports = 0; dev_dbg(dev, "Registering %d DAIs\n", active_port_count); - err = snd_soc_register_component(dev, &cygnus_ssp_component, + err = devm_snd_soc_register_component(dev, &cygnus_ssp_component, cygnus_ssp_dai, active_port_count); if (err) { dev_err(dev, "snd_soc_register_dai failed\n"); @@ -1345,32 +1345,27 @@ static int cygnus_ssp_probe(struct platform_device *pdev) if (cygaud->irq_num <= 0) { dev_err(dev, "platform_get_irq failed\n"); err = cygaud->irq_num; - goto err_irq; + return err; } err = audio_clk_init(pdev, cygaud); if (err) { dev_err(dev, "audio clock initialization failed\n"); - goto err_irq; + return err; } err = cygnus_soc_platform_register(dev, cygaud); if (err) { dev_err(dev, "platform reg error %d\n", err); - goto err_irq; + return err; } return 0; - -err_irq: - snd_soc_unregister_component(dev); - return err; } static int cygnus_ssp_remove(struct platform_device *pdev) { cygnus_soc_platform_unregister(&pdev->dev); - snd_soc_unregister_component(&pdev->dev); return 0; } diff --git a/sound/soc/codecs/Kconfig b/sound/soc/codecs/Kconfig @@ -82,6 +82,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_ES7241 select SND_SOC_GTM601 select SND_SOC_HDAC_HDMI + select SND_SOC_HDAC_HDA select SND_SOC_ICS43432 select SND_SOC_INNO_RK3036 select SND_SOC_ISABELLE if I2C @@ -109,6 +110,7 @@ config SND_SOC_ALL_CODECS select SND_SOC_MT6351 if MTK_PMIC_WRAP select SND_SOC_NAU8540 if I2C select SND_SOC_NAU8810 if I2C + select SND_SOC_NAU8822 if I2C select SND_SOC_NAU8824 if I2C select SND_SOC_NAU8825 if I2C select SND_SOC_HDMI_CODEC @@ -119,6 +121,8 @@ config SND_SOC_ALL_CODECS select SND_SOC_PCM186X_I2C if I2C select SND_SOC_PCM186X_SPI if SPI_MASTER select SND_SOC_PCM3008 + select SND_SOC_PCM3060_I2C if I2C + select SND_SOC_PCM3060_SPI if SPI_MASTER select SND_SOC_PCM3168A_I2C if I2C select SND_SOC_PCM3168A_SPI if SPI_MASTER select SND_SOC_PCM5102A @@ -575,7 +579,11 @@ config SND_SOC_DA9055 tristate config SND_SOC_DMIC - tristate + tristate "Generic Digital Microphone CODEC" + depends on GPIOLIB + help + Enable support for the Generic Digital Microphone CODEC. + Select this if your sound card has DMICs. config SND_SOC_HDMI_CODEC tristate @@ -615,6 +623,10 @@ config SND_SOC_HDAC_HDMI select SND_PCM_ELD select HDMI +config SND_SOC_HDAC_HDA + tristate + select SND_HDA + config SND_SOC_ICS43432 tristate @@ -629,7 +641,8 @@ config SND_SOC_LM49453 tristate config SND_SOC_MAX98088 - tristate + tristate "Maxim MAX98088/9 Low-Power, Stereo Audio Codec" + depends on I2C config SND_SOC_MAX98090 tristate @@ -732,6 +745,21 @@ config SND_SOC_PCM186X_SPI config SND_SOC_PCM3008 tristate +config SND_SOC_PCM3060 + tristate + +config SND_SOC_PCM3060_I2C + tristate "Texas Instruments PCM3060 CODEC - I2C" + depends on I2C + select SND_SOC_PCM3060 + select REGMAP_I2C + +config SND_SOC_PCM3060_SPI + tristate "Texas Instruments PCM3060 CODEC - SPI" + depends on SPI_MASTER + select SND_SOC_PCM3060 + select REGMAP_SPI + config SND_SOC_PCM3168A tristate @@ -1299,6 +1327,10 @@ config SND_SOC_NAU8810 tristate "Nuvoton Technology Corporation NAU88C10 CODEC" depends on I2C +config SND_SOC_NAU8822 + tristate "Nuvoton Technology Corporation NAU88C22 CODEC" + depends on I2C + config SND_SOC_NAU8824 tristate "Nuvoton Technology Corporation NAU88L24 CODEC" depends on I2C diff --git a/sound/soc/codecs/Makefile b/sound/soc/codecs/Makefile @@ -78,6 +78,7 @@ snd-soc-es8328-i2c-objs := es8328-i2c.o snd-soc-es8328-spi-objs := es8328-spi.o snd-soc-gtm601-objs := gtm601.o snd-soc-hdac-hdmi-objs := hdac_hdmi.o +snd-soc-hdac-hda-objs := hdac_hda.o snd-soc-ics43432-objs := ics43432.o snd-soc-inno-rk3036-objs := inno_rk3036.o snd-soc-isabelle-objs := isabelle.o @@ -106,6 +107,7 @@ snd-soc-msm8916-digital-objs := msm8916-wcd-digital.o snd-soc-mt6351-objs := mt6351.o snd-soc-nau8540-objs := nau8540.o snd-soc-nau8810-objs := nau8810.o +snd-soc-nau8822-objs := nau8822.o snd-soc-nau8824-objs := nau8824.o snd-soc-nau8825-objs := nau8825.o snd-soc-hdmi-codec-objs := hdmi-codec.o @@ -119,6 +121,9 @@ snd-soc-pcm186x-objs := pcm186x.o snd-soc-pcm186x-i2c-objs := pcm186x-i2c.o snd-soc-pcm186x-spi-objs := pcm186x-spi.o snd-soc-pcm3008-objs := pcm3008.o +snd-soc-pcm3060-objs := pcm3060.o +snd-soc-pcm3060-i2c-objs := pcm3060-i2c.o +snd-soc-pcm3060-spi-objs := pcm3060-spi.o snd-soc-pcm3168a-objs := pcm3168a.o snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o snd-soc-pcm3168a-spi-objs := pcm3168a-spi.o @@ -338,6 +343,7 @@ obj-$(CONFIG_SND_SOC_ES8328_I2C)+= snd-soc-es8328-i2c.o obj-$(CONFIG_SND_SOC_ES8328_SPI)+= snd-soc-es8328-spi.o obj-$(CONFIG_SND_SOC_GTM601) += snd-soc-gtm601.o obj-$(CONFIG_SND_SOC_HDAC_HDMI) += snd-soc-hdac-hdmi.o +obj-$(CONFIG_SND_SOC_HDAC_HDA) += snd-soc-hdac-hda.o obj-$(CONFIG_SND_SOC_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 @@ -366,6 +372,7 @@ 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_NAU8540) += snd-soc-nau8540.o obj-$(CONFIG_SND_SOC_NAU8810) += snd-soc-nau8810.o +obj-$(CONFIG_SND_SOC_NAU8822) += snd-soc-nau8822.o obj-$(CONFIG_SND_SOC_NAU8824) += snd-soc-nau8824.o obj-$(CONFIG_SND_SOC_NAU8825) += snd-soc-nau8825.o obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o @@ -379,6 +386,9 @@ obj-$(CONFIG_SND_SOC_PCM186X) += snd-soc-pcm186x.o obj-$(CONFIG_SND_SOC_PCM186X_I2C) += snd-soc-pcm186x-i2c.o obj-$(CONFIG_SND_SOC_PCM186X_SPI) += snd-soc-pcm186x-spi.o obj-$(CONFIG_SND_SOC_PCM3008) += snd-soc-pcm3008.o +obj-$(CONFIG_SND_SOC_PCM3060) += snd-soc-pcm3060.o +obj-$(CONFIG_SND_SOC_PCM3060_I2C) += snd-soc-pcm3060-i2c.o +obj-$(CONFIG_SND_SOC_PCM3060_SPI) += snd-soc-pcm3060-spi.o obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o obj-$(CONFIG_SND_SOC_PCM3168A_I2C) += snd-soc-pcm3168a-i2c.o obj-$(CONFIG_SND_SOC_PCM3168A_SPI) += snd-soc-pcm3168a-spi.o diff --git a/sound/soc/codecs/adau1761.c b/sound/soc/codecs/adau1761.c @@ -518,7 +518,8 @@ static int adau1761_setup_digmic_jackdetect(struct snd_soc_component *component) ARRAY_SIZE(adau1761_jack_detect_controls)); if (ret) return ret; - case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: /* fallthrough */ + /* fall through */ + case ADAU1761_DIGMIC_JACKDET_PIN_MODE_NONE: ret = snd_soc_dapm_add_routes(dapm, adau1761_no_dmic_routes, ARRAY_SIZE(adau1761_no_dmic_routes)); if (ret) diff --git a/sound/soc/codecs/adau17x1.c b/sound/soc/codecs/adau17x1.c @@ -21,11 +21,18 @@ #include <linux/i2c.h> #include <linux/spi/spi.h> #include <linux/regmap.h> +#include <asm/unaligned.h> #include "sigmadsp.h" #include "adau17x1.h" #include "adau-utils.h" +#define ADAU17X1_SAFELOAD_TARGET_ADDRESS 0x0006 +#define ADAU17X1_SAFELOAD_TRIGGER 0x0007 +#define ADAU17X1_SAFELOAD_DATA 0x0001 +#define ADAU17X1_SAFELOAD_DATA_SIZE 20 +#define ADAU17X1_WORD_SIZE 4 + static const char * const adau17x1_capture_mixer_boost_text[] = { "Normal operation", "Boost Level 1", "Boost Level 2", "Boost Level 3", }; @@ -60,6 +67,9 @@ static const struct snd_kcontrol_new adau17x1_controls[] = { SOC_ENUM("Mic Bias Mode", adau17x1_mic_bias_mode_enum), }; +static int adau17x1_setup_firmware(struct snd_soc_component *component, + unsigned int rate); + static int adau17x1_pll_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -313,7 +323,7 @@ static const struct snd_soc_dapm_route adau17x1_no_dsp_dapm_routes[] = { { "Capture", NULL, "Right Decimator" }, }; -bool adau17x1_has_dsp(struct adau *adau) +static bool adau17x1_has_dsp(struct adau *adau) { switch (adau->type) { case ADAU1761: @@ -324,7 +334,17 @@ bool adau17x1_has_dsp(struct adau *adau) return false; } } -EXPORT_SYMBOL_GPL(adau17x1_has_dsp); + +static bool adau17x1_has_safeload(struct adau *adau) +{ + switch (adau->type) { + case ADAU1761: + case ADAU1781: + return true; + default: + return false; + } +} static int adau17x1_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, unsigned int freq_in, unsigned int freq_out) @@ -836,7 +856,7 @@ bool adau17x1_volatile_register(struct device *dev, unsigned int reg) } EXPORT_SYMBOL_GPL(adau17x1_volatile_register); -int adau17x1_setup_firmware(struct snd_soc_component *component, +static int adau17x1_setup_firmware(struct snd_soc_component *component, unsigned int rate) { int ret; @@ -880,7 +900,6 @@ err: return ret; } -EXPORT_SYMBOL_GPL(adau17x1_setup_firmware); int adau17x1_add_widgets(struct snd_soc_component *component) { @@ -957,6 +976,56 @@ int adau17x1_resume(struct snd_soc_component *component) } EXPORT_SYMBOL_GPL(adau17x1_resume); +static int adau17x1_safeload(struct sigmadsp *sigmadsp, unsigned int addr, + const uint8_t bytes[], size_t len) +{ + uint8_t buf[ADAU17X1_WORD_SIZE]; + uint8_t data[ADAU17X1_SAFELOAD_DATA_SIZE]; + unsigned int addr_offset; + unsigned int nbr_words; + int ret; + + /* write data to safeload addresses. Check if len is not a multiple of + * 4 bytes, if so we need to zero pad. + */ + nbr_words = len / ADAU17X1_WORD_SIZE; + if ((len - nbr_words * ADAU17X1_WORD_SIZE) == 0) { + ret = regmap_raw_write(sigmadsp->control_data, + ADAU17X1_SAFELOAD_DATA, bytes, len); + } else { + nbr_words++; + memset(data, 0, ADAU17X1_SAFELOAD_DATA_SIZE); + memcpy(data, bytes, len); + ret = regmap_raw_write(sigmadsp->control_data, + ADAU17X1_SAFELOAD_DATA, data, + nbr_words * ADAU17X1_WORD_SIZE); + } + + if (ret < 0) + return ret; + + /* Write target address, target address is offset by 1 */ + addr_offset = addr - 1; + put_unaligned_be32(addr_offset, buf); + ret = regmap_raw_write(sigmadsp->control_data, + ADAU17X1_SAFELOAD_TARGET_ADDRESS, buf, ADAU17X1_WORD_SIZE); + if (ret < 0) + return ret; + + /* write nbr of words to trigger address */ + put_unaligned_be32(nbr_words, buf); + ret = regmap_raw_write(sigmadsp->control_data, + ADAU17X1_SAFELOAD_TRIGGER, buf, ADAU17X1_WORD_SIZE); + if (ret < 0) + return ret; + + return 0; +} + +static const struct sigmadsp_ops adau17x1_sigmadsp_ops = { + .safeload = adau17x1_safeload, +}; + int adau17x1_probe(struct device *dev, struct regmap *regmap, enum adau17x1_type type, void (*switch_mode)(struct device *dev), const char *firmware_name) @@ -1002,8 +1071,13 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap, dev_set_drvdata(dev, adau); if (firmware_name) { - adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, NULL, - firmware_name); + if (adau17x1_has_safeload(adau)) { + adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, + &adau17x1_sigmadsp_ops, firmware_name); + } else { + adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, + NULL, firmware_name); + } if (IS_ERR(adau->sigmadsp)) { dev_warn(dev, "Could not find firmware file: %ld\n", PTR_ERR(adau->sigmadsp)); diff --git a/sound/soc/codecs/adau17x1.h b/sound/soc/codecs/adau17x1.h @@ -68,10 +68,6 @@ int adau17x1_resume(struct snd_soc_component *component); extern const struct snd_soc_dai_ops adau17x1_dai_ops; -int adau17x1_setup_firmware(struct snd_soc_component *component, - unsigned int rate); -bool adau17x1_has_dsp(struct adau *adau); - #define ADAU17X1_CLOCK_CONTROL 0x4000 #define ADAU17X1_PLL_CONTROL 0x4002 #define ADAU17X1_REC_POWER_MGMT 0x4009 diff --git a/sound/soc/codecs/cs4265.c b/sound/soc/codecs/cs4265.c @@ -154,11 +154,11 @@ static const struct snd_kcontrol_new cs4265_snd_controls[] = { SOC_SINGLE("E to F Buffer Disable Switch", CS4265_SPDIF_CTL1, 6, 1, 0), SOC_ENUM("C Data Access", cam_mode_enum), + SOC_SINGLE("SPDIF Switch", CS4265_SPDIF_CTL2, 5, 1, 1), SOC_SINGLE("Validity Bit Control Switch", CS4265_SPDIF_CTL2, 3, 1, 0), SOC_ENUM("SPDIF Mono/Stereo", spdif_mono_stereo_enum), - SOC_SINGLE("MMTLR Data Switch", CS4265_SPDIF_CTL2, - 0, 1, 0), + SOC_SINGLE("MMTLR Data Switch", CS4265_SPDIF_CTL2, 0, 1, 0), SOC_ENUM("Mono Channel Select", spdif_mono_select_enum), SND_SOC_BYTES("C Data Buffer", CS4265_C_DATA_BUFF, 24), }; @@ -221,10 +221,11 @@ static const struct snd_soc_dapm_route cs4265_audio_map[] = { {"LINEOUTR", NULL, "DAC"}, {"SPDIFOUT", NULL, "SPDIF"}, + {"Pre-amp MIC", NULL, "MICL"}, + {"Pre-amp MIC", NULL, "MICR"}, + {"ADC Mux", "MIC", "Pre-amp MIC"}, {"ADC Mux", "LINEIN", "LINEINL"}, {"ADC Mux", "LINEIN", "LINEINR"}, - {"ADC Mux", "MIC", "MICL"}, - {"ADC Mux", "MIC", "MICR"}, {"ADC", NULL, "ADC Mux"}, {"DOUT", NULL, "ADC"}, {"DAI1 Capture", NULL, "DOUT"}, @@ -496,7 +497,8 @@ static int cs4265_set_bias_level(struct snd_soc_component *component, SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) #define CS4265_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_U16_LE | \ - SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE) + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_U32_LE) static const struct snd_soc_dai_ops cs4265_ops = { .hw_params = cs4265_pcm_hw_params, diff --git a/sound/soc/codecs/cs42l51.c b/sound/soc/codecs/cs42l51.c @@ -21,6 +21,7 @@ * - master mode *NOT* supported */ +#include <linux/clk.h> #include <linux/module.h> #include <linux/slab.h> #include <sound/core.h> @@ -41,6 +42,7 @@ enum master_slave_mode { struct cs42l51_private { unsigned int mclk; + struct clk *mclk_handle; unsigned int audio_mode; /* The mode (I2S or left-justified) */ enum master_slave_mode func; }; @@ -237,6 +239,10 @@ static const struct snd_soc_dapm_widget cs42l51_dapm_widgets[] = { &cs42l51_adcr_mux_controls), }; +static const struct snd_soc_dapm_widget cs42l51_dapm_mclk_widgets[] = { + SND_SOC_DAPM_CLOCK_SUPPLY("MCLK") +}; + static const struct snd_soc_dapm_route cs42l51_routes[] = { {"HPL", NULL, "Left DAC"}, {"HPR", NULL, "Right DAC"}, @@ -487,6 +493,14 @@ static struct snd_soc_dai_driver cs42l51_dai = { static int cs42l51_component_probe(struct snd_soc_component *component) { int ret, reg; + struct snd_soc_dapm_context *dapm; + struct cs42l51_private *cs42l51; + + cs42l51 = snd_soc_component_get_drvdata(component); + dapm = snd_soc_component_get_dapm(component); + + if (cs42l51->mclk_handle) + snd_soc_dapm_new_controls(dapm, cs42l51_dapm_mclk_widgets, 1); /* * DAC configuration @@ -540,6 +554,13 @@ int cs42l51_probe(struct device *dev, struct regmap *regmap) dev_set_drvdata(dev, cs42l51); + cs42l51->mclk_handle = devm_clk_get(dev, "MCLK"); + if (IS_ERR(cs42l51->mclk_handle)) { + if (PTR_ERR(cs42l51->mclk_handle) != -ENOENT) + return PTR_ERR(cs42l51->mclk_handle); + cs42l51->mclk_handle = NULL; + } + /* Verify that we have a CS42L51 */ ret = regmap_read(regmap, CS42L51_CHIP_REV_ID, &val); if (ret < 0) { diff --git a/sound/soc/codecs/dmic.c b/sound/soc/codecs/dmic.c @@ -148,6 +148,7 @@ static const struct of_device_id dmic_dev_match[] = { {.compatible = "dmic-codec"}, {} }; +MODULE_DEVICE_TABLE(of, dmic_dev_match); static struct platform_driver dmic_driver = { .driver = { diff --git a/sound/soc/codecs/es8328.c b/sound/soc/codecs/es8328.c @@ -566,14 +566,14 @@ static int es8328_set_sysclk(struct snd_soc_dai *codec_dai, break; case 22579200: mclkdiv2 = 1; - /* fallthru */ + /* fall through */ case 11289600: es8328->sysclk_constraints = &constraints_11289; es8328->mclk_ratios = ratios_11289; break; case 24576000: mclkdiv2 = 1; - /* fallthru */ + /* fall through */ case 12288000: es8328->sysclk_constraints = &constraints_12288; es8328->mclk_ratios = ratios_12288; diff --git a/sound/soc/codecs/hdac_hda.c b/sound/soc/codecs/hdac_hda.c @@ -0,0 +1,483 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2015-18 Intel Corporation. + +/* + * hdac_hda.c - ASoC extensions to reuse the legacy HDA codec drivers + * with ASoC platform drivers. These APIs are called by the legacy HDA + * codec drivers using hdac_ext_bus_ops ops. + */ + +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/module.h> +#include <linux/pm_runtime.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/hdaudio_ext.h> +#include <sound/hda_codec.h> +#include <sound/hda_register.h> +#include "hdac_hda.h" + +#define HDAC_ANALOG_DAI_ID 0 +#define HDAC_DIGITAL_DAI_ID 1 +#define HDAC_ALT_ANALOG_DAI_ID 2 + +#define STUB_FORMATS (SNDRV_PCM_FMTBIT_S8 | \ + SNDRV_PCM_FMTBIT_U8 | \ + SNDRV_PCM_FMTBIT_S16_LE | \ + SNDRV_PCM_FMTBIT_U16_LE | \ + SNDRV_PCM_FMTBIT_S24_LE | \ + SNDRV_PCM_FMTBIT_U24_LE | \ + SNDRV_PCM_FMTBIT_S32_LE | \ + SNDRV_PCM_FMTBIT_U32_LE | \ + SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE) + +static int hdac_hda_dai_open(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +static void hdac_hda_dai_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai); +static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width); +static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, + struct snd_soc_dai *dai); + +static struct snd_soc_dai_ops hdac_hda_dai_ops = { + .startup = hdac_hda_dai_open, + .shutdown = hdac_hda_dai_close, + .prepare = hdac_hda_dai_prepare, + .hw_free = hdac_hda_dai_hw_free, + .set_tdm_slot = hdac_hda_dai_set_tdm_slot, +}; + +static struct snd_soc_dai_driver hdac_hda_dais[] = { +{ + .id = HDAC_ANALOG_DAI_ID, + .name = "Analog Codec DAI", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "Analog Codec Playback", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Analog Codec Capture", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, +{ + .id = HDAC_DIGITAL_DAI_ID, + .name = "Digital Codec DAI", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "Digital Codec Playback", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Digital Codec Capture", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +}, +{ + .id = HDAC_ALT_ANALOG_DAI_ID, + .name = "Alt Analog Codec DAI", + .ops = &hdac_hda_dai_ops, + .playback = { + .stream_name = "Alt Analog Codec Playback", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, + .capture = { + .stream_name = "Alt Analog Codec Capture", + .channels_min = 1, + .channels_max = 16, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = STUB_FORMATS, + .sig_bits = 24, + }, +} + +}; + +static int hdac_hda_dai_set_tdm_slot(struct snd_soc_dai *dai, + unsigned int tx_mask, unsigned int rx_mask, + int slots, int slot_width) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct hdac_hda_pcm *pcm; + + hda_pvt = snd_soc_component_get_drvdata(component); + pcm = &hda_pvt->pcm[dai->id]; + if (tx_mask) + pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_PLAYBACK] = tx_mask; + else + pcm[dai->id].stream_tag[SNDRV_PCM_STREAM_CAPTURE] = rx_mask; + + return 0; +} + +static int hdac_hda_dai_hw_free(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct hda_pcm_stream *hda_stream; + struct hda_pcm *pcm; + + hda_pvt = snd_soc_component_get_drvdata(component); + pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); + if (!pcm) + return -EINVAL; + + hda_stream = &pcm->stream[substream->stream]; + snd_hda_codec_cleanup(&hda_pvt->codec, hda_stream, substream); + + return 0; +} + +static int hdac_hda_dai_prepare(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct snd_pcm_runtime *runtime = substream->runtime; + struct hdac_device *hdev; + struct hda_pcm_stream *hda_stream; + unsigned int format_val; + struct hda_pcm *pcm; + unsigned int stream; + int ret = 0; + + hda_pvt = snd_soc_component_get_drvdata(component); + hdev = &hda_pvt->codec.core; + pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); + if (!pcm) + return -EINVAL; + + hda_stream = &pcm->stream[substream->stream]; + + format_val = snd_hdac_calc_stream_format(runtime->rate, + runtime->channels, + runtime->format, + hda_stream->maxbps, + 0); + if (!format_val) { + dev_err(&hdev->dev, + "invalid format_val, rate=%d, ch=%d, format=%d\n", + runtime->rate, runtime->channels, runtime->format); + return -EINVAL; + } + + stream = hda_pvt->pcm[dai->id].stream_tag[substream->stream]; + + ret = snd_hda_codec_prepare(&hda_pvt->codec, hda_stream, + stream, format_val, substream); + if (ret < 0) + dev_err(&hdev->dev, "codec prepare failed %d\n", ret); + + return ret; +} + +static int hdac_hda_dai_open(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct hda_pcm_stream *hda_stream; + struct hda_pcm *pcm; + int ret; + + hda_pvt = snd_soc_component_get_drvdata(component); + pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); + if (!pcm) + return -EINVAL; + + snd_hda_codec_pcm_get(pcm); + + hda_stream = &pcm->stream[substream->stream]; + + ret = hda_stream->ops.open(hda_stream, &hda_pvt->codec, substream); + if (ret < 0) + snd_hda_codec_pcm_put(pcm); + + return ret; +} + +static void hdac_hda_dai_close(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct hdac_hda_priv *hda_pvt; + struct hda_pcm_stream *hda_stream; + struct hda_pcm *pcm; + + hda_pvt = snd_soc_component_get_drvdata(component); + pcm = snd_soc_find_pcm_from_dai(hda_pvt, dai); + if (!pcm) + return; + + hda_stream = &pcm->stream[substream->stream]; + + hda_stream->ops.close(hda_stream, &hda_pvt->codec, substream); + + snd_hda_codec_pcm_put(pcm); +} + +static struct hda_pcm *snd_soc_find_pcm_from_dai(struct hdac_hda_priv *hda_pvt, + struct snd_soc_dai *dai) +{ + struct hda_codec *hcodec = &hda_pvt->codec; + struct hda_pcm *cpcm; + const char *pcm_name; + + switch (dai->id) { + case HDAC_ANALOG_DAI_ID: + pcm_name = "Analog"; + break; + case HDAC_DIGITAL_DAI_ID: + pcm_name = "Digital"; + break; + case HDAC_ALT_ANALOG_DAI_ID: + pcm_name = "Alt Analog"; + break; + default: + dev_err(&hcodec->core.dev, "invalid dai id %d\n", dai->id); + return NULL; + } + + list_for_each_entry(cpcm, &hcodec->pcm_list_head, list) { + if (strpbrk(cpcm->name, pcm_name)) + return cpcm; + } + + dev_err(&hcodec->core.dev, "didn't find PCM for DAI %s\n", dai->name); + return NULL; +} + +static int hdac_hda_codec_probe(struct snd_soc_component *component) +{ + struct hdac_hda_priv *hda_pvt = + snd_soc_component_get_drvdata(component); + struct snd_soc_dapm_context *dapm = + snd_soc_component_get_dapm(component); + struct hdac_device *hdev = &hda_pvt->codec.core; + struct hda_codec *hcodec = &hda_pvt->codec; + struct hdac_ext_link *hlink; + hda_codec_patch_t patch; + int ret; + + hlink = snd_hdac_ext_bus_get_link(hdev->bus, dev_name(&hdev->dev)); + if (!hlink) { + dev_err(&hdev->dev, "hdac link not found\n"); + return -EIO; + } + + snd_hdac_ext_bus_link_get(hdev->bus, hlink); + + ret = snd_hda_codec_device_new(hcodec->bus, component->card->snd_card, + hdev->addr, hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "failed to create hda codec %d\n", ret); + goto error_no_pm; + } + + /* + * snd_hda_codec_device_new decrements the usage count so call get pm + * else the device will be powered off + */ + pm_runtime_get_noresume(&hdev->dev); + + hcodec->bus->card = dapm->card->snd_card; + + ret = snd_hda_codec_set_name(hcodec, hcodec->preset->name); + if (ret < 0) { + dev_err(&hdev->dev, "name failed %s\n", hcodec->preset->name); + goto error; + } + + ret = snd_hdac_regmap_init(&hcodec->core); + if (ret < 0) { + dev_err(&hdev->dev, "regmap init failed\n"); + goto error; + } + + patch = (hda_codec_patch_t)hcodec->preset->driver_data; + if (patch) { + ret = patch(hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "patch failed %d\n", ret); + goto error; + } + } else { + dev_dbg(&hdev->dev, "no patch file found\n"); + } + + ret = snd_hda_codec_parse_pcms(hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "unable to map pcms to dai %d\n", ret); + goto error; + } + + ret = snd_hda_codec_build_controls(hcodec); + if (ret < 0) { + dev_err(&hdev->dev, "unable to create controls %d\n", ret); + goto error; + } + + hcodec->core.lazy_cache = true; + + /* + * hdac_device core already sets the state to active and calls + * get_noresume. So enable runtime and set the device to suspend. + * pm_runtime_enable is also called during codec registeration + */ + pm_runtime_put(&hdev->dev); + pm_runtime_suspend(&hdev->dev); + + return 0; + +error: + pm_runtime_put(&hdev->dev); +error_no_pm: + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + return ret; +} + +static void hdac_hda_codec_remove(struct snd_soc_component *component) +{ + struct hdac_hda_priv *hda_pvt = + snd_soc_component_get_drvdata(component); + struct hdac_device *hdev = &hda_pvt->codec.core; + struct hdac_ext_link *hlink = NULL; + + hlink = snd_hdac_ext_bus_get_link(hdev->bus, dev_name(&hdev->dev)); + if (!hlink) { + dev_err(&hdev->dev, "hdac link not found\n"); + return; + } + + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + pm_runtime_disable(&hdev->dev); +} + +static const struct snd_soc_dapm_route hdac_hda_dapm_routes[] = { + {"AIF1TX", NULL, "Codec Input Pin1"}, + {"AIF2TX", NULL, "Codec Input Pin2"}, + {"AIF3TX", NULL, "Codec Input Pin3"}, + + {"Codec Output Pin1", NULL, "AIF1RX"}, + {"Codec Output Pin2", NULL, "AIF2RX"}, + {"Codec Output Pin3", NULL, "AIF3RX"}, +}; + +static const struct snd_soc_dapm_widget hdac_hda_dapm_widgets[] = { + /* Audio Interface */ + SND_SOC_DAPM_AIF_IN("AIF1RX", "Analog Codec Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF2RX", "Digital Codec Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_IN("AIF3RX", "Alt Analog Codec Playback", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF1TX", "Analog Codec Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF2TX", "Digital Codec Capture", 0, + SND_SOC_NOPM, 0, 0), + SND_SOC_DAPM_AIF_OUT("AIF3TX", "Alt Analog Codec Capture", 0, + SND_SOC_NOPM, 0, 0), + + /* Input Pins */ + SND_SOC_DAPM_INPUT("Codec Input Pin1"), + SND_SOC_DAPM_INPUT("Codec Input Pin2"), + SND_SOC_DAPM_INPUT("Codec Input Pin3"), + + /* Output Pins */ + SND_SOC_DAPM_OUTPUT("Codec Output Pin1"), + SND_SOC_DAPM_OUTPUT("Codec Output Pin2"), + SND_SOC_DAPM_OUTPUT("Codec Output Pin3"), +}; + +static const struct snd_soc_component_driver hdac_hda_codec = { + .probe = hdac_hda_codec_probe, + .remove = hdac_hda_codec_remove, + .idle_bias_on = false, + .dapm_widgets = hdac_hda_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(hdac_hda_dapm_widgets), + .dapm_routes = hdac_hda_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(hdac_hda_dapm_routes), +}; + +static int hdac_hda_dev_probe(struct hdac_device *hdev) +{ + struct hdac_ext_link *hlink; + struct hdac_hda_priv *hda_pvt; + int ret; + + /* hold the ref while we probe */ + hlink = snd_hdac_ext_bus_get_link(hdev->bus, dev_name(&hdev->dev)); + if (!hlink) { + dev_err(&hdev->dev, "hdac link not found\n"); + return -EIO; + } + snd_hdac_ext_bus_link_get(hdev->bus, hlink); + + hda_pvt = hdac_to_hda_priv(hdev); + if (!hda_pvt) + return -ENOMEM; + + /* ASoC specific initialization */ + ret = devm_snd_soc_register_component(&hdev->dev, + &hdac_hda_codec, hdac_hda_dais, + ARRAY_SIZE(hdac_hda_dais)); + if (ret < 0) { + dev_err(&hdev->dev, "failed to register HDA codec %d\n", ret); + return ret; + } + + dev_set_drvdata(&hdev->dev, hda_pvt); + snd_hdac_ext_bus_link_put(hdev->bus, hlink); + + return ret; +} + +static int hdac_hda_dev_remove(struct hdac_device *hdev) +{ + return 0; +} + +static struct hdac_ext_bus_ops hdac_ops = { + .hdev_attach = hdac_hda_dev_probe, + .hdev_detach = hdac_hda_dev_remove, +}; + +struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void) +{ + return &hdac_ops; +} +EXPORT_SYMBOL_GPL(snd_soc_hdac_hda_get_ops); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("ASoC Extensions for legacy HDA Drivers"); +MODULE_AUTHOR("Rakesh Ughreja<rakesh.a.ughreja@intel.com>"); diff --git a/sound/soc/codecs/hdac_hda.h b/sound/soc/codecs/hdac_hda.h @@ -0,0 +1,24 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2015-18 Intel Corporation. + */ + +#ifndef __HDAC_HDA_H__ +#define __HDAC_HDA_H__ + +struct hdac_hda_pcm { + int stream_tag[2]; +}; + +struct hdac_hda_priv { + struct hda_codec codec; + struct hdac_hda_pcm pcm[2]; +}; + +#define hdac_to_hda_priv(_hdac) \ + container_of(_hdac, struct hdac_hda_priv, codec.core) +#define hdac_to_hda_codec(_hdac) container_of(_hdac, struct hda_codec, core) + +struct hdac_ext_bus_ops *snd_soc_hdac_hda_get_ops(void); + +#endif /* __HDAC_HDA_H__ */ diff --git a/sound/soc/codecs/hdac_hdmi.c b/sound/soc/codecs/hdac_hdmi.c @@ -1410,6 +1410,12 @@ static int hdac_hdmi_create_dais(struct hdac_device *hdev, if (ret) return ret; + /* Filter out 44.1, 88.2 and 176.4Khz */ + rates &= ~(SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_88200 | + SNDRV_PCM_RATE_176400); + if (!rates) + return -EINVAL; + sprintf(dai_name, "intel-hdmi-hifi%d", i+1); hdmi_dais[i].name = devm_kstrdup(&hdev->dev, dai_name, GFP_KERNEL); @@ -1598,7 +1604,7 @@ static struct snd_pcm *hdac_hdmi_get_pcm_from_id(struct snd_soc_card *card, { struct snd_soc_pcm_runtime *rtd; - list_for_each_entry(rtd, &card->rtd_list, list) { + for_each_card_rtds(card, rtd) { if (rtd->pcm && (rtd->pcm->device == device)) return rtd->pcm; } @@ -1961,9 +1967,6 @@ static int hdac_hdmi_get_spk_alloc(struct hdac_device *hdev, int pcm_idx) port = list_first_entry(&pcm->port_list, struct hdac_hdmi_port, head); - if (!port) - return 0; - if (!port || !port->eld.eld_valid) return 0; diff --git a/sound/soc/codecs/max98088.c b/sound/soc/codecs/max98088.c @@ -16,6 +16,7 @@ #include <linux/pm.h> #include <linux/i2c.h> #include <linux/regmap.h> +#include <linux/clk.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/pcm_params.h> @@ -42,6 +43,7 @@ struct max98088_priv { struct regmap *regmap; enum max98088_type devtype; struct max98088_pdata *pdata; + struct clk *mclk; unsigned int sysclk; struct max98088_cdata dai[2]; int eq_textcnt; @@ -1103,6 +1105,11 @@ static int max98088_dai_set_sysclk(struct snd_soc_dai *dai, if (freq == max98088->sysclk) return 0; + if (!IS_ERR(max98088->mclk)) { + freq = clk_round_rate(max98088->mclk, freq); + clk_set_rate(max98088->mclk, freq); + } + /* Setup clocks for slave mode, and using the PLL * PSCLK = 0x01 (when master clk is 10MHz to 20MHz) * 0x02 (when master clk is 20MHz to 30MHz).. @@ -1310,6 +1317,20 @@ static int max98088_set_bias_level(struct snd_soc_component *component, break; case SND_SOC_BIAS_PREPARE: + /* + * SND_SOC_BIAS_PREPARE is called while preparing for a + * transition to ON or away from ON. If current bias_level + * is SND_SOC_BIAS_ON, then it is preparing for a transition + * away from ON. Disable the clock in that case, otherwise + * enable it. + */ + if (!IS_ERR(max98088->mclk)) { + if (snd_soc_component_get_bias_level(component) == + SND_SOC_BIAS_ON) + clk_disable_unprepare(max98088->mclk); + else + clk_prepare_enable(max98088->mclk); + } break; case SND_SOC_BIAS_STANDBY: @@ -1725,6 +1746,11 @@ static int max98088_i2c_probe(struct i2c_client *i2c, if (IS_ERR(max98088->regmap)) return PTR_ERR(max98088->regmap); + max98088->mclk = devm_clk_get(&i2c->dev, "mclk"); + if (IS_ERR(max98088->mclk)) + if (PTR_ERR(max98088->mclk) == -EPROBE_DEFER) + return PTR_ERR(max98088->mclk); + max98088->devtype = id->driver_data; i2c_set_clientdata(i2c, max98088); @@ -1742,9 +1768,19 @@ static const struct i2c_device_id max98088_i2c_id[] = { }; MODULE_DEVICE_TABLE(i2c, max98088_i2c_id); +#if defined(CONFIG_OF) +static const struct of_device_id max98088_of_match[] = { + { .compatible = "maxim,max98088" }, + { .compatible = "maxim,max98089" }, + { } +}; +MODULE_DEVICE_TABLE(of, max98088_of_match); +#endif + static struct i2c_driver max98088_i2c_driver = { .driver = { .name = "max98088", + .of_match_table = of_match_ptr(max98088_of_match), }, .probe = max98088_i2c_probe, .id_table = max98088_i2c_id, diff --git a/sound/soc/codecs/max98373.c b/sound/soc/codecs/max98373.c @@ -2,6 +2,7 @@ // Copyright (c) 2017, Maxim Integrated #include <linux/acpi.h> +#include <linux/delay.h> #include <linux/i2c.h> #include <linux/module.h> #include <linux/regmap.h> @@ -454,7 +455,7 @@ SND_SOC_DAPM_SIGGEN("IMON"), SND_SOC_DAPM_SIGGEN("FBMON"), }; -static DECLARE_TLV_DB_SCALE(max98373_digital_tlv, 0, -50, 0); +static DECLARE_TLV_DB_SCALE(max98373_digital_tlv, -6350, 50, 1); static const DECLARE_TLV_DB_RANGE(max98373_spk_tlv, 0, 8, TLV_DB_SCALE_ITEM(0, 50, 0), 9, 10, TLV_DB_SCALE_ITEM(500, 100, 0), @@ -470,19 +471,19 @@ static const DECLARE_TLV_DB_RANGE(max98373_dht_spkgain_min_tlv, 0, 9, TLV_DB_SCALE_ITEM(800, 100, 0), ); static const DECLARE_TLV_DB_RANGE(max98373_dht_rotation_point_tlv, - 0, 1, TLV_DB_SCALE_ITEM(-50, -50, 0), - 2, 7, TLV_DB_SCALE_ITEM(-200, -100, 0), - 8, 9, TLV_DB_SCALE_ITEM(-1000, -200, 0), - 10, 11, TLV_DB_SCALE_ITEM(-1500, -300, 0), - 12, 13, TLV_DB_SCALE_ITEM(-2000, -200, 0), - 14, 15, TLV_DB_SCALE_ITEM(-2500, -500, 0), + 0, 1, TLV_DB_SCALE_ITEM(-3000, 500, 0), + 2, 4, TLV_DB_SCALE_ITEM(-2200, 200, 0), + 5, 6, TLV_DB_SCALE_ITEM(-1500, 300, 0), + 7, 9, TLV_DB_SCALE_ITEM(-1000, 200, 0), + 10, 13, TLV_DB_SCALE_ITEM(-500, 100, 0), + 14, 15, TLV_DB_SCALE_ITEM(-100, 50, 0), ); static const DECLARE_TLV_DB_RANGE(max98373_limiter_thresh_tlv, - 0, 15, TLV_DB_SCALE_ITEM(0, -100, 0), + 0, 15, TLV_DB_SCALE_ITEM(-1500, 100, 0), ); static const DECLARE_TLV_DB_RANGE(max98373_bde_gain_tlv, - 0, 60, TLV_DB_SCALE_ITEM(0, -25, 0), + 0, 60, TLV_DB_SCALE_ITEM(-1500, 25, 0), ); static bool max98373_readable_register(struct device *dev, unsigned int reg) @@ -604,7 +605,7 @@ SOC_SINGLE("Dither Switch", MAX98373_R203F_AMP_DSP_CFG, SOC_SINGLE("DC Blocker Switch", MAX98373_R203F_AMP_DSP_CFG, MAX98373_AMP_DSP_CFG_DCBLK_SHIFT, 1, 0), SOC_SINGLE_TLV("Digital Volume", MAX98373_R203D_AMP_DIG_VOL_CTRL, - 0, 0x7F, 0, max98373_digital_tlv), + 0, 0x7F, 1, max98373_digital_tlv), SOC_SINGLE_TLV("Speaker Volume", MAX98373_R203E_AMP_PATH_GAIN, MAX98373_SPK_DIGI_GAIN_SHIFT, 10, 0, max98373_spk_tlv), SOC_SINGLE_TLV("FS Max Volume", MAX98373_R203E_AMP_PATH_GAIN, @@ -616,7 +617,7 @@ SOC_SINGLE("DHT Switch", MAX98373_R20D4_DHT_EN, SOC_SINGLE_TLV("DHT Min Volume", MAX98373_R20D1_DHT_CFG, MAX98373_DHT_SPK_GAIN_MIN_SHIFT, 9, 0, max98373_dht_spkgain_min_tlv), SOC_SINGLE_TLV("DHT Rot Pnt Volume", MAX98373_R20D1_DHT_CFG, - MAX98373_DHT_ROT_PNT_SHIFT, 15, 0, max98373_dht_rotation_point_tlv), + MAX98373_DHT_ROT_PNT_SHIFT, 15, 1, max98373_dht_rotation_point_tlv), SOC_SINGLE_TLV("DHT Attack Step Volume", MAX98373_R20D2_DHT_ATTACK_CFG, MAX98373_DHT_ATTACK_STEP_SHIFT, 4, 0, max98373_dht_step_size_tlv), SOC_SINGLE_TLV("DHT Release Step Volume", MAX98373_R20D3_DHT_RELEASE_CFG, @@ -653,29 +654,29 @@ SOC_SINGLE("BDE Hold Time", MAX98373_R2090_BDE_LVL_HOLD, 0, 0xFF, 0), SOC_SINGLE("BDE Attack Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 4, 0xF, 0), SOC_SINGLE("BDE Release Rate", MAX98373_R2091_BDE_GAIN_ATK_REL_RATE, 0, 0xF, 0), SOC_SINGLE_TLV("BDE LVL1 Clip Thresh Volume", MAX98373_R20A9_BDE_L1_CFG_2, - 0, 0x3C, 0, max98373_bde_gain_tlv), + 0, 0x3C, 1, max98373_bde_gain_tlv), SOC_SINGLE_TLV("BDE LVL2 Clip Thresh Volume", MAX98373_R20AC_BDE_L2_CFG_2, - 0, 0x3C, 0, max98373_bde_gain_tlv), + 0, 0x3C, 1, max98373_bde_gain_tlv), SOC_SINGLE_TLV("BDE LVL3 Clip Thresh Volume", MAX98373_R20AF_BDE_L3_CFG_2, - 0, 0x3C, 0, max98373_bde_gain_tlv), + 0, 0x3C, 1, max98373_bde_gain_tlv), SOC_SINGLE_TLV("BDE LVL4 Clip Thresh Volume", MAX98373_R20B2_BDE_L4_CFG_2, - 0, 0x3C, 0, max98373_bde_gain_tlv), + 0, 0x3C, 1, max98373_bde_gain_tlv), SOC_SINGLE_TLV("BDE LVL1 Clip Reduction Volume", MAX98373_R20AA_BDE_L1_CFG_3, - 0, 0x3C, 0, max98373_bde_gain_tlv), + 0, 0x3C, 1, max98373_bde_gain_tlv), SOC_SINGLE_TLV("BDE LVL2 Clip Reduction Volume", MAX98373_R20AD_BDE_L2_CFG_3, - 0, 0x3C, 0, max98373_bde_gain_tlv), + 0, 0x3C, 1, max98373_bde_gain_tlv), SOC_SINGLE_TLV("BDE LVL3 Clip Reduction Volume", MAX98373_R20B0_BDE_L3_CFG_3, - 0, 0x3C, 0, max98373_bde_gain_tlv), + 0, 0x3C, 1, max98373_bde_gain_tlv), SOC_SINGLE_TLV("BDE LVL4 Clip Reduction Volume", MAX98373_R20B3_BDE_L4_CFG_3, - 0, 0x3C, 0, max98373_bde_gain_tlv), + 0, 0x3C, 1, max98373_bde_gain_tlv), SOC_SINGLE_TLV("BDE LVL1 Limiter Thresh Volume", MAX98373_R20A8_BDE_L1_CFG_1, - 0, 0xF, 0, max98373_limiter_thresh_tlv), + 0, 0xF, 1, max98373_limiter_thresh_tlv), SOC_SINGLE_TLV("BDE LVL2 Limiter Thresh Volume", MAX98373_R20AB_BDE_L2_CFG_1, - 0, 0xF, 0, max98373_limiter_thresh_tlv), + 0, 0xF, 1, max98373_limiter_thresh_tlv), SOC_SINGLE_TLV("BDE LVL3 Limiter Thresh Volume", MAX98373_R20AE_BDE_L3_CFG_1, - 0, 0xF, 0, max98373_limiter_thresh_tlv), + 0, 0xF, 1, max98373_limiter_thresh_tlv), SOC_SINGLE_TLV("BDE LVL4 Limiter Thresh Volume", MAX98373_R20B1_BDE_L4_CFG_1, - 0, 0xF, 0, max98373_limiter_thresh_tlv), + 0, 0xF, 1, max98373_limiter_thresh_tlv), /* Limiter */ SOC_SINGLE("Limiter Switch", MAX98373_R20E2_LIMITER_EN, MAX98373_LIMITER_EN_SHIFT, 1, 0), diff --git a/sound/soc/codecs/nau8822.c b/sound/soc/codecs/nau8822.c @@ -0,0 +1,1136 @@ +/* + * nau8822.c -- NAU8822 ALSA Soc Audio Codec driver + * + * Copyright 2017 Nuvoton Technology Corp. + * + * Author: David Lin <ctlin0@nuvoton.com> + * Co-author: John Hsu <kchsu0@nuvoton.com> + * Co-author: Seven Li <wtli@nuvoton.com> + * + * Based on WM8974.c + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#include <linux/module.h> +#include <linux/moduleparam.h> +#include <linux/kernel.h> +#include <linux/init.h> +#include <linux/delay.h> +#include <linux/pm.h> +#include <linux/i2c.h> +#include <linux/regmap.h> +#include <linux/slab.h> +#include <sound/core.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/initval.h> +#include <sound/tlv.h> +#include <asm/div64.h> +#include "nau8822.h" + +#define NAU_PLL_FREQ_MAX 100000000 +#define NAU_PLL_FREQ_MIN 90000000 +#define NAU_PLL_REF_MAX 33000000 +#define NAU_PLL_REF_MIN 8000000 +#define NAU_PLL_OPTOP_MIN 6 + +static const int nau8822_mclk_scaler[] = { 10, 15, 20, 30, 40, 60, 80, 120 }; + +static const struct reg_default nau8822_reg_defaults[] = { + { NAU8822_REG_POWER_MANAGEMENT_1, 0x0000 }, + { NAU8822_REG_POWER_MANAGEMENT_2, 0x0000 }, + { NAU8822_REG_POWER_MANAGEMENT_3, 0x0000 }, + { NAU8822_REG_AUDIO_INTERFACE, 0x0050 }, + { NAU8822_REG_COMPANDING_CONTROL, 0x0000 }, + { NAU8822_REG_CLOCKING, 0x0140 }, + { NAU8822_REG_ADDITIONAL_CONTROL, 0x0000 }, + { NAU8822_REG_GPIO_CONTROL, 0x0000 }, + { NAU8822_REG_JACK_DETECT_CONTROL_1, 0x0000 }, + { NAU8822_REG_DAC_CONTROL, 0x0000 }, + { NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME, 0x00ff }, + { NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME, 0x00ff }, + { NAU8822_REG_JACK_DETECT_CONTROL_2, 0x0000 }, + { NAU8822_REG_ADC_CONTROL, 0x0100 }, + { NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME, 0x00ff }, + { NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME, 0x00ff }, + { NAU8822_REG_EQ1, 0x012c }, + { NAU8822_REG_EQ2, 0x002c }, + { NAU8822_REG_EQ3, 0x002c }, + { NAU8822_REG_EQ4, 0x002c }, + { NAU8822_REG_EQ5, 0x002c }, + { NAU8822_REG_DAC_LIMITER_1, 0x0032 }, + { NAU8822_REG_DAC_LIMITER_2, 0x0000 }, + { NAU8822_REG_NOTCH_FILTER_1, 0x0000 }, + { NAU8822_REG_NOTCH_FILTER_2, 0x0000 }, + { NAU8822_REG_NOTCH_FILTER_3, 0x0000 }, + { NAU8822_REG_NOTCH_FILTER_4, 0x0000 }, + { NAU8822_REG_ALC_CONTROL_1, 0x0038 }, + { NAU8822_REG_ALC_CONTROL_2, 0x000b }, + { NAU8822_REG_ALC_CONTROL_3, 0x0032 }, + { NAU8822_REG_NOISE_GATE, 0x0010 }, + { NAU8822_REG_PLL_N, 0x0008 }, + { NAU8822_REG_PLL_K1, 0x000c }, + { NAU8822_REG_PLL_K2, 0x0093 }, + { NAU8822_REG_PLL_K3, 0x00e9 }, + { NAU8822_REG_3D_CONTROL, 0x0000 }, + { NAU8822_REG_RIGHT_SPEAKER_CONTROL, 0x0000 }, + { NAU8822_REG_INPUT_CONTROL, 0x0033 }, + { NAU8822_REG_LEFT_INP_PGA_CONTROL, 0x0010 }, + { NAU8822_REG_RIGHT_INP_PGA_CONTROL, 0x0010 }, + { NAU8822_REG_LEFT_ADC_BOOST_CONTROL, 0x0100 }, + { NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 0x0100 }, + { NAU8822_REG_OUTPUT_CONTROL, 0x0002 }, + { NAU8822_REG_LEFT_MIXER_CONTROL, 0x0001 }, + { NAU8822_REG_RIGHT_MIXER_CONTROL, 0x0001 }, + { NAU8822_REG_LHP_VOLUME, 0x0039 }, + { NAU8822_REG_RHP_VOLUME, 0x0039 }, + { NAU8822_REG_LSPKOUT_VOLUME, 0x0039 }, + { NAU8822_REG_RSPKOUT_VOLUME, 0x0039 }, + { NAU8822_REG_AUX2_MIXER, 0x0001 }, + { NAU8822_REG_AUX1_MIXER, 0x0001 }, + { NAU8822_REG_POWER_MANAGEMENT_4, 0x0000 }, + { NAU8822_REG_LEFT_TIME_SLOT, 0x0000 }, + { NAU8822_REG_MISC, 0x0020 }, + { NAU8822_REG_RIGHT_TIME_SLOT, 0x0000 }, + { NAU8822_REG_DEVICE_REVISION, 0x007f }, + { NAU8822_REG_DEVICE_ID, 0x001a }, + { NAU8822_REG_DAC_DITHER, 0x0114 }, + { NAU8822_REG_ALC_ENHANCE_1, 0x0000 }, + { NAU8822_REG_ALC_ENHANCE_2, 0x0000 }, + { NAU8822_REG_192KHZ_SAMPLING, 0x0008 }, + { NAU8822_REG_MISC_CONTROL, 0x0000 }, + { NAU8822_REG_INPUT_TIEOFF, 0x0000 }, + { NAU8822_REG_POWER_REDUCTION, 0x0000 }, + { NAU8822_REG_AGC_PEAK2PEAK, 0x0000 }, + { NAU8822_REG_AGC_PEAK_DETECT, 0x0000 }, + { NAU8822_REG_AUTOMUTE_CONTROL, 0x0000 }, + { NAU8822_REG_OUTPUT_TIEOFF, 0x0000 }, +}; + +static bool nau8822_readable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8822_REG_RESET ... NAU8822_REG_JACK_DETECT_CONTROL_1: + case NAU8822_REG_DAC_CONTROL ... NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME: + case NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME: + case NAU8822_REG_EQ1 ... NAU8822_REG_EQ5: + case NAU8822_REG_DAC_LIMITER_1 ... NAU8822_REG_DAC_LIMITER_2: + case NAU8822_REG_NOTCH_FILTER_1 ... NAU8822_REG_NOTCH_FILTER_4: + case NAU8822_REG_ALC_CONTROL_1 ...NAU8822_REG_PLL_K3: + case NAU8822_REG_3D_CONTROL: + case NAU8822_REG_RIGHT_SPEAKER_CONTROL: + case NAU8822_REG_INPUT_CONTROL ... NAU8822_REG_LEFT_ADC_BOOST_CONTROL: + case NAU8822_REG_RIGHT_ADC_BOOST_CONTROL ... NAU8822_REG_AUX1_MIXER: + case NAU8822_REG_POWER_MANAGEMENT_4 ... NAU8822_REG_DEVICE_ID: + case NAU8822_REG_DAC_DITHER: + case NAU8822_REG_ALC_ENHANCE_1 ... NAU8822_REG_MISC_CONTROL: + case NAU8822_REG_INPUT_TIEOFF ... NAU8822_REG_OUTPUT_TIEOFF: + return true; + default: + return false; + } +} + +static bool nau8822_writeable_reg(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8822_REG_RESET ... NAU8822_REG_JACK_DETECT_CONTROL_1: + case NAU8822_REG_DAC_CONTROL ... NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME: + case NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME: + case NAU8822_REG_EQ1 ... NAU8822_REG_EQ5: + case NAU8822_REG_DAC_LIMITER_1 ... NAU8822_REG_DAC_LIMITER_2: + case NAU8822_REG_NOTCH_FILTER_1 ... NAU8822_REG_NOTCH_FILTER_4: + case NAU8822_REG_ALC_CONTROL_1 ...NAU8822_REG_PLL_K3: + case NAU8822_REG_3D_CONTROL: + case NAU8822_REG_RIGHT_SPEAKER_CONTROL: + case NAU8822_REG_INPUT_CONTROL ... NAU8822_REG_LEFT_ADC_BOOST_CONTROL: + case NAU8822_REG_RIGHT_ADC_BOOST_CONTROL ... NAU8822_REG_AUX1_MIXER: + case NAU8822_REG_POWER_MANAGEMENT_4 ... NAU8822_REG_DEVICE_ID: + case NAU8822_REG_DAC_DITHER: + case NAU8822_REG_ALC_ENHANCE_1 ... NAU8822_REG_MISC_CONTROL: + case NAU8822_REG_INPUT_TIEOFF ... NAU8822_REG_OUTPUT_TIEOFF: + return true; + default: + return false; + } +} + +static bool nau8822_volatile(struct device *dev, unsigned int reg) +{ + switch (reg) { + case NAU8822_REG_RESET: + case NAU8822_REG_DEVICE_REVISION: + case NAU8822_REG_DEVICE_ID: + case NAU8822_REG_AGC_PEAK2PEAK: + case NAU8822_REG_AGC_PEAK_DETECT: + case NAU8822_REG_AUTOMUTE_CONTROL: + return true; + default: + return false; + } +} + +/* The EQ parameters get function is to get the 5 band equalizer control. + * The regmap raw read can't work here because regmap doesn't provide + * value format for value width of 9 bits. Therefore, the driver reads data + * from cache and makes value format according to the endianness of + * bytes type control element. + */ +static int nau8822_eq_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + int i, reg; + u16 reg_val, *val; + + val = (u16 *)ucontrol->value.bytes.data; + reg = NAU8822_REG_EQ1; + for (i = 0; i < params->max / sizeof(u16); i++) { + reg_val = snd_soc_component_read32(component, reg + i); + /* conversion of 16-bit integers between native CPU format + * and big endian format + */ + reg_val = cpu_to_be16(reg_val); + memcpy(val + i, &reg_val, sizeof(reg_val)); + } + + return 0; +} + +/* The EQ parameters put function is to make configuration of 5 band equalizer + * control. These configuration includes central frequency, equalizer gain, + * cut-off frequency, bandwidth control, and equalizer path. + * The regmap raw write can't work here because regmap doesn't provide + * register and value format for register with address 7 bits and value 9 bits. + * Therefore, the driver makes value format according to the endianness of + * bytes type control element and writes data to codec. + */ +static int nau8822_eq_put(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = + snd_soc_kcontrol_component(kcontrol); + struct soc_bytes_ext *params = (void *)kcontrol->private_value; + void *data; + u16 *val, value; + int i, reg, ret; + + data = kmemdup(ucontrol->value.bytes.data, + params->max, GFP_KERNEL | GFP_DMA); + if (!data) + return -ENOMEM; + + val = (u16 *)data; + reg = NAU8822_REG_EQ1; + for (i = 0; i < params->max / sizeof(u16); i++) { + /* conversion of 16-bit integers between native CPU format + * and big endian format + */ + value = be16_to_cpu(*(val + i)); + ret = snd_soc_component_write(component, reg + i, value); + if (ret) { + dev_err(component->dev, + "EQ configuration fail, register: %x ret: %d\n", + reg + i, ret); + kfree(data); + return ret; + } + } + kfree(data); + + return 0; +} + +static const char * const nau8822_companding[] = { + "Off", "NC", "u-law", "A-law"}; + +static const struct soc_enum nau8822_companding_adc_enum = + SOC_ENUM_SINGLE(NAU8822_REG_COMPANDING_CONTROL, NAU8822_ADCCM_SFT, + ARRAY_SIZE(nau8822_companding), nau8822_companding); + +static const struct soc_enum nau8822_companding_dac_enum = + SOC_ENUM_SINGLE(NAU8822_REG_COMPANDING_CONTROL, NAU8822_DACCM_SFT, + ARRAY_SIZE(nau8822_companding), nau8822_companding); + +static const char * const nau8822_eqmode[] = {"Capture", "Playback"}; + +static const struct soc_enum nau8822_eqmode_enum = + SOC_ENUM_SINGLE(NAU8822_REG_EQ1, NAU8822_EQM_SFT, + ARRAY_SIZE(nau8822_eqmode), nau8822_eqmode); + +static const char * const nau8822_alc1[] = {"Off", "Right", "Left", "Both"}; +static const char * const nau8822_alc3[] = {"Normal", "Limiter"}; + +static const struct soc_enum nau8822_alc_enable_enum = + SOC_ENUM_SINGLE(NAU8822_REG_ALC_CONTROL_1, NAU8822_ALCEN_SFT, + ARRAY_SIZE(nau8822_alc1), nau8822_alc1); + +static const struct soc_enum nau8822_alc_mode_enum = + SOC_ENUM_SINGLE(NAU8822_REG_ALC_CONTROL_3, NAU8822_ALCM_SFT, + ARRAY_SIZE(nau8822_alc3), nau8822_alc3); + +static const DECLARE_TLV_DB_SCALE(digital_tlv, -12750, 50, 1); +static const DECLARE_TLV_DB_SCALE(inpga_tlv, -1200, 75, 0); +static const DECLARE_TLV_DB_SCALE(spk_tlv, -5700, 100, 0); +static const DECLARE_TLV_DB_SCALE(pga_boost_tlv, 0, 2000, 0); +static const DECLARE_TLV_DB_SCALE(boost_tlv, -1500, 300, 1); +static const DECLARE_TLV_DB_SCALE(limiter_tlv, 0, 100, 0); + +static const struct snd_kcontrol_new nau8822_snd_controls[] = { + SOC_ENUM("ADC Companding", nau8822_companding_adc_enum), + SOC_ENUM("DAC Companding", nau8822_companding_dac_enum), + + SOC_ENUM("EQ Function", nau8822_eqmode_enum), + SND_SOC_BYTES_EXT("EQ Parameters", 10, + nau8822_eq_get, nau8822_eq_put), + + SOC_DOUBLE("DAC Inversion Switch", + NAU8822_REG_DAC_CONTROL, 0, 1, 1, 0), + SOC_DOUBLE_R_TLV("PCM Volume", + NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME, + NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME, 0, 255, 0, digital_tlv), + + SOC_SINGLE("High Pass Filter Switch", + NAU8822_REG_ADC_CONTROL, 8, 1, 0), + SOC_SINGLE("High Pass Cut Off", + NAU8822_REG_ADC_CONTROL, 4, 7, 0), + + SOC_DOUBLE("ADC Inversion Switch", + NAU8822_REG_ADC_CONTROL, 0, 1, 1, 0), + SOC_DOUBLE_R_TLV("ADC Volume", + NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME, + NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME, 0, 255, 0, digital_tlv), + + SOC_SINGLE("DAC Limiter Switch", + NAU8822_REG_DAC_LIMITER_1, 8, 1, 0), + SOC_SINGLE("DAC Limiter Decay", + NAU8822_REG_DAC_LIMITER_1, 4, 15, 0), + SOC_SINGLE("DAC Limiter Attack", + NAU8822_REG_DAC_LIMITER_1, 0, 15, 0), + SOC_SINGLE("DAC Limiter Threshold", + NAU8822_REG_DAC_LIMITER_2, 4, 7, 0), + SOC_SINGLE_TLV("DAC Limiter Volume", + NAU8822_REG_DAC_LIMITER_2, 0, 12, 0, limiter_tlv), + + SOC_ENUM("ALC Mode", nau8822_alc_mode_enum), + SOC_ENUM("ALC Enable Switch", nau8822_alc_enable_enum), + SOC_SINGLE("ALC Min Gain", + NAU8822_REG_ALC_CONTROL_1, 0, 7, 0), + SOC_SINGLE("ALC Max Gain", + NAU8822_REG_ALC_CONTROL_1, 3, 7, 0), + SOC_SINGLE("ALC Hold", + NAU8822_REG_ALC_CONTROL_2, 4, 10, 0), + SOC_SINGLE("ALC Target", + NAU8822_REG_ALC_CONTROL_2, 0, 15, 0), + SOC_SINGLE("ALC Decay", + NAU8822_REG_ALC_CONTROL_3, 4, 10, 0), + SOC_SINGLE("ALC Attack", + NAU8822_REG_ALC_CONTROL_3, 0, 10, 0), + SOC_SINGLE("ALC Noise Gate Switch", + NAU8822_REG_NOISE_GATE, 3, 1, 0), + SOC_SINGLE("ALC Noise Gate Threshold", + NAU8822_REG_NOISE_GATE, 0, 7, 0), + + SOC_DOUBLE_R("PGA ZC Switch", + NAU8822_REG_LEFT_INP_PGA_CONTROL, + NAU8822_REG_RIGHT_INP_PGA_CONTROL, + 7, 1, 0), + SOC_DOUBLE_R_TLV("PGA Volume", + NAU8822_REG_LEFT_INP_PGA_CONTROL, + NAU8822_REG_RIGHT_INP_PGA_CONTROL, 0, 63, 0, inpga_tlv), + + SOC_DOUBLE_R("Headphone ZC Switch", + NAU8822_REG_LHP_VOLUME, + NAU8822_REG_RHP_VOLUME, 7, 1, 0), + SOC_DOUBLE_R("Headphone Playback Switch", + NAU8822_REG_LHP_VOLUME, + NAU8822_REG_RHP_VOLUME, 6, 1, 1), + SOC_DOUBLE_R_TLV("Headphone Volume", + NAU8822_REG_LHP_VOLUME, + NAU8822_REG_RHP_VOLUME, 0, 63, 0, spk_tlv), + + SOC_DOUBLE_R("Speaker ZC Switch", + NAU8822_REG_LSPKOUT_VOLUME, + NAU8822_REG_RSPKOUT_VOLUME, 7, 1, 0), + SOC_DOUBLE_R("Speaker Playback Switch", + NAU8822_REG_LSPKOUT_VOLUME, + NAU8822_REG_RSPKOUT_VOLUME, 6, 1, 1), + SOC_DOUBLE_R_TLV("Speaker Volume", + NAU8822_REG_LSPKOUT_VOLUME, + NAU8822_REG_RSPKOUT_VOLUME, 0, 63, 0, spk_tlv), + + SOC_DOUBLE_R("AUXOUT Playback Switch", + NAU8822_REG_AUX2_MIXER, + NAU8822_REG_AUX1_MIXER, 6, 1, 1), + + SOC_DOUBLE_R_TLV("PGA Boost Volume", + NAU8822_REG_LEFT_ADC_BOOST_CONTROL, + NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 8, 1, 0, pga_boost_tlv), + SOC_DOUBLE_R_TLV("L2/R2 Boost Volume", + NAU8822_REG_LEFT_ADC_BOOST_CONTROL, + NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 4, 7, 0, boost_tlv), + SOC_DOUBLE_R_TLV("Aux Boost Volume", + NAU8822_REG_LEFT_ADC_BOOST_CONTROL, + NAU8822_REG_RIGHT_ADC_BOOST_CONTROL, 0, 7, 0, boost_tlv), + + SOC_SINGLE("DAC 128x Oversampling Switch", + NAU8822_REG_DAC_CONTROL, 5, 1, 0), + SOC_SINGLE("ADC 128x Oversampling Switch", + NAU8822_REG_ADC_CONTROL, 5, 1, 0), +}; + +/* LMAIN and RMAIN Mixer */ +static const struct snd_kcontrol_new nau8822_left_out_mixer[] = { + SOC_DAPM_SINGLE("LINMIX Switch", + NAU8822_REG_LEFT_MIXER_CONTROL, 1, 1, 0), + SOC_DAPM_SINGLE("LAUX Switch", + NAU8822_REG_LEFT_MIXER_CONTROL, 5, 1, 0), + SOC_DAPM_SINGLE("LDAC Switch", + NAU8822_REG_LEFT_MIXER_CONTROL, 0, 1, 0), + SOC_DAPM_SINGLE("RDAC Switch", + NAU8822_REG_OUTPUT_CONTROL, 5, 1, 0), +}; + +static const struct snd_kcontrol_new nau8822_right_out_mixer[] = { + SOC_DAPM_SINGLE("RINMIX Switch", + NAU8822_REG_RIGHT_MIXER_CONTROL, 1, 1, 0), + SOC_DAPM_SINGLE("RAUX Switch", + NAU8822_REG_RIGHT_MIXER_CONTROL, 5, 1, 0), + SOC_DAPM_SINGLE("RDAC Switch", + NAU8822_REG_RIGHT_MIXER_CONTROL, 0, 1, 0), + SOC_DAPM_SINGLE("LDAC Switch", + NAU8822_REG_OUTPUT_CONTROL, 6, 1, 0), +}; + +/* AUX1 and AUX2 Mixer */ +static const struct snd_kcontrol_new nau8822_auxout1_mixer[] = { + SOC_DAPM_SINGLE("RDAC Switch", NAU8822_REG_AUX1_MIXER, 0, 1, 0), + SOC_DAPM_SINGLE("RMIX Switch", NAU8822_REG_AUX1_MIXER, 1, 1, 0), + SOC_DAPM_SINGLE("RINMIX Switch", NAU8822_REG_AUX1_MIXER, 2, 1, 0), + SOC_DAPM_SINGLE("LDAC Switch", NAU8822_REG_AUX1_MIXER, 3, 1, 0), + SOC_DAPM_SINGLE("LMIX Switch", NAU8822_REG_AUX1_MIXER, 4, 1, 0), +}; + +static const struct snd_kcontrol_new nau8822_auxout2_mixer[] = { + SOC_DAPM_SINGLE("LDAC Switch", NAU8822_REG_AUX2_MIXER, 0, 1, 0), + SOC_DAPM_SINGLE("LMIX Switch", NAU8822_REG_AUX2_MIXER, 1, 1, 0), + SOC_DAPM_SINGLE("LINMIX Switch", NAU8822_REG_AUX2_MIXER, 2, 1, 0), + SOC_DAPM_SINGLE("AUX1MIX Output Switch", + NAU8822_REG_AUX2_MIXER, 3, 1, 0), +}; + +/* Input PGA */ +static const struct snd_kcontrol_new nau8822_left_input_mixer[] = { + SOC_DAPM_SINGLE("L2 Switch", NAU8822_REG_INPUT_CONTROL, 2, 1, 0), + SOC_DAPM_SINGLE("MicN Switch", NAU8822_REG_INPUT_CONTROL, 1, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", NAU8822_REG_INPUT_CONTROL, 0, 1, 0), +}; +static const struct snd_kcontrol_new nau8822_right_input_mixer[] = { + SOC_DAPM_SINGLE("R2 Switch", NAU8822_REG_INPUT_CONTROL, 6, 1, 0), + SOC_DAPM_SINGLE("MicN Switch", NAU8822_REG_INPUT_CONTROL, 5, 1, 0), + SOC_DAPM_SINGLE("MicP Switch", NAU8822_REG_INPUT_CONTROL, 4, 1, 0), +}; + +/* Loopback Switch */ +static const struct snd_kcontrol_new nau8822_loopback = + SOC_DAPM_SINGLE("Switch", NAU8822_REG_COMPANDING_CONTROL, + NAU8822_ADDAP_SFT, 1, 0); + +static int check_mclk_select_pll(struct snd_soc_dapm_widget *source, + struct snd_soc_dapm_widget *sink) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(source->dapm); + unsigned int value; + + value = snd_soc_component_read32(component, NAU8822_REG_CLOCKING); + + return (value & NAU8822_CLKM_MASK); +} + +static const struct snd_soc_dapm_widget nau8822_dapm_widgets[] = { + SND_SOC_DAPM_DAC("Left DAC", "Left HiFi Playback", + NAU8822_REG_POWER_MANAGEMENT_3, 0, 0), + SND_SOC_DAPM_DAC("Right DAC", "Right HiFi Playback", + NAU8822_REG_POWER_MANAGEMENT_3, 1, 0), + SND_SOC_DAPM_ADC("Left ADC", "Left HiFi Capture", + NAU8822_REG_POWER_MANAGEMENT_2, 0, 0), + SND_SOC_DAPM_ADC("Right ADC", "Right HiFi Capture", + NAU8822_REG_POWER_MANAGEMENT_2, 1, 0), + + SOC_MIXER_ARRAY("Left Output Mixer", + NAU8822_REG_POWER_MANAGEMENT_3, 2, 0, nau8822_left_out_mixer), + SOC_MIXER_ARRAY("Right Output Mixer", + NAU8822_REG_POWER_MANAGEMENT_3, 3, 0, nau8822_right_out_mixer), + SOC_MIXER_ARRAY("AUX1 Output Mixer", + NAU8822_REG_POWER_MANAGEMENT_1, 7, 0, nau8822_auxout1_mixer), + SOC_MIXER_ARRAY("AUX2 Output Mixer", + NAU8822_REG_POWER_MANAGEMENT_1, 6, 0, nau8822_auxout2_mixer), + + SOC_MIXER_ARRAY("Left Input Mixer", + NAU8822_REG_POWER_MANAGEMENT_2, + 2, 0, nau8822_left_input_mixer), + SOC_MIXER_ARRAY("Right Input Mixer", + NAU8822_REG_POWER_MANAGEMENT_2, + 3, 0, nau8822_right_input_mixer), + + SND_SOC_DAPM_PGA("Left Boost Mixer", + NAU8822_REG_POWER_MANAGEMENT_2, 4, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Boost Mixer", + NAU8822_REG_POWER_MANAGEMENT_2, 5, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Left Capture PGA", + NAU8822_REG_LEFT_INP_PGA_CONTROL, 6, 1, NULL, 0), + SND_SOC_DAPM_PGA("Right Capture PGA", + NAU8822_REG_RIGHT_INP_PGA_CONTROL, 6, 1, NULL, 0), + + SND_SOC_DAPM_PGA("Left Headphone Out", + NAU8822_REG_POWER_MANAGEMENT_2, 7, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Headphone Out", + NAU8822_REG_POWER_MANAGEMENT_2, 8, 0, NULL, 0), + + SND_SOC_DAPM_PGA("Left Speaker Out", + NAU8822_REG_POWER_MANAGEMENT_3, 6, 0, NULL, 0), + SND_SOC_DAPM_PGA("Right Speaker Out", + NAU8822_REG_POWER_MANAGEMENT_3, 5, 0, NULL, 0), + + SND_SOC_DAPM_PGA("AUX1 Out", + NAU8822_REG_POWER_MANAGEMENT_3, 8, 0, NULL, 0), + SND_SOC_DAPM_PGA("AUX2 Out", + NAU8822_REG_POWER_MANAGEMENT_3, 7, 0, NULL, 0), + + SND_SOC_DAPM_SUPPLY("Mic Bias", + NAU8822_REG_POWER_MANAGEMENT_1, 4, 0, NULL, 0), + SND_SOC_DAPM_SUPPLY("PLL", + NAU8822_REG_POWER_MANAGEMENT_1, 5, 0, NULL, 0), + + SND_SOC_DAPM_SWITCH("Digital Loopback", SND_SOC_NOPM, 0, 0, + &nau8822_loopback), + + SND_SOC_DAPM_INPUT("LMICN"), + SND_SOC_DAPM_INPUT("LMICP"), + SND_SOC_DAPM_INPUT("RMICN"), + SND_SOC_DAPM_INPUT("RMICP"), + SND_SOC_DAPM_INPUT("LAUX"), + SND_SOC_DAPM_INPUT("RAUX"), + SND_SOC_DAPM_INPUT("L2"), + SND_SOC_DAPM_INPUT("R2"), + SND_SOC_DAPM_OUTPUT("LHP"), + SND_SOC_DAPM_OUTPUT("RHP"), + SND_SOC_DAPM_OUTPUT("LSPK"), + SND_SOC_DAPM_OUTPUT("RSPK"), + SND_SOC_DAPM_OUTPUT("AUXOUT1"), + SND_SOC_DAPM_OUTPUT("AUXOUT2"), +}; + +static const struct snd_soc_dapm_route nau8822_dapm_routes[] = { + {"Right DAC", NULL, "PLL", check_mclk_select_pll}, + {"Left DAC", NULL, "PLL", check_mclk_select_pll}, + + /* LMAIN and RMAIN Mixer */ + {"Right Output Mixer", "LDAC Switch", "Left DAC"}, + {"Right Output Mixer", "RDAC Switch", "Right DAC"}, + {"Right Output Mixer", "RAUX Switch", "RAUX"}, + {"Right Output Mixer", "RINMIX Switch", "Right Boost Mixer"}, + + {"Left Output Mixer", "LDAC Switch", "Left DAC"}, + {"Left Output Mixer", "RDAC Switch", "Right DAC"}, + {"Left Output Mixer", "LAUX Switch", "LAUX"}, + {"Left Output Mixer", "LINMIX Switch", "Left Boost Mixer"}, + + /* AUX1 and AUX2 Mixer */ + {"AUX1 Output Mixer", "RDAC Switch", "Right DAC"}, + {"AUX1 Output Mixer", "RMIX Switch", "Right Output Mixer"}, + {"AUX1 Output Mixer", "RINMIX Switch", "Right Boost Mixer"}, + {"AUX1 Output Mixer", "LDAC Switch", "Left DAC"}, + {"AUX1 Output Mixer", "LMIX Switch", "Left Output Mixer"}, + + {"AUX2 Output Mixer", "LDAC Switch", "Left DAC"}, + {"AUX2 Output Mixer", "LMIX Switch", "Left Output Mixer"}, + {"AUX2 Output Mixer", "LINMIX Switch", "Left Boost Mixer"}, + {"AUX2 Output Mixer", "AUX1MIX Output Switch", "AUX1 Output Mixer"}, + + /* Outputs */ + {"Right Headphone Out", NULL, "Right Output Mixer"}, + {"RHP", NULL, "Right Headphone Out"}, + + {"Left Headphone Out", NULL, "Left Output Mixer"}, + {"LHP", NULL, "Left Headphone Out"}, + + {"Right Speaker Out", NULL, "Right Output Mixer"}, + {"RSPK", NULL, "Right Speaker Out"}, + + {"Left Speaker Out", NULL, "Left Output Mixer"}, + {"LSPK", NULL, "Left Speaker Out"}, + + {"AUX1 Out", NULL, "AUX1 Output Mixer"}, + {"AUX2 Out", NULL, "AUX2 Output Mixer"}, + {"AUXOUT1", NULL, "AUX1 Out"}, + {"AUXOUT2", NULL, "AUX2 Out"}, + + /* Boost Mixer */ + {"Right ADC", NULL, "PLL", check_mclk_select_pll}, + {"Left ADC", NULL, "PLL", check_mclk_select_pll}, + + {"Right ADC", NULL, "Right Boost Mixer"}, + + {"Right Boost Mixer", NULL, "RAUX"}, + {"Right Boost Mixer", NULL, "Right Capture PGA"}, + {"Right Boost Mixer", NULL, "R2"}, + + {"Left ADC", NULL, "Left Boost Mixer"}, + + {"Left Boost Mixer", NULL, "LAUX"}, + {"Left Boost Mixer", NULL, "Left Capture PGA"}, + {"Left Boost Mixer", NULL, "L2"}, + + /* Input PGA */ + {"Right Capture PGA", NULL, "Right Input Mixer"}, + {"Left Capture PGA", NULL, "Left Input Mixer"}, + + /* Enable Microphone Power */ + {"Right Capture PGA", NULL, "Mic Bias"}, + {"Left Capture PGA", NULL, "Mic Bias"}, + + {"Right Input Mixer", "R2 Switch", "R2"}, + {"Right Input Mixer", "MicN Switch", "RMICN"}, + {"Right Input Mixer", "MicP Switch", "RMICP"}, + + {"Left Input Mixer", "L2 Switch", "L2"}, + {"Left Input Mixer", "MicN Switch", "LMICN"}, + {"Left Input Mixer", "MicP Switch", "LMICP"}, + + /* Digital Loopback */ + {"Digital Loopback", "Switch", "Left ADC"}, + {"Digital Loopback", "Switch", "Right ADC"}, + {"Left DAC", NULL, "Digital Loopback"}, + {"Right DAC", NULL, "Digital Loopback"}, +}; + +static int nau8822_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *component = dai->component; + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + + nau8822->div_id = clk_id; + nau8822->sysclk = freq; + dev_dbg(component->dev, "master sysclk %dHz, source %s\n", freq, + clk_id == NAU8822_CLK_PLL ? "PLL" : "MCLK"); + + return 0; +} + +static int nau8822_calc_pll(unsigned int pll_in, unsigned int fs, + struct nau8822_pll *pll_param) +{ + u64 f2, f2_max, pll_ratio; + int i, scal_sel; + + if (pll_in > NAU_PLL_REF_MAX || pll_in < NAU_PLL_REF_MIN) + return -EINVAL; + f2_max = 0; + scal_sel = ARRAY_SIZE(nau8822_mclk_scaler); + + for (i = 0; i < scal_sel; i++) { + f2 = 256 * fs * 4 * nau8822_mclk_scaler[i] / 10; + if (f2 > NAU_PLL_FREQ_MIN && f2 < NAU_PLL_FREQ_MAX && + f2_max < f2) { + f2_max = f2; + scal_sel = i; + } + } + + if (ARRAY_SIZE(nau8822_mclk_scaler) == scal_sel) + return -EINVAL; + pll_param->mclk_scaler = scal_sel; + f2 = f2_max; + + /* Calculate the PLL 4-bit integer input and the PLL 24-bit fractional + * input; round up the 24+4bit. + */ + pll_ratio = div_u64(f2 << 28, pll_in); + pll_param->pre_factor = 0; + if (((pll_ratio >> 28) & 0xF) < NAU_PLL_OPTOP_MIN) { + pll_ratio <<= 1; + pll_param->pre_factor = 1; + } + pll_param->pll_int = (pll_ratio >> 28) & 0xF; + pll_param->pll_frac = ((pll_ratio & 0xFFFFFFF) >> 4); + + return 0; +} + +static int nau8822_config_clkdiv(struct snd_soc_dai *dai, int div, int rate) +{ + struct snd_soc_component *component = dai->component; + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + struct nau8822_pll *pll = &nau8822->pll; + int i, sclk, imclk; + + switch (nau8822->div_id) { + case NAU8822_CLK_MCLK: + /* Configure the master clock prescaler div to make system + * clock to approximate the internal master clock (IMCLK); + * and large or equal to IMCLK. + */ + div = 0; + imclk = rate * 256; + for (i = 1; i < ARRAY_SIZE(nau8822_mclk_scaler); i++) { + sclk = (nau8822->sysclk * 10) / nau8822_mclk_scaler[i]; + if (sclk < imclk) + break; + div = i; + } + dev_dbg(component->dev, "master clock prescaler %x for fs %d\n", + div, rate); + + /* master clock from MCLK and disable PLL */ + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK, + (div << NAU8822_MCLKSEL_SFT)); + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK, + NAU8822_CLKM_MCLK); + break; + + case NAU8822_CLK_PLL: + /* master clock from PLL and enable PLL */ + if (pll->mclk_scaler != div) { + dev_err(component->dev, + "master clock prescaler not meet PLL parameters\n"); + return -EINVAL; + } + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK, + (div << NAU8822_MCLKSEL_SFT)); + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK, + NAU8822_CLKM_PLL); + break; + + default: + return -EINVAL; + } + + return 0; +} + +static int nau8822_set_pll(struct snd_soc_dai *dai, int pll_id, int source, + unsigned int freq_in, unsigned int freq_out) +{ + struct snd_soc_component *component = dai->component; + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + struct nau8822_pll *pll_param = &nau8822->pll; + int ret, fs; + + fs = freq_out / 256; + + ret = nau8822_calc_pll(freq_in, fs, pll_param); + if (ret < 0) { + dev_err(component->dev, "Unsupported input clock %d\n", + freq_in); + return ret; + } + + dev_info(component->dev, + "pll_int=%x pll_frac=%x mclk_scaler=%x pre_factor=%x\n", + pll_param->pll_int, pll_param->pll_frac, + pll_param->mclk_scaler, pll_param->pre_factor); + + snd_soc_component_update_bits(component, + NAU8822_REG_PLL_N, NAU8822_PLLMCLK_DIV2 | NAU8822_PLLN_MASK, + (pll_param->pre_factor ? NAU8822_PLLMCLK_DIV2 : 0) | + pll_param->pll_int); + snd_soc_component_write(component, + NAU8822_REG_PLL_K1, (pll_param->pll_frac >> NAU8822_PLLK1_SFT) & + NAU8822_PLLK1_MASK); + snd_soc_component_write(component, + NAU8822_REG_PLL_K2, (pll_param->pll_frac >> NAU8822_PLLK2_SFT) & + NAU8822_PLLK2_MASK); + snd_soc_component_write(component, + NAU8822_REG_PLL_K3, pll_param->pll_frac & NAU8822_PLLK3_MASK); + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_MCLKSEL_MASK, + pll_param->mclk_scaler << NAU8822_MCLKSEL_SFT); + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_CLKM_MASK, NAU8822_CLKM_PLL); + + return 0; +} + +static int nau8822_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *component = dai->component; + u16 ctrl1_val = 0, ctrl2_val = 0; + + dev_dbg(component->dev, "%s\n", __func__); + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + ctrl2_val |= 1; + break; + case SND_SOC_DAIFMT_CBS_CFS: + ctrl2_val &= ~1; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + ctrl1_val |= 0x10; + break; + case SND_SOC_DAIFMT_RIGHT_J: + break; + case SND_SOC_DAIFMT_LEFT_J: + ctrl1_val |= 0x8; + break; + case SND_SOC_DAIFMT_DSP_A: + ctrl1_val |= 0x18; + break; + default: + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_INV_MASK) { + case SND_SOC_DAIFMT_NB_NF: + break; + case SND_SOC_DAIFMT_IB_IF: + ctrl1_val |= 0x180; + break; + case SND_SOC_DAIFMT_IB_NF: + ctrl1_val |= 0x100; + break; + case SND_SOC_DAIFMT_NB_IF: + ctrl1_val |= 0x80; + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, + NAU8822_REG_AUDIO_INTERFACE, + NAU8822_AIFMT_MASK | NAU8822_LRP_MASK | NAU8822_BCLKP_MASK, + ctrl1_val); + snd_soc_component_update_bits(component, + NAU8822_REG_CLOCKING, NAU8822_CLKIOEN_MASK, ctrl2_val); + + return 0; +} + +static int nau8822_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + int val_len = 0, val_rate = 0; + + switch (params_format(params)) { + case SNDRV_PCM_FORMAT_S16_LE: + break; + case SNDRV_PCM_FORMAT_S20_3LE: + val_len |= NAU8822_WLEN_20; + break; + case SNDRV_PCM_FORMAT_S24_LE: + val_len |= NAU8822_WLEN_24; + break; + case SNDRV_PCM_FORMAT_S32_LE: + val_len |= NAU8822_WLEN_32; + break; + default: + return -EINVAL; + } + + switch (params_rate(params)) { + case 8000: + val_rate |= NAU8822_SMPLR_8K; + break; + case 11025: + val_rate |= NAU8822_SMPLR_12K; + break; + case 16000: + val_rate |= NAU8822_SMPLR_16K; + break; + case 22050: + val_rate |= NAU8822_SMPLR_24K; + break; + case 32000: + val_rate |= NAU8822_SMPLR_32K; + break; + case 44100: + case 48000: + break; + default: + return -EINVAL; + } + + snd_soc_component_update_bits(component, + NAU8822_REG_AUDIO_INTERFACE, NAU8822_WLEN_MASK, val_len); + snd_soc_component_update_bits(component, + NAU8822_REG_ADDITIONAL_CONTROL, NAU8822_SMPLR_MASK, val_rate); + + /* If the master clock is from MCLK, provide the runtime FS for driver + * to get the master clock prescaler configuration. + */ + if (nau8822->div_id == NAU8822_CLK_MCLK) + nau8822_config_clkdiv(dai, 0, params_rate(params)); + + return 0; +} + +static int nau8822_mute(struct snd_soc_dai *dai, int mute) +{ + struct snd_soc_component *component = dai->component; + + dev_dbg(component->dev, "%s: %d\n", __func__, mute); + + if (mute) + snd_soc_component_update_bits(component, + NAU8822_REG_DAC_CONTROL, 0x40, 0x40); + else + snd_soc_component_update_bits(component, + NAU8822_REG_DAC_CONTROL, 0x40, 0); + + return 0; +} + +static int nau8822_set_bias_level(struct snd_soc_component *component, + enum snd_soc_bias_level level) +{ + switch (level) { + case SND_SOC_BIAS_ON: + case SND_SOC_BIAS_PREPARE: + snd_soc_component_update_bits(component, + NAU8822_REG_POWER_MANAGEMENT_1, + NAU8822_REFIMP_MASK, NAU8822_REFIMP_80K); + break; + + case SND_SOC_BIAS_STANDBY: + snd_soc_component_update_bits(component, + NAU8822_REG_POWER_MANAGEMENT_1, + NAU8822_IOBUF_EN | NAU8822_ABIAS_EN, + NAU8822_IOBUF_EN | NAU8822_ABIAS_EN); + + if (snd_soc_component_get_bias_level(component) == + SND_SOC_BIAS_OFF) { + snd_soc_component_update_bits(component, + NAU8822_REG_POWER_MANAGEMENT_1, + NAU8822_REFIMP_MASK, NAU8822_REFIMP_3K); + mdelay(100); + } + snd_soc_component_update_bits(component, + NAU8822_REG_POWER_MANAGEMENT_1, + NAU8822_REFIMP_MASK, NAU8822_REFIMP_300K); + break; + + case SND_SOC_BIAS_OFF: + snd_soc_component_write(component, + NAU8822_REG_POWER_MANAGEMENT_1, 0); + snd_soc_component_write(component, + NAU8822_REG_POWER_MANAGEMENT_2, 0); + snd_soc_component_write(component, + NAU8822_REG_POWER_MANAGEMENT_3, 0); + break; + } + + dev_dbg(component->dev, "%s: %d\n", __func__, level); + + return 0; +} + +#define NAU8822_RATES (SNDRV_PCM_RATE_8000_48000) + +#define NAU8822_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \ + SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE) + +static const struct snd_soc_dai_ops nau8822_dai_ops = { + .hw_params = nau8822_hw_params, + .digital_mute = nau8822_mute, + .set_fmt = nau8822_set_dai_fmt, + .set_sysclk = nau8822_set_dai_sysclk, + .set_pll = nau8822_set_pll, +}; + +static struct snd_soc_dai_driver nau8822_dai = { + .name = "nau8822-hifi", + .playback = { + .stream_name = "Playback", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8822_RATES, + .formats = NAU8822_FORMATS, + }, + .capture = { + .stream_name = "Capture", + .channels_min = 1, + .channels_max = 2, + .rates = NAU8822_RATES, + .formats = NAU8822_FORMATS, + }, + .ops = &nau8822_dai_ops, + .symmetric_rates = 1, +}; + +static int nau8822_suspend(struct snd_soc_component *component) +{ + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_OFF); + + regcache_mark_dirty(nau8822->regmap); + + return 0; +} + +static int nau8822_resume(struct snd_soc_component *component) +{ + struct nau8822 *nau8822 = snd_soc_component_get_drvdata(component); + + regcache_sync(nau8822->regmap); + + snd_soc_component_force_bias_level(component, SND_SOC_BIAS_STANDBY); + + return 0; +} + +/* + * These registers contain an "update" bit - bit 8. This means, for example, + * that one can write new DAC digital volume for both channels, but only when + * the update bit is set, will also the volume be updated - simultaneously for + * both channels. + */ +static const int update_reg[] = { + NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME, + NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME, + NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME, + NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME, + NAU8822_REG_LEFT_INP_PGA_CONTROL, + NAU8822_REG_RIGHT_INP_PGA_CONTROL, + NAU8822_REG_LHP_VOLUME, + NAU8822_REG_RHP_VOLUME, + NAU8822_REG_LSPKOUT_VOLUME, + NAU8822_REG_RSPKOUT_VOLUME, +}; + +static int nau8822_probe(struct snd_soc_component *component) +{ + int i; + + /* + * Set the update bit in all registers, that have one. This way all + * writes to those registers will also cause the update bit to be + * written. + */ + for (i = 0; i < ARRAY_SIZE(update_reg); i++) + snd_soc_component_update_bits(component, + update_reg[i], 0x100, 0x100); + + return 0; +} + +static const struct snd_soc_component_driver soc_component_dev_nau8822 = { + .probe = nau8822_probe, + .suspend = nau8822_suspend, + .resume = nau8822_resume, + .set_bias_level = nau8822_set_bias_level, + .controls = nau8822_snd_controls, + .num_controls = ARRAY_SIZE(nau8822_snd_controls), + .dapm_widgets = nau8822_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(nau8822_dapm_widgets), + .dapm_routes = nau8822_dapm_routes, + .num_dapm_routes = ARRAY_SIZE(nau8822_dapm_routes), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + +static const struct regmap_config nau8822_regmap_config = { + .reg_bits = 7, + .val_bits = 9, + + .max_register = NAU8822_REG_MAX_REGISTER, + .volatile_reg = nau8822_volatile, + + .readable_reg = nau8822_readable_reg, + .writeable_reg = nau8822_writeable_reg, + + .cache_type = REGCACHE_RBTREE, + .reg_defaults = nau8822_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(nau8822_reg_defaults), +}; + +static int nau8822_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct device *dev = &i2c->dev; + struct nau8822 *nau8822 = dev_get_platdata(dev); + int ret; + + if (!nau8822) { + nau8822 = devm_kzalloc(dev, sizeof(*nau8822), GFP_KERNEL); + if (nau8822 == NULL) + return -ENOMEM; + } + i2c_set_clientdata(i2c, nau8822); + + nau8822->regmap = devm_regmap_init_i2c(i2c, &nau8822_regmap_config); + if (IS_ERR(nau8822->regmap)) { + ret = PTR_ERR(nau8822->regmap); + dev_err(&i2c->dev, "Failed to allocate regmap: %d\n", ret); + return ret; + } + nau8822->dev = dev; + + /* Reset the codec */ + ret = regmap_write(nau8822->regmap, NAU8822_REG_RESET, 0x00); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to issue reset: %d\n", ret); + return ret; + } + + ret = devm_snd_soc_register_component(dev, &soc_component_dev_nau8822, + &nau8822_dai, 1); + if (ret != 0) { + dev_err(&i2c->dev, "Failed to register CODEC: %d\n", ret); + return ret; + } + + return 0; +} + +static const struct i2c_device_id nau8822_i2c_id[] = { + { "nau8822", 0 }, + { } +}; +MODULE_DEVICE_TABLE(i2c, nau8822_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id nau8822_of_match[] = { + { .compatible = "nuvoton,nau8822", }, + { } +}; +MODULE_DEVICE_TABLE(of, nau8822_of_match); +#endif + +static struct i2c_driver nau8822_i2c_driver = { + .driver = { + .name = "nau8822", + .of_match_table = of_match_ptr(nau8822_of_match), + }, + .probe = nau8822_i2c_probe, + .id_table = nau8822_i2c_id, +}; +module_i2c_driver(nau8822_i2c_driver); + +MODULE_DESCRIPTION("ASoC NAU8822 codec driver"); +MODULE_AUTHOR("David Lin <ctlin0@nuvoton.com>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/nau8822.h b/sound/soc/codecs/nau8822.h @@ -0,0 +1,204 @@ +/* + * nau8822.h -- NAU8822 Soc Audio Codec driver + * + * Author: David Lin <ctlin0@nuvoton.com> + * Co-author: John Hsu <kchsu0@nuvoton.com> + * Co-author: Seven Li <wtli@nuvoton.com> + * + * This program is free software; you can redistribute it and/or modify + * it under the terms of the GNU General Public License version 2 as + * published by the Free Software Foundation. + */ + +#ifndef __NAU8822_H__ +#define __NAU8822_H__ + +#define NAU8822_REG_RESET 0x00 +#define NAU8822_REG_POWER_MANAGEMENT_1 0x01 +#define NAU8822_REG_POWER_MANAGEMENT_2 0x02 +#define NAU8822_REG_POWER_MANAGEMENT_3 0x03 +#define NAU8822_REG_AUDIO_INTERFACE 0x04 +#define NAU8822_REG_COMPANDING_CONTROL 0x05 +#define NAU8822_REG_CLOCKING 0x06 +#define NAU8822_REG_ADDITIONAL_CONTROL 0x07 +#define NAU8822_REG_GPIO_CONTROL 0x08 +#define NAU8822_REG_JACK_DETECT_CONTROL_1 0x09 +#define NAU8822_REG_DAC_CONTROL 0x0A +#define NAU8822_REG_LEFT_DAC_DIGITAL_VOLUME 0x0B +#define NAU8822_REG_RIGHT_DAC_DIGITAL_VOLUME 0x0C +#define NAU8822_REG_JACK_DETECT_CONTROL_2 0x0D +#define NAU8822_REG_ADC_CONTROL 0x0E +#define NAU8822_REG_LEFT_ADC_DIGITAL_VOLUME 0x0F +#define NAU8822_REG_RIGHT_ADC_DIGITAL_VOLUME 0x10 +#define NAU8822_REG_EQ1 0x12 +#define NAU8822_REG_EQ2 0x13 +#define NAU8822_REG_EQ3 0x14 +#define NAU8822_REG_EQ4 0x15 +#define NAU8822_REG_EQ5 0x16 +#define NAU8822_REG_DAC_LIMITER_1 0x18 +#define NAU8822_REG_DAC_LIMITER_2 0x19 +#define NAU8822_REG_NOTCH_FILTER_1 0x1B +#define NAU8822_REG_NOTCH_FILTER_2 0x1C +#define NAU8822_REG_NOTCH_FILTER_3 0x1D +#define NAU8822_REG_NOTCH_FILTER_4 0x1E +#define NAU8822_REG_ALC_CONTROL_1 0x20 +#define NAU8822_REG_ALC_CONTROL_2 0x21 +#define NAU8822_REG_ALC_CONTROL_3 0x22 +#define NAU8822_REG_NOISE_GATE 0x23 +#define NAU8822_REG_PLL_N 0x24 +#define NAU8822_REG_PLL_K1 0x25 +#define NAU8822_REG_PLL_K2 0x26 +#define NAU8822_REG_PLL_K3 0x27 +#define NAU8822_REG_3D_CONTROL 0x29 +#define NAU8822_REG_RIGHT_SPEAKER_CONTROL 0x2B +#define NAU8822_REG_INPUT_CONTROL 0x2C +#define NAU8822_REG_LEFT_INP_PGA_CONTROL 0x2D +#define NAU8822_REG_RIGHT_INP_PGA_CONTROL 0x2E +#define NAU8822_REG_LEFT_ADC_BOOST_CONTROL 0x2F +#define NAU8822_REG_RIGHT_ADC_BOOST_CONTROL 0x30 +#define NAU8822_REG_OUTPUT_CONTROL 0x31 +#define NAU8822_REG_LEFT_MIXER_CONTROL 0x32 +#define NAU8822_REG_RIGHT_MIXER_CONTROL 0x33 +#define NAU8822_REG_LHP_VOLUME 0x34 +#define NAU8822_REG_RHP_VOLUME 0x35 +#define NAU8822_REG_LSPKOUT_VOLUME 0x36 +#define NAU8822_REG_RSPKOUT_VOLUME 0x37 +#define NAU8822_REG_AUX2_MIXER 0x38 +#define NAU8822_REG_AUX1_MIXER 0x39 +#define NAU8822_REG_POWER_MANAGEMENT_4 0x3A +#define NAU8822_REG_LEFT_TIME_SLOT 0x3B +#define NAU8822_REG_MISC 0x3C +#define NAU8822_REG_RIGHT_TIME_SLOT 0x3D +#define NAU8822_REG_DEVICE_REVISION 0x3E +#define NAU8822_REG_DEVICE_ID 0x3F +#define NAU8822_REG_DAC_DITHER 0x41 +#define NAU8822_REG_ALC_ENHANCE_1 0x46 +#define NAU8822_REG_ALC_ENHANCE_2 0x47 +#define NAU8822_REG_192KHZ_SAMPLING 0x48 +#define NAU8822_REG_MISC_CONTROL 0x49 +#define NAU8822_REG_INPUT_TIEOFF 0x4A +#define NAU8822_REG_POWER_REDUCTION 0x4B +#define NAU8822_REG_AGC_PEAK2PEAK 0x4C +#define NAU8822_REG_AGC_PEAK_DETECT 0x4D +#define NAU8822_REG_AUTOMUTE_CONTROL 0x4E +#define NAU8822_REG_OUTPUT_TIEOFF 0x4F +#define NAU8822_REG_MAX_REGISTER NAU8822_REG_OUTPUT_TIEOFF + +/* NAU8822_REG_POWER_MANAGEMENT_1 (0x1) */ +#define NAU8822_REFIMP_MASK 0x3 +#define NAU8822_REFIMP_80K 0x1 +#define NAU8822_REFIMP_300K 0x2 +#define NAU8822_REFIMP_3K 0x3 +#define NAU8822_IOBUF_EN (0x1 << 2) +#define NAU8822_ABIAS_EN (0x1 << 3) + +/* NAU8822_REG_AUDIO_INTERFACE (0x4) */ +#define NAU8822_AIFMT_MASK (0x3 << 3) +#define NAU8822_WLEN_MASK (0x3 << 5) +#define NAU8822_WLEN_20 (0x1 << 5) +#define NAU8822_WLEN_24 (0x2 << 5) +#define NAU8822_WLEN_32 (0x3 << 5) +#define NAU8822_LRP_MASK (0x1 << 7) +#define NAU8822_BCLKP_MASK (0x1 << 8) + +/* NAU8822_REG_COMPANDING_CONTROL (0x5) */ +#define NAU8822_ADDAP_SFT 0 +#define NAU8822_ADCCM_SFT 1 +#define NAU8822_DACCM_SFT 3 + +/* NAU8822_REG_CLOCKING (0x6) */ +#define NAU8822_CLKIOEN_MASK 0x1 +#define NAU8822_MCLKSEL_SFT 5 +#define NAU8822_MCLKSEL_MASK (0x7 << 5) +#define NAU8822_BCLKSEL_SFT 2 +#define NAU8822_BCLKSEL_MASK (0x7 << 2) +#define NAU8822_CLKM_MASK (0x1 << 8) +#define NAU8822_CLKM_MCLK (0x0 << 8) +#define NAU8822_CLKM_PLL (0x1 << 8) + +/* NAU8822_REG_ADDITIONAL_CONTROL (0x08) */ +#define NAU8822_SMPLR_SFT 1 +#define NAU8822_SMPLR_MASK (0x7 << 1) +#define NAU8822_SMPLR_48K (0x0 << 1) +#define NAU8822_SMPLR_32K (0x1 << 1) +#define NAU8822_SMPLR_24K (0x2 << 1) +#define NAU8822_SMPLR_16K (0x3 << 1) +#define NAU8822_SMPLR_12K (0x4 << 1) +#define NAU8822_SMPLR_8K (0x5 << 1) + +/* NAU8822_REG_EQ1 (0x12) */ +#define NAU8822_EQ1GC_SFT 0 +#define NAU8822_EQ1CF_SFT 5 +#define NAU8822_EQM_SFT 8 + +/* NAU8822_REG_EQ2 (0x13) */ +#define NAU8822_EQ2GC_SFT 0 +#define NAU8822_EQ2CF_SFT 5 +#define NAU8822_EQ2BW_SFT 8 + +/* NAU8822_REG_EQ3 (0x14) */ +#define NAU8822_EQ3GC_SFT 0 +#define NAU8822_EQ3CF_SFT 5 +#define NAU8822_EQ3BW_SFT 8 + +/* NAU8822_REG_EQ4 (0x15) */ +#define NAU8822_EQ4GC_SFT 0 +#define NAU8822_EQ4CF_SFT 5 +#define NAU8822_EQ4BW_SFT 8 + +/* NAU8822_REG_EQ5 (0x16) */ +#define NAU8822_EQ5GC_SFT 0 +#define NAU8822_EQ5CF_SFT 5 + +/* NAU8822_REG_ALC_CONTROL_1 (0x20) */ +#define NAU8822_ALCMINGAIN_SFT 0 +#define NAU8822_ALCMXGAIN_SFT 3 +#define NAU8822_ALCEN_SFT 7 + +/* NAU8822_REG_ALC_CONTROL_2 (0x21) */ +#define NAU8822_ALCSL_SFT 0 +#define NAU8822_ALCHT_SFT 4 + +/* NAU8822_REG_ALC_CONTROL_3 (0x22) */ +#define NAU8822_ALCATK_SFT 0 +#define NAU8822_ALCDCY_SFT 4 +#define NAU8822_ALCM_SFT 8 + +/* NAU8822_REG_PLL_N (0x24) */ +#define NAU8822_PLLMCLK_DIV2 (0x1 << 4) +#define NAU8822_PLLN_MASK 0xF + +#define NAU8822_PLLK1_SFT 18 +#define NAU8822_PLLK1_MASK 0x3F + +/* NAU8822_REG_PLL_K2 (0x26) */ +#define NAU8822_PLLK2_SFT 9 +#define NAU8822_PLLK2_MASK 0x1FF + +/* NAU8822_REG_PLL_K3 (0x27) */ +#define NAU8822_PLLK3_MASK 0x1FF + +/* System Clock Source */ +enum { + NAU8822_CLK_MCLK, + NAU8822_CLK_PLL, +}; + +struct nau8822_pll { + int pre_factor; + int mclk_scaler; + int pll_frac; + int pll_int; +}; + +/* Codec Private Data */ +struct nau8822 { + struct device *dev; + struct regmap *regmap; + int mclk_idx; + struct nau8822_pll pll; + int sysclk; + int div_id; +}; + +#endif /* __NAU8822_H__ */ diff --git a/sound/soc/codecs/pcm186x.c b/sound/soc/codecs/pcm186x.c @@ -401,7 +401,8 @@ static int pcm186x_set_fmt(struct snd_soc_dai *dai, unsigned int format) break; case SND_SOC_DAIFMT_DSP_A: priv->tdm_offset += 1; - /* Fall through... DSP_A uses the same basic config as DSP_B + /* fall through */ + /* DSP_A uses the same basic config as DSP_B * except we need to shift the TDM output by one BCK cycle */ case SND_SOC_DAIFMT_DSP_B: diff --git a/sound/soc/codecs/pcm3060-i2c.c b/sound/soc/codecs/pcm3060-i2c.c @@ -0,0 +1,60 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// PCM3060 I2C driver +// +// Copyright (C) 2018 Kirill Marinushkin <kmarinushkin@birdec.tech> + +#include <linux/i2c.h> +#include <linux/module.h> +#include <sound/soc.h> + +#include "pcm3060.h" + +static int pcm3060_i2c_probe(struct i2c_client *i2c, + const struct i2c_device_id *id) +{ + struct pcm3060_priv *priv; + + priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + i2c_set_clientdata(i2c, priv); + + priv->regmap = devm_regmap_init_i2c(i2c, &pcm3060_regmap); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + return pcm3060_probe(&i2c->dev); +} + +static const struct i2c_device_id pcm3060_i2c_id[] = { + { .name = "pcm3060" }, + { }, +}; +MODULE_DEVICE_TABLE(i2c, pcm3060_i2c_id); + +#ifdef CONFIG_OF +static const struct of_device_id pcm3060_of_match[] = { + { .compatible = "ti,pcm3060" }, + { }, +}; +MODULE_DEVICE_TABLE(of, pcm3060_of_match); +#endif /* CONFIG_OF */ + +static struct i2c_driver pcm3060_i2c_driver = { + .driver = { + .name = "pcm3060", +#ifdef CONFIG_OF + .of_match_table = pcm3060_of_match, +#endif /* CONFIG_OF */ + }, + .id_table = pcm3060_i2c_id, + .probe = pcm3060_i2c_probe, +}; + +module_i2c_driver(pcm3060_i2c_driver); + +MODULE_DESCRIPTION("PCM3060 I2C driver"); +MODULE_AUTHOR("Kirill Marinushkin <kmarinushkin@birdec.tech>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm3060-spi.c b/sound/soc/codecs/pcm3060-spi.c @@ -0,0 +1,59 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// PCM3060 SPI driver +// +// Copyright (C) 2018 Kirill Marinushkin <kmarinushkin@birdec.tech> + +#include <linux/module.h> +#include <linux/spi/spi.h> +#include <sound/soc.h> + +#include "pcm3060.h" + +static int pcm3060_spi_probe(struct spi_device *spi) +{ + struct pcm3060_priv *priv; + + priv = devm_kzalloc(&spi->dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + spi_set_drvdata(spi, priv); + + priv->regmap = devm_regmap_init_spi(spi, &pcm3060_regmap); + if (IS_ERR(priv->regmap)) + return PTR_ERR(priv->regmap); + + return pcm3060_probe(&spi->dev); +} + +static const struct spi_device_id pcm3060_spi_id[] = { + { .name = "pcm3060" }, + { }, +}; +MODULE_DEVICE_TABLE(spi, pcm3060_spi_id); + +#ifdef CONFIG_OF +static const struct of_device_id pcm3060_of_match[] = { + { .compatible = "ti,pcm3060" }, + { }, +}; +MODULE_DEVICE_TABLE(of, pcm3060_of_match); +#endif /* CONFIG_OF */ + +static struct spi_driver pcm3060_spi_driver = { + .driver = { + .name = "pcm3060", +#ifdef CONFIG_OF + .of_match_table = pcm3060_of_match, +#endif /* CONFIG_OF */ + }, + .id_table = pcm3060_spi_id, + .probe = pcm3060_spi_probe, +}; + +module_spi_driver(pcm3060_spi_driver); + +MODULE_DESCRIPTION("PCM3060 SPI driver"); +MODULE_AUTHOR("Kirill Marinushkin <kmarinushkin@birdec.tech>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm3060.c b/sound/soc/codecs/pcm3060.c @@ -0,0 +1,295 @@ +// SPDX-License-Identifier: GPL-2.0 +// +// PCM3060 codec driver +// +// Copyright (C) 2018 Kirill Marinushkin <kmarinushkin@birdec.tech> + +#include <linux/module.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include <sound/tlv.h> + +#include "pcm3060.h" + +/* dai */ + +static int pcm3060_set_sysclk(struct snd_soc_dai *dai, int clk_id, + unsigned int freq, int dir) +{ + struct snd_soc_component *comp = dai->component; + struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp); + + if (dir != SND_SOC_CLOCK_IN) { + dev_err(comp->dev, "unsupported sysclock dir: %d\n", dir); + return -EINVAL; + } + + priv->dai[dai->id].sclk_freq = freq; + + return 0; +} + +static int pcm3060_set_fmt(struct snd_soc_dai *dai, unsigned int fmt) +{ + struct snd_soc_component *comp = dai->component; + struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp); + unsigned int reg; + unsigned int val; + + if ((fmt & SND_SOC_DAIFMT_INV_MASK) != SND_SOC_DAIFMT_NB_NF) { + dev_err(comp->dev, "unsupported DAI polarity: 0x%x\n", fmt); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) { + case SND_SOC_DAIFMT_CBM_CFM: + priv->dai[dai->id].is_master = true; + break; + case SND_SOC_DAIFMT_CBS_CFS: + priv->dai[dai->id].is_master = false; + break; + default: + dev_err(comp->dev, "unsupported DAI master mode: 0x%x\n", fmt); + return -EINVAL; + } + + switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) { + case SND_SOC_DAIFMT_I2S: + val = PCM3060_REG_FMT_I2S; + break; + case SND_SOC_DAIFMT_RIGHT_J: + val = PCM3060_REG_FMT_RJ; + break; + case SND_SOC_DAIFMT_LEFT_J: + val = PCM3060_REG_FMT_LJ; + break; + default: + dev_err(comp->dev, "unsupported DAI format: 0x%x\n", fmt); + return -EINVAL; + } + + if (dai->id == PCM3060_DAI_ID_DAC) + reg = PCM3060_REG67; + else + reg = PCM3060_REG72; + + regmap_update_bits(priv->regmap, reg, PCM3060_REG_MASK_FMT, val); + + return 0; +} + +static int pcm3060_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *comp = dai->component; + struct pcm3060_priv *priv = snd_soc_component_get_drvdata(comp); + unsigned int rate; + unsigned int ratio; + unsigned int reg; + unsigned int val; + + if (!priv->dai[dai->id].is_master) { + val = PCM3060_REG_MS_S; + goto val_ready; + } + + rate = params_rate(params); + if (!rate) { + dev_err(comp->dev, "rate is not configured\n"); + return -EINVAL; + } + + ratio = priv->dai[dai->id].sclk_freq / rate; + + switch (ratio) { + case 768: + val = PCM3060_REG_MS_M768; + break; + case 512: + val = PCM3060_REG_MS_M512; + break; + case 384: + val = PCM3060_REG_MS_M384; + break; + case 256: + val = PCM3060_REG_MS_M256; + break; + case 192: + val = PCM3060_REG_MS_M192; + break; + case 128: + val = PCM3060_REG_MS_M128; + break; + default: + dev_err(comp->dev, "unsupported ratio: %d\n", ratio); + return -EINVAL; + } + +val_ready: + if (dai->id == PCM3060_DAI_ID_DAC) + reg = PCM3060_REG67; + else + reg = PCM3060_REG72; + + regmap_update_bits(priv->regmap, reg, PCM3060_REG_MASK_MS, val); + + return 0; +} + +static const struct snd_soc_dai_ops pcm3060_dai_ops = { + .set_sysclk = pcm3060_set_sysclk, + .set_fmt = pcm3060_set_fmt, + .hw_params = pcm3060_hw_params, +}; + +#define PCM3060_DAI_RATES_ADC (SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_32000 | \ + SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 | \ + SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) + +#define PCM3060_DAI_RATES_DAC (PCM3060_DAI_RATES_ADC | \ + SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000) + +static struct snd_soc_dai_driver pcm3060_dai[] = { + { + .name = "pcm3060-dac", + .id = PCM3060_DAI_ID_DAC, + .playback = { + .stream_name = "Playback", + .channels_min = 2, + .channels_max = 2, + .rates = PCM3060_DAI_RATES_DAC, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &pcm3060_dai_ops, + }, + { + .name = "pcm3060-adc", + .id = PCM3060_DAI_ID_ADC, + .capture = { + .stream_name = "Capture", + .channels_min = 2, + .channels_max = 2, + .rates = PCM3060_DAI_RATES_ADC, + .formats = SNDRV_PCM_FMTBIT_S24_LE, + }, + .ops = &pcm3060_dai_ops, + }, +}; + +/* dapm */ + +static DECLARE_TLV_DB_SCALE(pcm3060_dapm_tlv, -10050, 50, 1); + +static const struct snd_kcontrol_new pcm3060_dapm_controls[] = { + SOC_DOUBLE_R_RANGE_TLV("Master Playback Volume", + PCM3060_REG65, PCM3060_REG66, 0, + PCM3060_REG_AT2_MIN, PCM3060_REG_AT2_MAX, + 0, pcm3060_dapm_tlv), + SOC_DOUBLE("Master Playback Switch", PCM3060_REG68, + PCM3060_REG_SHIFT_MUT21, PCM3060_REG_SHIFT_MUT22, 1, 1), + + SOC_DOUBLE_R_RANGE_TLV("Master Capture Volume", + PCM3060_REG70, PCM3060_REG71, 0, + PCM3060_REG_AT1_MIN, PCM3060_REG_AT1_MAX, + 0, pcm3060_dapm_tlv), + SOC_DOUBLE("Master Capture Switch", PCM3060_REG73, + PCM3060_REG_SHIFT_MUT11, PCM3060_REG_SHIFT_MUT12, 1, 1), +}; + +static const struct snd_soc_dapm_widget pcm3060_dapm_widgets[] = { + SND_SOC_DAPM_OUTPUT("OUTL+"), + SND_SOC_DAPM_OUTPUT("OUTR+"), + SND_SOC_DAPM_OUTPUT("OUTL-"), + SND_SOC_DAPM_OUTPUT("OUTR-"), + + SND_SOC_DAPM_INPUT("INL"), + SND_SOC_DAPM_INPUT("INR"), +}; + +static const struct snd_soc_dapm_route pcm3060_dapm_map[] = { + { "OUTL+", NULL, "Playback" }, + { "OUTR+", NULL, "Playback" }, + { "OUTL-", NULL, "Playback" }, + { "OUTR-", NULL, "Playback" }, + + { "Capture", NULL, "INL" }, + { "Capture", NULL, "INR" }, +}; + +/* soc component */ + +static const struct snd_soc_component_driver pcm3060_soc_comp_driver = { + .controls = pcm3060_dapm_controls, + .num_controls = ARRAY_SIZE(pcm3060_dapm_controls), + .dapm_widgets = pcm3060_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(pcm3060_dapm_widgets), + .dapm_routes = pcm3060_dapm_map, + .num_dapm_routes = ARRAY_SIZE(pcm3060_dapm_map), +}; + +/* regmap */ + +static bool pcm3060_reg_writeable(struct device *dev, unsigned int reg) +{ + return (reg >= PCM3060_REG64); +} + +static bool pcm3060_reg_readable(struct device *dev, unsigned int reg) +{ + return (reg >= PCM3060_REG64); +} + +static bool pcm3060_reg_volatile(struct device *dev, unsigned int reg) +{ + /* PCM3060_REG64 is volatile */ + return (reg == PCM3060_REG64); +} + +static const struct reg_default pcm3060_reg_defaults[] = { + { PCM3060_REG64, 0xF0 }, + { PCM3060_REG65, 0xFF }, + { PCM3060_REG66, 0xFF }, + { PCM3060_REG67, 0x00 }, + { PCM3060_REG68, 0x00 }, + { PCM3060_REG69, 0x00 }, + { PCM3060_REG70, 0xD7 }, + { PCM3060_REG71, 0xD7 }, + { PCM3060_REG72, 0x00 }, + { PCM3060_REG73, 0x00 }, +}; + +const struct regmap_config pcm3060_regmap = { + .reg_bits = 8, + .val_bits = 8, + .writeable_reg = pcm3060_reg_writeable, + .readable_reg = pcm3060_reg_readable, + .volatile_reg = pcm3060_reg_volatile, + .max_register = PCM3060_REG73, + .reg_defaults = pcm3060_reg_defaults, + .num_reg_defaults = ARRAY_SIZE(pcm3060_reg_defaults), + .cache_type = REGCACHE_RBTREE, +}; +EXPORT_SYMBOL(pcm3060_regmap); + +/* device */ + +int pcm3060_probe(struct device *dev) +{ + int rc; + + rc = devm_snd_soc_register_component(dev, &pcm3060_soc_comp_driver, + pcm3060_dai, + ARRAY_SIZE(pcm3060_dai)); + if (rc) { + dev_err(dev, "failed to register component, rc=%d\n", rc); + return rc; + } + + return 0; +} +EXPORT_SYMBOL(pcm3060_probe); + +MODULE_DESCRIPTION("PCM3060 codec driver"); +MODULE_AUTHOR("Kirill Marinushkin <kmarinushkin@birdec.tech>"); +MODULE_LICENSE("GPL v2"); diff --git a/sound/soc/codecs/pcm3060.h b/sound/soc/codecs/pcm3060.h @@ -0,0 +1,88 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * PCM3060 codec driver + * + * Copyright (C) 2018 Kirill Marinushkin <kmarinushkin@birdec.tech> + */ + +#ifndef _SND_SOC_PCM3060_H +#define _SND_SOC_PCM3060_H + +#include <linux/device.h> +#include <linux/regmap.h> + +extern const struct regmap_config pcm3060_regmap; + +#define PCM3060_DAI_ID_DAC 0 +#define PCM3060_DAI_ID_ADC 1 +#define PCM3060_DAI_IDS_NUM 2 + +struct pcm3060_priv_dai { + bool is_master; + unsigned int sclk_freq; +}; + +struct pcm3060_priv { + struct regmap *regmap; + struct pcm3060_priv_dai dai[PCM3060_DAI_IDS_NUM]; +}; + +int pcm3060_probe(struct device *dev); +int pcm3060_remove(struct device *dev); + +/* registers */ + +#define PCM3060_REG64 0x40 +#define PCM3060_REG_MRST 0x80 +#define PCM3060_REG_SRST 0x40 +#define PCM3060_REG_ADPSV 0x20 +#define PCM3060_REG_DAPSV 0x10 +#define PCM3060_REG_SE 0x01 + +#define PCM3060_REG65 0x41 +#define PCM3060_REG66 0x42 +#define PCM3060_REG_AT2_MIN 0x36 +#define PCM3060_REG_AT2_MAX 0xFF + +#define PCM3060_REG67 0x43 +#define PCM3060_REG72 0x48 +#define PCM3060_REG_CSEL 0x80 +#define PCM3060_REG_MASK_MS 0x70 +#define PCM3060_REG_MS_S 0x00 +#define PCM3060_REG_MS_M768 (0x01 << 4) +#define PCM3060_REG_MS_M512 (0x02 << 4) +#define PCM3060_REG_MS_M384 (0x03 << 4) +#define PCM3060_REG_MS_M256 (0x04 << 4) +#define PCM3060_REG_MS_M192 (0x05 << 4) +#define PCM3060_REG_MS_M128 (0x06 << 4) +#define PCM3060_REG_MASK_FMT 0x03 +#define PCM3060_REG_FMT_I2S 0x00 +#define PCM3060_REG_FMT_LJ 0x01 +#define PCM3060_REG_FMT_RJ 0x02 + +#define PCM3060_REG68 0x44 +#define PCM3060_REG_OVER 0x40 +#define PCM3060_REG_DREV2 0x04 +#define PCM3060_REG_SHIFT_MUT21 0x00 +#define PCM3060_REG_SHIFT_MUT22 0x01 + +#define PCM3060_REG69 0x45 +#define PCM3060_REG_FLT 0x80 +#define PCM3060_REG_MASK_DMF 0x60 +#define PCM3060_REG_DMC 0x10 +#define PCM3060_REG_ZREV 0x02 +#define PCM3060_REG_AZRO 0x01 + +#define PCM3060_REG70 0x46 +#define PCM3060_REG71 0x47 +#define PCM3060_REG_AT1_MIN 0x0E +#define PCM3060_REG_AT1_MAX 0xFF + +#define PCM3060_REG73 0x49 +#define PCM3060_REG_ZCDD 0x10 +#define PCM3060_REG_BYP 0x08 +#define PCM3060_REG_DREV1 0x04 +#define PCM3060_REG_SHIFT_MUT11 0x00 +#define PCM3060_REG_SHIFT_MUT12 0x01 + +#endif /* _SND_SOC_PCM3060_H */ diff --git a/sound/soc/codecs/pcm3168a.c b/sound/soc/codecs/pcm3168a.c @@ -33,6 +33,8 @@ #define PCM3168A_FMT_RIGHT_J_16 0x3 #define PCM3168A_FMT_DSP_A 0x4 #define PCM3168A_FMT_DSP_B 0x5 +#define PCM3168A_FMT_I2S_TDM 0x6 +#define PCM3168A_FMT_LEFT_J_TDM 0x7 #define PCM3168A_FMT_DSP_MASK 0x4 #define PCM3168A_NUM_SUPPLIES 6 @@ -401,9 +403,11 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, bool tx, master_mode; u32 val, mask, shift, reg; unsigned int rate, fmt, ratio, max_ratio; + unsigned int chan; int i, min_frame_size; rate = params_rate(params); + chan = params_channels(params); ratio = pcm3168a->sysclk / rate; @@ -456,6 +460,21 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, return -EINVAL; } + /* for TDM */ + if (chan > 2) { + switch (fmt) { + case PCM3168A_FMT_I2S: + fmt = PCM3168A_FMT_I2S_TDM; + break; + case PCM3168A_FMT_LEFT_J: + fmt = PCM3168A_FMT_LEFT_J_TDM; + break; + default: + dev_err(component->dev, "TDM is supported under I2S/Left_J only\n"); + return -EINVAL; + } + } + if (master_mode) val = ((i + 1) << shift); else @@ -476,7 +495,69 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream, return 0; } +static int pcm3168a_startup(struct snd_pcm_substream *substream, + struct snd_soc_dai *dai) +{ + struct snd_soc_component *component = dai->component; + struct pcm3168a_priv *pcm3168a = snd_soc_component_get_drvdata(component); + bool tx = substream->stream == SNDRV_PCM_STREAM_PLAYBACK; + unsigned int fmt; + unsigned int sample_min; + unsigned int channel_max; + + if (tx) + fmt = pcm3168a->dac_fmt; + else + fmt = pcm3168a->adc_fmt; + + /* + * Available Data Bits + * + * RIGHT_J : 24 / 16 + * LEFT_J : 24 + * I2S : 24 + * + * TDM available + * + * I2S + * LEFT_J + */ + switch (fmt) { + case PCM3168A_FMT_RIGHT_J: + sample_min = 16; + channel_max = 2; + break; + case PCM3168A_FMT_LEFT_J: + sample_min = 24; + if (tx) + channel_max = 8; + else + channel_max = 6; + break; + case PCM3168A_FMT_I2S: + sample_min = 24; + if (tx) + channel_max = 8; + else + channel_max = 6; + break; + default: + sample_min = 24; + channel_max = 2; + } + + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_SAMPLE_BITS, + sample_min, 32); + + snd_pcm_hw_constraint_minmax(substream->runtime, + SNDRV_PCM_HW_PARAM_CHANNELS, + 2, channel_max); + + return 0; +} static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = { + .startup = pcm3168a_startup, .set_fmt = pcm3168a_set_dai_fmt_dac, .set_sysclk = pcm3168a_set_dai_sysclk, .hw_params = pcm3168a_hw_params, @@ -484,6 +565,7 @@ static const struct snd_soc_dai_ops pcm3168a_dac_dai_ops = { }; static const struct snd_soc_dai_ops pcm3168a_adc_dai_ops = { + .startup = pcm3168a_startup, .set_fmt = pcm3168a_set_dai_fmt_adc, .set_sysclk = pcm3168a_set_dai_sysclk, .hw_params = pcm3168a_hw_params diff --git a/sound/soc/codecs/rt274.c b/sound/soc/codecs/rt274.c @@ -755,6 +755,7 @@ static int rt274_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, break; default: dev_warn(component->dev, "invalid pll source, use BCLK\n"); + /* fall through */ case RT274_PLL2_S_BCLK: snd_soc_component_update_bits(component, RT274_PLL2_CTRL, RT274_PLL2_SRC_MASK, RT274_PLL2_SRC_BCLK); @@ -782,6 +783,7 @@ static int rt274_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source, break; default: dev_warn(component->dev, "invalid freq_in, assume 4.8M\n"); + /* fall through */ case 100: snd_soc_component_write(component, 0x7a, 0xaab6); snd_soc_component_write(component, 0x7b, 0x0301); diff --git a/sound/soc/codecs/rt5514-spi.c b/sound/soc/codecs/rt5514-spi.c @@ -91,6 +91,14 @@ static void rt5514_spi_copy_work(struct work_struct *work) runtime = rt5514_dsp->substream->runtime; period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream); + if (!period_bytes) { + schedule_delayed_work(&rt5514_dsp->copy_work, 5); + goto done; + } + + if (rt5514_dsp->buf_size % period_bytes) + rt5514_dsp->buf_size = (rt5514_dsp->buf_size / period_bytes) * + period_bytes; if (rt5514_dsp->get_size >= rt5514_dsp->buf_size) { rt5514_spi_burst_read(RT5514_BUFFER_VOICE_WP, (u8 *)&buf, @@ -149,13 +157,11 @@ done: static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp) { - size_t period_bytes; u8 buf[8]; if (!rt5514_dsp->substream) return; - period_bytes = snd_pcm_lib_period_bytes(rt5514_dsp->substream); rt5514_dsp->get_size = 0; /** @@ -183,10 +189,6 @@ static void rt5514_schedule_copy(struct rt5514_dsp *rt5514_dsp) rt5514_dsp->buf_size = rt5514_dsp->buf_limit - rt5514_dsp->buf_base; - if (rt5514_dsp->buf_size % period_bytes) - rt5514_dsp->buf_size = (rt5514_dsp->buf_size / period_bytes) * - period_bytes; - if (rt5514_dsp->buf_base && rt5514_dsp->buf_limit && rt5514_dsp->buf_rp && rt5514_dsp->buf_size) schedule_delayed_work(&rt5514_dsp->copy_work, 0); diff --git a/sound/soc/codecs/rt5651.c b/sound/soc/codecs/rt5651.c @@ -10,7 +10,6 @@ */ #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> diff --git a/sound/soc/codecs/rt5663.c b/sound/soc/codecs/rt5663.c @@ -72,6 +72,7 @@ struct rt5663_priv { static const struct reg_sequence rt5663_patch_list[] = { { 0x002a, 0x8020 }, { 0x0086, 0x0028 }, + { 0x0100, 0xa020 }, { 0x0117, 0x0f28 }, { 0x02fb, 0x8089 }, }; @@ -580,7 +581,7 @@ static const struct reg_default rt5663_reg[] = { { 0x00fd, 0x0001 }, { 0x00fe, 0x10ec }, { 0x00ff, 0x6406 }, - { 0x0100, 0xa0a0 }, + { 0x0100, 0xa020 }, { 0x0108, 0x4444 }, { 0x0109, 0x4444 }, { 0x010a, 0xaaaa }, @@ -2337,6 +2338,8 @@ static int rt5663_hp_event(struct snd_soc_dapm_widget *w, 0x8000); snd_soc_component_update_bits(component, RT5663_DEPOP_1, 0x3000, 0x3000); + snd_soc_component_update_bits(component, + RT5663_DIG_VOL_ZCD, 0x00c0, 0x0080); } break; @@ -2351,6 +2354,8 @@ static int rt5663_hp_event(struct snd_soc_dapm_widget *w, RT5663_OVCD_HP_MASK, RT5663_OVCD_HP_EN); snd_soc_component_update_bits(component, RT5663_DACREF_LDO, 0x3e0e, 0); + snd_soc_component_update_bits(component, + RT5663_DIG_VOL_ZCD, 0x00c0, 0); } break; diff --git a/sound/soc/codecs/rt5668.c b/sound/soc/codecs/rt5668.c @@ -2588,17 +2588,10 @@ static int rt5668_i2c_probe(struct i2c_client *i2c, } - return snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5668, + return devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5668, rt5668_dai, ARRAY_SIZE(rt5668_dai)); } -static int rt5668_i2c_remove(struct i2c_client *i2c) -{ - snd_soc_unregister_component(&i2c->dev); - - return 0; -} - static void rt5668_i2c_shutdown(struct i2c_client *client) { struct rt5668_priv *rt5668 = i2c_get_clientdata(client); @@ -2629,7 +2622,6 @@ static struct i2c_driver rt5668_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5668_acpi_match), }, .probe = rt5668_i2c_probe, - .remove = rt5668_i2c_remove, .shutdown = rt5668_i2c_shutdown, .id_table = rt5668_i2c_id, }; diff --git a/sound/soc/codecs/rt5670.c b/sound/soc/codecs/rt5670.c @@ -2878,6 +2878,18 @@ static const struct dmi_system_id dmi_platform_intel_quirks[] = { }, { .callback = rt5670_quirk_cb, + .ident = "Lenovo Thinkpad Tablet 8", + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"), + }, + .driver_data = (unsigned long *)(RT5670_DMIC_EN | + RT5670_DMIC2_INR | + RT5670_DEV_GPIO | + RT5670_JD_MODE1), + }, + { + .callback = rt5670_quirk_cb, .ident = "Lenovo Thinkpad Tablet 10", .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c @@ -18,7 +18,6 @@ #include <linux/interrupt.h> #include <linux/irq.h> #include <linux/slab.h> -#include <linux/gpio.h> #include <linux/sched.h> #include <linux/uaccess.h> #include <linux/regulator/consumer.h> diff --git a/sound/soc/codecs/rt5682.c b/sound/soc/codecs/rt5682.c @@ -67,7 +67,8 @@ struct rt5682_priv { }; static const struct reg_sequence patch_list[] = { - {0x01c1, 0x1000}, + {RT5682_HP_IMP_SENS_CTRL_19, 0x1000}, + {RT5682_DAC_ADC_DIG_VOL1, 0xa020}, }; static const struct reg_default rt5682_reg[] = { @@ -749,7 +750,6 @@ static bool rt5682_readable_register(struct device *dev, unsigned int reg) } } -static const DECLARE_TLV_DB_SCALE(hp_vol_tlv, -2250, 150, 0); static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0); static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0); static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0); @@ -1108,10 +1108,6 @@ static void rt5682_jack_detect_handler(struct work_struct *work) } static const struct snd_kcontrol_new rt5682_snd_controls[] = { - /* Headphone Output Volume */ - SOC_DOUBLE_R_TLV("Headphone Playback Volume", RT5682_HPL_GAIN, - RT5682_HPR_GAIN, RT5682_G_HP_SFT, 15, 1, hp_vol_tlv), - /* DAC Digital Volume */ SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5682_DAC1_DIG_VOL, RT5682_L_VOL_SFT + 1, RT5682_R_VOL_SFT + 1, 86, 0, dac_vol_tlv), @@ -1437,6 +1433,28 @@ static const struct snd_kcontrol_new hpor_switch = SOC_DAPM_SINGLE_AUTODISABLE("Switch", RT5682_HP_CTRL_1, RT5682_R_MUTE_SFT, 1, 1); +static int rt5682_charge_pump_event(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *kcontrol, int event) +{ + struct snd_soc_component *component = + snd_soc_dapm_to_component(w->dapm); + + switch (event) { + case SND_SOC_DAPM_PRE_PMU: + snd_soc_component_update_bits(component, + RT5682_HP_CHARGE_PUMP_1, RT5682_PM_HP_MASK, RT5682_PM_HP_HV); + break; + case SND_SOC_DAPM_POST_PMD: + snd_soc_component_update_bits(component, + RT5682_HP_CHARGE_PUMP_1, RT5682_PM_HP_MASK, RT5682_PM_HP_LV); + break; + default: + return 0; + } + + return 0; +} + static int rt5682_hp_event(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol, int event) { @@ -1449,10 +1467,10 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w, RT5682_HP_LOGIC_CTRL_2, 0x0012); snd_soc_component_write(component, RT5682_HP_CTRL_2, 0x6000); - snd_soc_component_update_bits(component, RT5682_STO_NG2_CTRL_1, - RT5682_NG2_EN_MASK, RT5682_NG2_EN); snd_soc_component_update_bits(component, RT5682_DEPOP_1, 0x60, 0x60); + snd_soc_component_update_bits(component, + RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0080); break; case SND_SOC_DAPM_POST_PMD: @@ -1460,6 +1478,8 @@ static int rt5682_hp_event(struct snd_soc_dapm_widget *w, RT5682_DEPOP_1, 0x60, 0x0); snd_soc_component_write(component, RT5682_HP_CTRL_2, 0x0000); + snd_soc_component_update_bits(component, + RT5682_DAC_ADC_DIG_VOL1, 0x00c0, 0x0000); break; default: @@ -1723,7 +1743,8 @@ static const struct snd_soc_dapm_widget rt5682_dapm_widgets[] = { SND_SOC_DAPM_SUPPLY("HP Amp R", RT5682_PWR_ANLG_1, RT5682_PWR_HA_R_BIT, 0, NULL, 0), SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, RT5682_DEPOP_1, - RT5682_PUMP_EN_SFT, 0, NULL, 0), + RT5682_PUMP_EN_SFT, 0, rt5682_charge_pump_event, + SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD), SND_SOC_DAPM_SUPPLY_S("Capless", 2, RT5682_DEPOP_1, RT5682_CAPLESS_EN_SFT, 0, NULL, 0), @@ -1884,6 +1905,7 @@ static const struct snd_soc_dapm_route rt5682_dapm_routes[] = { {"HP Amp", NULL, "Charge Pump"}, {"HP Amp", NULL, "CLKDET SYS"}, {"HP Amp", NULL, "CBJ Power"}, + {"HP Amp", NULL, "Vref1"}, {"HP Amp", NULL, "Vref2"}, {"HPOL Playback", "Switch", "HP Amp"}, {"HPOR Playback", "Switch", "HP Amp"}, @@ -2452,30 +2474,23 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682) mutex_lock(&rt5682->calibrate_mutex); rt5682_reset(rt5682->regmap); - regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2bf); + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xa2af); usleep_range(15000, 20000); - regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2bf); - regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0380); - regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x8001); - regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); - regmap_write(rt5682->regmap, RT5682_STO1_DAC_MIXER, 0x2080); - regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x4040); - regmap_write(rt5682->regmap, RT5682_DEPOP_1, 0x0069); + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0xf2af); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0300); + regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x8000); + regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0100); + regmap_write(rt5682->regmap, RT5682_HP_IMP_SENS_CTRL_19, 0x3800); regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x3000); - regmap_write(rt5682->regmap, RT5682_HP_CTRL_2, 0x6000); - regmap_write(rt5682->regmap, RT5682_HP_CHARGE_PUMP_1, 0x0f26); - regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7f05); + regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x7005); regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0x686c); regmap_write(rt5682->regmap, RT5682_CAL_REC, 0x0d0d); - regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_9, 0x000f); - regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x8d01); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_2, 0x0321); regmap_write(rt5682->regmap, RT5682_HP_LOGIC_CTRL_2, 0x0004); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_3, 0x06a1); regmap_write(rt5682->regmap, RT5682_A_DAC1_MUX, 0x0311); - regmap_write(rt5682->regmap, RT5682_RESET_HPF_CTRL, 0x0000); - regmap_write(rt5682->regmap, RT5682_ADC_STO1_HP_CTRL_1, 0x3320); + regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0x7c00); regmap_write(rt5682->regmap, RT5682_HP_CALIB_CTRL_1, 0xfc00); @@ -2491,8 +2506,12 @@ static void rt5682_calibrate(struct rt5682_priv *rt5682) pr_err("HP Calibration Failure\n"); /* restore settings */ - regmap_write(rt5682->regmap, RT5682_STO1_ADC_MIXER, 0xc0c4); + regmap_write(rt5682->regmap, RT5682_PWR_ANLG_1, 0x02af); + regmap_write(rt5682->regmap, RT5682_MICBIAS_2, 0x0080); + regmap_write(rt5682->regmap, RT5682_GLB_CLK, 0x0000); regmap_write(rt5682->regmap, RT5682_PWR_DIG_1, 0x0000); + regmap_write(rt5682->regmap, RT5682_CHOP_DAC, 0x2000); + regmap_write(rt5682->regmap, RT5682_CALIB_ADC_CTRL, 0x2005); mutex_unlock(&rt5682->calibrate_mutex); @@ -2566,7 +2585,7 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, rt5682_calibrate(rt5682); - ret = regmap_register_patch(rt5682->regmap, patch_list, + ret = regmap_multi_reg_write(rt5682->regmap, patch_list, ARRAY_SIZE(patch_list)); if (ret != 0) dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret); @@ -2620,6 +2639,10 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, RT5682_GP4_PIN_MASK | RT5682_GP5_PIN_MASK, RT5682_GP4_PIN_ADCDAT1 | RT5682_GP5_PIN_DACDAT1); regmap_write(rt5682->regmap, RT5682_TEST_MODE_CTRL_1, 0x0000); + regmap_update_bits(rt5682->regmap, RT5682_BIAS_CUR_CTRL_8, + RT5682_HPA_CP_BIAS_CTRL_MASK, RT5682_HPA_CP_BIAS_3UA); + regmap_update_bits(rt5682->regmap, RT5682_CHARGE_PUMP_1, + RT5682_CP_CLK_HP_MASK, RT5682_CP_CLK_HP_300KHZ); INIT_DELAYED_WORK(&rt5682->jack_detect_work, rt5682_jack_detect_handler); @@ -2637,11 +2660,17 @@ static int rt5682_i2c_probe(struct i2c_client *i2c, } - return devm_snd_soc_register_component(&i2c->dev, - &soc_component_dev_rt5682, + return snd_soc_register_component(&i2c->dev, &soc_component_dev_rt5682, rt5682_dai, ARRAY_SIZE(rt5682_dai)); } +static int rt5682_i2c_remove(struct i2c_client *i2c) +{ + snd_soc_unregister_component(&i2c->dev); + + return 0; +} + static void rt5682_i2c_shutdown(struct i2c_client *client) { struct rt5682_priv *rt5682 = i2c_get_clientdata(client); @@ -2672,6 +2701,7 @@ static struct i2c_driver rt5682_i2c_driver = { .acpi_match_table = ACPI_PTR(rt5682_acpi_match), }, .probe = rt5682_i2c_probe, + .remove = rt5682_i2c_remove, .shutdown = rt5682_i2c_shutdown, .id_table = rt5682_i2c_id, }; diff --git a/sound/soc/codecs/rt5682.h b/sound/soc/codecs/rt5682.h @@ -1214,6 +1214,20 @@ #define RT5682_JDH_NO_PLUG (0x1 << 4) #define RT5682_JDH_PLUG (0x0 << 4) +/* Bias current control 8 (0x0111) */ +#define RT5682_HPA_CP_BIAS_CTRL_MASK (0x3 << 2) +#define RT5682_HPA_CP_BIAS_2UA (0x0 << 2) +#define RT5682_HPA_CP_BIAS_3UA (0x1 << 2) +#define RT5682_HPA_CP_BIAS_4UA (0x2 << 2) +#define RT5682_HPA_CP_BIAS_6UA (0x3 << 2) + +/* Charge Pump Internal Register1 (0x0125) */ +#define RT5682_CP_CLK_HP_MASK (0x3 << 4) +#define RT5682_CP_CLK_HP_100KHZ (0x0 << 4) +#define RT5682_CP_CLK_HP_200KHZ (0x1 << 4) +#define RT5682_CP_CLK_HP_300KHZ (0x2 << 4) +#define RT5682_CP_CLK_HP_600KHZ (0x3 << 4) + /* Chopper and Clock control for DAC (0x013a)*/ #define RT5682_CKXEN_DAC1_MASK (0x1 << 13) #define RT5682_CKXEN_DAC1_SFT 13 diff --git a/sound/soc/codecs/sgtl5000.c b/sound/soc/codecs/sgtl5000.c @@ -1218,7 +1218,7 @@ static int sgtl5000_set_power_regs(struct snd_soc_component *component) * Searching for a suitable index solving this formula: * idx = 40 * log10(vag_val / lo_cagcntrl) + 15 */ - vol_quot = (vag * 100) / lo_vag; + vol_quot = lo_vag ? (vag * 100) / lo_vag : 0; lo_vol = 0; for (i = 0; i < ARRAY_SIZE(vol_quot_table); i++) { if (vol_quot >= vol_quot_table[i]) diff --git a/sound/soc/codecs/sta32x.c b/sound/soc/codecs/sta32x.c @@ -21,6 +21,7 @@ #include <linux/module.h> #include <linux/moduleparam.h> #include <linux/init.h> +#include <linux/clk.h> #include <linux/delay.h> #include <linux/pm.h> #include <linux/i2c.h> @@ -142,6 +143,7 @@ static const char *sta32x_supply_names[] = { /* codec private data */ struct sta32x_priv { struct regmap *regmap; + struct clk *xti_clk; struct regulator_bulk_data supplies[ARRAY_SIZE(sta32x_supply_names)]; struct snd_soc_component *component; struct sta32x_platform_data *pdata; @@ -879,6 +881,18 @@ static int sta32x_probe(struct snd_soc_component *component) struct sta32x_priv *sta32x = snd_soc_component_get_drvdata(component); struct sta32x_platform_data *pdata = sta32x->pdata; int i, ret = 0, thermal = 0; + + sta32x->component = component; + + if (sta32x->xti_clk) { + ret = clk_prepare_enable(sta32x->xti_clk); + if (ret != 0) { + dev_err(component->dev, + "Failed to enable clock: %d\n", ret); + return ret; + } + } + ret = regulator_bulk_enable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); if (ret != 0) { @@ -981,6 +995,9 @@ static void sta32x_remove(struct snd_soc_component *component) sta32x_watchdog_stop(sta32x); regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies); + + if (sta32x->xti_clk) + clk_disable_unprepare(sta32x->xti_clk); } static const struct snd_soc_component_driver sta32x_component = { @@ -1038,6 +1055,8 @@ static int sta32x_probe_dt(struct device *dev, struct sta32x_priv *sta32x) of_property_read_u8(np, "st,ch3-output-mapping", &pdata->ch3_output_mapping); + if (of_get_property(np, "st,fault-detect-recovery", NULL)) + pdata->fault_detect_recovery = 1; if (of_get_property(np, "st,thermal-warning-recovery", NULL)) pdata->thermal_warning_recovery = 1; if (of_get_property(np, "st,thermal-warning-adjustment", NULL)) @@ -1095,6 +1114,17 @@ static int sta32x_i2c_probe(struct i2c_client *i2c, } #endif + /* Clock */ + sta32x->xti_clk = devm_clk_get(dev, "xti"); + if (IS_ERR(sta32x->xti_clk)) { + ret = PTR_ERR(sta32x->xti_clk); + + if (ret == -EPROBE_DEFER) + return ret; + + sta32x->xti_clk = NULL; + } + /* GPIOs */ sta32x->gpiod_nreset = devm_gpiod_get_optional(dev, "reset", GPIOD_OUT_LOW); diff --git a/sound/soc/codecs/tas5720.c b/sound/soc/codecs/tas5720.c @@ -152,6 +152,7 @@ static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai, int slots, int slot_width) { struct snd_soc_component *component = dai->component; + struct tas5720_data *tas5720 = snd_soc_component_get_drvdata(component); unsigned int first_slot; int ret; @@ -185,6 +186,20 @@ static int tas5720_set_dai_tdm_slot(struct snd_soc_dai *dai, if (ret < 0) goto error_snd_soc_component_update_bits; + /* Configure TDM slot width. This is only applicable to TAS5722. */ + switch (tas5720->devtype) { + case TAS5722: + ret = snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG, + TAS5722_TDM_SLOT_16B, + slot_width == 16 ? + TAS5722_TDM_SLOT_16B : 0); + if (ret < 0) + goto error_snd_soc_component_update_bits; + break; + default: + break; + } + return 0; error_snd_soc_component_update_bits: @@ -485,15 +500,56 @@ static const DECLARE_TLV_DB_RANGE(dac_analog_tlv, ); /* - * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB steps. Note that - * setting the gain below -100 dB (register value <0x7) is effectively a MUTE - * as per device datasheet. + * DAC digital volumes. From -103.5 to 24 dB in 0.5 dB or 0.25 dB steps + * depending on the device. Note that setting the gain below -100 dB + * (register value <0x7) is effectively a MUTE as per device datasheet. + * + * Note that for the TAS5722 the digital volume controls are actually split + * over two registers, so we need custom getters/setters for access. */ -static DECLARE_TLV_DB_SCALE(dac_tlv, -10350, 50, 0); +static DECLARE_TLV_DB_SCALE(tas5720_dac_tlv, -10350, 50, 0); +static DECLARE_TLV_DB_SCALE(tas5722_dac_tlv, -10350, 25, 0); + +static int tas5722_volume_get(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int val; + + snd_soc_component_read(component, TAS5720_VOLUME_CTRL_REG, &val); + ucontrol->value.integer.value[0] = val << 1; + + snd_soc_component_read(component, TAS5722_DIGITAL_CTRL2_REG, &val); + ucontrol->value.integer.value[0] |= val & TAS5722_VOL_CONTROL_LSB; + + return 0; +} + +static int tas5722_volume_set(struct snd_kcontrol *kcontrol, + struct snd_ctl_elem_value *ucontrol) +{ + struct snd_soc_component *component = snd_soc_kcontrol_component(kcontrol); + unsigned int sel = ucontrol->value.integer.value[0]; + + snd_soc_component_write(component, TAS5720_VOLUME_CTRL_REG, sel >> 1); + snd_soc_component_update_bits(component, TAS5722_DIGITAL_CTRL2_REG, + TAS5722_VOL_CONTROL_LSB, sel); + + return 0; +} static const struct snd_kcontrol_new tas5720_snd_controls[] = { SOC_SINGLE_TLV("Speaker Driver Playback Volume", - TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, dac_tlv), + TAS5720_VOLUME_CTRL_REG, 0, 0xff, 0, tas5720_dac_tlv), + SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG, + TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv), +}; + +static const struct snd_kcontrol_new tas5722_snd_controls[] = { + SOC_SINGLE_EXT_TLV("Speaker Driver Playback Volume", + 0, 0, 511, 0, + tas5722_volume_get, tas5722_volume_set, + tas5722_dac_tlv), SOC_SINGLE_TLV("Speaker Driver Analog Gain", TAS5720_ANALOG_CTRL_REG, TAS5720_ANALOG_GAIN_SHIFT, 3, 0, dac_analog_tlv), }; @@ -527,6 +583,23 @@ static const struct snd_soc_component_driver soc_component_dev_tas5720 = { .non_legacy_dai_naming = 1, }; +static const struct snd_soc_component_driver soc_component_dev_tas5722 = { + .probe = tas5720_codec_probe, + .remove = tas5720_codec_remove, + .suspend = tas5720_suspend, + .resume = tas5720_resume, + .controls = tas5722_snd_controls, + .num_controls = ARRAY_SIZE(tas5722_snd_controls), + .dapm_widgets = tas5720_dapm_widgets, + .num_dapm_widgets = ARRAY_SIZE(tas5720_dapm_widgets), + .dapm_routes = tas5720_audio_map, + .num_dapm_routes = ARRAY_SIZE(tas5720_audio_map), + .idle_bias_on = 1, + .use_pmdown_time = 1, + .endianness = 1, + .non_legacy_dai_naming = 1, +}; + /* PCM rates supported by the TAS5720 driver */ #define TAS5720_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |\ SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000) @@ -613,9 +686,23 @@ static int tas5720_probe(struct i2c_client *client, dev_set_drvdata(dev, data); - ret = devm_snd_soc_register_component(&client->dev, - &soc_component_dev_tas5720, - tas5720_dai, ARRAY_SIZE(tas5720_dai)); + switch (id->driver_data) { + case TAS5720: + ret = devm_snd_soc_register_component(&client->dev, + &soc_component_dev_tas5720, + tas5720_dai, + ARRAY_SIZE(tas5720_dai)); + break; + case TAS5722: + ret = devm_snd_soc_register_component(&client->dev, + &soc_component_dev_tas5722, + tas5720_dai, + ARRAY_SIZE(tas5720_dai)); + break; + default: + dev_err(dev, "unexpected private driver data\n"); + return -EINVAL; + } if (ret < 0) { dev_err(dev, "failed to register component: %d\n", ret); return ret; diff --git a/sound/soc/codecs/tas6424.c b/sound/soc/codecs/tas6424.c @@ -41,6 +41,7 @@ struct tas6424_data { struct regmap *regmap; struct regulator_bulk_data supplies[TAS6424_NUM_SUPPLIES]; struct delayed_work fault_check_work; + unsigned int last_cfault; unsigned int last_fault1; unsigned int last_fault2; unsigned int last_warn; @@ -406,9 +407,54 @@ static void tas6424_fault_check_work(struct work_struct *work) unsigned int reg; int ret; + ret = regmap_read(tas6424->regmap, TAS6424_CHANNEL_FAULT, &reg); + if (ret < 0) { + dev_err(dev, "failed to read CHANNEL_FAULT register: %d\n", ret); + goto out; + } + + if (!reg) { + tas6424->last_cfault = reg; + goto check_global_fault1_reg; + } + + /* + * Only flag errors once for a given occurrence. This is needed as + * the TAS6424 will take time clearing the fault condition internally + * during which we don't want to bombard the system with the same + * error message over and over. + */ + if ((reg & TAS6424_FAULT_OC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH1)) + dev_crit(dev, "experienced a channel 1 overcurrent fault\n"); + + if ((reg & TAS6424_FAULT_OC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH2)) + dev_crit(dev, "experienced a channel 2 overcurrent fault\n"); + + if ((reg & TAS6424_FAULT_OC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH3)) + dev_crit(dev, "experienced a channel 3 overcurrent fault\n"); + + if ((reg & TAS6424_FAULT_OC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_OC_CH4)) + dev_crit(dev, "experienced a channel 4 overcurrent fault\n"); + + if ((reg & TAS6424_FAULT_DC_CH1) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH1)) + dev_crit(dev, "experienced a channel 1 DC fault\n"); + + if ((reg & TAS6424_FAULT_DC_CH2) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH2)) + dev_crit(dev, "experienced a channel 2 DC fault\n"); + + if ((reg & TAS6424_FAULT_DC_CH3) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH3)) + dev_crit(dev, "experienced a channel 3 DC fault\n"); + + if ((reg & TAS6424_FAULT_DC_CH4) && !(tas6424->last_cfault & TAS6424_FAULT_DC_CH4)) + dev_crit(dev, "experienced a channel 4 DC fault\n"); + + /* Store current fault1 value so we can detect any changes next time */ + tas6424->last_cfault = reg; + +check_global_fault1_reg: ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT1, &reg); if (ret < 0) { - dev_err(dev, "failed to read FAULT1 register: %d\n", ret); + dev_err(dev, "failed to read GLOB_FAULT1 register: %d\n", ret); goto out; } @@ -429,12 +475,6 @@ static void tas6424_fault_check_work(struct work_struct *work) goto check_global_fault2_reg; } - /* - * Only flag errors once for a given occurrence. This is needed as - * the TAS6424 will take time clearing the fault condition internally - * during which we don't want to bombard the system with the same - * error message over and over. - */ if ((reg & TAS6424_FAULT_PVDD_OV) && !(tas6424->last_fault1 & TAS6424_FAULT_PVDD_OV)) dev_crit(dev, "experienced a PVDD overvoltage fault\n"); @@ -453,7 +493,7 @@ static void tas6424_fault_check_work(struct work_struct *work) check_global_fault2_reg: ret = regmap_read(tas6424->regmap, TAS6424_GLOB_FAULT2, &reg); if (ret < 0) { - dev_err(dev, "failed to read FAULT2 register: %d\n", ret); + dev_err(dev, "failed to read GLOB_FAULT2 register: %d\n", ret); goto out; } @@ -530,7 +570,7 @@ check_warn_reg: /* Store current warn value so we can detect any changes next time */ tas6424->last_warn = reg; - /* Clear any faults by toggling the CLEAR_FAULT control bit */ + /* Clear any warnings by toggling the CLEAR_FAULT control bit */ ret = regmap_write_bits(tas6424->regmap, TAS6424_MISC_CTRL3, TAS6424_CLEAR_FAULT, TAS6424_CLEAR_FAULT); if (ret < 0) diff --git a/sound/soc/codecs/tas6424.h b/sound/soc/codecs/tas6424.h @@ -116,6 +116,16 @@ #define TAS6424_LDGBYPASS_MASK BIT(TAS6424_LDGBYPASS_SHIFT) /* TAS6424_GLOB_FAULT1_REG */ +#define TAS6424_FAULT_OC_CH1 BIT(7) +#define TAS6424_FAULT_OC_CH2 BIT(6) +#define TAS6424_FAULT_OC_CH3 BIT(5) +#define TAS6424_FAULT_OC_CH4 BIT(4) +#define TAS6424_FAULT_DC_CH1 BIT(3) +#define TAS6424_FAULT_DC_CH2 BIT(2) +#define TAS6424_FAULT_DC_CH3 BIT(1) +#define TAS6424_FAULT_DC_CH4 BIT(0) + +/* TAS6424_GLOB_FAULT1_REG */ #define TAS6424_FAULT_CLOCK BIT(4) #define TAS6424_FAULT_PVDD_OV BIT(3) #define TAS6424_FAULT_VBAT_OV BIT(2) diff --git a/sound/soc/codecs/tlv320aic31xx.c b/sound/soc/codecs/tlv320aic31xx.c @@ -167,6 +167,7 @@ struct aic31xx_priv { u8 p_div; int rate_div_line; bool master_dapm_route_applied; + int irq; }; struct aic31xx_rate_divs { @@ -1391,6 +1392,69 @@ static const struct acpi_device_id aic31xx_acpi_match[] = { MODULE_DEVICE_TABLE(acpi, aic31xx_acpi_match); #endif +static irqreturn_t aic31xx_irq(int irq, void *data) +{ + struct aic31xx_priv *aic31xx = data; + struct device *dev = aic31xx->dev; + unsigned int value; + bool handled = false; + int ret; + + ret = regmap_read(aic31xx->regmap, AIC31XX_INTRDACFLAG, &value); + if (ret) { + dev_err(dev, "Failed to read interrupt mask: %d\n", ret); + goto exit; + } + + if (value) + handled = true; + else + goto read_overflow; + + if (value & AIC31XX_HPLSCDETECT) + dev_err(dev, "Short circuit on Left output is detected\n"); + if (value & AIC31XX_HPRSCDETECT) + dev_err(dev, "Short circuit on Right output is detected\n"); + if (value & ~(AIC31XX_HPLSCDETECT | + AIC31XX_HPRSCDETECT)) + dev_err(dev, "Unknown DAC interrupt flags: 0x%08x\n", value); + +read_overflow: + ret = regmap_read(aic31xx->regmap, AIC31XX_OFFLAG, &value); + if (ret) { + dev_err(dev, "Failed to read overflow flag: %d\n", ret); + goto exit; + } + + if (value) + handled = true; + else + goto exit; + + if (value & AIC31XX_DAC_OF_LEFT) + dev_warn(dev, "Left-channel DAC overflow has occurred\n"); + if (value & AIC31XX_DAC_OF_RIGHT) + dev_warn(dev, "Right-channel DAC overflow has occurred\n"); + if (value & AIC31XX_DAC_OF_SHIFTER) + dev_warn(dev, "DAC barrel shifter overflow has occurred\n"); + if (value & AIC31XX_ADC_OF) + dev_warn(dev, "ADC overflow has occurred\n"); + if (value & AIC31XX_ADC_OF_SHIFTER) + dev_warn(dev, "ADC barrel shifter overflow has occurred\n"); + if (value & ~(AIC31XX_DAC_OF_LEFT | + AIC31XX_DAC_OF_RIGHT | + AIC31XX_DAC_OF_SHIFTER | + AIC31XX_ADC_OF | + AIC31XX_ADC_OF_SHIFTER)) + dev_warn(dev, "Unknown overflow interrupt flags: 0x%08x\n", value); + +exit: + if (handled) + return IRQ_HANDLED; + else + return IRQ_NONE; +} + static int aic31xx_i2c_probe(struct i2c_client *i2c, const struct i2c_device_id *id) { @@ -1413,6 +1477,7 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c, return ret; } aic31xx->dev = &i2c->dev; + aic31xx->irq = i2c->irq; aic31xx->codec_type = id->driver_data; @@ -1456,6 +1521,26 @@ static int aic31xx_i2c_probe(struct i2c_client *i2c, return ret; } + if (aic31xx->irq > 0) { + regmap_update_bits(aic31xx->regmap, AIC31XX_GPIO1, + AIC31XX_GPIO1_FUNC_MASK, + AIC31XX_GPIO1_INT1 << + AIC31XX_GPIO1_FUNC_SHIFT); + + regmap_write(aic31xx->regmap, AIC31XX_INT1CTRL, + AIC31XX_SC | + AIC31XX_ENGINE); + + ret = devm_request_threaded_irq(aic31xx->dev, aic31xx->irq, + NULL, aic31xx_irq, + IRQF_ONESHOT, "aic31xx-irq", + aic31xx); + if (ret) { + dev_err(aic31xx->dev, "Unable to request IRQ\n"); + return ret; + } + } + if (aic31xx->codec_type & DAC31XX_BIT) return devm_snd_soc_register_component(&i2c->dev, &soc_codec_driver_aic31xx, diff --git a/sound/soc/codecs/tlv320aic31xx.h b/sound/soc/codecs/tlv320aic31xx.h @@ -173,6 +173,13 @@ struct aic31xx_pdata { #define AIC31XX_HPRDRVPWRSTATUS_MASK BIT(1) #define AIC31XX_SPRDRVPWRSTATUS_MASK BIT(0) +/* AIC31XX_OFFLAG */ +#define AIC31XX_DAC_OF_LEFT BIT(7) +#define AIC31XX_DAC_OF_RIGHT BIT(6) +#define AIC31XX_DAC_OF_SHIFTER BIT(5) +#define AIC31XX_ADC_OF BIT(3) +#define AIC31XX_ADC_OF_SHIFTER BIT(1) + /* AIC31XX_INTRDACFLAG */ #define AIC31XX_HPLSCDETECT BIT(7) #define AIC31XX_HPRSCDETECT BIT(6) @@ -191,6 +198,22 @@ struct aic31xx_pdata { #define AIC31XX_SC BIT(3) #define AIC31XX_ENGINE BIT(2) +/* AIC31XX_GPIO1 */ +#define AIC31XX_GPIO1_FUNC_MASK GENMASK(5, 2) +#define AIC31XX_GPIO1_FUNC_SHIFT 2 +#define AIC31XX_GPIO1_DISABLED 0x00 +#define AIC31XX_GPIO1_INPUT 0x01 +#define AIC31XX_GPIO1_GPI 0x02 +#define AIC31XX_GPIO1_GPO 0x03 +#define AIC31XX_GPIO1_CLKOUT 0x04 +#define AIC31XX_GPIO1_INT1 0x05 +#define AIC31XX_GPIO1_INT2 0x06 +#define AIC31XX_GPIO1_ADC_WCLK 0x07 +#define AIC31XX_GPIO1_SBCLK 0x08 +#define AIC31XX_GPIO1_SWCLK 0x09 +#define AIC31XX_GPIO1_ADC_MOD_CLK 0x10 +#define AIC31XX_GPIO1_SDOUT 0x11 + /* AIC31XX_DACSETUP */ #define AIC31XX_SOFTSTEP_MASK GENMASK(1, 0) diff --git a/sound/soc/codecs/tscs454.c b/sound/soc/codecs/tscs454.c @@ -3459,7 +3459,7 @@ static int tscs454_i2c_probe(struct i2c_client *i2c, /* Sync pg sel reg with cache */ regmap_write(tscs454->regmap, R_PAGESEL, 0x00); - ret = snd_soc_register_component(&i2c->dev, &soc_component_dev_tscs454, + ret = devm_snd_soc_register_component(&i2c->dev, &soc_component_dev_tscs454, tscs454_dais, ARRAY_SIZE(tscs454_dais)); if (ret) { dev_err(&i2c->dev, "Failed to register component (%d)\n", ret); diff --git a/sound/soc/codecs/wm2000.c b/sound/soc/codecs/wm2000.c @@ -88,19 +88,6 @@ static int wm2000_write(struct i2c_client *i2c, unsigned int reg, return regmap_write(wm2000->regmap, reg, value); } -static unsigned int wm2000_read(struct i2c_client *i2c, unsigned int r) -{ - struct wm2000_priv *wm2000 = i2c_get_clientdata(i2c); - unsigned int val; - int ret; - - ret = regmap_read(wm2000->regmap, r, &val); - if (ret < 0) - return -1; - - return val; -} - static void wm2000_reset(struct wm2000_priv *wm2000) { struct i2c_client *i2c = wm2000->i2c; @@ -115,14 +102,15 @@ static void wm2000_reset(struct wm2000_priv *wm2000) static int wm2000_poll_bit(struct i2c_client *i2c, unsigned int reg, u8 mask) { + struct wm2000_priv *wm2000 = i2c_get_clientdata(i2c); int timeout = 4000; - int val; + unsigned int val; - val = wm2000_read(i2c, reg); + regmap_read(wm2000->regmap, reg, &val); while (!(val & mask) && --timeout) { msleep(1); - val = wm2000_read(i2c, reg); + regmap_read(wm2000->regmap, reg, &val); } if (timeout == 0) @@ -135,6 +123,7 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue) { struct wm2000_priv *wm2000 = dev_get_drvdata(&i2c->dev); unsigned long rate; + unsigned int val; int ret; if (WARN_ON(wm2000->anc_mode != ANC_OFF)) @@ -213,12 +202,17 @@ static int wm2000_power_up(struct i2c_client *i2c, int analogue) WM2000_MODE_THERMAL_ENABLE); } - ret = wm2000_read(i2c, WM2000_REG_SPEECH_CLARITY); + ret = regmap_read(wm2000->regmap, WM2000_REG_SPEECH_CLARITY, &val); + if (ret != 0) { + dev_err(&i2c->dev, "Unable to read Speech Clarity: %d\n", ret); + regulator_bulk_disable(WM2000_NUM_SUPPLIES, wm2000->supplies); + return ret; + } if (wm2000->speech_clarity) - ret |= WM2000_SPEECH_CLARITY; + val |= WM2000_SPEECH_CLARITY; else - ret &= ~WM2000_SPEECH_CLARITY; - wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, ret); + val &= ~WM2000_SPEECH_CLARITY; + wm2000_write(i2c, WM2000_REG_SPEECH_CLARITY, val); wm2000_write(i2c, WM2000_REG_SYS_START0, 0x33); wm2000_write(i2c, WM2000_REG_SYS_START1, 0x02); @@ -824,7 +818,7 @@ static int wm2000_i2c_probe(struct i2c_client *i2c, const char *filename; const struct firmware *fw = NULL; int ret, i; - int reg; + unsigned int reg; u16 id; wm2000 = devm_kzalloc(&i2c->dev, sizeof(*wm2000), GFP_KERNEL); @@ -860,9 +854,17 @@ static int wm2000_i2c_probe(struct i2c_client *i2c, } /* Verify that this is a WM2000 */ - reg = wm2000_read(i2c, WM2000_REG_ID1); + ret = regmap_read(wm2000->regmap, WM2000_REG_ID1, &reg); + if (ret != 0) { + dev_err(&i2c->dev, "Unable to read ID1: %d\n", ret); + return ret; + } id = reg << 8; - reg = wm2000_read(i2c, WM2000_REG_ID2); + ret = regmap_read(wm2000->regmap, WM2000_REG_ID2, &reg); + if (ret != 0) { + dev_err(&i2c->dev, "Unable to read ID2: %d\n", ret); + return ret; + } id |= reg & 0xff; if (id != 0x2000) { @@ -871,7 +873,11 @@ static int wm2000_i2c_probe(struct i2c_client *i2c, goto err_supplies; } - reg = wm2000_read(i2c, WM2000_REG_REVISON); + ret = regmap_read(wm2000->regmap, WM2000_REG_REVISON, &reg); + if (ret != 0) { + dev_err(&i2c->dev, "Unable to read Revision: %d\n", ret); + return ret; + } dev_info(&i2c->dev, "revision %c\n", reg + 'A'); wm2000->mclk = devm_clk_get(&i2c->dev, "MCLK"); diff --git a/sound/soc/codecs/wm8782.c b/sound/soc/codecs/wm8782.c @@ -20,6 +20,7 @@ #include <linux/module.h> #include <linux/kernel.h> #include <linux/device.h> +#include <linux/regulator/consumer.h> #include <sound/core.h> #include <sound/pcm.h> #include <sound/ac97_codec.h> @@ -50,7 +51,51 @@ static struct snd_soc_dai_driver wm8782_dai = { }, }; +/* regulator power supply names */ +static const char *supply_names[] = { + "Vdda", /* analog supply, 2.7V - 3.6V */ + "Vdd", /* digital supply, 2.7V - 5.5V */ +}; + +struct wm8782_priv { + struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)]; +}; + +static int wm8782_soc_probe(struct snd_soc_component *component) +{ + struct wm8782_priv *priv = snd_soc_component_get_drvdata(component); + return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); +} + +static void wm8782_soc_remove(struct snd_soc_component *component) +{ + struct wm8782_priv *priv = snd_soc_component_get_drvdata(component); + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); +} + +#ifdef CONFIG_PM +static int wm8782_soc_suspend(struct snd_soc_component *component) +{ + struct wm8782_priv *priv = snd_soc_component_get_drvdata(component); + regulator_bulk_disable(ARRAY_SIZE(priv->supplies), priv->supplies); + return 0; +} + +static int wm8782_soc_resume(struct snd_soc_component *component) +{ + struct wm8782_priv *priv = snd_soc_component_get_drvdata(component); + return regulator_bulk_enable(ARRAY_SIZE(priv->supplies), priv->supplies); +} +#else +#define wm8782_soc_suspend NULL +#define wm8782_soc_resume NULL +#endif /* CONFIG_PM */ + static const struct snd_soc_component_driver soc_component_dev_wm8782 = { + .probe = wm8782_soc_probe, + .remove = wm8782_soc_remove, + .suspend = wm8782_soc_suspend, + .resume = wm8782_soc_resume, .dapm_widgets = wm8782_dapm_widgets, .num_dapm_widgets = ARRAY_SIZE(wm8782_dapm_widgets), .dapm_routes = wm8782_dapm_routes, @@ -63,6 +108,24 @@ static const struct snd_soc_component_driver soc_component_dev_wm8782 = { static int wm8782_probe(struct platform_device *pdev) { + struct device *dev = &pdev->dev; + struct wm8782_priv *priv; + int ret, i; + + priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); + if (!priv) + return -ENOMEM; + + dev_set_drvdata(dev, priv); + + for (i = 0; i < ARRAY_SIZE(supply_names); i++) + priv->supplies[i].supply = supply_names[i]; + + ret = devm_regulator_bulk_get(dev, ARRAY_SIZE(priv->supplies), + priv->supplies); + if (ret < 0) + return ret; + return devm_snd_soc_register_component(&pdev->dev, &soc_component_dev_wm8782, &wm8782_dai, 1); } diff --git a/sound/soc/codecs/wm8904.c b/sound/soc/codecs/wm8904.c @@ -13,7 +13,6 @@ #include <linux/clk.h> #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/init.h> #include <linux/delay.h> #include <linux/pm.h> diff --git a/sound/soc/codecs/wm8974.c b/sound/soc/codecs/wm8974.c @@ -11,7 +11,6 @@ */ #include <linux/module.h> -#include <linux/moduleparam.h> #include <linux/kernel.h> #include <linux/init.h> #include <linux/delay.h> diff --git a/sound/soc/codecs/wm9712.c b/sound/soc/codecs/wm9712.c @@ -638,13 +638,14 @@ static int wm9712_soc_probe(struct snd_soc_component *component) { struct wm9712_priv *wm9712 = snd_soc_component_get_drvdata(component); struct regmap *regmap; - int ret; if (wm9712->mfd_pdata) { wm9712->ac97 = wm9712->mfd_pdata->ac97; regmap = wm9712->mfd_pdata->regmap; } else { #ifdef CONFIG_SND_SOC_AC97_BUS + int ret; + wm9712->ac97 = snd_soc_new_ac97_component(component, WM9712_VENDOR_ID, WM9712_VENDOR_ID_MASK); if (IS_ERR(wm9712->ac97)) { diff --git a/sound/soc/codecs/wm_adsp.c b/sound/soc/codecs/wm_adsp.c @@ -311,12 +311,12 @@ struct wm_adsp_alg_xm_struct { }; struct wm_adsp_buffer { - __be32 X_buf_base; /* XM base addr of first X area */ - __be32 X_buf_size; /* Size of 1st X area in words */ - __be32 X_buf_base2; /* XM base addr of 2nd X area */ - __be32 X_buf_brk; /* Total X size in words */ - __be32 Y_buf_base; /* YM base addr of Y area */ - __be32 wrap; /* Total size X and Y in words */ + __be32 buf1_base; /* Base addr of first buffer area */ + __be32 buf1_size; /* Size of buf1 area in DSP words */ + __be32 buf2_base; /* Base addr of 2nd buffer area */ + __be32 buf1_buf2_size; /* Size of buf1+buf2 in DSP words */ + __be32 buf3_base; /* Base addr of buf3 area */ + __be32 buf_total_size; /* Size of buf1+buf2+buf3 in DSP words */ __be32 high_water_mark; /* Point at which IRQ is asserted */ __be32 irq_count; /* bits 1-31 count IRQ assertions */ __be32 irq_ack; /* acked IRQ count, bit 0 enables IRQ */ @@ -393,18 +393,18 @@ struct wm_adsp_buffer_region_def { static const struct wm_adsp_buffer_region_def default_regions[] = { { .mem_type = WMFW_ADSP2_XM, - .base_offset = HOST_BUFFER_FIELD(X_buf_base), - .size_offset = HOST_BUFFER_FIELD(X_buf_size), + .base_offset = HOST_BUFFER_FIELD(buf1_base), + .size_offset = HOST_BUFFER_FIELD(buf1_size), }, { .mem_type = WMFW_ADSP2_XM, - .base_offset = HOST_BUFFER_FIELD(X_buf_base2), - .size_offset = HOST_BUFFER_FIELD(X_buf_brk), + .base_offset = HOST_BUFFER_FIELD(buf2_base), + .size_offset = HOST_BUFFER_FIELD(buf1_buf2_size), }, { .mem_type = WMFW_ADSP2_YM, - .base_offset = HOST_BUFFER_FIELD(Y_buf_base), - .size_offset = HOST_BUFFER_FIELD(wrap), + .base_offset = HOST_BUFFER_FIELD(buf3_base), + .size_offset = HOST_BUFFER_FIELD(buf_total_size), }, }; @@ -3345,7 +3345,7 @@ static int wm_adsp_buffer_populate(struct wm_adsp_compr_buf *buf) region->cumulative_size = offset; adsp_dbg(buf->dsp, - "region=%d type=%d base=%04x off=%04x size=%04x\n", + "region=%d type=%d base=%08x off=%08x size=%08x\n", i, region->mem_type, region->base_addr, region->offset, region->cumulative_size); } diff --git a/sound/soc/davinci/davinci-mcasp.c b/sound/soc/davinci/davinci-mcasp.c @@ -1041,6 +1041,42 @@ static int davinci_mcasp_calc_clk_div(struct davinci_mcasp *mcasp, return error_ppm; } +static inline u32 davinci_mcasp_tx_delay(struct davinci_mcasp *mcasp) +{ + if (!mcasp->txnumevt) + return 0; + + return mcasp_get_reg(mcasp, mcasp->fifo_base + MCASP_WFIFOSTS_OFFSET); +} + +static inline u32 davinci_mcasp_rx_delay(struct davinci_mcasp *mcasp) +{ + if (!mcasp->rxnumevt) + return 0; + + return mcasp_get_reg(mcasp, mcasp->fifo_base + MCASP_RFIFOSTS_OFFSET); +} + +static snd_pcm_sframes_t davinci_mcasp_delay( + struct snd_pcm_substream *substream, + struct snd_soc_dai *cpu_dai) +{ + struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai); + u32 fifo_use; + + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + fifo_use = davinci_mcasp_tx_delay(mcasp); + else + fifo_use = davinci_mcasp_rx_delay(mcasp); + + /* + * Divide the used locations with the channel count to get the + * FIFO usage in samples (don't care about partial samples in the + * buffer). + */ + return fifo_use / substream->runtime->channels; +} + static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params, struct snd_soc_dai *cpu_dai) @@ -1365,6 +1401,7 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = { .startup = davinci_mcasp_startup, .shutdown = davinci_mcasp_shutdown, .trigger = davinci_mcasp_trigger, + .delay = davinci_mcasp_delay, .hw_params = davinci_mcasp_hw_params, .set_fmt = davinci_mcasp_set_dai_fmt, .set_clkdiv = davinci_mcasp_set_clkdiv, diff --git a/sound/soc/fsl/fsl_asrc_dma.c b/sound/soc/fsl/fsl_asrc_dma.c @@ -151,7 +151,7 @@ static int fsl_asrc_dma_hw_params(struct snd_pcm_substream *substream, int ret; /* Fetch the Back-End dma_data from DPCM */ - list_for_each_entry(dpcm, &rtd->dpcm[stream].be_clients, list_be) { + for_each_dpcm_be(rtd, stream, dpcm) { struct snd_soc_pcm_runtime *be = dpcm->be; struct snd_pcm_substream *substream_be; struct snd_soc_dai *dai = be->cpu_dai; diff --git a/sound/soc/fsl/fsl_esai.c b/sound/soc/fsl/fsl_esai.c @@ -807,7 +807,7 @@ static int fsl_esai_probe(struct platform_device *pdev) return -ENOMEM; esai_priv->pdev = pdev; - strncpy(esai_priv->name, np->name, sizeof(esai_priv->name) - 1); + snprintf(esai_priv->name, sizeof(esai_priv->name), "%pOFn", np); /* Get the addresses and IRQ */ res = platform_get_resource(pdev, IORESOURCE_MEM, 0); diff --git a/sound/soc/fsl/fsl_utils.c b/sound/soc/fsl/fsl_utils.c @@ -57,8 +57,8 @@ int fsl_asoc_get_dma_channel(struct device_node *ssi_np, of_node_put(dma_channel_np); return ret; } - snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%s", - (unsigned long long) res.start, dma_channel_np->name); + snprintf((char *)dai->platform_name, DAI_NAME_SIZE, "%llx.%pOFn", + (unsigned long long) res.start, dma_channel_np); iprop = of_get_property(dma_channel_np, "cell-index", NULL); if (!iprop) { diff --git a/sound/soc/fsl/pcm030-audio-fabric.c b/sound/soc/fsl/pcm030-audio-fabric.c @@ -57,6 +57,7 @@ static int pcm030_fabric_probe(struct platform_device *op) struct device_node *platform_np; struct snd_soc_card *card = &pcm030_card; struct pcm030_audio_data *pdata; + struct snd_soc_dai_link *dai_link; int ret; int i; @@ -78,8 +79,8 @@ static int pcm030_fabric_probe(struct platform_device *op) return -ENODEV; } - for (i = 0; i < card->num_links; i++) - card->dai_link[i].platform_of_node = platform_np; + for_each_card_prelinks(card, i, dai_link) + dai_link->platform_of_node = platform_np; ret = request_module("snd-soc-wm9712"); if (ret) diff --git a/sound/soc/generic/audio-graph-card.c b/sound/soc/generic/audio-graph-card.c @@ -25,6 +25,8 @@ struct graph_card_data { struct graph_dai_props { struct asoc_simple_dai cpu_dai; struct asoc_simple_dai codec_dai; + struct snd_soc_dai_link_component codecs; /* single codec */ + struct snd_soc_dai_link_component platform; unsigned int mclk_fs; } *dai_props; unsigned int mclk_fs; @@ -180,7 +182,8 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, if (ret < 0) goto dai_link_of_err; - of_property_read_u32(rcpu_ep, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(cpu_ep, "mclk-fs", &dai_props->mclk_fs); + of_property_read_u32(codec_ep, "mclk-fs", &dai_props->mclk_fs); ret = asoc_simple_card_parse_graph_cpu(cpu_ep, dai_link); if (ret < 0) @@ -213,7 +216,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *cpu_port, ret = asoc_simple_card_set_dailink_name(dev, dai_link, "%s-%s", dai_link->cpu_dai_name, - dai_link->codec_dai_name); + dai_link->codecs->dai_name); if (ret < 0) goto dai_link_of_err; @@ -299,7 +302,7 @@ static int asoc_graph_card_probe(struct platform_device *pdev) struct graph_dai_props *dai_props; struct device *dev = &pdev->dev; struct snd_soc_card *card; - int num, ret; + int num, ret, i; /* Allocate the private data and the DAI link array */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -315,6 +318,18 @@ static int asoc_graph_card_probe(struct platform_device *pdev) if (!dai_props || !dai_link) return -ENOMEM; + /* + * Use snd_soc_dai_link_component instead of legacy style + * It is codec only. but cpu/platform will be supported in the future. + * see + * soc-core.c :: snd_soc_init_multicodec() + */ + for (i = 0; i < num; i++) { + dai_link[i].codecs = &dai_props[i].codecs; + dai_link[i].num_codecs = 1; + dai_link[i].platform = &dai_props[i].platform; + } + priv->pa_gpio = devm_gpiod_get_optional(dev, "pa", GPIOD_OUT_LOW); if (IS_ERR(priv->pa_gpio)) { ret = PTR_ERR(priv->pa_gpio); diff --git a/sound/soc/generic/audio-graph-scu-card.c b/sound/soc/generic/audio-graph-scu-card.c @@ -25,7 +25,11 @@ struct graph_card_data { struct snd_soc_card snd_card; struct snd_soc_codec_conf codec_conf; - struct asoc_simple_dai *dai_props; + struct graph_dai_props { + struct asoc_simple_dai dai; + struct snd_soc_dai_link_component codecs; + struct snd_soc_dai_link_component platform; + } *dai_props; struct snd_soc_dai_link *dai_link; struct asoc_simple_card_data adata; }; @@ -39,18 +43,18 @@ static int asoc_graph_card_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); - return asoc_simple_card_clk_enable(dai_props); + return asoc_simple_card_clk_enable(&dai_props->dai); } static void asoc_graph_card_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, rtd->num); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, rtd->num); - asoc_simple_card_clk_disable(dai_props); + asoc_simple_card_clk_disable(&dai_props->dai); } static const struct snd_soc_ops asoc_graph_card_ops = { @@ -63,7 +67,7 @@ static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) struct graph_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *dai; struct snd_soc_dai_link *dai_link; - struct asoc_simple_dai *dai_props; + struct graph_dai_props *dai_props; int num = rtd->num; dai_link = graph_priv_to_link(priv, num); @@ -72,7 +76,7 @@ static int asoc_graph_card_dai_init(struct snd_soc_pcm_runtime *rtd) rtd->cpu_dai : rtd->codec_dai; - return asoc_simple_card_init_dai(dai, dai_props); + return asoc_simple_card_init_dai(dai, &dai_props->dai); } static int asoc_graph_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, @@ -92,15 +96,18 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep, { struct device *dev = graph_priv_to_dev(priv); struct snd_soc_dai_link *dai_link = graph_priv_to_link(priv, idx); - struct asoc_simple_dai *dai_props = graph_priv_to_props(priv, idx); + struct graph_dai_props *dai_props = graph_priv_to_props(priv, idx); struct snd_soc_card *card = graph_priv_to_card(priv); int ret; if (is_fe) { + struct snd_soc_dai_link_component *codecs; + /* BE is dummy */ - dai_link->codec_of_node = NULL; - dai_link->codec_dai_name = "snd-soc-dummy-dai"; - dai_link->codec_name = "snd-soc-dummy"; + codecs = dai_link->codecs; + codecs->of_node = NULL; + codecs->dai_name = "snd-soc-dummy-dai"; + codecs->name = "snd-soc-dummy"; /* FE settings */ dai_link->dynamic = 1; @@ -110,7 +117,7 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep, if (ret) return ret; - ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, dai_props); + ret = asoc_simple_card_parse_clk_cpu(dev, ep, dai_link, &dai_props->dai); if (ret < 0) return ret; @@ -137,23 +144,23 @@ static int asoc_graph_card_dai_link_of(struct device_node *ep, if (ret < 0) return ret; - ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, dai_props); + ret = asoc_simple_card_parse_clk_codec(dev, ep, dai_link, &dai_props->dai); if (ret < 0) return ret; ret = asoc_simple_card_set_dailink_name(dev, dai_link, "be.%s", - dai_link->codec_dai_name); + dai_link->codecs->dai_name); if (ret < 0) return ret; snd_soc_of_parse_audio_prefix(card, &priv->codec_conf, - dai_link->codec_of_node, + dai_link->codecs->of_node, "prefix"); } - ret = asoc_simple_card_of_parse_tdm(ep, dai_props); + ret = asoc_simple_card_of_parse_tdm(ep, &dai_props->dai); if (ret) return ret; @@ -331,10 +338,10 @@ static int asoc_graph_card_probe(struct platform_device *pdev) { struct graph_card_data *priv; struct snd_soc_dai_link *dai_link; - struct asoc_simple_dai *dai_props; + struct graph_dai_props *dai_props; struct device *dev = &pdev->dev; struct snd_soc_card *card; - int num, ret; + int num, ret, i; /* Allocate the private data and the DAI link array */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -350,6 +357,18 @@ static int asoc_graph_card_probe(struct platform_device *pdev) if (!dai_props || !dai_link) return -ENOMEM; + /* + * Use snd_soc_dai_link_component instead of legacy style + * It is codec only. but cpu/platform will be supported in the future. + * see + * soc-core.c :: snd_soc_init_multicodec() + */ + for (i = 0; i < num; i++) { + dai_link[i].codecs = &dai_props[i].codecs; + dai_link[i].num_codecs = 1; + dai_link[i].platform = &dai_props[i].platform; + } + priv->dai_props = dai_props; priv->dai_link = dai_link; diff --git a/sound/soc/generic/simple-card-utils.c b/sound/soc/generic/simple-card-utils.c @@ -173,12 +173,24 @@ int asoc_simple_card_parse_clk(struct device *dev, struct device_node *node, struct device_node *dai_of_node, struct asoc_simple_dai *simple_dai, - const char *name) + const char *dai_name, + struct snd_soc_dai_link_component *dlc) { struct clk *clk; u32 val; /* + * Use snd_soc_dai_link_component instead of legacy style. + * It is only for codec, but cpu will be supported in the future. + * see + * soc-core.c :: snd_soc_init_multicodec() + */ + if (dlc) { + dai_of_node = dlc->of_node; + dai_name = dlc->dai_name; + } + + /* * Parse dai->sysclk come from "clocks = <&xxx>" * (if system has common clock) * or "system-clock-frequency = <xxx>" @@ -200,7 +212,7 @@ int asoc_simple_card_parse_clk(struct device *dev, if (of_property_read_bool(node, "system-clock-direction-out")) simple_dai->clk_direction = SND_SOC_CLOCK_OUT; - dev_dbg(dev, "%s : sysclk = %d, direction %d\n", name, + dev_dbg(dev, "%s : sysclk = %d, direction %d\n", dai_name, simple_dai->sysclk, simple_dai->clk_direction); return 0; @@ -208,6 +220,7 @@ int asoc_simple_card_parse_clk(struct device *dev, EXPORT_SYMBOL_GPL(asoc_simple_card_parse_clk); int asoc_simple_card_parse_dai(struct device_node *node, + struct snd_soc_dai_link_component *dlc, struct device_node **dai_of_node, const char **dai_name, const char *list_name, @@ -221,6 +234,17 @@ int asoc_simple_card_parse_dai(struct device_node *node, return 0; /* + * Use snd_soc_dai_link_component instead of legacy style. + * It is only for codec, but cpu will be supported in the future. + * see + * soc-core.c :: snd_soc_init_multicodec() + */ + if (dlc) { + dai_name = &dlc->dai_name; + dai_of_node = &dlc->of_node; + } + + /* * Get node via "sound-dai = <&phandle port>" * it will be used as xxx_of_node on soc_bind_dai_link() */ @@ -278,6 +302,7 @@ static int asoc_simple_card_get_dai_id(struct device_node *ep) } int asoc_simple_card_parse_graph_dai(struct device_node *ep, + struct snd_soc_dai_link_component *dlc, struct device_node **dai_of_node, const char **dai_name) { @@ -285,6 +310,17 @@ int asoc_simple_card_parse_graph_dai(struct device_node *ep, struct of_phandle_args args; int ret; + /* + * Use snd_soc_dai_link_component instead of legacy style. + * It is only for codec, but cpu will be supported in the future. + * see + * soc-core.c :: snd_soc_init_multicodec() + */ + if (dlc) { + dai_name = &dlc->dai_name; + dai_of_node = &dlc->of_node; + } + if (!ep) return 0; if (!dai_name) @@ -340,10 +376,11 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_init_dai); int asoc_simple_card_canonicalize_dailink(struct snd_soc_dai_link *dai_link) { /* Assumes platform == cpu */ - if (!dai_link->platform_of_node) - dai_link->platform_of_node = dai_link->cpu_of_node; + if (!dai_link->platform->of_node) + dai_link->platform->of_node = dai_link->cpu_of_node; return 0; + } EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_dailink); @@ -367,13 +404,11 @@ EXPORT_SYMBOL_GPL(asoc_simple_card_canonicalize_cpu); int asoc_simple_card_clean_reference(struct snd_soc_card *card) { struct snd_soc_dai_link *dai_link; - int num_links; + int i; - for (num_links = 0, dai_link = card->dai_link; - num_links < card->num_links; - num_links++, dai_link++) { + for_each_card_prelinks(card, i, dai_link) { of_node_put(dai_link->cpu_of_node); - of_node_put(dai_link->codec_of_node); + of_node_put(dai_link->codecs->of_node); } return 0; } diff --git a/sound/soc/generic/simple-card.c b/sound/soc/generic/simple-card.c @@ -20,6 +20,8 @@ struct simple_card_data { struct simple_dai_props { struct asoc_simple_dai cpu_dai; struct asoc_simple_dai codec_dai; + struct snd_soc_dai_link_component codecs; /* single codec */ + struct snd_soc_dai_link_component platform; unsigned int mclk_fs; } *dai_props; unsigned int mclk_fs; @@ -234,7 +236,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *node, ret = asoc_simple_card_set_dailink_name(dev, dai_link, "%s-%s", dai_link->cpu_dai_name, - dai_link->codec_dai_name); + dai_link->codecs->dai_name); if (ret < 0) goto dai_link_of_err; @@ -363,7 +365,7 @@ static int asoc_simple_card_probe(struct platform_device *pdev) struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; struct snd_soc_card *card; - int num, ret; + int num, ret, i; /* Get the number of DAI links */ if (np && of_get_child_by_name(np, PREFIX "dai-link")) @@ -381,6 +383,18 @@ static int asoc_simple_card_probe(struct platform_device *pdev) if (!dai_props || !dai_link) return -ENOMEM; + /* + * Use snd_soc_dai_link_component instead of legacy style + * It is codec only. but cpu/platform will be supported in the future. + * see + * soc-core.c :: snd_soc_init_multicodec() + */ + for (i = 0; i < num; i++) { + dai_link[i].codecs = &dai_props[i].codecs; + dai_link[i].num_codecs = 1; + dai_link[i].platform = &dai_props[i].platform; + } + priv->dai_props = dai_props; priv->dai_link = dai_link; @@ -403,6 +417,8 @@ static int asoc_simple_card_probe(struct platform_device *pdev) } else { struct asoc_simple_card_info *cinfo; + struct snd_soc_dai_link_component *codecs; + struct snd_soc_dai_link_component *platform; cinfo = dev->platform_data; if (!cinfo) { @@ -419,13 +435,17 @@ static int asoc_simple_card_probe(struct platform_device *pdev) return -EINVAL; } + codecs = dai_link->codecs; + codecs->name = cinfo->codec; + codecs->dai_name = cinfo->codec_dai.name; + + platform = dai_link->platform; + platform->name = cinfo->platform; + card->name = (cinfo->card) ? cinfo->card : cinfo->name; dai_link->name = cinfo->name; dai_link->stream_name = cinfo->name; - dai_link->platform_name = cinfo->platform; - dai_link->codec_name = cinfo->codec; dai_link->cpu_dai_name = cinfo->cpu_dai.name; - dai_link->codec_dai_name = cinfo->codec_dai.name; dai_link->dai_fmt = cinfo->daifmt; dai_link->init = asoc_simple_card_dai_init; memcpy(&priv->dai_props->cpu_dai, &cinfo->cpu_dai, diff --git a/sound/soc/generic/simple-scu-card.c b/sound/soc/generic/simple-scu-card.c @@ -22,7 +22,11 @@ struct simple_card_data { struct snd_soc_card snd_card; struct snd_soc_codec_conf codec_conf; - struct asoc_simple_dai *dai_props; + struct simple_dai_props { + struct asoc_simple_dai dai; + struct snd_soc_dai_link_component codecs; + struct snd_soc_dai_link_component platform; + } *dai_props; struct snd_soc_dai_link *dai_link; struct asoc_simple_card_data adata; }; @@ -40,20 +44,20 @@ static int asoc_simple_card_startup(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct asoc_simple_dai *dai_props = + struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); - return asoc_simple_card_clk_enable(dai_props); + return asoc_simple_card_clk_enable(&dai_props->dai); } static void asoc_simple_card_shutdown(struct snd_pcm_substream *substream) { struct snd_soc_pcm_runtime *rtd = substream->private_data; struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); - struct asoc_simple_dai *dai_props = + struct simple_dai_props *dai_props = simple_priv_to_props(priv, rtd->num); - asoc_simple_card_clk_disable(dai_props); + asoc_simple_card_clk_disable(&dai_props->dai); } static const struct snd_soc_ops asoc_simple_card_ops = { @@ -66,7 +70,7 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) struct simple_card_data *priv = snd_soc_card_get_drvdata(rtd->card); struct snd_soc_dai *dai; struct snd_soc_dai_link *dai_link; - struct asoc_simple_dai *dai_props; + struct simple_dai_props *dai_props; int num = rtd->num; dai_link = simple_priv_to_link(priv, num); @@ -75,7 +79,7 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd) rtd->cpu_dai : rtd->codec_dai; - return asoc_simple_card_init_dai(dai, dai_props); + return asoc_simple_card_init_dai(dai, &dai_props->dai); } static int asoc_simple_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd, @@ -95,17 +99,19 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, { struct device *dev = simple_priv_to_dev(priv); struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx); - struct asoc_simple_dai *dai_props = simple_priv_to_props(priv, idx); + struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx); struct snd_soc_card *card = simple_priv_to_card(priv); int ret; if (is_fe) { int is_single_links = 0; + struct snd_soc_dai_link_component *codecs; /* BE is dummy */ - dai_link->codec_of_node = NULL; - dai_link->codec_dai_name = "snd-soc-dummy-dai"; - dai_link->codec_name = "snd-soc-dummy"; + codecs = dai_link->codecs; + codecs->of_node = NULL; + codecs->dai_name = "snd-soc-dummy-dai"; + codecs->name = "snd-soc-dummy"; /* FE settings */ dai_link->dynamic = 1; @@ -116,7 +122,7 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, if (ret) return ret; - ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, dai_props); + ret = asoc_simple_card_parse_clk_cpu(dev, np, dai_link, &dai_props->dai); if (ret < 0) return ret; @@ -141,23 +147,23 @@ static int asoc_simple_card_dai_link_of(struct device_node *np, if (ret < 0) return ret; - ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, dai_props); + ret = asoc_simple_card_parse_clk_codec(dev, np, dai_link, &dai_props->dai); if (ret < 0) return ret; ret = asoc_simple_card_set_dailink_name(dev, dai_link, "be.%s", - dai_link->codec_dai_name); + dai_link->codecs->dai_name); if (ret < 0) return ret; snd_soc_of_parse_audio_prefix(card, &priv->codec_conf, - dai_link->codec_of_node, + dai_link->codecs->of_node, PREFIX "prefix"); } - ret = asoc_simple_card_of_parse_tdm(np, dai_props); + ret = asoc_simple_card_of_parse_tdm(np, &dai_props->dai); if (ret) return ret; @@ -230,11 +236,11 @@ static int asoc_simple_card_probe(struct platform_device *pdev) { struct simple_card_data *priv; struct snd_soc_dai_link *dai_link; - struct asoc_simple_dai *dai_props; + struct simple_dai_props *dai_props; struct snd_soc_card *card; struct device *dev = &pdev->dev; struct device_node *np = dev->of_node; - int num, ret; + int num, ret, i; /* Allocate the private data */ priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL); @@ -248,6 +254,18 @@ static int asoc_simple_card_probe(struct platform_device *pdev) if (!dai_props || !dai_link) return -ENOMEM; + /* + * Use snd_soc_dai_link_component instead of legacy style + * It is codec only. but cpu/platform will be supported in the future. + * see + * soc-core.c :: snd_soc_init_multicodec() + */ + for (i = 0; i < num; i++) { + dai_link[i].codecs = &dai_props[i].codecs; + dai_link[i].num_codecs = 1; + dai_link[i].platform = &dai_props[i].platform; + } + priv->dai_props = dai_props; priv->dai_link = dai_link; diff --git a/sound/soc/hisilicon/hi6210-i2s.c b/sound/soc/hisilicon/hi6210-i2s.c @@ -269,13 +269,13 @@ static int hi6210_i2s_hw_params(struct snd_pcm_substream *substream, switch (params_format(params)) { case SNDRV_PCM_FORMAT_U16_LE: signed_data = HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT; - /* fallthru */ + /* fall through */ case SNDRV_PCM_FORMAT_S16_LE: bits = HII2S_BITS_16; break; case SNDRV_PCM_FORMAT_U24_LE: signed_data = HII2S_I2S_CFG__S2_CODEC_DATA_FORMAT; - /* fallthru */ + /* fall through */ case SNDRV_PCM_FORMAT_S24_LE: bits = HII2S_BITS_24; break; diff --git a/sound/soc/intel/atom/sst-mfld-platform-pcm.c b/sound/soc/intel/atom/sst-mfld-platform-pcm.c @@ -765,7 +765,7 @@ static int sst_soc_prepare(struct device *dev) snd_soc_poweroff(drv->soc_card->dev); /* set the SSPs to idle */ - list_for_each_entry(rtd, &drv->soc_card->rtd_list, list) { + for_each_card_rtds(drv->soc_card, rtd) { struct snd_soc_dai *dai = rtd->cpu_dai; if (dai->active) { @@ -786,7 +786,7 @@ static void sst_soc_complete(struct device *dev) return; /* restart SSPs */ - list_for_each_entry(rtd, &drv->soc_card->rtd_list, list) { + for_each_card_rtds(drv->soc_card, rtd) { struct snd_soc_dai *dai = rtd->cpu_dai; if (dai->active) { diff --git a/sound/soc/intel/boards/Kconfig b/sound/soc/intel/boards/Kconfig @@ -279,6 +279,28 @@ config SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH This adds support for ASoC Onboard Codec I2S machine driver. This will create an alsa sound card for DA7219 + MAX98357A I2S audio codec. Say Y if you have such a device. + +config SND_SOC_INTEL_KBL_DA7219_MAX98927_MACH + tristate "KBL with DA7219 and MAX98927 in I2S Mode" + depends on MFD_INTEL_LPSS && I2C && ACPI + select SND_SOC_DA7219 + select SND_SOC_MAX98927 + select SND_SOC_DMIC + select SND_SOC_HDAC_HDMI + help + This adds support for ASoC Onboard Codec I2S machine driver. This will + create an alsa sound card for DA7219 + MAX98927 I2S audio codec. + Say Y if you have such a device. + If unsure select "N". + +config SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH + tristate "SKL/KBL/BXT/APL with HDA Codecs" + select SND_SOC_HDAC_HDMI + select SND_SOC_HDAC_HDA + help + This adds support for ASoC machine driver for Intel platforms + SKL/KBL/BXT/APL with iDisp, HDA audio codecs. + Say Y or m if you have such a device. This is a recommended option. If unsure select "N". config SND_SOC_INTEL_GLK_RT5682_MAX98357A_MACH diff --git a/sound/soc/intel/boards/Makefile b/sound/soc/intel/boards/Makefile @@ -17,9 +17,11 @@ snd-soc-sst-byt-cht-da7213-objs := bytcht_da7213.o snd-soc-sst-byt-cht-es8316-objs := bytcht_es8316.o snd-soc-sst-byt-cht-nocodec-objs := bytcht_nocodec.o snd-soc-kbl_da7219_max98357a-objs := kbl_da7219_max98357a.o +snd-soc-kbl_da7219_max98927-objs := kbl_da7219_max98927.o snd-soc-kbl_rt5663_max98927-objs := kbl_rt5663_max98927.o snd-soc-kbl_rt5663_rt5514_max98927-objs := kbl_rt5663_rt5514_max98927.o snd-soc-skl_rt286-objs := skl_rt286.o +snd-soc-skl_hda_dsp-objs := skl_hda_dsp_generic.o skl_hda_dsp_common.o snd-skl_nau88l25_max98357a-objs := skl_nau88l25_max98357a.o snd-soc-skl_nau88l25_ssm4567-objs := skl_nau88l25_ssm4567.o @@ -41,8 +43,10 @@ obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_DA7213_MACH) += snd-soc-sst-byt-cht-da7213.o obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_ES8316_MACH) += snd-soc-sst-byt-cht-es8316.o obj-$(CONFIG_SND_SOC_INTEL_BYT_CHT_NOCODEC_MACH) += snd-soc-sst-byt-cht-nocodec.o obj-$(CONFIG_SND_SOC_INTEL_KBL_DA7219_MAX98357A_MACH) += snd-soc-kbl_da7219_max98357a.o +obj-$(CONFIG_SND_SOC_INTEL_KBL_DA7219_MAX98927_MACH) += snd-soc-kbl_da7219_max98927.o obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_MAX98927_MACH) += snd-soc-kbl_rt5663_max98927.o obj-$(CONFIG_SND_SOC_INTEL_KBL_RT5663_RT5514_MAX98927_MACH) += snd-soc-kbl_rt5663_rt5514_max98927.o obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH) += snd-skl_nau88l25_max98357a.o obj-$(CONFIG_SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH) += snd-soc-skl_nau88l25_ssm4567.o +obj-$(CONFIG_SND_SOC_INTEL_SKL_HDA_DSP_GENERIC_MACH) += snd-soc-skl_hda_dsp.o diff --git a/sound/soc/intel/boards/broadwell.c b/sound/soc/intel/boards/broadwell.c @@ -223,7 +223,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = { static int broadwell_suspend(struct snd_soc_card *card){ struct snd_soc_component *component; - list_for_each_entry(component, &card->component_dev_list, card_list) { + for_each_card_components(card, component) { if (!strcmp(component->name, "i2c-INT343A:00")) { dev_dbg(component->dev, "disabling jack detect before going to suspend.\n"); @@ -237,7 +237,7 @@ static int broadwell_suspend(struct snd_soc_card *card){ static int broadwell_resume(struct snd_soc_card *card){ struct snd_soc_component *component; - list_for_each_entry(component, &card->component_dev_list, card_list) { + for_each_card_components(card, component) { if (!strcmp(component->name, "i2c-INT343A:00")) { dev_dbg(component->dev, "enabling jack detect for resume.\n"); diff --git a/sound/soc/intel/boards/bytcr_rt5640.c b/sound/soc/intel/boards/bytcr_rt5640.c @@ -1048,7 +1048,7 @@ static int byt_rt5640_suspend(struct snd_soc_card *card) if (!BYT_RT5640_JDSRC(byt_rt5640_quirk)) return 0; - list_for_each_entry(component, &card->component_dev_list, card_list) { + for_each_card_components(card, component) { if (!strcmp(component->name, byt_rt5640_codec_name)) { dev_dbg(component->dev, "disabling jack detect before suspend\n"); snd_soc_component_set_jack(component, NULL, NULL); @@ -1067,7 +1067,7 @@ static int byt_rt5640_resume(struct snd_soc_card *card) if (!BYT_RT5640_JDSRC(byt_rt5640_quirk)) return 0; - list_for_each_entry(component, &card->component_dev_list, card_list) { + for_each_card_components(card, component) { if (!strcmp(component->name, byt_rt5640_codec_name)) { dev_dbg(component->dev, "re-enabling jack detect after resume\n"); snd_soc_component_set_jack(component, &priv->jack, NULL); diff --git a/sound/soc/intel/boards/bytcr_rt5651.c b/sound/soc/intel/boards/bytcr_rt5651.c @@ -742,7 +742,7 @@ static int byt_rt5651_suspend(struct snd_soc_card *card) if (!BYT_RT5651_JDSRC(byt_rt5651_quirk)) return 0; - list_for_each_entry(component, &card->component_dev_list, card_list) { + for_each_card_components(card, component) { if (!strcmp(component->name, byt_rt5651_codec_name)) { dev_dbg(component->dev, "disabling jack detect before suspend\n"); snd_soc_component_set_jack(component, NULL, NULL); @@ -761,7 +761,7 @@ static int byt_rt5651_resume(struct snd_soc_card *card) if (!BYT_RT5651_JDSRC(byt_rt5651_quirk)) return 0; - list_for_each_entry(component, &card->component_dev_list, card_list) { + for_each_card_components(card, component) { if (!strcmp(component->name, byt_rt5651_codec_name)) { dev_dbg(component->dev, "re-enabling jack detect after resume\n"); snd_soc_component_set_jack(component, &priv->jack, NULL); diff --git a/sound/soc/intel/boards/cht_bsw_rt5672.c b/sound/soc/intel/boards/cht_bsw_rt5672.c @@ -16,6 +16,7 @@ * General Public License for more details. */ +#include <linux/input.h> #include <linux/module.h> #include <linux/platform_device.h> #include <linux/slab.h> @@ -212,6 +213,10 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime) if (ret) return ret; + snd_jack_set_key(ctx->headset.jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(ctx->headset.jack, SND_JACK_BTN_1, KEY_VOLUMEUP); + snd_jack_set_key(ctx->headset.jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN); + rt5670_set_jack_detect(component, &ctx->headset); if (ctx->mclk) { /* @@ -342,7 +347,7 @@ static int cht_suspend_pre(struct snd_soc_card *card) struct snd_soc_component *component; struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); - list_for_each_entry(component, &card->component_dev_list, card_list) { + for_each_card_components(card, component) { if (!strncmp(component->name, ctx->codec_name, sizeof(ctx->codec_name))) { @@ -359,7 +364,7 @@ static int cht_resume_post(struct snd_soc_card *card) struct snd_soc_component *component; struct cht_mc_private *ctx = snd_soc_card_get_drvdata(card); - list_for_each_entry(component, &card->component_dev_list, card_list) { + for_each_card_components(card, component) { if (!strncmp(component->name, ctx->codec_name, sizeof(ctx->codec_name))) { diff --git a/sound/soc/intel/boards/kbl_da7219_max98927.c b/sound/soc/intel/boards/kbl_da7219_max98927.c @@ -0,0 +1,983 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2018 Intel Corporation. + +/* + * Intel Kabylake I2S Machine Driver with MAX98927 & DA7219 Codecs + * + * Modified from: + * Intel Kabylake I2S Machine driver supporting MAX98927 and + * RT5663 codecs + */ + +#include <linux/input.h> +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../../codecs/da7219.h" +#include "../../codecs/hdac_hdmi.h" +#include "../skylake/skl.h" +#include "../../codecs/da7219-aad.h" + +#define KBL_DIALOG_CODEC_DAI "da7219-hifi" +#define MAX98927_CODEC_DAI "max98927-aif1" +#define MAXIM_DEV0_NAME "i2c-MX98927:00" +#define MAXIM_DEV1_NAME "i2c-MX98927:01" +#define DUAL_CHANNEL 2 +#define QUAD_CHANNEL 4 +#define NAME_SIZE 32 + +static struct snd_soc_card *kabylake_audio_card; +static struct snd_soc_jack kabylake_hdmi[3]; + +struct kbl_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + int device; +}; + +struct kbl_codec_private { + struct snd_soc_jack kabylake_headset; + struct list_head hdmi_pcm_list; +}; + +enum { + KBL_DPCM_AUDIO_PB = 0, + KBL_DPCM_AUDIO_CP, + KBL_DPCM_AUDIO_ECHO_REF_CP, + KBL_DPCM_AUDIO_REF_CP, + KBL_DPCM_AUDIO_DMIC_CP, + KBL_DPCM_AUDIO_HDMI1_PB, + KBL_DPCM_AUDIO_HDMI2_PB, + KBL_DPCM_AUDIO_HDMI3_PB, + KBL_DPCM_AUDIO_HS_PB, +}; + +static int platform_clock_control(struct snd_soc_dapm_widget *w, + struct snd_kcontrol *k, int event) +{ + struct snd_soc_dapm_context *dapm = w->dapm; + struct snd_soc_card *card = dapm->card; + struct snd_soc_dai *codec_dai; + int ret = 0; + + codec_dai = snd_soc_card_get_codec_dai(card, KBL_DIALOG_CODEC_DAI); + if (!codec_dai) { + dev_err(card->dev, "Codec dai not found; Unable to set/unset codec pll\n"); + return -EIO; + } + + /* Configure sysclk for codec */ + ret = snd_soc_dai_set_sysclk(codec_dai, DA7219_CLKSRC_MCLK, 24576000, + SND_SOC_CLOCK_IN); + if (ret) { + dev_err(card->dev, "can't set codec sysclk configuration\n"); + return ret; + } + + if (SND_SOC_DAPM_EVENT_OFF(event)) { + ret = snd_soc_dai_set_pll(codec_dai, 0, + DA7219_SYSCLK_MCLK, 0, 0); + if (ret) + dev_err(card->dev, "failed to stop PLL: %d\n", ret); + } else if (SND_SOC_DAPM_EVENT_ON(event)) { + ret = snd_soc_dai_set_pll(codec_dai, 0, DA7219_SYSCLK_PLL_SRM, + 0, DA7219_PLL_FREQ_OUT_98304); + if (ret) + dev_err(card->dev, "failed to start PLL: %d\n", ret); + } + + return ret; +} + +static const struct snd_kcontrol_new kabylake_controls[] = { + SOC_DAPM_PIN_SWITCH("Headphone Jack"), + SOC_DAPM_PIN_SWITCH("Headset Mic"), + SOC_DAPM_PIN_SWITCH("Left Spk"), + SOC_DAPM_PIN_SWITCH("Right Spk"), +}; + +static const struct snd_soc_dapm_widget kabylake_widgets[] = { + SND_SOC_DAPM_HP("Headphone Jack", NULL), + SND_SOC_DAPM_MIC("Headset Mic", NULL), + SND_SOC_DAPM_SPK("Left Spk", NULL), + SND_SOC_DAPM_SPK("Right Spk", NULL), + SND_SOC_DAPM_MIC("SoC DMIC", NULL), + SND_SOC_DAPM_SPK("DP", NULL), + SND_SOC_DAPM_SPK("HDMI", NULL), + SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0, + platform_clock_control, SND_SOC_DAPM_PRE_PMU | + SND_SOC_DAPM_POST_PMD), +}; + +static const struct snd_soc_dapm_route kabylake_map[] = { + /* speaker */ + { "Left Spk", NULL, "Left BE_OUT" }, + { "Right Spk", NULL, "Right BE_OUT" }, + + /* other jacks */ + { "DMic", NULL, "SoC DMIC" }, + + { "HDMI", NULL, "hif5 Output" }, + { "DP", NULL, "hif6 Output" }, + + /* CODEC BE connections */ + { "Left HiFi Playback", NULL, "ssp0 Tx" }, + { "Right HiFi Playback", NULL, "ssp0 Tx" }, + { "ssp0 Tx", NULL, "spk_out" }, + + /* IV feedback path */ + { "codec0_fb_in", NULL, "ssp0 Rx"}, + { "ssp0 Rx", NULL, "Left HiFi Capture" }, + { "ssp0 Rx", NULL, "Right HiFi Capture" }, + + /* AEC capture path */ + { "echo_ref_out", NULL, "ssp0 Rx" }, + + /* DMIC */ + { "dmic01_hifi", NULL, "DMIC01 Rx" }, + { "DMIC01 Rx", NULL, "DMIC AIF" }, + + { "hifi1", NULL, "iDisp1 Tx" }, + { "iDisp1 Tx", NULL, "iDisp1_out" }, + { "hifi2", NULL, "iDisp2 Tx" }, + { "iDisp2 Tx", NULL, "iDisp2_out" }, + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, +}; + +static const struct snd_soc_dapm_route kabylake_ssp1_map[] = { + { "Headphone Jack", NULL, "HPL" }, + { "Headphone Jack", NULL, "HPR" }, + + /* other jacks */ + { "MIC", NULL, "Headset Mic" }, + + /* CODEC BE connections */ + { "Playback", NULL, "ssp1 Tx" }, + { "ssp1 Tx", NULL, "codec1_out" }, + + { "hs_in", NULL, "ssp1 Rx" }, + { "ssp1 Rx", NULL, "Capture" }, + + { "Headphone Jack", NULL, "Platform Clock" }, + { "Headset Mic", NULL, "Platform Clock" }, +}; + +static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, + struct snd_pcm_hw_params *params) +{ + struct snd_soc_pcm_runtime *runtime = substream->private_data; + int ret = 0, j; + + for (j = 0; j < runtime->num_codecs; j++) { + struct snd_soc_dai *codec_dai = runtime->codec_dais[j]; + + if (!strcmp(codec_dai->component->name, MAXIM_DEV0_NAME)) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0x30, 3, 8, 16); + if (ret < 0) { + dev_err(runtime->dev, "DEV0 TDM slot err:%d\n", ret); + return ret; + } + } + if (!strcmp(codec_dai->component->name, MAXIM_DEV1_NAME)) { + ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xC0, 3, 8, 16); + if (ret < 0) { + dev_err(runtime->dev, "DEV1 TDM slot err:%d\n", ret); + return ret; + } + } + } + + return 0; +} + +static struct snd_soc_ops kabylake_ssp0_ops = { + .hw_params = kabylake_ssp0_hw_params, +}; + +static int kabylake_ssp_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *rate = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_RATE); + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + struct snd_mask *fmt = hw_param_mask(params, SNDRV_PCM_HW_PARAM_FORMAT); + struct snd_soc_dpcm *dpcm = container_of( + params, struct snd_soc_dpcm, hw_params); + struct snd_soc_dai_link *fe_dai_link = dpcm->fe->dai_link; + struct snd_soc_dai_link *be_dai_link = dpcm->be->dai_link; + + /* + * The ADSP will convert the FE rate to 48k, stereo, 24 bit + */ + if (!strcmp(fe_dai_link->name, "Kbl Audio Port") || + !strcmp(fe_dai_link->name, "Kbl Audio Headset Playback") || + !strcmp(fe_dai_link->name, "Kbl Audio Capture Port")) { + rate->min = rate->max = 48000; + channels->min = channels->max = 2; + snd_mask_none(fmt); + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S24_LE); + } + + /* + * The speaker on the SSP0 supports S16_LE and not S24_LE. + * thus changing the mask here + */ + if (!strcmp(be_dai_link->name, "SSP0-Codec")) + snd_mask_set(fmt, SNDRV_PCM_FORMAT_S16_LE); + + return 0; +} + +static int kabylake_da7219_codec_init(struct snd_soc_pcm_runtime *rtd) +{ + struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_component *component = rtd->codec_dai->component; + struct snd_soc_jack *jack; + struct snd_soc_card *card = rtd->card; + int ret; + + + ret = snd_soc_dapm_add_routes(&card->dapm, + kabylake_ssp1_map, + ARRAY_SIZE(kabylake_ssp1_map)); + + /* + * Headset buttons map to the google Reference headset. + * These can be configured by userspace. + */ + ret = snd_soc_card_jack_new(kabylake_audio_card, "Headset Jack", + SND_JACK_HEADSET | SND_JACK_BTN_0 | SND_JACK_BTN_1 | + SND_JACK_BTN_2 | SND_JACK_BTN_3 | SND_JACK_LINEOUT, + &ctx->kabylake_headset, NULL, 0); + if (ret) { + dev_err(rtd->dev, "Headset Jack creation failed: %d\n", ret); + return ret; + } + + jack = &ctx->kabylake_headset; + snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_PLAYPAUSE); + snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOICECOMMAND); + snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEUP); + snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOLUMEDOWN); + + da7219_aad_jack_det(component, &ctx->kabylake_headset); + + ret = snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC"); + if (ret) + dev_err(rtd->dev, "SoC DMIC - Ignore suspend failed %d\n", ret); + + return ret; +} + +static int kabylake_hdmi_init(struct snd_soc_pcm_runtime *rtd, int device) +{ + struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(rtd->card); + struct snd_soc_dai *dai = rtd->codec_dai; + struct kbl_hdmi_pcm *pcm; + + pcm = devm_kzalloc(rtd->card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + pcm->device = device; + pcm->codec_dai = dai; + + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +static int kabylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd) +{ + return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI1_PB); +} + +static int kabylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd) +{ + return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI2_PB); +} + +static int kabylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd) +{ + return kabylake_hdmi_init(rtd, KBL_DPCM_AUDIO_HDMI3_PB); +} + +static int kabylake_da7219_fe_init(struct snd_soc_pcm_runtime *rtd) +{ + struct snd_soc_dapm_context *dapm; + struct snd_soc_component *component = rtd->cpu_dai->component; + + dapm = snd_soc_component_get_dapm(component); + snd_soc_dapm_ignore_suspend(dapm, "Reference Capture"); + + return 0; +} + +static const unsigned int rates[] = { + 48000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_rates = { + .count = ARRAY_SIZE(rates), + .list = rates, + .mask = 0, +}; + +static const unsigned int channels[] = { + DUAL_CHANNEL, +}; + +static const struct snd_pcm_hw_constraint_list constraints_channels = { + .count = ARRAY_SIZE(channels), + .list = channels, + .mask = 0, +}; + +static unsigned int channels_quad[] = { + QUAD_CHANNEL, +}; + +static struct snd_pcm_hw_constraint_list constraints_channels_quad = { + .count = ARRAY_SIZE(channels_quad), + .list = channels_quad, + .mask = 0, +}; + +static int kbl_fe_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + /* + * On this platform for PCM device we support, + * 48Khz + * stereo + * 16 bit audio + */ + + runtime->hw.channels_max = DUAL_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels); + + runtime->hw.formats = SNDRV_PCM_FMTBIT_S16_LE; + snd_pcm_hw_constraint_msbits(runtime, 0, 16, 16); + + snd_pcm_hw_constraint_list(runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); + + return 0; +} + +static const struct snd_soc_ops kabylake_da7219_fe_ops = { + .startup = kbl_fe_startup, +}; + +static int kabylake_dmic_fixup(struct snd_soc_pcm_runtime *rtd, + struct snd_pcm_hw_params *params) +{ + struct snd_interval *channels = hw_param_interval(params, + SNDRV_PCM_HW_PARAM_CHANNELS); + + /* + * set BE channel constraint as user FE channels + */ + + if (params_channels(params) == 2) + channels->min = channels->max = 2; + else + channels->min = channels->max = 4; + + return 0; +} + +static int kabylake_dmic_startup(struct snd_pcm_substream *substream) +{ + struct snd_pcm_runtime *runtime = substream->runtime; + + runtime->hw.channels_min = runtime->hw.channels_max = QUAD_CHANNEL; + snd_pcm_hw_constraint_list(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_channels_quad); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, &constraints_rates); +} + +static struct snd_soc_ops kabylake_dmic_ops = { + .startup = kabylake_dmic_startup, +}; + +static const unsigned int rates_16000[] = { + 16000, +}; + +static const struct snd_pcm_hw_constraint_list constraints_16000 = { + .count = ARRAY_SIZE(rates_16000), + .list = rates_16000, +}; + +static const unsigned int ch_mono[] = { + 1, +}; +static const struct snd_pcm_hw_constraint_list constraints_refcap = { + .count = ARRAY_SIZE(ch_mono), + .list = ch_mono, +}; + +static int kabylake_refcap_startup(struct snd_pcm_substream *substream) +{ + substream->runtime->hw.channels_max = 1; + snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_CHANNELS, + &constraints_refcap); + + return snd_pcm_hw_constraint_list(substream->runtime, 0, + SNDRV_PCM_HW_PARAM_RATE, + &constraints_16000); +} + + +static struct snd_soc_ops skylaye_refcap_ops = { + .startup = kabylake_refcap_startup, +}; + +static struct snd_soc_codec_conf max98927_codec_conf[] = { + + { + .dev_name = MAXIM_DEV0_NAME, + .name_prefix = "Right", + }, + + { + .dev_name = MAXIM_DEV1_NAME, + .name_prefix = "Left", + }, +}; + +static struct snd_soc_dai_link_component ssp0_codec_components[] = { + { /* Left */ + .name = MAXIM_DEV0_NAME, + .dai_name = MAX98927_CODEC_DAI, + }, + + { /* For Right */ + .name = MAXIM_DEV1_NAME, + .dai_name = MAX98927_CODEC_DAI, + }, + +}; + +/* kabylake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link kabylake_dais[] = { + /* Front End DAI links */ + [KBL_DPCM_AUDIO_PB] = { + .name = "Kbl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .init = kabylake_da7219_fe_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .ops = &kabylake_da7219_fe_ops, + }, + [KBL_DPCM_AUDIO_CP] = { + .name = "Kbl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + .ops = &kabylake_da7219_fe_ops, + }, + [KBL_DPCM_AUDIO_ECHO_REF_CP] = { + .name = "Kbl Audio Echo Reference cap", + .stream_name = "Echoreference Capture", + .cpu_dai_name = "Echoref Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .capture_only = 1, + .nonatomic = 1, + }, + [KBL_DPCM_AUDIO_REF_CP] = { + .name = "Kbl Audio Reference cap", + .stream_name = "Wake on Voice", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &skylaye_refcap_ops, + }, + [KBL_DPCM_AUDIO_DMIC_CP] = { + .name = "Kbl Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &kabylake_dmic_ops, + }, + [KBL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Kbl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [KBL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Kbl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [KBL_DPCM_AUDIO_HDMI3_PB] = { + .name = "Kbl HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + [KBL_DPCM_AUDIO_HS_PB] = { + .name = "Kbl Audio Headset Playback", + .stream_name = "Headset Audio", + .cpu_dai_name = "System Pin2", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .nonatomic = 1, + .dynamic = 1, + .init = kabylake_da7219_fe_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .ops = &kabylake_da7219_fe_ops, + + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codecs = ssp0_codec_components, + .num_codecs = ARRAY_SIZE(ssp0_codec_components), + .dai_fmt = SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = kabylake_ssp_fixup, + .ops = &kabylake_ssp0_ops, + }, + { + /* SSP1 - Codec */ + .name = "SSP1-Codec", + .id = 1, + .cpu_dai_name = "SSP1 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codec_name = "i2c-DLGS7219:00", + .codec_dai_name = KBL_DIALOG_CODEC_DAI, + .init = kabylake_da7219_codec_init, + .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = kabylake_ssp_fixup, + .dpcm_playback = 1, + .dpcm_capture = 1, + }, + { + .name = "dmic01", + .id = 2, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:1f.3", + .be_hw_params_fixup = kabylake_dmic_fixup, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + .name = "iDisp1", + .id = 3, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = kabylake_hdmi1_init, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 4, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi2_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 5, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi3_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +/* kabylake digital audio interface glue - connects codec <--> CPU */ +static struct snd_soc_dai_link kabylake_max98927_dais[] = { + /* Front End DAI links */ + [KBL_DPCM_AUDIO_PB] = { + .name = "Kbl Audio Port", + .stream_name = "Audio", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .init = kabylake_da7219_fe_init, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .ops = &kabylake_da7219_fe_ops, + }, + [KBL_DPCM_AUDIO_CP] = { + .name = "Kbl Audio Capture Port", + .stream_name = "Audio Record", + .cpu_dai_name = "System Pin", + .platform_name = "0000:00:1f.3", + .dynamic = 1, + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .nonatomic = 1, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_capture = 1, + .ops = &kabylake_da7219_fe_ops, + }, + [KBL_DPCM_AUDIO_ECHO_REF_CP] = { + .name = "Kbl Audio Echo Reference cap", + .stream_name = "Echoreference Capture", + .cpu_dai_name = "Echoref Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .capture_only = 1, + .nonatomic = 1, + }, + [KBL_DPCM_AUDIO_REF_CP] = { + .name = "Kbl Audio Reference cap", + .stream_name = "Wake on Voice", + .cpu_dai_name = "Reference Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &skylaye_refcap_ops, + }, + [KBL_DPCM_AUDIO_DMIC_CP] = { + .name = "Kbl Audio DMIC cap", + .stream_name = "dmiccap", + .cpu_dai_name = "DMIC Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .init = NULL, + .dpcm_capture = 1, + .nonatomic = 1, + .dynamic = 1, + .ops = &kabylake_dmic_ops, + }, + [KBL_DPCM_AUDIO_HDMI1_PB] = { + .name = "Kbl HDMI Port1", + .stream_name = "Hdmi1", + .cpu_dai_name = "HDMI1 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [KBL_DPCM_AUDIO_HDMI2_PB] = { + .name = "Kbl HDMI Port2", + .stream_name = "Hdmi2", + .cpu_dai_name = "HDMI2 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = NULL, + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .nonatomic = 1, + .dynamic = 1, + }, + [KBL_DPCM_AUDIO_HDMI3_PB] = { + .name = "Kbl HDMI Port3", + .stream_name = "Hdmi3", + .cpu_dai_name = "HDMI3 Pin", + .codec_name = "snd-soc-dummy", + .codec_dai_name = "snd-soc-dummy-dai", + .platform_name = "0000:00:1f.3", + .trigger = { + SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST}, + .dpcm_playback = 1, + .init = NULL, + .nonatomic = 1, + .dynamic = 1, + }, + + /* Back End DAI links */ + { + /* SSP0 - Codec */ + .name = "SSP0-Codec", + .id = 0, + .cpu_dai_name = "SSP0 Pin", + .platform_name = "0000:00:1f.3", + .no_pcm = 1, + .codecs = ssp0_codec_components, + .num_codecs = ARRAY_SIZE(ssp0_codec_components), + .dai_fmt = SND_SOC_DAIFMT_DSP_B | + SND_SOC_DAIFMT_NB_NF | + SND_SOC_DAIFMT_CBS_CFS, + .dpcm_playback = 1, + .dpcm_capture = 1, + .ignore_pmdown_time = 1, + .be_hw_params_fixup = kabylake_ssp_fixup, + .ops = &kabylake_ssp0_ops, + }, + { + .name = "dmic01", + .id = 1, + .cpu_dai_name = "DMIC01 Pin", + .codec_name = "dmic-codec", + .codec_dai_name = "dmic-hifi", + .platform_name = "0000:00:1f.3", + .be_hw_params_fixup = kabylake_dmic_fixup, + .ignore_suspend = 1, + .dpcm_capture = 1, + .no_pcm = 1, + }, + { + .name = "iDisp1", + .id = 2, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .init = kabylake_hdmi1_init, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 3, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi2_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 4, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .platform_name = "0000:00:1f.3", + .init = kabylake_hdmi3_init, + .dpcm_playback = 1, + .no_pcm = 1, + }, +}; + +static int kabylake_card_late_probe(struct snd_soc_card *card) +{ + struct kbl_codec_private *ctx = snd_soc_card_get_drvdata(card); + struct kbl_hdmi_pcm *pcm; + struct snd_soc_component *component = NULL; + int err, i = 0; + char jack_name[NAME_SIZE]; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &kabylake_hdmi[i], + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &kabylake_hdmi[i]); + if (err < 0) + return err; + + i++; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); + + return 0; +} + +/* kabylake audio machine driver for SPT + DA7219 */ +static struct snd_soc_card kbl_audio_card_da7219_m98927 = { + .name = "kblda7219m98927", + .owner = THIS_MODULE, + .dai_link = kabylake_dais, + .num_links = ARRAY_SIZE(kabylake_dais), + .controls = kabylake_controls, + .num_controls = ARRAY_SIZE(kabylake_controls), + .dapm_widgets = kabylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(kabylake_widgets), + .dapm_routes = kabylake_map, + .num_dapm_routes = ARRAY_SIZE(kabylake_map), + .codec_conf = max98927_codec_conf, + .num_configs = ARRAY_SIZE(max98927_codec_conf), + .fully_routed = true, + .late_probe = kabylake_card_late_probe, +}; + +/* kabylake audio machine driver for Maxim98927 */ +static struct snd_soc_card kbl_audio_card_max98927 = { + .name = "kblmax98927", + .owner = THIS_MODULE, + .dai_link = kabylake_max98927_dais, + .num_links = ARRAY_SIZE(kabylake_max98927_dais), + .controls = kabylake_controls, + .num_controls = ARRAY_SIZE(kabylake_controls), + .dapm_widgets = kabylake_widgets, + .num_dapm_widgets = ARRAY_SIZE(kabylake_widgets), + .dapm_routes = kabylake_map, + .num_dapm_routes = ARRAY_SIZE(kabylake_map), + .codec_conf = max98927_codec_conf, + .num_configs = ARRAY_SIZE(max98927_codec_conf), + .fully_routed = true, + .late_probe = kabylake_card_late_probe, +}; + +static int kabylake_audio_probe(struct platform_device *pdev) +{ + struct kbl_codec_private *ctx; + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + kabylake_audio_card = + (struct snd_soc_card *)pdev->id_entry->driver_data; + + kabylake_audio_card->dev = &pdev->dev; + snd_soc_card_set_drvdata(kabylake_audio_card, ctx); + + return devm_snd_soc_register_card(&pdev->dev, kabylake_audio_card); +} + +static const struct platform_device_id kbl_board_ids[] = { + { + .name = "kbl_da7219_max98927", + .driver_data = + (kernel_ulong_t)&kbl_audio_card_da7219_m98927, + }, + { + .name = "kbl_max98927", + .driver_data = + (kernel_ulong_t)&kbl_audio_card_max98927, + }, + { } +}; + +static struct platform_driver kabylake_audio = { + .probe = kabylake_audio_probe, + .driver = { + .name = "kbl_da7219_max98927", + .pm = &snd_soc_pm_ops, + }, + .id_table = kbl_board_ids, +}; + +module_platform_driver(kabylake_audio) + +/* Module information */ +MODULE_DESCRIPTION("Audio KabyLake Machine driver for MAX98927 & DA7219"); +MODULE_AUTHOR("Mac Chiang <mac.chiang@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:kbl_da7219_max98927"); +MODULE_ALIAS("platform:kbl_max98927"); diff --git a/sound/soc/intel/boards/kbl_rt5663_max98927.c b/sound/soc/intel/boards/kbl_rt5663_max98927.c @@ -488,11 +488,10 @@ static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai; int ret = 0, j; - for (j = 0; j < rtd->num_codecs; j++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; - + for_each_rtd_codec_dai(rtd, j, codec_dai) { if (!strcmp(codec_dai->component->name, MAXIM_DEV0_NAME)) { /* * Use channel 4 and 5 for the first amp diff --git a/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c b/sound/soc/intel/boards/kbl_rt5663_rt5514_max98927.c @@ -353,11 +353,10 @@ static int kabylake_ssp0_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai; int ret = 0, j; - for (j = 0; j < rtd->num_codecs; j++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[j]; - + for_each_rtd_codec_dai(rtd, j, codec_dai) { if (!strcmp(codec_dai->component->name, RT5514_DEV_NAME)) { ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0, 8, 16); if (ret < 0) { diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.c b/sound/soc/intel/boards/skl_hda_dsp_common.c @@ -0,0 +1,127 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2015-18 Intel Corporation. + +/* + * Common functions used in different Intel machine drivers + */ +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../../codecs/hdac_hdmi.h" +#include "../skylake/skl.h" +#include "skl_hda_dsp_common.h" + +#define NAME_SIZE 32 + +int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device) +{ + struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); + struct skl_hda_hdmi_pcm *pcm; + char dai_name[NAME_SIZE]; + + pcm = devm_kzalloc(card->dev, sizeof(*pcm), GFP_KERNEL); + if (!pcm) + return -ENOMEM; + + snprintf(dai_name, sizeof(dai_name), "intel-hdmi-hifi%d", + ctx->dai_index); + pcm->codec_dai = snd_soc_card_get_codec_dai(card, dai_name); + if (!pcm->codec_dai) + return -EINVAL; + + pcm->device = device; + list_add_tail(&pcm->head, &ctx->hdmi_pcm_list); + + return 0; +} + +/* skl_hda_digital audio interface glue - connects codec <--> CPU */ +struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS] = { + /* Back End DAI links */ + { + .name = "iDisp1", + .id = 1, + .cpu_dai_name = "iDisp1 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi1", + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp2", + .id = 2, + .cpu_dai_name = "iDisp2 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi2", + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "iDisp3", + .id = 3, + .cpu_dai_name = "iDisp3 Pin", + .codec_name = "ehdaudio0D2", + .codec_dai_name = "intel-hdmi-hifi3", + .dpcm_playback = 1, + .no_pcm = 1, + }, + { + .name = "Analog Playback and Capture", + .id = 4, + .cpu_dai_name = "Analog CPU DAI", + .codec_name = "ehdaudio0D0", + .codec_dai_name = "Analog Codec DAI", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = NULL, + .no_pcm = 1, + }, + { + .name = "Digital Playback and Capture", + .id = 5, + .cpu_dai_name = "Digital CPU DAI", + .codec_name = "ehdaudio0D0", + .codec_dai_name = "Digital Codec DAI", + .platform_name = "0000:00:1f.3", + .dpcm_playback = 1, + .dpcm_capture = 1, + .init = NULL, + .no_pcm = 1, + }, +}; + +int skl_hda_hdmi_jack_init(struct snd_soc_card *card) +{ + struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); + struct snd_soc_component *component = NULL; + struct skl_hda_hdmi_pcm *pcm; + char jack_name[NAME_SIZE]; + int err; + + list_for_each_entry(pcm, &ctx->hdmi_pcm_list, head) { + component = pcm->codec_dai->component; + snprintf(jack_name, sizeof(jack_name), + "HDMI/DP, pcm=%d Jack", pcm->device); + err = snd_soc_card_jack_new(card, jack_name, + SND_JACK_AVOUT, &pcm->hdmi_jack, + NULL, 0); + + if (err) + return err; + + err = hdac_hdmi_jack_init(pcm->codec_dai, pcm->device, + &pcm->hdmi_jack); + if (err < 0) + return err; + } + + if (!component) + return -EINVAL; + + return hdac_hdmi_jack_port_init(component, &card->dapm); +} diff --git a/sound/soc/intel/boards/skl_hda_dsp_common.h b/sound/soc/intel/boards/skl_hda_dsp_common.h @@ -0,0 +1,38 @@ +/* SPDX-License-Identifier: GPL-2.0 */ +/* + * Copyright(c) 2015-18 Intel Corporation. + */ + +/* + * This file defines data structures used in Machine Driver for Intel + * platforms with HDA Codecs. + */ + +#ifndef __SOUND_SOC_HDA_DSP_COMMON_H +#define __SOUND_SOC_HDA_DSP_COMMON_H +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/jack.h> + +#define HDA_DSP_MAX_BE_DAI_LINKS 5 + +struct skl_hda_hdmi_pcm { + struct list_head head; + struct snd_soc_dai *codec_dai; + struct snd_soc_jack hdmi_jack; + int device; +}; + +struct skl_hda_private { + struct list_head hdmi_pcm_list; + int pcm_count; + int dai_index; + const char *platform_name; +}; + +extern struct snd_soc_dai_link skl_hda_be_dai_links[HDA_DSP_MAX_BE_DAI_LINKS]; +int skl_hda_hdmi_jack_init(struct snd_soc_card *card); +int skl_hda_hdmi_add_pcm(struct snd_soc_card *card, int device); + +#endif /* __SOUND_SOC_HDA_DSP_COMMON_H */ diff --git a/sound/soc/intel/boards/skl_hda_dsp_generic.c b/sound/soc/intel/boards/skl_hda_dsp_generic.c @@ -0,0 +1,183 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright(c) 2015-18 Intel Corporation. + +/* + * Machine Driver for SKL+ platforms with DSP and iDisp, HDA Codecs + */ + +#include <linux/module.h> +#include <linux/platform_device.h> +#include <sound/core.h> +#include <sound/jack.h> +#include <sound/pcm.h> +#include <sound/pcm_params.h> +#include <sound/soc.h> +#include "../../codecs/hdac_hdmi.h" +#include "../skylake/skl.h" +#include "skl_hda_dsp_common.h" + +static const struct snd_soc_dapm_widget skl_hda_widgets[] = { + SND_SOC_DAPM_HP("Analog Out", NULL), + SND_SOC_DAPM_MIC("Analog In", NULL), + SND_SOC_DAPM_HP("Alt Analog Out", NULL), + SND_SOC_DAPM_MIC("Alt Analog In", NULL), + SND_SOC_DAPM_SPK("Digital Out", NULL), + SND_SOC_DAPM_MIC("Digital In", NULL), +}; + +static const struct snd_soc_dapm_route skl_hda_map[] = { + { "hifi3", NULL, "iDisp3 Tx"}, + { "iDisp3 Tx", NULL, "iDisp3_out"}, + { "hifi2", NULL, "iDisp2 Tx"}, + { "iDisp2 Tx", NULL, "iDisp2_out"}, + { "hifi1", NULL, "iDisp1 Tx"}, + { "iDisp1 Tx", NULL, "iDisp1_out"}, + + { "Analog Out", NULL, "Codec Output Pin1" }, + { "Digital Out", NULL, "Codec Output Pin2" }, + { "Alt Analog Out", NULL, "Codec Output Pin3" }, + + { "Codec Input Pin1", NULL, "Analog In" }, + { "Codec Input Pin2", NULL, "Digital In" }, + { "Codec Input Pin3", NULL, "Alt Analog In" }, + + /* CODEC BE connections */ + { "Analog Codec Playback", NULL, "Analog CPU Playback" }, + { "Analog CPU Playback", NULL, "codec0_out" }, + { "Digital Codec Playback", NULL, "Digital CPU Playback" }, + { "Digital CPU Playback", NULL, "codec1_out" }, + { "Alt Analog Codec Playback", NULL, "Alt Analog CPU Playback" }, + { "Alt Analog CPU Playback", NULL, "codec2_out" }, + + { "codec0_in", NULL, "Analog CPU Capture" }, + { "Analog CPU Capture", NULL, "Analog Codec Capture" }, + { "codec1_in", NULL, "Digital CPU Capture" }, + { "Digital CPU Capture", NULL, "Digital Codec Capture" }, + { "codec2_in", NULL, "Alt Analog CPU Capture" }, + { "Alt Analog CPU Capture", NULL, "Alt Analog Codec Capture" }, +}; + +static int skl_hda_card_late_probe(struct snd_soc_card *card) +{ + return skl_hda_hdmi_jack_init(card); +} + +static int +skl_hda_add_dai_link(struct snd_soc_card *card, struct snd_soc_dai_link *link) +{ + struct skl_hda_private *ctx = snd_soc_card_get_drvdata(card); + int ret = 0; + + dev_dbg(card->dev, "%s: dai link name - %s\n", __func__, link->name); + link->platform_name = ctx->platform_name; + link->nonatomic = 1; + + if (strstr(link->name, "HDMI")) { + ret = skl_hda_hdmi_add_pcm(card, ctx->pcm_count); + + if (ret < 0) + return ret; + + ctx->dai_index++; + } + + ctx->pcm_count++; + return ret; +} + +static struct snd_soc_card hda_soc_card = { + .name = "skl_hda_card", + .owner = THIS_MODULE, + .dai_link = skl_hda_be_dai_links, + .dapm_widgets = skl_hda_widgets, + .dapm_routes = skl_hda_map, + .add_dai_link = skl_hda_add_dai_link, + .fully_routed = true, + .late_probe = skl_hda_card_late_probe, +}; + +#define IDISP_DAI_COUNT 3 +/* there are two routes per iDisp output */ +#define IDISP_ROUTE_COUNT (IDISP_DAI_COUNT * 2) +#define IDISP_CODEC_MASK 0x4 + +static int skl_hda_fill_card_info(struct skl_machine_pdata *pdata) +{ + struct snd_soc_card *card = &hda_soc_card; + struct snd_soc_dai_link *dai_link; + u32 codec_count, codec_mask; + int i, num_links, num_route; + + codec_mask = pdata->codec_mask; + codec_count = hweight_long(codec_mask); + + if (codec_count == 1 && pdata->codec_mask & IDISP_CODEC_MASK) { + num_links = IDISP_DAI_COUNT; + num_route = IDISP_ROUTE_COUNT; + } else if (codec_count == 2 && codec_mask & IDISP_CODEC_MASK) { + num_links = ARRAY_SIZE(skl_hda_be_dai_links); + num_route = ARRAY_SIZE(skl_hda_map), + card->dapm_widgets = skl_hda_widgets; + card->num_dapm_widgets = ARRAY_SIZE(skl_hda_widgets); + } else { + return -EINVAL; + } + + card->num_links = num_links; + card->num_dapm_routes = num_route; + + for_each_card_prelinks(card, i, dai_link) + dai_link->platform_name = pdata->platform; + + return 0; +} + +static int skl_hda_audio_probe(struct platform_device *pdev) +{ + struct skl_machine_pdata *pdata; + struct skl_hda_private *ctx; + int ret; + + dev_dbg(&pdev->dev, "%s: entry\n", __func__); + + ctx = devm_kzalloc(&pdev->dev, sizeof(*ctx), GFP_ATOMIC); + if (!ctx) + return -ENOMEM; + + INIT_LIST_HEAD(&ctx->hdmi_pcm_list); + + pdata = dev_get_drvdata(&pdev->dev); + if (!pdata) + return -EINVAL; + + ret = skl_hda_fill_card_info(pdata); + if (ret < 0) { + dev_err(&pdev->dev, "Unsupported HDAudio/iDisp configuration found\n"); + return ret; + } + + ctx->pcm_count = hda_soc_card.num_links; + ctx->dai_index = 1; /* hdmi codec dai name starts from index 1 */ + ctx->platform_name = pdata->platform; + + hda_soc_card.dev = &pdev->dev; + snd_soc_card_set_drvdata(&hda_soc_card, ctx); + + return devm_snd_soc_register_card(&pdev->dev, &hda_soc_card); +} + +static struct platform_driver skl_hda_audio = { + .probe = skl_hda_audio_probe, + .driver = { + .name = "skl_hda_dsp_generic", + .pm = &snd_soc_pm_ops, + }, +}; + +module_platform_driver(skl_hda_audio) + +/* Module information */ +MODULE_DESCRIPTION("SKL/KBL/BXT/APL HDA Generic Machine driver"); +MODULE_AUTHOR("Rakesh Ughreja <rakesh.a.ughreja@intel.com>"); +MODULE_LICENSE("GPL v2"); +MODULE_ALIAS("platform:skl_hda_dsp_generic"); diff --git a/sound/soc/intel/common/Makefile b/sound/soc/intel/common/Makefile @@ -7,7 +7,8 @@ snd-soc-acpi-intel-match-objs := soc-acpi-intel-byt-match.o soc-acpi-intel-cht-m soc-acpi-intel-hsw-bdw-match.o \ soc-acpi-intel-skl-match.o soc-acpi-intel-kbl-match.o \ soc-acpi-intel-bxt-match.o soc-acpi-intel-glk-match.o \ - soc-acpi-intel-cnl-match.o + soc-acpi-intel-cnl-match.o \ + soc-acpi-intel-hda-match.o obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o diff --git a/sound/soc/intel/common/soc-acpi-intel-byt-match.c b/sound/soc/intel/common/soc-acpi-intel-byt-match.c @@ -34,6 +34,13 @@ static const struct dmi_system_id byt_table[] = { .callback = byt_thinkpad10_quirk_cb, .matches = { DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), + DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 8"), + }, + }, + { + .callback = byt_thinkpad10_quirk_cb, + .matches = { + DMI_MATCH(DMI_SYS_VENDOR, "LENOVO"), DMI_MATCH(DMI_PRODUCT_VERSION, "ThinkPad 10"), }, }, diff --git a/sound/soc/intel/common/soc-acpi-intel-hda-match.c b/sound/soc/intel/common/soc-acpi-intel-hda-match.c @@ -0,0 +1,40 @@ +// SPDX-License-Identifier: GPL-2.0 +// Copyright (c) 2018, Intel Corporation. + +/* + * soc-apci-intel-hda-match.c - tables and support for HDA+ACPI enumeration. + * + */ + +#include <sound/soc-acpi.h> +#include <sound/soc-acpi-intel-match.h> +#include "../skylake/skl.h" + +static struct skl_machine_pdata hda_pdata = { + .use_tplg_pcm = true, +}; + +struct snd_soc_acpi_mach snd_soc_acpi_intel_hda_machines[] = { + { + /* .id is not used in this file */ + .drv_name = "skl_hda_dsp_generic", + + /* .fw_filename is dynamically set in skylake driver */ + + /* .sof_fw_filename is dynamically set in sof/intel driver */ + + .sof_tplg_filename = "intel/sof-hda-generic.tplg", + + /* + * .machine_quirk and .quirk_data are not used here but + * can be used if we need a more complicated machine driver + * combining HDA+other device (e.g. DMIC). + */ + .pdata = &hda_pdata, + }, + {}, +}; +EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_hda_machines); + +MODULE_LICENSE("GPL v2"); +MODULE_DESCRIPTION("Intel Common ACPI Match module"); diff --git a/sound/soc/intel/common/soc-acpi-intel-kbl-match.c b/sound/soc/intel/common/soc-acpi-intel-kbl-match.c @@ -32,6 +32,11 @@ static struct snd_soc_acpi_codecs kbl_7219_98357_codecs = { .codecs = {"MX98357A"} }; +static struct snd_soc_acpi_codecs kbl_7219_98927_codecs = { + .num_codecs = 1, + .codecs = {"MX98927"} +}; + struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { { .id = "INT343A", @@ -83,6 +88,14 @@ struct snd_soc_acpi_mach snd_soc_acpi_intel_kbl_machines[] = { .quirk_data = &kbl_7219_98357_codecs, .pdata = &skl_dmic_data, }, + { + .id = "DLGS7219", + .drv_name = "kbl_da7219_max98927", + .fw_filename = "intel/dsp_fw_kbl.bin", + .machine_quirk = snd_soc_acpi_codec_list, + .quirk_data = &kbl_7219_98927_codecs, + .pdata = &skl_dmic_data + }, {}, }; EXPORT_SYMBOL_GPL(snd_soc_acpi_intel_kbl_machines); diff --git a/sound/soc/intel/common/sst-firmware.c b/sound/soc/intel/common/sst-firmware.c @@ -355,7 +355,7 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp, /* allocate DMA buffer to store FW data */ sst_fw->dma_buf = dma_alloc_coherent(dsp->dma_dev, sst_fw->size, - &sst_fw->dmable_fw_paddr, GFP_DMA | GFP_KERNEL); + &sst_fw->dmable_fw_paddr, GFP_KERNEL); if (!sst_fw->dma_buf) { dev_err(dsp->dev, "error: DMA alloc failed\n"); kfree(sst_fw); diff --git a/sound/soc/intel/skylake/skl-pcm.c b/sound/soc/intel/skylake/skl-pcm.c @@ -32,6 +32,7 @@ #define HDA_MONO 1 #define HDA_STEREO 2 #define HDA_QUAD 4 +#define HDA_MAX 8 static const struct snd_pcm_hardware azx_pcm_hw = { .info = (SNDRV_PCM_INFO_MMAP | @@ -494,6 +495,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd, stream->lpib); snd_hdac_ext_stream_set_lpib(stream, stream->lpib); } + /* fall through */ case SNDRV_PCM_TRIGGER_START: case SNDRV_PCM_TRIGGER_PAUSE_RELEASE: @@ -569,7 +571,10 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream, stream_tag = hdac_stream(link_dev)->stream_tag; /* set the stream tag in the codec dai dma params */ - snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); + if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) + snd_soc_dai_set_tdm_slot(codec_dai, stream_tag, 0, 0, 0); + else + snd_soc_dai_set_tdm_slot(codec_dai, 0, stream_tag, 0, 0); p_params.s_fmt = snd_pcm_format_width(params_format(params)); p_params.ch = params_channels(params); @@ -995,21 +1000,63 @@ static struct snd_soc_dai_driver skl_platform_dai[] = { }, }, { - .name = "HD-Codec Pin", + .name = "Analog CPU DAI", .ops = &skl_link_dai_ops, .playback = { - .stream_name = "HD-Codec Tx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .stream_name = "Analog CPU Playback", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, .capture = { - .stream_name = "HD-Codec Rx", - .channels_min = HDA_STEREO, - .channels_max = HDA_STEREO, - .rates = SNDRV_PCM_RATE_48000, - .formats = SNDRV_PCM_FMTBIT_S16_LE, + .stream_name = "Analog CPU Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}, +{ + .name = "Alt Analog CPU DAI", + .ops = &skl_link_dai_ops, + .playback = { + .stream_name = "Alt Analog CPU Playback", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "Alt Analog CPU Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, +}, +{ + .name = "Digital CPU DAI", + .ops = &skl_link_dai_ops, + .playback = { + .stream_name = "Digital CPU Playback", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, + }, + .capture = { + .stream_name = "Digital CPU Capture", + .channels_min = HDA_MONO, + .channels_max = HDA_MAX, + .rates = SNDRV_PCM_RATE_8000_192000, + .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE | + SNDRV_PCM_FMTBIT_S32_LE, }, }, }; diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c @@ -898,11 +898,10 @@ static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w, bc = (struct skl_algo_data *)sb->dobj.private; if (bc->set_params == SKL_PARAM_BIND) { - params = kzalloc(bc->max, GFP_KERNEL); + params = kmemdup(bc->params, bc->max, GFP_KERNEL); if (!params) return -ENOMEM; - memcpy(params, bc->params, bc->max); skl_fill_sink_instance_id(ctx, params, bc->max, mconfig); @@ -2461,6 +2460,7 @@ static int skl_tplg_get_token(struct device *dev, case SKL_TKN_U8_CORE_ID: mconfig->core_id = tkn_elem->value; + break; case SKL_TKN_U8_MOD_TYPE: mconfig->m_type = tkn_elem->value; diff --git a/sound/soc/intel/skylake/skl.c b/sound/soc/intel/skylake/skl.c @@ -33,9 +33,11 @@ #include <sound/hda_register.h> #include <sound/hdaudio.h> #include <sound/hda_i915.h> +#include <sound/hda_codec.h> #include "skl.h" #include "skl-sst-dsp.h" #include "skl-sst-ipc.h" +#include "../../../soc/codecs/hdac_hda.h" /* * initialize the PCI registers @@ -472,6 +474,25 @@ static struct skl_ssp_clk skl_ssp_clks[] = { {.name = "ssp5_sclkfs"}, }; +static struct snd_soc_acpi_mach *skl_find_hda_machine(struct skl *skl, + struct snd_soc_acpi_mach *machines) +{ + struct hdac_bus *bus = skl_to_bus(skl); + struct snd_soc_acpi_mach *mach; + + /* check if we have any codecs detected on bus */ + if (bus->codec_mask == 0) + return NULL; + + /* point to common table */ + mach = snd_soc_acpi_intel_hda_machines; + + /* all entries in the machine table use the same firmware */ + mach->fw_filename = machines->fw_filename; + + return mach; +} + static int skl_find_machine(struct skl *skl, void *driver_data) { struct hdac_bus *bus = skl_to_bus(skl); @@ -479,9 +500,13 @@ static int skl_find_machine(struct skl *skl, void *driver_data) struct skl_machine_pdata *pdata; mach = snd_soc_acpi_find_machine(mach); - if (mach == NULL) { - dev_err(bus->dev, "No matching machine driver found\n"); - return -ENODEV; + if (!mach) { + dev_dbg(bus->dev, "No matching I2S machine driver found\n"); + mach = skl_find_hda_machine(skl, driver_data); + if (!mach) { + dev_err(bus->dev, "No matching machine driver found\n"); + return -ENODEV; + } } skl->mach = mach; @@ -498,8 +523,9 @@ static int skl_find_machine(struct skl *skl, void *driver_data) static int skl_machine_device_register(struct skl *skl) { - struct hdac_bus *bus = skl_to_bus(skl); struct snd_soc_acpi_mach *mach = skl->mach; + struct hdac_bus *bus = skl_to_bus(skl); + struct skl_machine_pdata *pdata; struct platform_device *pdev; int ret; @@ -516,8 +542,12 @@ static int skl_machine_device_register(struct skl *skl) return -EIO; } - if (mach->pdata) + if (mach->pdata) { + pdata = (struct skl_machine_pdata *)mach->pdata; + pdata->platform = dev_name(bus->dev); + pdata->codec_mask = bus->codec_mask; dev_set_drvdata(&pdev->dev, mach->pdata); + } skl->i2s_dev = pdev; @@ -628,6 +658,24 @@ static void skl_clock_device_unregister(struct skl *skl) platform_device_unregister(skl->clk_dev); } +#define IDISP_INTEL_VENDOR_ID 0x80860000 + +/* + * load the legacy codec driver + */ +static void load_codec_module(struct hda_codec *codec) +{ +#ifdef MODULE + char modalias[MODULE_NAME_LEN]; + const char *mod = NULL; + + snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias)); + mod = modalias; + dev_dbg(&codec->core.dev, "loading %s codec module\n", mod); + request_module(mod); +#endif +} + /* * Probe the given codec address */ @@ -637,7 +685,9 @@ static int probe_codec(struct hdac_bus *bus, int addr) (AC_VERB_PARAMETERS << 8) | AC_PAR_VENDOR_ID; unsigned int res = -1; struct skl *skl = bus_to_skl(bus); + struct hdac_hda_priv *hda_codec; struct hdac_device *hdev; + int err; mutex_lock(&bus->cmd_mutex); snd_hdac_bus_send_cmd(bus, cmd); @@ -645,13 +695,26 @@ static int probe_codec(struct hdac_bus *bus, int addr) mutex_unlock(&bus->cmd_mutex); if (res == -1) return -EIO; - dev_dbg(bus->dev, "codec #%d probed OK\n", addr); + dev_dbg(bus->dev, "codec #%d probed OK: %x\n", addr, res); - hdev = devm_kzalloc(&skl->pci->dev, sizeof(*hdev), GFP_KERNEL); - if (!hdev) + hda_codec = devm_kzalloc(&skl->pci->dev, sizeof(*hda_codec), + GFP_KERNEL); + if (!hda_codec) return -ENOMEM; - return snd_hdac_ext_bus_device_init(bus, addr, hdev); + hda_codec->codec.bus = skl_to_hbus(skl); + hdev = &hda_codec->codec.core; + + err = snd_hdac_ext_bus_device_init(bus, addr, hdev); + if (err < 0) + return err; + + /* use legacy bus only for HDA codecs, idisp uses ext bus */ + if ((res & 0xFFFF0000) != IDISP_INTEL_VENDOR_ID) { + hdev->type = HDA_DEV_LEGACY; + load_codec_module(&hda_codec->codec); + } + return 0; } /* Codec initialization */ @@ -786,9 +849,10 @@ static int skl_create(struct pci_dev *pci, const struct hdac_io_ops *io_ops, struct skl **rskl) { + struct hdac_ext_bus_ops *ext_ops = NULL; struct skl *skl; struct hdac_bus *bus; - + struct hda_bus *hbus; int err; *rskl = NULL; @@ -803,13 +867,23 @@ static int skl_create(struct pci_dev *pci, return -ENOMEM; } + hbus = skl_to_hbus(skl); bus = skl_to_bus(skl); - snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops, NULL); + +#if IS_ENABLED(CONFIG_SND_SOC_HDAC_HDA) + ext_ops = snd_soc_hdac_hda_get_ops(); +#endif + snd_hdac_ext_bus_init(bus, &pci->dev, &bus_core_ops, io_ops, ext_ops); bus->use_posbuf = 1; skl->pci = pci; INIT_WORK(&skl->probe_work, skl_probe_work); bus->bdl_pos_adj = 0; + mutex_init(&hbus->prepare_mutex); + hbus->pci = pci; + hbus->mixer_assigned = -1; + hbus->modelname = "sklbus"; + *rskl = skl; return 0; diff --git a/sound/soc/intel/skylake/skl.h b/sound/soc/intel/skylake/skl.h @@ -23,6 +23,7 @@ #include <sound/hda_register.h> #include <sound/hdaudio_ext.h> +#include <sound/hda_codec.h> #include <sound/soc.h> #include "skl-nhlt.h" #include "skl-ssp-clk.h" @@ -71,7 +72,7 @@ struct skl_fw_config { }; struct skl { - struct hdac_bus hbus; + struct hda_bus hbus; struct pci_dev *pci; unsigned int init_done:1; /* delayed init status */ @@ -105,8 +106,11 @@ struct skl { struct snd_soc_acpi_mach *mach; }; -#define skl_to_bus(s) (&(s)->hbus) -#define bus_to_skl(bus) container_of(bus, struct skl, hbus) +#define skl_to_bus(s) (&(s)->hbus.core) +#define bus_to_skl(bus) container_of(bus, struct skl, hbus.core) + +#define skl_to_hbus(s) (&(s)->hbus) +#define hbus_to_skl(hbus) container_of((hbus), struct skl, (hbus)) /* to pass dai dma data */ struct skl_dma_params { @@ -117,6 +121,8 @@ struct skl_dma_params { struct skl_machine_pdata { u32 dmic_num; bool use_tplg_pcm; /* use dais and dai links from topology */ + const char *platform; + u32 codec_mask; }; struct skl_dsp_ops { diff --git a/sound/soc/mediatek/mt2701/mt2701-cs42448.c b/sound/soc/mediatek/mt2701/mt2701-cs42448.c @@ -299,6 +299,7 @@ static int mt2701_cs42448_machine_probe(struct platform_device *pdev) devm_kzalloc(&pdev->dev, sizeof(struct mt2701_cs42448_private), GFP_KERNEL); struct device *dev = &pdev->dev; + struct snd_soc_dai_link *dai_link; if (!priv) return -ENOMEM; @@ -309,10 +310,10 @@ static int mt2701_cs42448_machine_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); return -EINVAL; } - for (i = 0; i < card->num_links; i++) { - if (mt2701_cs42448_dai_links[i].platform_name) + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->platform_name) continue; - mt2701_cs42448_dai_links[i].platform_of_node = platform_node; + dai_link->platform_of_node = platform_node; } card->dev = dev; @@ -324,10 +325,10 @@ static int mt2701_cs42448_machine_probe(struct platform_device *pdev) "Property 'audio-codec' missing or invalid\n"); return -EINVAL; } - for (i = 0; i < card->num_links; i++) { - if (mt2701_cs42448_dai_links[i].codec_name) + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->codec_name) continue; - mt2701_cs42448_dai_links[i].codec_of_node = codec_node; + dai_link->codec_of_node = codec_node; } codec_node_bt_mrg = of_parse_phandle(pdev->dev.of_node, diff --git a/sound/soc/mediatek/mt2701/mt2701-wm8960.c b/sound/soc/mediatek/mt2701/mt2701-wm8960.c @@ -97,6 +97,7 @@ static int mt2701_wm8960_machine_probe(struct platform_device *pdev) { struct snd_soc_card *card = &mt2701_wm8960_card; struct device_node *platform_node, *codec_node; + struct snd_soc_dai_link *dai_link; int ret, i; platform_node = of_parse_phandle(pdev->dev.of_node, @@ -105,10 +106,10 @@ static int mt2701_wm8960_machine_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); return -EINVAL; } - for (i = 0; i < card->num_links; i++) { - if (mt2701_wm8960_dai_links[i].platform_name) + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->platform_name) continue; - mt2701_wm8960_dai_links[i].platform_of_node = platform_node; + dai_link->platform_of_node = platform_node; } card->dev = &pdev->dev; @@ -120,10 +121,10 @@ static int mt2701_wm8960_machine_probe(struct platform_device *pdev) "Property 'audio-codec' missing or invalid\n"); return -EINVAL; } - for (i = 0; i < card->num_links; i++) { - if (mt2701_wm8960_dai_links[i].codec_name) + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->codec_name) continue; - mt2701_wm8960_dai_links[i].codec_of_node = codec_node; + dai_link->codec_of_node = codec_node; } ret = snd_soc_of_parse_audio_routing(card, "audio-routing"); @@ -150,7 +151,6 @@ static const struct of_device_id mt2701_wm8960_machine_dt_match[] = { static struct platform_driver mt2701_wm8960_machine = { .driver = { .name = "mt2701-wm8960", - .owner = THIS_MODULE, #ifdef CONFIG_OF .of_match_table = mt2701_wm8960_machine_dt_match, #endif diff --git a/sound/soc/mediatek/mt6797/mt6797-mt6351.c b/sound/soc/mediatek/mt6797/mt6797-mt6351.c @@ -158,6 +158,7 @@ static int mt6797_mt6351_dev_probe(struct platform_device *pdev) { struct snd_soc_card *card = &mt6797_mt6351_card; struct device_node *platform_node, *codec_node; + struct snd_soc_dai_link *dai_link; int ret, i; card->dev = &pdev->dev; @@ -168,10 +169,10 @@ static int mt6797_mt6351_dev_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); return -EINVAL; } - for (i = 0; i < card->num_links; i++) { - if (mt6797_mt6351_dai_links[i].platform_name) + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->platform_name) continue; - mt6797_mt6351_dai_links[i].platform_of_node = platform_node; + dai_link->platform_of_node = platform_node; } codec_node = of_parse_phandle(pdev->dev.of_node, @@ -181,10 +182,10 @@ static int mt6797_mt6351_dev_probe(struct platform_device *pdev) "Property 'audio-codec' missing or invalid\n"); return -EINVAL; } - for (i = 0; i < card->num_links; i++) { - if (mt6797_mt6351_dai_links[i].codec_name) + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->codec_name) continue; - mt6797_mt6351_dai_links[i].codec_of_node = codec_node; + dai_link->codec_of_node = codec_node; } ret = devm_snd_soc_register_card(&pdev->dev, card); @@ -205,7 +206,6 @@ static const struct of_device_id mt6797_mt6351_dt_match[] = { static struct platform_driver mt6797_mt6351_driver = { .driver = { .name = "mt6797-mt6351", - .owner = THIS_MODULE, #ifdef CONFIG_OF .of_match_table = mt6797_mt6351_dt_match, #endif diff --git a/sound/soc/mediatek/mt8173/mt8173-max98090.c b/sound/soc/mediatek/mt8173/mt8173-max98090.c @@ -137,6 +137,7 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev) { struct snd_soc_card *card = &mt8173_max98090_card; struct device_node *codec_node, *platform_node; + struct snd_soc_dai_link *dai_link; int ret, i; platform_node = of_parse_phandle(pdev->dev.of_node, @@ -145,10 +146,10 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev) dev_err(&pdev->dev, "Property 'platform' missing or invalid\n"); return -EINVAL; } - for (i = 0; i < card->num_links; i++) { - if (mt8173_max98090_dais[i].platform_name) + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->platform_name) continue; - mt8173_max98090_dais[i].platform_of_node = platform_node; + dai_link->platform_of_node = platform_node; } codec_node = of_parse_phandle(pdev->dev.of_node, @@ -158,10 +159,10 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev) "Property 'audio-codec' missing or invalid\n"); return -EINVAL; } - for (i = 0; i < card->num_links; i++) { - if (mt8173_max98090_dais[i].codec_name) + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->codec_name) continue; - mt8173_max98090_dais[i].codec_of_node = codec_node; + dai_link->codec_of_node = codec_node; } card->dev = &pdev->dev; diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5514.c @@ -44,11 +44,10 @@ static int mt8173_rt5650_rt5514_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai; int i, ret; - for (i = 0; i < rtd->num_codecs; i++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; - + for_each_rtd_codec_dai(rtd, i, codec_dai) { /* pll from mclk 12.288M */ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, params_rate(params) * 512); @@ -179,6 +178,7 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev) { struct snd_soc_card *card = &mt8173_rt5650_rt5514_card; struct device_node *platform_node; + struct snd_soc_dai_link *dai_link; int i, ret; platform_node = of_parse_phandle(pdev->dev.of_node, @@ -188,10 +188,10 @@ static int mt8173_rt5650_rt5514_dev_probe(struct platform_device *pdev) return -EINVAL; } - for (i = 0; i < card->num_links; i++) { - if (mt8173_rt5650_rt5514_dais[i].platform_name) + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->platform_name) continue; - mt8173_rt5650_rt5514_dais[i].platform_of_node = platform_node; + dai_link->platform_of_node = platform_node; } mt8173_rt5650_rt5514_codecs[0].of_node = diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c b/sound/soc/mediatek/mt8173/mt8173-rt5650-rt5676.c @@ -48,11 +48,10 @@ static int mt8173_rt5650_rt5676_hw_params(struct snd_pcm_substream *substream, struct snd_pcm_hw_params *params) { struct snd_soc_pcm_runtime *rtd = substream->private_data; + struct snd_soc_dai *codec_dai; int i, ret; - for (i = 0; i < rtd->num_codecs; i++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; - + for_each_rtd_codec_dai(rtd, i, codec_dai) { /* pll from mclk 12.288M */ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS, params_rate(params) * 512); @@ -225,6 +224,7 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) { struct snd_soc_card *card = &mt8173_rt5650_rt5676_card; struct device_node *platform_node; + struct snd_soc_dai_link *dai_link; int i, ret; platform_node = of_parse_phandle(pdev->dev.of_node, @@ -234,10 +234,10 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev) return -EINVAL; } - for (i = 0; i < card->num_links; i++) { - if (mt8173_rt5650_rt5676_dais[i].platform_name) + for_each_card_prelinks(card, i, dai_link) { + if (dai_link->platform_name) continue; - mt8173_rt5650_rt5676_dais[i].platform_of_node = platform_node; + dai_link->platform_of_node = platform_node; } mt8173_rt5650_rt5676_codecs[0].of_node = diff --git a/sound/soc/mediatek/mt8173/mt8173-rt5650.c b/sound/soc/mediatek/mt8173/mt8173-rt5650.c @@ -59,6 +59,7 @@ static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream, { struct snd_soc_pcm_runtime *rtd = substream->private_data; unsigned int mclk_clock; + struct snd_soc_dai *codec_dai; int i, ret; switch (mt8173_rt5650_priv.pll_from) { @@ -76,9 +77,7 @@ static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream, break; } - for (i = 0; i < rtd->num_codecs; i++) { - struct snd_soc_dai *codec_dai = rtd->codec_dais[i]; - + for_each_rtd_codec_dai(rtd, i, codec_dai) { /* pll from mclk */ ret = snd_soc_dai_set_pll(codec_dai, 0, 0, mclk_clock, params_rate(params) * 512); @@ -240,6 +239,7 @@ static int