]> asedeno.scripts.mit.edu Git - linux.git/commitdiff
Merge tag 'sound-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound
authorLinus Torvalds <torvalds@linux-foundation.org>
Fri, 18 Mar 2016 17:05:46 +0000 (10:05 -0700)
committerLinus Torvalds <torvalds@linux-foundation.org>
Fri, 18 Mar 2016 17:05:46 +0000 (10:05 -0700)
Pull sound updates from Takashi Iwai:
 "After a heavy storm by syzkaller in 4.5 cycle, we have relatively few
  changes in the core at this time while a lot of changes are found in
  the driver side, unsurprisingly.  Below are some highlights:

  ALSA core:
   - A few more hardening in ALSA timer codes
   - An extension of sequencer API for advertising the card / pid
   - Small fixes in compress-offload and jack layers

  HD-audio:
   - Dynamic PCM assignment in HDMI/DP codec; preparation for upcoming
     DP-MST support
   - Lots of code refactoring for sharing with ASoC SKL driver
   - Regression fixes for Intel HDMI/DP
   - Fixups for CX20724 codec, Lenovo AiO

  USB-audio:
   - Add quirk_alias option to make quirk debugging easier
   - Fixes for possible Oops by malformed firmware

  Firewire:
   - Add support for FW-1804 in tascam driver
   - Improvements / changes in card registration, multi stream handling,
     etc for DICE
   - Lots of code refactoring

  ASoC:
   - Enhancements of still ongoing topology API
   - Lots of commits for Intel Skylake support including HDMI support
   - A few Intel Atom driver updates for recent devices
   - Lots of improvements to the Renesas drivers
   - Capture support for Qualcomm drivers
   - Support for TI DaVinci DRA7xxx devices
   - New machine drivers for Freescale systems with Cirrus CODECs,
     Mediatek systems with RT5650 CODECs
   - New CPU drivers for Allwinner S/PDIF controllers
   - New CODEC drivers for Maxim MAX9867 and MAX98926 and Realtek RT5514"

* tag 'sound-4.6-rc1' of git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound: (291 commits)
  ALSA: hda - Fix mutex deadlock at HDMI/DP hotplug
  ALSA: ctl: change return value in compatibility layer so that it's the same value in core implementation
  ALSA: mixart: silence an uninitialized variable warning
  ALSA: usb-audio: Add sanity checks for endpoint accesses
  ALSA: usb-audio: Minor code cleanup in create_fixed_stream_quirk()
  ALSA: usb-audio: Fix NULL dereference in create_fixed_stream_quirk()
  ALSA: hda - Limit i915 HDMI binding only for HSW and later
  ALSA: hda - Fix unconditional GPIO toggle via automute
  ALSA: mixart: silence unitialized variable warnings
  ALSA: hda - Fixes double fault in nvhdmi_chmap_cea_alloc_validate_get_type
  ALSA: intel8x0: Add clock quirk entry for AD1981B on IBM ThinkPad X41.
  ALSA: hda - Add new GPU codec ID 0x10de0082 to snd-hda
  ASoC: rsnd: add simplified module explanation
  ASoC: hdac_hdmi: Add broxton device ID
  ASoC: Intel: Bxtn: Add Broxton PCI ID
  ASoC: Intel: Skylake: Move Skylake dsp ops & loader ops
  ASoC: Intel: add dmabuffer to common sst_dsp
  ASoC: Intel: Skylake: Unstatify skl_dsp_enable_core
  ASoC: Intel: Skylake: Fix whitepsace issues
  ASoC: Intel: Skylake: Move module id defines
  ...

202 files changed:
Documentation/devicetree/bindings/sound/adi,adau17x1.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
Documentation/devicetree/bindings/sound/max9867.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/max98926.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/mt8173-rt5650.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/pcm179x.txt
Documentation/devicetree/bindings/sound/renesas,rsnd.txt
Documentation/devicetree/bindings/sound/renesas,rsrc-card.txt
Documentation/devicetree/bindings/sound/rockchip-i2s.txt
Documentation/devicetree/bindings/sound/rockchip-spdif.txt
Documentation/devicetree/bindings/sound/rt5514.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/rt5616.txt
Documentation/devicetree/bindings/sound/rt5640.txt
Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/ti,ads117x.txt [new file with mode: 0644]
Documentation/sound/alsa/ALSA-Configuration.txt
Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt [new file with mode: 0644]
drivers/misc/atmel-ssc.c
include/drm/drm_edid.h
include/linux/platform_data/adau17x1.h
include/sound/hda_chmap.h [new file with mode: 0644]
include/sound/hdaudio.h
include/sound/jack.h
include/sound/pcm.h
include/sound/soc-topology.h
include/sound/soc.h
include/uapi/sound/asequencer.h
include/uapi/sound/asound.h
sound/core/Kconfig
sound/core/compress_offload.c
sound/core/control_compat.c
sound/core/jack.c
sound/core/pcm_lib.c
sound/core/pcm_misc.c
sound/core/seq/seq_clientmgr.c
sound/core/seq/seq_clientmgr.h
sound/core/timer.c
sound/drivers/mts64.c
sound/drivers/portman2x4.c
sound/firewire/bebob/bebob.c
sound/firewire/bebob/bebob.h
sound/firewire/bebob/bebob_midi.c
sound/firewire/bebob/bebob_pcm.c
sound/firewire/bebob/bebob_stream.c
sound/firewire/dice/dice-midi.c
sound/firewire/dice/dice-pcm.c
sound/firewire/dice/dice-stream.c
sound/firewire/dice/dice-transaction.c
sound/firewire/dice/dice.c
sound/firewire/dice/dice.h
sound/firewire/fireworks/fireworks.c
sound/firewire/fireworks/fireworks_stream.c
sound/firewire/oxfw/oxfw-scs1x.c
sound/hda/Makefile
sound/hda/hdac_device.c
sound/hda/hdac_i915.c
sound/hda/hdac_regmap.c
sound/hda/hdmi_chmap.c [new file with mode: 0644]
sound/mips/Kconfig
sound/mips/Makefile
sound/mips/au1x00.c [deleted file]
sound/pci/Kconfig
sound/pci/hda/Kconfig
sound/pci/hda/hda_eld.c
sound/pci/hda/hda_intel.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/thinkpad_helper.c
sound/pci/intel8x0.c
sound/pci/mixart/mixart.c
sound/pci/mixart/mixart_mixer.c
sound/soc/Kconfig
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/bcm/bcm2835-i2s.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ab8500-codec.c
sound/soc/codecs/adau1761-i2c.c
sound/soc/codecs/adau1761-spi.c
sound/soc/codecs/adau1761.c
sound/soc/codecs/adau1781-i2c.c
sound/soc/codecs/adau1781-spi.c
sound/soc/codecs/adau1781.c
sound/soc/codecs/ads117x.c
sound/soc/codecs/arizona.c
sound/soc/codecs/arizona.h
sound/soc/codecs/cs42xx8.c
sound/soc/codecs/cs47l24.c
sound/soc/codecs/hdac_hdmi.c
sound/soc/codecs/hdac_hdmi.h [new file with mode: 0644]
sound/soc/codecs/max9867.c [new file with mode: 0755]
sound/soc/codecs/max9867.h [new file with mode: 0755]
sound/soc/codecs/max98926.c [new file with mode: 0644]
sound/soc/codecs/max98926.h [new file with mode: 0644]
sound/soc/codecs/nau8825.c
sound/soc/codecs/nau8825.h
sound/soc/codecs/pcm179x-i2c.c [new file with mode: 0644]
sound/soc/codecs/pcm179x-spi.c [new file with mode: 0644]
sound/soc/codecs/pcm179x.c
sound/soc/codecs/pcm179x.h
sound/soc/codecs/pcm3168a.c
sound/soc/codecs/rt298.c
sound/soc/codecs/rt298.h
sound/soc/codecs/rt5514.c [new file with mode: 0644]
sound/soc/codecs/rt5514.h [new file with mode: 0644]
sound/soc/codecs/rt5616.c
sound/soc/codecs/rt5640.c
sound/soc/codecs/rt5640.h
sound/soc/codecs/rt5645.c
sound/soc/codecs/rt5659.c
sound/soc/codecs/ssm4567.c
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm8974.c
sound/soc/codecs/wm8997.c
sound/soc/codecs/wm8998.c
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.h
sound/soc/davinci/Kconfig
sound/soc/davinci/davinci-mcasp.c
sound/soc/fsl/Kconfig
sound/soc/fsl/fsl-asoc-card.c
sound/soc/fsl/fsl_sai.c
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/mpc5200_psc_ac97.c
sound/soc/intel/Kconfig
sound/soc/intel/atom/sst/sst_acpi.c
sound/soc/intel/atom/sst/sst_ipc.c
sound/soc/intel/boards/bytcr_rt5640.c
sound/soc/intel/boards/cht_bsw_max98090_ti.c
sound/soc/intel/boards/cht_bsw_rt5645.c
sound/soc/intel/boards/skl_nau88l25_max98357a.c
sound/soc/intel/boards/skl_nau88l25_ssm4567.c
sound/soc/intel/boards/skl_rt286.c
sound/soc/intel/common/sst-acpi.h
sound/soc/intel/common/sst-dsp-priv.h
sound/soc/intel/common/sst-match-acpi.c
sound/soc/intel/skylake/skl-messages.c
sound/soc/intel/skylake/skl-nhlt.c
sound/soc/intel/skylake/skl-pcm.c
sound/soc/intel/skylake/skl-sst-dsp.c
sound/soc/intel/skylake/skl-sst-dsp.h
sound/soc/intel/skylake/skl-sst.c
sound/soc/intel/skylake/skl-topology.c
sound/soc/intel/skylake/skl-topology.h
sound/soc/intel/skylake/skl-tplg-interface.h
sound/soc/intel/skylake/skl.c
sound/soc/intel/skylake/skl.h
sound/soc/mediatek/Kconfig
sound/soc/mediatek/Makefile
sound/soc/mediatek/mt8173-rt5650-rt5514.c [new file with mode: 0644]
sound/soc/mediatek/mt8173-rt5650-rt5676.c
sound/soc/mediatek/mt8173-rt5650.c [new file with mode: 0644]
sound/soc/mediatek/mtk-afe-common.h
sound/soc/mediatek/mtk-afe-pcm.c
sound/soc/mxs/mxs-saif.c
sound/soc/omap/omap-hdmi-audio.c
sound/soc/pxa/brownstone.c
sound/soc/qcom/Kconfig
sound/soc/qcom/apq8016_sbc.c
sound/soc/qcom/lpass-apq8016.c
sound/soc/qcom/lpass-cpu.c
sound/soc/qcom/lpass-ipq806x.c
sound/soc/qcom/lpass-lpaif-reg.h
sound/soc/qcom/lpass-platform.c
sound/soc/qcom/lpass.h
sound/soc/rockchip/rockchip_i2s.c
sound/soc/rockchip/rockchip_spdif.c
sound/soc/samsung/s3c-i2s-v2.c
sound/soc/samsung/s3c-i2s-v2.h
sound/soc/sh/rcar/adg.c
sound/soc/sh/rcar/cmd.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/ctu.c
sound/soc/sh/rcar/dma.c
sound/soc/sh/rcar/dvc.c
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/mix.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/rsrc-card.c
sound/soc/sh/rcar/src.c
sound/soc/sh/rcar/ssi.c
sound/soc/sh/rcar/ssiu.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-pcm.c
sound/soc/soc-topology.c
sound/soc/sunxi/Kconfig
sound/soc/sunxi/Makefile
sound/soc/sunxi/sun4i-spdif.c [new file with mode: 0644]
sound/usb/card.c
sound/usb/clock.c
sound/usb/endpoint.c
sound/usb/midi.c
sound/usb/midi.h
sound/usb/mixer_quirks.c
sound/usb/pcm.c
sound/usb/quirks.c
sound/usb/quirks.h

diff --git a/Documentation/devicetree/bindings/sound/adi,adau17x1.txt b/Documentation/devicetree/bindings/sound/adi,adau17x1.txt
new file mode 100644 (file)
index 0000000..8dbce0e
--- /dev/null
@@ -0,0 +1,24 @@
+Analog Devices ADAU1361/ADAU1461/ADAU1761/ADAU1961/ADAU1381/ADAU1781
+
+Required properties:
+
+ - compatible:         Should contain one of the following:
+                       "adi,adau1361"
+                       "adi,adau1461"
+                       "adi,adau1761"
+                       "adi,adau1961"
+                       "adi,adau1381"
+                       "adi,adau1781"
+
+ - reg:                        The i2c address. Value depends on the state of ADDR0
+                       and ADDR1, as wired in hardware.
+
+Examples:
+#include <dt-bindings/sound/adau17x1.h>
+
+       i2c_bus {
+               adau1361@38 {
+                       compatible = "adi,adau1761";
+                       reg = <0x38>;
+               };
+       };
index 4da41bf1888e9baaf2471a73db7d8019be9d6d0c..ceaef512698967ff16677b3771d2fd8c5227aaac 100644 (file)
@@ -24,6 +24,9 @@ The compatible list for this generic sound card currently:
 
  "fsl,imx-audio-cs42888"
 
+ "fsl,imx-audio-cs427x"
+ (compatible with CS4271 and CS4272)
+
  "fsl,imx-audio-wm8962"
  (compatible with Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt)
 
@@ -63,6 +66,12 @@ Optional properties:
   - audio-asrc         : The phandle of ASRC. It can be absent if there's no
                          need to add ASRC support via DPCM.
 
+Optional unless SSI is selected as a CPU DAI:
+
+  - mux-int-port       : The internal port of the i.MX audio muxer (AUDMUX)
+
+  - mux-ext-port       : The external port of the i.MX audio muxer
+
 Example:
 sound-cs42888 {
        compatible = "fsl,imx-audio-cs42888";
diff --git a/Documentation/devicetree/bindings/sound/max9867.txt b/Documentation/devicetree/bindings/sound/max9867.txt
new file mode 100644 (file)
index 0000000..394cd4e
--- /dev/null
@@ -0,0 +1,17 @@
+max9867 codec
+
+This device supports I2C mode only.
+
+Required properties:
+
+- compatible : "maxim,max9867"
+- reg : The chip select number on the I2C bus
+
+Example:
+
+&i2c {
+       max9867: max9867@0x18 {
+               compatible = "maxim,max9867";
+               reg = <0x18>;
+       };
+};
diff --git a/Documentation/devicetree/bindings/sound/max98926.txt b/Documentation/devicetree/bindings/sound/max98926.txt
new file mode 100644 (file)
index 0000000..0b7f4e4
--- /dev/null
@@ -0,0 +1,32 @@
+max98926 audio CODEC
+
+This device supports I2C.
+
+Required properties:
+
+  - compatible : "maxim,max98926"
+
+  - vmon-slot-no : slot number used to send voltage information
+                   or in inteleave mode this will be used as
+                   interleave slot.
+
+  - imon-slot-no : slot number used to send current information
+
+  - interleave-mode : When using two MAX98926 in a system it is
+                      possible to create ADC data that that will
+                      overflow the frame size. Digital Audio Interleave
+                      mode provides a means to output VMON and IMON data
+                      from two devices on a single DOUT line when running
+                      smaller frames sizes such as 32 BCLKS per LRCLK or
+                      48 BCLKS per LRCLK.
+
+  - reg : the I2C address of the device for I2C
+
+Example:
+
+codec: max98926@1a {
+   compatible = "maxim,max98926";
+   vmon-slot-no = <0>;
+   imon-slot-no = <2>;
+   reg = <0x1a>;
+};
diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650-rt5514.txt
new file mode 100644 (file)
index 0000000..e8b3c80
--- /dev/null
@@ -0,0 +1,15 @@
+MT8173 with RT5650 RT5514 CODECS
+
+Required properties:
+- compatible : "mediatek,mt8173-rt5650-rt5514"
+- mediatek,audio-codec: the phandles of rt5650 and rt5514 codecs
+- mediatek,platform: the phandle of MT8173 ASoC platform
+
+Example:
+
+       sound {
+               compatible = "mediatek,mt8173-rt5650-rt5514";
+               mediatek,audio-codec = <&rt5650 &rt5514>;
+               mediatek,platform = <&afe>;
+       };
+
diff --git a/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt b/Documentation/devicetree/bindings/sound/mt8173-rt5650.txt
new file mode 100644 (file)
index 0000000..fe5a5ef
--- /dev/null
@@ -0,0 +1,15 @@
+MT8173 with RT5650 CODECS
+
+Required properties:
+- compatible : "mediatek,mt8173-rt5650"
+- mediatek,audio-codec: the phandles of rt5650 codecs
+- mediatek,platform: the phandle of MT8173 ASoC platform
+
+Example:
+
+       sound {
+               compatible = "mediatek,mt8173-rt5650";
+               mediatek,audio-codec = <&rt5650>;
+               mediatek,platform = <&afe>;
+       };
+
index 4ae70d3462d65e6539278f4036e3fe26dc73f0ca..436c2b247693f5a9a60a58b28c365aecd1741644 100644 (file)
@@ -1,6 +1,6 @@
 Texas Instruments pcm179x DT bindings
 
-This driver supports the SPI bus.
+This driver supports both the I2C and SPI bus.
 
 Required properties:
 
@@ -9,6 +9,11 @@ Required properties:
 For required properties on SPI, please consult
 Documentation/devicetree/bindings/spi/spi-bus.txt
 
+Required properties on I2C:
+
+ - reg: the I2C address
+
+
 Examples:
 
        codec_spi: 1792a@0 {
@@ -16,3 +21,7 @@ Examples:
                spi-max-frequency = <600000>;
        };
 
+       codec_i2c: 1792a@4c {
+               compatible = "ti,pcm1792a";
+               reg = <0x4c>;
+       };
index 8ee0fa91e4a067e622435d937378bee5288f6c53..c7b29df4a96353735f23d75576f284d8f16aa649 100644 (file)
@@ -1,6 +1,337 @@
 Renesas R-Car sound
 
+=============================================
+* Modules
+=============================================
+
+Renesas R-Car sound is constructed from below modules
+(for Gen2 or later)
+
+ SCU           : Sampling Rate Converter Unit
+  - SRC                : Sampling Rate Converter
+  - CMD
+   - CTU       : Channel Transfer Unit
+   - MIX       : Mixer
+   - DVC       : Digital Volume and Mute Function
+ SSIU          : Serial Sound Interface Unit
+ SSI           : Serial Sound Interface
+
+See detail of each module's channels, connection, limitation on datasheet
+
+=============================================
+* Multi channel
+=============================================
+
+Multi channel is supported by Multi-SSI, or TDM-SSI.
+
+ Multi-SSI     : 6ch case, you can use stereo x 3 SSI
+ TDM-SSI       : 6ch case, you can use TDM
+
+=============================================
+* Enable/Disable each modules
+=============================================
+
+See datasheet to check SRC/CTU/MIX/DVC connect-limitation.
+DT controls enabling/disabling module.
+${LINUX}/arch/arm/boot/dts/r8a7790-lager.dts can be good example.
+This is example of
+
+Playback: [MEM] -> [SRC2] -> [DVC0] -> [SSIU0/SSI0] -> [codec]
+Capture:  [MEM] <- [DVC1] <- [SRC3] <- [SSIU1/SSI1] <- [codec]
+
+       &rcar_sound {
+               ...
+               rcar_sound,dai {
+                       dai0 {
+                               playback = <&ssi0 &src2 &dvc0>;
+                               capture  = <&ssi1 &src3 &dvc1>;
+                       };
+               };
+       };
+
+You can use below.
+${LINUX}/arch/arm/boot/dts/r8a7790.dts can be good example.
+
+       &src0   &ctu00  &mix0   &dvc0   &ssi0
+       &src1   &ctu01  &mix1   &dvc1   &ssi1
+       &src2   &ctu02                  &ssi2
+       &src3   &ctu03                  &ssi3
+       &src4                           &ssi4
+       &src5   &ctu10                  &ssi5
+       &src6   &ctu11                  &ssi6
+       &src7   &ctu12                  &ssi7
+       &src8   &ctu13                  &ssi8
+       &src9                           &ssi9
+
+=============================================
+* SRC (Sampling Rate Converter)
+=============================================
+
+ [xx]Hz        [yy]Hz
+ ------> [SRC] ------>
+
+SRC can convert [xx]Hz to [yy]Hz. Then, it has below 2 modes
+
+ Asynchronous mode:    input data / output data are based on different clocks.
+                       you can use this mode on Playback / Capture
+ Synchronous mode:     input data / output data are based on same clocks.
+                       This mode will be used if system doesn't have its input clock,
+                       for example digital TV case.
+                       you can use this mode on Playback
+
+------------------
+**     Asynchronous mode
+------------------
+
+You need to use "renesas,rsrc-card" sound card for it.
+example)
+
+       sound {
+               compatible = "renesas,rsrc-card";
+               ...
+               /*
+                * SRC Asynchronous mode setting
+                * Playback:
+                * All input data will be converted to 48kHz
+                * Capture:
+                * Inputed 48kHz data will be converted to
+                * system specified Hz
+                */
+               convert-rate = <48000>;
+               ...
+               cpu {
+                       sound-dai = <&rcar_sound>;
+               };
+               codec {
+                       ...
+               };
+       };
+
+------------------
+**     Synchronous mode
+------------------
+
+       > amixer set "SRC Out Rate" on
+       > aplay xxxx.wav
+       > amixer set "SRC Out Rate" 48000
+       > amixer set "SRC Out Rate" 44100
+
+=============================================
+* CTU (Channel Transfer Unit)
+=============================================
+
+ [xx]ch        [yy]ch
+ ------> [CTU] -------->
+
+CTU can convert [xx]ch to [yy]ch, or exchange outputed channel.
+CTU conversion needs matrix settings.
+For more detail information, see below
+
+       Renesas R-Car datasheet
+        - Sampling Rate Converter Unit (SCU)
+         - SCU Operation
+          - CMD Block
+           - Functional Blocks in CMD
+
+       Renesas R-Car datasheet
+        - Sampling Rate Converter Unit (SCU)
+         - Register Description
+          - CTUn Scale Value exx Register (CTUn_SVxxR)
+
+       ${LINUX}/sound/soc/sh/rcar/ctu.c
+        - comment of header
+
+You need to use "renesas,rsrc-card" sound card for it.
+example)
+
+       sound {
+               compatible = "renesas,rsrc-card";
+               ...
+               /*
+                * CTU setting
+                * All input data will be converted to 2ch
+                * as output data
+                */
+               convert-channels = <2>;
+               ...
+               cpu {
+                       sound-dai = <&rcar_sound>;
+               };
+               codec {
+                       ...
+               };
+       };
+
+Ex) Exchange output channel
+ Input -> Output
+  1ch  ->  0ch
+  0ch  ->  1ch
+
+  example of using matrix
+       output 0ch = (input 0ch x 0) + (input 1ch x 1)
+       output 1ch = (input 0ch x 1) + (input 1ch x 0)
+
+       amixer set "CTU Reset" on
+       amixer set "CTU Pass" 9,10
+       amixer set "CTU SV0" 0,4194304
+       amixer set "CTU SV1" 4194304,0
+
+ example of changing connection
+       amixer set "CTU Reset" on
+       amixer set "CTU Pass" 2,1
+
+=============================================
+* MIX (Mixer)
+=============================================
+
+MIX merges 2 sounds path. You can see 2 sound interface on system,
+and these sounds will be merged by MIX.
+
+       aplay -D plughw:0,0 xxxx.wav &
+       aplay -D plughw:0,1 yyyy.wav
+
+You need to use "renesas,rsrc-card" sound card for it.
+Ex)
+       [MEM] -> [SRC1] -> [CTU02] -+-> [MIX0] -> [DVC0] -> [SSI0]
+                                   |
+       [MEM] -> [SRC2] -> [CTU03] -+
+
+       sound {
+               compatible = "renesas,rsrc-card";
+               ...
+               cpu@0 {
+                       sound-dai = <&rcar_sound 0>;
+               };
+               cpu@1 {
+                       sound-dai = <&rcar_sound 1>;
+               };
+               codec {
+                       ...
+               };
+       };
+
+       &rcar_sound {
+               ...
+               rcar_sound,dai {
+                       dai0 {
+                               playback = <&src1 &ctu02 &mix0 &dvc0 &ssi0>;
+                       };
+                       dai1 {
+                               playback = <&src2 &ctu03 &mix0 &dvc0 &ssi0>;
+                       };
+               };
+       };
+
+=============================================
+* DVC (Digital Volume and Mute Function)
+=============================================
+
+DVC controls Playback/Capture volume.
+
+Playback Volume
+       amixer set "DVC Out" 100%
+
+Capture Volume
+       amixer set "DVC In" 100%
+
+Playback Mute
+       amixer set "DVC Out Mute" on
+
+Capture Mute
+       amixer set "DVC In Mute" on
+
+Volume Ramp
+       amixer set "DVC Out Ramp Up Rate"   "0.125 dB/64 steps"
+       amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps"
+       amixer set "DVC Out Ramp" on
+       aplay xxx.wav &
+       amixer set "DVC Out"  80%  // Volume Down
+       amixer set "DVC Out" 100%  // Volume Up
+
+=============================================
+* SSIU (Serial Sound Interface Unit)
+=============================================
+
+There is no DT settings for SSIU, because SSIU will be automatically
+selected via SSI.
+SSIU can avoid some under/over run error, because it has some buffer.
+But you can't use it if SSI was PIO mode.
+In DMA mode, you can select not to use SSIU by using "no-busif" on DT.
+
+       &ssi0 {
+               no-busif;
+       };
+
+=============================================
+* SSI (Serial Sound Interface)
+=============================================
+
+**  PIO mode
+
+You can use PIO mode which is for connection check by using.
+Note: The system will drop non-SSI modules in PIO mode
+even though if DT is selecting other modules.
+
+       &ssi0 {
+               pio-transfer
+       };
+
+** DMA mode without SSIU
+
+You can use DMA without SSIU.
+Note: under/over run, or noise are likely to occur
+
+       &ssi0 {
+               no-busif;
+       };
+
+** PIN sharing
+
+Each SSI can share WS pin. It is based on platform.
+This is example if SSI1 want to share WS pin with SSI0
+
+       &ssi1 {
+               shared-pin;
+       };
+
+** Multi-SSI
+
+You can use Multi-SSI.
+This is example of SSI0/SSI1/SSI2 (= for 6ch)
+
+       &rcar_sound {
+               ...
+               rcar_sound,dai {
+                       dai0 {
+                               playback = <&ssi0 &ssi1 &ssi2 &src0 &dvc0>;
+                       };
+               };
+       };
+
+** TDM-SSI
+
+You can use TDM with SSI.
+This is example of TDM 6ch.
+Driver can automatically switches TDM <-> stereo mode in this case.
+
+       rsnd_tdm: sound {
+               compatible = "simple-audio-card";
+               ...
+               simple-audio-card,cpu {
+                       /* system can use TDM 6ch */
+                       dai-tdm-slot-num = <6>;
+                       sound-dai = <&rcar_sound>;
+               };
+               simple-audio-card,codec {
+                       ...
+               };
+       };
+
+
+=============================================
 Required properties:
+=============================================
+
 - compatible                   : "renesas,rcar_sound-<soctype>", fallbacks
                                  "renesas,rcar_sound-gen1" if generation1, and
                                  "renesas,rcar_sound-gen2" if generation2
@@ -64,7 +395,10 @@ DAI subnode properties:
 - playback                     : list of playback modules
 - capture                      : list of capture  modules
 
+
+=============================================
 Example:
+=============================================
 
 rcar_sound: sound@ec500000 {
        #sound-dai-cells = <1>;
@@ -250,7 +584,9 @@ rcar_sound: sound@ec500000 {
        };
 };
 
+=============================================
 Example: simple sound card
+=============================================
 
        rsnd_ak4643: sound {
                compatible = "simple-audio-card";
@@ -290,7 +626,9 @@ Example: simple sound card
        shared-pin;
 };
 
+=============================================
 Example: simple sound card for TDM
+=============================================
 
        rsnd_tdm: sound {
                compatible = "simple-audio-card";
@@ -309,7 +647,9 @@ Example: simple sound card for TDM
                };
        };
 
+=============================================
 Example: simple sound card for Multi channel
+=============================================
 
 &rcar_sound {
        pinctrl-0 = <&sound_pins &sound_clk_pins>;
index 2b2caa281ce38abcb46f688318a3479efcf02cfa..255ece3043adc42decf1ede321618321057d3dd8 100644 (file)
@@ -30,6 +30,7 @@ Optional subnode properties:
 - frame-inversion                      : bool property. Add this if the
                                          dai-link uses frame clock inversion.
 - convert-rate                         : platform specified sampling rate convert
+- convert-channels                     : platform specified converted channel size (2 - 8 ch)
 - audio-prefix                         : see audio-routing
 - audio-routing                                : A list of the connections between audio components.
                                          Each entry is a pair of strings, the first being the connection's sink,
index b7f3a9325ebd564c9e455ced818acbe113b6182d..6e86d8aa29b4639397f51a8e73b8d8ede79c28fe 100644 (file)
@@ -9,6 +9,7 @@ Required properties:
    - "rockchip,rk3066-i2s": for rk3066
    - "rockchip,rk3188-i2s", "rockchip,rk3066-i2s": for rk3188
    - "rockchip,rk3288-i2s", "rockchip,rk3066-i2s": for rk3288
+   - "rockchip,rk3399-i2s", "rockchip,rk3066-i2s": for rk3399
 - reg: physical base address of the controller and length of memory mapped
   region.
 - interrupts: should contain the I2S interrupt.
index e64dbdea7db9b4e8f4745b343dccbfcbd0268d98..11046429a118a00356850528450c07a680c30a55 100644 (file)
@@ -7,8 +7,12 @@ a fibre cable.
 Required properties:
 
 - compatible: should be one of the following:
-   - "rockchip,rk3288-spdif", "rockchip,rk3188-spdif" or
-     "rockchip,rk3066-spdif"
+   - "rockchip,rk3066-spdif"
+   - "rockchip,rk3188-spdif"
+   - "rockchip,rk3288-spdif"
+   - "rockchip,rk3366-spdif"
+   - "rockchip,rk3368-spdif"
+   - "rockchip,rk3399-spdif"
 - reg: physical base address of the controller and length of memory mapped
   region.
 - interrupts: should contain the SPDIF interrupt.
diff --git a/Documentation/devicetree/bindings/sound/rt5514.txt b/Documentation/devicetree/bindings/sound/rt5514.txt
new file mode 100644 (file)
index 0000000..e24436f
--- /dev/null
@@ -0,0 +1,25 @@
+RT5514 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+- compatible : "realtek,rt5514".
+
+- reg : The I2C address of the device.
+
+Pins on the device (for linking into audio routes) for RT5514:
+
+  * DMIC1L
+  * DMIC1R
+  * DMIC2L
+  * DMIC2R
+  * AMICL
+  * AMICR
+
+Example:
+
+codec: rt5514@57 {
+       compatible = "realtek,rt5514";
+       reg = <0x57>;
+};
index efc48c65198db044c10c2516d9988ebcf8c91f76..e410858185596a0fb74ec5812cef3dd3e6282762 100644 (file)
@@ -8,6 +8,12 @@ Required properties:
 
 - reg : The I2C address of the device.
 
+Optional properties:
+
+- clocks: The phandle of the master clock to the CODEC.
+
+- clock-names: Should be "mclk".
+
 Pins on the device (for linking into audio routes) for RT5616:
 
   * IN1P
index 9e62f6eb348f9499923dbcb473dad6ff22678bfa..57fe64643050a5e9f41072dd0d4ae0376f5d6958 100644 (file)
@@ -12,6 +12,9 @@ Required properties:
 
 Optional properties:
 
+- clocks: The phandle of the master clock to the CODEC
+- clock-names: Should be "mclk"
+
 - realtek,in1-differential
 - realtek,in2-differential
 - realtek,in3-differential
diff --git a/Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt b/Documentation/devicetree/bindings/sound/sunxi,sun4i-spdif.txt
new file mode 100644 (file)
index 0000000..13503aa
--- /dev/null
@@ -0,0 +1,39 @@
+Allwinner Sony/Philips Digital Interface Format (S/PDIF) Controller
+
+The Allwinner S/PDIF audio block is a transceiver that allows the
+processor to receive and transmit digital audio via an coaxial cable or
+a fibre cable.
+For now only playback is supported.
+
+Required properties:
+
+  - compatible         : should be one of the following:
+    - "allwinner,sun4i-a10-spdif": for the Allwinner A10 SoC
+
+  - reg                        : Offset and length of the register set for the device.
+
+  - interrupts         : Contains the spdif interrupt.
+
+  - dmas               : Generic dma devicetree binding as described in
+                         Documentation/devicetree/bindings/dma/dma.txt.
+
+  - dma-names          : Two dmas have to be defined, "tx" and "rx".
+
+  - clocks             : Contains an entry for each entry in clock-names.
+
+  - clock-names                : Includes the following entries:
+       "apb"             clock for the spdif bus.
+       "spdif"           clock for spdif controller.
+
+Example:
+
+spdif: spdif@01c21000 {
+       compatible = "allwinner,sun4i-a10-spdif";
+       reg = <0x01c21000 0x40>;
+       interrupts = <13>;
+       clocks = <&apb0_gates 1>, <&spdif_clk>;
+       clock-names = "apb", "spdif";
+       dmas = <&dma 0 2>, <&dma 0 2>;
+       dma-names = "rx", "tx";
+       status = "okay";
+};
diff --git a/Documentation/devicetree/bindings/sound/ti,ads117x.txt b/Documentation/devicetree/bindings/sound/ti,ads117x.txt
new file mode 100644 (file)
index 0000000..7db19b5
--- /dev/null
@@ -0,0 +1,11 @@
+Texas Intstruments ADS117x ADC
+
+Required properties:
+
+  - compatible : "ti,ads1174" or "ti,ads1178"
+
+Example:
+
+ads1178 {
+       compatible = "ti,ads1178";
+};
index 48148d6d930786849c97cf79329b92fec755e946..fc53ccd9a6293c542b60cb62d3466dd7cc70e07b 100644 (file)
@@ -1910,6 +1910,12 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
                     - Default: 0x0000 
     ignore_ctl_error - Ignore any USB-controller regarding mixer
                       interface (default: no)
+    autoclock      - Enable auto-clock selection for UAC2 devices
+                     (default: yes)
+    quirk_alias            - Quirk alias list, pass strings like
+                     "0123abcd:5678beef", which applies the existing
+                     quirk for the device 5678:beef to a new device
+                     0123:abcd.
 
     This module supports multiple devices, autoprobe and hotplugging.
 
@@ -1919,6 +1925,9 @@ Prior to version 0.9.0rc4 options had a 'snd_' prefix. This was removed.
     NB: ignore_ctl_error=1 may help when you get an error at accessing
         the mixer element such as URB error -22.  This happens on some
         buggy USB device or the controller.
+    NB: quirk_alias option is provided only for testing / development.
+        If you want to have a proper support, contact to upstream for
+       adding the matching quirk in the driver code statically.
 
   Module snd-usb-caiaq
   --------------------
diff --git a/Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt b/Documentation/sound/alsa/HD-Audio-DP-MST-audio.txt
new file mode 100644 (file)
index 0000000..82744ac
--- /dev/null
@@ -0,0 +1,74 @@
+To support DP MST audio, HD Audio hdmi codec driver introduces virtual pin
+and dynamic pcm assignment.
+
+Virtual pin is an extension of per_pin. The most difference of DP MST
+from legacy is that DP MST introduces device entry. Each pin can contain
+several device entries. Each device entry behaves as a pin.
+
+As each pin may contain several device entries and each codec may contain
+several pins, if we use one pcm per per_pin, there will be many PCMs.
+The new solution is to create a few PCMs and to dynamically bind pcm to
+per_pin. Driver uses spec->dyn_pcm_assign flag to indicate whether to use
+the new solution.
+
+PCM
+===
+To be added
+
+
+Jack
+====
+
+Presume:
+ - MST must be dyn_pcm_assign, and it is acomp (for Intel scenario);
+ - NON-MST may or may not be dyn_pcm_assign, it can be acomp or !acomp;
+
+So there are the following scenarios:
+ a. MST (&& dyn_pcm_assign && acomp)
+ b. NON-MST && dyn_pcm_assign && acomp
+ c. NON-MST && !dyn_pcm_assign && !acomp
+
+Below discussion will ignore MST and NON-MST difference as it doesn't
+impact on jack handling too much.
+
+Driver uses struct hdmi_pcm pcm[] array in hdmi_spec and snd_jack is
+a member of hdmi_pcm. Each pin has one struct hdmi_pcm * pcm pointer.
+
+For !dyn_pcm_assign, per_pin->pcm will assigned to spec->pcm[n] statically.
+
+For dyn_pcm_assign, per_pin->pcm will assigned to spec->pcm[n]
+when monitor is hotplugged.
+
+
+Build Jack
+----------
+
+- dyn_pcm_assign
+Will not use hda_jack but use snd_jack in spec->pcm_rec[pcm_idx].jack directly.
+
+- !dyn_pcm_assign
+Use hda_jack and assign spec->pcm_rec[pcm_idx].jack = jack->jack statically.
+
+
+Unsolicited Event Enabling
+--------------------------
+Enable unsolicited event if !acomp.
+
+
+Monitor Hotplug Event Handling
+------------------------------
+- acomp
+pin_eld_notify() -> check_presence_and_report() -> hdmi_present_sense() ->
+sync_eld_via_acomp().
+Use directly snd_jack_report() on spec->pcm_rec[pcm_idx].jack for
+both dyn_pcm_assign and !dyn_pcm_assign
+
+- !acomp
+Hdmi_unsol_event() -> hdmi_intrinsic_event() -> check_presence_and_report() ->
+hdmi_present_sense() -> hdmi_prepsent_sense_via_verbs()
+Use directly snd_jack_report() on spec->pcm_rec[pcm_idx].jack for dyn_pcm_assign.
+Use hda_jack mechanism to handle jack events.
+
+
+Others to be added later
+========================
index e11a0bd6c66e3f6068fbe032e1c67412ab09b6d3..0516ecda54d35fc6a590750a3f27e76083e80003 100644 (file)
@@ -34,6 +34,7 @@ struct ssc_device *ssc_request(unsigned int ssc_num)
                if (ssc->pdev->dev.of_node) {
                        if (of_alias_get_id(ssc->pdev->dev.of_node, "ssc")
                                == ssc_num) {
+                               ssc->pdev->id = ssc_num;
                                ssc_valid = 1;
                                break;
                        }
index 2af97691e8781382d3b2484d3bffc4eb5a43a247..dec6221e819801a674cde431bb8f9b5c50b23b80 100644 (file)
@@ -403,6 +403,18 @@ static inline int drm_eld_size(const uint8_t *eld)
        return DRM_ELD_HEADER_BLOCK_SIZE + eld[DRM_ELD_BASELINE_ELD_LEN] * 4;
 }
 
+/**
+ * drm_eld_get_conn_type - Get device type hdmi/dp connected
+ * @eld: pointer to an ELD memory structure
+ *
+ * The caller need to use %DRM_ELD_CONN_TYPE_HDMI or %DRM_ELD_CONN_TYPE_DP to
+ * identify the display type connected.
+ */
+static inline u8 drm_eld_get_conn_type(const uint8_t *eld)
+{
+       return eld[DRM_ELD_SAD_COUNT_CONN_TYPE] & DRM_ELD_CONN_TYPE_MASK;
+}
+
 struct edid *drm_do_get_edid(struct drm_connector *connector,
        int (*get_edid_block)(void *data, u8 *buf, unsigned int block,
                              size_t len),
index a81766cae230ebf37998edfff5922e8054817232..9db1b905df2487e8d014e1c7d6592f29152059d2 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961/ADAU1781/ADAU1781 codecs
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961/ADAU1381/ADAU1781 codecs
  *
  * Copyright 2011-2014 Analog Devices Inc.
  * Author: Lars-Peter Clausen <lars@metafoo.de>
diff --git a/include/sound/hda_chmap.h b/include/sound/hda_chmap.h
new file mode 100644 (file)
index 0000000..e20d219
--- /dev/null
@@ -0,0 +1,76 @@
+/*
+ * For multichannel support
+ */
+
+#ifndef __SOUND_HDA_CHMAP_H
+#define __SOUND_HDA_CHMAP_H
+
+#include <sound/pcm.h>
+#include <sound/hdaudio.h>
+
+
+#define SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE 80
+
+struct hdac_cea_channel_speaker_allocation {
+       int ca_index;
+       int speakers[8];
+
+       /* derived values, just for convenience */
+       int channels;
+       int spk_mask;
+};
+struct hdac_chmap;
+
+struct hdac_chmap_ops {
+       /*
+        * Helpers for producing the channel map TLVs. These can be overridden
+        * for devices that have non-standard mapping requirements.
+        */
+       int (*chmap_cea_alloc_validate_get_type)(struct hdac_chmap *chmap,
+               struct hdac_cea_channel_speaker_allocation *cap, int channels);
+       void (*cea_alloc_to_tlv_chmap)(struct hdac_chmap *hchmap,
+               struct hdac_cea_channel_speaker_allocation *cap,
+               unsigned int *chmap, int channels);
+
+       /* check that the user-given chmap is supported */
+       int (*chmap_validate)(struct hdac_chmap *hchmap, int ca,
+                       int channels, unsigned char *chmap);
+
+       void (*get_chmap)(struct hdac_device *hdac, int pcm_idx,
+                                       unsigned char *chmap);
+       void (*set_chmap)(struct hdac_device *hdac, int pcm_idx,
+                       unsigned char *chmap, int prepared);
+       bool (*is_pcm_attached)(struct hdac_device *hdac, int pcm_idx);
+
+       /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */
+       int (*pin_get_slot_channel)(struct hdac_device *codec,
+                       hda_nid_t pin_nid, int asp_slot);
+       int (*pin_set_slot_channel)(struct hdac_device *codec,
+                       hda_nid_t pin_nid, int asp_slot, int channel);
+       void (*set_channel_count)(struct hdac_device *codec,
+                               hda_nid_t cvt_nid, int chs);
+};
+
+struct hdac_chmap {
+       unsigned int channels_max; /* max over all cvts */
+       struct hdac_chmap_ops ops;
+       struct hdac_device *hdac;
+};
+
+void snd_hdac_register_chmap_ops(struct hdac_device *hdac,
+                               struct hdac_chmap *chmap);
+int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,
+                       int channels, bool chmap_set,
+                       bool non_pcm, unsigned char *map);
+int snd_hdac_get_active_channels(int ca);
+void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,
+                      hda_nid_t pin_nid, bool non_pcm, int ca,
+                      int channels, unsigned char *map,
+                      bool chmap_set);
+void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen);
+struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca);
+int snd_hdac_chmap_to_spk_mask(unsigned char c);
+int snd_hdac_spk_to_chmap(int spk);
+int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
+                               struct hdac_chmap *chmap);
+#endif /* __SOUND_HDA_CHMAP_H */
index c21c38ce74501dbdac327e9cac8b0b6d051a42b0..93e63c56f48fbdbedeecd07a3dca10127debf1b9 100644 (file)
@@ -168,11 +168,13 @@ int snd_hdac_power_up(struct hdac_device *codec);
 int snd_hdac_power_down(struct hdac_device *codec);
 int snd_hdac_power_up_pm(struct hdac_device *codec);
 int snd_hdac_power_down_pm(struct hdac_device *codec);
+int snd_hdac_keep_power_up(struct hdac_device *codec);
 #else
 static inline int snd_hdac_power_up(struct hdac_device *codec) { return 0; }
 static inline int snd_hdac_power_down(struct hdac_device *codec) { return 0; }
 static inline int snd_hdac_power_up_pm(struct hdac_device *codec) { return 0; }
 static inline int snd_hdac_power_down_pm(struct hdac_device *codec) { return 0; }
+static inline int snd_hdac_keep_power_up(struct hdac_device *codec) { return 0; }
 #endif
 
 /*
index 23bede121c78b97cd0df352d9aeb2fb36a148f85..1e84bfb553cf75017f9485a355b311460320235e 100644 (file)
@@ -72,14 +72,16 @@ enum snd_jack_types {
 #define SND_JACK_SWITCH_TYPES 6
 
 struct snd_jack {
-       struct input_dev *input_dev;
        struct list_head kctl_list;
        struct snd_card *card;
+       const char *id;
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+       struct input_dev *input_dev;
        int registered;
        int type;
-       const char *id;
        char name[100];
        unsigned int key[6];   /* Keep in sync with definitions above */
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
        void *private_data;
        void (*private_free)(struct snd_jack *);
 };
@@ -89,10 +91,11 @@ struct snd_jack {
 int snd_jack_new(struct snd_card *card, const char *id, int type,
                 struct snd_jack **jack, bool initial_kctl, bool phantom_jack);
 int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name, int mask);
+#ifdef CONFIG_SND_JACK_INPUT_DEV
 void snd_jack_set_parent(struct snd_jack *jack, struct device *parent);
 int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
                     int keytype);
-
+#endif
 void snd_jack_report(struct snd_jack *jack, int status);
 
 #else
@@ -107,6 +110,13 @@ static inline int snd_jack_add_new_kctl(struct snd_jack *jack, const char * name
        return 0;
 }
 
+static inline void snd_jack_report(struct snd_jack *jack, int status)
+{
+}
+
+#endif
+
+#if !defined(CONFIG_SND_JACK) || !defined(CONFIG_SND_JACK_INPUT_DEV)
 static inline void snd_jack_set_parent(struct snd_jack *jack,
                                       struct device *parent)
 {
@@ -118,11 +128,6 @@ static inline int snd_jack_set_key(struct snd_jack *jack,
 {
        return 0;
 }
-
-static inline void snd_jack_report(struct snd_jack *jack, int status)
-{
-}
-
-#endif
+#endif /* !CONFIG_SND_JACK || !CONFIG_SND_JACK_INPUT_DEV */
 
 #endif
index b0be09279943fc1a911a6071507ebdd79eed2912..af1fb37c6b265cf20de58dee1e0d7490669a9bf1 100644 (file)
@@ -1093,6 +1093,8 @@ unsigned int snd_pcm_rate_to_rate_bit(unsigned int rate);
 unsigned int snd_pcm_rate_bit_to_rate(unsigned int rate_bit);
 unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a,
                                         unsigned int rates_b);
+unsigned int snd_pcm_rate_range_to_bits(unsigned int rate_min,
+                                       unsigned int rate_max);
 
 /**
  * snd_pcm_set_runtime_buffer - Set the PCM runtime buffer
index 5b68e3f5aa85b301eaab5ff6975b76e257a00c52..b897b9d6316119bae25dcf81aac8c21bee1970ab 100644 (file)
@@ -56,12 +56,6 @@ struct snd_soc_dobj_widget {
        unsigned int kcontrol_enum:1;   /* this widget is an enum kcontrol */
 };
 
-/* dynamic PCM DAI object */
-struct snd_soc_dobj_pcm_dai {
-       struct snd_soc_tplg_pcm_dai *pd;
-       unsigned int count;
-};
-
 /* generic dynamic object - all dynamic objects belong to this struct */
 struct snd_soc_dobj {
        enum snd_soc_dobj_type type;
@@ -71,7 +65,6 @@ struct snd_soc_dobj {
        union {
                struct snd_soc_dobj_control control;
                struct snd_soc_dobj_widget widget;
-               struct snd_soc_dobj_pcm_dai pcm_dai;
        };
        void *private; /* core does not touch this */
 };
@@ -126,10 +119,16 @@ struct snd_soc_tplg_ops {
        int (*widget_unload)(struct snd_soc_component *,
                struct snd_soc_dobj *);
 
-       /* FE - used for any driver specific init */
-       int (*pcm_dai_load)(struct snd_soc_component *,
-               struct snd_soc_tplg_pcm_dai *pcm_dai, int num_fe);
-       int (*pcm_dai_unload)(struct snd_soc_component *,
+       /* FE DAI - used for any driver specific init */
+       int (*dai_load)(struct snd_soc_component *,
+               struct snd_soc_dai_driver *dai_drv);
+       int (*dai_unload)(struct snd_soc_component *,
+               struct snd_soc_dobj *);
+
+       /* DAI link - used for any driver specific init */
+       int (*link_load)(struct snd_soc_component *,
+               struct snd_soc_dai_link *link);
+       int (*link_unload)(struct snd_soc_component *,
                struct snd_soc_dobj *);
 
        /* callback to handle vendor bespoke data */
index 7afb72ceac5628b5efbdb616bde8e123580e3260..02b4a215fd751e6fcddf1856f667fe8c3e56a2eb 100644 (file)
@@ -27,7 +27,6 @@
 #include <sound/compress_driver.h>
 #include <sound/control.h>
 #include <sound/ac97_codec.h>
-#include <sound/soc-topology.h>
 
 /*
  * Convenience kcontrol builders
@@ -404,6 +403,7 @@ struct snd_soc_jack_zone;
 struct snd_soc_jack_pin;
 #include <sound/soc-dapm.h>
 #include <sound/soc-dpcm.h>
+#include <sound/soc-topology.h>
 
 struct snd_soc_jack_gpio;
 
index 5a5fa4956ebd66f989f7e8886d77e09566127ac0..7b7659a79ac4c79cfac5061defb1af90e5c6f144 100644 (file)
@@ -25,7 +25,7 @@
 #include <sound/asound.h>
 
 /** version of the sequencer */
-#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION (1, 0, 1)
+#define SNDRV_SEQ_VERSION SNDRV_PROTOCOL_VERSION(1, 0, 2)
 
 /**
  * definition of sequencer event types
@@ -357,7 +357,9 @@ struct snd_seq_client_info {
        unsigned char event_filter[32]; /* event filter bitmap */
        int num_ports;                  /* RO: number of ports */
        int event_lost;                 /* number of lost events */
-       char reserved[64];              /* for future use */
+       int card;                       /* RO: card number[kernel] */
+       int pid;                        /* RO: pid[user] */
+       char reserved[56];              /* for future use */
 };
 
 
@@ -594,14 +596,8 @@ struct snd_seq_query_subs {
 #define SNDRV_SEQ_IOCTL_GET_QUEUE_STATUS _IOWR('S', 0x40, struct snd_seq_queue_status)
 #define SNDRV_SEQ_IOCTL_GET_QUEUE_TEMPO        _IOWR('S', 0x41, struct snd_seq_queue_tempo)
 #define SNDRV_SEQ_IOCTL_SET_QUEUE_TEMPO        _IOW ('S', 0x42, struct snd_seq_queue_tempo)
-#define SNDRV_SEQ_IOCTL_GET_QUEUE_OWNER        _IOWR('S', 0x43, struct snd_seq_queue_owner)
-#define SNDRV_SEQ_IOCTL_SET_QUEUE_OWNER        _IOW ('S', 0x44, struct snd_seq_queue_owner)
 #define SNDRV_SEQ_IOCTL_GET_QUEUE_TIMER        _IOWR('S', 0x45, struct snd_seq_queue_timer)
 #define SNDRV_SEQ_IOCTL_SET_QUEUE_TIMER        _IOW ('S', 0x46, struct snd_seq_queue_timer)
-/* XXX
-#define SNDRV_SEQ_IOCTL_GET_QUEUE_SYNC _IOWR('S', 0x53, struct snd_seq_queue_sync)
-#define SNDRV_SEQ_IOCTL_SET_QUEUE_SYNC _IOW ('S', 0x54, struct snd_seq_queue_sync)
-*/
 #define SNDRV_SEQ_IOCTL_GET_QUEUE_CLIENT       _IOWR('S', 0x49, struct snd_seq_queue_client)
 #define SNDRV_SEQ_IOCTL_SET_QUEUE_CLIENT       _IOW ('S', 0x4a, struct snd_seq_queue_client)
 #define SNDRV_SEQ_IOCTL_GET_CLIENT_POOL        _IOWR('S', 0x4b, struct snd_seq_client_pool)
index a82108e5d1c0ccfb627daf84ac80b153fe123f66..67bf49d8c94407ca6fe5547e7ce07ef992bab3fc 100644 (file)
 #ifndef _UAPI__SOUND_ASOUND_H
 #define _UAPI__SOUND_ASOUND_H
 
+#if defined(__KERNEL__) || defined(__linux__)
 #include <linux/types.h>
+#else
+#include <sys/ioctl.h>
+#endif
 
 #ifndef __KERNEL__
 #include <stdlib.h>
index a2a1e24becc6b8f2f6288a8853b02e6dd884becd..6d12ca9bcb807d3a79a20f342eb15093ee8db9c9 100644 (file)
@@ -24,12 +24,15 @@ config SND_RAWMIDI
 config SND_COMPRESS_OFFLOAD
        tristate
 
-# To be effective this also requires INPUT - users should say:
-#    select SND_JACK if INPUT=y || INPUT=SND
-# to avoid having to force INPUT on.
 config SND_JACK
        bool
 
+# enable input device support in jack layer
+config SND_JACK_INPUT_DEV
+       bool
+       depends on SND_JACK
+       default y if INPUT=y || INPUT=SND
+
 config SND_SEQUENCER
        tristate "Sequencer support"
        select SND_TIMER
index 7fac3cae8abd0a232a3d7a1c2563999495df6ec2..a9933c07a6bf542e38b54ec8ed70204fbc429d55 100644 (file)
@@ -69,11 +69,14 @@ struct snd_compr_file {
 
 /*
  * a note on stream states used:
- * we use follwing states in the compressed core
+ * we use following states in the compressed core
  * SNDRV_PCM_STATE_OPEN: When stream has been opened.
  * SNDRV_PCM_STATE_SETUP: When stream has been initialized. This is done by
- *     calling SNDRV_COMPRESS_SET_PARAMS. running streams will come to this
+ *     calling SNDRV_COMPRESS_SET_PARAMS. Running streams will come to this
  *     state at stop by calling SNDRV_COMPRESS_STOP, or at end of drain.
+ * SNDRV_PCM_STATE_PREPARED: When a stream has been written to (for
+ *     playback only). User after setting up stream writes the data buffer
+ *     before starting the stream.
  * SNDRV_PCM_STATE_RUNNING: When stream has been started and is
  *     decoding/encoding and rendering/capturing data.
  * SNDRV_PCM_STATE_DRAINING: When stream is draining current data. This is done
@@ -286,6 +289,7 @@ static ssize_t snd_compr_write(struct file *f, const char __user *buf,
        mutex_lock(&stream->device->lock);
        /* write is allowed when stream is running or has been steup */
        if (stream->runtime->state != SNDRV_PCM_STATE_SETUP &&
+           stream->runtime->state != SNDRV_PCM_STATE_PREPARED &&
                        stream->runtime->state != SNDRV_PCM_STATE_RUNNING) {
                mutex_unlock(&stream->device->lock);
                return -EBADFD;
@@ -700,7 +704,7 @@ static int snd_compress_wait_for_drain(struct snd_compr_stream *stream)
 
        /*
         * We are called with lock held. So drop the lock while we wait for
-        * drain complete notfication from the driver
+        * drain complete notification from the driver
         *
         * It is expected that driver will notify the drain completion and then
         * stream will be moved to SETUP state, even if draining resulted in an
@@ -755,7 +759,7 @@ static int snd_compr_next_track(struct snd_compr_stream *stream)
        if (stream->runtime->state != SNDRV_PCM_STATE_RUNNING)
                return -EPERM;
 
-       /* you can signal next track isf this is intended to be a gapless stream
+       /* you can signal next track if this is intended to be a gapless stream
         * and current track metadata is set
         */
        if (stream->metadata_set == false)
index 0608f216f3592fc597c85c2c92594b891e499647..1fa70766ffabedea3cd5d3bf20779dfb7e3fcff8 100644 (file)
@@ -196,7 +196,7 @@ static int get_ctl_type(struct snd_card *card, struct snd_ctl_elem_id *id,
        kctl = snd_ctl_find_id(card, id);
        if (! kctl) {
                up_read(&card->controls_rwsem);
-               return -ENXIO;
+               return -ENOENT;
        }
        info = kzalloc(sizeof(*info), GFP_KERNEL);
        if (info == NULL) {
index 7237acbdcbbc8e617ac2c06fd268adc6a84b325c..f652e90efd7e7a3a8ab38a254cdd50ba4b369dab 100644 (file)
@@ -32,6 +32,7 @@ struct snd_jack_kctl {
        unsigned int mask_bits; /* only masked status bits are reported via kctl */
 };
 
+#ifdef CONFIG_SND_JACK_INPUT_DEV
 static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
        SW_HEADPHONE_INSERT,
        SW_MICROPHONE_INSERT,
@@ -40,9 +41,11 @@ static int jack_switch_types[SND_JACK_SWITCH_TYPES] = {
        SW_VIDEOOUT_INSERT,
        SW_LINEIN_INSERT,
 };
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
 
 static int snd_jack_dev_disconnect(struct snd_device *device)
 {
+#ifdef CONFIG_SND_JACK_INPUT_DEV
        struct snd_jack *jack = device->device_data;
 
        if (!jack->input_dev)
@@ -55,6 +58,7 @@ static int snd_jack_dev_disconnect(struct snd_device *device)
        else
                input_free_device(jack->input_dev);
        jack->input_dev = NULL;
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
        return 0;
 }
 
@@ -79,6 +83,7 @@ static int snd_jack_dev_free(struct snd_device *device)
        return 0;
 }
 
+#ifdef CONFIG_SND_JACK_INPUT_DEV
 static int snd_jack_dev_register(struct snd_device *device)
 {
        struct snd_jack *jack = device->device_data;
@@ -116,6 +121,7 @@ static int snd_jack_dev_register(struct snd_device *device)
 
        return err;
 }
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
 
 static void snd_jack_kctl_private_free(struct snd_kcontrol *kctl)
 {
@@ -209,11 +215,12 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
        struct snd_jack *jack;
        struct snd_jack_kctl *jack_kctl = NULL;
        int err;
-       int i;
        static struct snd_device_ops ops = {
                .dev_free = snd_jack_dev_free,
+#ifdef CONFIG_SND_JACK_INPUT_DEV
                .dev_register = snd_jack_dev_register,
                .dev_disconnect = snd_jack_dev_disconnect,
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
        };
 
        if (initial_kctl) {
@@ -230,6 +237,9 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
 
        /* don't creat input device for phantom jack */
        if (!phantom_jack) {
+#ifdef CONFIG_SND_JACK_INPUT_DEV
+               int i;
+
                jack->input_dev = input_allocate_device();
                if (jack->input_dev == NULL) {
                        err = -ENOMEM;
@@ -245,6 +255,7 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
                                input_set_capability(jack->input_dev, EV_SW,
                                                     jack_switch_types[i]);
 
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
        }
 
        err = snd_device_new(card, SNDRV_DEV_JACK, jack, &ops);
@@ -262,13 +273,16 @@ int snd_jack_new(struct snd_card *card, const char *id, int type,
        return 0;
 
 fail_input:
+#ifdef CONFIG_SND_JACK_INPUT_DEV
        input_free_device(jack->input_dev);
+#endif
        kfree(jack->id);
        kfree(jack);
        return err;
 }
 EXPORT_SYMBOL(snd_jack_new);
 
+#ifdef CONFIG_SND_JACK_INPUT_DEV
 /**
  * snd_jack_set_parent - Set the parent device for a jack
  *
@@ -326,10 +340,10 @@ int snd_jack_set_key(struct snd_jack *jack, enum snd_jack_types type,
 
        jack->type |= type;
        jack->key[key] = keytype;
-
        return 0;
 }
 EXPORT_SYMBOL(snd_jack_set_key);
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
 
 /**
  * snd_jack_report - Report the current status of a jack
@@ -340,7 +354,9 @@ EXPORT_SYMBOL(snd_jack_set_key);
 void snd_jack_report(struct snd_jack *jack, int status)
 {
        struct snd_jack_kctl *jack_kctl;
+#ifdef CONFIG_SND_JACK_INPUT_DEV
        int i;
+#endif
 
        if (!jack)
                return;
@@ -349,6 +365,7 @@ void snd_jack_report(struct snd_jack *jack, int status)
                snd_kctl_jack_report(jack->card, jack_kctl->kctl,
                                            status & jack_kctl->mask_bits);
 
+#ifdef CONFIG_SND_JACK_INPUT_DEV
        if (!jack->input_dev)
                return;
 
@@ -369,6 +386,6 @@ void snd_jack_report(struct snd_jack *jack, int status)
        }
 
        input_sync(jack->input_dev);
-
+#endif /* CONFIG_SND_JACK_INPUT_DEV */
 }
 EXPORT_SYMBOL(snd_jack_report);
index 6b5a811e01a544f3379829b8ebfa3773904691bb..3a9b66c6e09c38933b63a4af6ee20e34efa09118 100644 (file)
@@ -322,7 +322,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
                        char name[16];
                        snd_pcm_debug_name(substream, name, sizeof(name));
                        pcm_err(substream->pcm,
-                               "BUG: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
+                               "invalid position: %s, pos = %ld, buffer size = %ld, period size = %ld\n",
                                name, pos, runtime->buffer_size,
                                runtime->period_size);
                }
index ebe8444de6c6ea8f44a5cacfb39b963939d9880d..53dc37357bca9dea5450d818f2bcee9d71577aec 100644 (file)
@@ -565,3 +565,33 @@ unsigned int snd_pcm_rate_mask_intersect(unsigned int rates_a,
        return rates_a & rates_b;
 }
 EXPORT_SYMBOL_GPL(snd_pcm_rate_mask_intersect);
+
+/**
+ * snd_pcm_rate_range_to_bits - converts rate range to SNDRV_PCM_RATE_xxx bit
+ * @rate_min: the minimum sample rate
+ * @rate_max: the maximum sample rate
+ *
+ * This function has an implicit assumption: the rates in the given range have
+ * only the pre-defined rates like 44100 or 16000.
+ *
+ * Return: The SNDRV_PCM_RATE_xxx flag that corresponds to the given rate range,
+ * or SNDRV_PCM_RATE_KNOT for an unknown range.
+ */
+unsigned int snd_pcm_rate_range_to_bits(unsigned int rate_min,
+       unsigned int rate_max)
+{
+       unsigned int rates = 0;
+       int i;
+
+       for (i = 0; i < snd_pcm_known_rates.count; i++) {
+               if (snd_pcm_known_rates.list[i] >= rate_min
+                       && snd_pcm_known_rates.list[i] <= rate_max)
+                       rates |= 1 << i;
+       }
+
+       if (!rates)
+               rates = SNDRV_PCM_RATE_KNOT;
+
+       return rates;
+}
+EXPORT_SYMBOL_GPL(snd_pcm_rate_range_to_bits);
index 58e79e02f2174e29f409cf129030af87d240dfce..d6d9419d8bacd28959a3b154ff76cdee54abaac4 100644 (file)
@@ -364,6 +364,7 @@ static int snd_seq_open(struct inode *inode, struct file *file)
        /* fill client data */
        user->file = file;
        sprintf(client->name, "Client-%d", c);
+       client->data.user.owner = get_pid(task_pid(current));
 
        /* make others aware this new client */
        snd_seq_system_client_ev_client_start(c);
@@ -380,6 +381,7 @@ static int snd_seq_release(struct inode *inode, struct file *file)
                seq_free_client(client);
                if (client->data.user.fifo)
                        snd_seq_fifo_delete(&client->data.user.fifo);
+               put_pid(client->data.user.owner);
                kfree(client);
        }
 
@@ -1197,6 +1199,17 @@ static void get_client_info(struct snd_seq_client *cptr,
        info->event_lost = cptr->event_lost;
        memcpy(info->event_filter, cptr->event_filter, 32);
        info->num_ports = cptr->num_ports;
+
+       if (cptr->type == USER_CLIENT)
+               info->pid = pid_vnr(cptr->data.user.owner);
+       else
+               info->pid = -1;
+
+       if (cptr->type == KERNEL_CLIENT)
+               info->card = cptr->data.kernel.card ? cptr->data.kernel.card->number : -1;
+       else
+               info->card = -1;
+
        memset(info->reserved, 0, sizeof(info->reserved));
 }
 
@@ -2271,6 +2284,7 @@ int snd_seq_create_kernel_client(struct snd_card *card, int client_index,
 
        client->accept_input = 1;
        client->accept_output = 1;
+       client->data.kernel.card = card;
                
        va_start(args, name_fmt);
        vsnprintf(client->name, sizeof(client->name), name_fmt, args);
index 20f0a725ec7db293a1edc18c83af60fe3ab11d34..c6614254ef8af2f56b68193dc4ce4d35d49b3773 100644 (file)
@@ -33,6 +33,7 @@
 struct snd_seq_user_client {
        struct file *file;      /* file struct of client */
        /* ... */
+       struct pid *owner;
        
        /* fifo */
        struct snd_seq_fifo *fifo;      /* queue for incoming events */
@@ -41,6 +42,7 @@ struct snd_seq_user_client {
 
 struct snd_seq_kernel_client {
        /* ... */
+       struct snd_card *card;
 };
 
 
index dca817fc78941b5f9109e8117b3fc5adb621562e..aa1b15c155d1da20268eb9494144e604bad29fe6 100644 (file)
@@ -305,8 +305,6 @@ int snd_timer_open(struct snd_timer_instance **ti,
        return 0;
 }
 
-static int _snd_timer_stop(struct snd_timer_instance *timeri, int event);
-
 /*
  * close a timer instance
  */
@@ -318,25 +316,14 @@ int snd_timer_close(struct snd_timer_instance *timeri)
        if (snd_BUG_ON(!timeri))
                return -ENXIO;
 
+       mutex_lock(&register_mutex);
+       list_del(&timeri->open_list);
+
        /* force to stop the timer */
        snd_timer_stop(timeri);
 
-       if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
-               /* wait, until the active callback is finished */
-               spin_lock_irq(&slave_active_lock);
-               while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
-                       spin_unlock_irq(&slave_active_lock);
-                       udelay(10);
-                       spin_lock_irq(&slave_active_lock);
-               }
-               spin_unlock_irq(&slave_active_lock);
-               mutex_lock(&register_mutex);
-               list_del(&timeri->open_list);
-               mutex_unlock(&register_mutex);
-       } else {
-               timer = timeri->timer;
-               if (snd_BUG_ON(!timer))
-                       goto out;
+       timer = timeri->timer;
+       if (timer) {
                /* wait, until the active callback is finished */
                spin_lock_irq(&timer->lock);
                while (timeri->flags & SNDRV_TIMER_IFLG_CALLBACK) {
@@ -345,11 +332,7 @@ int snd_timer_close(struct snd_timer_instance *timeri)
                        spin_lock_irq(&timer->lock);
                }
                spin_unlock_irq(&timer->lock);
-               mutex_lock(&register_mutex);
-               list_del(&timeri->open_list);
-               if (list_empty(&timer->open_list_head) &&
-                   timer->hw.close)
-                       timer->hw.close(timer);
+
                /* remove slave links */
                spin_lock_irq(&slave_active_lock);
                spin_lock(&timer->lock);
@@ -363,18 +346,27 @@ int snd_timer_close(struct snd_timer_instance *timeri)
                }
                spin_unlock(&timer->lock);
                spin_unlock_irq(&slave_active_lock);
-               /* release a card refcount for safe disconnection */
-               if (timer->card)
-                       put_device(&timer->card->card_dev);
-               mutex_unlock(&register_mutex);
+
+               /* slave doesn't need to release timer resources below */
+               if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+                       timer = NULL;
        }
- out:
+
        if (timeri->private_free)
                timeri->private_free(timeri);
        kfree(timeri->owner);
        kfree(timeri);
-       if (timer)
+
+       if (timer) {
+               if (list_empty(&timer->open_list_head) && timer->hw.close)
+                       timer->hw.close(timer);
+               /* release a card refcount for safe disconnection */
+               if (timer->card)
+                       put_device(&timer->card->card_dev);
                module_put(timer->module);
+       }
+
+       mutex_unlock(&register_mutex);
        return 0;
 }
 
@@ -395,7 +387,6 @@ unsigned long snd_timer_resolution(struct snd_timer_instance *timeri)
 static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
 {
        struct snd_timer *timer;
-       unsigned long flags;
        unsigned long resolution = 0;
        struct snd_timer_instance *ts;
        struct timespec tstamp;
@@ -419,34 +410,66 @@ static void snd_timer_notify1(struct snd_timer_instance *ti, int event)
                return;
        if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
                return;
-       spin_lock_irqsave(&timer->lock, flags);
        list_for_each_entry(ts, &ti->slave_active_head, active_list)
                if (ts->ccallback)
                        ts->ccallback(ts, event + 100, &tstamp, resolution);
-       spin_unlock_irqrestore(&timer->lock, flags);
 }
 
-static int snd_timer_start1(struct snd_timer *timer, struct snd_timer_instance *timeri,
-                           unsigned long sticks)
+/* start/continue a master timer */
+static int snd_timer_start1(struct snd_timer_instance *timeri,
+                           bool start, unsigned long ticks)
 {
+       struct snd_timer *timer;
+       int result;
+       unsigned long flags;
+
+       timer = timeri->timer;
+       if (!timer)
+               return -EINVAL;
+
+       spin_lock_irqsave(&timer->lock, flags);
+       if (timer->card && timer->card->shutdown) {
+               result = -ENODEV;
+               goto unlock;
+       }
+       if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
+                            SNDRV_TIMER_IFLG_START)) {
+               result = -EBUSY;
+               goto unlock;
+       }
+
+       if (start)
+               timeri->ticks = timeri->cticks = ticks;
+       else if (!timeri->cticks)
+               timeri->cticks = 1;
+       timeri->pticks = 0;
+
        list_move_tail(&timeri->active_list, &timer->active_list_head);
        if (timer->running) {
                if (timer->hw.flags & SNDRV_TIMER_HW_SLAVE)
                        goto __start_now;
                timer->flags |= SNDRV_TIMER_FLG_RESCHED;
                timeri->flags |= SNDRV_TIMER_IFLG_START;
-               return 1;       /* delayed start */
+               result = 1; /* delayed start */
        } else {
-               timer->sticks = sticks;
+               if (start)
+                       timer->sticks = ticks;
                timer->hw.start(timer);
              __start_now:
                timer->running++;
                timeri->flags |= SNDRV_TIMER_IFLG_RUNNING;
-               return 0;
+               result = 0;
        }
+       snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
+                         SNDRV_TIMER_EVENT_CONTINUE);
+ unlock:
+       spin_unlock_irqrestore(&timer->lock, flags);
+       return result;
 }
 
-static int snd_timer_start_slave(struct snd_timer_instance *timeri)
+/* start/continue a slave timer */
+static int snd_timer_start_slave(struct snd_timer_instance *timeri,
+                                bool start)
 {
        unsigned long flags;
 
@@ -460,88 +483,37 @@ static int snd_timer_start_slave(struct snd_timer_instance *timeri)
                spin_lock(&timeri->timer->lock);
                list_add_tail(&timeri->active_list,
                              &timeri->master->slave_active_head);
+               snd_timer_notify1(timeri, start ? SNDRV_TIMER_EVENT_START :
+                                 SNDRV_TIMER_EVENT_CONTINUE);
                spin_unlock(&timeri->timer->lock);
        }
        spin_unlock_irqrestore(&slave_active_lock, flags);
        return 1; /* delayed start */
 }
 
-/*
- *  start the timer instance
- */
-int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
+/* stop/pause a master timer */
+static int snd_timer_stop1(struct snd_timer_instance *timeri, bool stop)
 {
        struct snd_timer *timer;
-       int result = -EINVAL;
+       int result = 0;
        unsigned long flags;
 
-       if (timeri == NULL || ticks < 1)
-               return -EINVAL;
-       if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
-               result = snd_timer_start_slave(timeri);
-               if (result >= 0)
-                       snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
-               return result;
-       }
-       timer = timeri->timer;
-       if (timer == NULL)
-               return -EINVAL;
-       if (timer->card && timer->card->shutdown)
-               return -ENODEV;
-       spin_lock_irqsave(&timer->lock, flags);
-       if (timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
-                            SNDRV_TIMER_IFLG_START)) {
-               result = -EBUSY;
-               goto unlock;
-       }
-       timeri->ticks = timeri->cticks = ticks;
-       timeri->pticks = 0;
-       result = snd_timer_start1(timer, timeri, ticks);
- unlock:
-       spin_unlock_irqrestore(&timer->lock, flags);
-       if (result >= 0)
-               snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_START);
-       return result;
-}
-
-static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
-{
-       struct snd_timer *timer;
-       unsigned long flags;
-
-       if (snd_BUG_ON(!timeri))
-               return -ENXIO;
-
-       if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE) {
-               spin_lock_irqsave(&slave_active_lock, flags);
-               if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
-                       spin_unlock_irqrestore(&slave_active_lock, flags);
-                       return -EBUSY;
-               }
-               if (timeri->timer)
-                       spin_lock(&timeri->timer->lock);
-               timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
-               list_del_init(&timeri->ack_list);
-               list_del_init(&timeri->active_list);
-               if (timeri->timer)
-                       spin_unlock(&timeri->timer->lock);
-               spin_unlock_irqrestore(&slave_active_lock, flags);
-               goto __end;
-       }
        timer = timeri->timer;
        if (!timer)
                return -EINVAL;
        spin_lock_irqsave(&timer->lock, flags);
        if (!(timeri->flags & (SNDRV_TIMER_IFLG_RUNNING |
                               SNDRV_TIMER_IFLG_START))) {
-               spin_unlock_irqrestore(&timer->lock, flags);
-               return -EBUSY;
+               result = -EBUSY;
+               goto unlock;
        }
        list_del_init(&timeri->ack_list);
        list_del_init(&timeri->active_list);
-       if (timer->card && timer->card->shutdown) {
-               spin_unlock_irqrestore(&timer->lock, flags);
-               return 0;
+       if (timer->card && timer->card->shutdown)
+               goto unlock;
+       if (stop) {
+               timeri->cticks = timeri->ticks;
+               timeri->pticks = 0;
        }
        if ((timeri->flags & SNDRV_TIMER_IFLG_RUNNING) &&
            !(--timer->running)) {
@@ -556,13 +528,49 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
                }
        }
        timeri->flags &= ~(SNDRV_TIMER_IFLG_RUNNING | SNDRV_TIMER_IFLG_START);
+       snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
+                         SNDRV_TIMER_EVENT_CONTINUE);
+ unlock:
        spin_unlock_irqrestore(&timer->lock, flags);
-      __end:
-       if (event != SNDRV_TIMER_EVENT_RESOLUTION)
-               snd_timer_notify1(timeri, event);
+       return result;
+}
+
+/* stop/pause a slave timer */
+static int snd_timer_stop_slave(struct snd_timer_instance *timeri, bool stop)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&slave_active_lock, flags);
+       if (!(timeri->flags & SNDRV_TIMER_IFLG_RUNNING)) {
+               spin_unlock_irqrestore(&slave_active_lock, flags);
+               return -EBUSY;
+       }
+       timeri->flags &= ~SNDRV_TIMER_IFLG_RUNNING;
+       if (timeri->timer) {
+               spin_lock(&timeri->timer->lock);
+               list_del_init(&timeri->ack_list);
+               list_del_init(&timeri->active_list);
+               snd_timer_notify1(timeri, stop ? SNDRV_TIMER_EVENT_STOP :
+                                 SNDRV_TIMER_EVENT_CONTINUE);
+               spin_unlock(&timeri->timer->lock);
+       }
+       spin_unlock_irqrestore(&slave_active_lock, flags);
        return 0;
 }
 
+/*
+ *  start the timer instance
+ */
+int snd_timer_start(struct snd_timer_instance *timeri, unsigned int ticks)
+{
+       if (timeri == NULL || ticks < 1)
+               return -EINVAL;
+       if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+               return snd_timer_start_slave(timeri, true);
+       else
+               return snd_timer_start1(timeri, true, ticks);
+}
+
 /*
  * stop the timer instance.
  *
@@ -570,21 +578,10 @@ static int _snd_timer_stop(struct snd_timer_instance *timeri, int event)
  */
 int snd_timer_stop(struct snd_timer_instance *timeri)
 {
-       struct snd_timer *timer;
-       unsigned long flags;
-       int err;
-
-       err = _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_STOP);
-       if (err < 0)
-               return err;
-       timer = timeri->timer;
-       if (!timer)
-               return -EINVAL;
-       spin_lock_irqsave(&timer->lock, flags);
-       timeri->cticks = timeri->ticks;
-       timeri->pticks = 0;
-       spin_unlock_irqrestore(&timer->lock, flags);
-       return 0;
+       if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+               return snd_timer_stop_slave(timeri, true);
+       else
+               return snd_timer_stop1(timeri, true);
 }
 
 /*
@@ -592,32 +589,10 @@ int snd_timer_stop(struct snd_timer_instance *timeri)
  */
 int snd_timer_continue(struct snd_timer_instance *timeri)
 {
-       struct snd_timer *timer;
-       int result = -EINVAL;
-       unsigned long flags;
-
-       if (timeri == NULL)
-               return result;
        if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
-               return snd_timer_start_slave(timeri);
-       timer = timeri->timer;
-       if (! timer)
-               return -EINVAL;
-       if (timer->card && timer->card->shutdown)
-               return -ENODEV;
-       spin_lock_irqsave(&timer->lock, flags);
-       if (timeri->flags & SNDRV_TIMER_IFLG_RUNNING) {
-               result = -EBUSY;
-               goto unlock;
-       }
-       if (!timeri->cticks)
-               timeri->cticks = 1;
-       timeri->pticks = 0;
-       result = snd_timer_start1(timer, timeri, timer->sticks);
- unlock:
-       spin_unlock_irqrestore(&timer->lock, flags);
-       snd_timer_notify1(timeri, SNDRV_TIMER_EVENT_CONTINUE);
-       return result;
+               return snd_timer_start_slave(timeri, false);
+       else
+               return snd_timer_start1(timeri, false, 0);
 }
 
 /*
@@ -625,7 +600,10 @@ int snd_timer_continue(struct snd_timer_instance *timeri)
  */
 int snd_timer_pause(struct snd_timer_instance * timeri)
 {
-       return _snd_timer_stop(timeri, SNDRV_TIMER_EVENT_PAUSE);
+       if (timeri->flags & SNDRV_TIMER_IFLG_SLAVE)
+               return snd_timer_stop_slave(timeri, false);
+       else
+               return snd_timer_stop1(timeri, false);
 }
 
 /*
index 2a008a9ccf85c48f743b7eb7ffd7cece8d76758c..fd4d18df84d3666c62482c07da0a6cf694f61e47 100644 (file)
@@ -65,8 +65,6 @@ struct mts64 {
        struct snd_card *card;
        struct snd_rawmidi *rmidi;
        struct pardevice *pardev;
-       int pardev_claimed;
-
        int open_count;
        int current_midi_output_port;
        int current_midi_input_port;
@@ -850,30 +848,6 @@ static void snd_mts64_interrupt(void *private)
        spin_unlock(&mts->lock);
 }
 
-static int snd_mts64_probe_port(struct parport *p)
-{
-       struct pardevice *pardev;
-       int res;
-
-       pardev = parport_register_device(p, DRIVER_NAME,
-                                        NULL, NULL, NULL,
-                                        0, NULL);
-       if (!pardev)
-               return -EIO;
-       
-       if (parport_claim(pardev)) {
-               parport_unregister_device(pardev);
-               return -EIO;
-       }
-
-       res = mts64_probe(p);
-
-       parport_release(pardev);
-       parport_unregister_device(pardev);
-
-       return res;
-}
-
 static void snd_mts64_attach(struct parport *p)
 {
        struct platform_device *device;
@@ -907,10 +881,20 @@ static void snd_mts64_detach(struct parport *p)
        /* nothing to do here */
 }
 
+static int snd_mts64_dev_probe(struct pardevice *pardev)
+{
+       if (strcmp(pardev->name, DRIVER_NAME))
+               return -ENODEV;
+
+       return 0;
+}
+
 static struct parport_driver mts64_parport_driver = {
-       .name   = "mts64",
-       .attach = snd_mts64_attach,
-       .detach = snd_mts64_detach
+       .name           = "mts64",
+       .probe          = snd_mts64_dev_probe,
+       .match_port     = snd_mts64_attach,
+       .detach         = snd_mts64_detach,
+       .devmodel       = true,
 };
 
 /*********************************************************************
@@ -922,8 +906,7 @@ static void snd_mts64_card_private_free(struct snd_card *card)
        struct pardevice *pardev = mts->pardev;
 
        if (pardev) {
-               if (mts->pardev_claimed)
-                       parport_release(pardev);
+               parport_release(pardev);
                parport_unregister_device(pardev);
        }
 
@@ -938,6 +921,12 @@ static int snd_mts64_probe(struct platform_device *pdev)
        struct snd_card *card = NULL;
        struct mts64 *mts = NULL;
        int err;
+       struct pardev_cb mts64_cb = {
+               .preempt = NULL,
+               .wakeup = NULL,
+               .irq_func = snd_mts64_interrupt,        /* ISR */
+               .flags = PARPORT_DEV_EXCL,              /* flags */
+       };
 
        p = platform_get_drvdata(pdev);
        platform_set_drvdata(pdev, NULL);
@@ -946,8 +935,6 @@ static int snd_mts64_probe(struct platform_device *pdev)
                return -ENODEV;
        if (!enable[dev]) 
                return -ENOENT;
-       if ((err = snd_mts64_probe_port(p)) < 0)
-               return err;
 
        err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
                           0, &card);
@@ -960,40 +947,42 @@ static int snd_mts64_probe(struct platform_device *pdev)
        sprintf(card->longname,  "%s at 0x%lx, irq %i", 
                card->shortname, p->base, p->irq);
 
-       pardev = parport_register_device(p,                   /* port */
-                                        DRIVER_NAME,         /* name */
-                                        NULL,                /* preempt */
-                                        NULL,                /* wakeup */
-                                        snd_mts64_interrupt, /* ISR */
-                                        PARPORT_DEV_EXCL,    /* flags */
-                                        (void *)card);       /* private */
-       if (pardev == NULL) {
+       mts64_cb.private = card;                         /* private */
+       pardev = parport_register_dev_model(p,           /* port */
+                                           DRIVER_NAME, /* name */
+                                           &mts64_cb,   /* callbacks */
+                                           pdev->id);   /* device number */
+       if (!pardev) {
                snd_printd("Cannot register pardevice\n");
                err = -EIO;
                goto __err;
        }
 
+       /* claim parport */
+       if (parport_claim(pardev)) {
+               snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+               err = -EIO;
+               goto free_pardev;
+       }
+
        if ((err = snd_mts64_create(card, pardev, &mts)) < 0) {
                snd_printd("Cannot create main component\n");
-               parport_unregister_device(pardev);
-               goto __err;
+               goto release_pardev;
        }
        card->private_data = mts;
        card->private_free = snd_mts64_card_private_free;
+
+       err = mts64_probe(p);
+       if (err) {
+               err = -EIO;
+               goto __err;
+       }
        
        if ((err = snd_mts64_rawmidi_create(card)) < 0) {
                snd_printd("Creating Rawmidi component failed\n");
                goto __err;
        }
 
-       /* claim parport */
-       if (parport_claim(pardev)) {
-               snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
-               err = -EIO;
-               goto __err;
-       }
-       mts->pardev_claimed = 1;
-
        /* init device */
        if ((err = mts64_device_init(p)) < 0)
                goto __err;
@@ -1009,6 +998,10 @@ static int snd_mts64_probe(struct platform_device *pdev)
        snd_printk(KERN_INFO "ESI Miditerminal 4140 on 0x%lx\n", p->base);
        return 0;
 
+release_pardev:
+       parport_release(pardev);
+free_pardev:
+       parport_unregister_device(pardev);
 __err:
        snd_card_free(card);
        return err;
@@ -1024,7 +1017,6 @@ static int snd_mts64_remove(struct platform_device *pdev)
        return 0;
 }
 
-
 static struct platform_driver snd_mts64_driver = {
        .probe  = snd_mts64_probe,
        .remove = snd_mts64_remove,
index 464385a480e491e2c325ceb01f56d9cade82f4fd..189e3e7028af4d8aa6637f57d122d3f7e23fa40b 100644 (file)
@@ -83,8 +83,6 @@ struct portman {
        struct snd_card *card;
        struct snd_rawmidi *rmidi;
        struct pardevice *pardev;
-       int pardev_claimed;
-
        int open_count;
        int mode[PORTMAN_NUM_INPUT_PORTS];
        struct snd_rawmidi_substream *midi_input[PORTMAN_NUM_INPUT_PORTS];
@@ -648,30 +646,6 @@ static void snd_portman_interrupt(void *userdata)
        spin_unlock(&pm->reg_lock);
 }
 
-static int snd_portman_probe_port(struct parport *p)
-{
-       struct pardevice *pardev;
-       int res;
-
-       pardev = parport_register_device(p, DRIVER_NAME,
-                                        NULL, NULL, NULL,
-                                        0, NULL);
-       if (!pardev)
-               return -EIO;
-       
-       if (parport_claim(pardev)) {
-               parport_unregister_device(pardev);
-               return -EIO;
-       }
-
-       res = portman_probe(p);
-
-       parport_release(pardev);
-       parport_unregister_device(pardev);
-
-       return res ? -EIO : 0;
-}
-
 static void snd_portman_attach(struct parport *p)
 {
        struct platform_device *device;
@@ -705,10 +679,20 @@ static void snd_portman_detach(struct parport *p)
        /* nothing to do here */
 }
 
+static int snd_portman_dev_probe(struct pardevice *pardev)
+{
+       if (strcmp(pardev->name, DRIVER_NAME))
+               return -ENODEV;
+
+       return 0;
+}
+
 static struct parport_driver portman_parport_driver = {
-       .name   = "portman2x4",
-       .attach = snd_portman_attach,
-       .detach = snd_portman_detach
+       .name           = "portman2x4",
+       .probe          = snd_portman_dev_probe,
+       .match_port     = snd_portman_attach,
+       .detach         = snd_portman_detach,
+       .devmodel       = true,
 };
 
 /*********************************************************************
@@ -720,8 +704,7 @@ static void snd_portman_card_private_free(struct snd_card *card)
        struct pardevice *pardev = pm->pardev;
 
        if (pardev) {
-               if (pm->pardev_claimed)
-                       parport_release(pardev);
+               parport_release(pardev);
                parport_unregister_device(pardev);
        }
 
@@ -736,6 +719,12 @@ static int snd_portman_probe(struct platform_device *pdev)
        struct snd_card *card = NULL;
        struct portman *pm = NULL;
        int err;
+       struct pardev_cb portman_cb = {
+               .preempt = NULL,
+               .wakeup = NULL,
+               .irq_func = snd_portman_interrupt,      /* ISR */
+               .flags = PARPORT_DEV_EXCL,              /* flags */
+       };
 
        p = platform_get_drvdata(pdev);
        platform_set_drvdata(pdev, NULL);
@@ -745,9 +734,6 @@ static int snd_portman_probe(struct platform_device *pdev)
        if (!enable[dev]) 
                return -ENOENT;
 
-       if ((err = snd_portman_probe_port(p)) < 0)
-               return err;
-
        err = snd_card_new(&pdev->dev, index[dev], id[dev], THIS_MODULE,
                           0, &card);
        if (err < 0) {
@@ -759,40 +745,42 @@ static int snd_portman_probe(struct platform_device *pdev)
        sprintf(card->longname,  "%s at 0x%lx, irq %i", 
                card->shortname, p->base, p->irq);
 
-       pardev = parport_register_device(p,                     /* port */
-                                        DRIVER_NAME,           /* name */
-                                        NULL,                  /* preempt */
-                                        NULL,                  /* wakeup */
-                                        snd_portman_interrupt, /* ISR */
-                                        PARPORT_DEV_EXCL,      /* flags */
-                                        (void *)card);         /* private */
+       portman_cb.private = card;                         /* private */
+       pardev = parport_register_dev_model(p,             /* port */
+                                           DRIVER_NAME,   /* name */
+                                           &portman_cb,   /* callbacks */
+                                           pdev->id);     /* device number */
        if (pardev == NULL) {
                snd_printd("Cannot register pardevice\n");
                err = -EIO;
                goto __err;
        }
 
+       /* claim parport */
+       if (parport_claim(pardev)) {
+               snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
+               err = -EIO;
+               goto free_pardev;
+       }
+
        if ((err = portman_create(card, pardev, &pm)) < 0) {
                snd_printd("Cannot create main component\n");
-               parport_unregister_device(pardev);
-               goto __err;
+               goto release_pardev;
        }
        card->private_data = pm;
        card->private_free = snd_portman_card_private_free;
+
+       err = portman_probe(p);
+       if (err) {
+               err = -EIO;
+               goto __err;
+       }
        
        if ((err = snd_portman_rawmidi_create(card)) < 0) {
                snd_printd("Creating Rawmidi component failed\n");
                goto __err;
        }
 
-       /* claim parport */
-       if (parport_claim(pardev)) {
-               snd_printd("Cannot claim parport 0x%lx\n", pardev->port->base);
-               err = -EIO;
-               goto __err;
-       }
-       pm->pardev_claimed = 1;
-
        /* init device */
        if ((err = portman_device_init(pm)) < 0)
                goto __err;
@@ -808,6 +796,10 @@ static int snd_portman_probe(struct platform_device *pdev)
        snd_printk(KERN_INFO "Portman 2x4 on 0x%lx\n", p->base);
        return 0;
 
+release_pardev:
+       parport_release(pardev);
+free_pardev:
+       parport_unregister_device(pardev);
 __err:
        snd_card_free(card);
        return err;
index 091290d1f3ea0a99b8a6d81d501a385863efe64d..3e4e0756e3febfeb90547f5f9085f13f6d948451 100644 (file)
@@ -300,6 +300,22 @@ bebob_probe(struct fw_unit *unit,
        return err;
 }
 
+/*
+ * This driver doesn't update streams in bus reset handler.
+ *
+ * DM1000/ DM1100/DM1500 chipsets with BeBoB firmware transfer packets with
+ * discontinued counter at bus reset. This discontinuity is immediately
+ * detected in packet streaming layer, then it sets XRUN to PCM substream.
+ *
+ * ALSA PCM applications can know the XRUN by getting -EPIPE from PCM operation.
+ * Then, they can recover the PCM substream by executing ioctl(2) with
+ * SNDRV_PCM_IOCTL_PREPARE. 'struct snd_pcm_ops.prepare' is called and drivers
+ * restart packet streaming.
+ *
+ * The above processing may be executed before this bus-reset handler is
+ * executed. When this handler updates streams with current isochronous
+ * channels, the streams already have the current ones.
+ */
 static void
 bebob_update(struct fw_unit *unit)
 {
@@ -309,7 +325,6 @@ bebob_update(struct fw_unit *unit)
                return;
 
        fcp_bus_reset(bebob->unit);
-       snd_bebob_stream_update_duplex(bebob);
 
        if (bebob->deferred_registration) {
                if (snd_card_register(bebob->card) < 0) {
@@ -327,10 +342,6 @@ static void bebob_remove(struct fw_unit *unit)
        if (bebob == NULL)
                return;
 
-       /* Awake bus-reset waiters. */
-       if (!completion_done(&bebob->bus_reset))
-               complete_all(&bebob->bus_reset);
-
        /* No need to wait for releasing card object in this context. */
        snd_card_free_when_closed(bebob->card);
 }
index 4d8fcc78e747cb5d4232ed9f76c3662a242d72d3..b50bb33d9d46616b1186143b03495b309967f770 100644 (file)
@@ -88,8 +88,6 @@ struct snd_bebob {
        unsigned int midi_input_ports;
        unsigned int midi_output_ports;
 
-       /* for bus reset quirk */
-       struct completion bus_reset;
        bool connected;
 
        struct amdtp_stream *master;
@@ -97,7 +95,7 @@ struct snd_bebob {
        struct amdtp_stream rx_stream;
        struct cmp_connection out_conn;
        struct cmp_connection in_conn;
-       atomic_t substreams_counter;
+       unsigned int substreams_counter;
 
        struct snd_bebob_stream_formation
                tx_stream_formations[SND_BEBOB_STRM_FMT_ENTRIES];
@@ -219,7 +217,6 @@ int snd_bebob_stream_discover(struct snd_bebob *bebob);
 int snd_bebob_stream_init_duplex(struct snd_bebob *bebob);
 int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate);
 void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob);
-void snd_bebob_stream_update_duplex(struct snd_bebob *bebob);
 void snd_bebob_stream_destroy_duplex(struct snd_bebob *bebob);
 
 void snd_bebob_stream_lock_changed(struct snd_bebob *bebob);
index 90d95be499b079c5d32c99b1b7b56a409ac377ec..868eb0decbec00a6c3005417b87b617f342348b3 100644 (file)
@@ -17,8 +17,10 @@ static int midi_capture_open(struct snd_rawmidi_substream *substream)
        if (err < 0)
                goto end;
 
-       atomic_inc(&bebob->substreams_counter);
+       mutex_lock(&bebob->mutex);
+       bebob->substreams_counter++;
        err = snd_bebob_stream_start_duplex(bebob, 0);
+       mutex_unlock(&bebob->mutex);
        if (err < 0)
                snd_bebob_stream_lock_release(bebob);
 end:
@@ -34,8 +36,10 @@ static int midi_playback_open(struct snd_rawmidi_substream *substream)
        if (err < 0)
                goto end;
 
-       atomic_inc(&bebob->substreams_counter);
+       mutex_lock(&bebob->mutex);
+       bebob->substreams_counter++;
        err = snd_bebob_stream_start_duplex(bebob, 0);
+       mutex_unlock(&bebob->mutex);
        if (err < 0)
                snd_bebob_stream_lock_release(bebob);
 end:
@@ -46,8 +50,10 @@ static int midi_capture_close(struct snd_rawmidi_substream *substream)
 {
        struct snd_bebob *bebob = substream->rmidi->private_data;
 
-       atomic_dec(&bebob->substreams_counter);
+       mutex_lock(&bebob->mutex);
+       bebob->substreams_counter--;
        snd_bebob_stream_stop_duplex(bebob);
+       mutex_unlock(&bebob->mutex);
 
        snd_bebob_stream_lock_release(bebob);
        return 0;
@@ -57,8 +63,10 @@ static int midi_playback_close(struct snd_rawmidi_substream *substream)
 {
        struct snd_bebob *bebob = substream->rmidi->private_data;
 
-       atomic_dec(&bebob->substreams_counter);
+       mutex_lock(&bebob->mutex);
+       bebob->substreams_counter--;
        snd_bebob_stream_stop_duplex(bebob);
+       mutex_unlock(&bebob->mutex);
 
        snd_bebob_stream_lock_release(bebob);
        return 0;
index ef224d6f5c248d0d28cf9754f96f20b8fc99df5c..5d7b9343fa85683268b6f6d6bfdfdeabe58e39cf 100644 (file)
@@ -218,8 +218,11 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
        if (err < 0)
                return err;
 
-       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
-               atomic_inc(&bebob->substreams_counter);
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&bebob->mutex);
+               bebob->substreams_counter++;
+               mutex_unlock(&bebob->mutex);
+       }
 
        amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params));
 
@@ -237,8 +240,11 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream,
        if (err < 0)
                return err;
 
-       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
-               atomic_inc(&bebob->substreams_counter);
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&bebob->mutex);
+               bebob->substreams_counter++;
+               mutex_unlock(&bebob->mutex);
+       }
 
        amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params));
 
@@ -250,8 +256,11 @@ pcm_capture_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_bebob *bebob = substream->private_data;
 
-       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               atomic_dec(&bebob->substreams_counter);
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&bebob->mutex);
+               bebob->substreams_counter--;
+               mutex_unlock(&bebob->mutex);
+       }
 
        snd_bebob_stream_stop_duplex(bebob);
 
@@ -262,8 +271,11 @@ pcm_playback_hw_free(struct snd_pcm_substream *substream)
 {
        struct snd_bebob *bebob = substream->private_data;
 
-       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
-               atomic_dec(&bebob->substreams_counter);
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&bebob->mutex);
+               bebob->substreams_counter--;
+               mutex_unlock(&bebob->mutex);
+       }
 
        snd_bebob_stream_stop_duplex(bebob);
 
index 5022c9b97ddfc007bc6119ab6a0cb41fafb2c16d..77cbb02bff342271555398bbd582ab5a4a578bed 100644 (file)
@@ -549,8 +549,7 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
                destroy_both_connections(bebob);
                goto end;
        }
-       /* See comments in next function */
-       init_completion(&bebob->bus_reset);
+
        bebob->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK;
 
        /*
@@ -588,29 +587,10 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
        struct amdtp_stream *master, *slave;
        enum cip_flags sync_mode;
        unsigned int curr_rate;
-       bool updated = false;
        int err = 0;
 
-       /*
-        * Normal BeBoB firmware has a quirk at bus reset to transmits packets
-        * with discontinuous value in dbc field.
-        *
-        * This 'struct completion' is used to call .update() at first to update
-        * connections/streams. Next following codes handle streaming error.
-        */
-       if (amdtp_streaming_error(&bebob->tx_stream)) {
-               if (completion_done(&bebob->bus_reset))
-                       reinit_completion(&bebob->bus_reset);
-
-               updated = (wait_for_completion_interruptible_timeout(
-                               &bebob->bus_reset,
-                               msecs_to_jiffies(FW_ISO_RESOURCE_DELAY)) > 0);
-       }
-
-       mutex_lock(&bebob->mutex);
-
        /* Need no substreams */
-       if (atomic_read(&bebob->substreams_counter) == 0)
+       if (bebob->substreams_counter == 0)
                goto end;
 
        err = get_sync_mode(bebob, &sync_mode);
@@ -642,8 +622,7 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
                amdtp_stream_stop(master);
        if (amdtp_streaming_error(slave))
                amdtp_stream_stop(slave);
-       if (!updated &&
-           !amdtp_stream_running(master) && !amdtp_stream_running(slave))
+       if (!amdtp_stream_running(master) && !amdtp_stream_running(slave))
                break_both_connections(bebob);
 
        /* stop streams if rate is different */
@@ -741,7 +720,6 @@ int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
                }
        }
 end:
-       mutex_unlock(&bebob->mutex);
        return err;
 }
 
@@ -757,9 +735,7 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
                master = &bebob->tx_stream;
        }
 
-       mutex_lock(&bebob->mutex);
-
-       if (atomic_read(&bebob->substreams_counter) == 0) {
+       if (bebob->substreams_counter == 0) {
                amdtp_stream_pcm_abort(master);
                amdtp_stream_stop(master);
 
@@ -768,32 +744,6 @@ void snd_bebob_stream_stop_duplex(struct snd_bebob *bebob)
 
                break_both_connections(bebob);
        }
-
-       mutex_unlock(&bebob->mutex);
-}
-
-void snd_bebob_stream_update_duplex(struct snd_bebob *bebob)
-{
-       /* vs. XRUN recovery due to discontinuity at bus reset */
-       mutex_lock(&bebob->mutex);
-
-       if ((cmp_connection_update(&bebob->in_conn) < 0) ||
-           (cmp_connection_update(&bebob->out_conn) < 0)) {
-               amdtp_stream_pcm_abort(&bebob->rx_stream);
-               amdtp_stream_pcm_abort(&bebob->tx_stream);
-               amdtp_stream_stop(&bebob->rx_stream);
-               amdtp_stream_stop(&bebob->tx_stream);
-               break_both_connections(bebob);
-       } else {
-               amdtp_stream_update(&bebob->rx_stream);
-               amdtp_stream_update(&bebob->tx_stream);
-       }
-
-       /* wake up stream_start_duplex() */
-       if (!completion_done(&bebob->bus_reset))
-               complete_all(&bebob->bus_reset);
-
-       mutex_unlock(&bebob->mutex);
 }
 
 /*
index 151b09f240f280d14233817ff64ef77d14d3a31b..a040617505a75df7024508deb1a2909ee9a9e8e9 100644 (file)
@@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&dice->lock, flags);
 
        if (up)
-               amdtp_am824_midi_trigger(&dice->tx_stream,
+               amdtp_am824_midi_trigger(&dice->tx_stream[0],
                                          substrm->number, substrm);
        else
-               amdtp_am824_midi_trigger(&dice->tx_stream,
+               amdtp_am824_midi_trigger(&dice->tx_stream[0],
                                          substrm->number, NULL);
 
        spin_unlock_irqrestore(&dice->lock, flags);
@@ -69,10 +69,10 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&dice->lock, flags);
 
        if (up)
-               amdtp_am824_midi_trigger(&dice->rx_stream,
+               amdtp_am824_midi_trigger(&dice->rx_stream[0],
                                         substrm->number, substrm);
        else
-               amdtp_am824_midi_trigger(&dice->rx_stream,
+               amdtp_am824_midi_trigger(&dice->rx_stream[0],
                                         substrm->number, NULL);
 
        spin_unlock_irqrestore(&dice->lock, flags);
@@ -103,16 +103,27 @@ static void set_midi_substream_names(struct snd_dice *dice,
 
 int snd_dice_create_midi(struct snd_dice *dice)
 {
+       __be32 reg;
        struct snd_rawmidi *rmidi;
        struct snd_rawmidi_str *str;
-       unsigned int i, midi_in_ports, midi_out_ports;
+       unsigned int midi_in_ports, midi_out_ports;
        int err;
 
-       midi_in_ports = midi_out_ports = 0;
-       for (i = 0; i < 3; i++) {
-               midi_in_ports = max(dice->tx_midi_ports[i], midi_in_ports);
-               midi_out_ports = max(dice->rx_midi_ports[i], midi_out_ports);
-       }
+       /*
+        * Use the number of MIDI conformant data channel at current sampling
+        * transfer frequency.
+        */
+       err = snd_dice_transaction_read_tx(dice, TX_NUMBER_MIDI,
+                                          &reg, sizeof(reg));
+       if (err < 0)
+               return err;
+       midi_in_ports = be32_to_cpu(reg);
+
+       err = snd_dice_transaction_read_rx(dice, RX_NUMBER_MIDI,
+                                          &reg, sizeof(reg));
+       if (err < 0)
+               return err;
+       midi_out_ports = be32_to_cpu(reg);
 
        if (midi_in_ports + midi_out_ports == 0)
                return 0;
index 9b3431999fc8b6df8dac3b58c42d6e1604c617e4..4aa0249826fd1378661d76a75cbb59a9b96ba8db 100644 (file)
@@ -9,99 +9,46 @@
 
 #include "dice.h"
 
-static int dice_rate_constraint(struct snd_pcm_hw_params *params,
-                               struct snd_pcm_hw_rule *rule)
-{
-       struct snd_pcm_substream *substream = rule->private;
-       struct snd_dice *dice = substream->private_data;
-
-       const struct snd_interval *c =
-               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-       struct snd_interval *r =
-               hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-       struct snd_interval rates = {
-               .min = UINT_MAX, .max = 0, .integer = 1
-       };
-       unsigned int i, rate, mode, *pcm_channels;
-
-       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-               pcm_channels = dice->tx_channels;
-       else
-               pcm_channels = dice->rx_channels;
-
-       for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
-               rate = snd_dice_rates[i];
-               if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
-                       continue;
-
-               if (!snd_interval_test(c, pcm_channels[mode]))
-                       continue;
-
-               rates.min = min(rates.min, rate);
-               rates.max = max(rates.max, rate);
-       }
-
-       return snd_interval_refine(r, &rates);
-}
-
-static int dice_channels_constraint(struct snd_pcm_hw_params *params,
-                                   struct snd_pcm_hw_rule *rule)
-{
-       struct snd_pcm_substream *substream = rule->private;
-       struct snd_dice *dice = substream->private_data;
-
-       const struct snd_interval *r =
-               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
-       struct snd_interval *c =
-               hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-       struct snd_interval channels = {
-               .min = UINT_MAX, .max = 0, .integer = 1
-       };
-       unsigned int i, rate, mode, *pcm_channels;
-
-       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
-               pcm_channels = dice->tx_channels;
-       else
-               pcm_channels = dice->rx_channels;
-
-       for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
-               rate = snd_dice_rates[i];
-               if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
-                       continue;
-
-               if (!snd_interval_test(r, rate))
-                       continue;
-
-               channels.min = min(channels.min, pcm_channels[mode]);
-               channels.max = max(channels.max, pcm_channels[mode]);
-       }
-
-       return snd_interval_refine(c, &channels);
-}
-
-static void limit_channels_and_rates(struct snd_dice *dice,
-                                    struct snd_pcm_runtime *runtime,
-                                    unsigned int *pcm_channels)
+static int limit_channels_and_rates(struct snd_dice *dice,
+                                   struct snd_pcm_runtime *runtime,
+                                   enum amdtp_stream_direction dir,
+                                   unsigned int index, unsigned int size)
 {
        struct snd_pcm_hardware *hw = &runtime->hw;
-       unsigned int i, rate, mode;
+       struct amdtp_stream *stream;
+       unsigned int rate;
+       __be32 reg;
+       int err;
 
-       hw->channels_min = UINT_MAX;
-       hw->channels_max = 0;
+       /*
+        * Retrieve current Multi Bit Linear Audio data channel and limit to
+        * it.
+        */
+       if (dir == AMDTP_IN_STREAM) {
+               stream = &dice->tx_stream[index];
+               err = snd_dice_transaction_read_tx(dice,
+                               size * index + TX_NUMBER_AUDIO,
+                               &reg, sizeof(reg));
+       } else {
+               stream = &dice->rx_stream[index];
+               err = snd_dice_transaction_read_rx(dice,
+                               size * index + RX_NUMBER_AUDIO,
+                               &reg, sizeof(reg));
+       }
+       if (err < 0)
+               return err;
 
-       for (i = 0; i < ARRAY_SIZE(snd_dice_rates); ++i) {
-               rate = snd_dice_rates[i];
-               if (snd_dice_stream_get_rate_mode(dice, rate, &mode) < 0)
-                       continue;
-               hw->rates |= snd_pcm_rate_to_rate_bit(rate);
+       hw->channels_min = hw->channels_max = be32_to_cpu(reg);
 
-               if (pcm_channels[mode] == 0)
-                       continue;
-               hw->channels_min = min(hw->channels_min, pcm_channels[mode]);
-               hw->channels_max = max(hw->channels_max, pcm_channels[mode]);
-       }
+       /* Retrieve current sampling transfer frequency and limit to it. */
+       err = snd_dice_transaction_get_rate(dice, &rate);
+       if (err < 0)
+               return err;
 
+       hw->rates = snd_pcm_rate_to_rate_bit(rate);
        snd_pcm_limit_hw_rates(runtime);
+
+       return 0;
 }
 
 static void limit_period_and_buffer(struct snd_pcm_hardware *hw)
@@ -121,8 +68,10 @@ static int init_hw_info(struct snd_dice *dice,
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_pcm_hardware *hw = &runtime->hw;
+       enum amdtp_stream_direction dir;
        struct amdtp_stream *stream;
-       unsigned int *pcm_channels;
+       __be32 reg[2];
+       unsigned int count, size;
        int err;
 
        hw->info = SNDRV_PCM_INFO_MMAP |
@@ -134,38 +83,38 @@ static int init_hw_info(struct snd_dice *dice,
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
                hw->formats = AM824_IN_PCM_FORMAT_BITS;
-               stream = &dice->tx_stream;
-               pcm_channels = dice->tx_channels;
+               dir = AMDTP_IN_STREAM;
+               stream = &dice->tx_stream[substream->pcm->device];
+               err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg,
+                                                  sizeof(reg));
        } else {
                hw->formats = AM824_OUT_PCM_FORMAT_BITS;
-               stream = &dice->rx_stream;
-               pcm_channels = dice->rx_channels;
+               dir = AMDTP_OUT_STREAM;
+               stream = &dice->rx_stream[substream->pcm->device];
+               err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg,
+                                                  sizeof(reg));
        }
 
-       limit_channels_and_rates(dice, runtime, pcm_channels);
-       limit_period_and_buffer(hw);
-
-       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_RATE,
-                                 dice_rate_constraint, substream,
-                                 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
        if (err < 0)
-               goto end;
-       err = snd_pcm_hw_rule_add(runtime, 0, SNDRV_PCM_HW_PARAM_CHANNELS,
-                                 dice_channels_constraint, substream,
-                                 SNDRV_PCM_HW_PARAM_RATE, -1);
+               return err;
+
+       count = min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+       if (substream->pcm->device >= count)
+               return -ENXIO;
+
+       size = be32_to_cpu(reg[1]) * 4;
+       err = limit_channels_and_rates(dice, substream->runtime, dir,
+                                      substream->pcm->device, size);
        if (err < 0)
-               goto end;
+               return err;
+       limit_period_and_buffer(hw);
 
-       err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
-end:
-       return err;
+       return amdtp_am824_add_pcm_hw_constraints(stream, runtime);
 }
 
 static int pcm_open(struct snd_pcm_substream *substream)
 {
        struct snd_dice *dice = substream->private_data;
-       unsigned int source, rate;
-       bool internal;
        int err;
 
        err = snd_dice_stream_lock_try(dice);
@@ -176,39 +125,6 @@ static int pcm_open(struct snd_pcm_substream *substream)
        if (err < 0)
                goto err_locked;
 
-       err = snd_dice_transaction_get_clock_source(dice, &source);
-       if (err < 0)
-               goto err_locked;
-       switch (source) {
-       case CLOCK_SOURCE_AES1:
-       case CLOCK_SOURCE_AES2:
-       case CLOCK_SOURCE_AES3:
-       case CLOCK_SOURCE_AES4:
-       case CLOCK_SOURCE_AES_ANY:
-       case CLOCK_SOURCE_ADAT:
-       case CLOCK_SOURCE_TDIF:
-       case CLOCK_SOURCE_WC:
-               internal = false;
-               break;
-       default:
-               internal = true;
-               break;
-       }
-
-       /*
-        * When source of clock is not internal or any PCM streams are running,
-        * available sampling rate is limited at current sampling rate.
-        */
-       if (!internal ||
-           amdtp_stream_pcm_running(&dice->tx_stream) ||
-           amdtp_stream_pcm_running(&dice->rx_stream)) {
-               err = snd_dice_transaction_get_rate(dice, &rate);
-               if (err < 0)
-                       goto err_locked;
-               substream->runtime->hw.rate_min = rate;
-               substream->runtime->hw.rate_max = rate;
-       }
-
        snd_pcm_set_sync(substream);
 end:
        return err;
@@ -230,6 +146,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *hw_params)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
        int err;
 
        err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -243,7 +160,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
                mutex_unlock(&dice->mutex);
        }
 
-       amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params));
+       amdtp_am824_set_pcm_format(stream, params_format(hw_params));
 
        return 0;
 }
@@ -251,6 +168,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
                              struct snd_pcm_hw_params *hw_params)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
        int err;
 
        err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
@@ -264,7 +182,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
                mutex_unlock(&dice->mutex);
        }
 
-       amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params));
+       amdtp_am824_set_pcm_format(stream, params_format(hw_params));
 
        return 0;
 }
@@ -304,26 +222,28 @@ static int playback_hw_free(struct snd_pcm_substream *substream)
 static int capture_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
        int err;
 
        mutex_lock(&dice->mutex);
        err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
        mutex_unlock(&dice->mutex);
        if (err >= 0)
-               amdtp_stream_pcm_prepare(&dice->tx_stream);
+               amdtp_stream_pcm_prepare(stream);
 
        return 0;
 }
 static int playback_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
        int err;
 
        mutex_lock(&dice->mutex);
        err = snd_dice_stream_start_duplex(dice, substream->runtime->rate);
        mutex_unlock(&dice->mutex);
        if (err >= 0)
-               amdtp_stream_pcm_prepare(&dice->rx_stream);
+               amdtp_stream_pcm_prepare(stream);
 
        return err;
 }
@@ -331,13 +251,14 @@ static int playback_prepare(struct snd_pcm_substream *substream)
 static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-               amdtp_stream_pcm_trigger(&dice->tx_stream, substream);
+               amdtp_stream_pcm_trigger(stream, substream);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
-               amdtp_stream_pcm_trigger(&dice->tx_stream, NULL);
+               amdtp_stream_pcm_trigger(stream, NULL);
                break;
        default:
                return -EINVAL;
@@ -348,13 +269,14 @@ static int capture_trigger(struct snd_pcm_substream *substream, int cmd)
 static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
-               amdtp_stream_pcm_trigger(&dice->rx_stream, substream);
+               amdtp_stream_pcm_trigger(stream, substream);
                break;
        case SNDRV_PCM_TRIGGER_STOP:
-               amdtp_stream_pcm_trigger(&dice->rx_stream, NULL);
+               amdtp_stream_pcm_trigger(stream, NULL);
                break;
        default:
                return -EINVAL;
@@ -366,14 +288,16 @@ static int playback_trigger(struct snd_pcm_substream *substream, int cmd)
 static snd_pcm_uframes_t capture_pointer(struct snd_pcm_substream *substream)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->tx_stream[substream->pcm->device];
 
-       return amdtp_stream_pcm_pointer(&dice->tx_stream);
+       return amdtp_stream_pcm_pointer(stream);
 }
 static snd_pcm_uframes_t playback_pointer(struct snd_pcm_substream *substream)
 {
        struct snd_dice *dice = substream->private_data;
+       struct amdtp_stream *stream = &dice->rx_stream[substream->pcm->device];
 
-       return amdtp_stream_pcm_pointer(&dice->rx_stream);
+       return amdtp_stream_pcm_pointer(stream);
 }
 
 int snd_dice_create_pcm(struct snd_dice *dice)
@@ -402,29 +326,53 @@ int snd_dice_create_pcm(struct snd_dice *dice)
                .page      = snd_pcm_lib_get_vmalloc_page,
                .mmap      = snd_pcm_lib_mmap_vmalloc,
        };
+       __be32 reg;
        struct snd_pcm *pcm;
-       unsigned int i, capture, playback;
+       unsigned int i, max_capture, max_playback, capture, playback;
        int err;
 
-       capture = playback = 0;
-       for (i = 0; i < 3; i++) {
-               if (dice->tx_channels[i] > 0)
+       /* Check whether PCM substreams are required. */
+       if (dice->force_two_pcms) {
+               max_capture = max_playback = 2;
+       } else {
+               max_capture = max_playback = 0;
+               err = snd_dice_transaction_read_tx(dice, TX_NUMBER, &reg,
+                                                  sizeof(reg));
+               if (err < 0)
+                       return err;
+               max_capture = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
+
+               err = snd_dice_transaction_read_rx(dice, RX_NUMBER, &reg,
+                                                  sizeof(reg));
+               if (err < 0)
+                       return err;
+               max_playback = min_t(unsigned int, be32_to_cpu(reg), MAX_STREAMS);
+       }
+
+       for (i = 0; i < MAX_STREAMS; i++) {
+               capture = playback = 0;
+               if (i < max_capture)
                        capture = 1;
-               if (dice->rx_channels[i] > 0)
+               if (i < max_playback)
                        playback = 1;
-       }
+               if (capture == 0 && playback == 0)
+                       break;
 
-       err = snd_pcm_new(dice->card, "DICE", 0, playback, capture, &pcm);
-       if (err < 0)
-               return err;
-       pcm->private_data = dice;
-       strcpy(pcm->name, dice->card->shortname);
+               err = snd_pcm_new(dice->card, "DICE", i, playback, capture,
+                                 &pcm);
+               if (err < 0)
+                       return err;
+               pcm->private_data = dice;
+               strcpy(pcm->name, dice->card->shortname);
 
-       if (capture > 0)
-               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &capture_ops);
+               if (capture > 0)
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
+                                       &capture_ops);
 
-       if (playback > 0)
-               snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &playback_ops);
+               if (playback > 0)
+                       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
+                                       &playback_ops);
+       }
 
        return 0;
 }
index a6a39f7ef58d8e32cf6e6ff60d45bcbbb23b1e04..845d5e5884a4af0bec211e0aa8aa643cb00df9e0 100644 (file)
 #include "dice.h"
 
 #define        CALLBACK_TIMEOUT        200
+#define NOTIFICATION_TIMEOUT_MS        (2 * MSEC_PER_SEC)
+
+struct reg_params {
+       unsigned int count;
+       unsigned int size;
+};
 
 const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
        /* mode 0 */
@@ -24,96 +30,126 @@ const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT] = {
        [6] = 192000,
 };
 
-int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
-                                 unsigned int *mode)
+/*
+ * This operation has an effect to synchronize GLOBAL_STATUS/GLOBAL_SAMPLE_RATE
+ * to GLOBAL_STATUS. Especially, just after powering on, these are different.
+ */
+static int ensure_phase_lock(struct snd_dice *dice)
 {
-       int i;
+       __be32 reg, nominal;
+       int err;
+
+       err = snd_dice_transaction_read_global(dice, GLOBAL_CLOCK_SELECT,
+                                              &reg, sizeof(reg));
+       if (err < 0)
+               return err;
 
-       for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
-               if (!(dice->clock_caps & BIT(i)))
-                       continue;
-               if (snd_dice_rates[i] != rate)
-                       continue;
+       if (completion_done(&dice->clock_accepted))
+               reinit_completion(&dice->clock_accepted);
 
-               *mode = (i - 1) / 2;
-               return 0;
+       err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
+                                               &reg, sizeof(reg));
+       if (err < 0)
+               return err;
+
+       if (wait_for_completion_timeout(&dice->clock_accepted,
+                       msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0) {
+               /*
+                * Old versions of Dice firmware transfer no notification when
+                * the same clock status as current one is set. In this case,
+                * just check current clock status.
+                */
+               err = snd_dice_transaction_read_global(dice, GLOBAL_STATUS,
+                                               &nominal, sizeof(nominal));
+               if (err < 0)
+                       return err;
+               if (!(be32_to_cpu(nominal) & STATUS_SOURCE_LOCKED))
+                       return -ETIMEDOUT;
        }
-       return -EINVAL;
-}
 
-static void release_resources(struct snd_dice *dice,
-                             struct fw_iso_resources *resources)
-{
-       __be32 channel;
-
-       /* Reset channel number */
-       channel = cpu_to_be32((u32)-1);
-       if (resources == &dice->tx_resources)
-               snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
-                                             &channel, sizeof(channel));
-       else
-               snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
-                                             &channel, sizeof(channel));
-
-       fw_iso_resources_free(resources);
+       return 0;
 }
 
-static int keep_resources(struct snd_dice *dice,
-                         struct fw_iso_resources *resources,
-                         unsigned int max_payload_bytes)
+static int get_register_params(struct snd_dice *dice,
+                              struct reg_params *tx_params,
+                              struct reg_params *rx_params)
 {
-       __be32 channel;
+       __be32 reg[2];
        int err;
 
-       err = fw_iso_resources_allocate(resources, max_payload_bytes,
-                               fw_parent_device(dice->unit)->max_speed);
+       err = snd_dice_transaction_read_tx(dice, TX_NUMBER, reg, sizeof(reg));
        if (err < 0)
-               goto end;
+               return err;
+       tx_params->count =
+                       min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+       tx_params->size = be32_to_cpu(reg[1]) * 4;
 
-       /* Set channel number */
-       channel = cpu_to_be32(resources->channel);
-       if (resources == &dice->tx_resources)
-               err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
-                                                   &channel, sizeof(channel));
-       else
-               err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
-                                                   &channel, sizeof(channel));
+       err = snd_dice_transaction_read_rx(dice, RX_NUMBER, reg, sizeof(reg));
        if (err < 0)
-               release_resources(dice, resources);
-end:
-       return err;
+               return err;
+       rx_params->count =
+                       min_t(unsigned int, be32_to_cpu(reg[0]), MAX_STREAMS);
+       rx_params->size = be32_to_cpu(reg[1]) * 4;
+
+       return 0;
 }
 
-static void stop_stream(struct snd_dice *dice, struct amdtp_stream *stream)
+static void release_resources(struct snd_dice *dice)
 {
-       amdtp_stream_pcm_abort(stream);
-       amdtp_stream_stop(stream);
+       unsigned int i;
 
-       if (stream == &dice->tx_stream)
-               release_resources(dice, &dice->tx_resources);
-       else
-               release_resources(dice, &dice->rx_resources);
+       for (i = 0; i < MAX_STREAMS; i++) {
+               if (amdtp_stream_running(&dice->tx_stream[i])) {
+                       amdtp_stream_pcm_abort(&dice->tx_stream[i]);
+                       amdtp_stream_stop(&dice->tx_stream[i]);
+               }
+               if (amdtp_stream_running(&dice->rx_stream[i])) {
+                       amdtp_stream_pcm_abort(&dice->rx_stream[i]);
+                       amdtp_stream_stop(&dice->rx_stream[i]);
+               }
+
+               fw_iso_resources_free(&dice->tx_resources[i]);
+               fw_iso_resources_free(&dice->rx_resources[i]);
+       }
 }
 
-static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
-                       unsigned int rate)
+static void stop_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
+                        struct reg_params *params)
 {
+       __be32 reg;
+       unsigned int i;
+
+       for (i = 0; i < params->count; i++) {
+               reg = cpu_to_be32((u32)-1);
+               if (dir == AMDTP_IN_STREAM) {
+                       snd_dice_transaction_write_tx(dice,
+                                       params->size * i + TX_ISOCHRONOUS,
+                                       &reg, sizeof(reg));
+               } else {
+                       snd_dice_transaction_write_rx(dice,
+                                       params->size * i + RX_ISOCHRONOUS,
+                                       &reg, sizeof(reg));
+               }
+       }
+}
+
+static int keep_resources(struct snd_dice *dice,
+                         enum amdtp_stream_direction dir, unsigned int index,
+                         unsigned int rate, unsigned int pcm_chs,
+                         unsigned int midi_ports)
+{
+       struct amdtp_stream *stream;
        struct fw_iso_resources *resources;
-       unsigned int i, mode, pcm_chs, midi_ports;
        bool double_pcm_frames;
+       unsigned int i;
        int err;
 
-       err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
-       if (err < 0)
-               goto end;
-       if (stream == &dice->tx_stream) {
-               resources = &dice->tx_resources;
-               pcm_chs = dice->tx_channels[mode];
-               midi_ports = dice->tx_midi_ports[mode];
+       if (dir == AMDTP_IN_STREAM) {
+               stream = &dice->tx_stream[index];
+               resources = &dice->tx_resources[index];
        } else {
-               resources = &dice->rx_resources;
-               pcm_chs = dice->rx_channels[mode];
-               midi_ports = dice->rx_midi_ports[mode];
+               stream = &dice->rx_stream[index];
+               resources = &dice->rx_resources[index];
        }
 
        /*
@@ -126,7 +162,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
         * For this quirk, blocking mode is required and PCM buffer size should
         * be aligned to SYT_INTERVAL.
         */
-       double_pcm_frames = mode > 1;
+       double_pcm_frames = rate > 96000;
        if (double_pcm_frames) {
                rate /= 2;
                pcm_chs *= 2;
@@ -135,7 +171,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
        err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
                                         double_pcm_frames);
        if (err < 0)
-               goto end;
+               return err;
 
        if (double_pcm_frames) {
                pcm_chs /= 2;
@@ -147,158 +183,201 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
                }
        }
 
-       err = keep_resources(dice, resources,
-                            amdtp_stream_get_max_payload(stream));
-       if (err < 0) {
-               dev_err(&dice->unit->device,
-                       "fail to keep isochronous resources\n");
-               goto end;
-       }
-
-       err = amdtp_stream_start(stream, resources->channel,
-                                fw_parent_device(dice->unit)->max_speed);
-       if (err < 0)
-               release_resources(dice, resources);
-end:
-       return err;
+       return fw_iso_resources_allocate(resources,
+                               amdtp_stream_get_max_payload(stream),
+                               fw_parent_device(dice->unit)->max_speed);
 }
 
-static int get_sync_mode(struct snd_dice *dice, enum cip_flags *sync_mode)
+static int start_streams(struct snd_dice *dice, enum amdtp_stream_direction dir,
+                        unsigned int rate, struct reg_params *params)
 {
-       u32 source;
-       int err;
+       __be32 reg[2];
+       unsigned int i, pcm_chs, midi_ports;
+       struct amdtp_stream *streams;
+       struct fw_iso_resources *resources;
+       int err = 0;
 
-       err = snd_dice_transaction_get_clock_source(dice, &source);
-       if (err < 0)
-               goto end;
+       if (dir == AMDTP_IN_STREAM) {
+               streams = dice->tx_stream;
+               resources = dice->tx_resources;
+       } else {
+               streams = dice->rx_stream;
+               resources = dice->rx_resources;
+       }
+
+       for (i = 0; i < params->count; i++) {
+               if (dir == AMDTP_IN_STREAM) {
+                       err = snd_dice_transaction_read_tx(dice,
+                                       params->size * i + TX_NUMBER_AUDIO,
+                                       reg, sizeof(reg));
+               } else {
+                       err = snd_dice_transaction_read_rx(dice,
+                                       params->size * i + RX_NUMBER_AUDIO,
+                                       reg, sizeof(reg));
+               }
+               if (err < 0)
+                       return err;
+               pcm_chs = be32_to_cpu(reg[0]);
+               midi_ports = be32_to_cpu(reg[1]);
+
+               err = keep_resources(dice, dir, i, rate, pcm_chs, midi_ports);
+               if (err < 0)
+                       return err;
+
+               reg[0] = cpu_to_be32(resources[i].channel);
+               if (dir == AMDTP_IN_STREAM) {
+                       err = snd_dice_transaction_write_tx(dice,
+                                       params->size * i + TX_ISOCHRONOUS,
+                                       reg, sizeof(reg[0]));
+               } else {
+                       err = snd_dice_transaction_write_rx(dice,
+                                       params->size * i + RX_ISOCHRONOUS,
+                                       reg, sizeof(reg[0]));
+               }
+               if (err < 0)
+                       return err;
 
-       switch (source) {
-       /* So-called 'SYT Match' modes, sync_to_syt value of packets received */
-       case CLOCK_SOURCE_ARX4: /* in 4th stream */
-       case CLOCK_SOURCE_ARX3: /* in 3rd stream */
-       case CLOCK_SOURCE_ARX2: /* in 2nd stream */
-               err = -ENOSYS;
-               break;
-       case CLOCK_SOURCE_ARX1: /* in 1st stream, which this driver uses */
-               *sync_mode = 0;
-               break;
-       default:
-               *sync_mode = CIP_SYNC_TO_DEVICE;
-               break;
+               err = amdtp_stream_start(&streams[i], resources[i].channel,
+                               fw_parent_device(dice->unit)->max_speed);
+               if (err < 0)
+                       return err;
        }
-end:
+
        return err;
 }
 
+/*
+ * MEMO: After this function, there're two states of streams:
+ *  - None streams are running.
+ *  - All streams are running.
+ */
 int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate)
 {
-       struct amdtp_stream *master, *slave;
        unsigned int curr_rate;
-       enum cip_flags sync_mode;
-       int err = 0;
+       unsigned int i;
+       struct reg_params tx_params, rx_params;
+       bool need_to_start;
+       int err;
 
        if (dice->substreams_counter == 0)
-               goto end;
+               return -EIO;
 
-       err = get_sync_mode(dice, &sync_mode);
+       err = get_register_params(dice, &tx_params, &rx_params);
        if (err < 0)
-               goto end;
-       if (sync_mode == CIP_SYNC_TO_DEVICE) {
-               master = &dice->tx_stream;
-               slave  = &dice->rx_stream;
-       } else {
-               master = &dice->rx_stream;
-               slave  = &dice->tx_stream;
-       }
-
-       /* Some packet queueing errors. */
-       if (amdtp_streaming_error(master) || amdtp_streaming_error(slave))
-               stop_stream(dice, master);
+               return err;
 
-       /* Stop stream if rate is different. */
        err = snd_dice_transaction_get_rate(dice, &curr_rate);
        if (err < 0) {
                dev_err(&dice->unit->device,
                        "fail to get sampling rate\n");
-               goto end;
+               return err;
        }
        if (rate == 0)
                rate = curr_rate;
        if (rate != curr_rate)
-               stop_stream(dice, master);
+               return -EINVAL;
+
+       /* Judge to need to restart streams. */
+       for (i = 0; i < MAX_STREAMS; i++) {
+               if (i < tx_params.count) {
+                       if (amdtp_streaming_error(&dice->tx_stream[i]) ||
+                           !amdtp_stream_running(&dice->tx_stream[i]))
+                               break;
+               }
+               if (i < rx_params.count) {
+                       if (amdtp_streaming_error(&dice->rx_stream[i]) ||
+                           !amdtp_stream_running(&dice->rx_stream[i]))
+                               break;
+               }
+       }
+       need_to_start = (i < MAX_STREAMS);
 
-       if (!amdtp_stream_running(master)) {
-               stop_stream(dice, slave);
+       if (need_to_start) {
+               /* Stop transmission. */
                snd_dice_transaction_clear_enable(dice);
+               stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+               stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+               release_resources(dice);
 
-               amdtp_stream_set_sync(sync_mode, master, slave);
-
-               err = snd_dice_transaction_set_rate(dice, rate);
+               err = ensure_phase_lock(dice);
                if (err < 0) {
                        dev_err(&dice->unit->device,
-                               "fail to set sampling rate\n");
-                       goto end;
+                               "fail to ensure phase lock\n");
+                       return err;
                }
 
                /* Start both streams. */
-               err = start_stream(dice, master, rate);
-               if (err < 0) {
-                       dev_err(&dice->unit->device,
-                               "fail to start AMDTP master stream\n");
-                       goto end;
-               }
-               err = start_stream(dice, slave, rate);
-               if (err < 0) {
-                       dev_err(&dice->unit->device,
-                               "fail to start AMDTP slave stream\n");
-                       stop_stream(dice, master);
-                       goto end;
-               }
+               err = start_streams(dice, AMDTP_IN_STREAM, rate, &tx_params);
+               if (err < 0)
+                       goto error;
+               err = start_streams(dice, AMDTP_OUT_STREAM, rate, &rx_params);
+               if (err < 0)
+                       goto error;
+
                err = snd_dice_transaction_set_enable(dice);
                if (err < 0) {
                        dev_err(&dice->unit->device,
                                "fail to enable interface\n");
-                       stop_stream(dice, master);
-                       stop_stream(dice, slave);
-                       goto end;
+                       goto error;
                }
 
-               /* Wait first callbacks */
-               if (!amdtp_stream_wait_callback(master, CALLBACK_TIMEOUT) ||
-                   !amdtp_stream_wait_callback(slave, CALLBACK_TIMEOUT)) {
-                       snd_dice_transaction_clear_enable(dice);
-                       stop_stream(dice, master);
-                       stop_stream(dice, slave);
-                       err = -ETIMEDOUT;
+               for (i = 0; i < MAX_STREAMS; i++) {
+                       if ((i < tx_params.count &&
+                           !amdtp_stream_wait_callback(&dice->tx_stream[i],
+                                                       CALLBACK_TIMEOUT)) ||
+                           (i < rx_params.count &&
+                            !amdtp_stream_wait_callback(&dice->rx_stream[i],
+                                                        CALLBACK_TIMEOUT))) {
+                               err = -ETIMEDOUT;
+                               goto error;
+                       }
                }
        }
-end:
+
+       return err;
+error:
+       snd_dice_transaction_clear_enable(dice);
+       stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+       stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+       release_resources(dice);
        return err;
 }
 
+/*
+ * MEMO: After this function, there're two states of streams:
+ *  - None streams are running.
+ *  - All streams are running.
+ */
 void snd_dice_stream_stop_duplex(struct snd_dice *dice)
 {
+       struct reg_params tx_params, rx_params;
+
        if (dice->substreams_counter > 0)
                return;
 
        snd_dice_transaction_clear_enable(dice);
 
-       stop_stream(dice, &dice->tx_stream);
-       stop_stream(dice, &dice->rx_stream);
+       if (get_register_params(dice, &tx_params, &rx_params) == 0) {
+               stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+               stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+       }
+
+       release_resources(dice);
 }
 
-static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream)
+static int init_stream(struct snd_dice *dice, enum amdtp_stream_direction dir,
+                      unsigned int index)
 {
-       int err;
+       struct amdtp_stream *stream;
        struct fw_iso_resources *resources;
-       enum amdtp_stream_direction dir;
+       int err;
 
-       if (stream == &dice->tx_stream) {
-               resources = &dice->tx_resources;
-               dir = AMDTP_IN_STREAM;
+       if (dir == AMDTP_IN_STREAM) {
+               stream = &dice->tx_stream[index];
+               resources = &dice->tx_resources[index];
        } else {
-               resources = &dice->rx_resources;
-               dir = AMDTP_OUT_STREAM;
+               stream = &dice->rx_stream[index];
+               resources = &dice->rx_resources[index];
        }
 
        err = fw_iso_resources_init(resources, dice->unit);
@@ -319,14 +398,20 @@ static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream)
  * This function should be called before starting streams or after stopping
  * streams.
  */
-static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream)
+static void destroy_stream(struct snd_dice *dice,
+                          enum amdtp_stream_direction dir,
+                          unsigned int index)
 {
+       struct amdtp_stream *stream;
        struct fw_iso_resources *resources;
 
-       if (stream == &dice->tx_stream)
-               resources = &dice->tx_resources;
-       else
-               resources = &dice->rx_resources;
+       if (dir == AMDTP_IN_STREAM) {
+               stream = &dice->tx_stream[index];
+               resources = &dice->tx_resources[index];
+       } else {
+               stream = &dice->rx_stream[index];
+               resources = &dice->rx_resources[index];
+       }
 
        amdtp_stream_destroy(stream);
        fw_iso_resources_destroy(resources);
@@ -334,33 +419,51 @@ static void destroy_stream(struct snd_dice *dice, struct amdtp_stream *stream)
 
 int snd_dice_stream_init_duplex(struct snd_dice *dice)
 {
-       int err;
-
-       dice->substreams_counter = 0;
+       int i, err;
 
-       err = init_stream(dice, &dice->tx_stream);
-       if (err < 0)
-               goto end;
+       for (i = 0; i < MAX_STREAMS; i++) {
+               err = init_stream(dice, AMDTP_IN_STREAM, i);
+               if (err < 0) {
+                       for (; i >= 0; i--)
+                               destroy_stream(dice, AMDTP_OUT_STREAM, i);
+                       goto end;
+               }
+       }
 
-       err = init_stream(dice, &dice->rx_stream);
-       if (err < 0)
-               destroy_stream(dice, &dice->tx_stream);
+       for (i = 0; i < MAX_STREAMS; i++) {
+               err = init_stream(dice, AMDTP_OUT_STREAM, i);
+               if (err < 0) {
+                       for (; i >= 0; i--)
+                               destroy_stream(dice, AMDTP_OUT_STREAM, i);
+                       for (i = 0; i < MAX_STREAMS; i++)
+                               destroy_stream(dice, AMDTP_IN_STREAM, i);
+                       break;
+               }
+       }
 end:
        return err;
 }
 
 void snd_dice_stream_destroy_duplex(struct snd_dice *dice)
 {
+       struct reg_params tx_params, rx_params;
+
        snd_dice_transaction_clear_enable(dice);
 
-       destroy_stream(dice, &dice->tx_stream);
-       destroy_stream(dice, &dice->rx_stream);
+       if (get_register_params(dice, &tx_params, &rx_params) == 0) {
+               stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+               stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+       }
+
+       release_resources(dice);
 
        dice->substreams_counter = 0;
 }
 
 void snd_dice_stream_update_duplex(struct snd_dice *dice)
 {
+       struct reg_params tx_params, rx_params;
+
        /*
         * On a bus reset, the DICE firmware disables streaming and then goes
         * off contemplating its own navel for hundreds of milliseconds before
@@ -371,11 +474,10 @@ void snd_dice_stream_update_duplex(struct snd_dice *dice)
         */
        dice->global_enabled = false;
 
-       stop_stream(dice, &dice->rx_stream);
-       stop_stream(dice, &dice->tx_stream);
-
-       fw_iso_resources_update(&dice->rx_resources);
-       fw_iso_resources_update(&dice->tx_resources);
+       if (get_register_params(dice, &tx_params, &rx_params) == 0) {
+               stop_streams(dice, AMDTP_IN_STREAM, &tx_params);
+               stop_streams(dice, AMDTP_OUT_STREAM, &rx_params);
+       }
 }
 
 static void dice_lock_changed(struct snd_dice *dice)
index a4ff4e0bc0af1ad143471624df0c00a89eca27c4..0f0350320ae8ceecfc1e8654a2799565ea8be914 100644 (file)
@@ -9,8 +9,6 @@
 
 #include "dice.h"
 
-#define NOTIFICATION_TIMEOUT_MS        (2 * MSEC_PER_SEC)
-
 static u64 get_subaddr(struct snd_dice *dice, enum snd_dice_addr_type type,
                       u64 offset)
 {
@@ -62,54 +60,6 @@ static unsigned int get_clock_info(struct snd_dice *dice, __be32 *info)
                                                info, 4);
 }
 
-static int set_clock_info(struct snd_dice *dice,
-                         unsigned int rate, unsigned int source)
-{
-       unsigned int i;
-       __be32 info;
-       u32 mask;
-       u32 clock;
-       int err;
-
-       err = get_clock_info(dice, &info);
-       if (err < 0)
-               return err;
-
-       clock = be32_to_cpu(info);
-       if (source != UINT_MAX) {
-               mask = CLOCK_SOURCE_MASK;
-               clock &= ~mask;
-               clock |= source;
-       }
-       if (rate != UINT_MAX) {
-               for (i = 0; i < ARRAY_SIZE(snd_dice_rates); i++) {
-                       if (snd_dice_rates[i] == rate)
-                               break;
-               }
-               if (i == ARRAY_SIZE(snd_dice_rates))
-                       return -EINVAL;
-
-               mask = CLOCK_RATE_MASK;
-               clock &= ~mask;
-               clock |= i << CLOCK_RATE_SHIFT;
-       }
-       info = cpu_to_be32(clock);
-
-       if (completion_done(&dice->clock_accepted))
-               reinit_completion(&dice->clock_accepted);
-
-       err = snd_dice_transaction_write_global(dice, GLOBAL_CLOCK_SELECT,
-                                               &info, 4);
-       if (err < 0)
-               return err;
-
-       if (wait_for_completion_timeout(&dice->clock_accepted,
-                       msecs_to_jiffies(NOTIFICATION_TIMEOUT_MS)) == 0)
-               return -ETIMEDOUT;
-
-       return 0;
-}
-
 int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
                                          unsigned int *source)
 {
@@ -143,10 +93,6 @@ int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate)
 end:
        return err;
 }
-int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate)
-{
-       return set_clock_info(dice, rate, UINT_MAX);
-}
 
 int snd_dice_transaction_set_enable(struct snd_dice *dice)
 {
@@ -210,7 +156,7 @@ static void dice_notification(struct fw_card *card, struct fw_request *request,
 
        fw_send_response(card, request, RCODE_COMPLETE);
 
-       if (bits & NOTIFY_CLOCK_ACCEPTED)
+       if (bits & NOTIFY_LOCK_CHG)
                complete(&dice->clock_accepted);
        wake_up(&dice->hwdep_wait);
 }
index b91b3739c8106f551472d84ae29ef38ee09dd6bb..8b64aef31a864523d428e6fa26b8c6a5f45db154 100644 (file)
@@ -13,6 +13,8 @@ MODULE_LICENSE("GPL v2");
 
 #define OUI_WEISS              0x001c6a
 #define OUI_LOUD               0x000ff2
+#define OUI_FOCUSRITE          0x00130e
+#define OUI_TCELECTRONIC       0x001486
 
 #define DICE_CATEGORY_ID       0x04
 #define WEISS_CATEGORY_ID      0x00
@@ -20,6 +22,36 @@ MODULE_LICENSE("GPL v2");
 
 #define PROBE_DELAY_MS         (2 * MSEC_PER_SEC)
 
+/*
+ * Some models support several isochronous channels, while these streams are not
+ * always available. In this case, add the model name to this list.
+ */
+static bool force_two_pcm_support(struct fw_unit *unit)
+{
+       const char *const models[] = {
+               /* TC Electronic models. */
+               "StudioKonnekt48",
+               /* Focusrite models. */
+               "SAFFIRE_PRO_40",
+               "LIQUID_SAFFIRE_56",
+               "SAFFIRE_PRO_40_1",
+       };
+       char model[32];
+       unsigned int i;
+       int err;
+
+       err = fw_csr_string(unit->directory, CSR_MODEL, model, sizeof(model));
+       if (err < 0)
+               return false;
+
+       for (i = 0; i < ARRAY_SIZE(models); i++) {
+               if (strcmp(models[i], model) == 0)
+                       break;
+       }
+
+       return i < ARRAY_SIZE(models);
+}
+
 static int check_dice_category(struct fw_unit *unit)
 {
        struct fw_device *device = fw_parent_device(unit);
@@ -44,6 +76,12 @@ static int check_dice_category(struct fw_unit *unit)
                        break;
                }
        }
+
+       if (vendor == OUI_FOCUSRITE || vendor == OUI_TCELECTRONIC) {
+               if (force_two_pcm_support(unit))
+                       return 0;
+       }
+
        if (vendor == OUI_WEISS)
                category = WEISS_CATEGORY_ID;
        else if (vendor == OUI_LOUD)
@@ -57,65 +95,10 @@ static int check_dice_category(struct fw_unit *unit)
        return 0;
 }
 
-static int highest_supported_mode_rate(struct snd_dice *dice,
-                                      unsigned int mode, unsigned int *rate)
-{
-       unsigned int i, m;
-
-       for (i = ARRAY_SIZE(snd_dice_rates); i > 0; i--) {
-               *rate = snd_dice_rates[i - 1];
-               if (snd_dice_stream_get_rate_mode(dice, *rate, &m) < 0)
-                       continue;
-               if (mode == m)
-                       break;
-       }
-       if (i == 0)
-               return -EINVAL;
-
-       return 0;
-}
-
-static int dice_read_mode_params(struct snd_dice *dice, unsigned int mode)
-{
-       __be32 values[2];
-       unsigned int rate;
-       int err;
-
-       if (highest_supported_mode_rate(dice, mode, &rate) < 0) {
-               dice->tx_channels[mode] = 0;
-               dice->tx_midi_ports[mode] = 0;
-               dice->rx_channels[mode] = 0;
-               dice->rx_midi_ports[mode] = 0;
-               return 0;
-       }
-
-       err = snd_dice_transaction_set_rate(dice, rate);
-       if (err < 0)
-               return err;
-
-       err = snd_dice_transaction_read_tx(dice, TX_NUMBER_AUDIO,
-                                          values, sizeof(values));
-       if (err < 0)
-               return err;
-
-       dice->tx_channels[mode]   = be32_to_cpu(values[0]);
-       dice->tx_midi_ports[mode] = be32_to_cpu(values[1]);
-
-       err = snd_dice_transaction_read_rx(dice, RX_NUMBER_AUDIO,
-                                          values, sizeof(values));
-       if (err < 0)
-               return err;
-
-       dice->rx_channels[mode]   = be32_to_cpu(values[0]);
-       dice->rx_midi_ports[mode] = be32_to_cpu(values[1]);
-
-       return 0;
-}
-
-static int dice_read_params(struct snd_dice *dice)
+static int check_clock_caps(struct snd_dice *dice)
 {
        __be32 value;
-       int mode, err;
+       int err;
 
        /* some very old firmwares don't tell about their clock support */
        if (dice->clock_caps > 0) {
@@ -133,12 +116,6 @@ static int dice_read_params(struct snd_dice *dice)
                                   CLOCK_CAP_SOURCE_INTERNAL;
        }
 
-       for (mode = 2; mode >= 0; --mode) {
-               err = dice_read_mode_params(dice, mode);
-               if (err < 0)
-                       return err;
-       }
-
        return 0;
 }
 
@@ -211,11 +188,14 @@ static void do_registration(struct work_struct *work)
        if (err < 0)
                return;
 
+       if (force_two_pcm_support(dice->unit))
+               dice->force_two_pcms = true;
+
        err = snd_dice_transaction_init(dice);
        if (err < 0)
                goto error;
 
-       err = dice_read_params(dice);
+       err = check_clock_caps(dice);
        if (err < 0)
                goto error;
 
index 3d5ebebe61ea2238cfbb410a403cc6da5452dc01..e6c07857f4755fb1963d09ae2091f66f5ffef0f3 100644 (file)
 #include "../lib.h"
 #include "dice-interface.h"
 
+/*
+ * This module support maximum 2 pairs of tx/rx isochronous streams for
+ * our convinience.
+ *
+ * In documents for ASICs called with a name of 'DICE':
+ *  - ASIC for DICE II:
+ *   - Maximum 2 tx and 4 rx are supported.
+ *   - A packet supports maximum 16 data channels.
+ *  - TCD2210/2210-E (so-called 'Dice Mini'):
+ *   - Maximum 2 tx and 2 rx are supported.
+ *   - A packet supports maximum 16 data channels.
+ *  - TCD2220/2220-E (so-called 'Dice Jr.')
+ *   - 2 tx and 2 rx are supported.
+ *   - A packet supports maximum 16 data channels.
+ *  - TCD3070-CH (so-called 'Dice III')
+ *   - Maximum 2 tx and 2 rx are supported.
+ *   - A packet supports maximum 32 data channels.
+ *
+ * For the above, MIDI conformant data channel is just on the first isochronous
+ * stream.
+ */
+#define MAX_STREAMS    2
+
 struct snd_dice {
        struct snd_card *card;
        struct fw_unit *unit;
@@ -56,10 +79,6 @@ struct snd_dice {
        unsigned int rsrv_offset;
 
        unsigned int clock_caps;
-       unsigned int tx_channels[3];
-       unsigned int rx_channels[3];
-       unsigned int tx_midi_ports[3];
-       unsigned int rx_midi_ports[3];
 
        struct fw_address_handler notification_handler;
        int owner_generation;
@@ -71,13 +90,15 @@ struct snd_dice {
        wait_queue_head_t hwdep_wait;
 
        /* For streaming */
-       struct fw_iso_resources tx_resources;
-       struct fw_iso_resources rx_resources;
-       struct amdtp_stream tx_stream;
-       struct amdtp_stream rx_stream;
+       struct fw_iso_resources tx_resources[MAX_STREAMS];
+       struct fw_iso_resources rx_resources[MAX_STREAMS];
+       struct amdtp_stream tx_stream[MAX_STREAMS];
+       struct amdtp_stream rx_stream[MAX_STREAMS];
        bool global_enabled;
        struct completion clock_accepted;
        unsigned int substreams_counter;
+
+       bool force_two_pcms;
 };
 
 enum snd_dice_addr_type {
@@ -158,7 +179,6 @@ static inline int snd_dice_transaction_read_sync(struct snd_dice *dice,
 
 int snd_dice_transaction_get_clock_source(struct snd_dice *dice,
                                          unsigned int *source);
-int snd_dice_transaction_set_rate(struct snd_dice *dice, unsigned int rate);
 int snd_dice_transaction_get_rate(struct snd_dice *dice, unsigned int *rate);
 int snd_dice_transaction_set_enable(struct snd_dice *dice);
 void snd_dice_transaction_clear_enable(struct snd_dice *dice);
@@ -169,9 +189,6 @@ void snd_dice_transaction_destroy(struct snd_dice *dice);
 #define SND_DICE_RATES_COUNT   7
 extern const unsigned int snd_dice_rates[SND_DICE_RATES_COUNT];
 
-int snd_dice_stream_get_rate_mode(struct snd_dice *dice,
-                                 unsigned int rate, unsigned int *mode);
-
 int snd_dice_stream_start_duplex(struct snd_dice *dice, unsigned int rate);
 void snd_dice_stream_stop_duplex(struct snd_dice *dice);
 int snd_dice_stream_init_duplex(struct snd_dice *dice);
index d5b19bc11e5999480f030e682fa4087cc91067ac..8f27b67503c880bcf5d1c1e4a9e84ba2ad4bd006 100644 (file)
@@ -301,7 +301,10 @@ static void efw_update(struct fw_unit *unit)
        struct snd_efw *efw = dev_get_drvdata(&unit->device);
 
        snd_efw_transaction_bus_reset(efw->unit);
+
+       mutex_lock(&efw->mutex);
        snd_efw_stream_update_duplex(efw);
+       mutex_unlock(&efw->mutex);
 }
 
 static void efw_remove(struct fw_unit *unit)
index 968a40a1beb2cba3a48526cd776af08fa689e269..425db8d88235ad755b054f3701fc9b0c14072709 100644 (file)
@@ -313,12 +313,10 @@ void snd_efw_stream_stop_duplex(struct snd_efw *efw)
 
 void snd_efw_stream_update_duplex(struct snd_efw *efw)
 {
-       if ((cmp_connection_update(&efw->out_conn) < 0) ||
-           (cmp_connection_update(&efw->in_conn) < 0)) {
-               mutex_lock(&efw->mutex);
+       if (cmp_connection_update(&efw->out_conn) < 0 ||
+           cmp_connection_update(&efw->in_conn) < 0) {
                stop_stream(efw, &efw->rx_stream);
                stop_stream(efw, &efw->tx_stream);
-               mutex_unlock(&efw->mutex);
        } else {
                amdtp_stream_update(&efw->rx_stream);
                amdtp_stream_update(&efw->tx_stream);
index bb53eb35721b50daba0d1ac5effeefef66a9f370..f897c9831077c5a49289c385b4aeed263a2aab2c 100644 (file)
@@ -26,11 +26,13 @@ struct fw_scs1x {
        u8 output_bytes;
        bool output_escaped;
        bool output_escape_high_nibble;
-       struct tasklet_struct tasklet;
+       struct work_struct work;
        wait_queue_head_t idle_wait;
        u8 buffer[HSS1394_MAX_PACKET_SIZE];
        bool transaction_running;
        struct fw_transaction transaction;
+       unsigned int transaction_bytes;
+       bool error;
        struct fw_device *fw_dev;
 };
 
@@ -125,11 +127,16 @@ static void scs_write_callback(struct fw_card *card, int rcode,
 {
        struct fw_scs1x *scs = callback_data;
 
-       if (rcode == RCODE_GENERATION)
-               ;       /* TODO: retry this packet */
+       if (!rcode_is_permanent_error(rcode)) {
+               /* Don't retry for this data. */
+               if (rcode == RCODE_COMPLETE)
+                       scs->transaction_bytes = 0;
+       } else {
+               scs->error = true;
+       }
 
        scs->transaction_running = false;
-       tasklet_schedule(&scs->tasklet);
+       schedule_work(&scs->work);
 }
 
 static bool is_valid_running_status(u8 status)
@@ -165,9 +172,9 @@ static bool is_invalid_cmd(u8 status)
               status == 0xfd;
 }
 
-static void scs_output_tasklet(unsigned long data)
+static void scs_output_work(struct work_struct *work)
 {
-       struct fw_scs1x *scs = (struct fw_scs1x *)data;
+       struct fw_scs1x *scs = container_of(work, struct fw_scs1x, work);
        struct snd_rawmidi_substream *stream;
        unsigned int i;
        u8 byte;
@@ -177,12 +184,15 @@ static void scs_output_tasklet(unsigned long data)
                return;
 
        stream = ACCESS_ONCE(scs->output);
-       if (!stream) {
+       if (!stream || scs->error) {
                scs->output_idle = true;
                wake_up(&scs->idle_wait);
                return;
        }
 
+       if (scs->transaction_bytes > 0)
+               goto retry;
+
        i = scs->output_bytes;
        for (;;) {
                if (snd_rawmidi_transmit(stream, &byte, 1) != 1) {
@@ -253,13 +263,16 @@ static void scs_output_tasklet(unsigned long data)
        scs->output_bytes = 1;
        scs->output_escaped = false;
 
+       scs->transaction_bytes = i;
+retry:
        scs->transaction_running = true;
        generation = scs->fw_dev->generation;
        smp_rmb(); /* node_id vs. generation */
        fw_send_request(scs->fw_dev->card, &scs->transaction,
                        TCODE_WRITE_BLOCK_REQUEST, scs->fw_dev->node_id,
                        generation, scs->fw_dev->max_speed, HSS1394_ADDRESS,
-                       scs->buffer, i, scs_write_callback, scs);
+                       scs->buffer, scs->transaction_bytes,
+                       scs_write_callback, scs);
 }
 
 static int midi_capture_open(struct snd_rawmidi_substream *stream)
@@ -309,9 +322,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *stream, int up)
                scs->output_bytes = 1;
                scs->output_escaped = false;
                scs->output_idle = false;
+               scs->transaction_bytes = 0;
+               scs->error = false;
 
                ACCESS_ONCE(scs->output) = stream;
-               tasklet_schedule(&scs->tasklet);
+               schedule_work(&scs->work);
        } else {
                ACCESS_ONCE(scs->output) = NULL;
        }
@@ -395,7 +410,7 @@ int snd_oxfw_scs1x_add(struct snd_oxfw *oxfw)
        snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
                            &midi_playback_ops);
 
-       tasklet_init(&scs->tasklet, scs_output_tasklet, (unsigned long)scs);
+       INIT_WORK(&scs->work, scs_output_work);
        init_waitqueue_head(&scs->idle_wait);
        scs->output_idle = true;
 
index 7e999c995cdc59fff11d8210dc5fd8dc4b9e464c..3b9bedee2fa45051641cd2e97d2163309e2e4e44 100644 (file)
@@ -1,5 +1,5 @@
 snd-hda-core-objs := hda_bus_type.o hdac_bus.o hdac_device.o hdac_sysfs.o \
-       hdac_regmap.o hdac_controller.o hdac_stream.o array.o
+       hdac_regmap.o hdac_controller.o hdac_stream.o array.o hdmi_chmap.o
 
 snd-hda-core-objs += trace.o
 CFLAGS_trace.o := -I$(src)
index e361024eabb6371bef25a888bcd8ea58a5499913..d1a4d697333077126f99f4951c00f647d06aaf5e 100644 (file)
@@ -611,6 +611,22 @@ int snd_hdac_power_up_pm(struct hdac_device *codec)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_power_up_pm);
 
+/* like snd_hdac_power_up_pm(), but only increment the pm count when
+ * already powered up.  Returns -1 if not powered up, 1 if incremented
+ * or 0 if unchanged.  Only used in hdac_regmap.c
+ */
+int snd_hdac_keep_power_up(struct hdac_device *codec)
+{
+       if (!atomic_inc_not_zero(&codec->in_pm)) {
+               int ret = pm_runtime_get_if_in_use(&codec->dev);
+               if (!ret)
+                       return -1;
+               if (ret < 0)
+                       return 0;
+       }
+       return 1;
+}
+
 /**
  * snd_hdac_power_down_pm - power down the codec
  * @codec: the codec object
index f6854dbd7d8d0eed41b5158c25b20e03211ccac5..fb96aead825707e7bca496bf9fcd1af389873dbc 100644 (file)
@@ -126,6 +126,8 @@ EXPORT_SYMBOL_GPL(snd_hdac_get_display_clk);
  */
 static int pin2port(hda_nid_t pin_nid)
 {
+       if (WARN_ON(pin_nid < 5 || pin_nid > 7))
+               return -1;
        return pin_nid - 4;
 }
 
@@ -144,10 +146,14 @@ static int pin2port(hda_nid_t pin_nid)
 int snd_hdac_sync_audio_rate(struct hdac_bus *bus, hda_nid_t nid, int rate)
 {
        struct i915_audio_component *acomp = bus->audio_component;
+       int port;
 
        if (!acomp || !acomp->ops || !acomp->ops->sync_audio_rate)
                return -ENODEV;
-       return acomp->ops->sync_audio_rate(acomp->dev, pin2port(nid), rate);
+       port = pin2port(nid);
+       if (port < 0)
+               return -EINVAL;
+       return acomp->ops->sync_audio_rate(acomp->dev, port, rate);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_sync_audio_rate);
 
@@ -175,11 +181,15 @@ int snd_hdac_acomp_get_eld(struct hdac_bus *bus, hda_nid_t nid,
                           bool *audio_enabled, char *buffer, int max_bytes)
 {
        struct i915_audio_component *acomp = bus->audio_component;
+       int port;
 
        if (!acomp || !acomp->ops || !acomp->ops->get_eld)
                return -ENODEV;
 
-       return acomp->ops->get_eld(acomp->dev, pin2port(nid), audio_enabled,
+       port = pin2port(nid);
+       if (port < 0)
+               return -EINVAL;
+       return acomp->ops->get_eld(acomp->dev, port, audio_enabled,
                                   buffer, max_bytes);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_acomp_get_eld);
index eb8f7c30cb0941275ebd495ee41467a0171e3562..bdbcd6b75ff61cb0495e36072eaefa2ec2478dc2 100644 (file)
 #include <sound/hdaudio.h>
 #include <sound/hda_regmap.h>
 
-#ifdef CONFIG_PM
-#define codec_is_running(codec)                                \
-       (atomic_read(&(codec)->in_pm) ||                \
-        !pm_runtime_suspended(&(codec)->dev))
-#else
-#define codec_is_running(codec)                true
-#endif
+static int codec_pm_lock(struct hdac_device *codec)
+{
+       return snd_hdac_keep_power_up(codec);
+}
+
+static void codec_pm_unlock(struct hdac_device *codec, int lock)
+{
+       if (lock == 1)
+               snd_hdac_power_down_pm(codec);
+}
 
 #define get_verb(reg)  (((reg) >> 8) & 0xfff)
 
@@ -238,20 +241,28 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
        struct hdac_device *codec = context;
        int verb = get_verb(reg);
        int err;
+       int pm_lock = 0;
 
-       if (!codec_is_running(codec) && verb != AC_VERB_GET_POWER_STATE)
-               return -EAGAIN;
+       if (verb != AC_VERB_GET_POWER_STATE) {
+               pm_lock = codec_pm_lock(codec);
+               if (pm_lock < 0)
+                       return -EAGAIN;
+       }
        reg |= (codec->addr << 28);
-       if (is_stereo_amp_verb(reg))
-               return hda_reg_read_stereo_amp(codec, reg, val);
-       if (verb == AC_VERB_GET_PROC_COEF)
-               return hda_reg_read_coef(codec, reg, val);
+       if (is_stereo_amp_verb(reg)) {
+               err = hda_reg_read_stereo_amp(codec, reg, val);
+               goto out;
+       }
+       if (verb == AC_VERB_GET_PROC_COEF) {
+               err = hda_reg_read_coef(codec, reg, val);
+               goto out;
+       }
        if ((verb & 0x700) == AC_VERB_SET_AMP_GAIN_MUTE)
                reg &= ~AC_AMP_FAKE_MUTE;
 
        err = snd_hdac_exec_verb(codec, reg, 0, val);
        if (err < 0)
-               return err;
+               goto out;
        /* special handling for asymmetric reads */
        if (verb == AC_VERB_GET_POWER_STATE) {
                if (*val & AC_PWRST_ERROR)
@@ -259,7 +270,9 @@ static int hda_reg_read(void *context, unsigned int reg, unsigned int *val)
                else /* take only the actual state */
                        *val = (*val >> 4) & 0x0f;
        }
-       return 0;
+ out:
+       codec_pm_unlock(codec, pm_lock);
+       return err;
 }
 
 static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
@@ -267,6 +280,7 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
        struct hdac_device *codec = context;
        unsigned int verb;
        int i, bytes, err;
+       int pm_lock = 0;
 
        if (codec->caps_overwriting)
                return 0;
@@ -275,14 +289,21 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
        reg |= (codec->addr << 28);
        verb = get_verb(reg);
 
-       if (!codec_is_running(codec) && verb != AC_VERB_SET_POWER_STATE)
-               return codec->lazy_cache ? 0 : -EAGAIN;
+       if (verb != AC_VERB_SET_POWER_STATE) {
+               pm_lock = codec_pm_lock(codec);
+               if (pm_lock < 0)
+                       return codec->lazy_cache ? 0 : -EAGAIN;
+       }
 
-       if (is_stereo_amp_verb(reg))
-               return hda_reg_write_stereo_amp(codec, reg, val);
+       if (is_stereo_amp_verb(reg)) {
+               err = hda_reg_write_stereo_amp(codec, reg, val);
+               goto out;
+       }
 
-       if (verb == AC_VERB_SET_PROC_COEF)
-               return hda_reg_write_coef(codec, reg, val);
+       if (verb == AC_VERB_SET_PROC_COEF) {
+               err = hda_reg_write_coef(codec, reg, val);
+               goto out;
+       }
 
        switch (verb & 0xf00) {
        case AC_VERB_SET_AMP_GAIN_MUTE:
@@ -319,10 +340,12 @@ static int hda_reg_write(void *context, unsigned int reg, unsigned int val)
                reg |= (verb + i) << 8 | ((val >> (8 * i)) & 0xff);
                err = snd_hdac_exec_verb(codec, reg, 0, NULL);
                if (err < 0)
-                       return err;
+                       goto out;
        }
 
-       return 0;
+ out:
+       codec_pm_unlock(codec, pm_lock);
+       return err;
 }
 
 static const struct regmap_config hda_regmap_cfg = {
diff --git a/sound/hda/hdmi_chmap.c b/sound/hda/hdmi_chmap.c
new file mode 100644 (file)
index 0000000..d7ec862
--- /dev/null
@@ -0,0 +1,791 @@
+/*
+ * HDMI Channel map support helpers
+ */
+
+#include <linux/module.h>
+#include <sound/control.h>
+#include <sound/tlv.h>
+#include <sound/hda_chmap.h>
+
+/*
+ * CEA speaker placement:
+ *
+ *        FLH       FCH        FRH
+ *  FLW    FL  FLC   FC   FRC   FR   FRW
+ *
+ *                                  LFE
+ *                     TC
+ *
+ *          RL  RLC   RC   RRC   RR
+ *
+ * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
+ * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
+ */
+enum cea_speaker_placement {
+       FL  = (1 <<  0),        /* Front Left           */
+       FC  = (1 <<  1),        /* Front Center         */
+       FR  = (1 <<  2),        /* Front Right          */
+       FLC = (1 <<  3),        /* Front Left Center    */
+       FRC = (1 <<  4),        /* Front Right Center   */
+       RL  = (1 <<  5),        /* Rear Left            */
+       RC  = (1 <<  6),        /* Rear Center          */
+       RR  = (1 <<  7),        /* Rear Right           */
+       RLC = (1 <<  8),        /* Rear Left Center     */
+       RRC = (1 <<  9),        /* Rear Right Center    */
+       LFE = (1 << 10),        /* Low Frequency Effect */
+       FLW = (1 << 11),        /* Front Left Wide      */
+       FRW = (1 << 12),        /* Front Right Wide     */
+       FLH = (1 << 13),        /* Front Left High      */
+       FCH = (1 << 14),        /* Front Center High    */
+       FRH = (1 << 15),        /* Front Right High     */
+       TC  = (1 << 16),        /* Top Center           */
+};
+
+static const char * const cea_speaker_allocation_names[] = {
+       /*  0 */ "FL/FR",
+       /*  1 */ "LFE",
+       /*  2 */ "FC",
+       /*  3 */ "RL/RR",
+       /*  4 */ "RC",
+       /*  5 */ "FLC/FRC",
+       /*  6 */ "RLC/RRC",
+       /*  7 */ "FLW/FRW",
+       /*  8 */ "FLH/FRH",
+       /*  9 */ "TC",
+       /* 10 */ "FCH",
+};
+
+/*
+ * ELD SA bits in the CEA Speaker Allocation data block
+ */
+static int eld_speaker_allocation_bits[] = {
+       [0] = FL | FR,
+       [1] = LFE,
+       [2] = FC,
+       [3] = RL | RR,
+       [4] = RC,
+       [5] = FLC | FRC,
+       [6] = RLC | RRC,
+       /* the following are not defined in ELD yet */
+       [7] = FLW | FRW,
+       [8] = FLH | FRH,
+       [9] = TC,
+       [10] = FCH,
+};
+
+/*
+ * ALSA sequence is:
+ *
+ *       surround40   surround41   surround50   surround51   surround71
+ * ch0   front left   =            =            =            =
+ * ch1   front right  =            =            =            =
+ * ch2   rear left    =            =            =            =
+ * ch3   rear right   =            =            =            =
+ * ch4                LFE          center       center       center
+ * ch5                                          LFE          LFE
+ * ch6                                                       side left
+ * ch7                                                       side right
+ *
+ * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
+ */
+static int hdmi_channel_mapping[0x32][8] = {
+       /* stereo */
+       [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+       /* 2.1 */
+       [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
+       /* Dolby Surround */
+       [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
+       /* surround40 */
+       [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
+       /* 4ch */
+       [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
+       /* surround41 */
+       [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
+       /* surround50 */
+       [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
+       /* surround51 */
+       [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
+       /* 7.1 */
+       [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
+};
+
+/*
+ * This is an ordered list!
+ *
+ * The preceding ones have better chances to be selected by
+ * hdmi_channel_allocation().
+ */
+static struct hdac_cea_channel_speaker_allocation channel_allocations[] = {
+/*                       channel:   7     6    5    4    3     2    1    0  */
+{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },
+                                /* 2.1 */
+{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },
+                                /* Dolby Surround */
+{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },
+                                /* surround40 */
+{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },
+                                /* surround41 */
+{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },
+                                /* surround50 */
+{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+                                /* surround51 */
+{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+                                /* 6.1 */
+{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+                                /* surround71 */
+{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+
+{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },
+{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },
+{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },
+{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },
+{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },
+{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },
+{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
+};
+
+static int hdmi_pin_set_slot_channel(struct hdac_device *codec,
+               hda_nid_t pin_nid, int asp_slot, int channel)
+{
+       return snd_hdac_codec_write(codec, pin_nid, 0,
+                               AC_VERB_SET_HDMI_CHAN_SLOT,
+                               (channel << 4) | asp_slot);
+}
+
+static int hdmi_pin_get_slot_channel(struct hdac_device *codec,
+                       hda_nid_t pin_nid, int asp_slot)
+{
+       return (snd_hdac_codec_read(codec, pin_nid, 0,
+                                  AC_VERB_GET_HDMI_CHAN_SLOT,
+                                  asp_slot) & 0xf0) >> 4;
+}
+
+static int hdmi_get_channel_count(struct hdac_device *codec, hda_nid_t cvt_nid)
+{
+       return 1 + snd_hdac_codec_read(codec, cvt_nid, 0,
+                                       AC_VERB_GET_CVT_CHAN_COUNT, 0);
+}
+
+static void hdmi_set_channel_count(struct hdac_device *codec,
+                                  hda_nid_t cvt_nid, int chs)
+{
+       if (chs != hdmi_get_channel_count(codec, cvt_nid))
+               snd_hdac_codec_write(codec, cvt_nid, 0,
+                                   AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
+}
+
+/*
+ * Channel mapping routines
+ */
+
+/*
+ * Compute derived values in channel_allocations[].
+ */
+static void init_channel_allocations(void)
+{
+       int i, j;
+       struct hdac_cea_channel_speaker_allocation *p;
+
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               p = channel_allocations + i;
+               p->channels = 0;
+               p->spk_mask = 0;
+               for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
+                       if (p->speakers[j]) {
+                               p->channels++;
+                               p->spk_mask |= p->speakers[j];
+                       }
+       }
+}
+
+static int get_channel_allocation_order(int ca)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               if (channel_allocations[i].ca_index == ca)
+                       break;
+       }
+       return i;
+}
+
+void snd_hdac_print_channel_allocation(int spk_alloc, char *buf, int buflen)
+{
+       int i, j;
+
+       for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
+               if (spk_alloc & (1 << i))
+                       j += snprintf(buf + j, buflen - j,  " %s",
+                                       cea_speaker_allocation_names[i]);
+       }
+       buf[j] = '\0';  /* necessary when j == 0 */
+}
+EXPORT_SYMBOL_GPL(snd_hdac_print_channel_allocation);
+
+/*
+ * The transformation takes two steps:
+ *
+ *     eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
+ *           spk_mask => (channel_allocations[])         => ai->CA
+ *
+ * TODO: it could select the wrong CA from multiple candidates.
+*/
+static int hdmi_channel_allocation_spk_alloc_blk(struct hdac_device *codec,
+                                  int spk_alloc, int channels)
+{
+       int i;
+       int ca = 0;
+       int spk_mask = 0;
+       char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
+
+       /*
+        * CA defaults to 0 for basic stereo audio
+        */
+       if (channels <= 2)
+               return 0;
+
+       /*
+        * expand ELD's speaker allocation mask
+        *
+        * ELD tells the speaker mask in a compact(paired) form,
+        * expand ELD's notions to match the ones used by Audio InfoFrame.
+        */
+       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
+               if (spk_alloc & (1 << i))
+                       spk_mask |= eld_speaker_allocation_bits[i];
+       }
+
+       /* search for the first working match in the CA table */
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               if (channels == channel_allocations[i].channels &&
+                   (spk_mask & channel_allocations[i].spk_mask) ==
+                               channel_allocations[i].spk_mask) {
+                       ca = channel_allocations[i].ca_index;
+                       break;
+               }
+       }
+
+       if (!ca) {
+               /*
+                * if there was no match, select the regular ALSA channel
+                * allocation with the matching number of channels
+                */
+               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+                       if (channels == channel_allocations[i].channels) {
+                               ca = channel_allocations[i].ca_index;
+                               break;
+                       }
+               }
+       }
+
+       snd_hdac_print_channel_allocation(spk_alloc, buf, sizeof(buf));
+       dev_dbg(&codec->dev, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
+                   ca, channels, buf);
+
+       return ca;
+}
+
+static void hdmi_debug_channel_mapping(struct hdac_chmap *chmap,
+                                      hda_nid_t pin_nid)
+{
+#ifdef CONFIG_SND_DEBUG_VERBOSE
+       int i;
+       int channel;
+
+       for (i = 0; i < 8; i++) {
+               channel = chmap->ops.pin_get_slot_channel(
+                               chmap->hdac, pin_nid, i);
+               dev_dbg(&chmap->hdac->dev, "HDMI: ASP channel %d => slot %d\n",
+                                               channel, i);
+       }
+#endif
+}
+
+static void hdmi_std_setup_channel_mapping(struct hdac_chmap *chmap,
+                                      hda_nid_t pin_nid,
+                                      bool non_pcm,
+                                      int ca)
+{
+       struct hdac_cea_channel_speaker_allocation *ch_alloc;
+       int i;
+       int err;
+       int order;
+       int non_pcm_mapping[8];
+
+       order = get_channel_allocation_order(ca);
+       ch_alloc = &channel_allocations[order];
+
+       if (hdmi_channel_mapping[ca][1] == 0) {
+               int hdmi_slot = 0;
+               /* fill actual channel mappings in ALSA channel (i) order */
+               for (i = 0; i < ch_alloc->channels; i++) {
+                       while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8))
+                               hdmi_slot++; /* skip zero slots */
+
+                       hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
+               }
+               /* fill the rest of the slots with ALSA channel 0xf */
+               for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
+                       if (!ch_alloc->speakers[7 - hdmi_slot])
+                               hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
+       }
+
+       if (non_pcm) {
+               for (i = 0; i < ch_alloc->channels; i++)
+                       non_pcm_mapping[i] = (i << 4) | i;
+               for (; i < 8; i++)
+                       non_pcm_mapping[i] = (0xf << 4) | i;
+       }
+
+       for (i = 0; i < 8; i++) {
+               int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
+               int hdmi_slot = slotsetup & 0x0f;
+               int channel = (slotsetup & 0xf0) >> 4;
+
+               err = chmap->ops.pin_set_slot_channel(chmap->hdac,
+                               pin_nid, hdmi_slot, channel);
+               if (err) {
+                       dev_dbg(&chmap->hdac->dev, "HDMI: channel mapping failed\n");
+                       break;
+               }
+       }
+}
+
+struct channel_map_table {
+       unsigned char map;              /* ALSA API channel map position */
+       int spk_mask;                   /* speaker position bit mask */
+};
+
+static struct channel_map_table map_tables[] = {
+       { SNDRV_CHMAP_FL,       FL },
+       { SNDRV_CHMAP_FR,       FR },
+       { SNDRV_CHMAP_RL,       RL },
+       { SNDRV_CHMAP_RR,       RR },
+       { SNDRV_CHMAP_LFE,      LFE },
+       { SNDRV_CHMAP_FC,       FC },
+       { SNDRV_CHMAP_RLC,      RLC },
+       { SNDRV_CHMAP_RRC,      RRC },
+       { SNDRV_CHMAP_RC,       RC },
+       { SNDRV_CHMAP_FLC,      FLC },
+       { SNDRV_CHMAP_FRC,      FRC },
+       { SNDRV_CHMAP_TFL,      FLH },
+       { SNDRV_CHMAP_TFR,      FRH },
+       { SNDRV_CHMAP_FLW,      FLW },
+       { SNDRV_CHMAP_FRW,      FRW },
+       { SNDRV_CHMAP_TC,       TC },
+       { SNDRV_CHMAP_TFC,      FCH },
+       {} /* terminator */
+};
+
+/* from ALSA API channel position to speaker bit mask */
+int snd_hdac_chmap_to_spk_mask(unsigned char c)
+{
+       struct channel_map_table *t = map_tables;
+
+       for (; t->map; t++) {
+               if (t->map == c)
+                       return t->spk_mask;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_chmap_to_spk_mask);
+
+/* from ALSA API channel position to CEA slot */
+static int to_cea_slot(int ordered_ca, unsigned char pos)
+{
+       int mask = snd_hdac_chmap_to_spk_mask(pos);
+       int i;
+
+       if (mask) {
+               for (i = 0; i < 8; i++) {
+                       if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
+                               return i;
+               }
+       }
+
+       return -1;
+}
+
+/* from speaker bit mask to ALSA API channel position */
+int snd_hdac_spk_to_chmap(int spk)
+{
+       struct channel_map_table *t = map_tables;
+
+       for (; t->map; t++) {
+               if (t->spk_mask == spk)
+                       return t->map;
+       }
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_spk_to_chmap);
+
+/* from CEA slot to ALSA API channel position */
+static int from_cea_slot(int ordered_ca, unsigned char slot)
+{
+       int mask = channel_allocations[ordered_ca].speakers[7 - slot];
+
+       return snd_hdac_spk_to_chmap(mask);
+}
+
+/* get the CA index corresponding to the given ALSA API channel map */
+static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
+{
+       int i, spks = 0, spk_mask = 0;
+
+       for (i = 0; i < chs; i++) {
+               int mask = snd_hdac_chmap_to_spk_mask(map[i]);
+
+               if (mask) {
+                       spk_mask |= mask;
+                       spks++;
+               }
+       }
+
+       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
+               if ((chs == channel_allocations[i].channels ||
+                    spks == channel_allocations[i].channels) &&
+                   (spk_mask & channel_allocations[i].spk_mask) ==
+                               channel_allocations[i].spk_mask)
+                       return channel_allocations[i].ca_index;
+       }
+       return -1;
+}
+
+/* set up the channel slots for the given ALSA API channel map */
+static int hdmi_manual_setup_channel_mapping(struct hdac_chmap *chmap,
+                                            hda_nid_t pin_nid,
+                                            int chs, unsigned char *map,
+                                            int ca)
+{
+       int ordered_ca = get_channel_allocation_order(ca);
+       int alsa_pos, hdmi_slot;
+       int assignments[8] = {[0 ... 7] = 0xf};
+
+       for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
+
+               hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
+
+               if (hdmi_slot < 0)
+                       continue; /* unassigned channel */
+
+               assignments[hdmi_slot] = alsa_pos;
+       }
+
+       for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
+               int err;
+
+               err = chmap->ops.pin_set_slot_channel(chmap->hdac,
+                               pin_nid, hdmi_slot, assignments[hdmi_slot]);
+               if (err)
+                       return -EINVAL;
+       }
+       return 0;
+}
+
+/* store ALSA API channel map from the current default map */
+static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
+{
+       int i;
+       int ordered_ca = get_channel_allocation_order(ca);
+
+       for (i = 0; i < 8; i++) {
+               if (i < channel_allocations[ordered_ca].channels)
+                       map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
+               else
+                       map[i] = 0;
+       }
+}
+
+void snd_hdac_setup_channel_mapping(struct hdac_chmap *chmap,
+                                      hda_nid_t pin_nid, bool non_pcm, int ca,
+                                      int channels, unsigned char *map,
+                                      bool chmap_set)
+{
+       if (!non_pcm && chmap_set) {
+               hdmi_manual_setup_channel_mapping(chmap, pin_nid,
+                                                 channels, map, ca);
+       } else {
+               hdmi_std_setup_channel_mapping(chmap, pin_nid, non_pcm, ca);
+               hdmi_setup_fake_chmap(map, ca);
+       }
+
+       hdmi_debug_channel_mapping(chmap, pin_nid);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_setup_channel_mapping);
+
+int snd_hdac_get_active_channels(int ca)
+{
+       int ordered_ca = get_channel_allocation_order(ca);
+
+       return channel_allocations[ordered_ca].channels;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_active_channels);
+
+struct hdac_cea_channel_speaker_allocation *snd_hdac_get_ch_alloc_from_ca(int ca)
+{
+       return &channel_allocations[get_channel_allocation_order(ca)];
+}
+EXPORT_SYMBOL_GPL(snd_hdac_get_ch_alloc_from_ca);
+
+int snd_hdac_channel_allocation(struct hdac_device *hdac, int spk_alloc,
+               int channels, bool chmap_set, bool non_pcm, unsigned char *map)
+{
+       int ca;
+
+       if (!non_pcm && chmap_set)
+               ca = hdmi_manual_channel_allocation(channels, map);
+       else
+               ca = hdmi_channel_allocation_spk_alloc_blk(hdac,
+                                       spk_alloc, channels);
+
+       if (ca < 0)
+               ca = 0;
+
+       return ca;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_channel_allocation);
+
+/*
+ * ALSA API channel-map control callbacks
+ */
+static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_info *uinfo)
+{
+       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+       struct hdac_chmap *chmap = info->private_data;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = chmap->channels_max;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = SNDRV_CHMAP_LAST;
+       return 0;
+}
+
+static int hdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
+               struct hdac_cea_channel_speaker_allocation *cap, int channels)
+{
+       /* If the speaker allocation matches the channel count, it is OK.*/
+       if (cap->channels != channels)
+               return -1;
+
+       /* all channels are remappable freely */
+       return SNDRV_CTL_TLVT_CHMAP_VAR;
+}
+
+static void hdmi_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
+               struct hdac_cea_channel_speaker_allocation *cap,
+               unsigned int *chmap, int channels)
+{
+       int count = 0;
+       int c;
+
+       for (c = 7; c >= 0; c--) {
+               int spk = cap->speakers[c];
+
+               if (!spk)
+                       continue;
+
+               chmap[count++] = snd_hdac_spk_to_chmap(spk);
+       }
+
+       WARN_ON(count != channels);
+}
+
+static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
+                             unsigned int size, unsigned int __user *tlv)
+{
+       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+       struct hdac_chmap *chmap = info->private_data;
+       unsigned int __user *dst;
+       int chs, count = 0;
+
+       if (size < 8)
+               return -ENOMEM;
+       if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
+               return -EFAULT;
+       size -= 8;
+       dst = tlv + 2;
+       for (chs = 2; chs <= chmap->channels_max; chs++) {
+               int i;
+               struct hdac_cea_channel_speaker_allocation *cap;
+
+               cap = channel_allocations;
+               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
+                       int chs_bytes = chs * 4;
+                       int type = chmap->ops.chmap_cea_alloc_validate_get_type(
+                                                               chmap, cap, chs);
+                       unsigned int tlv_chmap[8];
+
+                       if (type < 0)
+                               continue;
+                       if (size < 8)
+                               return -ENOMEM;
+                       if (put_user(type, dst) ||
+                           put_user(chs_bytes, dst + 1))
+                               return -EFAULT;
+                       dst += 2;
+                       size -= 8;
+                       count += 8;
+                       if (size < chs_bytes)
+                               return -ENOMEM;
+                       size -= chs_bytes;
+                       count += chs_bytes;
+                       chmap->ops.cea_alloc_to_tlv_chmap(chmap, cap,
+                                               tlv_chmap, chs);
+                       if (copy_to_user(dst, tlv_chmap, chs_bytes))
+                               return -EFAULT;
+                       dst += chs;
+               }
+       }
+       if (put_user(count, tlv + 1))
+               return -EFAULT;
+       return 0;
+}
+
+static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+       struct hdac_chmap *chmap = info->private_data;
+       int pcm_idx = kcontrol->private_value;
+       unsigned char pcm_chmap[8];
+       int i;
+
+       memset(pcm_chmap, 0, sizeof(pcm_chmap));
+       chmap->ops.get_chmap(chmap->hdac, pcm_idx, pcm_chmap);
+
+       for (i = 0; i < sizeof(chmap); i++)
+               ucontrol->value.integer.value[i] = pcm_chmap[i];
+
+       return 0;
+}
+
+static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
+       struct hdac_chmap *hchmap = info->private_data;
+       int pcm_idx = kcontrol->private_value;
+       unsigned int ctl_idx;
+       struct snd_pcm_substream *substream;
+       unsigned char chmap[8], per_pin_chmap[8];
+       int i, err, ca, prepared = 0;
+
+       /* No monitor is connected in dyn_pcm_assign.
+        * It's invalid to setup the chmap
+        */
+       if (!hchmap->ops.is_pcm_attached(hchmap->hdac, pcm_idx))
+               return 0;
+
+       ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
+       substream = snd_pcm_chmap_substream(info, ctl_idx);
+       if (!substream || !substream->runtime)
+               return 0; /* just for avoiding error from alsactl restore */
+       switch (substream->runtime->status->state) {
+       case SNDRV_PCM_STATE_OPEN:
+       case SNDRV_PCM_STATE_SETUP:
+               break;
+       case SNDRV_PCM_STATE_PREPARED:
+               prepared = 1;
+               break;
+       default:
+               return -EBUSY;
+       }
+       memset(chmap, 0, sizeof(chmap));
+       for (i = 0; i < ARRAY_SIZE(chmap); i++)
+               chmap[i] = ucontrol->value.integer.value[i];
+
+       hchmap->ops.get_chmap(hchmap->hdac, pcm_idx, per_pin_chmap);
+       if (!memcmp(chmap, per_pin_chmap, sizeof(chmap)))
+               return 0;
+       ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
+       if (ca < 0)
+               return -EINVAL;
+       if (hchmap->ops.chmap_validate) {
+               err = hchmap->ops.chmap_validate(hchmap, ca,
+                               ARRAY_SIZE(chmap), chmap);
+               if (err)
+                       return err;
+       }
+
+       hchmap->ops.set_chmap(hchmap->hdac, pcm_idx, chmap, prepared);
+
+       return 0;
+}
+
+static const struct hdac_chmap_ops chmap_ops = {
+       .chmap_cea_alloc_validate_get_type      = hdmi_chmap_cea_alloc_validate_get_type,
+       .cea_alloc_to_tlv_chmap                 = hdmi_cea_alloc_to_tlv_chmap,
+       .pin_get_slot_channel                   = hdmi_pin_get_slot_channel,
+       .pin_set_slot_channel                   = hdmi_pin_set_slot_channel,
+       .set_channel_count                      = hdmi_set_channel_count,
+};
+
+void snd_hdac_register_chmap_ops(struct hdac_device *hdac,
+                               struct hdac_chmap *chmap)
+{
+       chmap->ops = chmap_ops;
+       chmap->hdac = hdac;
+       init_channel_allocations();
+}
+EXPORT_SYMBOL_GPL(snd_hdac_register_chmap_ops);
+
+int snd_hdac_add_chmap_ctls(struct snd_pcm *pcm, int pcm_idx,
+                               struct hdac_chmap *hchmap)
+{
+       struct snd_pcm_chmap *chmap;
+       struct snd_kcontrol *kctl;
+       int err, i;
+
+       err = snd_pcm_add_chmap_ctls(pcm,
+                                    SNDRV_PCM_STREAM_PLAYBACK,
+                                    NULL, 0, pcm_idx, &chmap);
+       if (err < 0)
+               return err;
+       /* override handlers */
+       chmap->private_data = hchmap;
+       kctl = chmap->kctl;
+       for (i = 0; i < kctl->count; i++)
+               kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
+       kctl->info = hdmi_chmap_ctl_info;
+       kctl->get = hdmi_chmap_ctl_get;
+       kctl->put = hdmi_chmap_ctl_put;
+       kctl->tlv.c = hdmi_chmap_ctl_tlv;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_add_chmap_ctls);
index 2153d31fb66312025cb6221afd8476d313261785..4a4705031cb96ce24d0a4db4876e32733a9bd8dc 100644 (file)
@@ -23,17 +23,5 @@ config SND_SGI_HAL2
         help
                 Sound support for the SGI Indy and Indigo2 Workstation.
 
-
-config SND_AU1X00
-       tristate "Au1x00 AC97 Port Driver (DEPRECATED)"
-       depends on MIPS_ALCHEMY
-       select SND_PCM
-       select SND_AC97_CODEC
-       help
-         ALSA Sound driver for the Au1x00's AC97 port.
-
-         Newer drivers for ASoC are available, please do not use
-         this driver as it will be removed in the future.
-
 endif  # SND_MIPS
 
index 861ec0a574b46efd5315878f4666edc87d288271..b977c44330d67c6e86cef15e1c8eecf68f217b57 100644 (file)
@@ -2,11 +2,9 @@
 # Makefile for ALSA
 #
 
-snd-au1x00-objs := au1x00.o
 snd-sgi-o2-objs := sgio2audio.o ad1843.o
 snd-sgi-hal2-objs := hal2.o
 
 # Toplevel Module Dependency
-obj-$(CONFIG_SND_AU1X00) += snd-au1x00.o
 obj-$(CONFIG_SND_SGI_O2) += snd-sgi-o2.o
 obj-$(CONFIG_SND_SGI_HAL2) += snd-sgi-hal2.o
diff --git a/sound/mips/au1x00.c b/sound/mips/au1x00.c
deleted file mode 100644 (file)
index 1e30e84..0000000
+++ /dev/null
@@ -1,734 +0,0 @@
-/*
- * BRIEF MODULE DESCRIPTION
- *  Driver for AMD Au1000 MIPS Processor, AC'97 Sound Port
- *
- * Copyright 2004 Cooper Street Innovations Inc.
- * Author: Charles Eidsness    <charles@cooper-street.com>
- *
- *  This program is free software; you can redistribute  it and/or modify it
- *  under  the terms of  the GNU General  Public License as published by the
- *  Free Software Foundation;  either version 2 of the  License, or (at your
- *  option) any later version.
- *
- *  THIS  SOFTWARE  IS PROVIDED   ``AS  IS'' AND   ANY  EXPRESS OR IMPLIED
- *  WARRANTIES,   INCLUDING, BUT NOT  LIMITED  TO, THE IMPLIED WARRANTIES OF
- *  MERCHANTABILITY AND FITNESS FOR A PARTICULAR PURPOSE ARE DISCLAIMED.  IN
- *  NO  EVENT  SHALL   THE AUTHOR  BE    LIABLE FOR ANY   DIRECT, INDIRECT,
- *  INCIDENTAL, SPECIAL, EXEMPLARY, OR CONSEQUENTIAL DAMAGES (INCLUDING, BUT
- *  NOT LIMITED   TO, PROCUREMENT OF  SUBSTITUTE GOODS  OR SERVICES; LOSS OF
- *  USE, DATA,  OR PROFITS; OR  BUSINESS INTERRUPTION) HOWEVER CAUSED AND ON
- *  ANY THEORY OF LIABILITY, WHETHER IN  CONTRACT, STRICT LIABILITY, OR TORT
- *  (INCLUDING NEGLIGENCE OR OTHERWISE) ARISING IN ANY WAY OUT OF THE USE OF
- *  THIS SOFTWARE, EVEN IF ADVISED OF THE POSSIBILITY OF SUCH DAMAGE.
- *
- *  You should have received a copy of the  GNU General Public License along
- *  with this program; if not, write  to the Free Software Foundation, Inc.,
- *  675 Mass Ave, Cambridge, MA 02139, USA.
- *
- * History:
- *
- * 2004-09-09 Charles Eidsness -- Original verion -- based on
- *                               sa11xx-uda1341.c ALSA driver and the
- *                               au1000.c OSS driver.
- * 2004-09-09 Matt Porter      -- Added support for ALSA 1.0.6
- *
- */
-
-#include <linux/ioport.h>
-#include <linux/interrupt.h>
-#include <linux/init.h>
-#include <linux/platform_device.h>
-#include <linux/slab.h>
-#include <linux/module.h>
-#include <sound/core.h>
-#include <sound/initval.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/ac97_codec.h>
-#include <asm/mach-au1x00/au1000.h>
-#include <asm/mach-au1x00/au1000_dma.h>
-
-MODULE_AUTHOR("Charles Eidsness <charles@cooper-street.com>");
-MODULE_DESCRIPTION("Au1000 AC'97 ALSA Driver");
-MODULE_LICENSE("GPL");
-MODULE_SUPPORTED_DEVICE("{{AMD,Au1000 AC'97}}");
-
-#define PLAYBACK 0
-#define CAPTURE 1
-#define AC97_SLOT_3 0x01
-#define AC97_SLOT_4 0x02
-#define AC97_SLOT_6 0x08
-#define AC97_CMD_IRQ 31
-#define READ 0
-#define WRITE 1
-#define READ_WAIT 2
-#define RW_DONE 3
-
-struct au1000_period
-{
-       u32 start;
-       u32 relative_end;       /*realtive to start of buffer*/
-       struct au1000_period * next;
-};
-
-/*Au1000 AC97 Port Control Reisters*/
-struct au1000_ac97_reg {
-       u32 volatile config;
-       u32 volatile status;
-       u32 volatile data;
-       u32 volatile cmd;
-       u32 volatile cntrl;
-};
-
-struct audio_stream {
-       struct snd_pcm_substream *substream;
-       int dma;
-       spinlock_t dma_lock;
-       struct au1000_period * buffer;
-       unsigned int period_size;
-       unsigned int periods;
-};
-
-struct snd_au1000 {
-       struct snd_card *card;
-       struct au1000_ac97_reg volatile *ac97_ioport;
-
-       struct resource *ac97_res_port;
-       spinlock_t ac97_lock;
-       struct snd_ac97 *ac97;
-
-       struct snd_pcm *pcm;
-       struct audio_stream *stream[2]; /* playback & capture */
-       int dmaid[2];           /* tx(0)/rx(1) DMA ids */
-};
-
-/*--------------------------- Local Functions --------------------------------*/
-static void
-au1000_set_ac97_xmit_slots(struct snd_au1000 *au1000, long xmit_slots)
-{
-       u32 volatile ac97_config;
-
-       spin_lock(&au1000->ac97_lock);
-       ac97_config = au1000->ac97_ioport->config;
-       ac97_config = ac97_config & ~AC97C_XMIT_SLOTS_MASK;
-       ac97_config |= (xmit_slots << AC97C_XMIT_SLOTS_BIT);
-       au1000->ac97_ioport->config = ac97_config;
-       spin_unlock(&au1000->ac97_lock);
-}
-
-static void
-au1000_set_ac97_recv_slots(struct snd_au1000 *au1000, long recv_slots)
-{
-       u32 volatile ac97_config;
-
-       spin_lock(&au1000->ac97_lock);
-       ac97_config = au1000->ac97_ioport->config;
-       ac97_config = ac97_config & ~AC97C_RECV_SLOTS_MASK;
-       ac97_config |= (recv_slots << AC97C_RECV_SLOTS_BIT);
-       au1000->ac97_ioport->config = ac97_config;
-       spin_unlock(&au1000->ac97_lock);
-}
-
-
-static void
-au1000_release_dma_link(struct audio_stream *stream)
-{
-       struct au1000_period * pointer;
-       struct au1000_period * pointer_next;
-
-       stream->period_size = 0;
-       stream->periods = 0;
-       pointer = stream->buffer;
-       if (! pointer)
-               return;
-       do {
-               pointer_next = pointer->next;
-               kfree(pointer);
-               pointer = pointer_next;
-       } while (pointer != stream->buffer);
-       stream->buffer = NULL;
-}
-
-static int
-au1000_setup_dma_link(struct audio_stream *stream, unsigned int period_bytes,
-                     unsigned int periods)
-{
-       struct snd_pcm_substream *substream = stream->substream;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct au1000_period *pointer;
-       unsigned long dma_start;
-       int i;
-
-       dma_start = virt_to_phys(runtime->dma_area);
-
-       if (stream->period_size == period_bytes &&
-           stream->periods == periods)
-               return 0; /* not changed */
-
-       au1000_release_dma_link(stream);
-
-       stream->period_size = period_bytes;
-       stream->periods = periods;
-
-       stream->buffer = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
-       if (! stream->buffer)
-               return -ENOMEM;
-       pointer = stream->buffer;
-       for (i = 0; i < periods; i++) {
-               pointer->start = (u32)(dma_start + (i * period_bytes));
-               pointer->relative_end = (u32) (((i+1) * period_bytes) - 0x1);
-               if (i < periods - 1) {
-                       pointer->next = kmalloc(sizeof(struct au1000_period), GFP_KERNEL);
-                       if (! pointer->next) {
-                               au1000_release_dma_link(stream);
-                               return -ENOMEM;
-                       }
-                       pointer = pointer->next;
-               }
-       }
-       pointer->next = stream->buffer;
-       return 0;
-}
-
-static void
-au1000_dma_stop(struct audio_stream *stream)
-{
-       if (snd_BUG_ON(!stream->buffer))
-               return;
-       disable_dma(stream->dma);
-}
-
-static void
-au1000_dma_start(struct audio_stream *stream)
-{
-       if (snd_BUG_ON(!stream->buffer))
-               return;
-
-       init_dma(stream->dma);
-       if (get_dma_active_buffer(stream->dma) == 0) {
-               clear_dma_done0(stream->dma);
-               set_dma_addr0(stream->dma, stream->buffer->start);
-               set_dma_count0(stream->dma, stream->period_size >> 1);
-               set_dma_addr1(stream->dma, stream->buffer->next->start);
-               set_dma_count1(stream->dma, stream->period_size >> 1);
-       } else {
-               clear_dma_done1(stream->dma);
-               set_dma_addr1(stream->dma, stream->buffer->start);
-               set_dma_count1(stream->dma, stream->period_size >> 1);
-               set_dma_addr0(stream->dma, stream->buffer->next->start);
-               set_dma_count0(stream->dma, stream->period_size >> 1);
-       }
-       enable_dma_buffers(stream->dma);
-       start_dma(stream->dma);
-}
-
-static irqreturn_t
-au1000_dma_interrupt(int irq, void *dev_id)
-{
-       struct audio_stream *stream = (struct audio_stream *) dev_id;
-       struct snd_pcm_substream *substream = stream->substream;
-
-       spin_lock(&stream->dma_lock);
-       switch (get_dma_buffer_done(stream->dma)) {
-       case DMA_D0:
-               stream->buffer = stream->buffer->next;
-               clear_dma_done0(stream->dma);
-               set_dma_addr0(stream->dma, stream->buffer->next->start);
-               set_dma_count0(stream->dma, stream->period_size >> 1);
-               enable_dma_buffer0(stream->dma);
-               break;
-       case DMA_D1:
-               stream->buffer = stream->buffer->next;
-               clear_dma_done1(stream->dma);
-               set_dma_addr1(stream->dma, stream->buffer->next->start);
-               set_dma_count1(stream->dma, stream->period_size >> 1);
-               enable_dma_buffer1(stream->dma);
-               break;
-       case (DMA_D0 | DMA_D1):
-               printk(KERN_ERR "DMA %d missed interrupt.\n",stream->dma);
-               au1000_dma_stop(stream);
-               au1000_dma_start(stream);
-               break;
-       case (~DMA_D0 & ~DMA_D1):
-               printk(KERN_ERR "DMA %d empty irq.\n",stream->dma);
-       }
-       spin_unlock(&stream->dma_lock);
-       snd_pcm_period_elapsed(substream);
-       return IRQ_HANDLED;
-}
-
-/*-------------------------- PCM Audio Streams -------------------------------*/
-
-static unsigned int rates[] = {8000, 11025, 16000, 22050};
-static struct snd_pcm_hw_constraint_list hw_constraints_rates = {
-       .count  = ARRAY_SIZE(rates),
-       .list   = rates,
-       .mask   = 0,
-};
-
-static struct snd_pcm_hardware snd_au1000_hw =
-{
-       .info                   = (SNDRV_PCM_INFO_INTERLEAVED | \
-                               SNDRV_PCM_INFO_MMAP | SNDRV_PCM_INFO_MMAP_VALID),
-       .formats                = SNDRV_PCM_FMTBIT_S16_LE,
-       .rates                  = (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |
-                               SNDRV_PCM_RATE_16000 | SNDRV_PCM_RATE_22050),
-       .rate_min               = 8000,
-       .rate_max               = 22050,
-       .channels_min           = 1,
-       .channels_max           = 2,
-       .buffer_bytes_max       = 128*1024,
-       .period_bytes_min       = 32,
-       .period_bytes_max       = 16*1024,
-       .periods_min            = 8,
-       .periods_max            = 255,
-       .fifo_size              = 16,
-};
-
-static int
-snd_au1000_playback_open(struct snd_pcm_substream *substream)
-{
-       struct snd_au1000 *au1000 = substream->pcm->private_data;
-
-       au1000->stream[PLAYBACK]->substream = substream;
-       au1000->stream[PLAYBACK]->buffer = NULL;
-       substream->private_data = au1000->stream[PLAYBACK];
-       substream->runtime->hw = snd_au1000_hw;
-       return (snd_pcm_hw_constraint_list(substream->runtime, 0,
-               SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
-}
-
-static int
-snd_au1000_capture_open(struct snd_pcm_substream *substream)
-{
-       struct snd_au1000 *au1000 = substream->pcm->private_data;
-
-       au1000->stream[CAPTURE]->substream = substream;
-       au1000->stream[CAPTURE]->buffer = NULL;
-       substream->private_data = au1000->stream[CAPTURE];
-       substream->runtime->hw = snd_au1000_hw;
-       return (snd_pcm_hw_constraint_list(substream->runtime, 0,
-               SNDRV_PCM_HW_PARAM_RATE, &hw_constraints_rates) < 0);
-}
-
-static int
-snd_au1000_playback_close(struct snd_pcm_substream *substream)
-{
-       struct snd_au1000 *au1000 = substream->pcm->private_data;
-
-       au1000->stream[PLAYBACK]->substream = NULL;
-       return 0;
-}
-
-static int
-snd_au1000_capture_close(struct snd_pcm_substream *substream)
-{
-       struct snd_au1000 *au1000 = substream->pcm->private_data;
-
-       au1000->stream[CAPTURE]->substream = NULL;
-       return 0;
-}
-
-static int
-snd_au1000_hw_params(struct snd_pcm_substream *substream,
-                                       struct snd_pcm_hw_params *hw_params)
-{
-       struct audio_stream *stream = substream->private_data;
-       int err;
-
-       err = snd_pcm_lib_malloc_pages(substream,
-                                      params_buffer_bytes(hw_params));
-       if (err < 0)
-               return err;
-       return au1000_setup_dma_link(stream,
-                                    params_period_bytes(hw_params),
-                                    params_periods(hw_params));
-}
-
-static int
-snd_au1000_hw_free(struct snd_pcm_substream *substream)
-{
-       struct audio_stream *stream = substream->private_data;
-       au1000_release_dma_link(stream);
-       return snd_pcm_lib_free_pages(substream);
-}
-
-static int
-snd_au1000_playback_prepare(struct snd_pcm_substream *substream)
-{
-       struct snd_au1000 *au1000 = substream->pcm->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-
-       if (runtime->channels == 1)
-               au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_4);
-       else
-               au1000_set_ac97_xmit_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
-       snd_ac97_set_rate(au1000->ac97, AC97_PCM_FRONT_DAC_RATE, runtime->rate);
-       return 0;
-}
-
-static int
-snd_au1000_capture_prepare(struct snd_pcm_substream *substream)
-{
-       struct snd_au1000 *au1000 = substream->pcm->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-
-       if (runtime->channels == 1)
-               au1000_set_ac97_recv_slots(au1000, AC97_SLOT_4);
-       else
-               au1000_set_ac97_recv_slots(au1000, AC97_SLOT_3 | AC97_SLOT_4);
-       snd_ac97_set_rate(au1000->ac97, AC97_PCM_LR_ADC_RATE, runtime->rate);
-       return 0;
-}
-
-static int
-snd_au1000_trigger(struct snd_pcm_substream *substream, int cmd)
-{
-       struct audio_stream *stream = substream->private_data;
-       int err = 0;
-
-       spin_lock(&stream->dma_lock);
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               au1000_dma_start(stream);
-               break;
-       case SNDRV_PCM_TRIGGER_STOP:
-               au1000_dma_stop(stream);
-               break;
-       default:
-               err = -EINVAL;
-               break;
-       }
-       spin_unlock(&stream->dma_lock);
-       return err;
-}
-
-static snd_pcm_uframes_t
-snd_au1000_pointer(struct snd_pcm_substream *substream)
-{
-       struct audio_stream *stream = substream->private_data;
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       long location;
-
-       spin_lock(&stream->dma_lock);
-       location = get_dma_residue(stream->dma);
-       spin_unlock(&stream->dma_lock);
-       location = stream->buffer->relative_end - location;
-       if (location == -1)
-               location = 0;
-       return bytes_to_frames(runtime,location);
-}
-
-static struct snd_pcm_ops snd_card_au1000_playback_ops = {
-       .open                   = snd_au1000_playback_open,
-       .close                  = snd_au1000_playback_close,
-       .ioctl                  = snd_pcm_lib_ioctl,
-       .hw_params              = snd_au1000_hw_params,
-       .hw_free                = snd_au1000_hw_free,
-       .prepare                = snd_au1000_playback_prepare,
-       .trigger                = snd_au1000_trigger,
-       .pointer                = snd_au1000_pointer,
-};
-
-static struct snd_pcm_ops snd_card_au1000_capture_ops = {
-       .open                   = snd_au1000_capture_open,
-       .close                  = snd_au1000_capture_close,
-       .ioctl                  = snd_pcm_lib_ioctl,
-       .hw_params              = snd_au1000_hw_params,
-       .hw_free                = snd_au1000_hw_free,
-       .prepare                = snd_au1000_capture_prepare,
-       .trigger                = snd_au1000_trigger,
-       .pointer                = snd_au1000_pointer,
-};
-
-static int
-snd_au1000_pcm_new(struct snd_au1000 *au1000)
-{
-       struct snd_pcm *pcm;
-       int err;
-       unsigned long flags;
-
-       if ((err = snd_pcm_new(au1000->card, "AU1000 AC97 PCM", 0, 1, 1, &pcm)) < 0)
-               return err;
-
-       snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_CONTINUOUS,
-               snd_dma_continuous_data(GFP_KERNEL), 128*1024, 128*1024);
-
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK,
-               &snd_card_au1000_playback_ops);
-       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE,
-               &snd_card_au1000_capture_ops);
-
-       pcm->private_data = au1000;
-       pcm->info_flags = 0;
-       strcpy(pcm->name, "Au1000 AC97 PCM");
-
-       spin_lock_init(&au1000->stream[PLAYBACK]->dma_lock);
-       spin_lock_init(&au1000->stream[CAPTURE]->dma_lock);
-
-       flags = claim_dma_lock();
-       au1000->stream[PLAYBACK]->dma = request_au1000_dma(au1000->dmaid[0],
-                       "AC97 TX", au1000_dma_interrupt, 0,
-                       au1000->stream[PLAYBACK]);
-       if (au1000->stream[PLAYBACK]->dma < 0) {
-               release_dma_lock(flags);
-               return -EBUSY;
-       }
-       au1000->stream[CAPTURE]->dma = request_au1000_dma(au1000->dmaid[1],
-                       "AC97 RX", au1000_dma_interrupt, 0,
-                       au1000->stream[CAPTURE]);
-       if (au1000->stream[CAPTURE]->dma < 0){
-               release_dma_lock(flags);
-               return -EBUSY;
-       }
-       /* enable DMA coherency in read/write DMA channels */
-       set_dma_mode(au1000->stream[PLAYBACK]->dma,
-                    get_dma_mode(au1000->stream[PLAYBACK]->dma) & ~DMA_NC);
-       set_dma_mode(au1000->stream[CAPTURE]->dma,
-                    get_dma_mode(au1000->stream[CAPTURE]->dma) & ~DMA_NC);
-       release_dma_lock(flags);
-       au1000->pcm = pcm;
-       return 0;
-}
-
-
-/*-------------------------- AC97 CODEC Control ------------------------------*/
-
-static unsigned short
-snd_au1000_ac97_read(struct snd_ac97 *ac97, unsigned short reg)
-{
-       struct snd_au1000 *au1000 = ac97->private_data;
-       u32 volatile cmd;
-       u16 volatile data;
-       int             i;
-
-       spin_lock(&au1000->ac97_lock);
-/* would rather use the interrupt than this polling but it works and I can't
-get the interrupt driven case to work efficiently */
-       for (i = 0; i < 0x5000; i++)
-               if (!(au1000->ac97_ioport->status & AC97C_CP))
-                       break;
-       if (i == 0x5000)
-               printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
-
-       cmd = (u32) reg & AC97C_INDEX_MASK;
-       cmd |= AC97C_READ;
-       au1000->ac97_ioport->cmd = cmd;
-
-       /* now wait for the data */
-       for (i = 0; i < 0x5000; i++)
-               if (!(au1000->ac97_ioport->status & AC97C_CP))
-                       break;
-       if (i == 0x5000) {
-               printk(KERN_ERR "au1000 AC97: AC97 command read timeout\n");
-               spin_unlock(&au1000->ac97_lock);
-               return 0;
-       }
-
-       data = au1000->ac97_ioport->cmd & 0xffff;
-       spin_unlock(&au1000->ac97_lock);
-
-       return data;
-
-}
-
-
-static void
-snd_au1000_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
-{
-       struct snd_au1000 *au1000 = ac97->private_data;
-       u32 cmd;
-       int i;
-
-       spin_lock(&au1000->ac97_lock);
-/* would rather use the interrupt than this polling but it works and I can't
-get the interrupt driven case to work efficiently */
-       for (i = 0; i < 0x5000; i++)
-               if (!(au1000->ac97_ioport->status & AC97C_CP))
-                       break;
-       if (i == 0x5000)
-               printk(KERN_ERR "au1000 AC97: AC97 command write timeout\n");
-
-       cmd = (u32) reg & AC97C_INDEX_MASK;
-       cmd &= ~AC97C_READ;
-       cmd |= ((u32) val << AC97C_WD_BIT);
-       au1000->ac97_ioport->cmd = cmd;
-       spin_unlock(&au1000->ac97_lock);
-}
-
-/*------------------------------ Setup / Destroy ----------------------------*/
-
-static void snd_au1000_free(struct snd_card *card)
-{
-       struct snd_au1000 *au1000 = card->private_data;
-
-       if (au1000->stream[PLAYBACK]) {
-               if (au1000->stream[PLAYBACK]->dma >= 0)
-                       free_au1000_dma(au1000->stream[PLAYBACK]->dma);
-               kfree(au1000->stream[PLAYBACK]);
-       }
-
-       if (au1000->stream[CAPTURE]) {
-               if (au1000->stream[CAPTURE]->dma >= 0)
-                       free_au1000_dma(au1000->stream[CAPTURE]->dma);
-               kfree(au1000->stream[CAPTURE]);
-       }
-
-       if (au1000->ac97_res_port) {
-               /* put internal AC97 block into reset */
-               if (au1000->ac97_ioport) {
-                       au1000->ac97_ioport->cntrl = AC97C_RS;
-                       iounmap(au1000->ac97_ioport);
-                       au1000->ac97_ioport = NULL;
-               }
-               release_and_free_resource(au1000->ac97_res_port);
-               au1000->ac97_res_port = NULL;
-       }
-}
-
-static struct snd_ac97_bus_ops ops = {
-       .write  = snd_au1000_ac97_write,
-       .read   = snd_au1000_ac97_read,
-};
-
-static int au1000_ac97_probe(struct platform_device *pdev)
-{
-       int err;
-       void __iomem *io;
-       struct resource *r;
-       struct snd_card *card;
-       struct snd_au1000 *au1000;
-       struct snd_ac97_bus *pbus;
-       struct snd_ac97_template ac97;
-
-       err = snd_card_new(&pdev->dev, -1, "AC97", THIS_MODULE,
-                          sizeof(struct snd_au1000), &card);
-       if (err < 0)
-               return err;
-
-       au1000 = card->private_data;
-       au1000->card = card;
-       spin_lock_init(&au1000->ac97_lock);
-
-       /* from here on let ALSA call the special freeing function */
-       card->private_free = snd_au1000_free;
-
-       /* TX DMA ID */
-       r = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-       if (!r) {
-               err = -ENODEV;
-               snd_printk(KERN_INFO "no TX DMA platform resource!\n");
-               goto out;
-       }
-       au1000->dmaid[0] = r->start;
-
-       /* RX DMA ID */
-       r = platform_get_resource(pdev, IORESOURCE_DMA, 1);
-       if (!r) {
-               err = -ENODEV;
-               snd_printk(KERN_INFO "no RX DMA platform resource!\n");
-               goto out;
-       }
-       au1000->dmaid[1] = r->start;
-
-       au1000->stream[PLAYBACK] = kmalloc(sizeof(struct audio_stream),
-                                          GFP_KERNEL);
-       if (!au1000->stream[PLAYBACK]) {
-               err = -ENOMEM;
-               goto out;
-       }
-       au1000->stream[PLAYBACK]->dma = -1;
-
-       au1000->stream[CAPTURE] = kmalloc(sizeof(struct audio_stream),
-                                         GFP_KERNEL);
-       if (!au1000->stream[CAPTURE]) {
-               err = -ENOMEM;
-               goto out;
-       }
-       au1000->stream[CAPTURE]->dma = -1;
-
-       r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!r) {
-               err = -ENODEV;
-               goto out;
-       }
-
-       err = -EBUSY;
-       au1000->ac97_res_port = request_mem_region(r->start, resource_size(r),
-                                                  pdev->name);
-       if (!au1000->ac97_res_port) {
-               snd_printk(KERN_ERR "ALSA AC97: can't grab AC97 port\n");
-               goto out;
-       }
-
-       io = ioremap(r->start, resource_size(r));
-       if (!io)
-               goto out;
-
-       au1000->ac97_ioport = (struct au1000_ac97_reg *)io;
-
-       /* configure pins for AC'97
-       TODO: move to board_setup.c */
-       au_writel(au_readl(SYS_PINFUNC) & ~0x02, SYS_PINFUNC);
-
-       /* Initialise Au1000's AC'97 Control Block */
-       au1000->ac97_ioport->cntrl = AC97C_RS | AC97C_CE;
-       udelay(10);
-       au1000->ac97_ioport->cntrl = AC97C_CE;
-       udelay(10);
-
-       /* Initialise External CODEC -- cold reset */
-       au1000->ac97_ioport->config = AC97C_RESET;
-       udelay(10);
-       au1000->ac97_ioport->config = 0x0;
-       mdelay(5);
-
-       /* Initialise AC97 middle-layer */
-       err = snd_ac97_bus(au1000->card, 0, &ops, au1000, &pbus);
-       if (err < 0)
-               goto out;
-
-       memset(&ac97, 0, sizeof(ac97));
-       ac97.private_data = au1000;
-       err = snd_ac97_mixer(pbus, &ac97, &au1000->ac97);
-       if (err < 0)
-               goto out;
-
-       err = snd_au1000_pcm_new(au1000);
-       if (err < 0)
-               goto out;
-
-       strcpy(card->driver, "Au1000-AC97");
-       strcpy(card->shortname, "AMD Au1000-AC97");
-       sprintf(card->longname, "AMD Au1000--AC97 ALSA Driver");
-
-       err = snd_card_register(card);
-       if (err < 0)
-               goto out;
-
-       printk(KERN_INFO "ALSA AC97: Driver Initialized\n");
-
-       platform_set_drvdata(pdev, card);
-
-       return 0;
-
- out:
-       snd_card_free(card);
-       return err;
-}
-
-static int au1000_ac97_remove(struct platform_device *pdev)
-{
-       return snd_card_free(platform_get_drvdata(pdev));
-}
-
-struct platform_driver au1000_ac97c_driver = {
-       .driver = {
-               .name   = "au1000-ac97c",
-               .owner  = THIS_MODULE,
-       },
-       .probe          = au1000_ac97_probe,
-       .remove         = au1000_ac97_remove,
-};
-
-module_platform_driver(au1000_ac97c_driver);
index 8f6594a7d37f7b940ded27f6a8a0395ea8fa1442..32151d8c6bb8677b5a6f3b11b0a52eb9af35e80b 100644 (file)
@@ -866,7 +866,7 @@ config SND_VIRTUOSO
        select SND_OXYGEN_LIB
        select SND_PCM
        select SND_MPU401_UART
-       select SND_JACK if INPUT=y || INPUT=SND
+       select SND_JACK
        help
          Say Y here to include support for sound cards based on the
          Asus AV66/AV100/AV200 chips, i.e., Xonar D1, DX, D2, D2X, DS, DSX,
index e94cfd5c69f7e8e19658b6764dd5160d9f60e36f..bb02c2d48fd508d2892d39b5ec58e976831d59b2 100644 (file)
@@ -4,7 +4,7 @@ config SND_HDA
        tristate
        select SND_PCM
        select SND_VMASTER
-       select SND_JACK if INPUT=y || INPUT=SND
+       select SND_JACK
        select SND_HDA_CORE
 
 config SND_HDA_INTEL
index bc2e08257c2e55a91d03a64813684be6cc708c84..ba7fe9b6655c1f25279e5489081f34327f215d45 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/slab.h>
 #include <sound/core.h>
 #include <asm/unaligned.h>
+#include <sound/hda_chmap.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 
@@ -42,20 +43,6 @@ enum cea_edid_versions {
        CEA_EDID_VER_RESERVED   = 4,
 };
 
-static const char * const cea_speaker_allocation_names[] = {
-       /*  0 */ "FL/FR",
-       /*  1 */ "LFE",
-       /*  2 */ "FC",
-       /*  3 */ "RL/RR",
-       /*  4 */ "RC",
-       /*  5 */ "FLC/FRC",
-       /*  6 */ "RLC/RRC",
-       /*  7 */ "FLW/FRW",
-       /*  8 */ "FLH/FRH",
-       /*  9 */ "TC",
-       /* 10 */ "FCH",
-};
-
 static const char * const eld_connection_type_names[4] = {
        "HDMI",
        "DisplayPort",
@@ -419,18 +406,6 @@ static void hdmi_show_short_audio_desc(struct hda_codec *codec,
                  a->channels, buf, buf2);
 }
 
-void snd_print_channel_allocation(int spk_alloc, char *buf, int buflen)
-{
-       int i, j;
-
-       for (i = 0, j = 0; i < ARRAY_SIZE(cea_speaker_allocation_names); i++) {
-               if (spk_alloc & (1 << i))
-                       j += snprintf(buf + j, buflen - j,  " %s",
-                                       cea_speaker_allocation_names[i]);
-       }
-       buf[j] = '\0';  /* necessary when j == 0 */
-}
-
 void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e)
 {
        int i;
@@ -441,7 +416,7 @@ void snd_hdmi_show_eld(struct hda_codec *codec, struct parsed_hdmi_eld *e)
 
        if (e->spk_alloc) {
                char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
-               snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+               snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
                codec_dbg(codec, "HDMI: available speakers:%s\n", buf);
        }
 
@@ -516,7 +491,7 @@ void snd_hdmi_print_eld_info(struct hdmi_eld *eld,
        snd_iprintf(buffer, "support_ai\t\t%d\n", e->support_ai);
        snd_iprintf(buffer, "audio_sync_delay\t%d\n", e->aud_synch_delay);
 
-       snd_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
+       snd_hdac_print_channel_allocation(e->spk_alloc, buf, sizeof(buf));
        snd_iprintf(buffer, "speakers\t\t[0x%x]%s\n", e->spk_alloc, buf);
 
        snd_iprintf(buffer, "sad_count\t\t%d\n", e->sad_count);
index e5240cb3749f4186e79f70dfc9e2c6536eaaed1d..2624cfe98884b414b97e356bdc5f9e1297d245b9 100644 (file)
@@ -2145,7 +2145,7 @@ static int azx_probe_continue(struct azx *chip)
        azx_add_card_list(chip);
        snd_hda_set_power_save(&chip->bus, power_save * 1000);
        if (azx_has_pm_runtime(chip) || hda->use_vga_switcheroo)
-               pm_runtime_put_noidle(&pci->dev);
+               pm_runtime_put_autosuspend(&pci->dev);
 
 out_free:
        if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL
index c1c855a6c0af8199d03b04419d16d3494507ddeb..a47e8ae0eb308355416886d4753c9f950bd0cd56 100644 (file)
@@ -174,8 +174,12 @@ static void cs_automute(struct hda_codec *codec)
        snd_hda_gen_update_outputs(codec);
 
        if (spec->gpio_eapd_hp || spec->gpio_eapd_speaker) {
-               spec->gpio_data = spec->gen.hp_jack_present ?
-                       spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
+               if (spec->gen.automute_speaker)
+                       spec->gpio_data = spec->gen.hp_jack_present ?
+                               spec->gpio_eapd_hp : spec->gpio_eapd_speaker;
+               else
+                       spec->gpio_data =
+                               spec->gpio_eapd_hp | spec->gpio_eapd_speaker;
                snd_hda_codec_write(codec, 0x01, 0,
                                    AC_VERB_SET_GPIO_DATA, spec->gpio_data);
        }
index 6122b8ca872fd85b4d532919a0e7a7d10543b17e..56fefbd8578242ab0d602f64fb182db75bea7ab4 100644 (file)
@@ -204,8 +204,13 @@ static void cx_auto_reboot_notify(struct hda_codec *codec)
 {
        struct conexant_spec *spec = codec->spec;
 
-       if (codec->core.vendor_id != 0x14f150f2)
+       switch (codec->core.vendor_id) {
+       case 0x14f150f2: /* CX20722 */
+       case 0x14f150f4: /* CX20724 */
+               break;
+       default:
                return;
+       }
 
        /* Turn the CX20722 codec into D3 to avoid spurious noises
           from the internal speaker during (and after) reboot */
index bcbc4ee10130a5ee86de2c23815eb85ec8684459..49ee4e55dd16e9a8914258a8d9064a912145ad4e 100644 (file)
@@ -39,6 +39,7 @@
 #include <sound/tlv.h>
 #include <sound/hdaudio.h>
 #include <sound/hda_i915.h>
+#include <sound/hda_chmap.h>
 #include "hda_codec.h"
 #include "hda_local.h"
 #include "hda_jack.h"
@@ -75,6 +76,8 @@ struct hdmi_spec_per_cvt {
 
 struct hdmi_spec_per_pin {
        hda_nid_t pin_nid;
+       /* pin idx, different device entries on the same pin use the same idx */
+       int pin_nid_idx;
        int num_mux_nids;
        hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
        int mux_idx;
@@ -84,8 +87,8 @@ struct hdmi_spec_per_pin {
        struct hdmi_eld sink_eld;
        struct mutex lock;
        struct delayed_work work;
-       struct snd_kcontrol *eld_ctl;
-       struct snd_jack *acomp_jack; /* jack via audio component */
+       struct hdmi_pcm *pcm; /* pointer to spec->pcm_rec[n] dynamically*/
+       int pcm_idx; /* which pcm is attached. -1 means no pcm is attached */
        int repoll_count;
        bool setup; /* the stream has been set up by prepare callback */
        int channels; /* current number of channels */
@@ -97,19 +100,11 @@ struct hdmi_spec_per_pin {
 #endif
 };
 
-struct cea_channel_speaker_allocation;
-
 /* operations used by generic code that can be overridden by patches */
 struct hdmi_ops {
        int (*pin_get_eld)(struct hda_codec *codec, hda_nid_t pin_nid,
                           unsigned char *buf, int *eld_size);
 
-       /* get and set channel assigned to each HDMI ASP (audio sample packet) slot */
-       int (*pin_get_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid,
-                                   int asp_slot);
-       int (*pin_set_slot_channel)(struct hda_codec *codec, hda_nid_t pin_nid,
-                                   int asp_slot, int channel);
-
        void (*pin_setup_infoframe)(struct hda_codec *codec, hda_nid_t pin_nid,
                                    int ca, int active_channels, int conn_type);
 
@@ -119,15 +114,12 @@ struct hdmi_ops {
        int (*setup_stream)(struct hda_codec *codec, hda_nid_t cvt_nid,
                            hda_nid_t pin_nid, u32 stream_tag, int format);
 
-       /* Helpers for producing the channel map TLVs. These can be overridden
-        * for devices that have non-standard mapping requirements. */
-       int (*chmap_cea_alloc_validate_get_type)(struct cea_channel_speaker_allocation *cap,
-                                                int channels);
-       void (*cea_alloc_to_tlv_chmap)(struct cea_channel_speaker_allocation *cap,
-                                      unsigned int *chmap, int channels);
+};
 
-       /* check that the user-given chmap is supported */
-       int (*chmap_validate)(int ca, int channels, unsigned char *chmap);
+struct hdmi_pcm {
+       struct hda_pcm *pcm;
+       struct snd_jack *jack;
+       struct snd_kcontrol *eld_ctl;
 };
 
 struct hdmi_spec {
@@ -137,14 +129,22 @@ struct hdmi_spec {
 
        int num_pins;
        struct snd_array pins; /* struct hdmi_spec_per_pin */
-       struct hda_pcm *pcm_rec[16];
-       unsigned int channels_max; /* max over all cvts */
+       struct hdmi_pcm pcm_rec[16];
+       struct mutex pcm_lock;
+       /* pcm_bitmap means which pcms have been assigned to pins*/
+       unsigned long pcm_bitmap;
+       int pcm_used;   /* counter of pcm_rec[] */
+       /* bitmap shows whether the pcm is opened in user space
+        * bit 0 means the first playback PCM (PCM3);
+        * bit 1 means the second playback PCM, and so on.
+        */
+       unsigned long pcm_in_use;
 
        struct hdmi_eld temp_eld;
        struct hdmi_ops ops;
 
        bool dyn_pin_out;
-
+       bool dyn_pcm_assign;
        /*
         * Non-generic VIA/NVIDIA specific
         */
@@ -154,6 +154,8 @@ struct hdmi_spec {
        /* i915/powerwell (Haswell+/Valleyview+) specific */
        struct i915_audio_component_audio_ops i915_audio_ops;
        bool i915_bound; /* was i915 bound in this driver? */
+
+       struct hdac_chmap chmap;
 };
 
 #ifdef CONFIG_SND_HDA_I915
@@ -195,173 +197,6 @@ union audio_infoframe {
        u8 bytes[0];
 };
 
-/*
- * CEA speaker placement:
- *
- *        FLH       FCH        FRH
- *  FLW    FL  FLC   FC   FRC   FR   FRW
- *
- *                                  LFE
- *                     TC
- *
- *          RL  RLC   RC   RRC   RR
- *
- * The Left/Right Surround channel _notions_ LS/RS in SMPTE 320M corresponds to
- * CEA RL/RR; The SMPTE channel _assignment_ C/LFE is swapped to CEA LFE/FC.
- */
-enum cea_speaker_placement {
-       FL  = (1 <<  0),        /* Front Left           */
-       FC  = (1 <<  1),        /* Front Center         */
-       FR  = (1 <<  2),        /* Front Right          */
-       FLC = (1 <<  3),        /* Front Left Center    */
-       FRC = (1 <<  4),        /* Front Right Center   */
-       RL  = (1 <<  5),        /* Rear Left            */
-       RC  = (1 <<  6),        /* Rear Center          */
-       RR  = (1 <<  7),        /* Rear Right           */
-       RLC = (1 <<  8),        /* Rear Left Center     */
-       RRC = (1 <<  9),        /* Rear Right Center    */
-       LFE = (1 << 10),        /* Low Frequency Effect */
-       FLW = (1 << 11),        /* Front Left Wide      */
-       FRW = (1 << 12),        /* Front Right Wide     */
-       FLH = (1 << 13),        /* Front Left High      */
-       FCH = (1 << 14),        /* Front Center High    */
-       FRH = (1 << 15),        /* Front Right High     */
-       TC  = (1 << 16),        /* Top Center           */
-};
-
-/*
- * ELD SA bits in the CEA Speaker Allocation data block
- */
-static int eld_speaker_allocation_bits[] = {
-       [0] = FL | FR,
-       [1] = LFE,
-       [2] = FC,
-       [3] = RL | RR,
-       [4] = RC,
-       [5] = FLC | FRC,
-       [6] = RLC | RRC,
-       /* the following are not defined in ELD yet */
-       [7] = FLW | FRW,
-       [8] = FLH | FRH,
-       [9] = TC,
-       [10] = FCH,
-};
-
-struct cea_channel_speaker_allocation {
-       int ca_index;
-       int speakers[8];
-
-       /* derived values, just for convenience */
-       int channels;
-       int spk_mask;
-};
-
-/*
- * ALSA sequence is:
- *
- *       surround40   surround41   surround50   surround51   surround71
- * ch0   front left   =            =            =            =
- * ch1   front right  =            =            =            =
- * ch2   rear left    =            =            =            =
- * ch3   rear right   =            =            =            =
- * ch4                LFE          center       center       center
- * ch5                                          LFE          LFE
- * ch6                                                       side left
- * ch7                                                       side right
- *
- * surround71 = {FL, FR, RLC, RRC, FC, LFE, RL, RR}
- */
-static int hdmi_channel_mapping[0x32][8] = {
-       /* stereo */
-       [0x00] = { 0x00, 0x11, 0xf2, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* 2.1 */
-       [0x01] = { 0x00, 0x11, 0x22, 0xf3, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* Dolby Surround */
-       [0x02] = { 0x00, 0x11, 0x23, 0xf2, 0xf4, 0xf5, 0xf6, 0xf7 },
-       /* surround40 */
-       [0x08] = { 0x00, 0x11, 0x24, 0x35, 0xf3, 0xf2, 0xf6, 0xf7 },
-       /* 4ch */
-       [0x03] = { 0x00, 0x11, 0x23, 0x32, 0x44, 0xf5, 0xf6, 0xf7 },
-       /* surround41 */
-       [0x09] = { 0x00, 0x11, 0x24, 0x35, 0x42, 0xf3, 0xf6, 0xf7 },
-       /* surround50 */
-       [0x0a] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0xf2, 0xf6, 0xf7 },
-       /* surround51 */
-       [0x0b] = { 0x00, 0x11, 0x24, 0x35, 0x43, 0x52, 0xf6, 0xf7 },
-       /* 7.1 */
-       [0x13] = { 0x00, 0x11, 0x26, 0x37, 0x43, 0x52, 0x64, 0x75 },
-};
-
-/*
- * This is an ordered list!
- *
- * The preceding ones have better chances to be selected by
- * hdmi_channel_allocation().
- */
-static struct cea_channel_speaker_allocation channel_allocations[] = {
-/*                       channel:   7     6    5    4    3     2    1    0  */
-{ .ca_index = 0x00,  .speakers = {   0,    0,   0,   0,   0,    0,  FR,  FL } },
-                                /* 2.1 */
-{ .ca_index = 0x01,  .speakers = {   0,    0,   0,   0,   0,  LFE,  FR,  FL } },
-                                /* Dolby Surround */
-{ .ca_index = 0x02,  .speakers = {   0,    0,   0,   0,  FC,    0,  FR,  FL } },
-                                /* surround40 */
-{ .ca_index = 0x08,  .speakers = {   0,    0,  RR,  RL,   0,    0,  FR,  FL } },
-                                /* surround41 */
-{ .ca_index = 0x09,  .speakers = {   0,    0,  RR,  RL,   0,  LFE,  FR,  FL } },
-                                /* surround50 */
-{ .ca_index = 0x0a,  .speakers = {   0,    0,  RR,  RL,  FC,    0,  FR,  FL } },
-                                /* surround51 */
-{ .ca_index = 0x0b,  .speakers = {   0,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
-                                /* 6.1 */
-{ .ca_index = 0x0f,  .speakers = {   0,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-                                /* surround71 */
-{ .ca_index = 0x13,  .speakers = { RRC,  RLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-
-{ .ca_index = 0x03,  .speakers = {   0,    0,   0,   0,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x04,  .speakers = {   0,    0,   0,  RC,   0,    0,  FR,  FL } },
-{ .ca_index = 0x05,  .speakers = {   0,    0,   0,  RC,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x06,  .speakers = {   0,    0,   0,  RC,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x07,  .speakers = {   0,    0,   0,  RC,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x0c,  .speakers = {   0,   RC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x0d,  .speakers = {   0,   RC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x0e,  .speakers = {   0,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x10,  .speakers = { RRC,  RLC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x11,  .speakers = { RRC,  RLC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x12,  .speakers = { RRC,  RLC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x14,  .speakers = { FRC,  FLC,   0,   0,   0,    0,  FR,  FL } },
-{ .ca_index = 0x15,  .speakers = { FRC,  FLC,   0,   0,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x16,  .speakers = { FRC,  FLC,   0,   0,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x17,  .speakers = { FRC,  FLC,   0,   0,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x18,  .speakers = { FRC,  FLC,   0,  RC,   0,    0,  FR,  FL } },
-{ .ca_index = 0x19,  .speakers = { FRC,  FLC,   0,  RC,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x1a,  .speakers = { FRC,  FLC,   0,  RC,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x1b,  .speakers = { FRC,  FLC,   0,  RC,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x1c,  .speakers = { FRC,  FLC,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x1d,  .speakers = { FRC,  FLC,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x1e,  .speakers = { FRC,  FLC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x1f,  .speakers = { FRC,  FLC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x20,  .speakers = {   0,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x21,  .speakers = {   0,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x22,  .speakers = {  TC,    0,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x23,  .speakers = {  TC,    0,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x24,  .speakers = { FRH,  FLH,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x25,  .speakers = { FRH,  FLH,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x26,  .speakers = { FRW,  FLW,  RR,  RL,   0,    0,  FR,  FL } },
-{ .ca_index = 0x27,  .speakers = { FRW,  FLW,  RR,  RL,   0,  LFE,  FR,  FL } },
-{ .ca_index = 0x28,  .speakers = {  TC,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x29,  .speakers = {  TC,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2a,  .speakers = { FCH,   RC,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2b,  .speakers = { FCH,   RC,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2c,  .speakers = {  TC,  FCH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2d,  .speakers = {  TC,  FCH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x2e,  .speakers = { FRH,  FLH,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x2f,  .speakers = { FRH,  FLH,  RR,  RL,  FC,  LFE,  FR,  FL } },
-{ .ca_index = 0x30,  .speakers = { FRW,  FLW,  RR,  RL,  FC,    0,  FR,  FL } },
-{ .ca_index = 0x31,  .speakers = { FRW,  FLW,  RR,  RL,  FC,  LFE,  FR,  FL } },
-};
-
-
 /*
  * HDMI routines
  */
@@ -370,7 +205,10 @@ static struct cea_channel_speaker_allocation channel_allocations[] = {
        ((struct hdmi_spec_per_pin *)snd_array_elem(&spec->pins, idx))
 #define get_cvt(spec, idx) \
        ((struct hdmi_spec_per_cvt  *)snd_array_elem(&spec->cvts, idx))
-#define get_pcm_rec(spec, idx) ((spec)->pcm_rec[idx])
+/* obtain hdmi_pcm object assigned to idx */
+#define get_hdmi_pcm(spec, idx)        (&(spec)->pcm_rec[idx])
+/* obtain hda_pcm object assigned to idx */
+#define get_pcm_rec(spec, idx) (get_hdmi_pcm(spec, idx)->pcm)
 
 static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid)
 {
@@ -385,20 +223,52 @@ static int pin_nid_to_pin_index(struct hda_codec *codec, hda_nid_t pin_nid)
        return -EINVAL;
 }
 
+static int hinfo_to_pcm_index(struct hda_codec *codec,
+                       struct hda_pcm_stream *hinfo)
+{
+       struct hdmi_spec *spec = codec->spec;
+       int pcm_idx;
+
+       for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++)
+               if (get_pcm_rec(spec, pcm_idx)->stream == hinfo)
+                       return pcm_idx;
+
+       codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo);
+       return -EINVAL;
+}
+
 static int hinfo_to_pin_index(struct hda_codec *codec,
                              struct hda_pcm_stream *hinfo)
 {
        struct hdmi_spec *spec = codec->spec;
+       struct hdmi_spec_per_pin *per_pin;
        int pin_idx;
 
-       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++)
-               if (get_pcm_rec(spec, pin_idx)->stream == hinfo)
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+               per_pin = get_pin(spec, pin_idx);
+               if (per_pin->pcm &&
+                       per_pin->pcm->pcm->stream == hinfo)
                        return pin_idx;
+       }
 
-       codec_warn(codec, "HDMI: hinfo %p not registered\n", hinfo);
+       codec_dbg(codec, "HDMI: hinfo %p not registered\n", hinfo);
        return -EINVAL;
 }
 
+static struct hdmi_spec_per_pin *pcm_idx_to_pin(struct hdmi_spec *spec,
+                                               int pcm_idx)
+{
+       int i;
+       struct hdmi_spec_per_pin *per_pin;
+
+       for (i = 0; i < spec->num_pins; i++) {
+               per_pin = get_pin(spec, i);
+               if (per_pin->pcm_idx == pcm_idx)
+                       return per_pin;
+       }
+       return NULL;
+}
+
 static int cvt_nid_to_cvt_index(struct hda_codec *codec, hda_nid_t cvt_nid)
 {
        struct hdmi_spec *spec = codec->spec;
@@ -419,17 +289,22 @@ static int hdmi_eld_ctl_info(struct snd_kcontrol *kcontrol,
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_spec_per_pin *per_pin;
        struct hdmi_eld *eld;
-       int pin_idx;
+       int pcm_idx;
 
        uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
 
-       pin_idx = kcontrol->private_value;
-       per_pin = get_pin(spec, pin_idx);
+       pcm_idx = kcontrol->private_value;
+       mutex_lock(&spec->pcm_lock);
+       per_pin = pcm_idx_to_pin(spec, pcm_idx);
+       if (!per_pin) {
+               /* no pin is bound to the pcm */
+               uinfo->count = 0;
+               mutex_unlock(&spec->pcm_lock);
+               return 0;
+       }
        eld = &per_pin->sink_eld;
-
-       mutex_lock(&per_pin->lock);
        uinfo->count = eld->eld_valid ? eld->eld_size : 0;
-       mutex_unlock(&per_pin->lock);
+       mutex_unlock(&spec->pcm_lock);
 
        return 0;
 }
@@ -441,16 +316,23 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_spec_per_pin *per_pin;
        struct hdmi_eld *eld;
-       int pin_idx;
-
-       pin_idx = kcontrol->private_value;
-       per_pin = get_pin(spec, pin_idx);
+       int pcm_idx;
+
+       pcm_idx = kcontrol->private_value;
+       mutex_lock(&spec->pcm_lock);
+       per_pin = pcm_idx_to_pin(spec, pcm_idx);
+       if (!per_pin) {
+               /* no pin is bound to the pcm */
+               memset(ucontrol->value.bytes.data, 0,
+                      ARRAY_SIZE(ucontrol->value.bytes.data));
+               mutex_unlock(&spec->pcm_lock);
+               return 0;
+       }
        eld = &per_pin->sink_eld;
 
-       mutex_lock(&per_pin->lock);
        if (eld->eld_size > ARRAY_SIZE(ucontrol->value.bytes.data) ||
            eld->eld_size > ELD_MAX_SIZE) {
-               mutex_unlock(&per_pin->lock);
+               mutex_unlock(&spec->pcm_lock);
                snd_BUG();
                return -EINVAL;
        }
@@ -460,7 +342,7 @@ static int hdmi_eld_ctl_get(struct snd_kcontrol *kcontrol,
        if (eld->eld_valid)
                memcpy(ucontrol->value.bytes.data, eld->eld_buffer,
                       eld->eld_size);
-       mutex_unlock(&per_pin->lock);
+       mutex_unlock(&spec->pcm_lock);
 
        return 0;
 }
@@ -473,7 +355,7 @@ static struct snd_kcontrol_new eld_bytes_ctl = {
        .get = hdmi_eld_ctl_get,
 };
 
-static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
+static int hdmi_create_eld_ctl(struct hda_codec *codec, int pcm_idx,
                        int device)
 {
        struct snd_kcontrol *kctl;
@@ -483,14 +365,17 @@ static int hdmi_create_eld_ctl(struct hda_codec *codec, int pin_idx,
        kctl = snd_ctl_new1(&eld_bytes_ctl, codec);
        if (!kctl)
                return -ENOMEM;
-       kctl->private_value = pin_idx;
+       kctl->private_value = pcm_idx;
        kctl->id.device = device;
 
-       err = snd_hda_ctl_add(codec, get_pin(spec, pin_idx)->pin_nid, kctl);
+       /* no pin nid is associated with the kctl now
+        * tbd: associate pin nid to eld ctl later
+        */
+       err = snd_hda_ctl_add(codec, 0, kctl);
        if (err < 0)
                return err;
 
-       get_pin(spec, pin_idx)->eld_ctl = kctl;
+       get_hdmi_pcm(spec, pcm_idx)->eld_ctl = kctl;
        return 0;
 }
 
@@ -547,20 +432,6 @@ static void hdmi_init_pin(struct hda_codec *codec, hda_nid_t pin_nid)
                            AC_VERB_SET_PIN_WIDGET_CONTROL, pin_out);
 }
 
-static int hdmi_get_channel_count(struct hda_codec *codec, hda_nid_t cvt_nid)
-{
-       return 1 + snd_hda_codec_read(codec, cvt_nid, 0,
-                                       AC_VERB_GET_CVT_CHAN_COUNT, 0);
-}
-
-static void hdmi_set_channel_count(struct hda_codec *codec,
-                                  hda_nid_t cvt_nid, int chs)
-{
-       if (chs != hdmi_get_channel_count(codec, cvt_nid))
-               snd_hda_codec_write(codec, cvt_nid, 0,
-                                   AC_VERB_SET_CVT_CHAN_COUNT, chs - 1);
-}
-
 /*
  * ELD proc files
  */
@@ -624,339 +495,6 @@ static inline void eld_proc_free(struct hdmi_spec_per_pin *per_pin)
 }
 #endif
 
-/*
- * Channel mapping routines
- */
-
-/*
- * Compute derived values in channel_allocations[].
- */
-static void init_channel_allocations(void)
-{
-       int i, j;
-       struct cea_channel_speaker_allocation *p;
-
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               p = channel_allocations + i;
-               p->channels = 0;
-               p->spk_mask = 0;
-               for (j = 0; j < ARRAY_SIZE(p->speakers); j++)
-                       if (p->speakers[j]) {
-                               p->channels++;
-                               p->spk_mask |= p->speakers[j];
-                       }
-       }
-}
-
-static int get_channel_allocation_order(int ca)
-{
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               if (channel_allocations[i].ca_index == ca)
-                       break;
-       }
-       return i;
-}
-
-/*
- * The transformation takes two steps:
- *
- *     eld->spk_alloc => (eld_speaker_allocation_bits[]) => spk_mask
- *           spk_mask => (channel_allocations[])         => ai->CA
- *
- * TODO: it could select the wrong CA from multiple candidates.
-*/
-static int hdmi_channel_allocation(struct hda_codec *codec,
-                                  struct hdmi_eld *eld, int channels)
-{
-       int i;
-       int ca = 0;
-       int spk_mask = 0;
-       char buf[SND_PRINT_CHANNEL_ALLOCATION_ADVISED_BUFSIZE];
-
-       /*
-        * CA defaults to 0 for basic stereo audio
-        */
-       if (channels <= 2)
-               return 0;
-
-       /*
-        * expand ELD's speaker allocation mask
-        *
-        * ELD tells the speaker mask in a compact(paired) form,
-        * expand ELD's notions to match the ones used by Audio InfoFrame.
-        */
-       for (i = 0; i < ARRAY_SIZE(eld_speaker_allocation_bits); i++) {
-               if (eld->info.spk_alloc & (1 << i))
-                       spk_mask |= eld_speaker_allocation_bits[i];
-       }
-
-       /* search for the first working match in the CA table */
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               if (channels == channel_allocations[i].channels &&
-                   (spk_mask & channel_allocations[i].spk_mask) ==
-                               channel_allocations[i].spk_mask) {
-                       ca = channel_allocations[i].ca_index;
-                       break;
-               }
-       }
-
-       if (!ca) {
-               /* if there was no match, select the regular ALSA channel
-                * allocation with the matching number of channels */
-               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-                       if (channels == channel_allocations[i].channels) {
-                               ca = channel_allocations[i].ca_index;
-                               break;
-                       }
-               }
-       }
-
-       snd_print_channel_allocation(eld->info.spk_alloc, buf, sizeof(buf));
-       codec_dbg(codec, "HDMI: select CA 0x%x for %d-channel allocation: %s\n",
-                   ca, channels, buf);
-
-       return ca;
-}
-
-static void hdmi_debug_channel_mapping(struct hda_codec *codec,
-                                      hda_nid_t pin_nid)
-{
-#ifdef CONFIG_SND_DEBUG_VERBOSE
-       struct hdmi_spec *spec = codec->spec;
-       int i;
-       int channel;
-
-       for (i = 0; i < 8; i++) {
-               channel = spec->ops.pin_get_slot_channel(codec, pin_nid, i);
-               codec_dbg(codec, "HDMI: ASP channel %d => slot %d\n",
-                                               channel, i);
-       }
-#endif
-}
-
-static void hdmi_std_setup_channel_mapping(struct hda_codec *codec,
-                                      hda_nid_t pin_nid,
-                                      bool non_pcm,
-                                      int ca)
-{
-       struct hdmi_spec *spec = codec->spec;
-       struct cea_channel_speaker_allocation *ch_alloc;
-       int i;
-       int err;
-       int order;
-       int non_pcm_mapping[8];
-
-       order = get_channel_allocation_order(ca);
-       ch_alloc = &channel_allocations[order];
-
-       if (hdmi_channel_mapping[ca][1] == 0) {
-               int hdmi_slot = 0;
-               /* fill actual channel mappings in ALSA channel (i) order */
-               for (i = 0; i < ch_alloc->channels; i++) {
-                       while (!ch_alloc->speakers[7 - hdmi_slot] && !WARN_ON(hdmi_slot >= 8))
-                               hdmi_slot++; /* skip zero slots */
-
-                       hdmi_channel_mapping[ca][i] = (i << 4) | hdmi_slot++;
-               }
-               /* fill the rest of the slots with ALSA channel 0xf */
-               for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++)
-                       if (!ch_alloc->speakers[7 - hdmi_slot])
-                               hdmi_channel_mapping[ca][i++] = (0xf << 4) | hdmi_slot;
-       }
-
-       if (non_pcm) {
-               for (i = 0; i < ch_alloc->channels; i++)
-                       non_pcm_mapping[i] = (i << 4) | i;
-               for (; i < 8; i++)
-                       non_pcm_mapping[i] = (0xf << 4) | i;
-       }
-
-       for (i = 0; i < 8; i++) {
-               int slotsetup = non_pcm ? non_pcm_mapping[i] : hdmi_channel_mapping[ca][i];
-               int hdmi_slot = slotsetup & 0x0f;
-               int channel = (slotsetup & 0xf0) >> 4;
-               err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot, channel);
-               if (err) {
-                       codec_dbg(codec, "HDMI: channel mapping failed\n");
-                       break;
-               }
-       }
-}
-
-struct channel_map_table {
-       unsigned char map;              /* ALSA API channel map position */
-       int spk_mask;                   /* speaker position bit mask */
-};
-
-static struct channel_map_table map_tables[] = {
-       { SNDRV_CHMAP_FL,       FL },
-       { SNDRV_CHMAP_FR,       FR },
-       { SNDRV_CHMAP_RL,       RL },
-       { SNDRV_CHMAP_RR,       RR },
-       { SNDRV_CHMAP_LFE,      LFE },
-       { SNDRV_CHMAP_FC,       FC },
-       { SNDRV_CHMAP_RLC,      RLC },
-       { SNDRV_CHMAP_RRC,      RRC },
-       { SNDRV_CHMAP_RC,       RC },
-       { SNDRV_CHMAP_FLC,      FLC },
-       { SNDRV_CHMAP_FRC,      FRC },
-       { SNDRV_CHMAP_TFL,      FLH },
-       { SNDRV_CHMAP_TFR,      FRH },
-       { SNDRV_CHMAP_FLW,      FLW },
-       { SNDRV_CHMAP_FRW,      FRW },
-       { SNDRV_CHMAP_TC,       TC },
-       { SNDRV_CHMAP_TFC,      FCH },
-       {} /* terminator */
-};
-
-/* from ALSA API channel position to speaker bit mask */
-static int to_spk_mask(unsigned char c)
-{
-       struct channel_map_table *t = map_tables;
-       for (; t->map; t++) {
-               if (t->map == c)
-                       return t->spk_mask;
-       }
-       return 0;
-}
-
-/* from ALSA API channel position to CEA slot */
-static int to_cea_slot(int ordered_ca, unsigned char pos)
-{
-       int mask = to_spk_mask(pos);
-       int i;
-
-       if (mask) {
-               for (i = 0; i < 8; i++) {
-                       if (channel_allocations[ordered_ca].speakers[7 - i] == mask)
-                               return i;
-               }
-       }
-
-       return -1;
-}
-
-/* from speaker bit mask to ALSA API channel position */
-static int spk_to_chmap(int spk)
-{
-       struct channel_map_table *t = map_tables;
-       for (; t->map; t++) {
-               if (t->spk_mask == spk)
-                       return t->map;
-       }
-       return 0;
-}
-
-/* from CEA slot to ALSA API channel position */
-static int from_cea_slot(int ordered_ca, unsigned char slot)
-{
-       int mask = channel_allocations[ordered_ca].speakers[7 - slot];
-
-       return spk_to_chmap(mask);
-}
-
-/* get the CA index corresponding to the given ALSA API channel map */
-static int hdmi_manual_channel_allocation(int chs, unsigned char *map)
-{
-       int i, spks = 0, spk_mask = 0;
-
-       for (i = 0; i < chs; i++) {
-               int mask = to_spk_mask(map[i]);
-               if (mask) {
-                       spk_mask |= mask;
-                       spks++;
-               }
-       }
-
-       for (i = 0; i < ARRAY_SIZE(channel_allocations); i++) {
-               if ((chs == channel_allocations[i].channels ||
-                    spks == channel_allocations[i].channels) &&
-                   (spk_mask & channel_allocations[i].spk_mask) ==
-                               channel_allocations[i].spk_mask)
-                       return channel_allocations[i].ca_index;
-       }
-       return -1;
-}
-
-/* set up the channel slots for the given ALSA API channel map */
-static int hdmi_manual_setup_channel_mapping(struct hda_codec *codec,
-                                            hda_nid_t pin_nid,
-                                            int chs, unsigned char *map,
-                                            int ca)
-{
-       struct hdmi_spec *spec = codec->spec;
-       int ordered_ca = get_channel_allocation_order(ca);
-       int alsa_pos, hdmi_slot;
-       int assignments[8] = {[0 ... 7] = 0xf};
-
-       for (alsa_pos = 0; alsa_pos < chs; alsa_pos++) {
-
-               hdmi_slot = to_cea_slot(ordered_ca, map[alsa_pos]);
-
-               if (hdmi_slot < 0)
-                       continue; /* unassigned channel */
-
-               assignments[hdmi_slot] = alsa_pos;
-       }
-
-       for (hdmi_slot = 0; hdmi_slot < 8; hdmi_slot++) {
-               int err;
-
-               err = spec->ops.pin_set_slot_channel(codec, pin_nid, hdmi_slot,
-                                                    assignments[hdmi_slot]);
-               if (err)
-                       return -EINVAL;
-       }
-       return 0;
-}
-
-/* store ALSA API channel map from the current default map */
-static void hdmi_setup_fake_chmap(unsigned char *map, int ca)
-{
-       int i;
-       int ordered_ca = get_channel_allocation_order(ca);
-       for (i = 0; i < 8; i++) {
-               if (i < channel_allocations[ordered_ca].channels)
-                       map[i] = from_cea_slot(ordered_ca, hdmi_channel_mapping[ca][i] & 0x0f);
-               else
-                       map[i] = 0;
-       }
-}
-
-static void hdmi_setup_channel_mapping(struct hda_codec *codec,
-                                      hda_nid_t pin_nid, bool non_pcm, int ca,
-                                      int channels, unsigned char *map,
-                                      bool chmap_set)
-{
-       if (!non_pcm && chmap_set) {
-               hdmi_manual_setup_channel_mapping(codec, pin_nid,
-                                                 channels, map, ca);
-       } else {
-               hdmi_std_setup_channel_mapping(codec, pin_nid, non_pcm, ca);
-               hdmi_setup_fake_chmap(map, ca);
-       }
-
-       hdmi_debug_channel_mapping(codec, pin_nid);
-}
-
-static int hdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
-                                    int asp_slot, int channel)
-{
-       return snd_hda_codec_write(codec, pin_nid, 0,
-                                  AC_VERB_SET_HDMI_CHAN_SLOT,
-                                  (channel << 4) | asp_slot);
-}
-
-static int hdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
-                                    int asp_slot)
-{
-       return (snd_hda_codec_read(codec, pin_nid, 0,
-                                  AC_VERB_GET_HDMI_CHAN_SLOT,
-                                  asp_slot) & 0xf0) >> 4;
-}
-
 /*
  * Audio InfoFrame routines
  */
@@ -1132,11 +670,12 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
                                       bool non_pcm)
 {
        struct hdmi_spec *spec = codec->spec;
+       struct hdac_chmap *chmap = &spec->chmap;
        hda_nid_t pin_nid = per_pin->pin_nid;
        int channels = per_pin->channels;
        int active_channels;
        struct hdmi_eld *eld;
-       int ca, ordered_ca;
+       int ca;
 
        if (!channels)
                return;
@@ -1148,25 +687,22 @@ static void hdmi_setup_audio_infoframe(struct hda_codec *codec,
 
        eld = &per_pin->sink_eld;
 
-       if (!non_pcm && per_pin->chmap_set)
-               ca = hdmi_manual_channel_allocation(channels, per_pin->chmap);
-       else
-               ca = hdmi_channel_allocation(codec, eld, channels);
-       if (ca < 0)
-               ca = 0;
+       ca = snd_hdac_channel_allocation(&codec->core,
+                       eld->info.spk_alloc, channels,
+                       per_pin->chmap_set, non_pcm, per_pin->chmap);
 
-       ordered_ca = get_channel_allocation_order(ca);
-       active_channels = channel_allocations[ordered_ca].channels;
+       active_channels = snd_hdac_get_active_channels(ca);
 
-       hdmi_set_channel_count(codec, per_pin->cvt_nid, active_channels);
+       chmap->ops.set_channel_count(&codec->core, per_pin->cvt_nid,
+                                               active_channels);
 
        /*
         * always configure channel mapping, it may have been changed by the
         * user in the meantime
         */
-       hdmi_setup_channel_mapping(codec, pin_nid, non_pcm, ca,
-                                  channels, per_pin->chmap,
-                                  per_pin->chmap_set);
+       snd_hdac_setup_channel_mapping(&spec->chmap,
+                               pin_nid, non_pcm, ca, channels,
+                               per_pin->chmap, per_pin->chmap_set);
 
        spec->ops.pin_setup_infoframe(codec, pin_nid, ca, active_channels,
                                      eld->info.conn_type);
@@ -1338,6 +874,11 @@ static int hdmi_setup_stream(struct hda_codec *codec, hda_nid_t cvt_nid,
        return 0;
 }
 
+/* Try to find an available converter
+ * If pin_idx is less then zero, just try to find an available converter.
+ * Otherwise, try to find an available converter and get the cvt mux index
+ * of the pin.
+ */
 static int hdmi_choose_cvt(struct hda_codec *codec,
                        int pin_idx, int *cvt_id, int *mux_id)
 {
@@ -1346,7 +887,11 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
        struct hdmi_spec_per_cvt *per_cvt = NULL;
        int cvt_idx, mux_idx = 0;
 
-       per_pin = get_pin(spec, pin_idx);
+       /* pin_idx < 0 means no pin will be bound to the converter */
+       if (pin_idx < 0)
+               per_pin = NULL;
+       else
+               per_pin = get_pin(spec, pin_idx);
 
        /* Dynamically assign converter to stream */
        for (cvt_idx = 0; cvt_idx < spec->num_cvts; cvt_idx++) {
@@ -1355,6 +900,8 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
                /* Must not already be assigned */
                if (per_cvt->assigned)
                        continue;
+               if (per_pin == NULL)
+                       break;
                /* Must be in pin's mux's list of converters */
                for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
                        if (per_pin->mux_nids[mux_idx] == per_cvt->cvt_nid)
@@ -1367,9 +914,10 @@ static int hdmi_choose_cvt(struct hda_codec *codec,
 
        /* No free converters */
        if (cvt_idx == spec->num_cvts)
-               return -ENODEV;
+               return -EBUSY;
 
-       per_pin->mux_idx = mux_idx;
+       if (per_pin != NULL)
+               per_pin->mux_idx = mux_idx;
 
        if (cvt_id)
                *cvt_id = cvt_idx;
@@ -1395,6 +943,20 @@ static void intel_verify_pin_cvt_connect(struct hda_codec *codec,
                                            mux_idx);
 }
 
+/* get the mux index for the converter of the pins
+ * converter's mux index is the same for all pins on Intel platform
+ */
+static int intel_cvt_id_to_mux_idx(struct hdmi_spec *spec,
+                       hda_nid_t cvt_nid)
+{
+       int i;
+
+       for (i = 0; i < spec->num_cvts; i++)
+               if (spec->cvt_nids[i] == cvt_nid)
+                       return i;
+       return -EINVAL;
+}
+
 /* Intel HDMI workaround to fix audio routing issue:
  * For some Intel display codecs, pins share the same connection list.
  * So a conveter can be selected by multiple pins and playback on any of these
@@ -1446,6 +1008,74 @@ static void intel_not_share_assigned_cvt(struct hda_codec *codec,
        }
 }
 
+/* A wrapper of intel_not_share_asigned_cvt() */
+static void intel_not_share_assigned_cvt_nid(struct hda_codec *codec,
+                       hda_nid_t pin_nid, hda_nid_t cvt_nid)
+{
+       int mux_idx;
+       struct hdmi_spec *spec = codec->spec;
+
+       if (!is_haswell_plus(codec) && !is_valleyview_plus(codec))
+               return;
+
+       /* On Intel platform, the mapping of converter nid to
+        * mux index of the pins are always the same.
+        * The pin nid may be 0, this means all pins will not
+        * share the converter.
+        */
+       mux_idx = intel_cvt_id_to_mux_idx(spec, cvt_nid);
+       if (mux_idx >= 0)
+               intel_not_share_assigned_cvt(codec, pin_nid, mux_idx);
+}
+
+/* called in hdmi_pcm_open when no pin is assigned to the PCM
+ * in dyn_pcm_assign mode.
+ */
+static int hdmi_pcm_open_no_pin(struct hda_pcm_stream *hinfo,
+                        struct hda_codec *codec,
+                        struct snd_pcm_substream *substream)
+{
+       struct hdmi_spec *spec = codec->spec;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int cvt_idx, pcm_idx;
+       struct hdmi_spec_per_cvt *per_cvt = NULL;
+       int err;
+
+       pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+       if (pcm_idx < 0)
+               return -EINVAL;
+
+       err = hdmi_choose_cvt(codec, -1, &cvt_idx, NULL);
+       if (err)
+               return err;
+
+       per_cvt = get_cvt(spec, cvt_idx);
+       per_cvt->assigned = 1;
+       hinfo->nid = per_cvt->cvt_nid;
+
+       intel_not_share_assigned_cvt_nid(codec, 0, per_cvt->cvt_nid);
+
+       set_bit(pcm_idx, &spec->pcm_in_use);
+       /* todo: setup spdif ctls assign */
+
+       /* Initially set the converter's capabilities */
+       hinfo->channels_min = per_cvt->channels_min;
+       hinfo->channels_max = per_cvt->channels_max;
+       hinfo->rates = per_cvt->rates;
+       hinfo->formats = per_cvt->formats;
+       hinfo->maxbps = per_cvt->maxbps;
+
+       /* Store the updated parameters */
+       runtime->hw.channels_min = hinfo->channels_min;
+       runtime->hw.channels_max = hinfo->channels_max;
+       runtime->hw.formats = hinfo->formats;
+       runtime->hw.rates = hinfo->rates;
+
+       snd_pcm_hw_constraint_step(substream->runtime, 0,
+                                  SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+       return 0;
+}
+
 /*
  * HDA PCM callbacks
  */
@@ -1455,26 +1085,47 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
 {
        struct hdmi_spec *spec = codec->spec;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       int pin_idx, cvt_idx, mux_idx = 0;
+       int pin_idx, cvt_idx, pcm_idx, mux_idx = 0;
        struct hdmi_spec_per_pin *per_pin;
        struct hdmi_eld *eld;
        struct hdmi_spec_per_cvt *per_cvt = NULL;
        int err;
 
        /* Validate hinfo */
-       pin_idx = hinfo_to_pin_index(codec, hinfo);
-       if (snd_BUG_ON(pin_idx < 0))
+       pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+       if (pcm_idx < 0)
                return -EINVAL;
-       per_pin = get_pin(spec, pin_idx);
-       eld = &per_pin->sink_eld;
+
+       mutex_lock(&spec->pcm_lock);
+       pin_idx = hinfo_to_pin_index(codec, hinfo);
+       if (!spec->dyn_pcm_assign) {
+               if (snd_BUG_ON(pin_idx < 0)) {
+                       mutex_unlock(&spec->pcm_lock);
+                       return -EINVAL;
+               }
+       } else {
+               /* no pin is assigned to the PCM
+                * PA need pcm open successfully when probe
+                */
+               if (pin_idx < 0) {
+                       err = hdmi_pcm_open_no_pin(hinfo, codec, substream);
+                       mutex_unlock(&spec->pcm_lock);
+                       return err;
+               }
+       }
 
        err = hdmi_choose_cvt(codec, pin_idx, &cvt_idx, &mux_idx);
-       if (err < 0)
+       if (err < 0) {
+               mutex_unlock(&spec->pcm_lock);
                return err;
+       }
 
        per_cvt = get_cvt(spec, cvt_idx);
        /* Claim converter */
        per_cvt->assigned = 1;
+
+       set_bit(pcm_idx, &spec->pcm_in_use);
+       per_pin = get_pin(spec, pin_idx);
        per_pin->cvt_nid = per_cvt->cvt_nid;
        hinfo->nid = per_cvt->cvt_nid;
 
@@ -1486,7 +1137,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
        if (is_haswell_plus(codec) || is_valleyview_plus(codec))
                intel_not_share_assigned_cvt(codec, per_pin->pin_nid, mux_idx);
 
-       snd_hda_spdif_ctls_assign(codec, pin_idx, per_cvt->cvt_nid);
+       snd_hda_spdif_ctls_assign(codec, pcm_idx, per_cvt->cvt_nid);
 
        /* Initially set the converter's capabilities */
        hinfo->channels_min = per_cvt->channels_min;
@@ -1495,6 +1146,7 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
        hinfo->formats = per_cvt->formats;
        hinfo->maxbps = per_cvt->maxbps;
 
+       eld = &per_pin->sink_eld;
        /* Restrict capabilities by ELD if this isn't disabled */
        if (!static_hdmi_pcm && eld->eld_valid) {
                snd_hdmi_eld_update_pcm_info(&eld->info, hinfo);
@@ -1502,11 +1154,13 @@ static int hdmi_pcm_open(struct hda_pcm_stream *hinfo,
                    !hinfo->rates || !hinfo->formats) {
                        per_cvt->assigned = 0;
                        hinfo->nid = 0;
-                       snd_hda_spdif_ctls_unassign(codec, pin_idx);
+                       snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+                       mutex_unlock(&spec->pcm_lock);
                        return -ENODEV;
                }
        }
 
+       mutex_unlock(&spec->pcm_lock);
        /* Store the updated parameters */
        runtime->hw.channels_min = hinfo->channels_min;
        runtime->hw.channels_max = hinfo->channels_max;
@@ -1541,6 +1195,125 @@ static int hdmi_read_pin_conn(struct hda_codec *codec, int pin_idx)
        return 0;
 }
 
+static int hdmi_find_pcm_slot(struct hdmi_spec *spec,
+                               struct hdmi_spec_per_pin *per_pin)
+{
+       int i;
+
+       /* try the prefer PCM */
+       if (!test_bit(per_pin->pin_nid_idx, &spec->pcm_bitmap))
+               return per_pin->pin_nid_idx;
+
+       /* have a second try; check the "reserved area" over num_pins */
+       for (i = spec->num_pins; i < spec->pcm_used; i++) {
+               if (!test_bit(i, &spec->pcm_bitmap))
+                       return i;
+       }
+
+       /* the last try; check the empty slots in pins */
+       for (i = 0; i < spec->num_pins; i++) {
+               if (!test_bit(i, &spec->pcm_bitmap))
+                       return i;
+       }
+       return -EBUSY;
+}
+
+static void hdmi_attach_hda_pcm(struct hdmi_spec *spec,
+                               struct hdmi_spec_per_pin *per_pin)
+{
+       int idx;
+
+       /* pcm already be attached to the pin */
+       if (per_pin->pcm)
+               return;
+       idx = hdmi_find_pcm_slot(spec, per_pin);
+       if (idx == -EBUSY)
+               return;
+       per_pin->pcm_idx = idx;
+       per_pin->pcm = get_hdmi_pcm(spec, idx);
+       set_bit(idx, &spec->pcm_bitmap);
+}
+
+static void hdmi_detach_hda_pcm(struct hdmi_spec *spec,
+                               struct hdmi_spec_per_pin *per_pin)
+{
+       int idx;
+
+       /* pcm already be detached from the pin */
+       if (!per_pin->pcm)
+               return;
+       idx = per_pin->pcm_idx;
+       per_pin->pcm_idx = -1;
+       per_pin->pcm = NULL;
+       if (idx >= 0 && idx < spec->pcm_used)
+               clear_bit(idx, &spec->pcm_bitmap);
+}
+
+static int hdmi_get_pin_cvt_mux(struct hdmi_spec *spec,
+               struct hdmi_spec_per_pin *per_pin, hda_nid_t cvt_nid)
+{
+       int mux_idx;
+
+       for (mux_idx = 0; mux_idx < per_pin->num_mux_nids; mux_idx++)
+               if (per_pin->mux_nids[mux_idx] == cvt_nid)
+                       break;
+       return mux_idx;
+}
+
+static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid);
+
+static void hdmi_pcm_setup_pin(struct hdmi_spec *spec,
+                          struct hdmi_spec_per_pin *per_pin)
+{
+       struct hda_codec *codec = per_pin->codec;
+       struct hda_pcm *pcm;
+       struct hda_pcm_stream *hinfo;
+       struct snd_pcm_substream *substream;
+       int mux_idx;
+       bool non_pcm;
+
+       if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used)
+               pcm = get_pcm_rec(spec, per_pin->pcm_idx);
+       else
+               return;
+       if (!test_bit(per_pin->pcm_idx, &spec->pcm_in_use))
+               return;
+
+       /* hdmi audio only uses playback and one substream */
+       hinfo = pcm->stream;
+       substream = pcm->pcm->streams[0].substream;
+
+       per_pin->cvt_nid = hinfo->nid;
+
+       mux_idx = hdmi_get_pin_cvt_mux(spec, per_pin, hinfo->nid);
+       if (mux_idx < per_pin->num_mux_nids)
+               snd_hda_codec_write_cache(codec, per_pin->pin_nid, 0,
+                               AC_VERB_SET_CONNECT_SEL,
+                               mux_idx);
+       snd_hda_spdif_ctls_assign(codec, per_pin->pcm_idx, hinfo->nid);
+
+       non_pcm = check_non_pcm_per_cvt(codec, hinfo->nid);
+       if (substream->runtime)
+               per_pin->channels = substream->runtime->channels;
+       per_pin->setup = true;
+       per_pin->mux_idx = mux_idx;
+
+       hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
+}
+
+static void hdmi_pcm_reset_pin(struct hdmi_spec *spec,
+                          struct hdmi_spec_per_pin *per_pin)
+{
+       if (per_pin->pcm_idx >= 0 && per_pin->pcm_idx < spec->pcm_used)
+               snd_hda_spdif_ctls_unassign(per_pin->codec, per_pin->pcm_idx);
+
+       per_pin->chmap_set = false;
+       memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
+
+       per_pin->setup = false;
+       per_pin->channels = 0;
+}
+
 /* update per_pin ELD from the given new ELD;
  * setup info frame and notification accordingly
  */
@@ -1549,8 +1322,27 @@ static void update_eld(struct hda_codec *codec,
                       struct hdmi_eld *eld)
 {
        struct hdmi_eld *pin_eld = &per_pin->sink_eld;
+       struct hdmi_spec *spec = codec->spec;
        bool old_eld_valid = pin_eld->eld_valid;
        bool eld_changed;
+       int pcm_idx = -1;
+
+       /* for monitor disconnection, save pcm_idx firstly */
+       pcm_idx = per_pin->pcm_idx;
+       if (spec->dyn_pcm_assign) {
+               if (eld->eld_valid) {
+                       hdmi_attach_hda_pcm(spec, per_pin);
+                       hdmi_pcm_setup_pin(spec, per_pin);
+               } else {
+                       hdmi_pcm_reset_pin(spec, per_pin);
+                       hdmi_detach_hda_pcm(spec, per_pin);
+               }
+       }
+       /* if pcm_idx == -1, it means this is in monitor connection event
+        * we can get the correct pcm_idx now.
+        */
+       if (pcm_idx == -1)
+               pcm_idx = per_pin->pcm_idx;
 
        if (eld->eld_valid)
                snd_hdmi_show_eld(codec, &eld->info);
@@ -1584,11 +1376,11 @@ static void update_eld(struct hda_codec *codec,
                hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
        }
 
-       if (eld_changed)
+       if (eld_changed && pcm_idx >= 0)
                snd_ctl_notify(codec->card,
                               SNDRV_CTL_EVENT_MASK_VALUE |
                               SNDRV_CTL_EVENT_MASK_INFO,
-                              &per_pin->eld_ctl->id);
+                              &get_hdmi_pcm(spec, pcm_idx)->eld_ctl->id);
 }
 
 /* update ELD and jack state via HD-audio verbs */
@@ -1613,7 +1405,6 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
        bool ret;
        bool do_repoll = false;
 
-       snd_hda_power_up_pm(codec);
        present = snd_hda_pin_sense(codec, pin_nid);
 
        mutex_lock(&per_pin->lock);
@@ -1652,16 +1443,39 @@ static bool hdmi_present_sense_via_verbs(struct hdmi_spec_per_pin *per_pin,
                jack->block_report = !ret;
 
        mutex_unlock(&per_pin->lock);
-       snd_hda_power_down_pm(codec);
        return ret;
 }
 
+static struct snd_jack *pin_idx_to_jack(struct hda_codec *codec,
+                                struct hdmi_spec_per_pin *per_pin)
+{
+       struct hdmi_spec *spec = codec->spec;
+       struct snd_jack *jack = NULL;
+       struct hda_jack_tbl *jack_tbl;
+
+       /* if !dyn_pcm_assign, get jack from hda_jack_tbl
+        * in !dyn_pcm_assign case, spec->pcm_rec[].jack is not
+        * NULL even after snd_hda_jack_tbl_clear() is called to
+        * free snd_jack. This may cause access invalid memory
+        * when calling snd_jack_report
+        */
+       if (per_pin->pcm_idx >= 0 && spec->dyn_pcm_assign)
+               jack = spec->pcm_rec[per_pin->pcm_idx].jack;
+       else if (!spec->dyn_pcm_assign) {
+               jack_tbl = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
+               if (jack_tbl)
+                       jack = jack_tbl->jack;
+       }
+       return jack;
+}
+
 /* update ELD and jack state via audio component */
 static void sync_eld_via_acomp(struct hda_codec *codec,
                               struct hdmi_spec_per_pin *per_pin)
 {
        struct hdmi_spec *spec = codec->spec;
        struct hdmi_eld *eld = &spec->temp_eld;
+       struct snd_jack *jack = NULL;
        int size;
 
        mutex_lock(&per_pin->lock);
@@ -1685,8 +1499,16 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
                eld->eld_size = 0;
        }
 
+       /* pcm_idx >=0 before update_eld() means it is in monitor
+        * disconnected event. Jack must be fetched before update_eld()
+        */
+       jack = pin_idx_to_jack(codec, per_pin);
        update_eld(codec, per_pin, eld);
-       snd_jack_report(per_pin->acomp_jack,
+       if (jack == NULL)
+               jack = pin_idx_to_jack(codec, per_pin);
+       if (jack == NULL)
+               goto unlock;
+       snd_jack_report(jack,
                        eld->monitor_present ? SND_JACK_AVOUT : 0);
  unlock:
        mutex_unlock(&per_pin->lock);
@@ -1695,13 +1517,26 @@ static void sync_eld_via_acomp(struct hda_codec *codec,
 static bool hdmi_present_sense(struct hdmi_spec_per_pin *per_pin, int repoll)
 {
        struct hda_codec *codec = per_pin->codec;
+       struct hdmi_spec *spec = codec->spec;
+       int ret;
+
+       /* no temporary power up/down needed for component notifier */
+       if (!codec_has_acomp(codec))
+               snd_hda_power_up_pm(codec);
 
+       mutex_lock(&spec->pcm_lock);
        if (codec_has_acomp(codec)) {
                sync_eld_via_acomp(codec, per_pin);
-               return false; /* don't call snd_hda_jack_report_sync() */
+               ret = false; /* don't call snd_hda_jack_report_sync() */
        } else {
-               return hdmi_present_sense_via_verbs(per_pin, repoll);
+               ret = hdmi_present_sense_via_verbs(per_pin, repoll);
        }
+       mutex_unlock(&spec->pcm_lock);
+
+       if (!codec_has_acomp(codec))
+               snd_hda_power_down_pm(codec);
+
+       return ret;
 }
 
 static void hdmi_repoll_eld(struct work_struct *work)
@@ -1745,6 +1580,13 @@ static int hdmi_add_pin(struct hda_codec *codec, hda_nid_t pin_nid)
 
        per_pin->pin_nid = pin_nid;
        per_pin->non_pcm = false;
+       if (spec->dyn_pcm_assign)
+               per_pin->pcm_idx = -1;
+       else {
+               per_pin->pcm = get_hdmi_pcm(spec, pin_idx);
+               per_pin->pcm_idx = pin_idx;
+       }
+       per_pin->pin_nid_idx = pin_idx;
 
        err = hdmi_read_pin_conn(codec, pin_idx);
        if (err < 0)
@@ -1773,8 +1615,8 @@ static int hdmi_add_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
        per_cvt->channels_min = 2;
        if (chans <= 16) {
                per_cvt->channels_max = chans;
-               if (chans > spec->channels_max)
-                       spec->channels_max = chans;
+               if (chans > spec->chmap.channels_max)
+                       spec->chmap.channels_max = chans;
        }
 
        err = snd_hda_query_supported_pcm(codec, cvt_nid,
@@ -1851,13 +1693,34 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 {
        hda_nid_t cvt_nid = hinfo->nid;
        struct hdmi_spec *spec = codec->spec;
-       int pin_idx = hinfo_to_pin_index(codec, hinfo);
-       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-       hda_nid_t pin_nid = per_pin->pin_nid;
+       int pin_idx;
+       struct hdmi_spec_per_pin *per_pin;
+       hda_nid_t pin_nid;
        struct snd_pcm_runtime *runtime = substream->runtime;
        bool non_pcm;
        int pinctl;
+       int err;
+
+       mutex_lock(&spec->pcm_lock);
+       pin_idx = hinfo_to_pin_index(codec, hinfo);
+       if (spec->dyn_pcm_assign && pin_idx < 0) {
+               /* when dyn_pcm_assign and pcm is not bound to a pin
+                * skip pin setup and return 0 to make audio playback
+                * be ongoing
+                */
+               intel_not_share_assigned_cvt_nid(codec, 0, cvt_nid);
+               snd_hda_codec_setup_stream(codec, cvt_nid,
+                                       stream_tag, 0, format);
+               mutex_unlock(&spec->pcm_lock);
+               return 0;
+       }
 
+       if (snd_BUG_ON(pin_idx < 0)) {
+               mutex_unlock(&spec->pcm_lock);
+               return -EINVAL;
+       }
+       per_pin = get_pin(spec, pin_idx);
+       pin_nid = per_pin->pin_nid;
        if (is_haswell_plus(codec) || is_valleyview_plus(codec)) {
                /* Verify pin:cvt selections to avoid silent audio after S3.
                 * After S3, the audio driver restores pin:cvt selections
@@ -1882,7 +1745,6 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
 
        hdmi_setup_audio_infoframe(codec, per_pin, non_pcm);
        mutex_unlock(&per_pin->lock);
-
        if (spec->dyn_pin_out) {
                pinctl = snd_hda_codec_read(codec, pin_nid, 0,
                                            AC_VERB_GET_PIN_WIDGET_CONTROL, 0);
@@ -1891,7 +1753,10 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                                    pinctl | PIN_OUT);
        }
 
-       return spec->ops.setup_stream(codec, cvt_nid, pin_nid, stream_tag, format);
+       err = spec->ops.setup_stream(codec, cvt_nid, pin_nid,
+                                stream_tag, format);
+       mutex_unlock(&spec->pcm_lock);
+       return err;
 }
 
 static int generic_hdmi_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
@@ -1907,12 +1772,15 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                          struct snd_pcm_substream *substream)
 {
        struct hdmi_spec *spec = codec->spec;
-       int cvt_idx, pin_idx;
+       int cvt_idx, pin_idx, pcm_idx;
        struct hdmi_spec_per_cvt *per_cvt;
        struct hdmi_spec_per_pin *per_pin;
        int pinctl;
 
        if (hinfo->nid) {
+               pcm_idx = hinfo_to_pcm_index(codec, hinfo);
+               if (snd_BUG_ON(pcm_idx < 0))
+                       return -EINVAL;
                cvt_idx = cvt_nid_to_cvt_index(codec, hinfo->nid);
                if (snd_BUG_ON(cvt_idx < 0))
                        return -EINVAL;
@@ -1922,9 +1790,19 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                per_cvt->assigned = 0;
                hinfo->nid = 0;
 
+               mutex_lock(&spec->pcm_lock);
+               snd_hda_spdif_ctls_unassign(codec, pcm_idx);
+               clear_bit(pcm_idx, &spec->pcm_in_use);
                pin_idx = hinfo_to_pin_index(codec, hinfo);
-               if (snd_BUG_ON(pin_idx < 0))
+               if (spec->dyn_pcm_assign && pin_idx < 0) {
+                       mutex_unlock(&spec->pcm_lock);
+                       return 0;
+               }
+
+               if (snd_BUG_ON(pin_idx < 0)) {
+                       mutex_unlock(&spec->pcm_lock);
                        return -EINVAL;
+               }
                per_pin = get_pin(spec, pin_idx);
 
                if (spec->dyn_pin_out) {
@@ -1935,8 +1813,6 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                                            pinctl & ~PIN_OUT);
                }
 
-               snd_hda_spdif_ctls_unassign(codec, pin_idx);
-
                mutex_lock(&per_pin->lock);
                per_pin->chmap_set = false;
                memset(per_pin->chmap, 0, sizeof(per_pin->chmap));
@@ -1944,6 +1820,7 @@ static int hdmi_pcm_close(struct hda_pcm_stream *hinfo,
                per_pin->setup = false;
                per_pin->channels = 0;
                mutex_unlock(&per_pin->lock);
+               mutex_unlock(&spec->pcm_lock);
        }
 
        return 0;
@@ -1956,162 +1833,42 @@ static const struct hda_pcm_ops generic_ops = {
        .cleanup = generic_hdmi_playback_pcm_cleanup,
 };
 
-/*
- * ALSA API channel-map control callbacks
- */
-static int hdmi_chmap_ctl_info(struct snd_kcontrol *kcontrol,
-                              struct snd_ctl_elem_info *uinfo)
+static void hdmi_get_chmap(struct hdac_device *hdac, int pcm_idx,
+                                       unsigned char *chmap)
 {
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hda_codec *codec = info->private_data;
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
        struct hdmi_spec *spec = codec->spec;
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = spec->channels_max;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = SNDRV_CHMAP_LAST;
-       return 0;
-}
-
-static int hdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
-                                                 int channels)
-{
-       /* If the speaker allocation matches the channel count, it is OK.*/
-       if (cap->channels != channels)
-               return -1;
-
-       /* all channels are remappable freely */
-       return SNDRV_CTL_TLVT_CHMAP_VAR;
-}
-
-static void hdmi_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
-                                       unsigned int *chmap, int channels)
-{
-       int count = 0;
-       int c;
-
-       for (c = 7; c >= 0; c--) {
-               int spk = cap->speakers[c];
-               if (!spk)
-                       continue;
-
-               chmap[count++] = spk_to_chmap(spk);
-       }
+       struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
 
-       WARN_ON(count != channels);
-}
-
-static int hdmi_chmap_ctl_tlv(struct snd_kcontrol *kcontrol, int op_flag,
-                             unsigned int size, unsigned int __user *tlv)
-{
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hda_codec *codec = info->private_data;
-       struct hdmi_spec *spec = codec->spec;
-       unsigned int __user *dst;
-       int chs, count = 0;
+       /* chmap is already set to 0 in caller */
+       if (!per_pin)
+               return;
 
-       if (size < 8)
-               return -ENOMEM;
-       if (put_user(SNDRV_CTL_TLVT_CONTAINER, tlv))
-               return -EFAULT;
-       size -= 8;
-       dst = tlv + 2;
-       for (chs = 2; chs <= spec->channels_max; chs++) {
-               int i;
-               struct cea_channel_speaker_allocation *cap;
-               cap = channel_allocations;
-               for (i = 0; i < ARRAY_SIZE(channel_allocations); i++, cap++) {
-                       int chs_bytes = chs * 4;
-                       int type = spec->ops.chmap_cea_alloc_validate_get_type(cap, chs);
-                       unsigned int tlv_chmap[8];
-
-                       if (type < 0)
-                               continue;
-                       if (size < 8)
-                               return -ENOMEM;
-                       if (put_user(type, dst) ||
-                           put_user(chs_bytes, dst + 1))
-                               return -EFAULT;
-                       dst += 2;
-                       size -= 8;
-                       count += 8;
-                       if (size < chs_bytes)
-                               return -ENOMEM;
-                       size -= chs_bytes;
-                       count += chs_bytes;
-                       spec->ops.cea_alloc_to_tlv_chmap(cap, tlv_chmap, chs);
-                       if (copy_to_user(dst, tlv_chmap, chs_bytes))
-                               return -EFAULT;
-                       dst += chs;
-               }
-       }
-       if (put_user(count, tlv + 1))
-               return -EFAULT;
-       return 0;
+       memcpy(chmap, per_pin->chmap, ARRAY_SIZE(per_pin->chmap));
 }
 
-static int hdmi_chmap_ctl_get(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
+static void hdmi_set_chmap(struct hdac_device *hdac, int pcm_idx,
+                               unsigned char *chmap, int prepared)
 {
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hda_codec *codec = info->private_data;
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
        struct hdmi_spec *spec = codec->spec;
-       int pin_idx = kcontrol->private_value;
-       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-       int i;
-
-       for (i = 0; i < ARRAY_SIZE(per_pin->chmap); i++)
-               ucontrol->value.integer.value[i] = per_pin->chmap[i];
-       return 0;
-}
+       struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
 
-static int hdmi_chmap_ctl_put(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_pcm_chmap *info = snd_kcontrol_chip(kcontrol);
-       struct hda_codec *codec = info->private_data;
-       struct hdmi_spec *spec = codec->spec;
-       int pin_idx = kcontrol->private_value;
-       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-       unsigned int ctl_idx;
-       struct snd_pcm_substream *substream;
-       unsigned char chmap[8];
-       int i, err, ca, prepared = 0;
-
-       ctl_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-       substream = snd_pcm_chmap_substream(info, ctl_idx);
-       if (!substream || !substream->runtime)
-               return 0; /* just for avoiding error from alsactl restore */
-       switch (substream->runtime->status->state) {
-       case SNDRV_PCM_STATE_OPEN:
-       case SNDRV_PCM_STATE_SETUP:
-               break;
-       case SNDRV_PCM_STATE_PREPARED:
-               prepared = 1;
-               break;
-       default:
-               return -EBUSY;
-       }
-       memset(chmap, 0, sizeof(chmap));
-       for (i = 0; i < ARRAY_SIZE(chmap); i++)
-               chmap[i] = ucontrol->value.integer.value[i];
-       if (!memcmp(chmap, per_pin->chmap, sizeof(chmap)))
-               return 0;
-       ca = hdmi_manual_channel_allocation(ARRAY_SIZE(chmap), chmap);
-       if (ca < 0)
-               return -EINVAL;
-       if (spec->ops.chmap_validate) {
-               err = spec->ops.chmap_validate(ca, ARRAY_SIZE(chmap), chmap);
-               if (err)
-                       return err;
-       }
        mutex_lock(&per_pin->lock);
        per_pin->chmap_set = true;
-       memcpy(per_pin->chmap, chmap, sizeof(chmap));
+       memcpy(per_pin->chmap, chmap, ARRAY_SIZE(per_pin->chmap));
        if (prepared)
                hdmi_setup_audio_infoframe(codec, per_pin, per_pin->non_pcm);
        mutex_unlock(&per_pin->lock);
+}
 
-       return 0;
+static bool is_hdmi_pcm_attached(struct hdac_device *hdac, int pcm_idx)
+{
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
+       struct hdmi_spec *spec = codec->spec;
+       struct hdmi_spec_per_pin *per_pin = pcm_idx_to_pin(spec, pcm_idx);
+
+       return per_pin ? true:false;
 }
 
 static int generic_hdmi_build_pcms(struct hda_codec *codec)
@@ -2126,7 +1883,9 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
                info = snd_hda_codec_pcm_new(codec, "HDMI %d", pin_idx);
                if (!info)
                        return -ENOMEM;
-               spec->pcm_rec[pin_idx] = info;
+
+               spec->pcm_rec[pin_idx].pcm = info;
+               spec->pcm_used++;
                info->pcm_type = HDA_PCM_TYPE_HDMI;
                info->own_chmap = true;
 
@@ -2139,15 +1898,16 @@ static int generic_hdmi_build_pcms(struct hda_codec *codec)
        return 0;
 }
 
-static void free_acomp_jack_priv(struct snd_jack *jack)
+static void free_hdmi_jack_priv(struct snd_jack *jack)
 {
-       struct hdmi_spec_per_pin *per_pin = jack->private_data;
+       struct hdmi_pcm *pcm = jack->private_data;
 
-       per_pin->acomp_jack = NULL;
+       pcm->jack = NULL;
 }
 
-static int add_acomp_jack_kctl(struct hda_codec *codec,
-                              struct hdmi_spec_per_pin *per_pin,
+static int add_hdmi_jack_kctl(struct hda_codec *codec,
+                              struct hdmi_spec *spec,
+                              int pcm_idx,
                               const char *name)
 {
        struct snd_jack *jack;
@@ -2157,88 +1917,107 @@ static int add_acomp_jack_kctl(struct hda_codec *codec,
                           true, false);
        if (err < 0)
                return err;
-       per_pin->acomp_jack = jack;
-       jack->private_data = per_pin;
-       jack->private_free = free_acomp_jack_priv;
+
+       spec->pcm_rec[pcm_idx].jack = jack;
+       jack->private_data = &spec->pcm_rec[pcm_idx];
+       jack->private_free = free_hdmi_jack_priv;
        return 0;
 }
 
-static int generic_hdmi_build_jack(struct hda_codec *codec, int pin_idx)
+static int generic_hdmi_build_jack(struct hda_codec *codec, int pcm_idx)
 {
        char hdmi_str[32] = "HDMI/DP";
        struct hdmi_spec *spec = codec->spec;
-       struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-       int pcmdev = get_pcm_rec(spec, pin_idx)->device;
+       struct hdmi_spec_per_pin *per_pin;
+       struct hda_jack_tbl *jack;
+       int pcmdev = get_pcm_rec(spec, pcm_idx)->device;
        bool phantom_jack;
+       int ret;
 
        if (pcmdev > 0)
                sprintf(hdmi_str + strlen(hdmi_str), ",pcm=%d", pcmdev);
-       if (codec_has_acomp(codec))
-               return add_acomp_jack_kctl(codec, per_pin, hdmi_str);
+
+       if (spec->dyn_pcm_assign)
+               return add_hdmi_jack_kctl(codec, spec, pcm_idx, hdmi_str);
+
+       /* for !dyn_pcm_assign, we still use hda_jack for compatibility */
+       /* if !dyn_pcm_assign, it must be non-MST mode.
+        * This means pcms and pins are statically mapped.
+        * And pcm_idx is pin_idx.
+        */
+       per_pin = get_pin(spec, pcm_idx);
        phantom_jack = !is_jack_detectable(codec, per_pin->pin_nid);
        if (phantom_jack)
                strncat(hdmi_str, " Phantom",
                        sizeof(hdmi_str) - strlen(hdmi_str) - 1);
-
-       return snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str,
-                                    phantom_jack);
+       ret = snd_hda_jack_add_kctl(codec, per_pin->pin_nid, hdmi_str,
+                                   phantom_jack);
+       if (ret < 0)
+               return ret;
+       jack = snd_hda_jack_tbl_get(codec, per_pin->pin_nid);
+       if (jack == NULL)
+               return 0;
+       /* assign jack->jack to pcm_rec[].jack to
+        * align with dyn_pcm_assign mode
+        */
+       spec->pcm_rec[pcm_idx].jack = jack->jack;
+       return 0;
 }
 
 static int generic_hdmi_build_controls(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
        int err;
-       int pin_idx;
+       int pin_idx, pcm_idx;
 
-       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
-               struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
 
-               err = generic_hdmi_build_jack(codec, pin_idx);
+       for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+               err = generic_hdmi_build_jack(codec, pcm_idx);
                if (err < 0)
                        return err;
 
-               err = snd_hda_create_dig_out_ctls(codec,
+               /* create the spdif for each pcm
+                * pin will be bound when monitor is connected
+                */
+               if (spec->dyn_pcm_assign)
+                       err = snd_hda_create_dig_out_ctls(codec,
+                                         0, spec->cvt_nids[0],
+                                         HDA_PCM_TYPE_HDMI);
+               else {
+                       struct hdmi_spec_per_pin *per_pin =
+                               get_pin(spec, pcm_idx);
+                       err = snd_hda_create_dig_out_ctls(codec,
                                                  per_pin->pin_nid,
                                                  per_pin->mux_nids[0],
                                                  HDA_PCM_TYPE_HDMI);
+               }
                if (err < 0)
                        return err;
-               snd_hda_spdif_ctls_unassign(codec, pin_idx);
+               snd_hda_spdif_ctls_unassign(codec, pcm_idx);
 
                /* add control for ELD Bytes */
-               err = hdmi_create_eld_ctl(codec, pin_idx,
-                                         get_pcm_rec(spec, pin_idx)->device);
-
+               err = hdmi_create_eld_ctl(codec, pcm_idx,
+                                       get_pcm_rec(spec, pcm_idx)->device);
                if (err < 0)
                        return err;
+       }
+
+       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+               struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
 
                hdmi_present_sense(per_pin, 0);
        }
 
        /* add channel maps */
-       for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
+       for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
                struct hda_pcm *pcm;
-               struct snd_pcm_chmap *chmap;
-               struct snd_kcontrol *kctl;
-               int i;
 
-               pcm = spec->pcm_rec[pin_idx];
+               pcm = get_pcm_rec(spec, pcm_idx);
                if (!pcm || !pcm->pcm)
                        break;
-               err = snd_pcm_add_chmap_ctls(pcm->pcm,
-                                            SNDRV_PCM_STREAM_PLAYBACK,
-                                            NULL, 0, pin_idx, &chmap);
+               err = snd_hdac_add_chmap_ctls(pcm->pcm, pcm_idx, &spec->chmap);
                if (err < 0)
                        return err;
-               /* override handlers */
-               chmap->private_data = codec;
-               kctl = chmap->kctl;
-               for (i = 0; i < kctl->count; i++)
-                       kctl->vd[i].access |= SNDRV_CTL_ELEM_ACCESS_WRITE;
-               kctl->info = hdmi_chmap_ctl_info;
-               kctl->get = hdmi_chmap_ctl_get;
-               kctl->put = hdmi_chmap_ctl_put;
-               kctl->tlv.c = hdmi_chmap_ctl_tlv;
        }
 
        return 0;
@@ -2293,18 +2072,25 @@ static void hdmi_array_free(struct hdmi_spec *spec)
 static void generic_hdmi_free(struct hda_codec *codec)
 {
        struct hdmi_spec *spec = codec->spec;
-       int pin_idx;
+       int pin_idx, pcm_idx;
 
        if (codec_has_acomp(codec))
                snd_hdac_i915_register_notifier(NULL);
 
        for (pin_idx = 0; pin_idx < spec->num_pins; pin_idx++) {
                struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
-
                cancel_delayed_work_sync(&per_pin->work);
                eld_proc_free(per_pin);
-               if (per_pin->acomp_jack)
-                       snd_device_free(codec->card, per_pin->acomp_jack);
+       }
+
+       for (pcm_idx = 0; pcm_idx < spec->pcm_used; pcm_idx++) {
+               if (spec->pcm_rec[pcm_idx].jack == NULL)
+                       continue;
+               if (spec->dyn_pcm_assign)
+                       snd_device_free(codec->card,
+                                       spec->pcm_rec[pcm_idx].jack);
+               else
+                       spec->pcm_rec[pcm_idx].jack = NULL;
        }
 
        if (spec->i915_bound)
@@ -2343,16 +2129,11 @@ static const struct hda_codec_ops generic_hdmi_patch_ops = {
 
 static const struct hdmi_ops generic_standard_hdmi_ops = {
        .pin_get_eld                            = snd_hdmi_get_eld,
-       .pin_get_slot_channel                   = hdmi_pin_get_slot_channel,
-       .pin_set_slot_channel                   = hdmi_pin_set_slot_channel,
        .pin_setup_infoframe                    = hdmi_pin_setup_infoframe,
        .pin_hbr_setup                          = hdmi_pin_hbr_setup,
        .setup_stream                           = hdmi_setup_stream,
-       .chmap_cea_alloc_validate_get_type      = hdmi_chmap_cea_alloc_validate_get_type,
-       .cea_alloc_to_tlv_chmap                 = hdmi_cea_alloc_to_tlv_chmap,
 };
 
-
 static void intel_haswell_fixup_connect_list(struct hda_codec *codec,
                                             hda_nid_t nid)
 {
@@ -2432,6 +2213,10 @@ static void intel_pin_eld_notify(void *audio_ptr, int port)
        struct hda_codec *codec = audio_ptr;
        int pin_nid = port + 0x04;
 
+       /* we assume only from port-B to port-D */
+       if (port < 1 || port > 3)
+               return;
+
        /* skip notification during system suspend (but not in runtime PM);
         * the state will be updated at resume
         */
@@ -2453,12 +2238,20 @@ static int patch_generic_hdmi(struct hda_codec *codec)
                return -ENOMEM;
 
        spec->ops = generic_standard_hdmi_ops;
+       mutex_init(&spec->pcm_lock);
+       snd_hdac_register_chmap_ops(&codec->core, &spec->chmap);
+
+       spec->chmap.ops.get_chmap = hdmi_get_chmap;
+       spec->chmap.ops.set_chmap = hdmi_set_chmap;
+       spec->chmap.ops.is_pcm_attached = is_hdmi_pcm_attached;
+
        codec->spec = spec;
        hdmi_array_init(spec, 4);
 
-       /* Try to bind with i915 for any Intel codecs (if not done yet) */
+       /* Try to bind with i915 for Intel HSW+ codecs (if not done yet) */
        if (!codec_has_acomp(codec) &&
-           (codec->core.vendor_id >> 16) == 0x8086)
+           (codec->core.vendor_id >> 16) == 0x8086 &&
+           is_haswell_plus(codec))
                if (!snd_hdac_i915_init(&codec->bus->core))
                        spec->i915_bound = true;
 
@@ -2496,7 +2289,6 @@ static int patch_generic_hdmi(struct hda_codec *codec)
 
        generic_hdmi_init_per_pins(codec);
 
-       init_channel_allocations();
 
        if (codec_has_acomp(codec)) {
                codec->depop_delay = 0;
@@ -2510,6 +2302,7 @@ static int patch_generic_hdmi(struct hda_codec *codec)
                snd_hdac_i915_register_notifier(&spec->i915_audio_ops);
        }
 
+       WARN_ON(spec->dyn_pcm_assign && !codec_has_acomp(codec));
        return 0;
 }
 
@@ -2532,7 +2325,7 @@ static int simple_playback_build_pcms(struct hda_codec *codec)
        info = snd_hda_codec_pcm_new(codec, "HDMI 0");
        if (!info)
                return -ENOMEM;
-       spec->pcm_rec[0] = info;
+       spec->pcm_rec[0].pcm = info;
        info->pcm_type = HDA_PCM_TYPE_HDMI;
        pstr = &info->stream[SNDRV_PCM_STREAM_PLAYBACK];
        *pstr = spec->pcm_playback;
@@ -3043,16 +2836,22 @@ static int patch_nvhdmi_8ch_7x(struct hda_codec *codec)
  * - 0x10de0015
  * - 0x10de0040
  */
-static int nvhdmi_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
-                                                   int channels)
+static int nvhdmi_chmap_cea_alloc_validate_get_type(struct hdac_chmap *chmap,
+               struct hdac_cea_channel_speaker_allocation *cap, int channels)
 {
        if (cap->ca_index == 0x00 && channels == 2)
                return SNDRV_CTL_TLVT_CHMAP_FIXED;
 
-       return hdmi_chmap_cea_alloc_validate_get_type(cap, channels);
+       /* If the speaker allocation matches the channel count, it is OK. */
+       if (cap->channels != channels)
+               return -1;
+
+       /* all channels are remappable freely */
+       return SNDRV_CTL_TLVT_CHMAP_VAR;
 }
 
-static int nvhdmi_chmap_validate(int ca, int chs, unsigned char *map)
+static int nvhdmi_chmap_validate(struct hdac_chmap *chmap,
+               int ca, int chs, unsigned char *map)
 {
        if (ca == 0x00 && (map[0] != SNDRV_CHMAP_FL || map[1] != SNDRV_CHMAP_FR))
                return -EINVAL;
@@ -3072,9 +2871,9 @@ static int patch_nvhdmi(struct hda_codec *codec)
        spec = codec->spec;
        spec->dyn_pin_out = true;
 
-       spec->ops.chmap_cea_alloc_validate_get_type =
+       spec->chmap.ops.chmap_cea_alloc_validate_get_type =
                nvhdmi_chmap_cea_alloc_validate_get_type;
-       spec->ops.chmap_validate = nvhdmi_chmap_validate;
+       spec->chmap.ops.chmap_validate = nvhdmi_chmap_validate;
 
        return 0;
 }
@@ -3322,16 +3121,17 @@ static int atihdmi_paired_swap_fc_lfe(int pos)
        return pos;
 }
 
-static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
+static int atihdmi_paired_chmap_validate(struct hdac_chmap *chmap,
+                       int ca, int chs, unsigned char *map)
 {
-       struct cea_channel_speaker_allocation *cap;
+       struct hdac_cea_channel_speaker_allocation *cap;
        int i, j;
 
        /* check that only channel pairs need to be remapped on old pre-rev3 ATI/AMD */
 
-       cap = &channel_allocations[get_channel_allocation_order(ca)];
+       cap = snd_hdac_get_ch_alloc_from_ca(ca);
        for (i = 0; i < chs; ++i) {
-               int mask = to_spk_mask(map[i]);
+               int mask = snd_hdac_chmap_to_spk_mask(map[i]);
                bool ok = false;
                bool companion_ok = false;
 
@@ -3347,7 +3147,7 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
                                if (i % 2 == 0 && i + 1 < chs) {
                                        /* even channel, check the odd companion */
                                        int comp_chan_idx = 7 - atihdmi_paired_swap_fc_lfe(j + 1);
-                                       int comp_mask_req = to_spk_mask(map[i+1]);
+                                       int comp_mask_req = snd_hdac_chmap_to_spk_mask(map[i+1]);
                                        int comp_mask_act = cap->speakers[comp_chan_idx];
 
                                        if (comp_mask_req == comp_mask_act)
@@ -3369,9 +3169,10 @@ static int atihdmi_paired_chmap_validate(int ca, int chs, unsigned char *map)
        return 0;
 }
 
-static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
-                                       int hdmi_slot, int stream_channel)
+static int atihdmi_pin_set_slot_channel(struct hdac_device *hdac,
+               hda_nid_t pin_nid, int hdmi_slot, int stream_channel)
 {
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
        int verb;
        int ati_channel_setup = 0;
 
@@ -3404,9 +3205,10 @@ static int atihdmi_pin_set_slot_channel(struct hda_codec *codec, hda_nid_t pin_n
        return snd_hda_codec_write(codec, pin_nid, 0, verb, ati_channel_setup);
 }
 
-static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_nid,
-                                       int asp_slot)
+static int atihdmi_pin_get_slot_channel(struct hdac_device *hdac,
+                               hda_nid_t pin_nid, int asp_slot)
 {
+       struct hda_codec *codec = container_of(hdac, struct hda_codec, core);
        bool was_odd = false;
        int ati_asp_slot = asp_slot;
        int verb;
@@ -3433,8 +3235,10 @@ static int atihdmi_pin_get_slot_channel(struct hda_codec *codec, hda_nid_t pin_n
        return ((ati_channel_setup & 0xf0) >> 4) + !!was_odd;
 }
 
-static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_speaker_allocation *cap,
-                                                           int channels)
+static int atihdmi_paired_chmap_cea_alloc_validate_get_type(
+               struct hdac_chmap *chmap,
+               struct hdac_cea_channel_speaker_allocation *cap,
+               int channels)
 {
        int c;
 
@@ -3461,8 +3265,9 @@ static int atihdmi_paired_chmap_cea_alloc_validate_get_type(struct cea_channel_s
        return SNDRV_CTL_TLVT_CHMAP_PAIRED;
 }
 
-static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_allocation *cap,
-                                                 unsigned int *chmap, int channels)
+static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct hdac_chmap *hchmap,
+               struct hdac_cea_channel_speaker_allocation *cap,
+               unsigned int *chmap, int channels)
 {
        /* produce paired maps for pre-rev3 ATI/AMD codecs */
        int count = 0;
@@ -3479,7 +3284,7 @@ static void atihdmi_paired_cea_alloc_to_tlv_chmap(struct cea_channel_speaker_all
                        continue;
                }
 
-               chmap[count++] = spk_to_chmap(spk);
+               chmap[count++] = snd_hdac_spk_to_chmap(spk);
        }
 
        WARN_ON(count != channels);
@@ -3573,18 +3378,21 @@ static int patch_atihdmi(struct hda_codec *codec)
        spec = codec->spec;
 
        spec->ops.pin_get_eld = atihdmi_pin_get_eld;
-       spec->ops.pin_get_slot_channel = atihdmi_pin_get_slot_channel;
-       spec->ops.pin_set_slot_channel = atihdmi_pin_set_slot_channel;
        spec->ops.pin_setup_infoframe = atihdmi_pin_setup_infoframe;
        spec->ops.pin_hbr_setup = atihdmi_pin_hbr_setup;
        spec->ops.setup_stream = atihdmi_setup_stream;
 
        if (!has_amd_full_remap_support(codec)) {
                /* override to ATI/AMD-specific versions with pairwise mapping */
-               spec->ops.chmap_cea_alloc_validate_get_type =
+               spec->chmap.ops.chmap_cea_alloc_validate_get_type =
                        atihdmi_paired_chmap_cea_alloc_validate_get_type;
-               spec->ops.cea_alloc_to_tlv_chmap = atihdmi_paired_cea_alloc_to_tlv_chmap;
-               spec->ops.chmap_validate = atihdmi_paired_chmap_validate;
+               spec->chmap.ops.cea_alloc_to_tlv_chmap =
+                               atihdmi_paired_cea_alloc_to_tlv_chmap;
+               spec->chmap.ops.chmap_validate = atihdmi_paired_chmap_validate;
+               spec->chmap.ops.pin_get_slot_channel =
+                               atihdmi_pin_get_slot_channel;
+               spec->chmap.ops.pin_set_slot_channel =
+                               atihdmi_pin_set_slot_channel;
        }
 
        /* ATI/AMD converters do not advertise all of their capabilities */
@@ -3596,7 +3404,7 @@ static int patch_atihdmi(struct hda_codec *codec)
                per_cvt->maxbps = max(per_cvt->maxbps, 24u);
        }
 
-       spec->channels_max = max(spec->channels_max, 8u);
+       spec->chmap.channels_max = max(spec->chmap.channels_max, 8u);
 
        return 0;
 }
@@ -3659,6 +3467,7 @@ HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP",     patch_nvhdmi),
 HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP",  patch_nvhdmi),
 HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP",  patch_nvhdmi),
 HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0082, "GPU 82 HDMI/DP",  patch_nvhdmi),
 HDA_CODEC_ENTRY(0x10de0083, "GPU 83 HDMI/DP",  patch_nvhdmi),
 HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI",      patch_nvhdmi_2ch),
 HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP",   patch_via_hdmi),
index 93d2156b62410935f3c0f945f0d5d8a2fe182eba..4f5ca0b9ce27344ac67f6b8800ac031eaa7fc64d 100644 (file)
@@ -5556,6 +5556,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x2226, "ThinkPad X250", ALC292_FIXUP_TPT440_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x2233, "Thinkpad", ALC293_FIXUP_LENOVO_SPK_NOISE),
        SND_PCI_QUIRK(0x17aa, 0x30bb, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
+       SND_PCI_QUIRK(0x17aa, 0x30e2, "ThinkCentre AIO", ALC233_FIXUP_LENOVO_LINE2_MIC_HOTKEY),
        SND_PCI_QUIRK(0x17aa, 0x3902, "Lenovo E50-80", ALC269_FIXUP_DMIC_THINKPAD_ACPI),
        SND_PCI_QUIRK(0x17aa, 0x3977, "IdeaPad S210", ALC283_FIXUP_INT_MIC),
        SND_PCI_QUIRK(0x17aa, 0x3978, "IdeaPad Y410P", ALC269_FIXUP_NO_SHUTUP),
index 0a4ad5feb82e7817f7036f86c973d02b1dfecb9b..59ab6cee1ad8244a320940a45f09109d0423a207 100644 (file)
 static int (*led_set_func)(int, bool);
 static void (*old_vmaster_hook)(void *, int);
 
-static acpi_status acpi_check_cb(acpi_handle handle, u32 lvl, void *context,
-                                void **rv)
-{
-       bool *found = context;
-       *found = true;
-       return AE_OK;
-}
-
 static bool is_thinkpad(struct hda_codec *codec)
 {
-       bool found = false;
-       if (codec->core.subsystem_id >> 16 != 0x17aa)
-               return false;
-       if (ACPI_SUCCESS(acpi_get_devices("LEN0068", acpi_check_cb, &found, NULL)) && found)
-               return true;
-       found = false;
-       return ACPI_SUCCESS(acpi_get_devices("IBM0068", acpi_check_cb, &found, NULL)) && found;
+       return (codec->core.subsystem_id >> 16 == 0x17aa) &&
+              (acpi_dev_present("LEN0068") || acpi_dev_present("IBM0068"));
 }
 
 static void update_tpacpi_mute_led(void *private_data, int enabled)
index 12c2c180e4072755ba17a4826070093669b45723..8151318a69a2fb65fa53f44f07233792338a2011 100644 (file)
@@ -2879,6 +2879,7 @@ static void intel8x0_measure_ac97_clock(struct intel8x0 *chip)
 
 static struct snd_pci_quirk intel8x0_clock_list[] = {
        SND_PCI_QUIRK(0x0e11, 0x008a, "AD1885", 41000),
+       SND_PCI_QUIRK(0x1014, 0x0581, "AD1981B", 48000),
        SND_PCI_QUIRK(0x1028, 0x00be, "AD1885", 44100),
        SND_PCI_QUIRK(0x1028, 0x0177, "AD1980", 48000),
        SND_PCI_QUIRK(0x1028, 0x01ad, "AD1981B", 48000),
index bc81b9f75ed0dd904f8f1eeed2e78a97dbb70a50..25c0ddd3a53bb6219ca41c00bfaef708b374a34b 100644 (file)
@@ -132,7 +132,7 @@ static int mixart_set_pipe_state(struct mixart_mgr *mgr,
        }
 
        if(start) {
-               u32 stat;
+               u32 stat = 0;
 
                group_state.pipe_count = 0; /* in case of start same command once again with pipe_count=0 */
 
index 24a1955b8c2963ad0dfd737868a922fad59c3d71..58fd79ebac2082805662104e8616696166f2e8be 100644 (file)
@@ -726,7 +726,7 @@ int mixart_update_playback_stream_level(struct snd_mixart* chip, int is_aes, int
        int volume[2];
        struct mixart_msg request;
        struct mixart_set_out_stream_level_req set_level;
-       u32 status;
+       u32 status = 0;
        struct mixart_pipe *pipe;
 
        memset(&set_level, 0, sizeof(set_level));
@@ -778,7 +778,7 @@ int mixart_update_capture_stream_level(struct snd_mixart* chip, int is_aes)
        struct mixart_pipe *pipe;
        struct mixart_msg request;
        struct mixart_set_in_audio_level_req set_level;
-       u32 status;
+       u32 status = 0;
 
        if(is_aes) {
                idx = 1;
index 7ea66ee3653ffeec0ca1bbb24aeb29224f06f272..182d92efc7c80166edea6374456df776aa8079e9 100644 (file)
@@ -6,7 +6,7 @@ menuconfig SND_SOC
        tristate "ALSA for SoC audio support"
        select SND_PCM
        select AC97_BUS if SND_SOC_AC97_BUS
-       select SND_JACK if INPUT=y || INPUT=SND
+       select SND_JACK
        select REGMAP_I2C if I2C
        select REGMAP_SPI if SPI_MASTER
        ---help---
index ba8def5665c458842d2f333d35e34c2974b2136e..276897033639b0d719d0d4408e1ffb9ec9efe90d 100644 (file)
@@ -285,7 +285,8 @@ static int atmel_ssc_hw_rule_rate(struct snd_pcm_hw_params *params,
 static int atmel_ssc_startup(struct snd_pcm_substream *substream,
                             struct snd_soc_dai *dai)
 {
-       struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+       struct platform_device *pdev = to_platform_device(dai->dev);
+       struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
        struct atmel_pcm_dma_params *dma_params;
        int dir, dir_mask;
        int ret;
@@ -346,7 +347,8 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
 static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
                               struct snd_soc_dai *dai)
 {
-       struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+       struct platform_device *pdev = to_platform_device(dai->dev);
+       struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
        struct atmel_pcm_dma_params *dma_params;
        int dir, dir_mask;
 
@@ -392,7 +394,8 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
 static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                unsigned int fmt)
 {
-       struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+       struct platform_device *pdev = to_platform_device(cpu_dai->dev);
+       struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
 
        ssc_p->daifmt = fmt;
        return 0;
@@ -404,7 +407,8 @@ static int atmel_ssc_set_dai_fmt(struct snd_soc_dai *cpu_dai,
 static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
        int div_id, int div)
 {
-       struct atmel_ssc_info *ssc_p = &ssc_info[cpu_dai->id];
+       struct platform_device *pdev = to_platform_device(cpu_dai->dev);
+       struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
 
        switch (div_id) {
        case ATMEL_SSC_CMR_DIV:
@@ -445,7 +449,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params,
        struct snd_soc_dai *dai)
 {
-       int id = dai->id;
+       struct platform_device *pdev = to_platform_device(dai->dev);
+       int id = pdev->id;
        struct atmel_ssc_info *ssc_p = &ssc_info[id];
        struct ssc_device *ssc = ssc_p->ssc;
        struct atmel_pcm_dma_params *dma_params;
@@ -772,7 +777,8 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
 static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
                             struct snd_soc_dai *dai)
 {
-       struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+       struct platform_device *pdev = to_platform_device(dai->dev);
+       struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
        struct atmel_pcm_dma_params *dma_params;
        int dir;
 
@@ -795,7 +801,8 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
 static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
                             int cmd, struct snd_soc_dai *dai)
 {
-       struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
+       struct platform_device *pdev = to_platform_device(dai->dev);
+       struct atmel_ssc_info *ssc_p = &ssc_info[pdev->id];
        struct atmel_pcm_dma_params *dma_params;
        int dir;
 
@@ -824,11 +831,12 @@ static int atmel_ssc_trigger(struct snd_pcm_substream *substream,
 static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
 {
        struct atmel_ssc_info *ssc_p;
+       struct platform_device *pdev = to_platform_device(cpu_dai->dev);
 
        if (!cpu_dai->active)
                return 0;
 
-       ssc_p = &ssc_info[cpu_dai->id];
+       ssc_p = &ssc_info[pdev->id];
 
        /* Save the status register before disabling transmit and receive */
        ssc_p->ssc_state.ssc_sr = ssc_readl(ssc_p->ssc->regs, SR);
@@ -852,12 +860,13 @@ static int atmel_ssc_suspend(struct snd_soc_dai *cpu_dai)
 static int atmel_ssc_resume(struct snd_soc_dai *cpu_dai)
 {
        struct atmel_ssc_info *ssc_p;
+       struct platform_device *pdev = to_platform_device(cpu_dai->dev);
        u32 cr;
 
        if (!cpu_dai->active)
                return 0;
 
-       ssc_p = &ssc_info[cpu_dai->id];
+       ssc_p = &ssc_info[pdev->id];
 
        /* restore SSC register settings */
        ssc_writel(ssc_p->ssc->regs, TFMR, ssc_p->ssc_state.ssc_tfmr);
index 3303d5f58082f68b68b535b2bdf7ce8d14cafcc4..1c1f2210387b26e9551959378f6792f18648652e 100644 (file)
@@ -37,6 +37,7 @@
 #include <linux/init.h>
 #include <linux/io.h>
 #include <linux/module.h>
+#include <linux/of_address.h>
 #include <linux/slab.h>
 
 #include <sound/core.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 
-/* Clock registers */
-#define BCM2835_CLK_PCMCTL_REG  0x00
-#define BCM2835_CLK_PCMDIV_REG  0x04
-
-/* Clock register settings */
-#define BCM2835_CLK_PASSWD             (0x5a000000)
-#define BCM2835_CLK_PASSWD_MASK        (0xff000000)
-#define BCM2835_CLK_MASH(v)            ((v) << 9)
-#define BCM2835_CLK_FLIP               BIT(8)
-#define BCM2835_CLK_BUSY               BIT(7)
-#define BCM2835_CLK_KILL               BIT(5)
-#define BCM2835_CLK_ENAB               BIT(4)
-#define BCM2835_CLK_SRC(v)             (v)
-
-#define BCM2835_CLK_SHIFT              (12)
-#define BCM2835_CLK_DIVI(v)            ((v) << BCM2835_CLK_SHIFT)
-#define BCM2835_CLK_DIVF(v)            (v)
-#define BCM2835_CLK_DIVF_MASK          (0xFFF)
-
-enum {
-       BCM2835_CLK_MASH_0 = 0,
-       BCM2835_CLK_MASH_1,
-       BCM2835_CLK_MASH_2,
-       BCM2835_CLK_MASH_3,
-};
-
-enum {
-       BCM2835_CLK_SRC_GND = 0,
-       BCM2835_CLK_SRC_OSC,
-       BCM2835_CLK_SRC_DBG0,
-       BCM2835_CLK_SRC_DBG1,
-       BCM2835_CLK_SRC_PLLA,
-       BCM2835_CLK_SRC_PLLC,
-       BCM2835_CLK_SRC_PLLD,
-       BCM2835_CLK_SRC_HDMI,
-};
-
-/* Most clocks are not useable (freq = 0) */
-static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = {
-       [BCM2835_CLK_SRC_GND]           = 0,
-       [BCM2835_CLK_SRC_OSC]           = 19200000,
-       [BCM2835_CLK_SRC_DBG0]          = 0,
-       [BCM2835_CLK_SRC_DBG1]          = 0,
-       [BCM2835_CLK_SRC_PLLA]          = 0,
-       [BCM2835_CLK_SRC_PLLC]          = 0,
-       [BCM2835_CLK_SRC_PLLD]          = 500000000,
-       [BCM2835_CLK_SRC_HDMI]          = 0,
-};
-
 /* I2S registers */
 #define BCM2835_I2S_CS_A_REG           0x00
 #define BCM2835_I2S_FIFO_A_REG         0x04
@@ -158,10 +110,6 @@ static const unsigned int bcm2835_clk_freq[BCM2835_CLK_SRC_HDMI+1] = {
 #define BCM2835_I2S_INT_RXR            BIT(1)
 #define BCM2835_I2S_INT_TXW            BIT(0)
 
-/* I2S DMA interface */
-/* FIXME: Needs IOMMU support */
-#define BCM2835_VCMMU_SHIFT            (0x7E000000 - 0x20000000)
-
 /* General device struct */
 struct bcm2835_i2s_dev {
        struct device                           *dev;
@@ -169,21 +117,23 @@ struct bcm2835_i2s_dev {
        unsigned int                            fmt;
        unsigned int                            bclk_ratio;
 
-       struct regmap *i2s_regmap;
-       struct regmap *clk_regmap;
+       struct regmap                           *i2s_regmap;
+       struct clk                              *clk;
+       bool                                    clk_prepared;
 };
 
 static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev)
 {
-       /* Start the clock if in master mode */
        unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
 
+       if (dev->clk_prepared)
+               return;
+
        switch (master) {
        case SND_SOC_DAIFMT_CBS_CFS:
        case SND_SOC_DAIFMT_CBS_CFM:
-               regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
-                       BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
-                       BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB);
+               clk_prepare_enable(dev->clk);
+               dev->clk_prepared = true;
                break;
        default:
                break;
@@ -192,28 +142,9 @@ static void bcm2835_i2s_start_clock(struct bcm2835_i2s_dev *dev)
 
 static void bcm2835_i2s_stop_clock(struct bcm2835_i2s_dev *dev)
 {
-       uint32_t clkreg;
-       int timeout = 1000;
-
-       /* Stop clock */
-       regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
-                       BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
-                       BCM2835_CLK_PASSWD);
-
-       /* Wait for the BUSY flag going down */
-       while (--timeout) {
-               regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg);
-               if (!(clkreg & BCM2835_CLK_BUSY))
-                       break;
-       }
-
-       if (!timeout) {
-               /* KILL the clock */
-               dev_err(dev->dev, "I2S clock didn't stop. Kill the clock!\n");
-               regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
-                       BCM2835_CLK_KILL | BCM2835_CLK_PASSWD_MASK,
-                       BCM2835_CLK_KILL | BCM2835_CLK_PASSWD);
-       }
+       if (dev->clk_prepared)
+               clk_disable_unprepare(dev->clk);
+       dev->clk_prepared = false;
 }
 
 static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
@@ -223,8 +154,7 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
        uint32_t syncval;
        uint32_t csreg;
        uint32_t i2s_active_state;
-       uint32_t clkreg;
-       uint32_t clk_active_state;
+       bool clk_was_prepared;
        uint32_t off;
        uint32_t clr;
 
@@ -238,15 +168,10 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
        regmap_read(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, &csreg);
        i2s_active_state = csreg & (BCM2835_I2S_RXON | BCM2835_I2S_TXON);
 
-       regmap_read(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, &clkreg);
-       clk_active_state = clkreg & BCM2835_CLK_ENAB;
-
        /* Start clock if not running */
-       if (!clk_active_state) {
-               regmap_update_bits(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG,
-                       BCM2835_CLK_PASSWD_MASK | BCM2835_CLK_ENAB,
-                       BCM2835_CLK_PASSWD | BCM2835_CLK_ENAB);
-       }
+       clk_was_prepared = dev->clk_prepared;
+       if (!clk_was_prepared)
+               bcm2835_i2s_start_clock(dev);
 
        /* Stop I2S module */
        regmap_update_bits(dev->i2s_regmap, BCM2835_I2S_CS_A_REG, off, 0);
@@ -280,7 +205,7 @@ static void bcm2835_i2s_clear_fifos(struct bcm2835_i2s_dev *dev,
                dev_err(dev->dev, "I2S SYNC error!\n");
 
        /* Stop clock if it was not running before */
-       if (!clk_active_state)
+       if (!clk_was_prepared)
                bcm2835_i2s_stop_clock(dev);
 
        /* Restore I2S state */
@@ -309,19 +234,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
                                 struct snd_soc_dai *dai)
 {
        struct bcm2835_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
-
        unsigned int sampling_rate = params_rate(params);
        unsigned int data_length, data_delay, bclk_ratio;
        unsigned int ch1pos, ch2pos, mode, format;
-       unsigned int mash = BCM2835_CLK_MASH_1;
-       unsigned int divi, divf, target_frequency;
-       int clk_src = -1;
-       unsigned int master = dev->fmt & SND_SOC_DAIFMT_MASTER_MASK;
-       bool bit_master =       (master == SND_SOC_DAIFMT_CBS_CFS
-                                       || master == SND_SOC_DAIFMT_CBS_CFM);
-
-       bool frame_master =     (master == SND_SOC_DAIFMT_CBS_CFS
-                                       || master == SND_SOC_DAIFMT_CBM_CFS);
        uint32_t csreg;
 
        /*
@@ -343,11 +258,9 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S16_LE:
                data_length = 16;
-               bclk_ratio = 40;
                break;
        case SNDRV_PCM_FORMAT_S32_LE:
                data_length = 32;
-               bclk_ratio = 80;
                break;
        default:
                return -EINVAL;
@@ -356,69 +269,12 @@ static int bcm2835_i2s_hw_params(struct snd_pcm_substream *substream,
        /* If bclk_ratio already set, use that one. */
        if (dev->bclk_ratio)
                bclk_ratio = dev->bclk_ratio;
+       else
+               /* otherwise calculate a fitting block ratio */
+               bclk_ratio = 2 * data_length;
 
-       /*
-        * Clock Settings
-        *
-        * The target frequency of the bit clock is
-        *      sampling rate * frame length
-        *
-        * Integer mode:
-        * Sampling rates that are multiples of 8000 kHz
-        * can be driven by the oscillator of 19.2 MHz
-        * with an integer divider as long as the frame length
-        * is an integer divider of 19200000/8000=2400 as set up above.
-        * This is no longer possible if the sampling rate
-        * is too high (e.g. 192 kHz), because the oscillator is too slow.
-        *
-        * MASH mode:
-        * For all other sampling rates, it is not possible to
-        * have an integer divider. Approximate the clock
-        * with the MASH module that induces a slight frequency
-        * variance. To minimize that it is best to have the fastest
-        * clock here. That is PLLD with 500 MHz.
-        */
-       target_frequency = sampling_rate * bclk_ratio;
-       clk_src = BCM2835_CLK_SRC_OSC;
-       mash = BCM2835_CLK_MASH_0;
-
-       if (bcm2835_clk_freq[clk_src] % target_frequency == 0
-                       && bit_master && frame_master) {
-               divi = bcm2835_clk_freq[clk_src] / target_frequency;
-               divf = 0;
-       } else {
-               uint64_t dividend;
-
-               if (!dev->bclk_ratio) {
-                       /*
-                        * Overwrite bclk_ratio, because the
-                        * above trick is not needed or can
-                        * not be used.
-                        */
-                       bclk_ratio = 2 * data_length;
-               }
-
-               target_frequency = sampling_rate * bclk_ratio;
-
-               clk_src = BCM2835_CLK_SRC_PLLD;
-               mash = BCM2835_CLK_MASH_1;
-
-               dividend = bcm2835_clk_freq[clk_src];
-               dividend <<= BCM2835_CLK_SHIFT;
-               do_div(dividend, target_frequency);
-               divi = dividend >> BCM2835_CLK_SHIFT;
-               divf = dividend & BCM2835_CLK_DIVF_MASK;
-       }
-
-       /* Set clock divider */
-       regmap_write(dev->clk_regmap, BCM2835_CLK_PCMDIV_REG, BCM2835_CLK_PASSWD
-                       | BCM2835_CLK_DIVI(divi)
-                       | BCM2835_CLK_DIVF(divf));
-
-       /* Setup clock, but don't start it yet */
-       regmap_write(dev->clk_regmap, BCM2835_CLK_PCMCTL_REG, BCM2835_CLK_PASSWD
-                       | BCM2835_CLK_MASH(mash)
-                       | BCM2835_CLK_SRC(clk_src));
+       /* set target clock rate*/
+       clk_set_rate(dev->clk, sampling_rate * bclk_ratio);
 
        /* Setup the frame format */
        format = BCM2835_I2S_CHEN;
@@ -692,7 +548,7 @@ static const struct snd_soc_dai_ops bcm2835_i2s_dai_ops = {
        .trigger        = bcm2835_i2s_trigger,
        .hw_params      = bcm2835_i2s_hw_params,
        .set_fmt        = bcm2835_i2s_set_dai_fmt,
-       .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio
+       .set_bclk_ratio = bcm2835_i2s_set_dai_bclk_ratio,
 };
 
 static int bcm2835_i2s_dai_probe(struct snd_soc_dai *dai)
@@ -750,34 +606,14 @@ static bool bcm2835_i2s_precious_reg(struct device *dev, unsigned int reg)
        };
 }
 
-static bool bcm2835_clk_volatile_reg(struct device *dev, unsigned int reg)
-{
-       switch (reg) {
-       case BCM2835_CLK_PCMCTL_REG:
-               return true;
-       default:
-               return false;
-       };
-}
-
-static const struct regmap_config bcm2835_regmap_config[] = {
-       {
-               .reg_bits = 32,
-               .reg_stride = 4,
-               .val_bits = 32,
-               .max_register = BCM2835_I2S_GRAY_REG,
-               .precious_reg = bcm2835_i2s_precious_reg,
-               .volatile_reg = bcm2835_i2s_volatile_reg,
-               .cache_type = REGCACHE_RBTREE,
-       },
-       {
-               .reg_bits = 32,
-               .reg_stride = 4,
-               .val_bits = 32,
-               .max_register = BCM2835_CLK_PCMDIV_REG,
-               .volatile_reg = bcm2835_clk_volatile_reg,
-               .cache_type = REGCACHE_RBTREE,
-       },
+static const struct regmap_config bcm2835_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = BCM2835_I2S_GRAY_REG,
+       .precious_reg = bcm2835_i2s_precious_reg,
+       .volatile_reg = bcm2835_i2s_volatile_reg,
+       .cache_type = REGCACHE_RBTREE,
 };
 
 static const struct snd_soc_component_driver bcm2835_i2s_component = {
@@ -787,42 +623,50 @@ static const struct snd_soc_component_driver bcm2835_i2s_component = {
 static int bcm2835_i2s_probe(struct platform_device *pdev)
 {
        struct bcm2835_i2s_dev *dev;
-       int i;
        int ret;
-       struct regmap *regmap[2];
-       struct resource *mem[2];
-
-       /* Request both ioareas */
-       for (i = 0; i <= 1; i++) {
-               void __iomem *base;
-
-               mem[i] = platform_get_resource(pdev, IORESOURCE_MEM, i);
-               base = devm_ioremap_resource(&pdev->dev, mem[i]);
-               if (IS_ERR(base))
-                       return PTR_ERR(base);
-
-               regmap[i] = devm_regmap_init_mmio(&pdev->dev, base,
-                                           &bcm2835_regmap_config[i]);
-               if (IS_ERR(regmap[i]))
-                       return PTR_ERR(regmap[i]);
-       }
+       struct resource *mem;
+       void __iomem *base;
+       const __be32 *addr;
+       dma_addr_t dma_base;
 
        dev = devm_kzalloc(&pdev->dev, sizeof(*dev),
                           GFP_KERNEL);
        if (!dev)
                return -ENOMEM;
 
-       dev->i2s_regmap = regmap[0];
-       dev->clk_regmap = regmap[1];
+       /* get the clock */
+       dev->clk_prepared = false;
+       dev->clk = devm_clk_get(&pdev->dev, NULL);
+       if (IS_ERR(dev->clk)) {
+               dev_err(&pdev->dev, "could not get clk: %ld\n",
+                       PTR_ERR(dev->clk));
+               return PTR_ERR(dev->clk);
+       }
+
+       /* Request ioarea */
+       mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, mem);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       dev->i2s_regmap = devm_regmap_init_mmio(&pdev->dev, base,
+                               &bcm2835_regmap_config);
+       if (IS_ERR(dev->i2s_regmap))
+               return PTR_ERR(dev->i2s_regmap);
+
+       /* Set the DMA address - we have to parse DT ourselves */
+       addr = of_get_address(pdev->dev.of_node, 0, NULL, NULL);
+       if (!addr) {
+               dev_err(&pdev->dev, "could not get DMA-register address\n");
+               return -EINVAL;
+       }
+       dma_base = be32_to_cpup(addr);
 
-       /* Set the DMA address */
        dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr =
-               (dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG
-                                         + BCM2835_VCMMU_SHIFT;
+               dma_base + BCM2835_I2S_FIFO_A_REG;
 
        dev->dma_data[SNDRV_PCM_STREAM_CAPTURE].addr =
-               (dma_addr_t)mem[0]->start + BCM2835_I2S_FIFO_A_REG
-                                         + BCM2835_VCMMU_SHIFT;
+               dma_base + BCM2835_I2S_FIFO_A_REG;
 
        /* Set the bus width */
        dev->dma_data[SNDRV_PCM_STREAM_PLAYBACK].addr_width =
index 50693c867e71d06a2b6ccebf0d2f303c1ef9a0f5..649e92a252aec7699351d02d87f96f6965c4190a 100644 (file)
@@ -79,7 +79,9 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_MAX98090 if I2C
        select SND_SOC_MAX98095 if I2C
        select SND_SOC_MAX98357A if GPIOLIB
+       select SND_SOC_MAX9867 if I2C
        select SND_SOC_MAX98925 if I2C
+       select SND_SOC_MAX98926 if I2C
        select SND_SOC_MAX9850 if I2C
        select SND_SOC_MAX9768 if I2C
        select SND_SOC_MAX9877 if I2C
@@ -87,7 +89,8 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_ML26124 if I2C
        select SND_SOC_NAU8825 if I2C
        select SND_SOC_PCM1681 if I2C
-       select SND_SOC_PCM179X if SPI_MASTER
+       select SND_SOC_PCM179X_I2C if I2C
+       select SND_SOC_PCM179X_SPI if SPI_MASTER
        select SND_SOC_PCM3008
        select SND_SOC_PCM3168A_I2C if I2C
        select SND_SOC_PCM3168A_SPI if SPI_MASTER
@@ -95,6 +98,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_PCM512x_SPI if SPI_MASTER
        select SND_SOC_RT286 if I2C
        select SND_SOC_RT298 if I2C
+       select SND_SOC_RT5514 if I2C
        select SND_SOC_RT5616 if I2C
        select SND_SOC_RT5631 if I2C
        select SND_SOC_RT5640 if I2C
@@ -490,6 +494,7 @@ config SND_SOC_GTM601
 config SND_SOC_HDAC_HDMI
        tristate
        select SND_HDA_EXT_CORE
+       select SND_PCM_ELD
        select HDMI
 
 config SND_SOC_ICS43432
@@ -497,6 +502,7 @@ config SND_SOC_ICS43432
 
 config SND_SOC_INNO_RK3036
        tristate "Inno codec driver for RK3036 SoC"
+       select REGMAP_MMIO
 
 config SND_SOC_ISABELLE
         tristate
@@ -516,9 +522,15 @@ config SND_SOC_MAX98095
 config SND_SOC_MAX98357A
        tristate
 
+config SND_SOC_MAX9867
+       tristate
+
 config SND_SOC_MAX98925
        tristate
 
+config SND_SOC_MAX98926
+       tristate
+
 config SND_SOC_MAX9850
        tristate
 
@@ -527,8 +539,23 @@ config SND_SOC_PCM1681
        depends on I2C
 
 config SND_SOC_PCM179X
-       tristate "Texas Instruments PCM179X CODEC"
+       tristate
+
+config SND_SOC_PCM179X_I2C
+       tristate "Texas Instruments PCM179X CODEC (I2C)"
+       depends on I2C
+       select SND_SOC_PCM179X
+       help
+         Enable support for Texas Instruments PCM179x CODEC.
+         Select this if your PCM179x is connected via an I2C bus.
+
+config SND_SOC_PCM179X_SPI
+       tristate "Texas Instruments PCM179X CODEC (SPI)"
        depends on SPI_MASTER
+       select SND_SOC_PCM179X
+       help
+         Enable support for Texas Instruments PCM179x CODEC.
+         Select this if your PCM179x is connected via an SPI bus.
 
 config SND_SOC_PCM3008
        tristate
@@ -565,6 +592,7 @@ config SND_SOC_PCM512x_SPI
 
 config SND_SOC_RL6231
        tristate
+       default y if SND_SOC_RT5514=y
        default y if SND_SOC_RT5616=y
        default y if SND_SOC_RT5640=y
        default y if SND_SOC_RT5645=y
@@ -572,6 +600,7 @@ config SND_SOC_RL6231
        default y if SND_SOC_RT5659=y
        default y if SND_SOC_RT5670=y
        default y if SND_SOC_RT5677=y
+       default m if SND_SOC_RT5514=m
        default m if SND_SOC_RT5616=m
        default m if SND_SOC_RT5640=m
        default m if SND_SOC_RT5645=m
@@ -595,9 +624,12 @@ config SND_SOC_RT298
        tristate
        depends on I2C
 
-config SND_SOC_RT5616
+config SND_SOC_RT5514
        tristate
 
+config SND_SOC_RT5616
+       tristate "Realtek RT5616 CODEC"
+
 config SND_SOC_RT5631
        tristate "Realtek ALC5631/RT5631 CODEC"
        depends on I2C
index d44f7d347183cbe9a0aca29826b9c6477c52d6c4..185a712a7fe763bbba79fa1f4969cf5ba5424753 100644 (file)
@@ -74,13 +74,17 @@ snd-soc-max98088-objs := max98088.o
 snd-soc-max98090-objs := max98090.o
 snd-soc-max98095-objs := max98095.o
 snd-soc-max98357a-objs := max98357a.o
+snd-soc-max9867-objs := max9867.o
 snd-soc-max98925-objs := max98925.o
+snd-soc-max98926-objs := max98926.o
 snd-soc-max9850-objs := max9850.o
 snd-soc-mc13783-objs := mc13783.o
 snd-soc-ml26124-objs := ml26124.o
 snd-soc-nau8825-objs := nau8825.o
 snd-soc-pcm1681-objs := pcm1681.o
 snd-soc-pcm179x-codec-objs := pcm179x.o
+snd-soc-pcm179x-i2c-objs := pcm179x-i2c.o
+snd-soc-pcm179x-spi-objs := pcm179x-spi.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-pcm3168a-objs := pcm3168a.o
 snd-soc-pcm3168a-i2c-objs := pcm3168a-i2c.o
@@ -92,6 +96,7 @@ snd-soc-rl6231-objs := rl6231.o
 snd-soc-rl6347a-objs := rl6347a.o
 snd-soc-rt286-objs := rt286.o
 snd-soc-rt298-objs := rt298.o
+snd-soc-rt5514-objs := rt5514.o
 snd-soc-rt5616-objs := rt5616.o
 snd-soc-rt5631-objs := rt5631.o
 snd-soc-rt5640-objs := rt5640.o
@@ -278,13 +283,17 @@ obj-$(CONFIG_SND_SOC_MAX98088)    += snd-soc-max98088.o
 obj-$(CONFIG_SND_SOC_MAX98090) += snd-soc-max98090.o
 obj-$(CONFIG_SND_SOC_MAX98095) += snd-soc-max98095.o
 obj-$(CONFIG_SND_SOC_MAX98357A)        += snd-soc-max98357a.o
+obj-$(CONFIG_SND_SOC_MAX9867)  += snd-soc-max9867.o
 obj-$(CONFIG_SND_SOC_MAX98925) += snd-soc-max98925.o
+obj-$(CONFIG_SND_SOC_MAX98926) += snd-soc-max98926.o
 obj-$(CONFIG_SND_SOC_MAX9850)  += snd-soc-max9850.o
 obj-$(CONFIG_SND_SOC_MC13783)  += snd-soc-mc13783.o
 obj-$(CONFIG_SND_SOC_ML26124)  += snd-soc-ml26124.o
 obj-$(CONFIG_SND_SOC_NAU8825)   += snd-soc-nau8825.o
 obj-$(CONFIG_SND_SOC_PCM1681)  += snd-soc-pcm1681.o
 obj-$(CONFIG_SND_SOC_PCM179X)  += snd-soc-pcm179x-codec.o
+obj-$(CONFIG_SND_SOC_PCM179X_I2C)      += snd-soc-pcm179x-i2c.o
+obj-$(CONFIG_SND_SOC_PCM179X_SPI)      += snd-soc-pcm179x-spi.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_PCM3168A) += snd-soc-pcm3168a.o
 obj-$(CONFIG_SND_SOC_PCM3168A_I2C)     += snd-soc-pcm3168a-i2c.o
@@ -296,6 +305,7 @@ obj-$(CONFIG_SND_SOC_RL6231)        += snd-soc-rl6231.o
 obj-$(CONFIG_SND_SOC_RL6347A)  += snd-soc-rl6347a.o
 obj-$(CONFIG_SND_SOC_RT286)    += snd-soc-rt286.o
 obj-$(CONFIG_SND_SOC_RT298)    += snd-soc-rt298.o
+obj-$(CONFIG_SND_SOC_RT5514)   += snd-soc-rt5514.o
 obj-$(CONFIG_SND_SOC_RT5616)   += snd-soc-rt5616.o
 obj-$(CONFIG_SND_SOC_RT5631)   += snd-soc-rt5631.o
 obj-$(CONFIG_SND_SOC_RT5640)   += snd-soc-rt5640.o
index faae6936bae4f55851e4f0a7062f03fdebf674ee..8b1d0c1a783982f77318a6482e5639277abd12a8 100644 (file)
@@ -2134,7 +2134,6 @@ static int ab8500_codec_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
                        "%s: ERROR: Unsupporter master mask 0x%x\n",
                        __func__, fmt & SND_SOC_DAIFMT_MASTER_MASK);
                return -EINVAL;
-               break;
        }
 
        snd_soc_update_bits(codec, AB8500_DIGIFCONF3, mask, val);
index 348ccb17d3cc9c435e50cd11804e7dfb597906cd..8de010f758cd8d5007cf661a590f6e7c473da385 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
  *
  * Copyright 2014 Analog Devices Inc.
  *  Author: Lars-Peter Clausen <lars@metafoo.de>
@@ -44,9 +44,21 @@ static const struct i2c_device_id adau1761_i2c_ids[] = {
 };
 MODULE_DEVICE_TABLE(i2c, adau1761_i2c_ids);
 
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1761_i2c_dt_ids[] = {
+       { .compatible = "adi,adau1361", },
+       { .compatible = "adi,adau1461", },
+       { .compatible = "adi,adau1761", },
+       { .compatible = "adi,adau1961", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, adau1761_i2c_dt_ids);
+#endif
+
 static struct i2c_driver adau1761_i2c_driver = {
        .driver = {
                .name = "adau1761",
+               .of_match_table = of_match_ptr(adau1761_i2c_dt_ids),
        },
        .probe = adau1761_i2c_probe,
        .remove = adau1761_i2c_remove,
index 8bc1fbd25fccccd1c31c66f7a0bcb31eff07af01..d9171245bd9f11a177ce2429c0cbf50ef159af9d 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
  *
  * Copyright 2014 Analog Devices Inc.
  *  Author: Lars-Peter Clausen <lars@metafoo.de>
@@ -61,9 +61,21 @@ static const struct spi_device_id adau1761_spi_id[] = {
 };
 MODULE_DEVICE_TABLE(spi, adau1761_spi_id);
 
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1761_spi_dt_ids[] = {
+       { .compatible = "adi,adau1361", },
+       { .compatible = "adi,adau1461", },
+       { .compatible = "adi,adau1761", },
+       { .compatible = "adi,adau1961", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, adau1761_spi_dt_ids);
+#endif
+
 static struct spi_driver adau1761_spi_driver = {
        .driver = {
                .name = "adau1761",
+               .of_match_table = of_match_ptr(adau1761_spi_dt_ids),
        },
        .probe = adau1761_spi_probe,
        .remove = adau1761_spi_remove,
index 2f12477e539ebe36f90af0218ee83031b0ac178d..b95d29dbd13dcfe6fb00722bfc980122e7b74286 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Driver for ADAU1761/ADAU1461/ADAU1761/ADAU1961 codec
+ * Driver for ADAU1361/ADAU1461/ADAU1761/ADAU1961 codec
  *
  * Copyright 2011-2013 Analog Devices Inc.
  * Author: Lars-Peter Clausen <lars@metafoo.de>
@@ -456,13 +456,17 @@ static int adau1761_set_bias_level(struct snd_soc_codec *codec,
        case SND_SOC_BIAS_PREPARE:
                break;
        case SND_SOC_BIAS_STANDBY:
+               regcache_cache_only(adau->regmap, false);
                regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
                        ADAU17X1_CLOCK_CONTROL_SYSCLK_EN,
                        ADAU17X1_CLOCK_CONTROL_SYSCLK_EN);
+               if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+                       regcache_sync(adau->regmap);
                break;
        case SND_SOC_BIAS_OFF:
                regmap_update_bits(adau->regmap, ADAU17X1_CLOCK_CONTROL,
                        ADAU17X1_CLOCK_CONTROL_SYSCLK_EN, 0);
+               regcache_cache_only(adau->regmap, true);
                break;
 
        }
@@ -783,6 +787,10 @@ int adau1761_probe(struct device *dev, struct regmap *regmap,
        if (ret)
                return ret;
 
+       /* Enable cache only mode as we could miss writes before bias level
+        * reaches standby and the core clock is enabled */
+       regcache_cache_only(regmap, true);
+
        return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);
 }
 EXPORT_SYMBOL_GPL(adau1761_probe);
index 0e32bba92339b17182980c426818c0b58d2c21a7..06cbca84cf02150ab0a83bbf114955ffe6ea62d1 100644 (file)
@@ -42,9 +42,19 @@ static const struct i2c_device_id adau1781_i2c_ids[] = {
 };
 MODULE_DEVICE_TABLE(i2c, adau1781_i2c_ids);
 
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1781_i2c_dt_ids[] = {
+       { .compatible = "adi,adau1381", },
+       { .compatible = "adi,adau1781", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, adau1781_i2c_dt_ids);
+#endif
+
 static struct i2c_driver adau1781_i2c_driver = {
        .driver = {
                .name = "adau1781",
+               .of_match_table = of_match_ptr(adau1781_i2c_dt_ids),
        },
        .probe = adau1781_i2c_probe,
        .remove = adau1781_i2c_remove,
index 33a73ff78de4d2a980146c78b6374f67c0cabb80..3d965a01b99cee318da30a080baebc8a060852cc 100644 (file)
@@ -59,9 +59,19 @@ static const struct spi_device_id adau1781_spi_id[] = {
 };
 MODULE_DEVICE_TABLE(spi, adau1781_spi_id);
 
+#if defined(CONFIG_OF)
+static const struct of_device_id adau1781_spi_dt_ids[] = {
+       { .compatible = "adi,adau1381", },
+       { .compatible = "adi,adau1781", },
+       { },
+};
+MODULE_DEVICE_TABLE(of, adau1781_spi_dt_ids);
+#endif
+
 static struct spi_driver adau1781_spi_driver = {
        .driver = {
                .name = "adau1781",
+               .of_match_table = of_match_ptr(adau1781_spi_dt_ids),
        },
        .probe = adau1781_spi_probe,
        .remove = adau1781_spi_remove,
index fde9068550a6801b6ffbb538fcec13ccacc24f5f..bc1bb56dae63cd1101ca6b9e6823af9d1b01a0ba 100644 (file)
@@ -1,5 +1,5 @@
 /*
- * Driver for ADAU1781/ADAU1781 codec
+ * Driver for ADAU1381/ADAU1781 codec
  *
  * Copyright 2011-2013 Analog Devices Inc.
  * Author: Lars-Peter Clausen <lars@metafoo.de>
index 1222282e93c3ebd769605bdc177ccec32cbbe204..c5be1bdc2c9abfbeb61082e118fa2971db1258af 100644 (file)
@@ -20,6 +20,8 @@
 #include <sound/initval.h>
 #include <sound/soc.h>
 
+#include <linux/of.h>
+
 #define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
 #define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
 
@@ -75,9 +77,19 @@ static int ads117x_remove(struct platform_device *pdev)
        return 0;
 }
 
+#if defined(CONFIG_OF)
+static const struct of_device_id ads117x_dt_ids[] = {
+       { .compatible = "ti,ads1174" },
+       { .compatible = "ti,ads1178" },
+       { },
+};
+MODULE_DEVICE_TABLE(of, ads117x_dt_ids);
+#endif
+
 static struct platform_driver ads117x_codec_driver = {
        .driver = {
                        .name = "ads117x-codec",
+                       .of_match_table = of_match_ptr(ads117x_dt_ids),
        },
 
        .probe = ads117x_probe,
index 91785318b2834f325b2ebfb55476ce591b5e377b..92d22a018d68bf13bf57def12d6c2c82e1e9d933 100644 (file)
@@ -1398,29 +1398,6 @@ static const int arizona_48k_bclk_rates[] = {
        24576000,
 };
 
-static const unsigned int arizona_48k_rates[] = {
-       12000,
-       24000,
-       48000,
-       96000,
-       192000,
-       384000,
-       768000,
-       4000,
-       8000,
-       16000,
-       32000,
-       64000,
-       128000,
-       256000,
-       512000,
-};
-
-static const struct snd_pcm_hw_constraint_list arizona_48k_constraint = {
-       .count  = ARRAY_SIZE(arizona_48k_rates),
-       .list   = arizona_48k_rates,
-};
-
 static const int arizona_44k1_bclk_rates[] = {
        -1,
        44100,
@@ -1443,22 +1420,7 @@ static const int arizona_44k1_bclk_rates[] = {
        22579200,
 };
 
-static const unsigned int arizona_44k1_rates[] = {
-       11025,
-       22050,
-       44100,
-       88200,
-       176400,
-       352800,
-       705600,
-};
-
-static const struct snd_pcm_hw_constraint_list arizona_44k1_constraint = {
-       .count  = ARRAY_SIZE(arizona_44k1_rates),
-       .list   = arizona_44k1_rates,
-};
-
-static int arizona_sr_vals[] = {
+static const unsigned int arizona_sr_vals[] = {
        0,
        12000,
        24000,
@@ -1485,13 +1447,21 @@ static int arizona_sr_vals[] = {
        512000,
 };
 
+#define ARIZONA_48K_RATE_MASK  0x0F003E
+#define ARIZONA_44K1_RATE_MASK 0x003E00
+#define ARIZONA_RATE_MASK      (ARIZONA_48K_RATE_MASK | ARIZONA_44K1_RATE_MASK)
+
+static const struct snd_pcm_hw_constraint_list arizona_constraint = {
+       .count  = ARRAY_SIZE(arizona_sr_vals),
+       .list   = arizona_sr_vals,
+};
+
 static int arizona_startup(struct snd_pcm_substream *substream,
                           struct snd_soc_dai *dai)
 {
        struct snd_soc_codec *codec = dai->codec;
        struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
        struct arizona_dai_priv *dai_priv = &priv->dai[dai->id - 1];
-       const struct snd_pcm_hw_constraint_list *constraint;
        unsigned int base_rate;
 
        if (!substream->runtime)
@@ -1509,16 +1479,15 @@ static int arizona_startup(struct snd_pcm_substream *substream,
        }
 
        if (base_rate == 0)
-               return 0;
-
-       if (base_rate % 8000)
-               constraint = &arizona_44k1_constraint;
+               dai_priv->constraint.mask = ARIZONA_RATE_MASK;
+       else if (base_rate % 8000)
+               dai_priv->constraint.mask = ARIZONA_44K1_RATE_MASK;
        else
-               constraint = &arizona_48k_constraint;
+               dai_priv->constraint.mask = ARIZONA_48K_RATE_MASK;
 
        return snd_pcm_hw_constraint_list(substream->runtime, 0,
                                          SNDRV_PCM_HW_PARAM_RATE,
-                                         constraint);
+                                         &dai_priv->constraint);
 }
 
 static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
@@ -1911,6 +1880,7 @@ int arizona_init_dai(struct arizona_priv *priv, int id)
        struct arizona_dai_priv *dai_priv = &priv->dai[id];
 
        dai_priv->clk = ARIZONA_CLK_SYSCLK;
+       dai_priv->constraint = arizona_constraint;
 
        return 0;
 }
@@ -2179,11 +2149,12 @@ static int arizona_calc_fll(struct arizona_fll *fll,
                return -EINVAL;
        }
 
-       arizona_fll_dbg(fll, "N=%x THETA=%x LAMBDA=%x\n",
+       arizona_fll_dbg(fll, "N=%d THETA=%d LAMBDA=%d\n",
                        cfg->n, cfg->theta, cfg->lambda);
-       arizona_fll_dbg(fll, "FRATIO=%x(%d) OUTDIV=%x REFCLK_DIV=%x\n",
-                       cfg->fratio, cfg->fratio, cfg->outdiv, cfg->refdiv);
-       arizona_fll_dbg(fll, "GAIN=%d\n", cfg->gain);
+       arizona_fll_dbg(fll, "FRATIO=0x%x(%d) OUTDIV=%d REFCLK_DIV=0x%x(%d)\n",
+                       cfg->fratio, ratio, cfg->outdiv,
+                       cfg->refdiv, 1 << cfg->refdiv);
+       arizona_fll_dbg(fll, "GAIN=0x%x(%d)\n", cfg->gain, 1 << cfg->gain);
 
        return 0;
 
index 8b6adb5419bb896482139426167752a22aa4df57..1ea8e4ecf8d41bbf9d3a792f4d88cf66383052a4 100644 (file)
@@ -57,7 +57,7 @@
 #define ARIZONA_CLK_98MHZ  5
 #define ARIZONA_CLK_147MHZ 6
 
-#define ARIZONA_MAX_DAI  8
+#define ARIZONA_MAX_DAI  10
 #define ARIZONA_MAX_ADSP 4
 
 #define ARIZONA_DVFS_SR1_RQ    0x001
@@ -68,6 +68,8 @@ struct wm_adsp;
 
 struct arizona_dai_priv {
        int clk;
+
+       struct snd_pcm_hw_constraint_list constraint;
 };
 
 struct arizona_priv {
index d562e1b9a5d163bb22e990280b8f2e396e584846..1179101b2b055a639e711049dc389d34e999a011 100644 (file)
@@ -44,6 +44,7 @@ struct cs42xx8_priv {
 
        bool slave_mode;
        unsigned long sysclk;
+       u32 tx_channels;
 };
 
 /* -127.5dB to 0dB with step of 0.5dB */
@@ -257,6 +258,9 @@ static int cs42xx8_hw_params(struct snd_pcm_substream *substream,
        u32 ratio = cs42xx8->sysclk / params_rate(params);
        u32 i, fm, val, mask;
 
+       if (tx)
+               cs42xx8->tx_channels = params_channels(params);
+
        for (i = 0; i < ARRAY_SIZE(cs42xx8_ratios); i++) {
                if (cs42xx8_ratios[i].ratio == ratio)
                        break;
@@ -283,9 +287,11 @@ static int cs42xx8_digital_mute(struct snd_soc_dai *dai, int mute)
 {
        struct snd_soc_codec *codec = dai->codec;
        struct cs42xx8_priv *cs42xx8 = snd_soc_codec_get_drvdata(codec);
+       u8 dac_unmute = cs42xx8->tx_channels ?
+                       ~((0x1 << cs42xx8->tx_channels) - 1) : 0;
 
-       regmap_update_bits(cs42xx8->regmap, CS42XX8_DACMUTE,
-                          CS42XX8_DACMUTE_ALL, mute ? CS42XX8_DACMUTE_ALL : 0);
+       regmap_write(cs42xx8->regmap, CS42XX8_DACMUTE,
+                    mute ? CS42XX8_DACMUTE_ALL : dac_unmute);
 
        return 0;
 }
index dc5ae7f7a1bd77f64e480a0346877358670ffac9..576087bda330ce840bdb4f90777ee7c9f1d580d9 100644 (file)
@@ -57,6 +57,25 @@ static const struct wm_adsp_region *cs47l24_dsp_regions[] = {
        cs47l24_dsp3_regions,
 };
 
+static int cs47l24_adsp_power_ev(struct snd_soc_dapm_widget *w,
+                                struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+       unsigned int v;
+       int ret;
+
+       ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to read SYSCLK state: %d\n", ret);
+               return ret;
+       }
+
+       v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
+
+       return wm_adsp2_early_event(w, kcontrol, event, v);
+}
+
 static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
 static DECLARE_TLV_DB_SCALE(noise_tlv, -13200, 600, 0);
@@ -405,8 +424,8 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
 SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
                 NULL, 0),
 
-WM_ADSP2("DSP2", 1),
-WM_ADSP2("DSP3", 2),
+WM_ADSP2("DSP2", 1, cs47l24_adsp_power_ev),
+WM_ADSP2("DSP3", 2, cs47l24_adsp_power_ev),
 
 SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
                 ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
@@ -779,6 +798,9 @@ static const struct snd_soc_dapm_route cs47l24_dapm_routes[] = {
        { "AIF2 Capture", NULL, "SYSCLK" },
        { "AIF3 Capture", NULL, "SYSCLK" },
 
+       { "Voice Control DSP", NULL, "DSP3" },
+       { "Voice Control DSP", NULL, "SYSCLK" },
+
        { "IN1L PGA", NULL, "IN1L" },
        { "IN1R PGA", NULL, "IN1R" },
 
@@ -901,7 +923,7 @@ static int cs47l24_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
        }
 }
 
-#define CS47L24_RATES SNDRV_PCM_RATE_8000_192000
+#define CS47L24_RATES SNDRV_PCM_RATE_KNOT
 
 #define CS47L24_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                         SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -973,12 +995,68 @@ static struct snd_soc_dai_driver cs47l24_dai[] = {
                .symmetric_rates = 1,
                .symmetric_samplebits = 1,
        },
+       {
+               .name = "cs47l24-cpu-voicectrl",
+               .capture = {
+                       .stream_name = "Voice Control CPU",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = CS47L24_RATES,
+                       .formats = CS47L24_FORMATS,
+               },
+               .compress_new = snd_soc_new_compress,
+       },
+       {
+               .name = "cs47l24-dsp-voicectrl",
+               .capture = {
+                       .stream_name = "Voice Control DSP",
+                       .channels_min = 1,
+                       .channels_max = 1,
+                       .rates = CS47L24_RATES,
+                       .formats = CS47L24_FORMATS,
+               },
+       },
 };
 
+static int cs47l24_open(struct snd_compr_stream *stream)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
+       struct arizona *arizona = priv->core.arizona;
+       int n_adsp;
+
+       if (strcmp(rtd->codec_dai->name, "cs47l24-dsp-voicectrl") == 0) {
+               n_adsp = 2;
+       } else {
+               dev_err(arizona->dev,
+                       "No suitable compressed stream for DAI '%s'\n",
+                       rtd->codec_dai->name);
+               return -EINVAL;
+       }
+
+       return wm_adsp_compr_open(&priv->core.adsp[n_adsp], stream);
+}
+
+static irqreturn_t cs47l24_adsp2_irq(int irq, void *data)
+{
+       struct cs47l24_priv *priv = data;
+       struct arizona *arizona = priv->core.arizona;
+       int ret;
+
+       ret = wm_adsp_compr_handle_irq(&priv->core.adsp[2]);
+       if (ret == -ENODEV) {
+               dev_err(arizona->dev, "Spurious compressed data IRQ\n");
+               return IRQ_NONE;
+       }
+
+       return IRQ_HANDLED;
+}
+
 static int cs47l24_codec_probe(struct snd_soc_codec *codec)
 {
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->core.arizona;
        int ret;
 
        priv->core.arizona->dapm = dapm;
@@ -987,6 +1065,14 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
        arizona_init_gpio(codec);
        arizona_init_mono(codec);
 
+       ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+                                 "ADSP2 Compressed IRQ", cs47l24_adsp2_irq,
+                                 priv);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
+               return ret;
+       }
+
        ret = wm_adsp2_codec_probe(&priv->core.adsp[1], codec);
        if (ret)
                goto err_adsp2_codec_probe;
@@ -1014,13 +1100,14 @@ static int cs47l24_codec_probe(struct snd_soc_codec *codec)
 static int cs47l24_codec_remove(struct snd_soc_codec *codec)
 {
        struct cs47l24_priv *priv = snd_soc_codec_get_drvdata(codec);
-
+       struct arizona *arizona = priv->core.arizona;
 
        wm_adsp2_codec_remove(&priv->core.adsp[1], codec);
        wm_adsp2_codec_remove(&priv->core.adsp[2], codec);
 
        priv->core.arizona->dapm = NULL;
 
+       arizona_free_irq(arizona, ARIZONA_IRQ_DSP_IRQ1, priv);
        return 0;
 }
 
@@ -1057,6 +1144,19 @@ static struct snd_soc_codec_driver soc_codec_dev_cs47l24 = {
        .num_dapm_routes = ARRAY_SIZE(cs47l24_dapm_routes),
 };
 
+static struct snd_compr_ops cs47l24_compr_ops = {
+       .open = cs47l24_open,
+       .free = wm_adsp_compr_free,
+       .set_params = wm_adsp_compr_set_params,
+       .get_caps = wm_adsp_compr_get_caps,
+       .trigger = wm_adsp_compr_trigger,
+       .pointer = wm_adsp_compr_pointer,
+       .copy = wm_adsp_compr_copy,
+};
+
+static struct snd_soc_platform_driver cs47l24_compr_platform = {
+       .compr_ops = &cs47l24_compr_ops,
+};
 static int cs47l24_probe(struct platform_device *pdev)
 {
        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
@@ -1120,12 +1220,25 @@ static int cs47l24_probe(struct platform_device *pdev)
        pm_runtime_enable(&pdev->dev);
        pm_runtime_idle(&pdev->dev);
 
-       return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
+       ret = snd_soc_register_platform(&pdev->dev, &cs47l24_compr_platform);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
+               return ret;
+       }
+       ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_cs47l24,
                                      cs47l24_dai, ARRAY_SIZE(cs47l24_dai));
+
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
+               snd_soc_unregister_platform(&pdev->dev);
+       }
+
+       return ret;
 }
 
 static int cs47l24_remove(struct platform_device *pdev)
 {
+       snd_soc_unregister_platform(&pdev->dev);
        snd_soc_unregister_codec(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
index 5a1ec0f7a1a6370931e054cfe3ca85223a6b394d..26f9459cb3bc8e6bb60aa2b71ddeaf427cb869ab 100644 (file)
 #include <linux/module.h>
 #include <linux/pm_runtime.h>
 #include <linux/hdmi.h>
+#include <drm/drm_edid.h>
 #include <sound/pcm_params.h>
+#include <sound/jack.h>
 #include <sound/soc.h>
 #include <sound/hdaudio_ext.h>
 #include <sound/hda_i915.h>
+#include <sound/pcm_drm_eld.h>
 #include "../../hda/local.h"
+#include "hdac_hdmi.h"
+
+#define NAME_SIZE      32
 
 #define AMP_OUT_MUTE           0xb080
 #define AMP_OUT_UNMUTE         0xb000
 
 #define HDA_MAX_CONNECTIONS     32
 
+#define HDA_MAX_CVTS           3
+
+#define ELD_MAX_SIZE    256
+#define ELD_FIXED_BYTES        20
+
 struct hdac_hdmi_cvt_params {
        unsigned int channels_min;
        unsigned int channels_max;
@@ -45,14 +56,34 @@ struct hdac_hdmi_cvt_params {
 struct hdac_hdmi_cvt {
        struct list_head head;
        hda_nid_t nid;
+       const char *name;
        struct hdac_hdmi_cvt_params params;
 };
 
+struct hdac_hdmi_eld {
+       bool    monitor_present;
+       bool    eld_valid;
+       int     eld_size;
+       char    eld_buffer[ELD_MAX_SIZE];
+};
+
 struct hdac_hdmi_pin {
        struct list_head head;
        hda_nid_t nid;
        int num_mux_nids;
        hda_nid_t mux_nids[HDA_MAX_CONNECTIONS];
+       struct hdac_hdmi_eld eld;
+       struct hdac_ext_device *edev;
+       int repoll_count;
+       struct delayed_work work;
+};
+
+struct hdac_hdmi_pcm {
+       struct list_head head;
+       int pcm_id;
+       struct hdac_hdmi_pin *pin;
+       struct hdac_hdmi_cvt *cvt;
+       struct snd_jack *jack;
 };
 
 struct hdac_hdmi_dai_pin_map {
@@ -62,11 +93,13 @@ struct hdac_hdmi_dai_pin_map {
 };
 
 struct hdac_hdmi_priv {
-       struct hdac_hdmi_dai_pin_map dai_map[3];
+       struct hdac_hdmi_dai_pin_map dai_map[HDA_MAX_CVTS];
        struct list_head pin_list;
        struct list_head cvt_list;
+       struct list_head pcm_list;
        int num_pin;
        int num_cvt;
+       struct mutex pin_mutex;
 };
 
 static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
@@ -76,6 +109,119 @@ static inline struct hdac_ext_device *to_hda_ext_device(struct device *dev)
        return to_ehdac_device(hdac);
 }
 
+static unsigned int sad_format(const u8 *sad)
+{
+       return ((sad[0] >> 0x3) & 0x1f);
+}
+
+static unsigned int sad_sample_bits_lpcm(const u8 *sad)
+{
+       return (sad[2] & 7);
+}
+
+static int hdac_hdmi_eld_limit_formats(struct snd_pcm_runtime *runtime,
+                                               void *eld)
+{
+       u64 formats = SNDRV_PCM_FMTBIT_S16;
+       int i;
+       const u8 *sad, *eld_buf = eld;
+
+       sad = drm_eld_sad(eld_buf);
+       if (!sad)
+               goto format_constraint;
+
+       for (i = drm_eld_sad_count(eld_buf); i > 0; i--, sad += 3) {
+               if (sad_format(sad) == 1) { /* AUDIO_CODING_TYPE_LPCM */
+
+                       /*
+                        * the controller support 20 and 24 bits in 32 bit
+                        * container so we set S32
+                        */
+                       if (sad_sample_bits_lpcm(sad) & 0x6)
+                               formats |= SNDRV_PCM_FMTBIT_S32;
+               }
+       }
+
+format_constraint:
+       return snd_pcm_hw_constraint_mask64(runtime, SNDRV_PCM_HW_PARAM_FORMAT,
+                               formats);
+
+}
+
+ /* HDMI ELD routines */
+static unsigned int hdac_hdmi_get_eld_data(struct hdac_device *codec,
+                               hda_nid_t nid, int byte_index)
+{
+       unsigned int val;
+
+       val = snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_ELDD,
+                                                       byte_index);
+
+       dev_dbg(&codec->dev, "HDMI: ELD data byte %d: 0x%x\n",
+                                       byte_index, val);
+
+       return val;
+}
+
+static int hdac_hdmi_get_eld_size(struct hdac_device *codec, hda_nid_t nid)
+{
+       return snd_hdac_codec_read(codec, nid, 0, AC_VERB_GET_HDMI_DIP_SIZE,
+                                                AC_DIPSIZE_ELD_BUF);
+}
+
+/*
+ * This function queries the ELD size and ELD data and fills in the buffer
+ * passed by user
+ */
+static int hdac_hdmi_get_eld(struct hdac_device *codec, hda_nid_t nid,
+                            unsigned char *buf, int *eld_size)
+{
+       int i, size, ret = 0;
+
+       /*
+        * ELD size is initialized to zero in caller function. If no errors and
+        * ELD is valid, actual eld_size is assigned.
+        */
+
+       size = hdac_hdmi_get_eld_size(codec, nid);
+       if (size < ELD_FIXED_BYTES || size > ELD_MAX_SIZE) {
+               dev_err(&codec->dev, "HDMI: invalid ELD buf size %d\n", size);
+               return -ERANGE;
+       }
+
+       /* set ELD buffer */
+       for (i = 0; i < size; i++) {
+               unsigned int val = hdac_hdmi_get_eld_data(codec, nid, i);
+               /*
+                * Graphics driver might be writing to ELD buffer right now.
+                * Just abort. The caller will repoll after a while.
+                */
+               if (!(val & AC_ELDD_ELD_VALID)) {
+                       dev_err(&codec->dev,
+                               "HDMI: invalid ELD data byte %d\n", i);
+                       ret = -EINVAL;
+                       goto error;
+               }
+               val &= AC_ELDD_ELD_DATA;
+               /*
+                * The first byte cannot be zero. This can happen on some DVI
+                * connections. Some Intel chips may also need some 250ms delay
+                * to return non-zero ELD data, even when the graphics driver
+                * correctly writes ELD content before setting ELD_valid bit.
+                */
+               if (!val && !i) {
+                       dev_err(&codec->dev, "HDMI: 0 ELD data\n");
+                       ret = -EINVAL;
+                       goto error;
+               }
+               buf[i] = val;
+       }
+
+       *eld_size = size;
+error:
+       return ret;
+}
+
 static int hdac_hdmi_setup_stream(struct hdac_ext_device *hdac,
                                hda_nid_t cvt_nid, hda_nid_t pin_nid,
                                u32 stream_tag, int format)
@@ -107,27 +253,74 @@ hdac_hdmi_set_dip_index(struct hdac_ext_device *hdac, hda_nid_t pin_nid,
                                AC_VERB_SET_HDMI_DIP_INDEX, val);
 }
 
+struct dp_audio_infoframe {
+       u8 type; /* 0x84 */
+       u8 len;  /* 0x1b */
+       u8 ver;  /* 0x11 << 2 */
+
+       u8 CC02_CT47;   /* match with HDMI infoframe from this on */
+       u8 SS01_SF24;
+       u8 CXT04;
+       u8 CA;
+       u8 LFEPBL01_LSV36_DM_INH7;
+};
+
 static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
                                hda_nid_t cvt_nid, hda_nid_t pin_nid)
 {
        uint8_t buffer[HDMI_INFOFRAME_HEADER_SIZE + HDMI_AUDIO_INFOFRAME_SIZE];
        struct hdmi_audio_infoframe frame;
-       u8 *dip = (u8 *)&frame;
+       struct dp_audio_infoframe dp_ai;
+       struct hdac_hdmi_priv *hdmi = hdac->private_data;
+       struct hdac_hdmi_pin *pin;
+       u8 *dip;
        int ret;
        int i;
+       const u8 *eld_buf;
+       u8 conn_type;
+       int channels = 2;
 
-       hdmi_audio_infoframe_init(&frame);
+       list_for_each_entry(pin, &hdmi->pin_list, head) {
+               if (pin->nid == pin_nid)
+                       break;
+       }
 
-       /* Default stereo for now */
-       frame.channels = 2;
+       eld_buf = pin->eld.eld_buffer;
+       conn_type = drm_eld_get_conn_type(eld_buf);
 
        /* setup channel count */
        snd_hdac_codec_write(&hdac->hdac, cvt_nid, 0,
-                           AC_VERB_SET_CVT_CHAN_COUNT, frame.channels - 1);
+                           AC_VERB_SET_CVT_CHAN_COUNT, channels - 1);
 
-       ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
-       if (ret < 0)
-               return ret;
+       switch (conn_type) {
+       case DRM_ELD_CONN_TYPE_HDMI:
+               hdmi_audio_infoframe_init(&frame);
+
+               /* Default stereo for now */
+               frame.channels = channels;
+
+               ret = hdmi_audio_infoframe_pack(&frame, buffer, sizeof(buffer));
+               if (ret < 0)
+                       return ret;
+
+               break;
+
+       case DRM_ELD_CONN_TYPE_DP:
+               memset(&dp_ai, 0, sizeof(dp_ai));
+               dp_ai.type      = 0x84;
+               dp_ai.len       = 0x1b;
+               dp_ai.ver       = 0x11 << 2;
+               dp_ai.CC02_CT47 = channels - 1;
+               dp_ai.CA        = 0;
+
+               dip = (u8 *)&dp_ai;
+               break;
+
+       default:
+               dev_err(&hdac->hdac.dev, "Invalid connection type: %d\n",
+                                               conn_type);
+               return -EIO;
+       }
 
        /* stop infoframe transmission */
        hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
@@ -137,9 +330,15 @@ static int hdac_hdmi_setup_audio_infoframe(struct hdac_ext_device *hdac,
 
        /*  Fill infoframe. Index auto-incremented */
        hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
-       for (i = 0; i < sizeof(frame); i++)
-               snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
+       if (conn_type == DRM_ELD_CONN_TYPE_HDMI) {
+               for (i = 0; i < sizeof(buffer); i++)
+                       snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
+                               AC_VERB_SET_HDMI_DIP_DATA, buffer[i]);
+       } else {
+               for (i = 0; i < sizeof(dp_ai); i++)
+                       snd_hdac_codec_write(&hdac->hdac, pin_nid, 0,
                                AC_VERB_SET_HDMI_DIP_DATA, dip[i]);
+       }
 
        /* Start infoframe */
        hdac_hdmi_set_dip_index(hdac, pin_nid, 0x0, 0x0);
@@ -174,11 +373,6 @@ static int hdac_hdmi_playback_prepare(struct snd_pcm_substream *substream,
        struct hdac_ext_dma_params *dd;
        int ret;
 
-       if (dai->id > 0) {
-               dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
-               return -ENODEV;
-       }
-
        dai_map = &hdmi->dai_map[dai->id];
 
        dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
@@ -198,16 +392,30 @@ static int hdac_hdmi_set_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *hparams, struct snd_soc_dai *dai)
 {
        struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
+       struct hdac_hdmi_priv *hdmi = hdac->private_data;
+       struct hdac_hdmi_dai_pin_map *dai_map;
+       struct hdac_hdmi_pin *pin;
        struct hdac_ext_dma_params *dd;
 
-       if (dai->id > 0) {
-               dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
+       dai_map = &hdmi->dai_map[dai->id];
+       pin = dai_map->pin;
+
+       if (!pin)
+               return -ENODEV;
+
+       if ((!pin->eld.monitor_present) || (!pin->eld.eld_valid)) {
+               dev_err(&hdac->hdac.dev, "device is not configured for this pin: %d\n",
+                                                               pin->nid);
                return -ENODEV;
        }
 
-       dd = kzalloc(sizeof(*dd), GFP_KERNEL);
-       if (!dd)
-               return -ENOMEM;
+       dd = snd_soc_dai_get_dma_data(dai, substream);
+       if (!dd) {
+               dd = kzalloc(sizeof(*dd), GFP_KERNEL);
+               if (!dd)
+                       return -ENOMEM;
+       }
+
        dd->format = snd_hdac_calc_stream_format(params_rate(hparams),
                        params_channels(hparams), params_format(hparams),
                        24, 0);
@@ -227,50 +435,187 @@ static int hdac_hdmi_playback_cleanup(struct snd_pcm_substream *substream,
 
        dai_map = &hdmi->dai_map[dai->id];
 
+       dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
+
+       if (dd) {
+               snd_soc_dai_set_dma_data(dai, substream, NULL);
+               kfree(dd);
+       }
+
+       return 0;
+}
+
+static void hdac_hdmi_enable_cvt(struct hdac_ext_device *edev,
+               struct hdac_hdmi_dai_pin_map *dai_map)
+{
+       /* Enable transmission */
        snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
-                               AC_VERB_SET_CHANNEL_STREAMID, 0);
+                       AC_VERB_SET_DIGI_CONVERT_1, 1);
+
+       /* Category Code (CC) to zero */
        snd_hdac_codec_write(&edev->hdac, dai_map->cvt->nid, 0,
-                               AC_VERB_SET_STREAM_FORMAT, 0);
+                       AC_VERB_SET_DIGI_CONVERT_2, 0);
+}
 
-       dd = (struct hdac_ext_dma_params *)snd_soc_dai_get_dma_data(dai, substream);
-       snd_soc_dai_set_dma_data(dai, substream, NULL);
+static int hdac_hdmi_enable_pin(struct hdac_ext_device *hdac,
+               struct hdac_hdmi_dai_pin_map *dai_map)
+{
+       int mux_idx;
+       struct hdac_hdmi_pin *pin = dai_map->pin;
+
+       for (mux_idx = 0; mux_idx < pin->num_mux_nids; mux_idx++) {
+               if (pin->mux_nids[mux_idx] == dai_map->cvt->nid) {
+                       snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
+                                       AC_VERB_SET_CONNECT_SEL, mux_idx);
+                       break;
+               }
+       }
+
+       if (mux_idx == pin->num_mux_nids)
+               return -EIO;
+
+       /* Enable out path for this pin widget */
+       snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
+                       AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
 
-       kfree(dd);
+       hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
+
+       snd_hdac_codec_write(&hdac->hdac, pin->nid, 0,
+                       AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
 
        return 0;
 }
 
+static int hdac_hdmi_query_pin_connlist(struct hdac_ext_device *hdac,
+                                       struct hdac_hdmi_pin *pin)
+{
+       if (!(get_wcaps(&hdac->hdac, pin->nid) & AC_WCAP_CONN_LIST)) {
+               dev_warn(&hdac->hdac.dev,
+                       "HDMI: pin %d wcaps %#x does not support connection list\n",
+                       pin->nid, get_wcaps(&hdac->hdac, pin->nid));
+               return -EINVAL;
+       }
+
+       pin->num_mux_nids = snd_hdac_get_connections(&hdac->hdac, pin->nid,
+                       pin->mux_nids, HDA_MAX_CONNECTIONS);
+       if (pin->num_mux_nids == 0)
+               dev_warn(&hdac->hdac.dev, "No connections found for pin: %d\n",
+                                                               pin->nid);
+
+       dev_dbg(&hdac->hdac.dev, "num_mux_nids %d for pin: %d\n",
+                       pin->num_mux_nids, pin->nid);
+
+       return pin->num_mux_nids;
+}
+
+/*
+ * Query pcm list and return pin widget to which stream is routed.
+ *
+ * Also query connection list of the pin, to validate the cvt to pin map.
+ *
+ * Same stream rendering to multiple pins simultaneously can be done
+ * possibly, but not supported for now in driver. So return the first pin
+ * connected.
+ */
+static struct hdac_hdmi_pin *hdac_hdmi_get_pin_from_cvt(
+                       struct hdac_ext_device *edev,
+                       struct hdac_hdmi_priv *hdmi,
+                       struct hdac_hdmi_cvt *cvt)
+{
+       struct hdac_hdmi_pcm *pcm;
+       struct hdac_hdmi_pin *pin = NULL;
+       int ret, i;
+
+       list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+               if (pcm->cvt == cvt) {
+                       pin = pcm->pin;
+                       break;
+               }
+       }
+
+       if (pin) {
+               ret = hdac_hdmi_query_pin_connlist(edev, pin);
+               if (ret < 0)
+                       return NULL;
+
+               for (i = 0; i < pin->num_mux_nids; i++) {
+                       if (pin->mux_nids[i] == cvt->nid)
+                               return pin;
+               }
+       }
+
+       return NULL;
+}
+
+/*
+ * This tries to get a valid pin and set the HW constraints based on the
+ * ELD. Even if a valid pin is not found return success so that device open
+ * doesn't fail.
+ */
 static int hdac_hdmi_pcm_open(struct snd_pcm_substream *substream,
                        struct snd_soc_dai *dai)
 {
        struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
        struct hdac_hdmi_priv *hdmi = hdac->private_data;
        struct hdac_hdmi_dai_pin_map *dai_map;
-       int val;
-
-       if (dai->id > 0) {
-               dev_err(&hdac->hdac.dev, "Only one dai supported as of now\n");
-               return -ENODEV;
-       }
+       struct hdac_hdmi_cvt *cvt;
+       struct hdac_hdmi_pin *pin;
+       int ret;
 
        dai_map = &hdmi->dai_map[dai->id];
 
-       val = snd_hdac_codec_read(&hdac->hdac, dai_map->pin->nid, 0,
-                                       AC_VERB_GET_PIN_SENSE, 0);
-       dev_info(&hdac->hdac.dev, "Val for AC_VERB_GET_PIN_SENSE: %x\n", val);
+       cvt = dai_map->cvt;
+       pin = hdac_hdmi_get_pin_from_cvt(hdac, hdmi, cvt);
 
-       if ((!(val & AC_PINSENSE_PRESENCE)) || (!(val & AC_PINSENSE_ELDV))) {
-               dev_err(&hdac->hdac.dev, "Monitor presence invalid with val: %x\n", val);
-               return -ENODEV;
+       /*
+        * To make PA and other userland happy.
+        * userland scans devices so returning error does not help.
+        */
+       if (!pin)
+               return 0;
+
+       if ((!pin->eld.monitor_present) ||
+                       (!pin->eld.eld_valid)) {
+
+               dev_warn(&hdac->hdac.dev,
+                       "Failed: montior present? %d ELD valid?: %d for pin: %d\n",
+                       pin->eld.monitor_present, pin->eld.eld_valid, pin->nid);
+
+               return 0;
        }
 
-       hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D0);
+       dai_map->pin = pin;
 
-       snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
-                       AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE);
+       hdac_hdmi_enable_cvt(hdac, dai_map);
+       ret = hdac_hdmi_enable_pin(hdac, dai_map);
+       if (ret < 0)
+               return ret;
 
-       snd_pcm_hw_constraint_step(substream->runtime, 0,
-                                  SNDRV_PCM_HW_PARAM_CHANNELS, 2);
+       ret = hdac_hdmi_eld_limit_formats(substream->runtime,
+                               pin->eld.eld_buffer);
+       if (ret < 0)
+               return ret;
+
+       return snd_pcm_hw_constraint_eld(substream->runtime,
+                               pin->eld.eld_buffer);
+}
+
+static int hdac_hdmi_trigger(struct snd_pcm_substream *substream, int cmd,
+               struct snd_soc_dai *dai)
+{
+       struct hdac_hdmi_dai_pin_map *dai_map;
+       struct hdac_ext_device *hdac = snd_soc_dai_get_drvdata(dai);
+       struct hdac_hdmi_priv *hdmi = hdac->private_data;
+       int ret;
+
+       dai_map = &hdmi->dai_map[dai->id];
+       if (cmd == SNDRV_PCM_TRIGGER_RESUME) {
+               ret = hdac_hdmi_enable_pin(hdac, dai_map);
+               if (ret < 0)
+                       return ret;
+
+               return hdac_hdmi_playback_prepare(substream, dai);
+       }
 
        return 0;
 }
@@ -284,10 +629,19 @@ static void hdac_hdmi_pcm_close(struct snd_pcm_substream *substream,
 
        dai_map = &hdmi->dai_map[dai->id];
 
-       hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
+       if (dai_map->pin) {
+               snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0,
+                               AC_VERB_SET_CHANNEL_STREAMID, 0);
+               snd_hdac_codec_write(&hdac->hdac, dai_map->cvt->nid, 0,
+                               AC_VERB_SET_STREAM_FORMAT, 0);
+
+               hdac_hdmi_set_power_state(hdac, dai_map, AC_PWRST_D3);
 
-       snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
+               snd_hdac_codec_write(&hdac->hdac, dai_map->pin->nid, 0,
                        AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+               dai_map->pin = NULL;
+       }
 }
 
 static int
@@ -310,85 +664,326 @@ hdac_hdmi_query_cvt_params(struct hdac_device *hdac, struct hdac_hdmi_cvt *cvt)
        return err;
 }
 
-static void hdac_hdmi_fill_widget_info(struct snd_soc_dapm_widget *w,
-                               enum snd_soc_dapm_type id,
-                               const char *wname, const char *stream)
+static int hdac_hdmi_fill_widget_info(struct device *dev,
+                               struct snd_soc_dapm_widget *w,
+                               enum snd_soc_dapm_type id, void *priv,
+                               const char *wname, const char *stream,
+                               struct snd_kcontrol_new *wc, int numkc)
 {
        w->id = id;
-       w->name = wname;
+       w->name = devm_kstrdup(dev, wname, GFP_KERNEL);
+       if (!w->name)
+               return -ENOMEM;
+
        w->sname = stream;
        w->reg = SND_SOC_NOPM;
        w->shift = 0;
-       w->kcontrol_news = NULL;
-       w->num_kcontrols = 0;
-       w->priv = NULL;
+       w->kcontrol_news = wc;
+       w->num_kcontrols = numkc;
+       w->priv = priv;
+
+       return 0;
 }
 
 static void hdac_hdmi_fill_route(struct snd_soc_dapm_route *route,
-               const char *sink, const char *control, const char *src)
+               const char *sink, const char *control, const char *src,
+               int (*handler)(struct snd_soc_dapm_widget *src,
+                       struct snd_soc_dapm_widget *sink))
 {
        route->sink = sink;
        route->source = src;
        route->control = control;
-       route->connected = NULL;
+       route->connected = handler;
 }
 
-static void create_fill_widget_route_map(struct snd_soc_dapm_context *dapm,
-                                       struct hdac_hdmi_dai_pin_map *dai_map)
+static struct hdac_hdmi_pcm *hdac_hdmi_get_pcm(struct hdac_ext_device *edev,
+                                       struct hdac_hdmi_pin *pin)
 {
-       struct snd_soc_dapm_route route[1];
-       struct snd_soc_dapm_widget widgets[2] = { {0} };
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm = NULL;
 
-       memset(&route, 0, sizeof(route));
+       list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+               if (pcm->pin == pin)
+                       return pcm;
+       }
 
-       hdac_hdmi_fill_widget_info(&widgets[0], snd_soc_dapm_output,
-                       "hif1 Output", NULL);
-       hdac_hdmi_fill_widget_info(&widgets[1], snd_soc_dapm_aif_in,
-                       "Coverter 1", "hif1");
+       return NULL;
+}
 
-       hdac_hdmi_fill_route(&route[0], "hif1 Output", NULL, "Coverter 1");
+/*
+ * Based on user selection, map the PINs with the PCMs.
+ */
+static int hdac_hdmi_set_pin_mux(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       int ret;
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       struct snd_soc_dapm_widget *w = snd_soc_dapm_kcontrol_widget(kcontrol);
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct hdac_hdmi_pin *pin = w->priv;
+       struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm = NULL;
+       const char *cvt_name =  e->texts[ucontrol->value.enumerated.item[0]];
 
-       snd_soc_dapm_new_controls(dapm, widgets, ARRAY_SIZE(widgets));
-       snd_soc_dapm_add_routes(dapm, route, ARRAY_SIZE(route));
+       ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
+       if (ret < 0)
+               return ret;
+
+       mutex_lock(&hdmi->pin_mutex);
+       list_for_each_entry(pcm, &hdmi->pcm_list, head) {
+               if (pcm->pin == pin)
+                       pcm->pin = NULL;
+
+               /*
+                * Jack status is not reported during device probe as the
+                * PCMs are not registered by then. So report it here.
+                */
+               if (!strcmp(cvt_name, pcm->cvt->name) && !pcm->pin) {
+                       pcm->pin = pin;
+                       if (pin->eld.monitor_present && pin->eld.eld_valid) {
+                               dev_dbg(&edev->hdac.dev,
+                                       "jack report for pcm=%d\n",
+                                       pcm->pcm_id);
+
+                               snd_jack_report(pcm->jack, SND_JACK_AVOUT);
+                       }
+                       mutex_unlock(&hdmi->pin_mutex);
+                       return ret;
+               }
+       }
+       mutex_unlock(&hdmi->pin_mutex);
+
+       return ret;
 }
 
-static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
+/*
+ * Ideally the Mux inputs should be based on the num_muxs enumerated, but
+ * the display driver seem to be programming the connection list for the pin
+ * widget runtime.
+ *
+ * So programming all the possible inputs for the mux, the user has to take
+ * care of selecting the right one and leaving all other inputs selected to
+ * "NONE"
+ */
+static int hdac_hdmi_create_pin_muxs(struct hdac_ext_device *edev,
+                               struct hdac_hdmi_pin *pin,
+                               struct snd_soc_dapm_widget *widget,
+                               const char *widget_name)
+{
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct snd_kcontrol_new *kc;
+       struct hdac_hdmi_cvt *cvt;
+       struct soc_enum *se;
+       char kc_name[NAME_SIZE];
+       char mux_items[NAME_SIZE];
+       /* To hold inputs to the Pin mux */
+       char *items[HDA_MAX_CONNECTIONS];
+       int i = 0;
+       int num_items = hdmi->num_cvt + 1;
+
+       kc = devm_kzalloc(&edev->hdac.dev, sizeof(*kc), GFP_KERNEL);
+       if (!kc)
+               return -ENOMEM;
+
+       se = devm_kzalloc(&edev->hdac.dev, sizeof(*se), GFP_KERNEL);
+       if (!se)
+               return -ENOMEM;
+
+       sprintf(kc_name, "Pin %d Input", pin->nid);
+       kc->name = devm_kstrdup(&edev->hdac.dev, kc_name, GFP_KERNEL);
+       if (!kc->name)
+               return -ENOMEM;
+
+       kc->private_value = (long)se;
+       kc->iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       kc->access = 0;
+       kc->info = snd_soc_info_enum_double;
+       kc->put = hdac_hdmi_set_pin_mux;
+       kc->get = snd_soc_dapm_get_enum_double;
+
+       se->reg = SND_SOC_NOPM;
+
+       /* enum texts: ["NONE", "cvt #", "cvt #", ...] */
+       se->items = num_items;
+       se->mask = roundup_pow_of_two(se->items) - 1;
+
+       sprintf(mux_items, "NONE");
+       items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL);
+       if (!items[i])
+               return -ENOMEM;
+
+       list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+               i++;
+               sprintf(mux_items, "cvt %d", cvt->nid);
+               items[i] = devm_kstrdup(&edev->hdac.dev, mux_items, GFP_KERNEL);
+               if (!items[i])
+                       return -ENOMEM;
+       }
+
+       se->texts = devm_kmemdup(&edev->hdac.dev, items,
+                       (num_items  * sizeof(char *)), GFP_KERNEL);
+       if (!se->texts)
+               return -ENOMEM;
+
+       return hdac_hdmi_fill_widget_info(&edev->hdac.dev, widget,
+                       snd_soc_dapm_mux, pin, widget_name, NULL, kc, 1);
+}
+
+/* Add cvt <- input <- mux route map */
+static void hdac_hdmi_add_pinmux_cvt_route(struct hdac_ext_device *edev,
+                       struct snd_soc_dapm_widget *widgets,
+                       struct snd_soc_dapm_route *route, int rindex)
+{
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       const struct snd_kcontrol_new *kc;
+       struct soc_enum *se;
+       int mux_index = hdmi->num_cvt + hdmi->num_pin;
+       int i, j;
+
+       for (i = 0; i < hdmi->num_pin; i++) {
+               kc = widgets[mux_index].kcontrol_news;
+               se = (struct soc_enum *)kc->private_value;
+               for (j = 0; j < hdmi->num_cvt; j++) {
+                       hdac_hdmi_fill_route(&route[rindex],
+                                       widgets[mux_index].name,
+                                       se->texts[j + 1],
+                                       widgets[j].name, NULL);
+
+                       rindex++;
+               }
+
+               mux_index++;
+       }
+}
+
+/*
+ * Widgets are added in the below sequence
+ *     Converter widgets for num converters enumerated
+ *     Pin widgets for num pins enumerated
+ *     Pin mux widgets to represent connenction list of pin widget
+ *
+ * Total widgets elements = num_cvt + num_pin + num_pin;
+ *
+ * Routes are added as below:
+ *     pin mux -> pin (based on num_pins)
+ *     cvt -> "Input sel control" -> pin_mux
+ *
+ * Total route elements:
+ *     num_pins + (pin_muxes * num_cvt)
+ */
+static int create_fill_widget_route_map(struct snd_soc_dapm_context *dapm)
 {
+       struct snd_soc_dapm_widget *widgets;
+       struct snd_soc_dapm_route *route;
+       struct hdac_ext_device *edev = to_hda_ext_device(dapm->dev);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
-       struct hdac_hdmi_dai_pin_map *dai_map = &hdmi->dai_map[0];
+       struct snd_soc_dai_driver *dai_drv = dapm->component->dai_drv;
+       char widget_name[NAME_SIZE];
        struct hdac_hdmi_cvt *cvt;
        struct hdac_hdmi_pin *pin;
+       int ret, i = 0, num_routes = 0;
 
        if (list_empty(&hdmi->cvt_list) || list_empty(&hdmi->pin_list))
                return -EINVAL;
 
-       /*
-        * Currently on board only 1 pin and 1 converter is enabled for
-        * simplification, more will be added eventually
-        * So using fixed map for dai_id:pin:cvt
-        */
-       cvt = list_first_entry(&hdmi->cvt_list, struct hdac_hdmi_cvt, head);
-       pin = list_first_entry(&hdmi->pin_list, struct hdac_hdmi_pin, head);
+       widgets = devm_kzalloc(dapm->dev,
+               (sizeof(*widgets) * ((2 * hdmi->num_pin) + hdmi->num_cvt)),
+               GFP_KERNEL);
 
-       dai_map->dai_id = 0;
-       dai_map->pin = pin;
+       if (!widgets)
+               return -ENOMEM;
 
-       dai_map->cvt = cvt;
+       /* DAPM widgets to represent each converter widget */
+       list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+               sprintf(widget_name, "Converter %d", cvt->nid);
+               ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
+                       snd_soc_dapm_aif_in, &cvt->nid,
+                       widget_name, dai_drv[i].playback.stream_name, NULL, 0);
+               if (ret < 0)
+                       return ret;
+               i++;
+       }
 
-       /* Enable out path for this pin widget */
-       snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
-                       AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+       list_for_each_entry(pin, &hdmi->pin_list, head) {
+               sprintf(widget_name, "hif%d Output", pin->nid);
+               ret = hdac_hdmi_fill_widget_info(dapm->dev, &widgets[i],
+                               snd_soc_dapm_output, &pin->nid,
+                               widget_name, NULL, NULL, 0);
+               if (ret < 0)
+                       return ret;
+               i++;
+       }
 
-       /* Enable transmission */
-       snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
-                       AC_VERB_SET_DIGI_CONVERT_1, 1);
+       /* DAPM widgets to represent the connection list to pin widget */
+       list_for_each_entry(pin, &hdmi->pin_list, head) {
+               sprintf(widget_name, "Pin %d Mux", pin->nid);
+               ret = hdac_hdmi_create_pin_muxs(edev, pin, &widgets[i],
+                                                       widget_name);
+               if (ret < 0)
+                       return ret;
+               i++;
 
-       /* Category Code (CC) to zero */
-       snd_hdac_codec_write(&edev->hdac, cvt->nid, 0,
-                       AC_VERB_SET_DIGI_CONVERT_2, 0);
+               /* For cvt to pin_mux mapping */
+               num_routes += hdmi->num_cvt;
+
+               /* For pin_mux to pin mapping */
+               num_routes++;
+       }
 
-       snd_hdac_codec_write(&edev->hdac, pin->nid, 0,
-                       AC_VERB_SET_CONNECT_SEL, 0);
+       route = devm_kzalloc(dapm->dev, (sizeof(*route) * num_routes),
+                                                       GFP_KERNEL);
+       if (!route)
+               return -ENOMEM;
+
+       i = 0;
+       /* Add pin <- NULL <- mux route map */
+       list_for_each_entry(pin, &hdmi->pin_list, head) {
+               int sink_index = i + hdmi->num_cvt;
+               int src_index = sink_index + hdmi->num_pin;
+
+               hdac_hdmi_fill_route(&route[i],
+                               widgets[sink_index].name, NULL,
+                               widgets[src_index].name, NULL);
+               i++;
+
+       }
+
+       hdac_hdmi_add_pinmux_cvt_route(edev, widgets, route, i);
+
+       snd_soc_dapm_new_controls(dapm, widgets,
+               ((2 * hdmi->num_pin) + hdmi->num_cvt));
+
+       snd_soc_dapm_add_routes(dapm, route, num_routes);
+       snd_soc_dapm_new_widgets(dapm->card);
+
+       return 0;
+
+}
+
+static int hdac_hdmi_init_dai_map(struct hdac_ext_device *edev)
+{
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_dai_pin_map *dai_map;
+       struct hdac_hdmi_cvt *cvt;
+       int dai_id = 0;
+
+       if (list_empty(&hdmi->cvt_list))
+               return -EINVAL;
+
+       list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+               dai_map = &hdmi->dai_map[dai_id];
+               dai_map->dai_id = dai_id;
+               dai_map->cvt = cvt;
+
+               dai_id++;
+
+               if (dai_id == HDA_MAX_CVTS) {
+                       dev_warn(&edev->hdac.dev,
+                               "Max dais supported: %d\n", dai_id);
+                       break;
+               }
+       }
 
        return 0;
 }
@@ -397,12 +992,15 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
 {
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_cvt *cvt;
+       char name[NAME_SIZE];
 
        cvt = kzalloc(sizeof(*cvt), GFP_KERNEL);
        if (!cvt)
                return -ENOMEM;
 
        cvt->nid = nid;
+       sprintf(name, "cvt %d", cvt->nid);
+       cvt->name = kstrdup(name, GFP_KERNEL);
 
        list_add_tail(&cvt->head, &hdmi->cvt_list);
        hdmi->num_cvt++;
@@ -410,6 +1008,106 @@ static int hdac_hdmi_add_cvt(struct hdac_ext_device *edev, hda_nid_t nid)
        return hdac_hdmi_query_cvt_params(&edev->hdac, cvt);
 }
 
+static void hdac_hdmi_present_sense(struct hdac_hdmi_pin *pin, int repoll)
+{
+       struct hdac_ext_device *edev = pin->edev;
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm;
+       int val;
+
+       pin->repoll_count = repoll;
+
+       pm_runtime_get_sync(&edev->hdac.dev);
+       val = snd_hdac_codec_read(&edev->hdac, pin->nid, 0,
+                                       AC_VERB_GET_PIN_SENSE, 0);
+
+       dev_dbg(&edev->hdac.dev, "Pin sense val %x for pin: %d\n",
+                                               val, pin->nid);
+
+
+       mutex_lock(&hdmi->pin_mutex);
+       pin->eld.monitor_present = !!(val & AC_PINSENSE_PRESENCE);
+       pin->eld.eld_valid = !!(val & AC_PINSENSE_ELDV);
+
+       pcm = hdac_hdmi_get_pcm(edev, pin);
+
+       if (!pin->eld.monitor_present || !pin->eld.eld_valid) {
+
+               dev_dbg(&edev->hdac.dev, "%s: disconnect for pin %d\n",
+                                               __func__, pin->nid);
+
+               /*
+                * PCMs are not registered during device probe, so don't
+                * report jack here. It will be done in usermode mux
+                * control select.
+                */
+               if (pcm) {
+                       dev_dbg(&edev->hdac.dev,
+                               "jack report for pcm=%d\n", pcm->pcm_id);
+
+                       snd_jack_report(pcm->jack, 0);
+               }
+
+               mutex_unlock(&hdmi->pin_mutex);
+               goto put_hdac_device;
+       }
+
+       if (pin->eld.monitor_present && pin->eld.eld_valid) {
+               /* TODO: use i915 component for reading ELD later */
+               if (hdac_hdmi_get_eld(&edev->hdac, pin->nid,
+                               pin->eld.eld_buffer,
+                               &pin->eld.eld_size) == 0) {
+
+                       if (pcm) {
+                               dev_dbg(&edev->hdac.dev,
+                                       "jack report for pcm=%d\n",
+                                       pcm->pcm_id);
+
+                               snd_jack_report(pcm->jack, SND_JACK_AVOUT);
+                       }
+
+                       print_hex_dump_bytes("ELD: ", DUMP_PREFIX_OFFSET,
+                                       pin->eld.eld_buffer, pin->eld.eld_size);
+               } else {
+                       pin->eld.monitor_present = false;
+                       pin->eld.eld_valid = false;
+
+                       if (pcm) {
+                               dev_dbg(&edev->hdac.dev,
+                                       "jack report for pcm=%d\n",
+                                       pcm->pcm_id);
+
+                               snd_jack_report(pcm->jack, 0);
+                       }
+               }
+       }
+
+       mutex_unlock(&hdmi->pin_mutex);
+
+       /*
+        * Sometimes the pin_sense may present invalid monitor
+        * present and eld_valid. If ELD data is not valid, loop few
+        * more times to get correct pin sense and valid ELD.
+        */
+       if ((!pin->eld.monitor_present || !pin->eld.eld_valid) && repoll)
+               schedule_delayed_work(&pin->work, msecs_to_jiffies(300));
+
+put_hdac_device:
+       pm_runtime_put_sync(&edev->hdac.dev);
+}
+
+static void hdac_hdmi_repoll_eld(struct work_struct *work)
+{
+       struct hdac_hdmi_pin *pin =
+               container_of(to_delayed_work(work), struct hdac_hdmi_pin, work);
+
+       /* picked from legacy HDA driver */
+       if (pin->repoll_count++ > 6)
+               pin->repoll_count = 0;
+
+       hdac_hdmi_present_sense(pin, pin->repoll_count);
+}
+
 static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
 {
        struct hdac_hdmi_priv *hdmi = edev->private_data;
@@ -424,6 +1122,120 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
        list_add_tail(&pin->head, &hdmi->pin_list);
        hdmi->num_pin++;
 
+       pin->edev = edev;
+       INIT_DELAYED_WORK(&pin->work, hdac_hdmi_repoll_eld);
+
+       return 0;
+}
+
+#define INTEL_VENDOR_NID 0x08
+#define INTEL_GET_VENDOR_VERB 0xf81
+#define INTEL_SET_VENDOR_VERB 0x781
+#define INTEL_EN_DP12                  0x02 /* enable DP 1.2 features */
+#define INTEL_EN_ALL_PIN_CVTS  0x01 /* enable 2nd & 3rd pins and convertors */
+
+static void hdac_hdmi_skl_enable_all_pins(struct hdac_device *hdac)
+{
+       unsigned int vendor_param;
+
+       vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
+                               INTEL_GET_VENDOR_VERB, 0);
+       if (vendor_param == -1 || vendor_param & INTEL_EN_ALL_PIN_CVTS)
+               return;
+
+       vendor_param |= INTEL_EN_ALL_PIN_CVTS;
+       vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
+                               INTEL_SET_VENDOR_VERB, vendor_param);
+       if (vendor_param == -1)
+               return;
+}
+
+static void hdac_hdmi_skl_enable_dp12(struct hdac_device *hdac)
+{
+       unsigned int vendor_param;
+
+       vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
+                               INTEL_GET_VENDOR_VERB, 0);
+       if (vendor_param == -1 || vendor_param & INTEL_EN_DP12)
+               return;
+
+       /* enable DP1.2 mode */
+       vendor_param |= INTEL_EN_DP12;
+       vendor_param = snd_hdac_codec_read(hdac, INTEL_VENDOR_NID, 0,
+                               INTEL_SET_VENDOR_VERB, vendor_param);
+       if (vendor_param == -1)
+               return;
+
+}
+
+static struct snd_soc_dai_ops hdmi_dai_ops = {
+       .startup = hdac_hdmi_pcm_open,
+       .shutdown = hdac_hdmi_pcm_close,
+       .hw_params = hdac_hdmi_set_hw_params,
+       .prepare = hdac_hdmi_playback_prepare,
+       .trigger = hdac_hdmi_trigger,
+       .hw_free = hdac_hdmi_playback_cleanup,
+};
+
+/*
+ * Each converter can support a stream independently. So a dai is created
+ * based on the number of converter queried.
+ */
+static int hdac_hdmi_create_dais(struct hdac_device *hdac,
+               struct snd_soc_dai_driver **dais,
+               struct hdac_hdmi_priv *hdmi, int num_dais)
+{
+       struct snd_soc_dai_driver *hdmi_dais;
+       struct hdac_hdmi_cvt *cvt;
+       char name[NAME_SIZE], dai_name[NAME_SIZE];
+       int i = 0;
+       u32 rates, bps;
+       unsigned int rate_max = 384000, rate_min = 8000;
+       u64 formats;
+       int ret;
+
+       hdmi_dais = devm_kzalloc(&hdac->dev,
+                       (sizeof(*hdmi_dais) * num_dais),
+                       GFP_KERNEL);
+       if (!hdmi_dais)
+               return -ENOMEM;
+
+       list_for_each_entry(cvt, &hdmi->cvt_list, head) {
+               ret = snd_hdac_query_supported_pcm(hdac, cvt->nid,
+                                       &rates, &formats, &bps);
+               if (ret)
+                       return ret;
+
+               sprintf(dai_name, "intel-hdmi-hifi%d", i+1);
+               hdmi_dais[i].name = devm_kstrdup(&hdac->dev,
+                                       dai_name, GFP_KERNEL);
+
+               if (!hdmi_dais[i].name)
+                       return -ENOMEM;
+
+               snprintf(name, sizeof(name), "hifi%d", i+1);
+               hdmi_dais[i].playback.stream_name =
+                               devm_kstrdup(&hdac->dev, name, GFP_KERNEL);
+               if (!hdmi_dais[i].playback.stream_name)
+                       return -ENOMEM;
+
+               /*
+                * Set caps based on capability queried from the converter.
+                * It will be constrained runtime based on ELD queried.
+                */
+               hdmi_dais[i].playback.formats = formats;
+               hdmi_dais[i].playback.rates = rates;
+               hdmi_dais[i].playback.rate_max = rate_max;
+               hdmi_dais[i].playback.rate_min = rate_min;
+               hdmi_dais[i].playback.channels_min = 2;
+               hdmi_dais[i].playback.channels_max = 2;
+               hdmi_dais[i].ops = &hdmi_dai_ops;
+
+               i++;
+       }
+
+       *dais = hdmi_dais;
+
        return 0;
 }
 
@@ -431,7 +1243,8 @@ static int hdac_hdmi_add_pin(struct hdac_ext_device *edev, hda_nid_t nid)
  * Parse all nodes and store the cvt/pin nids in array
  * Add one time initialization for pin and cvt widgets
  */
-static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
+static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev,
+               struct snd_soc_dai_driver **dais, int *num_dais)
 {
        hda_nid_t nid;
        int i, num_nodes;
@@ -439,6 +1252,9 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        int ret;
 
+       hdac_hdmi_skl_enable_all_pins(hdac);
+       hdac_hdmi_skl_enable_dp12(hdac);
+
        num_nodes = snd_hdac_get_sub_nodes(hdac, hdac->afg, &nid);
        if (!nid || num_nodes <= 0) {
                dev_warn(&hdac->dev, "HDMI: failed to get afg sub nodes\n");
@@ -479,19 +1295,107 @@ static int hdac_hdmi_parse_and_map_nid(struct hdac_ext_device *edev)
        if (!hdmi->num_pin || !hdmi->num_cvt)
                return -EIO;
 
+       ret = hdac_hdmi_create_dais(hdac, dais, hdmi, hdmi->num_cvt);
+       if (ret) {
+               dev_err(&hdac->dev, "Failed to create dais with err: %d\n",
+                                                       ret);
+               return ret;
+       }
+
+       *num_dais = hdmi->num_cvt;
+
        return hdac_hdmi_init_dai_map(edev);
 }
 
+static void hdac_hdmi_eld_notify_cb(void *aptr, int port)
+{
+       struct hdac_ext_device *edev = aptr;
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pin *pin;
+       struct snd_soc_codec *codec = edev->scodec;
+
+       /* Don't know how this mapping is derived */
+       hda_nid_t pin_nid = port + 0x04;
+
+       dev_dbg(&edev->hdac.dev, "%s: for pin: %d\n", __func__, pin_nid);
+
+       /*
+        * skip notification during system suspend (but not in runtime PM);
+        * the state will be updated at resume. Also since the ELD and
+        * connection states are updated in anyway at the end of the resume,
+        * we can skip it when received during PM process.
+        */
+       if (snd_power_get_state(codec->component.card->snd_card) !=
+                       SNDRV_CTL_POWER_D0)
+               return;
+
+       if (atomic_read(&edev->hdac.in_pm))
+               return;
+
+       list_for_each_entry(pin, &hdmi->pin_list, head) {
+               if (pin->nid == pin_nid)
+                       hdac_hdmi_present_sense(pin, 1);
+       }
+}
+
+static struct i915_audio_component_audio_ops aops = {
+       .pin_eld_notify = hdac_hdmi_eld_notify_cb,
+};
+
+int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int device)
+{
+       char jack_name[NAME_SIZE];
+       struct snd_soc_codec *codec = dai->codec;
+       struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
+       struct snd_soc_dapm_context *dapm =
+               snd_soc_component_get_dapm(&codec->component);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pcm *pcm;
+
+       /*
+        * this is a new PCM device, create new pcm and
+        * add to the pcm list
+        */
+       pcm = kzalloc(sizeof(*pcm), GFP_KERNEL);
+       if (!pcm)
+               return -ENOMEM;
+       pcm->pcm_id = device;
+       pcm->cvt = hdmi->dai_map[dai->id].cvt;
+
+       list_add_tail(&pcm->head, &hdmi->pcm_list);
+
+       sprintf(jack_name, "HDMI/DP, pcm=%d Jack", device);
+
+       return snd_jack_new(dapm->card->snd_card, jack_name,
+               SND_JACK_AVOUT, &pcm->jack, true, false);
+}
+EXPORT_SYMBOL_GPL(hdac_hdmi_jack_init);
+
 static int hdmi_codec_probe(struct snd_soc_codec *codec)
 {
        struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct snd_soc_dapm_context *dapm =
                snd_soc_component_get_dapm(&codec->component);
+       struct hdac_hdmi_pin *pin;
+       int ret;
 
        edev->scodec = codec;
 
-       create_fill_widget_route_map(dapm, &hdmi->dai_map[0]);
+       ret = create_fill_widget_route_map(dapm);
+       if (ret < 0)
+               return ret;
+
+       aops.audio_ptr = edev;
+       ret = snd_hdac_i915_register_notifier(&aops);
+       if (ret < 0) {
+               dev_err(&edev->hdac.dev, "notifier register failed: err: %d\n",
+                               ret);
+               return ret;
+       }
+
+       list_for_each_entry(pin, &hdmi->pin_list, head)
+               hdac_hdmi_present_sense(pin, 1);
 
        /* Imp: Store the card pointer in hda_codec */
        edev->card = dapm->card->snd_card;
@@ -515,44 +1419,73 @@ static int hdmi_codec_remove(struct snd_soc_codec *codec)
        return 0;
 }
 
+#ifdef CONFIG_PM
+static int hdmi_codec_resume(struct snd_soc_codec *codec)
+{
+       struct hdac_ext_device *edev = snd_soc_codec_get_drvdata(codec);
+       struct hdac_hdmi_priv *hdmi = edev->private_data;
+       struct hdac_hdmi_pin *pin;
+       struct hdac_device *hdac = &edev->hdac;
+       struct hdac_bus *bus = hdac->bus;
+       int err;
+       unsigned long timeout;
+
+       hdac_hdmi_skl_enable_all_pins(&edev->hdac);
+       hdac_hdmi_skl_enable_dp12(&edev->hdac);
+
+       /* Power up afg */
+       if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)) {
+
+               snd_hdac_codec_write(hdac, hdac->afg, 0,
+                       AC_VERB_SET_POWER_STATE, AC_PWRST_D0);
+
+               /* Wait till power state is set to D0 */
+               timeout = jiffies + msecs_to_jiffies(1000);
+               while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0)
+                               && time_before(jiffies, timeout)) {
+                       msleep(50);
+               }
+       }
+
+       /*
+        * As the ELD notify callback request is not entertained while the
+        * device is in suspend state. Need to manually check detection of
+        * all pins here.
+        */
+       list_for_each_entry(pin, &hdmi->pin_list, head)
+               hdac_hdmi_present_sense(pin, 1);
+
+       /*
+        * Codec power is turned ON during controller resume.
+        * Turn it OFF here
+        */
+       err = snd_hdac_display_power(bus, false);
+       if (err < 0) {
+               dev_err(bus->dev,
+                       "Cannot turn OFF display power on i915, err: %d\n",
+                       err);
+               return err;
+       }
+
+       return 0;
+}
+#else
+#define hdmi_codec_resume NULL
+#endif
+
 static struct snd_soc_codec_driver hdmi_hda_codec = {
        .probe          = hdmi_codec_probe,
        .remove         = hdmi_codec_remove,
+       .resume         = hdmi_codec_resume,
        .idle_bias_off  = true,
 };
 
-static struct snd_soc_dai_ops hdmi_dai_ops = {
-       .startup = hdac_hdmi_pcm_open,
-       .shutdown = hdac_hdmi_pcm_close,
-       .hw_params = hdac_hdmi_set_hw_params,
-       .prepare = hdac_hdmi_playback_prepare,
-       .hw_free = hdac_hdmi_playback_cleanup,
-};
-
-static struct snd_soc_dai_driver hdmi_dais[] = {
-       {       .name = "intel-hdmi-hif1",
-               .playback = {
-                       .stream_name = "hif1",
-                       .channels_min = 2,
-                       .channels_max = 2,
-                       .rates = SNDRV_PCM_RATE_32000 |
-                               SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
-                               SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
-                               SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
-                       .formats = SNDRV_PCM_FMTBIT_S16_LE |
-                               SNDRV_PCM_FMTBIT_S20_3LE |
-                               SNDRV_PCM_FMTBIT_S24_LE |
-                               SNDRV_PCM_FMTBIT_S32_LE,
-
-               },
-               .ops = &hdmi_dai_ops,
-       },
-};
-
 static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
 {
        struct hdac_device *codec = &edev->hdac;
        struct hdac_hdmi_priv *hdmi_priv;
+       struct snd_soc_dai_driver *hdmi_dais = NULL;
+       int num_dais = 0;
        int ret = 0;
 
        hdmi_priv = devm_kzalloc(&codec->dev, sizeof(*hdmi_priv), GFP_KERNEL);
@@ -565,14 +1498,31 @@ static int hdac_hdmi_dev_probe(struct hdac_ext_device *edev)
 
        INIT_LIST_HEAD(&hdmi_priv->pin_list);
        INIT_LIST_HEAD(&hdmi_priv->cvt_list);
+       INIT_LIST_HEAD(&hdmi_priv->pcm_list);
+       mutex_init(&hdmi_priv->pin_mutex);
 
-       ret = hdac_hdmi_parse_and_map_nid(edev);
-       if (ret < 0)
+       /*
+        * Turned off in the runtime_suspend during the first explicit
+        * pm_runtime_suspend call.
+        */
+       ret = snd_hdac_display_power(edev->hdac.bus, true);
+       if (ret < 0) {
+               dev_err(&edev->hdac.dev,
+                       "Cannot turn on display power on i915 err: %d\n",
+                       ret);
                return ret;
+       }
+
+       ret = hdac_hdmi_parse_and_map_nid(edev, &hdmi_dais, &num_dais);
+       if (ret < 0) {
+               dev_err(&codec->dev,
+                       "Failed in parse and map nid with err: %d\n", ret);
+               return ret;
+       }
 
        /* ASoC specific initialization */
        return snd_soc_register_codec(&codec->dev, &hdmi_hda_codec,
-                       hdmi_dais, ARRAY_SIZE(hdmi_dais));
+                       hdmi_dais, num_dais);
 }
 
 static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
@@ -580,11 +1530,20 @@ static int hdac_hdmi_dev_remove(struct hdac_ext_device *edev)
        struct hdac_hdmi_priv *hdmi = edev->private_data;
        struct hdac_hdmi_pin *pin, *pin_next;
        struct hdac_hdmi_cvt *cvt, *cvt_next;
+       struct hdac_hdmi_pcm *pcm, *pcm_next;
 
        snd_soc_unregister_codec(&edev->hdac.dev);
 
+       list_for_each_entry_safe(pcm, pcm_next, &hdmi->pcm_list, head) {
+               pcm->cvt = NULL;
+               pcm->pin = NULL;
+               list_del(&pcm->head);
+               kfree(pcm);
+       }
+
        list_for_each_entry_safe(cvt, cvt_next, &hdmi->cvt_list, head) {
                list_del(&cvt->head);
+               kfree(cvt->name);
                kfree(cvt);
        }
 
@@ -602,6 +1561,7 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
        struct hdac_ext_device *edev = to_hda_ext_device(dev);
        struct hdac_device *hdac = &edev->hdac;
        struct hdac_bus *bus = hdac->bus;
+       unsigned long timeout;
        int err;
 
        dev_dbg(dev, "Enter: %s\n", __func__);
@@ -611,10 +1571,19 @@ static int hdac_hdmi_runtime_suspend(struct device *dev)
                return 0;
 
        /* Power down afg */
-       if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3))
+       if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)) {
                snd_hdac_codec_write(hdac, hdac->afg, 0,
                        AC_VERB_SET_POWER_STATE, AC_PWRST_D3);
 
+               /* Wait till power state is set to D3 */
+               timeout = jiffies + msecs_to_jiffies(1000);
+               while (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D3)
+                               && time_before(jiffies, timeout)) {
+
+                       msleep(50);
+               }
+       }
+
        err = snd_hdac_display_power(bus, false);
        if (err < 0) {
                dev_err(bus->dev, "Cannot turn on display power on i915\n");
@@ -643,6 +1612,9 @@ static int hdac_hdmi_runtime_resume(struct device *dev)
                return err;
        }
 
+       hdac_hdmi_skl_enable_all_pins(&edev->hdac);
+       hdac_hdmi_skl_enable_dp12(&edev->hdac);
+
        /* Power up afg */
        if (!snd_hdac_check_power_state(hdac, hdac->afg, AC_PWRST_D0))
                snd_hdac_codec_write(hdac, hdac->afg, 0,
@@ -661,6 +1633,7 @@ static const struct dev_pm_ops hdac_hdmi_pm = {
 
 static const struct hda_device_id hdmi_list[] = {
        HDA_CODEC_EXT_ENTRY(0x80862809, 0x100000, "Skylake HDMI", 0),
+       HDA_CODEC_EXT_ENTRY(0x8086280a, 0x100000, "Broxton HDMI", 0),
        {}
 };
 
diff --git a/sound/soc/codecs/hdac_hdmi.h b/sound/soc/codecs/hdac_hdmi.h
new file mode 100644 (file)
index 0000000..8dfd1e0
--- /dev/null
@@ -0,0 +1,6 @@
+#ifndef __HDAC_HDMI_H__
+#define __HDAC_HDMI_H__
+
+int hdac_hdmi_jack_init(struct snd_soc_dai *dai, int pcm);
+
+#endif /* __HDAC_HDMI_H__ */
diff --git a/sound/soc/codecs/max9867.c b/sound/soc/codecs/max9867.c
new file mode 100755 (executable)
index 0000000..2a22fdd
--- /dev/null
@@ -0,0 +1,546 @@
+/*
+ * max9867.c -- max9867 ALSA SoC Audio driver
+ *
+ * Copyright 2013-15 Maxim Integrated Products
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max9867.h"
+
+static const char *const max9867_spmode[] = {
+       "Stereo Diff", "Mono Diff",
+       "Stereo Cap", "Mono Cap",
+       "Stereo Single", "Mono Single",
+       "Stereo Single Fast", "Mono Single Fast"
+};
+static const char *const max9867_sidetone_text[] = {
+       "None", "Left", "Right", "LeftRight", "LeftRightDiv2",
+};
+static const char *const max9867_filter_text[] = {"IIR", "FIR"};
+
+static SOC_ENUM_SINGLE_DECL(max9867_filter, MAX9867_CODECFLTR, 7,
+       max9867_filter_text);
+static SOC_ENUM_SINGLE_DECL(max9867_spkmode, MAX9867_MODECONFIG, 0,
+       max9867_spmode);
+static SOC_ENUM_SINGLE_DECL(max9867_sidetone, MAX9867_DACGAIN, 6,
+       max9867_sidetone_text);
+static DECLARE_TLV_DB_SCALE(max9860_capture_tlv, -600, 200, 0);
+static DECLARE_TLV_DB_SCALE(max9860_mic_tlv, 2000, 100, 1);
+static DECLARE_TLV_DB_SCALE(max9860_adc_left_tlv, -1200, 100, 1);
+static DECLARE_TLV_DB_SCALE(max9860_adc_right_tlv, -1200, 100, 1);
+static const unsigned int max98088_micboost_tlv[] = {
+       TLV_DB_RANGE_HEAD(2),
+       0, 1, TLV_DB_SCALE_ITEM(0, 2000, 0),
+       2, 2, TLV_DB_SCALE_ITEM(3000, 0, 0),
+};
+
+static const struct snd_kcontrol_new max9867_snd_controls[] = {
+       SOC_DOUBLE_R("Master Playback Volume", MAX9867_LEFTVOL,
+                               MAX9867_RIGHTVOL, 0, 63, 1),
+       SOC_DOUBLE_R_TLV("Capture Volume", MAX9867_LEFTMICGAIN,
+                       MAX9867_RIGHTMICGAIN,
+                       0, 15, 1, max9860_capture_tlv),
+       SOC_DOUBLE_R_TLV("Mic Volume", MAX9867_LEFTMICGAIN,
+                       MAX9867_RIGHTMICGAIN, 0, 31, 1, max9860_mic_tlv),
+       SOC_DOUBLE_R_TLV("Mic Boost Volume", MAX9867_LEFTMICGAIN,
+                       MAX9867_RIGHTMICGAIN, 5, 3, 0, max98088_micboost_tlv),
+       SOC_ENUM("Digital Sidetone Src", max9867_sidetone),
+       SOC_SINGLE("Sidetone Volume", MAX9867_DACGAIN, 0, 31, 1),
+       SOC_SINGLE("DAC Volume", MAX9867_DACLEVEL, 4, 3, 0),
+       SOC_SINGLE("DAC Attenuation", MAX9867_DACLEVEL, 0, 15, 1),
+       SOC_SINGLE_TLV("ADC Left Volume", MAX9867_ADCLEVEL,
+                       4, 15, 1, max9860_adc_left_tlv),
+       SOC_SINGLE_TLV("ADC Right Volume", MAX9867_ADCLEVEL,
+                       0, 15, 1, max9860_adc_right_tlv),
+       SOC_ENUM("Speaker Mode", max9867_spkmode),
+       SOC_SINGLE("Volume Smoothing Switch", MAX9867_MODECONFIG, 6, 1, 0),
+       SOC_SINGLE("ZCD Switch", MAX9867_MODECONFIG, 5, 1, 0),
+       SOC_ENUM("DSP Filter", max9867_filter),
+};
+
+static const char *const max9867_mux[] = {"None", "Mic", "Line", "Mic_Line"};
+
+static SOC_ENUM_SINGLE_DECL(max9867_mux_enum,
+       MAX9867_INPUTCONFIG, MAX9867_INPUT_SHIFT,
+       max9867_mux);
+
+static const struct snd_kcontrol_new max9867_dapm_mux_controls =
+       SOC_DAPM_ENUM("Route", max9867_mux_enum);
+
+static const struct snd_kcontrol_new max9867_left_dapm_control =
+       SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 6, 1, 0);
+static const struct snd_kcontrol_new max9867_right_dapm_control =
+       SOC_DAPM_SINGLE("Switch", MAX9867_PWRMAN, 5, 1, 0);
+static const struct snd_kcontrol_new max9867_line_dapm_control =
+       SOC_DAPM_SINGLE("Switch", MAX9867_LEFTLINELVL, 6, 1, 1);
+
+static const struct snd_soc_dapm_widget max9867_dapm_widgets[] = {
+       SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_DAC("Left DAC", NULL, MAX9867_PWRMAN, 3, 0),
+       SND_SOC_DAPM_DAC("Right DAC", NULL, MAX9867_PWRMAN, 2, 0),
+       SND_SOC_DAPM_MIXER("Output Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_OUTPUT("HPOUT"),
+
+       SND_SOC_DAPM_AIF_IN("DAI_IN", "HiFi Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_ADC("Left ADC", "HiFi Capture", MAX9867_PWRMAN, 1, 0),
+       SND_SOC_DAPM_ADC("Right ADC", "HiFi Capture", MAX9867_PWRMAN, 0, 0),
+       SND_SOC_DAPM_MUX("Input Mux", SND_SOC_NOPM, 0, 0,
+               &max9867_dapm_mux_controls),
+
+       SND_SOC_DAPM_MIXER("Input Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_SWITCH("Left Line", MAX9867_LEFTLINELVL, 6, 1,
+               &max9867_left_dapm_control),
+       SND_SOC_DAPM_SWITCH("Right Line", MAX9867_RIGTHLINELVL, 6, 1,
+               &max9867_right_dapm_control),
+       SND_SOC_DAPM_SWITCH("Line Mixer", SND_SOC_NOPM, 0, 0,
+               &max9867_line_dapm_control),
+       SND_SOC_DAPM_INPUT("LINE_IN"),
+};
+
+static const struct snd_soc_dapm_route max9867_audio_map[] = {
+       {"Left DAC", NULL, "DAI_OUT"},
+       {"Right DAC", NULL, "DAI_OUT"},
+       {"Output Mixer", NULL, "Left DAC"},
+       {"Output Mixer", NULL, "Right DAC"},
+       {"HPOUT", NULL, "Output Mixer"},
+
+       {"Left ADC", NULL, "DAI_IN"},
+       {"Right ADC", NULL, "DAI_IN"},
+       {"Input Mixer", NULL, "Left ADC"},
+       {"Input Mixer", NULL, "Right ADC"},
+       {"Input Mux", "Line", "Input Mixer"},
+       {"Input Mux", "Mic", "Input Mixer"},
+       {"Input Mux", "Mic_Line", "Input Mixer"},
+       {"Right Line", "Switch", "Input Mux"},
+       {"Left Line", "Switch", "Input Mux"},
+       {"LINE_IN", NULL, "Left Line"},
+       {"LINE_IN", NULL, "Right Line"},
+};
+
+enum rates {
+       pcm_rate_8, pcm_rate_16, pcm_rate_24,
+       pcm_rate_32, pcm_rate_44,
+       pcm_rate_48, max_pcm_rate,
+};
+
+struct ni_div_rates {
+       u32 mclk;
+       u16 ni[max_pcm_rate];
+} ni_div[] = {
+       {11289600, {0x116A, 0x22D4, 0x343F, 0x45A9, 0x6000, 0x687D} },
+       {12000000, {0x1062, 0x20C5, 0x3127, 0x4189, 0x5A51, 0x624E} },
+       {12288000, {0x1000, 0x2000, 0x3000, 0x4000, 0x5833, 0x6000} },
+       {13000000, {0x0F20, 0x1E3F, 0x2D5F, 0x3C7F, 0x535F, 0x5ABE} },
+       {19200000, {0x0A3D, 0x147B, 0x1EB8, 0x28F6, 0x3873, 0x3D71} },
+       {24000000, {0x1062, 0x20C5, 0x1893, 0x4189, 0x5A51, 0x624E} },
+       {26000000, {0x0F20, 0x1E3F, 0x16AF, 0x3C7F, 0x535F, 0x5ABE} },
+       {27000000, {0x0E90, 0x1D21, 0x15D8, 0x3A41, 0x5048, 0x5762} },
+};
+
+static inline int get_ni_value(int mclk, int rate)
+{
+       int i, ret = 0;
+
+       /* find the closest rate index*/
+       for (i = 0; i < ARRAY_SIZE(ni_div); i++) {
+               if (ni_div[i].mclk >= mclk)
+                       break;
+       }
+       if (i == ARRAY_SIZE(ni_div))
+               return -EINVAL;
+
+       switch (rate) {
+       case 8000:
+               return ni_div[i].ni[pcm_rate_8];
+       case 16000:
+               return ni_div[i].ni[pcm_rate_16];
+       case 32000:
+               return ni_div[i].ni[pcm_rate_32];
+       case 44100:
+               return ni_div[i].ni[pcm_rate_44];
+       case 48000:
+               return ni_div[i].ni[pcm_rate_48];
+       default:
+               pr_err("%s wrong rate %d\n", __func__, rate);
+               ret = -EINVAL;
+       }
+       return ret;
+}
+
+static int max9867_dai_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+       unsigned int ni_h, ni_l;
+       int value;
+
+       value = get_ni_value(max9867->sysclk, params_rate(params));
+       if (value < 0)
+               return value;
+
+       ni_h = (0xFF00 & value) >> 8;
+       ni_l = 0x00FF & value;
+       /* set up the ni value */
+       regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
+               MAX9867_NI_HIGH_MASK, ni_h);
+       regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
+               MAX9867_NI_LOW_MASK, ni_l);
+       if (!max9867->master) {
+               /*
+                * digital pll locks on to any externally supplied LRCLK signal
+                * and also enable rapid lock mode.
+                */
+               regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKLOW,
+                       MAX9867_RAPID_LOCK, MAX9867_RAPID_LOCK);
+               regmap_update_bits(max9867->regmap, MAX9867_AUDIOCLKHIGH,
+                       MAX9867_PLL, MAX9867_PLL);
+       } else {
+               unsigned long int bclk_rate, pclk_bclk_ratio;
+               int bclk_value;
+
+               bclk_rate = params_rate(params) * 2 * params_width(params);
+               pclk_bclk_ratio = max9867->pclk/bclk_rate;
+               switch (params_width(params)) {
+               case 8:
+               case 16:
+                       switch (pclk_bclk_ratio) {
+                       case 2:
+                               bclk_value = MAX9867_IFC1B_PCLK_2;
+                               break;
+                       case 4:
+                               bclk_value = MAX9867_IFC1B_PCLK_4;
+                               break;
+                       case 8:
+                               bclk_value = MAX9867_IFC1B_PCLK_8;
+                               break;
+                       case 16:
+                               bclk_value = MAX9867_IFC1B_PCLK_16;
+                               break;
+                       default:
+                               dev_err(codec->dev,
+                                       "unsupported sampling rate\n");
+                               return -EINVAL;
+                       }
+                       break;
+               case 24:
+                       bclk_value = MAX9867_IFC1B_24BIT;
+                       break;
+               case 32:
+                       bclk_value = MAX9867_IFC1B_32BIT;
+                       break;
+               default:
+                       dev_err(codec->dev, "unsupported sampling rate\n");
+                       return -EINVAL;
+               }
+               regmap_update_bits(max9867->regmap, MAX9867_IFC1B,
+                       MAX9867_IFC1B_BCLK_MASK, bclk_value);
+       }
+       return 0;
+}
+
+static int max9867_prepare(struct snd_pcm_substream *substream,
+                        struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+
+       regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
+               MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK);
+       return 0;
+}
+
+static int max9867_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+
+       if (mute)
+               regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL,
+                       MAX9867_DAC_MUTE_MASK, MAX9867_DAC_MUTE_MASK);
+       else
+               regmap_update_bits(max9867->regmap, MAX9867_DACLEVEL,
+                       MAX9867_DAC_MUTE_MASK, 0);
+       return 0;
+}
+
+static int max9867_set_dai_sysclk(struct snd_soc_dai *codec_dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+       int value = 0;
+
+       /* Set the prescaler based on the master clock frequency*/
+       if (freq >= 10000000 && freq <= 20000000) {
+               value |= MAX9867_PSCLK_10_20;
+               max9867->pclk =  freq;
+       } else if (freq >= 20000000 && freq <= 40000000) {
+               value |= MAX9867_PSCLK_20_40;
+               max9867->pclk =  freq/2;
+       } else if (freq >= 40000000 && freq <= 60000000) {
+               value |= MAX9867_PSCLK_40_60;
+               max9867->pclk =  freq/4;
+       } else {
+               pr_err("bad clock frequency %d", freq);
+               return -EINVAL;
+       }
+       value = value << MAX9867_PSCLK_SHIFT;
+       max9867->sysclk = freq;
+       /* exact integer mode is not supported */
+       value &= ~MAX9867_FREQ_MASK;
+       regmap_update_bits(max9867->regmap, MAX9867_SYSCLK,
+                       MAX9867_PSCLK_MASK, value);
+       return 0;
+}
+
+static int max9867_dai_set_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+       u8 iface1A = 0, iface1B = 0;
+       int ret;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               max9867->master = 1;
+               iface1A |= MAX9867_MASTER;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               max9867->master = 0;
+               iface1A &= ~MAX9867_MASTER;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* for i2s compatible mode */
+       iface1A |= MAX9867_I2S_DLY;
+       /* SDOUT goes to hiz state after all data is transferred */
+       iface1A |= MAX9867_SDOUT_HIZ;
+
+       /* Clock inversion bits, BCI and WCI */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               iface1A |= MAX9867_WCI_MODE | MAX9867_BCI_MODE;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               iface1A |= MAX9867_BCI_MODE;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               iface1A |= MAX9867_WCI_MODE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = regmap_write(max9867->regmap, MAX9867_IFC1A, iface1A);
+       ret = regmap_write(max9867->regmap, MAX9867_IFC1B, iface1B);
+       return 0;
+}
+
+static struct snd_soc_dai_ops max9867_dai_ops = {
+       .set_fmt = max9867_dai_set_fmt,
+       .set_sysclk     = max9867_set_dai_sysclk,
+       .prepare        = max9867_prepare,
+       .digital_mute   = max9867_mute,
+       .hw_params = max9867_dai_hw_params,
+};
+
+#define MAX9867_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 |\
+       SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000)
+#define MAX9867_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
+
+static struct snd_soc_dai_driver max9867_dai[] = {
+       {
+       .name = "max9867-aif1",
+       .playback = {
+               .stream_name = "HiFi Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX9867_RATES,
+               .formats = MAX9867_FORMATS,
+       },
+       .capture = {
+               .stream_name = "HiFi Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = MAX9867_RATES,
+               .formats = MAX9867_FORMATS,
+       },
+       .ops = &max9867_dai_ops,
+       }
+};
+
+#ifdef CONFIG_PM_SLEEP
+static int max9867_suspend(struct device *dev)
+{
+       struct max9867_priv *max9867 = dev_get_drvdata(dev);
+
+       /* Drop down to power saving mode when system is suspended */
+       regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
+               MAX9867_SHTDOWN_MASK, ~MAX9867_SHTDOWN_MASK);
+       return 0;
+}
+
+static int max9867_resume(struct device *dev)
+{
+       struct max9867_priv *max9867 = dev_get_drvdata(dev);
+
+       regmap_update_bits(max9867->regmap, MAX9867_PWRMAN,
+               MAX9867_SHTDOWN_MASK, MAX9867_SHTDOWN_MASK);
+       return 0;
+}
+#endif
+
+static int max9867_probe(struct snd_soc_codec *codec)
+{
+       struct max9867_priv *max9867 = snd_soc_codec_get_drvdata(codec);
+
+       dev_dbg(codec->dev, "max98090_probe\n");
+       max9867->codec = codec;
+       return 0;
+}
+
+static struct snd_soc_codec_driver max9867_codec = {
+       .probe = max9867_probe,
+       .controls = max9867_snd_controls,
+       .num_controls = ARRAY_SIZE(max9867_snd_controls),
+       .dapm_routes = max9867_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(max9867_audio_map),
+       .dapm_widgets = max9867_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(max9867_dapm_widgets),
+};
+
+static bool max9867_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX9867_STATUS:
+       case MAX9867_JACKSTATUS:
+       case MAX9867_AUXHIGH:
+       case MAX9867_AUXLOW:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const struct reg_default max9867_reg[] = {
+       { 0x04, 0x00 },
+       { 0x05, 0x00 },
+       { 0x06, 0x00 },
+       { 0x07, 0x00 },
+       { 0x08, 0x00 },
+       { 0x09, 0x00 },
+       { 0x0A, 0x00 },
+       { 0x0B, 0x00 },
+       { 0x0C, 0x00 },
+       { 0x0D, 0x00 },
+       { 0x0E, 0x00 },
+       { 0x0F, 0x00 },
+       { 0x10, 0x00 },
+       { 0x11, 0x00 },
+       { 0x12, 0x00 },
+       { 0x13, 0x00 },
+       { 0x14, 0x00 },
+       { 0x15, 0x00 },
+       { 0x16, 0x00 },
+       { 0x17, 0x00 },
+};
+
+static const struct regmap_config max9867_regmap = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .max_register   = MAX9867_REVISION,
+       .reg_defaults   = max9867_reg,
+       .num_reg_defaults = ARRAY_SIZE(max9867_reg),
+       .volatile_reg   = max9867_volatile_register,
+       .cache_type     = REGCACHE_RBTREE,
+};
+
+static int max9867_i2c_probe(struct i2c_client *i2c,
+               const struct i2c_device_id *id)
+{
+       struct max9867_priv *max9867;
+       int ret = 0, reg;
+
+       max9867 = devm_kzalloc(&i2c->dev,
+                       sizeof(*max9867), GFP_KERNEL);
+       if (!max9867)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, max9867);
+       max9867->regmap = devm_regmap_init_i2c(i2c, &max9867_regmap);
+       if (IS_ERR(max9867->regmap)) {
+               ret = PTR_ERR(max9867->regmap);
+               dev_err(&i2c->dev,
+                               "Failed to allocate regmap: %d\n", ret);
+               return ret;
+       }
+       ret = regmap_read(max9867->regmap,
+                       MAX9867_REVISION, &reg);
+       if (ret < 0) {
+               dev_err(&i2c->dev, "Failed to read: %d\n", ret);
+               return ret;
+       }
+       dev_info(&i2c->dev, "device revision: %x\n", reg);
+       ret = snd_soc_register_codec(&i2c->dev, &max9867_codec,
+                       max9867_dai, ARRAY_SIZE(max9867_dai));
+       if (ret < 0) {
+               dev_err(&i2c->dev, "Failed to register codec: %d\n", ret);
+               return ret;
+       }
+       return ret;
+}
+
+static int max9867_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static const struct i2c_device_id max9867_i2c_id[] = {
+       { "max9867", 0 },
+       { }
+};
+
+static const struct of_device_id max9867_of_match[] = {
+       { .compatible = "maxim,max9867", },
+       { }
+};
+
+MODULE_DEVICE_TABLE(i2c, max9867_i2c_id);
+
+static const struct dev_pm_ops max9867_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(max9867_suspend, max9867_resume)
+};
+
+static struct i2c_driver max9867_i2c_driver = {
+       .driver = {
+               .name = "max9867",
+               .of_match_table = of_match_ptr(max9867_of_match),
+               .pm = &max9867_pm_ops,
+       },
+       .probe  = max9867_i2c_probe,
+       .remove = max9867_i2c_remove,
+       .id_table = max9867_i2c_id,
+};
+
+module_i2c_driver(max9867_i2c_driver);
+
+MODULE_AUTHOR("anish kumar <yesanishhere@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC MAX9867 driver");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max9867.h b/sound/soc/codecs/max9867.h
new file mode 100755 (executable)
index 0000000..65590b4
--- /dev/null
@@ -0,0 +1,83 @@
+/*
+ * max9867.h -- MAX9867 ALSA SoC Audio driver
+ *
+ * Copyright 2013-2015 Maxim Integrated Products
+ *
+ * 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 _MAX9867_H
+#define _MAX9867_H
+
+/* MAX9867 register space */
+
+#define MAX9867_STATUS        0x00
+#define MAX9867_JACKSTATUS   0x01
+#define MAX9867_AUXHIGH      0x02
+#define MAX9867_AUXLOW       0x03
+#define MAX9867_INTEN        0x04
+#define MAX9867_SYSCLK       0x05
+#define MAX9867_FREQ_MASK    0xF
+#define MAX9867_PSCLK_SHIFT  0x4
+#define MAX9867_PSCLK_WIDTH  0x2
+#define MAX9867_PSCLK_MASK   (0x03<<MAX9867_PSCLK_SHIFT)
+#define MAX9867_PSCLK_10_20  0x1
+#define MAX9867_PSCLK_20_40  0x2
+#define MAX9867_PSCLK_40_60  0x3
+#define MAX9867_AUDIOCLKHIGH 0x06
+#define MAX9867_NI_HIGH_WIDTH 0x7
+#define MAX9867_NI_HIGH_MASK 0x7F
+#define MAX9867_NI_LOW_MASK 0x7F
+#define MAX9867_NI_LOW_SHIFT 0x1
+#define MAX9867_PLL     (1<<7)
+#define MAX9867_AUDIOCLKLOW  0x07
+#define MAX9867_RAPID_LOCK   0x01
+#define MAX9867_IFC1A        0x08
+#define MAX9867_MASTER       (1<<7)
+#define MAX9867_I2S_DLY      (1<<4)
+#define MAX9867_SDOUT_HIZ    (1<<3)
+#define MAX9867_TDM_MODE     (1<<2)
+#define MAX9867_WCI_MODE     (1<<6)
+#define MAX9867_BCI_MODE     (1<<5)
+#define MAX9867_IFC1B        0x09
+#define MAX9867_IFC1B_BCLK_MASK 7
+#define MAX9867_IFC1B_32BIT  0x01
+#define MAX9867_IFC1B_24BIT  0x02
+#define MAX9867_IFC1B_PCLK_2 4
+#define MAX9867_IFC1B_PCLK_4 5
+#define MAX9867_IFC1B_PCLK_8 6
+#define MAX9867_IFC1B_PCLK_16 7
+#define MAX9867_CODECFLTR    0x0a
+#define MAX9867_DACGAIN      0x0b
+#define MAX9867_DACLEVEL     0x0c
+#define MAX9867_DAC_MUTE_SHIFT 0x6
+#define MAX9867_DAC_MUTE_WIDTH 0x1
+#define MAX9867_DAC_MUTE_MASK (0x1<<MAX9867_DAC_MUTE_SHIFT)
+#define MAX9867_ADCLEVEL     0x0d
+#define MAX9867_LEFTLINELVL  0x0e
+#define MAX9867_RIGTHLINELVL 0x0f
+#define MAX9867_LEFTVOL      0x10
+#define MAX9867_RIGHTVOL     0x11
+#define MAX9867_LEFTMICGAIN  0x12
+#define MAX9867_RIGHTMICGAIN 0x13
+#define MAX9867_INPUTCONFIG  0x14
+#define MAX9867_INPUT_SHIFT  0x6
+#define MAX9867_MICCONFIG    0x15
+#define MAX9867_MODECONFIG   0x16
+#define MAX9867_PWRMAN       0x17
+#define MAX9867_SHTDOWN_MASK (1<<7)
+#define MAX9867_REVISION     0xff
+
+#define MAX9867_CACHEREGNUM 10
+
+/* codec private data */
+struct max9867_priv {
+       struct regmap *regmap;
+       struct snd_soc_codec *codec;
+       unsigned int sysclk;
+       unsigned int pclk;
+       unsigned int master;
+};
+#endif
diff --git a/sound/soc/codecs/max98926.c b/sound/soc/codecs/max98926.c
new file mode 100644 (file)
index 0000000..8d14ada
--- /dev/null
@@ -0,0 +1,606 @@
+/*
+ * max98926.c -- ALSA SoC MAX98926 driver
+ * Copyright 2013-15 Maxim Integrated Products
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+#include <linux/delay.h>
+#include <linux/i2c.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/cdev.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include "max98926.h"
+
+static const char * const max98926_boost_voltage_txt[] = {
+       "8.5V", "8.25V", "8.0V", "7.75V", "7.5V", "7.25V", "7.0V", "6.75V",
+       "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V", "6.5V"
+};
+
+static const char * const max98926_boost_current_txt[] = {
+       "0.6", "0.8", "1.0", "1.2", "1.4", "1.6", "1.8", "2.0",
+       "2.2", "2.4", "2.6", "2.8", "3.2", "3.6", "4.0", "4.4"
+};
+
+static const char *const max98926_dai_txt[] = {
+       "Left", "Right", "LeftRight", "LeftRightDiv2",
+};
+
+static const char *const max98926_pdm_ch_text[] = {
+       "Current", "Voltage",
+};
+
+static const char *const max98926_hpf_cutoff_txt[] = {
+       "Disable", "DC Block", "100Hz",
+       "200Hz", "400Hz", "800Hz",
+};
+
+static const struct reg_default max98926_reg[] = {
+       { 0x0B, 0x00 }, /* IRQ Enable0 */
+       { 0x0C, 0x00 }, /* IRQ Enable1 */
+       { 0x0D, 0x00 }, /* IRQ Enable2 */
+       { 0x0E, 0x00 }, /* IRQ Clear0 */
+       { 0x0F, 0x00 }, /* IRQ Clear1 */
+       { 0x10, 0x00 }, /* IRQ Clear2 */
+       { 0x11, 0xC0 }, /* Map0 */
+       { 0x12, 0x00 }, /* Map1 */
+       { 0x13, 0x00 }, /* Map2 */
+       { 0x14, 0xF0 }, /* Map3 */
+       { 0x15, 0x00 }, /* Map4 */
+       { 0x16, 0xAB }, /* Map5 */
+       { 0x17, 0x89 }, /* Map6 */
+       { 0x18, 0x00 }, /* Map7 */
+       { 0x19, 0x00 }, /* Map8 */
+       { 0x1A, 0x04 }, /* DAI Clock Mode 1 */
+       { 0x1B, 0x00 }, /* DAI Clock Mode 2 */
+       { 0x1C, 0x00 }, /* DAI Clock Divider Denominator MSBs */
+       { 0x1D, 0x00 }, /* DAI Clock Divider Denominator LSBs */
+       { 0x1E, 0xF0 }, /* DAI Clock Divider Numerator MSBs */
+       { 0x1F, 0x00 }, /* DAI Clock Divider Numerator LSBs */
+       { 0x20, 0x50 }, /* Format */
+       { 0x21, 0x00 }, /* TDM Slot Select */
+       { 0x22, 0x00 }, /* DOUT Configuration VMON */
+       { 0x23, 0x00 }, /* DOUT Configuration IMON */
+       { 0x24, 0x00 }, /* DOUT Configuration VBAT */
+       { 0x25, 0x00 }, /* DOUT Configuration VBST */
+       { 0x26, 0x00 }, /* DOUT Configuration FLAG */
+       { 0x27, 0xFF }, /* DOUT HiZ Configuration 1 */
+       { 0x28, 0xFF }, /* DOUT HiZ Configuration 2 */
+       { 0x29, 0xFF }, /* DOUT HiZ Configuration 3 */
+       { 0x2A, 0xFF }, /* DOUT HiZ Configuration 4 */
+       { 0x2B, 0x02 }, /* DOUT Drive Strength */
+       { 0x2C, 0x90 }, /* Filters */
+       { 0x2D, 0x00 }, /* Gain */
+       { 0x2E, 0x02 }, /* Gain Ramping */
+       { 0x2F, 0x00 }, /* Speaker Amplifier */
+       { 0x30, 0x0A }, /* Threshold */
+       { 0x31, 0x00 }, /* ALC Attack */
+       { 0x32, 0x80 }, /* ALC Atten and Release */
+       { 0x33, 0x00 }, /* ALC Infinite Hold Release */
+       { 0x34, 0x92 }, /* ALC Configuration */
+       { 0x35, 0x01 }, /* Boost Converter */
+       { 0x36, 0x00 }, /* Block Enable */
+       { 0x37, 0x00 }, /* Configuration */
+       { 0x38, 0x00 }, /* Global Enable */
+       { 0x3A, 0x00 }, /* Boost Limiter */
+};
+
+static const struct soc_enum max98926_voltage_enum[] = {
+       SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS, 0,
+               ARRAY_SIZE(max98926_pdm_ch_text),
+               max98926_pdm_ch_text),
+};
+
+static const struct snd_kcontrol_new max98926_voltage_control =
+       SOC_DAPM_ENUM("Route", max98926_voltage_enum);
+
+static const struct soc_enum max98926_current_enum[] = {
+       SOC_ENUM_SINGLE(MAX98926_DAI_CLK_DIV_N_LSBS,
+               MAX98926_PDM_SOURCE_1_SHIFT,
+               ARRAY_SIZE(max98926_pdm_ch_text),
+               max98926_pdm_ch_text),
+};
+
+static const struct snd_kcontrol_new max98926_current_control =
+       SOC_DAPM_ENUM("Route", max98926_current_enum);
+
+static const struct snd_kcontrol_new max98926_mixer_controls[] = {
+       SOC_DAPM_SINGLE("PCM Single Switch", MAX98926_SPK_AMP,
+               MAX98926_INSELECT_MODE_SHIFT, 0, 0),
+       SOC_DAPM_SINGLE("PDM Single Switch", MAX98926_SPK_AMP,
+               MAX98926_INSELECT_MODE_SHIFT, 1, 0),
+};
+
+static const struct snd_kcontrol_new max98926_dai_controls[] = {
+       SOC_DAPM_SINGLE("Left", MAX98926_GAIN,
+               MAX98926_DAC_IN_SEL_SHIFT, 0, 0),
+       SOC_DAPM_SINGLE("Right", MAX98926_GAIN,
+               MAX98926_DAC_IN_SEL_SHIFT, 1, 0),
+       SOC_DAPM_SINGLE("LeftRight", MAX98926_GAIN,
+               MAX98926_DAC_IN_SEL_SHIFT, 2, 0),
+       SOC_DAPM_SINGLE("(Left+Right)/2 Switch", MAX98926_GAIN,
+               MAX98926_DAC_IN_SEL_SHIFT, 3, 0),
+};
+
+static const struct snd_soc_dapm_widget max98926_dapm_widgets[] = {
+       SND_SOC_DAPM_AIF_IN("DAI_OUT", "HiFi Playback", 0,
+               SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_DAC("Amp Enable", NULL, MAX98926_BLOCK_ENABLE,
+               MAX98926_SPK_EN_SHIFT, 0),
+       SND_SOC_DAPM_SUPPLY("Global Enable", MAX98926_GLOBAL_ENABLE,
+               MAX98926_EN_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("VI Enable", MAX98926_BLOCK_ENABLE,
+               MAX98926_ADC_IMON_EN_WIDTH |
+               MAX98926_ADC_VMON_EN_SHIFT,
+               0, NULL, 0),
+       SND_SOC_DAPM_PGA("BST Enable", MAX98926_BLOCK_ENABLE,
+               MAX98926_BST_EN_SHIFT, 0, NULL, 0),
+       SND_SOC_DAPM_OUTPUT("BE_OUT"),
+       SND_SOC_DAPM_MIXER("PCM Sel", MAX98926_SPK_AMP,
+               MAX98926_INSELECT_MODE_SHIFT, 0,
+               &max98926_mixer_controls[0],
+               ARRAY_SIZE(max98926_mixer_controls)),
+       SND_SOC_DAPM_MIXER("DAI Sel",
+               MAX98926_GAIN, MAX98926_DAC_IN_SEL_SHIFT, 0,
+               &max98926_dai_controls[0],
+               ARRAY_SIZE(max98926_dai_controls)),
+       SND_SOC_DAPM_MUX("PDM CH1 Source",
+               MAX98926_DAI_CLK_DIV_N_LSBS,
+               MAX98926_PDM_CURRENT_SHIFT,
+               0, &max98926_current_control),
+       SND_SOC_DAPM_MUX("PDM CH0 Source",
+               MAX98926_DAI_CLK_DIV_N_LSBS,
+               MAX98926_PDM_VOLTAGE_SHIFT,
+               0, &max98926_voltage_control),
+};
+
+static const struct snd_soc_dapm_route max98926_audio_map[] = {
+       {"VI Enable", NULL, "DAI_OUT"},
+       {"DAI Sel", "Left", "VI Enable"},
+       {"DAI Sel", "Right", "VI Enable"},
+       {"DAI Sel", "LeftRight", "VI Enable"},
+       {"DAI Sel", "LeftRightDiv2", "VI Enable"},
+       {"PCM Sel", "PCM", "DAI Sel"},
+
+       {"PDM CH1 Source", "Current", "DAI_OUT"},
+       {"PDM CH1 Source", "Voltage", "DAI_OUT"},
+       {"PDM CH0 Source", "Current", "DAI_OUT"},
+       {"PDM CH0 Source", "Voltage", "DAI_OUT"},
+       {"PCM Sel", "Analog", "PDM CH1 Source"},
+       {"PCM Sel", "Analog", "PDM CH0 Source"},
+       {"Amp Enable", NULL, "PCM Sel"},
+
+       {"BST Enable", NULL, "Amp Enable"},
+       {"BE_OUT", NULL, "BST Enable"},
+};
+
+static bool max98926_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX98926_VBAT_DATA:
+       case MAX98926_VBST_DATA:
+       case MAX98926_LIVE_STATUS0:
+       case MAX98926_LIVE_STATUS1:
+       case MAX98926_LIVE_STATUS2:
+       case MAX98926_STATE0:
+       case MAX98926_STATE1:
+       case MAX98926_STATE2:
+       case MAX98926_FLAG0:
+       case MAX98926_FLAG1:
+       case MAX98926_FLAG2:
+       case MAX98926_VERSION:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool max98926_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case MAX98926_IRQ_CLEAR0:
+       case MAX98926_IRQ_CLEAR1:
+       case MAX98926_IRQ_CLEAR2:
+       case MAX98926_ALC_HOLD_RLS:
+               return false;
+       default:
+               return true;
+       }
+};
+
+DECLARE_TLV_DB_SCALE(max98926_spk_tlv, -600, 100, 0);
+DECLARE_TLV_DB_RANGE(max98926_current_tlv,
+       0, 11, TLV_DB_SCALE_ITEM(20, 20, 0),
+       12, 15, TLV_DB_SCALE_ITEM(320, 40, 0),
+);
+
+static SOC_ENUM_SINGLE_DECL(max98926_dac_hpf_cutoff,
+               MAX98926_FILTERS, MAX98926_DAC_HPF_SHIFT,
+               max98926_hpf_cutoff_txt);
+
+static SOC_ENUM_SINGLE_DECL(max98926_boost_voltage,
+               MAX98926_CONFIGURATION, MAX98926_BST_VOUT_SHIFT,
+               max98926_boost_voltage_txt);
+
+static const struct snd_kcontrol_new max98926_snd_controls[] = {
+       SOC_SINGLE_TLV("Speaker Volume", MAX98926_GAIN,
+               MAX98926_SPK_GAIN_SHIFT,
+               (1<<MAX98926_SPK_GAIN_WIDTH)-1, 0,
+               max98926_spk_tlv),
+       SOC_SINGLE("Ramp Switch", MAX98926_GAIN_RAMPING,
+               MAX98926_SPK_RMP_EN_SHIFT, 1, 0),
+       SOC_SINGLE("ZCD Switch", MAX98926_GAIN_RAMPING,
+               MAX98926_SPK_ZCD_EN_SHIFT, 1, 0),
+       SOC_SINGLE("ALC Switch", MAX98926_THRESHOLD,
+               MAX98926_ALC_EN_SHIFT, 1, 0),
+       SOC_SINGLE("ALC Threshold", MAX98926_THRESHOLD,
+               MAX98926_ALC_TH_SHIFT,
+               (1<<MAX98926_ALC_TH_WIDTH)-1, 0),
+       SOC_ENUM("Boost Output Voltage", max98926_boost_voltage),
+       SOC_SINGLE_TLV("Boost Current Limit", MAX98926_BOOST_LIMITER,
+               MAX98926_BST_ILIM_SHIFT,
+               (1<<MAX98926_BST_ILIM_SHIFT)-1, 0,
+               max98926_current_tlv),
+       SOC_ENUM("DAC HPF Cutoff", max98926_dac_hpf_cutoff),
+       SOC_DOUBLE("PDM Channel One", MAX98926_DAI_CLK_DIV_N_LSBS,
+               MAX98926_PDM_CHANNEL_1_SHIFT,
+               MAX98926_PDM_CHANNEL_1_HIZ, 1, 0),
+       SOC_DOUBLE("PDM Channel Zero", MAX98926_DAI_CLK_DIV_N_LSBS,
+               MAX98926_PDM_CHANNEL_0_SHIFT,
+               MAX98926_PDM_CHANNEL_0_HIZ, 1, 0),
+};
+
+static const struct {
+       int rate;
+       int  sr;
+} rate_table[] = {
+       {
+               .rate = 8000,
+               .sr = 0,
+       },
+       {
+               .rate = 11025,
+               .sr = 1,
+       },
+       {
+               .rate = 12000,
+               .sr = 2,
+       },
+       {
+               .rate = 16000,
+               .sr = 3,
+       },
+       {
+               .rate = 22050,
+               .sr = 4,
+       },
+       {
+               .rate = 24000,
+               .sr = 5,
+       },
+       {
+               .rate = 32000,
+               .sr = 6,
+       },
+       {
+               .rate = 44100,
+               .sr = 7,
+       },
+       {
+               .rate = 48000,
+               .sr = 8,
+       },
+};
+
+static void max98926_set_sense_data(struct max98926_priv *max98926)
+{
+       regmap_update_bits(max98926->regmap,
+               MAX98926_DOUT_CFG_VMON,
+               MAX98926_DAI_VMON_EN_MASK,
+               MAX98926_DAI_VMON_EN_MASK);
+       regmap_update_bits(max98926->regmap,
+               MAX98926_DOUT_CFG_IMON,
+               MAX98926_DAI_IMON_EN_MASK,
+               MAX98926_DAI_IMON_EN_MASK);
+
+       if (!max98926->interleave_mode) {
+               /* set VMON slots */
+               regmap_update_bits(max98926->regmap,
+                       MAX98926_DOUT_CFG_VMON,
+                       MAX98926_DAI_VMON_SLOT_MASK,
+                       max98926->v_slot);
+               /* set IMON slots */
+               regmap_update_bits(max98926->regmap,
+                       MAX98926_DOUT_CFG_IMON,
+                       MAX98926_DAI_IMON_SLOT_MASK,
+                       max98926->i_slot);
+       } else {
+               /* enable interleave mode */
+               regmap_update_bits(max98926->regmap,
+                       MAX98926_FORMAT,
+                       MAX98926_DAI_INTERLEAVE_MASK,
+                       MAX98926_DAI_INTERLEAVE_MASK);
+               /* set interleave slots */
+               regmap_update_bits(max98926->regmap,
+                       MAX98926_DOUT_CFG_VBAT,
+                       MAX98926_DAI_INTERLEAVE_SLOT_MASK,
+                       max98926->v_slot);
+       }
+}
+
+static int max98926_dai_set_fmt(struct snd_soc_dai *codec_dai,
+               unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec);
+       unsigned int invert = 0;
+
+       dev_dbg(codec->dev, "%s: fmt 0x%08X\n", __func__, fmt);
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               max98926_set_sense_data(max98926);
+               break;
+       default:
+               dev_err(codec->dev, "DAI clock mode unsupported");
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               invert = MAX98926_DAI_WCI_MASK;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               invert = MAX98926_DAI_BCI_MASK;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               invert = MAX98926_DAI_BCI_MASK | MAX98926_DAI_WCI_MASK;
+               break;
+       default:
+               dev_err(codec->dev, "DAI invert mode unsupported");
+               return -EINVAL;
+       }
+
+       regmap_write(max98926->regmap,
+                       MAX98926_FORMAT, MAX98926_DAI_DLY_MASK);
+       regmap_update_bits(max98926->regmap, MAX98926_FORMAT,
+                       MAX98926_DAI_BCI_MASK, invert);
+       return 0;
+}
+
+static int max98926_dai_hw_params(struct snd_pcm_substream *substream,
+               struct snd_pcm_hw_params *params,
+               struct snd_soc_dai *dai)
+{
+       int dai_sr = -EINVAL;
+       int rate = params_rate(params), i;
+       struct snd_soc_codec *codec = dai->codec;
+       struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec);
+       int blr_clk_ratio;
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               regmap_update_bits(max98926->regmap,
+                       MAX98926_FORMAT,
+                       MAX98926_DAI_CHANSZ_MASK,
+                       MAX98926_DAI_CHANSZ_16);
+               max98926->ch_size = 16;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               regmap_update_bits(max98926->regmap,
+                       MAX98926_FORMAT,
+                       MAX98926_DAI_CHANSZ_MASK,
+                       MAX98926_DAI_CHANSZ_24);
+               max98926->ch_size = 24;
+               break;
+       case SNDRV_PCM_FORMAT_S32_LE:
+               regmap_update_bits(max98926->regmap,
+                       MAX98926_FORMAT,
+                       MAX98926_DAI_CHANSZ_MASK,
+                       MAX98926_DAI_CHANSZ_32);
+               max98926->ch_size = 32;
+               break;
+       default:
+               dev_dbg(codec->dev, "format unsupported %d",
+                       params_format(params));
+               return -EINVAL;
+       }
+
+       /* BCLK/LRCLK ratio calculation */
+       blr_clk_ratio = params_channels(params) * max98926->ch_size;
+
+       switch (blr_clk_ratio) {
+       case 32:
+               regmap_update_bits(max98926->regmap,
+                       MAX98926_DAI_CLK_MODE2,
+                       MAX98926_DAI_BSEL_MASK,
+                       MAX98926_DAI_BSEL_32);
+               break;
+       case 48:
+               regmap_update_bits(max98926->regmap,
+                       MAX98926_DAI_CLK_MODE2,
+                       MAX98926_DAI_BSEL_MASK,
+                       MAX98926_DAI_BSEL_48);
+               break;
+       case 64:
+               regmap_update_bits(max98926->regmap,
+                       MAX98926_DAI_CLK_MODE2,
+                       MAX98926_DAI_BSEL_MASK,
+                       MAX98926_DAI_BSEL_64);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* find the closest rate */
+       for (i = 0; i < ARRAY_SIZE(rate_table); i++) {
+               if (rate_table[i].rate >= rate) {
+                       dai_sr = rate_table[i].sr;
+                       break;
+               }
+       }
+       if (dai_sr < 0)
+               return -EINVAL;
+
+       /* set DAI_SR to correct LRCLK frequency */
+       regmap_update_bits(max98926->regmap,
+               MAX98926_DAI_CLK_MODE2,
+               MAX98926_DAI_SR_MASK, dai_sr << MAX98926_DAI_SR_SHIFT);
+       return 0;
+}
+
+#define MAX98926_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+               SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_ops max98926_dai_ops = {
+       .set_fmt = max98926_dai_set_fmt,
+       .hw_params = max98926_dai_hw_params,
+};
+
+static struct snd_soc_dai_driver max98926_dai[] = {
+{
+       .name = "max98926-aif1",
+       .playback = {
+               .stream_name = "HiFi Playback",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = MAX98926_FORMATS,
+       },
+       .capture = {
+               .stream_name = "HiFi Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = MAX98926_FORMATS,
+       },
+       .ops = &max98926_dai_ops,
+}
+};
+
+static int max98926_probe(struct snd_soc_codec *codec)
+{
+       struct max98926_priv *max98926 = snd_soc_codec_get_drvdata(codec);
+
+       max98926->codec = codec;
+       codec->control_data = max98926->regmap;
+       /* Hi-Z all the slots */
+       regmap_write(max98926->regmap, MAX98926_DOUT_HIZ_CFG4, 0xF0);
+       return 0;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_max98926 = {
+       .probe  = max98926_probe,
+       .controls = max98926_snd_controls,
+       .num_controls = ARRAY_SIZE(max98926_snd_controls),
+       .dapm_routes = max98926_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(max98926_audio_map),
+       .dapm_widgets = max98926_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(max98926_dapm_widgets),
+};
+
+static const struct regmap_config max98926_regmap = {
+       .reg_bits       = 8,
+       .val_bits       = 8,
+       .max_register   = MAX98926_VERSION,
+       .reg_defaults   = max98926_reg,
+       .num_reg_defaults = ARRAY_SIZE(max98926_reg),
+       .volatile_reg   = max98926_volatile_register,
+       .readable_reg   = max98926_readable_register,
+       .cache_type             = REGCACHE_RBTREE,
+};
+
+static int max98926_i2c_probe(struct i2c_client *i2c,
+               const struct i2c_device_id *id)
+{
+       int ret, reg;
+       u32 value;
+       struct max98926_priv *max98926;
+
+       max98926 = devm_kzalloc(&i2c->dev,
+                       sizeof(*max98926), GFP_KERNEL);
+       if (!max98926)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, max98926);
+       max98926->regmap = devm_regmap_init_i2c(i2c, &max98926_regmap);
+       if (IS_ERR(max98926->regmap)) {
+               ret = PTR_ERR(max98926->regmap);
+               dev_err(&i2c->dev,
+                               "Failed to allocate regmap: %d\n", ret);
+               goto err_out;
+       }
+       if (of_property_read_bool(i2c->dev.of_node, "interleave-mode"))
+               max98926->interleave_mode = true;
+
+       if (!of_property_read_u32(i2c->dev.of_node, "vmon-slot-no", &value)) {
+               if (value > MAX98926_DAI_VMON_SLOT_1E_1F) {
+                       dev_err(&i2c->dev, "vmon slot number is wrong:\n");
+                       return -EINVAL;
+               }
+               max98926->v_slot = value;
+       }
+       if (!of_property_read_u32(i2c->dev.of_node, "imon-slot-no", &value)) {
+               if (value > MAX98926_DAI_IMON_SLOT_1E_1F) {
+                       dev_err(&i2c->dev, "imon slot number is wrong:\n");
+                       return -EINVAL;
+               }
+               max98926->i_slot = value;
+       }
+       ret = regmap_read(max98926->regmap,
+                       MAX98926_VERSION, &reg);
+       if (ret < 0) {
+               dev_err(&i2c->dev, "Failed to read: %x\n", reg);
+               return ret;
+       }
+
+       ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_max98926,
+                       max98926_dai, ARRAY_SIZE(max98926_dai));
+       if (ret < 0)
+               dev_err(&i2c->dev,
+                               "Failed to register codec: %d\n", ret);
+       dev_info(&i2c->dev, "device version: %x\n", reg);
+err_out:
+       return ret;
+}
+
+static int max98926_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static const struct i2c_device_id max98926_i2c_id[] = {
+       { "max98926", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, max98926_i2c_id);
+
+static const struct of_device_id max98926_of_match[] = {
+       { .compatible = "maxim,max98926", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, max98926_of_match);
+
+static struct i2c_driver max98926_i2c_driver = {
+       .driver = {
+               .name = "max98926",
+               .of_match_table = of_match_ptr(max98926_of_match),
+               .pm = NULL,
+       },
+       .probe  = max98926_i2c_probe,
+       .remove = max98926_i2c_remove,
+       .id_table = max98926_i2c_id,
+};
+
+module_i2c_driver(max98926_i2c_driver)
+MODULE_DESCRIPTION("ALSA SoC MAX98926 driver");
+MODULE_AUTHOR("Anish kumar <anish.kumar@maximintegrated.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/max98926.h b/sound/soc/codecs/max98926.h
new file mode 100644 (file)
index 0000000..9d7ab6d
--- /dev/null
@@ -0,0 +1,848 @@
+/*
+ * max98926.h -- MAX98926 ALSA SoC Audio driver
+ * Copyright 2013-2015 Maxim Integrated Products
+ * 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 _MAX98926_H
+#define _MAX98926_H
+
+#define MAX98926_CHIP_VERSION   0x40
+#define MAX98926_CHIP_VERSION1  0x50
+
+#define MAX98926_VBAT_DATA          0x00
+#define MAX98926_VBST_DATA          0x01
+#define MAX98926_LIVE_STATUS0       0x02
+#define MAX98926_LIVE_STATUS1       0x03
+#define MAX98926_LIVE_STATUS2       0x04
+#define MAX98926_STATE0         0x05
+#define MAX98926_STATE1         0x06
+#define MAX98926_STATE2         0x07
+#define MAX98926_FLAG0          0x08
+#define MAX98926_FLAG1          0x09
+#define MAX98926_FLAG2          0x0A
+#define MAX98926_IRQ_ENABLE0        0x0B
+#define MAX98926_IRQ_ENABLE1        0x0C
+#define MAX98926_IRQ_ENABLE2        0x0D
+#define MAX98926_IRQ_CLEAR0     0x0E
+#define MAX98926_IRQ_CLEAR1     0x0F
+#define MAX98926_IRQ_CLEAR2     0x10
+#define MAX98926_MAP0           0x11
+#define MAX98926_MAP1           0x12
+#define MAX98926_MAP2           0x13
+#define MAX98926_MAP3           0x14
+#define MAX98926_MAP4           0x15
+#define MAX98926_MAP5           0x16
+#define MAX98926_MAP6           0x17
+#define MAX98926_MAP7           0x18
+#define MAX98926_MAP8           0x19
+#define MAX98926_DAI_CLK_MODE1      0x1A
+#define MAX98926_DAI_CLK_MODE2      0x1B
+#define MAX98926_DAI_CLK_DIV_M_MSBS 0x1C
+#define MAX98926_DAI_CLK_DIV_M_LSBS 0x1D
+#define MAX98926_DAI_CLK_DIV_N_MSBS 0x1E
+#define MAX98926_DAI_CLK_DIV_N_LSBS 0x1F
+#define MAX98926_FORMAT         0x20
+#define MAX98926_TDM_SLOT_SELECT        0x21
+#define MAX98926_DOUT_CFG_VMON      0x22
+#define MAX98926_DOUT_CFG_IMON      0x23
+#define MAX98926_DOUT_CFG_VBAT      0x24
+#define MAX98926_DOUT_CFG_VBST      0x25
+#define MAX98926_DOUT_CFG_FLAG      0x26
+#define MAX98926_DOUT_HIZ_CFG1      0x27
+#define MAX98926_DOUT_HIZ_CFG2      0x28
+#define MAX98926_DOUT_HIZ_CFG3      0x29
+#define MAX98926_DOUT_HIZ_CFG4      0x2A
+#define MAX98926_DOUT_DRV_STRENGTH      0x2B
+#define MAX98926_FILTERS            0x2C
+#define MAX98926_GAIN           0x2D
+#define MAX98926_GAIN_RAMPING       0x2E
+#define MAX98926_SPK_AMP            0x2F
+#define MAX98926_THRESHOLD          0x30
+#define MAX98926_ALC_ATTACK     0x31
+#define MAX98926_ALC_ATTEN_RLS      0x32
+#define MAX98926_ALC_HOLD_RLS       0x33
+#define MAX98926_ALC_CONFIGURATION      0x34
+#define MAX98926_BOOST_CONVERTER        0x35
+#define MAX98926_BLOCK_ENABLE       0x36
+#define MAX98926_CONFIGURATION      0x37
+#define MAX98926_GLOBAL_ENABLE      0x38
+#define MAX98926_BOOST_LIMITER      0x3A
+#define MAX98926_VERSION            0xFF
+
+#define MAX98926_REG_CNT               (MAX98926_R03A_BOOST_LIMITER+1)
+
+#define MAX98926_PDM_CURRENT_MASK (1<<7)
+#define MAX98926_PDM_CURRENT_SHIFT 7
+#define MAX98926_PDM_VOLTAGE_MASK (1<<3)
+#define MAX98926_PDM_VOLTAGE_SHIFT 3
+#define MAX98926_PDM_CHANNEL_0_MASK (1<<2)
+#define MAX98926_PDM_CHANNEL_0_SHIFT 2
+#define MAX98926_PDM_CHANNEL_1_MASK (1<<6)
+#define MAX98926_PDM_CHANNEL_1_SHIFT 6
+#define MAX98926_PDM_CHANNEL_1_HIZ 5
+#define MAX98926_PDM_CHANNEL_0_HIZ 1
+#define MAX98926_PDM_SOURCE_0_SHIFT 0
+#define MAX98926_PDM_SOURCE_0_MASK (1<<0)
+#define MAX98926_PDM_SOURCE_1_MASK (1<<4)
+#define MAX98926_PDM_SOURCE_1_SHIFT 4
+
+/* MAX98926 Register Bit Fields */
+
+/* MAX98926_R002_LIVE_STATUS0 */
+#define MAX98926_THERMWARN_STATUS_MASK          (1<<3)
+#define MAX98926_THERMWARN_STATUS_SHIFT         3
+#define MAX98926_THERMWARN_STATUS_WIDTH         1
+#define MAX98926_THERMSHDN_STATUS_MASK          (1<<1)
+#define MAX98926_THERMSHDN_STATUS_SHIFT         1
+#define MAX98926_THERMSHDN_STATUS_WIDTH         1
+
+/* MAX98926_R003_LIVE_STATUS1 */
+#define MAX98926_SPKCURNT_STATUS_MASK               (1<<5)
+#define MAX98926_SPKCURNT_STATUS_SHIFT          5
+#define MAX98926_SPKCURNT_STATUS_WIDTH          1
+#define MAX98926_WATCHFAIL_STATUS_MASK          (1<<4)
+#define MAX98926_WATCHFAIL_STATUS_SHIFT         4
+#define MAX98926_WATCHFAIL_STATUS_WIDTH         1
+#define MAX98926_ALCINFH_STATUS_MASK                (1<<3)
+#define MAX98926_ALCINFH_STATUS_SHIFT               3
+#define MAX98926_ALCINFH_STATUS_WIDTH               1
+#define MAX98926_ALCACT_STATUS_MASK             (1<<2)
+#define MAX98926_ALCACT_STATUS_SHIFT                2
+#define MAX98926_ALCACT_STATUS_WIDTH                1
+#define MAX98926_ALCMUT_STATUS_MASK             (1<<1)
+#define MAX98926_ALCMUT_STATUS_SHIFT                1
+#define MAX98926_ALCMUT_STATUS_WIDTH                1
+#define MAX98926_ACLP_STATUS_MASK                   (1<<0)
+#define MAX98926_ACLP_STATUS_SHIFT              0
+#define MAX98926_ACLP_STATUS_WIDTH              1
+
+/* MAX98926_R004_LIVE_STATUS2 */
+#define MAX98926_SLOTOVRN_STATUS_MASK               (1<<6)
+#define MAX98926_SLOTOVRN_STATUS_SHIFT          6
+#define MAX98926_SLOTOVRN_STATUS_WIDTH          1
+#define MAX98926_INVALSLOT_STATUS_MASK          (1<<5)
+#define MAX98926_INVALSLOT_STATUS_SHIFT         5
+#define MAX98926_INVALSLOT_STATUS_WIDTH         1
+#define MAX98926_SLOTCNFLT_STATUS_MASK          (1<<4)
+#define MAX98926_SLOTCNFLT_STATUS_SHIFT         4
+#define MAX98926_SLOTCNFLT_STATUS_WIDTH         1
+#define MAX98926_VBSTOVFL_STATUS_MASK               (1<<3)
+#define MAX98926_VBSTOVFL_STATUS_SHIFT          3
+#define MAX98926_VBSTOVFL_STATUS_WIDTH          1
+#define MAX98926_VBATOVFL_STATUS_MASK               (1<<2)
+#define MAX98926_VBATOVFL_STATUS_SHIFT          2
+#define MAX98926_VBATOVFL_STATUS_WIDTH          1
+#define MAX98926_IMONOVFL_STATUS_MASK               (1<<1)
+#define MAX98926_IMONOVFL_STATUS_SHIFT          1
+#define MAX98926_IMONOVFL_STATUS_WIDTH          1
+#define MAX98926_VMONOVFL_STATUS_MASK               (1<<0)
+#define MAX98926_VMONOVFL_STATUS_SHIFT          0
+#define MAX98926_VMONOVFL_STATUS_WIDTH          1
+
+/* MAX98926_R005_STATE0 */
+#define MAX98926_THERMWARN_END_STATE_MASK           (1<<3)
+#define MAX98926_THERMWARN_END_STATE_SHIFT      3
+#define MAX98926_THERMWARN_END_STATE_WIDTH      1
+#define MAX98926_THERMWARN_BGN_STATE_MASK           (1<<2)
+#define MAX98926_THERMWARN_BGN_STATE_SHIFT      1
+#define MAX98926_THERMWARN_BGN_STATE_WIDTH      1
+#define MAX98926_THERMSHDN_END_STATE_MASK           (1<<1)
+#define MAX98926_THERMSHDN_END_STATE_SHIFT      1
+#define MAX98926_THERMSHDN_END_STATE_WIDTH      1
+#define MAX98926_THERMSHDN_BGN_STATE_MASK           (1<<0)
+#define MAX98926_THERMSHDN_BGN_STATE_SHIFT      0
+#define MAX98926_THERMSHDN_BGN_STATE_WIDTH      1
+
+/* MAX98926_R006_STATE1 */
+#define MAX98926_SPRCURNT_STATE_MASK                (1<<5)
+#define MAX98926_SPRCURNT_STATE_SHIFT               5
+#define MAX98926_SPRCURNT_STATE_WIDTH               1
+#define MAX98926_WATCHFAIL_STATE_MASK               (1<<4)
+#define MAX98926_WATCHFAIL_STATE_SHIFT          4
+#define MAX98926_WATCHFAIL_STATE_WIDTH          1
+#define MAX98926_ALCINFH_STATE_MASK             (1<<3)
+#define MAX98926_ALCINFH_STATE_SHIFT                3
+#define MAX98926_ALCINFH_STATE_WIDTH                1
+#define MAX98926_ALCACT_STATE_MASK              (1<<2)
+#define MAX98926_ALCACT_STATE_SHIFT             2
+#define MAX98926_ALCACT_STATE_WIDTH             1
+#define MAX98926_ALCMUT_STATE_MASK              (1<<1)
+#define MAX98926_ALCMUT_STATE_SHIFT             1
+#define MAX98926_ALCMUT_STATE_WIDTH             1
+#define MAX98926_ALCP_STATE_MASK                    (1<<0)
+#define MAX98926_ALCP_STATE_SHIFT                   0
+#define MAX98926_ALCP_STATE_WIDTH                   1
+
+/* MAX98926_R007_STATE2 */
+#define MAX98926_SLOTOVRN_STATE_MASK                (1<<6)
+#define MAX98926_SLOTOVRN_STATE_SHIFT               6
+#define MAX98926_SLOTOVRN_STATE_WIDTH               1
+#define MAX98926_INVALSLOT_STATE_MASK               (1<<5)
+#define MAX98926_INVALSLOT_STATE_SHIFT          5
+#define MAX98926_INVALSLOT_STATE_WIDTH          1
+#define MAX98926_SLOTCNFLT_STATE_MASK               (1<<4)
+#define MAX98926_SLOTCNFLT_STATE_SHIFT          4
+#define MAX98926_SLOTCNFLT_STATE_WIDTH          1
+#define MAX98926_VBSTOVFL_STATE_MASK                (1<<3)
+#define MAX98926_VBSTOVFL_STATE_SHIFT               3
+#define MAX98926_VBSTOVFL_STATE_WIDTH               1
+#define MAX98926_VBATOVFL_STATE_MASK                (1<<2)
+#define MAX98926_VBATOVFL_STATE_SHIFT               2
+#define MAX98926_VBATOVFL_STATE_WIDTH               1
+#define MAX98926_IMONOVFL_STATE_MASK                (1<<1)
+#define MAX98926_IMONOVFL_STATE_SHIFT               1
+#define MAX98926_IMONOVFL_STATE_WIDTH               1
+#define MAX98926_VMONOVFL_STATE_MASK                (1<<0)
+#define MAX98926_VMONOVFL_STATE_SHIFT               0
+#define MAX98926_VMONOVFL_STATE_WIDTH               1
+
+/* MAX98926_R008_FLAG0 */
+#define MAX98926_THERMWARN_END_FLAG_MASK            (1<<3)
+#define MAX98926_THERMWARN_END_FLAG_SHIFT           3
+#define MAX98926_THERMWARN_END_FLAG_WIDTH           1
+#define MAX98926_THERMWARN_BGN_FLAG_MASK            (1<<2)
+#define MAX98926_THERMWARN_BGN_FLAG_SHIFT           2
+#define MAX98926_THERMWARN_BGN_FLAG_WIDTH           1
+#define MAX98926_THERMSHDN_END_FLAG_MASK            (1<<1)
+#define MAX98926_THERMSHDN_END_FLAG_SHIFT           1
+#define MAX98926_THERMSHDN_END_FLAG_WIDTH           1
+#define MAX98926_THERMSHDN_BGN_FLAG_MASK            (1<<0)
+#define MAX98926_THERMSHDN_BGN_FLAG_SHIFT           0
+#define MAX98926_THERMSHDN_BGN_FLAG_WIDTH           1
+
+/* MAX98926_R009_FLAG1 */
+#define MAX98926_SPKCURNT_FLAG_MASK             (1<<5)
+#define MAX98926_SPKCURNT_FLAG_SHIFT                5
+#define MAX98926_SPKCURNT_FLAG_WIDTH                1
+#define MAX98926_WATCHFAIL_FLAG_MASK                (1<<4)
+#define MAX98926_WATCHFAIL_FLAG_SHIFT               4
+#define MAX98926_WATCHFAIL_FLAG_WIDTH               1
+#define MAX98926_ALCINFH_FLAG_MASK              (1<<3)
+#define MAX98926_ALCINFH_FLAG_SHIFT             3
+#define MAX98926_ALCINFH_FLAG_WIDTH             1
+#define MAX98926_ALCACT_FLAG_MASK                   (1<<2)
+#define MAX98926_ALCACT_FLAG_SHIFT              2
+#define MAX98926_ALCACT_FLAG_WIDTH              1
+#define MAX98926_ALCMUT_FLAG_MASK                   (1<<1)
+#define MAX98926_ALCMUT_FLAG_SHIFT              1
+#define MAX98926_ALCMUT_FLAG_WIDTH              1
+#define MAX98926_ALCP_FLAG_MASK                 (1<<0)
+#define MAX98926_ALCP_FLAG_SHIFT                    0
+#define MAX98926_ALCP_FLAG_WIDTH                    1
+
+/* MAX98926_R00A_FLAG2 */
+#define MAX98926_SLOTOVRN_FLAG_MASK             (1<<6)
+#define MAX98926_SLOTOVRN_FLAG_SHIFT                6
+#define MAX98926_SLOTOVRN_FLAG_WIDTH                1
+#define MAX98926_INVALSLOT_FLAG_MASK                (1<<5)
+#define MAX98926_INVALSLOT_FLAG_SHIFT               5
+#define MAX98926_INVALSLOT_FLAG_WIDTH               1
+#define MAX98926_SLOTCNFLT_FLAG_MASK                (1<<4)
+#define MAX98926_SLOTCNFLT_FLAG_SHIFT               4
+#define MAX98926_SLOTCNFLT_FLAG_WIDTH               1
+#define MAX98926_VBSTOVFL_FLAG_MASK             (1<<3)
+#define MAX98926_VBSTOVFL_FLAG_SHIFT                3
+#define MAX98926_VBSTOVFL_FLAG_WIDTH                1
+#define MAX98926_VBATOVFL_FLAG_MASK             (1<<2)
+#define MAX98926_VBATOVFL_FLAG_SHIFT                2
+#define MAX98926_VBATOVFL_FLAG_WIDTH                1
+#define MAX98926_IMONOVFL_FLAG_MASK             (1<<1)
+#define MAX98926_IMONOVFL_FLAG_SHIFT                1
+#define MAX98926_IMONOVFL_FLAG_WIDTH                1
+#define MAX98926_VMONOVFL_FLAG_MASK             (1<<0)
+#define MAX98926_VMONOVFL_FLAG_SHIFT                0
+#define MAX98926_VMONOVFL_FLAG_WIDTH                1
+
+/* MAX98926_R00B_IRQ_ENABLE0 */
+#define MAX98926_THERMWARN_END_EN_MASK          (1<<3)
+#define MAX98926_THERMWARN_END_EN_SHIFT         3
+#define MAX98926_THERMWARN_END_EN_WIDTH         1
+#define MAX98926_THERMWARN_BGN_EN_MASK          (1<<2)
+#define MAX98926_THERMWARN_BGN_EN_SHIFT         2
+#define MAX98926_THERMWARN_BGN_EN_WIDTH         1
+#define MAX98926_THERMSHDN_END_EN_MASK          (1<<1)
+#define MAX98926_THERMSHDN_END_EN_SHIFT         1
+#define MAX98926_THERMSHDN_END_EN_WIDTH         1
+#define MAX98926_THERMSHDN_BGN_EN_MASK          (1<<0)
+#define MAX98926_THERMSHDN_BGN_EN_SHIFT         0
+#define MAX98926_THERMSHDN_BGN_EN_WIDTH         1
+
+/* MAX98926_R00C_IRQ_ENABLE1 */
+#define MAX98926_SPKCURNT_EN_MASK       (1<<5)
+#define MAX98926_SPKCURNT_EN_SHIFT  5
+#define MAX98926_SPKCURNT_EN_WIDTH  1
+#define MAX98926_WATCHFAIL_EN_MASK  (1<<4)
+#define MAX98926_WATCHFAIL_EN_SHIFT 4
+#define MAX98926_WATCHFAIL_EN_WIDTH 1
+#define MAX98926_ALCINFH_EN_MASK        (1<<3)
+#define MAX98926_ALCINFH_EN_SHIFT       3
+#define MAX98926_ALCINFH_EN_WIDTH       1
+#define MAX98926_ALCACT_EN_MASK     (1<<2)
+#define MAX98926_ALCACT_EN_SHIFT        2
+#define MAX98926_ALCACT_EN_WIDTH        1
+#define MAX98926_ALCMUT_EN_MASK     (1<<1)
+#define MAX98926_ALCMUT_EN_SHIFT        1
+#define MAX98926_ALCMUT_EN_WIDTH        1
+#define MAX98926_ALCP_EN_MASK           (1<<0)
+#define MAX98926_ALCP_EN_SHIFT      0
+#define MAX98926_ALCP_EN_WIDTH      1
+
+/* MAX98926_R00D_IRQ_ENABLE2 */
+#define MAX98926_SLOTOVRN_EN_MASK       (1<<6)
+#define MAX98926_SLOTOVRN_EN_SHIFT  6
+#define MAX98926_SLOTOVRN_EN_WIDTH  1
+#define MAX98926_INVALSLOT_EN_MASK  (1<<5)
+#define MAX98926_INVALSLOT_EN_SHIFT 5
+#define MAX98926_INVALSLOT_EN_WIDTH 1
+#define MAX98926_SLOTCNFLT_EN_MASK  (1<<4)
+#define MAX98926_SLOTCNFLT_EN_SHIFT 4
+#define MAX98926_SLOTCNFLT_EN_WIDTH 1
+#define MAX98926_VBSTOVFL_EN_MASK       (1<<3)
+#define MAX98926_VBSTOVFL_EN_SHIFT  3
+#define MAX98926_VBSTOVFL_EN_WIDTH  1
+#define MAX98926_VBATOVFL_EN_MASK       (1<<2)
+#define MAX98926_VBATOVFL_EN_SHIFT  2
+#define MAX98926_VBATOVFL_EN_WIDTH  1
+#define MAX98926_IMONOVFL_EN_MASK       (1<<1)
+#define MAX98926_IMONOVFL_EN_SHIFT  1
+#define MAX98926_IMONOVFL_EN_WIDTH  1
+#define MAX98926_VMONOVFL_EN_MASK       (1<<0)
+#define MAX98926_VMONOVFL_EN_SHIFT  0
+#define MAX98926_VMONOVFL_EN_WIDTH  1
+
+/* MAX98926_R00E_IRQ_CLEAR0 */
+#define MAX98926_THERMWARN_END_CLR_MASK         (1<<3)
+#define MAX98926_THERMWARN_END_CLR_SHIFT            3
+#define MAX98926_THERMWARN_END_CLR_WIDTH            1
+#define MAX98926_THERMWARN_BGN_CLR_MASK         (1<<2)
+#define MAX98926_THERMWARN_BGN_CLR_SHIFT            2
+#define MAX98926_THERMWARN_BGN_CLR_WIDTH            1
+#define MAX98926_THERMSHDN_END_CLR_MASK         (1<<1)
+#define MAX98926_THERMSHDN_END_CLR_SHIFT            1
+#define MAX98926_THERMSHDN_END_CLR_WIDTH            1
+#define MAX98926_THERMSHDN_BGN_CLR_MASK         (1<<0)
+#define MAX98926_THERMSHDN_BGN_CLR_SHIFT            0
+#define MAX98926_THERMSHDN_BGN_CLR_WIDTH            1
+
+/* MAX98926_R00F_IRQ_CLEAR1 */
+#define MAX98926_SPKCURNT_CLR_MASK      (1<<5)
+#define MAX98926_SPKCURNT_CLR_SHIFT     5
+#define MAX98926_SPKCURNT_CLR_WIDTH     1
+#define MAX98926_WATCHFAIL_CLR_MASK     (1<<4)
+#define MAX98926_WATCHFAIL_CLR_SHIFT        4
+#define MAX98926_WATCHFAIL_CLR_WIDTH        1
+#define MAX98926_ALCINFH_CLR_MASK           (1<<3)
+#define MAX98926_ALCINFH_CLR_SHIFT      3
+#define MAX98926_ALCINFH_CLR_WIDTH      1
+#define MAX98926_ALCACT_CLR_MASK            (1<<2)
+#define MAX98926_ALCACT_CLR_SHIFT           2
+#define MAX98926_ALCACT_CLR_WIDTH           1
+#define MAX98926_ALCMUT_CLR_MASK            (1<<1)
+#define MAX98926_ALCMUT_CLR_SHIFT           1
+#define MAX98926_ALCMUT_CLR_WIDTH           1
+#define MAX98926_ALCP_CLR_MASK          (1<<0)
+#define MAX98926_ALCP_CLR_SHIFT         0
+#define MAX98926_ALCP_CLR_WIDTH         1
+
+/* MAX98926_R010_IRQ_CLEAR2 */
+#define MAX98926_SLOTOVRN_CLR_MASK      (1<<6)
+#define MAX98926_SLOTOVRN_CLR_SHIFT     6
+#define MAX98926_SLOTOVRN_CLR_WIDTH     1
+#define MAX98926_INVALSLOT_CLR_MASK     (1<<5)
+#define MAX98926_INVALSLOT_CLR_SHIFT        5
+#define MAX98926_INVALSLOT_CLR_WIDTH        1
+#define MAX98926_SLOTCNFLT_CLR_MASK     (1<<4)
+#define MAX98926_SLOTCNFLT_CLR_SHIFT        4
+#define MAX98926_SLOTCNFLT_CLR_WIDTH        1
+#define MAX98926_VBSTOVFL_CLR_MASK      (1<<3)
+#define MAX98926_VBSTOVFL_CLR_SHIFT     3
+#define MAX98926_VBSTOVFL_CLR_WIDTH     1
+#define MAX98926_VBATOVFL_CLR_MASK      (1<<2)
+#define MAX98926_VBATOVFL_CLR_SHIFT     2
+#define MAX98926_VBATOVFL_CLR_WIDTH     1
+#define MAX98926_IMONOVFL_CLR_MASK      (1<<1)
+#define MAX98926_IMONOVFL_CLR_SHIFT     1
+#define MAX98926_IMONOVFL_CLR_WIDTH     1
+#define MAX98926_VMONOVFL_CLR_MASK          (1<<0)
+#define MAX98926_VMONOVFL_CLR_SHIFT         0
+#define MAX98926_VMONOVFL_CLR_WIDTH         1
+
+/* MAX98926_R011_MAP0 */
+#define MAX98926_ER_THERMWARN_EN_MASK               (1<<7)
+#define MAX98926_ER_THERMWARN_EN_SHIFT          7
+#define MAX98926_ER_THERMWARN_EN_WIDTH          1
+#define MAX98926_ER_THERMWARN_MAP_MASK          (0x07<<4)
+#define MAX98926_ER_THERMWARN_MAP_SHIFT         4
+#define MAX98926_ER_THERMWARN_MAP_WIDTH         3
+
+/* MAX98926_R012_MAP1 */
+#define MAX98926_ER_ALCMUT_EN_MASK      (1<<7)
+#define MAX98926_ER_ALCMUT_EN_SHIFT     7
+#define MAX98926_ER_ALCMUT_EN_WIDTH     1
+#define MAX98926_ER_ALCMUT_MAP_MASK     (0x07<<4)
+#define MAX98926_ER_ALCMUT_MAP_SHIFT        4
+#define MAX98926_ER_ALCMUT_MAP_WIDTH        3
+#define MAX98926_ER_ALCP_EN_MASK            (1<<3)
+#define MAX98926_ER_ALCP_EN_SHIFT           3
+#define MAX98926_ER_ALCP_EN_WIDTH           1
+#define MAX98926_ER_ALCP_MAP_MASK           (0x07<<0)
+#define MAX98926_ER_ALCP_MAP_SHIFT      0
+#define MAX98926_ER_ALCP_MAP_WIDTH      3
+
+/* MAX98926_R013_MAP2 */
+#define MAX98926_ER_ALCINFH_EN_MASK     (1<<7)
+#define MAX98926_ER_ALCINFH_EN_SHIFT        7
+#define MAX98926_ER_ALCINFH_EN_WIDTH        1
+#define MAX98926_ER_ALCINFH_MAP_MASK        (0x07<<4)
+#define MAX98926_ER_ALCINFH_MAP_SHIFT       4
+#define MAX98926_ER_ALCINFH_MAP_WIDTH       3
+#define MAX98926_ER_ALCACT_EN_MASK      (1<<3)
+#define MAX98926_ER_ALCACT_EN_SHIFT     3
+#define MAX98926_ER_ALCACT_EN_WIDTH     1
+#define MAX98926_ER_ALCACT_MAP_MASK     (0x07<<0)
+#define MAX98926_ER_ALCACT_MAP_SHIFT        0
+#define MAX98926_ER_ALCACT_MAP_WIDTH        3
+
+/* MAX98926_R014_MAP3 */
+#define MAX98926_ER_SPKCURNT_EN_MASK            (1<<7)
+#define MAX98926_ER_SPKCURNT_EN_SHIFT           7
+#define MAX98926_ER_SPKCURNT_EN_WIDTH           1
+#define MAX98926_ER_SPKCURNT_MAP_MASK           (0x07<<4)
+#define MAX98926_ER_SPKCURNT_MAP_SHIFT          4
+#define MAX98926_ER_SPKCURNT_MAP_WIDTH          3
+
+/* MAX98926_R015_MAP4 */
+/* RESERVED */
+
+/* MAX98926_R016_MAP5 */
+#define MAX98926_ER_IMONOVFL_EN_MASK            (1<<7)
+#define MAX98926_ER_IMONOVFL_EN_SHIFT           7
+#define MAX98926_ER_IMONOVFL_EN_WIDTH           1
+#define MAX98926_ER_IMONOVFL_MAP_MASK           (0x07<<4)
+#define MAX98926_ER_IMONOVFL_MAP_SHIFT          4
+#define MAX98926_ER_IMONOVFL_MAP_WIDTH          3
+#define MAX98926_ER_VMONOVFL_EN_MASK            (1<<3)
+#define MAX98926_ER_VMONOVFL_EN_SHIFT           3
+#define MAX98926_ER_VMONOVFL_EN_WIDTH           1
+#define MAX98926_ER_VMONOVFL_MAP_MASK           (0x07<<0)
+#define MAX98926_ER_VMONOVFL_MAP_SHIFT          0
+#define MAX98926_ER_VMONOVFL_MAP_WIDTH          3
+
+/* MAX98926_R017_MAP6 */
+#define MAX98926_ER_VBSTOVFL_EN_MASK            (1<<7)
+#define MAX98926_ER_VBSTOVFL_EN_SHIFT           7
+#define MAX98926_ER_VBSTOVFL_EN_WIDTH           1
+#define MAX98926_ER_VBSTOVFL_MAP_MASK           (0x07<<4)
+#define MAX98926_ER_VBSTOVFL_MAP_SHIFT          4
+#define MAX98926_ER_VBSTOVFL_MAP_WIDTH          3
+#define MAX98926_ER_VBATOVFL_EN_MASK            (1<<3)
+#define MAX98926_ER_VBATOVFL_EN_SHIFT           3
+#define MAX98926_ER_VBATOVFL_EN_WIDTH           1
+#define MAX98926_ER_VBATOVFL_MAP_MASK           (0x07<<0)
+#define MAX98926_ER_VBATOVFL_MAP_SHIFT          0
+#define MAX98926_ER_VBATOVFL_MAP_WIDTH          3
+
+/* MAX98926_R018_MAP7 */
+#define MAX98926_ER_INVALSLOT_EN_MASK               (1<<7)
+#define MAX98926_ER_INVALSLOT_EN_SHIFT          7
+#define MAX98926_ER_INVALSLOT_EN_WIDTH          1
+#define MAX98926_ER_INVALSLOT_MAP_MASK          (0x07<<4)
+#define MAX98926_ER_INVALSLOT_MAP_SHIFT         4
+#define MAX98926_ER_INVALSLOT_MAP_WIDTH         3
+#define MAX98926_ER_SLOTCNFLT_EN_MASK               (1<<3)
+#define MAX98926_ER_SLOTCNFLT_EN_SHIFT          3
+#define MAX98926_ER_SLOTCNFLT_EN_WIDTH          1
+#define MAX98926_ER_SLOTCNFLT_MAP_MASK          (0x07<<0)
+#define MAX98926_ER_SLOTCNFLT_MAP_SHIFT         0
+#define MAX98926_ER_SLOTCNFLT_MAP_WIDTH         3
+
+/* MAX98926_R019_MAP8 */
+#define MAX98926_ER_SLOTOVRN_EN_MASK    (1<<3)
+#define MAX98926_ER_SLOTOVRN_EN_SHIFT   3
+#define MAX98926_ER_SLOTOVRN_EN_WIDTH   1
+#define MAX98926_ER_SLOTOVRN_MAP_MASK   (0x07<<0)
+#define MAX98926_ER_SLOTOVRN_MAP_SHIFT  0
+#define MAX98926_ER_SLOTOVRN_MAP_WIDTH  3
+
+/* MAX98926_R01A_DAI_CLK_MODE1 */
+#define MAX98926_DAI_CLK_SOURCE_MASK    (1<<6)
+#define MAX98926_DAI_CLK_SOURCE_SHIFT   6
+#define MAX98926_DAI_CLK_SOURCE_WIDTH   1
+#define MAX98926_MDLL_MULT_MASK     (0x0F<<0)
+#define MAX98926_MDLL_MULT_SHIFT        0
+#define MAX98926_MDLL_MULT_WIDTH        4
+
+#define MAX98926_MDLL_MULT_MCLKx8       6
+#define MAX98926_MDLL_MULT_MCLKx16  8
+
+/* MAX98926_R01B_DAI_CLK_MODE2 */
+#define MAX98926_DAI_SR_MASK            (0x0F<<4)
+#define MAX98926_DAI_SR_SHIFT           4
+#define MAX98926_DAI_SR_WIDTH           4
+#define MAX98926_DAI_MAS_MASK           (1<<3)
+#define MAX98926_DAI_MAS_SHIFT          3
+#define MAX98926_DAI_MAS_WIDTH          1
+#define MAX98926_DAI_BSEL_MASK          (0x07<<0)
+#define MAX98926_DAI_BSEL_SHIFT         0
+#define MAX98926_DAI_BSEL_WIDTH         3
+
+#define MAX98926_DAI_BSEL_32 (0 << MAX98926_DAI_BSEL_SHIFT)
+#define MAX98926_DAI_BSEL_48 (1 << MAX98926_DAI_BSEL_SHIFT)
+#define MAX98926_DAI_BSEL_64 (2 << MAX98926_DAI_BSEL_SHIFT)
+#define MAX98926_DAI_BSEL_256 (6 << MAX98926_DAI_BSEL_SHIFT)
+
+/* MAX98926_R01C_DAI_CLK_DIV_M_MSBS */
+#define MAX98926_DAI_M_MSBS_MASK        (0xFF<<0)
+#define MAX98926_DAI_M_MSBS_SHIFT       0
+#define MAX98926_DAI_M_MSBS_WIDTH       8
+
+/* MAX98926_R01D_DAI_CLK_DIV_M_LSBS */
+#define MAX98926_DAI_M_LSBS_MASK        (0xFF<<0)
+#define MAX98926_DAI_M_LSBS_SHIFT       0
+#define MAX98926_DAI_M_LSBS_WIDTH       8
+
+/* MAX98926_R01E_DAI_CLK_DIV_N_MSBS */
+#define MAX98926_DAI_N_MSBS_MASK        (0x7F<<0)
+#define MAX98926_DAI_N_MSBS_SHIFT       0
+#define MAX98926_DAI_N_MSBS_WIDTH       7
+
+/* MAX98926_R01F_DAI_CLK_DIV_N_LSBS */
+#define MAX98926_DAI_N_LSBS_MASK        (0xFF<<0)
+#define MAX98926_DAI_N_LSBS_SHIFT       0
+#define MAX98926_DAI_N_LSBS_WIDTH       8
+
+/* MAX98926_R020_FORMAT */
+#define MAX98926_DAI_CHANSZ_MASK    (0x03<<6)
+#define MAX98926_DAI_CHANSZ_SHIFT   6
+#define MAX98926_DAI_CHANSZ_WIDTH   2
+#define MAX98926_DAI_INTERLEAVE_MASK        (1<<5)
+#define MAX98926_DAI_INTERLEAVE_SHIFT       5
+#define MAX98926_DAI_INTERLEAVE_WIDTH       1
+#define MAX98926_DAI_EXTBCLK_HIZ_MASK       (1<<4)
+#define MAX98926_DAI_EXTBCLK_HIZ_SHIFT      4
+#define MAX98926_DAI_EXTBCLK_HIZ_WIDTH      1
+#define MAX98926_DAI_WCI_MASK           (1<<3)
+#define MAX98926_DAI_WCI_SHIFT      3
+#define MAX98926_DAI_WCI_WIDTH      1
+#define MAX98926_DAI_BCI_MASK           (1<<2)
+#define MAX98926_DAI_BCI_SHIFT      2
+#define MAX98926_DAI_BCI_WIDTH      1
+#define MAX98926_DAI_DLY_MASK           (1<<1)
+#define MAX98926_DAI_DLY_SHIFT      1
+#define MAX98926_DAI_DLY_WIDTH      1
+#define MAX98926_DAI_TDM_MASK           (1<<0)
+#define MAX98926_DAI_TDM_SHIFT      0
+#define MAX98926_DAI_TDM_WIDTH      1
+
+#define MAX98926_DAI_CHANSZ_16 (1 << MAX98926_DAI_CHANSZ_SHIFT)
+#define MAX98926_DAI_CHANSZ_24 (2 << MAX98926_DAI_CHANSZ_SHIFT)
+#define MAX98926_DAI_CHANSZ_32 (3 << MAX98926_DAI_CHANSZ_SHIFT)
+
+/* MAX98926_R021_TDM_SLOT_SELECT */
+#define MAX98926_DAI_DO_EN_MASK     (1<<7)
+#define MAX98926_DAI_DO_EN_SHIFT        7
+#define MAX98926_DAI_DO_EN_WIDTH        1
+#define MAX98926_DAI_DIN_EN_MASK        (1<<6)
+#define MAX98926_DAI_DIN_EN_SHIFT       6
+#define MAX98926_DAI_DIN_EN_WIDTH       1
+#define MAX98926_DAI_INR_SOURCE_MASK    (0x07<<3)
+#define MAX98926_DAI_INR_SOURCE_SHIFT   3
+#define MAX98926_DAI_INR_SOURCE_WIDTH   3
+#define MAX98926_DAI_INL_SOURCE_MASK    (0x07<<0)
+#define MAX98926_DAI_INL_SOURCE_SHIFT   0
+#define MAX98926_DAI_INL_SOURCE_WIDTH   3
+
+/* MAX98926_R022_DOUT_CFG_VMON */
+#define MAX98926_DAI_VMON_EN_MASK       (1<<5)
+#define MAX98926_DAI_VMON_EN_SHIFT  5
+#define MAX98926_DAI_VMON_EN_WIDTH  1
+#define MAX98926_DAI_VMON_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_VMON_SLOT_SHIFT    0
+#define MAX98926_DAI_VMON_SLOT_WIDTH    5
+
+#define MAX98926_DAI_VMON_SLOT_00_01 (0 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_01_02 (1 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_02_03 (2 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_03_04 (3 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_04_05 (4 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_05_06 (5 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_06_07 (6 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_07_08 (7 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_08_09 (8 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_09_0A (9 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0A_0B (10 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0B_0C (11 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0C_0D (12 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0D_0E (13 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0E_0F (14 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_0F_10 (15 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_10_11 (16 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_11_12 (17 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_12_13 (18 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_13_14 (19 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_14_15 (20 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_15_16 (21 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_16_17 (22 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_17_18 (23 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_18_19 (24 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_19_1A (25 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1A_1B (26 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1B_1C (27 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1C_1D (28 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1D_1E (29 << MAX98926_DAI_VMON_SLOT_SHIFT)
+#define MAX98926_DAI_VMON_SLOT_1E_1F (30 << MAX98926_DAI_VMON_SLOT_SHIFT)
+
+/* MAX98926_R023_DOUT_CFG_IMON */
+#define MAX98926_DAI_IMON_EN_MASK       (1<<5)
+#define MAX98926_DAI_IMON_EN_SHIFT  5
+#define MAX98926_DAI_IMON_EN_WIDTH  1
+#define MAX98926_DAI_IMON_SLOT_MASK (0x1F<<0)
+#define MAX98926_DAI_IMON_SLOT_SHIFT    0
+#define MAX98926_DAI_IMON_SLOT_WIDTH    5
+
+#define MAX98926_DAI_IMON_SLOT_00_01 (0 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_01_02 (1 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_02_03 (2 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_03_04 (3 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_04_05 (4 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_05_06 (5 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_06_07 (6 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_07_08 (7 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_08_09 (8 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_09_0A (9 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0A_0B (10 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0B_0C (11 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0C_0D (12 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0D_0E (13 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0E_0F (14 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_0F_10 (15 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_10_11 (16 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_11_12 (17 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_12_13 (18 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_13_14 (19 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_14_15 (20 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_15_16 (21 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_16_17 (22 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_17_18 (23 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_18_19 (24 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_19_1A (25 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1A_1B (26 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1B_1C (27 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1C_1D (28 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1D_1E (29 << MAX98926_DAI_IMON_SLOT_SHIFT)
+#define MAX98926_DAI_IMON_SLOT_1E_1F (30 << MAX98926_DAI_IMON_SLOT_SHIFT)
+
+/* MAX98926_R024_DOUT_CFG_VBAT */
+#define MAX98926_DAI_INTERLEAVE_SLOT_MASK       (0x1F<<0)
+#define MAX98926_DAI_INTERLEAVE_SLOT_SHIFT      0
+#define MAX98926_DAI_INTERLEAVE_SLOT_WIDTH      5
+
+/* MAX98926_R025_DOUT_CFG_VBST */
+#define MAX98926_DAI_VBST_EN_MASK               (1<<5)
+#define MAX98926_DAI_VBST_EN_SHIFT          5
+#define MAX98926_DAI_VBST_EN_WIDTH          1
+#define MAX98926_DAI_VBST_SLOT_MASK         (0x1F<<0)
+#define MAX98926_DAI_VBST_SLOT_SHIFT            0
+#define MAX98926_DAI_VBST_SLOT_WIDTH            5
+
+/* MAX98926_R026_DOUT_CFG_FLAG */
+#define MAX98926_DAI_FLAG_EN_MASK               (1<<5)
+#define MAX98926_DAI_FLAG_EN_SHIFT          5
+#define MAX98926_DAI_FLAG_EN_WIDTH          1
+#define MAX98926_DAI_FLAG_SLOT_MASK         (0x1F<<0)
+#define MAX98926_DAI_FLAG_SLOT_SHIFT            0
+#define MAX98926_DAI_FLAG_SLOT_WIDTH            5
+
+/* MAX98926_R027_DOUT_HIZ_CFG1 */
+#define MAX98926_DAI_SLOT_HIZ_CFG1_MASK         (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG1_SHIFT            0
+#define MAX98926_DAI_SLOT_HIZ_CFG1_WIDTH            8
+
+/* MAX98926_R028_DOUT_HIZ_CFG2 */
+#define MAX98926_DAI_SLOT_HIZ_CFG2_MASK         (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG2_SHIFT            0
+#define MAX98926_DAI_SLOT_HIZ_CFG2_WIDTH            8
+
+/* MAX98926_R029_DOUT_HIZ_CFG3 */
+#define MAX98926_DAI_SLOT_HIZ_CFG3_MASK         (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG3_SHIFT            0
+#define MAX98926_DAI_SLOT_HIZ_CFG3_WIDTH            8
+
+/* MAX98926_R02A_DOUT_HIZ_CFG4 */
+#define MAX98926_DAI_SLOT_HIZ_CFG4_MASK         (0xFF<<0)
+#define MAX98926_DAI_SLOT_HIZ_CFG4_SHIFT            0
+#define MAX98926_DAI_SLOT_HIZ_CFG4_WIDTH            8
+
+/* MAX98926_R02B_DOUT_DRV_STRENGTH */
+#define MAX98926_DAI_OUT_DRIVE_MASK             (0x03<<0)
+#define MAX98926_DAI_OUT_DRIVE_SHIFT                0
+#define MAX98926_DAI_OUT_DRIVE_WIDTH                2
+
+/* MAX98926_R02C_FILTERS */
+#define MAX98926_ADC_DITHER_EN_MASK             (1<<7)
+#define MAX98926_ADC_DITHER_EN_SHIFT                7
+#define MAX98926_ADC_DITHER_EN_WIDTH                1
+#define MAX98926_IV_DCB_EN_MASK                 (1<<6)
+#define MAX98926_IV_DCB_EN_SHIFT                    6
+#define MAX98926_IV_DCB_EN_WIDTH                    1
+#define MAX98926_DAC_DITHER_EN_MASK             (1<<4)
+#define MAX98926_DAC_DITHER_EN_SHIFT                4
+#define MAX98926_DAC_DITHER_EN_WIDTH                1
+#define MAX98926_DAC_FILTER_MODE_MASK               (1<<3)
+#define MAX98926_DAC_FILTER_MODE_SHIFT          3
+#define MAX98926_DAC_FILTER_MODE_WIDTH          1
+#define MAX98926_DAC_HPF_MASK               (0x07<<0)
+#define MAX98926_DAC_HPF_SHIFT                  0
+#define MAX98926_DAC_HPF_WIDTH                  3
+#define MAX98926_DAC_HPF_DISABLE        (0 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_DC_BLOCK       (1 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_100     (2 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_200     (3 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_400     (4 << MAX98926_DAC_HPF_SHIFT)
+#define MAX98926_DAC_HPF_EN_800     (5 << MAX98926_DAC_HPF_SHIFT)
+
+/* MAX98926_R02D_GAIN */
+#define MAX98926_DAC_IN_SEL_MASK    (0x03<<5)
+#define MAX98926_DAC_IN_SEL_SHIFT   5
+#define MAX98926_DAC_IN_SEL_WIDTH   2
+#define MAX98926_SPK_GAIN_MASK      (0x1F<<0)
+#define MAX98926_SPK_GAIN_SHIFT     0
+#define MAX98926_SPK_GAIN_WIDTH     5
+
+#define MAX98926_DAC_IN_SEL_LEFT_DAI (0 << MAX98926_DAC_IN_SEL_SHIFT)
+#define MAX98926_DAC_IN_SEL_RIGHT_DAI (1 << MAX98926_DAC_IN_SEL_SHIFT)
+#define MAX98926_DAC_IN_SEL_SUMMED_DAI (2 << MAX98926_DAC_IN_SEL_SHIFT)
+#define MAX98926_DAC_IN_SEL_DIV2_SUMMED_DAI (3 << MAX98926_DAC_IN_SEL_SHIFT)
+
+/* MAX98926_R02E_GAIN_RAMPING */
+#define MAX98926_SPK_RMP_EN_MASK        (1<<1)
+#define MAX98926_SPK_RMP_EN_SHIFT       1
+#define MAX98926_SPK_RMP_EN_WIDTH       1
+#define MAX98926_SPK_ZCD_EN_MASK        (1<<0)
+#define MAX98926_SPK_ZCD_EN_SHIFT       0
+#define MAX98926_SPK_ZCD_EN_WIDTH       1
+
+/* MAX98926_R02F_SPK_AMP */
+#define MAX98926_SPK_MODE_MASK      (1<<0)
+#define MAX98926_SPK_MODE_SHIFT     0
+#define MAX98926_SPK_MODE_WIDTH     1
+#define MAX98926_INSELECT_MODE_MASK (1<<1)
+#define MAX98926_INSELECT_MODE_SHIFT    1
+#define MAX98926_INSELECT_MODE_WIDTH    1
+
+/* MAX98926_R030_THRESHOLD */
+#define MAX98926_ALC_EN_MASK            (1<<5)
+#define MAX98926_ALC_EN_SHIFT           5
+#define MAX98926_ALC_EN_WIDTH           1
+#define MAX98926_ALC_TH_MASK            (0x1F<<0)
+#define MAX98926_ALC_TH_SHIFT           0
+#define MAX98926_ALC_TH_WIDTH           5
+
+/* MAX98926_R031_ALC_ATTACK */
+#define MAX98926_ALC_ATK_STEP_MASK  (0x0F<<4)
+#define MAX98926_ALC_ATK_STEP_SHIFT 4
+#define MAX98926_ALC_ATK_STEP_WIDTH 4
+#define MAX98926_ALC_ATK_RATE_MASK  (0x7<<0)
+#define MAX98926_ALC_ATK_RATE_SHIFT 0
+#define MAX98926_ALC_ATK_RATE_WIDTH 3
+
+/* MAX98926_R032_ALC_ATTEN_RLS */
+#define MAX98926_ALC_MAX_ATTEN_MASK (0x0F<<4)
+#define MAX98926_ALC_MAX_ATTEN_SHIFT    4
+#define MAX98926_ALC_MAX_ATTEN_WIDTH    4
+#define MAX98926_ALC_RLS_RATE_MASK  (0x7<<0)
+#define MAX98926_ALC_RLS_RATE_SHIFT 0
+#define MAX98926_ALC_RLS_RATE_WIDTH 3
+
+/* MAX98926_R033_ALC_HOLD_RLS */
+#define MAX98926_ALC_RLS_TGR_MASK       (1<<0)
+#define MAX98926_ALC_RLS_TGR_SHIFT  0
+#define MAX98926_ALC_RLS_TGR_WIDTH  1
+
+/* MAX98926_R034_ALC_CONFIGURATION */
+#define MAX98926_ALC_MUTE_EN_MASK       (1<<7)
+#define MAX98926_ALC_MUTE_EN_SHIFT  7
+#define MAX98926_ALC_MUTE_EN_WIDTH  1
+#define MAX98926_ALC_MUTE_DLY_MASK  (0x07<<4)
+#define MAX98926_ALC_MUTE_DLY_SHIFT 4
+#define MAX98926_ALC_MUTE_DLY_WIDTH 3
+#define MAX98926_ALC_RLS_DBT_MASK       (0x07<<0)
+#define MAX98926_ALC_RLS_DBT_SHIFT  0
+#define MAX98926_ALC_RLS_DBT_WIDTH  3
+
+/* MAX98926_R035_BOOST_CONVERTER */
+#define MAX98926_BST_SYNC_MASK      (1<<7)
+#define MAX98926_BST_SYNC_SHIFT     7
+#define MAX98926_BST_SYNC_WIDTH     1
+#define MAX98926_BST_PHASE_MASK     (0x03<<4)
+#define MAX98926_BST_PHASE_SHIFT        4
+#define MAX98926_BST_PHASE_WIDTH        2
+#define MAX98926_BST_SKIP_MODE_MASK (0x03<<0)
+#define MAX98926_BST_SKIP_MODE_SHIFT    0
+#define MAX98926_BST_SKIP_MODE_WIDTH    2
+
+/* MAX98926_R036_BLOCK_ENABLE */
+#define MAX98926_BST_EN_MASK            (1<<7)
+#define MAX98926_BST_EN_SHIFT           7
+#define MAX98926_BST_EN_WIDTH           1
+#define MAX98926_WATCH_EN_MASK      (1<<6)
+#define MAX98926_WATCH_EN_SHIFT     6
+#define MAX98926_WATCH_EN_WIDTH     1
+#define MAX98926_CLKMON_EN_MASK     (1<<5)
+#define MAX98926_CLKMON_EN_SHIFT        5
+#define MAX98926_CLKMON_EN_WIDTH        1
+#define MAX98926_SPK_EN_MASK            (1<<4)
+#define MAX98926_SPK_EN_SHIFT           4
+#define MAX98926_SPK_EN_WIDTH           1
+#define MAX98926_ADC_VBST_EN_MASK       (1<<3)
+#define MAX98926_ADC_VBST_EN_SHIFT  3
+#define MAX98926_ADC_VBST_EN_WIDTH  1
+#define MAX98926_ADC_VBAT_EN_MASK       (1<<2)
+#define MAX98926_ADC_VBAT_EN_SHIFT  2
+#define MAX98926_ADC_VBAT_EN_WIDTH  1
+#define MAX98926_ADC_IMON_EN_MASK       (1<<1)
+#define MAX98926_ADC_IMON_EN_SHIFT  1
+#define MAX98926_ADC_IMON_EN_WIDTH  1
+#define MAX98926_ADC_VMON_EN_MASK       (1<<0)
+#define MAX98926_ADC_VMON_EN_SHIFT  0
+#define MAX98926_ADC_VMON_EN_WIDTH  1
+
+/* MAX98926_R037_CONFIGURATION */
+#define MAX98926_BST_VOUT_MASK      (0x0F<<4)
+#define MAX98926_BST_VOUT_SHIFT     4
+#define MAX98926_BST_VOUT_WIDTH     4
+#define MAX98926_THERMWARN_LEVEL_MASK   (0x03<<2)
+#define MAX98926_THERMWARN_LEVEL_SHIFT          2
+#define MAX98926_THERMWARN_LEVEL_WIDTH          2
+#define MAX98926_WATCH_TIME_MASK            (0x03<<0)
+#define MAX98926_WATCH_TIME_SHIFT           0
+#define MAX98926_WATCH_TIME_WIDTH           2
+
+/* MAX98926_R038_GLOBAL_ENABLE */
+#define MAX98926_EN_MASK            (1<<7)
+#define MAX98926_EN_SHIFT           7
+#define MAX98926_EN_WIDTH           1
+
+/* MAX98926_R03A_BOOST_LIMITER */
+#define MAX98926_BST_ILIM_MASK  (0xF<<4)
+#define MAX98926_BST_ILIM_SHIFT 4
+#define MAX98926_BST_ILIM_WIDTH 4
+
+/* MAX98926_R0FF_VERSION */
+#define MAX98926_REV_ID_MASK    (0xFF<<0)
+#define MAX98926_REV_ID_SHIFT   0
+#define MAX98926_REV_ID_WIDTH   8
+
+struct max98926_priv {
+       struct regmap *regmap;
+       struct snd_soc_codec *codec;
+       unsigned int sysclk;
+       unsigned int v_slot;
+       unsigned int i_slot;
+       unsigned int ch_size;
+       unsigned int interleave_mode;
+};
+#endif
index c1b87c5800b1d0980cf541e96a4d22ae76e9b32c..1c8729984c2b6332502a0f90df457b1df59f22f0 100644 (file)
@@ -84,6 +84,7 @@ static const struct nau8825_fll_attr fll_pre_scalar[] = {
 
 static const struct reg_default nau8825_reg_defaults[] = {
        { NAU8825_REG_ENA_CTRL, 0x00ff },
+       { NAU8825_REG_IIC_ADDR_SET, 0x0 },
        { NAU8825_REG_CLK_DIVIDER, 0x0050 },
        { NAU8825_REG_FLL1, 0x0 },
        { NAU8825_REG_FLL2, 0x3126 },
@@ -158,8 +159,7 @@ static const struct reg_default nau8825_reg_defaults[] = {
 static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
-       case NAU8825_REG_ENA_CTRL:
-       case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+       case NAU8825_REG_ENA_CTRL ... NAU8825_REG_FLL_VCO_RSV:
        case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
        case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL:
        case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
@@ -184,8 +184,7 @@ static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
 static bool nau8825_writeable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
-       case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL:
-       case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+       case NAU8825_REG_RESET ... NAU8825_REG_FLL_VCO_RSV:
        case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
        case NAU8825_REG_INTERRUPT_MASK:
        case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL:
@@ -227,10 +226,42 @@ static bool nau8825_volatile_reg(struct device *dev, unsigned int reg)
 static int nau8825_pump_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
                /* Prevent startup click by letting charge pump to ramp up */
                msleep(10);
+               regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+                       NAU8825_JAMNODCLOW, NAU8825_JAMNODCLOW);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               regmap_update_bits(nau8825->regmap, NAU8825_REG_CHARGE_PUMP,
+                       NAU8825_JAMNODCLOW, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int nau8825_output_dac_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               /* Disables the TESTDAC to let DAC signal pass through. */
+               regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+                       NAU8825_BIAS_TESTDAC_EN, 0);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+                       NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
                break;
        default:
                return -EINVAL;
@@ -316,10 +347,10 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
        SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL,
                NAU8825_SAR_ADC_EN_SFT, 0),
 
-       SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0),
-       SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0),
-       SND_SOC_DAPM_SUPPLY("ADACL Clock", NAU8825_REG_RDAC, 8, 0, NULL, 0),
-       SND_SOC_DAPM_SUPPLY("ADACR Clock", NAU8825_REG_RDAC, 9, 0, NULL, 0),
+       SND_SOC_DAPM_PGA_S("ADACL", 2, NAU8825_REG_RDAC, 12, 0, NULL, 0),
+       SND_SOC_DAPM_PGA_S("ADACR", 2, NAU8825_REG_RDAC, 13, 0, NULL, 0),
+       SND_SOC_DAPM_PGA_S("ADACL Clock", 3, NAU8825_REG_RDAC, 8, 0, NULL, 0),
+       SND_SOC_DAPM_PGA_S("ADACR Clock", 3, NAU8825_REG_RDAC, 9, 0, NULL, 0),
 
        SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL,
                NAU8825_ENABLE_DACR_SFT, 0),
@@ -330,29 +361,48 @@ static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
        SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux),
        SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux),
 
-       SND_SOC_DAPM_PGA("HP amp L", NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
-       SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL,
-               0),
+       SND_SOC_DAPM_PGA_S("HP amp L", 0,
+               NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0),
+       SND_SOC_DAPM_PGA_S("HP amp R", 0,
+               NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
 
-       SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0,
-               nau8825_pump_event, SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_S("Charge Pump", 1, NAU8825_REG_CHARGE_PUMP, 5, 0,
+               nau8825_pump_event, SND_SOC_DAPM_POST_PMU |
+               SND_SOC_DAPM_PRE_PMD),
 
-       SND_SOC_DAPM_PGA("Output Driver R Stage 1",
+       SND_SOC_DAPM_PGA_S("Output Driver R Stage 1", 4,
                NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("Output Driver L Stage 1",
+       SND_SOC_DAPM_PGA_S("Output Driver L Stage 1", 4,
                NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("Output Driver R Stage 2",
+       SND_SOC_DAPM_PGA_S("Output Driver R Stage 2", 5,
                NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("Output Driver L Stage 2",
+       SND_SOC_DAPM_PGA_S("Output Driver L Stage 2", 5,
                NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0),
-       SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 1,
+       SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 6,
                NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0),
-       SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 1,
+       SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 6,
                NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
 
-       SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0),
-       SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0),
+       SND_SOC_DAPM_PGA_S("Output DACL", 7,
+               NAU8825_REG_CHARGE_PUMP, 8, 1, nau8825_output_dac_event,
+               SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+       SND_SOC_DAPM_PGA_S("Output DACR", 7,
+               NAU8825_REG_CHARGE_PUMP, 9, 1, nau8825_output_dac_event,
+               SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       /* HPOL/R are ungrounded by disabling 16 Ohm pull-downs on playback */
+       SND_SOC_DAPM_PGA_S("HPOL Pulldown", 8,
+               NAU8825_REG_HSD_CTRL, 0, 1, NULL, 0),
+       SND_SOC_DAPM_PGA_S("HPOR Pulldown", 8,
+               NAU8825_REG_HSD_CTRL, 1, 1, NULL, 0),
+
+       /* High current HPOL/R boost driver */
+       SND_SOC_DAPM_PGA_S("HP Boost Driver", 9,
+               NAU8825_REG_BOOST, 9, 1, NULL, 0),
+
+       /* Class G operation control*/
+       SND_SOC_DAPM_PGA_S("Class G", 10,
+               NAU8825_REG_CLASSG_CTRL, 0, 0, NULL, 0),
 
        SND_SOC_DAPM_OUTPUT("HPOL"),
        SND_SOC_DAPM_OUTPUT("HPOR"),
@@ -375,24 +425,27 @@ static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
        {"DACR Mux", "DACR", "DDACR"},
        {"HP amp L", NULL, "DACL Mux"},
        {"HP amp R", NULL, "DACR Mux"},
-       {"HP amp L", NULL, "HP amp power"},
-       {"HP amp R", NULL, "HP amp power"},
-       {"ADACL", NULL, "HP amp L"},
-       {"ADACR", NULL, "HP amp R"},
-       {"ADACL", NULL, "ADACL Clock"},
-       {"ADACR", NULL, "ADACR Clock"},
-       {"Output Driver L Stage 1", NULL, "ADACL"},
-       {"Output Driver R Stage 1", NULL, "ADACR"},
+       {"Charge Pump", NULL, "HP amp L"},
+       {"Charge Pump", NULL, "HP amp R"},
+       {"ADACL", NULL, "Charge Pump"},
+       {"ADACR", NULL, "Charge Pump"},
+       {"ADACL Clock", NULL, "ADACL"},
+       {"ADACR Clock", NULL, "ADACR"},
+       {"Output Driver L Stage 1", NULL, "ADACL Clock"},
+       {"Output Driver R Stage 1", NULL, "ADACR Clock"},
        {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"},
        {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"},
        {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"},
        {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"},
        {"Output DACL", NULL, "Output Driver L Stage 3"},
        {"Output DACR", NULL, "Output Driver R Stage 3"},
-       {"HPOL", NULL, "Output DACL"},
-       {"HPOR", NULL, "Output DACR"},
-       {"HPOL", NULL, "Charge Pump"},
-       {"HPOR", NULL, "Charge Pump"},
+       {"HPOL Pulldown", NULL, "Output DACL"},
+       {"HPOR Pulldown", NULL, "Output DACR"},
+       {"HP Boost Driver", NULL, "HPOL Pulldown"},
+       {"HP Boost Driver", NULL, "HPOR Pulldown"},
+       {"Class G", NULL, "HP Boost Driver"},
+       {"HPOL", NULL, "Class G"},
+       {"HPOR", NULL, "Class G"},
 };
 
 static int nau8825_hw_params(struct snd_pcm_substream *substream,
@@ -659,11 +712,10 @@ static int nau8825_jack_insert(struct nau8825 *nau8825)
                break;
        }
 
-       if (type & SND_JACK_HEADPHONE) {
-               /* Unground HPL/R */
-               regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0);
-       }
-
+       /* Leaving HPOL/R grounded after jack insert by default. They will be
+        * ungrounded as part of the widget power up sequence at the beginning
+        * of playback to reduce pop.
+        */
        return type;
 }
 
@@ -768,6 +820,8 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
 {
        struct regmap *regmap = nau8825->regmap;
 
+       /* Latch IIC LSB value */
+       regmap_write(regmap, NAU8825_REG_IIC_ADDR_SET, 0x0001);
        /* Enable Bias/Vmid */
        regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
                NAU8825_BIAS_VMID, NAU8825_BIAS_VMID);
@@ -780,10 +834,10 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
                nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT);
        /* Disable Boost Driver, Automatic Short circuit protection enable */
        regmap_update_bits(regmap, NAU8825_REG_BOOST,
-               NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
-               NAU8825_SHORT_SHUTDOWN_EN,
-               NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
-               NAU8825_SHORT_SHUTDOWN_EN);
+               NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS |
+               NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN,
+               NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_DIS |
+               NAU8825_HP_BOOST_G_DIS | NAU8825_SHORT_SHUTDOWN_EN);
 
        regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
                NAU8825_JKDET_OUTPUT_EN,
@@ -822,6 +876,35 @@ static void nau8825_init_regs(struct nau8825 *nau8825)
                NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128);
        regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
                NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128);
+       /* Disable DACR/L power */
+       regmap_update_bits(regmap, NAU8825_REG_CHARGE_PUMP,
+               NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL,
+               NAU8825_POWER_DOWN_DACR | NAU8825_POWER_DOWN_DACL);
+       /* Enable TESTDAC. This sets the analog DAC inputs to a '0' input
+        * signal to avoid any glitches due to power up transients in both
+        * the analog and digital DAC circuit.
+        */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+               NAU8825_BIAS_TESTDAC_EN, NAU8825_BIAS_TESTDAC_EN);
+       /* CICCLP off */
+       regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
+               NAU8825_DAC_CLIP_OFF, NAU8825_DAC_CLIP_OFF);
+
+       /* Class AB bias current to 2x, DAC Capacitor enable MSB/LSB */
+       regmap_update_bits(regmap, NAU8825_REG_ANALOG_CONTROL_2,
+               NAU8825_HP_NON_CLASSG_CURRENT_2xADJ |
+               NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB,
+               NAU8825_HP_NON_CLASSG_CURRENT_2xADJ |
+               NAU8825_DAC_CAPACITOR_MSB | NAU8825_DAC_CAPACITOR_LSB);
+       /* Class G timer 64ms */
+       regmap_update_bits(regmap, NAU8825_REG_CLASSG_CTRL,
+               NAU8825_CLASSG_TIMER_MASK,
+               0x20 << NAU8825_CLASSG_TIMER_SFT);
+       /* DAC clock delay 2ns, VREF */
+       regmap_update_bits(regmap, NAU8825_REG_RDAC,
+               NAU8825_RDAC_CLK_DELAY_MASK | NAU8825_RDAC_VREF_MASK,
+               (0x2 << NAU8825_RDAC_CLK_DELAY_SFT) |
+               (0x3 << NAU8825_RDAC_VREF_SFT));
 }
 
 static const struct regmap_config nau8825_regmap_config = {
index dff8edb83bfdfeb034a230ddbfce02952617eace..8ceb5f38547846e89239e2023fac8bed20c4359e 100644 (file)
@@ -14,6 +14,7 @@
 
 #define NAU8825_REG_RESET              0x00
 #define NAU8825_REG_ENA_CTRL           0x01
+#define NAU8825_REG_IIC_ADDR_SET               0x02
 #define NAU8825_REG_CLK_DIVIDER                0x03
 #define NAU8825_REG_FLL1               0x04
 #define NAU8825_REG_FLL2               0x05
 
 /* HSD_CTRL (0xc) */
 #define NAU8825_HSD_AUTO_MODE  (1 << 6)
-/* 0 - short to GND, 1 - open */
+/* 0 - open, 1 - short to GND */
 #define NAU8825_SPKR_DWN1R     (1 << 1)
 #define NAU8825_SPKR_DWN1L     (1 << 0)
 
 /* DACR_CTRL (0x34) */
 #define NAU8825_DACR_CH_SEL_SFT        9
 
+/* CLASSG_CTRL (0x50) */
+#define NAU8825_CLASSG_TIMER_SFT       8
+#define NAU8825_CLASSG_TIMER_MASK      (0x3f << NAU8825_CLASSG_TIMER_SFT)
+#define NAU8825_CLASSG_EN              (1 << 0)
+
 /* I2C_DEVICE_ID (0x58) */
 #define NAU8825_GPIO2JD1       (1 << 7)
 #define NAU8825_SOFTWARE_ID_MASK       0x3
 #define NAU8825_SOFTWARE_ID_NAU8825    0x0
 
 /* BIAS_ADJ (0x66) */
+#define NAU8825_BIAS_TESTDAC_EN        (0x3 << 8)
 #define NAU8825_BIAS_VMID      (1 << 6)
 #define NAU8825_BIAS_VMID_SEL_SFT      4
 #define NAU8825_BIAS_VMID_SEL_MASK     (3 << NAU8825_BIAS_VMID_SEL_SFT)
 #define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB      (3 << 8)
 #define NAU8825_POWERUP_ADCL   (1 << 6)
 
+/* RDAC (0x73) */
+#define NAU8825_RDAC_CLK_DELAY_SFT     4
+#define NAU8825_RDAC_CLK_DELAY_MASK    (0x7 << NAU8825_RDAC_CLK_DELAY_SFT)
+#define NAU8825_RDAC_VREF_SFT  2
+#define NAU8825_RDAC_VREF_MASK (0x3 << NAU8825_RDAC_VREF_SFT)
+
 /* MIC_BIAS (0x74) */
 #define NAU8825_MICBIAS_JKSLV  (1 << 14)
 #define NAU8825_MICBIAS_JKR2   (1 << 12)
 /* BOOST (0x76) */
 #define NAU8825_PRECHARGE_DIS  (1 << 13)
 #define NAU8825_GLOBAL_BIAS_EN (1 << 12)
+#define NAU8825_HP_BOOST_DIS           (1 << 9)
 #define NAU8825_HP_BOOST_G_DIS (1 << 8)
 #define NAU8825_SHORT_SHUTDOWN_EN      (1 << 6)
 
diff --git a/sound/soc/codecs/pcm179x-i2c.c b/sound/soc/codecs/pcm179x-i2c.c
new file mode 100644 (file)
index 0000000..4118106
--- /dev/null
@@ -0,0 +1,73 @@
+/*
+ * PCM179X ASoC I2C driver
+ *
+ * Copyright (c) Teenage Engineering AB 2016
+ *
+ *     Jacob Siverskog <jacob@teenage.engineering>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+
+#include "pcm179x.h"
+
+static int pcm179x_i2c_probe(struct i2c_client *client,
+                             const struct i2c_device_id *id)
+{
+       struct regmap *regmap;
+       int ret;
+
+       regmap = devm_regmap_init_i2c(client, &pcm179x_regmap_config);
+       if (IS_ERR(regmap)) {
+               ret = PTR_ERR(regmap);
+               dev_err(&client->dev, "Failed to allocate regmap: %d\n", ret);
+               return ret;
+       }
+
+       return pcm179x_common_init(&client->dev, regmap);
+}
+
+static int pcm179x_i2c_remove(struct i2c_client *client)
+{
+       return pcm179x_common_exit(&client->dev);
+}
+
+static const struct of_device_id pcm179x_of_match[] = {
+       { .compatible = "ti,pcm1792a", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, pcm179x_of_match);
+
+static const struct i2c_device_id pcm179x_i2c_ids[] = {
+       { "pcm179x", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, pcm179x_i2c_ids);
+
+static struct i2c_driver pcm179x_i2c_driver = {
+       .driver = {
+               .name   = "pcm179x",
+               .of_match_table = of_match_ptr(pcm179x_of_match),
+       },
+       .id_table       = pcm179x_i2c_ids,
+       .probe          = pcm179x_i2c_probe,
+       .remove         = pcm179x_i2c_remove,
+};
+
+module_i2c_driver(pcm179x_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC PCM179X I2C driver");
+MODULE_AUTHOR("Jacob Siverskog <jacob@teenage.engineering>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm179x-spi.c b/sound/soc/codecs/pcm179x-spi.c
new file mode 100644 (file)
index 0000000..da924d4
--- /dev/null
@@ -0,0 +1,72 @@
+/*
+ * PCM179X ASoC SPI driver
+ *
+ * Copyright (c) Amarula Solutions B.V. 2013
+ *
+ *     Michael Trimarchi <michael@amarulasolutions.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/spi/spi.h>
+#include <linux/regmap.h>
+
+#include "pcm179x.h"
+
+static int pcm179x_spi_probe(struct spi_device *spi)
+{
+       struct regmap *regmap;
+       int ret;
+
+       regmap = devm_regmap_init_spi(spi, &pcm179x_regmap_config);
+       if (IS_ERR(regmap)) {
+               ret = PTR_ERR(regmap);
+               dev_err(&spi->dev, "Failed to allocate regmap: %d\n", ret);
+               return ret;
+       }
+
+       return pcm179x_common_init(&spi->dev, regmap);
+}
+
+static int pcm179x_spi_remove(struct spi_device *spi)
+{
+       return pcm179x_common_exit(&spi->dev);
+}
+
+static const struct of_device_id pcm179x_of_match[] = {
+       { .compatible = "ti,pcm1792a", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, pcm179x_of_match);
+
+static const struct spi_device_id pcm179x_spi_ids[] = {
+       { "pcm179x", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids);
+
+static struct spi_driver pcm179x_spi_driver = {
+       .driver = {
+               .name = "pcm179x",
+               .of_match_table = of_match_ptr(pcm179x_of_match),
+       },
+       .id_table = pcm179x_spi_ids,
+       .probe = pcm179x_spi_probe,
+       .remove = pcm179x_spi_remove,
+};
+
+module_spi_driver(pcm179x_spi_driver);
+
+MODULE_DESCRIPTION("ASoC PCM179X SPI driver");
+MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
+MODULE_LICENSE("GPL");
index a56c7b767d900cf8298d6f9f095b86740a74ca10..06a66579ca6d34964044c0ac297333a805b2a3c3 100644 (file)
@@ -20,7 +20,6 @@
 #include <linux/slab.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
-#include <linux/spi/spi.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -29,7 +28,6 @@
 #include <sound/soc.h>
 #include <sound/tlv.h>
 #include <linux/of.h>
-#include <linux/of_device.h>
 
 #include "pcm179x.h"
 
@@ -189,18 +187,14 @@ static struct snd_soc_dai_driver pcm179x_dai = {
                .stream_name = "Playback",
                .channels_min = 2,
                .channels_max = 2,
-               .rates = PCM1792A_RATES,
+               .rates = SNDRV_PCM_RATE_CONTINUOUS,
+               .rate_min = 10000,
+               .rate_max = 200000,
                .formats = PCM1792A_FORMATS, },
        .ops = &pcm179x_dai_ops,
 };
 
-static const struct of_device_id pcm179x_of_match[] = {
-       { .compatible = "ti,pcm1792a", },
-       { }
-};
-MODULE_DEVICE_TABLE(of, pcm179x_of_match);
-
-static const struct regmap_config pcm179x_regmap = {
+const struct regmap_config pcm179x_regmap_config = {
        .reg_bits               = 8,
        .val_bits               = 8,
        .max_register           = 23,
@@ -209,6 +203,7 @@ static const struct regmap_config pcm179x_regmap = {
        .writeable_reg          = pcm179x_writeable_reg,
        .readable_reg           = pcm179x_accessible_reg,
 };
+EXPORT_SYMBOL_GPL(pcm179x_regmap_config);
 
 static struct snd_soc_codec_driver soc_codec_dev_pcm179x = {
        .controls               = pcm179x_controls,
@@ -219,52 +214,29 @@ static struct snd_soc_codec_driver soc_codec_dev_pcm179x = {
        .num_dapm_routes        = ARRAY_SIZE(pcm179x_dapm_routes),
 };
 
-static int pcm179x_spi_probe(struct spi_device *spi)
+int pcm179x_common_init(struct device *dev, struct regmap *regmap)
 {
        struct pcm179x_private *pcm179x;
-       int ret;
 
-       pcm179x = devm_kzalloc(&spi->dev, sizeof(struct pcm179x_private),
+       pcm179x = devm_kzalloc(dev, sizeof(struct pcm179x_private),
                                GFP_KERNEL);
        if (!pcm179x)
                return -ENOMEM;
 
-       spi_set_drvdata(spi, pcm179x);
-
-       pcm179x->regmap = devm_regmap_init_spi(spi, &pcm179x_regmap);
-       if (IS_ERR(pcm179x->regmap)) {
-               ret = PTR_ERR(pcm179x->regmap);
-               dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
-               return ret;
-       }
+       pcm179x->regmap = regmap;
+       dev_set_drvdata(dev, pcm179x);
 
-       return snd_soc_register_codec(&spi->dev,
+       return snd_soc_register_codec(dev,
                        &soc_codec_dev_pcm179x, &pcm179x_dai, 1);
 }
+EXPORT_SYMBOL_GPL(pcm179x_common_init);
 
-static int pcm179x_spi_remove(struct spi_device *spi)
+int pcm179x_common_exit(struct device *dev)
 {
-       snd_soc_unregister_codec(&spi->dev);
+       snd_soc_unregister_codec(dev);
        return 0;
 }
-
-static const struct spi_device_id pcm179x_spi_ids[] = {
-       { "pcm179x", 0 },
-       { },
-};
-MODULE_DEVICE_TABLE(spi, pcm179x_spi_ids);
-
-static struct spi_driver pcm179x_codec_driver = {
-       .driver = {
-               .name = "pcm179x",
-               .of_match_table = of_match_ptr(pcm179x_of_match),
-       },
-       .id_table = pcm179x_spi_ids,
-       .probe = pcm179x_spi_probe,
-       .remove = pcm179x_spi_remove,
-};
-
-module_spi_driver(pcm179x_codec_driver);
+EXPORT_SYMBOL_GPL(pcm179x_common_exit);
 
 MODULE_DESCRIPTION("ASoC PCM179X driver");
 MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
index c6fdc062a497ef080ee3e4225ba90328e1c390cc..11e331268aae27c2523a9e12dfb4c44ce3967dd0 100644 (file)
 #ifndef __PCM179X_H__
 #define __PCM179X_H__
 
-#define PCM1792A_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_8000_48000 | \
-                       SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 | \
-                       SNDRV_PCM_RATE_192000)
-
 #define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
                          SNDRV_PCM_FMTBIT_S16_LE)
 
+extern const struct regmap_config pcm179x_regmap_config;
+
+int pcm179x_common_init(struct device *dev, struct regmap *regmap);
+int pcm179x_common_exit(struct device *dev);
+
 #endif
index 44b268aa4dd88e0cee6ac4c4a60663d51df0d1ab..992a77edcd5d1ff90d8857860972dadc4b86d7d7 100644 (file)
@@ -299,10 +299,15 @@ static int pcm3168a_set_dai_sysclk(struct snd_soc_dai *dai,
                                  int clk_id, unsigned int freq, int dir)
 {
        struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(dai->codec);
+       int ret;
 
        if (freq > PCM1368A_MAX_SYSCLK)
                return -EINVAL;
 
+       ret = clk_set_rate(pcm3168a->scki, freq);
+       if (ret)
+               return ret;
+
        pcm3168a->sysclk = freq;
 
        return 0;
@@ -395,13 +400,12 @@ static int pcm3168a_hw_params(struct snd_pcm_substream *substream,
        struct pcm3168a_priv *pcm3168a = snd_soc_codec_get_drvdata(codec);
        bool tx, master_mode;
        u32 val, mask, shift, reg;
-       unsigned int rate, channels, fmt, ratio, max_ratio;
+       unsigned int rate, fmt, ratio, max_ratio;
        int i, min_frame_size;
        snd_pcm_format_t format;
 
        rate = params_rate(params);
        format = params_format(params);
-       channels = params_channels(params);
 
        ratio = pcm3168a->sysclk / rate;
 
index 30c6de62ae6c980584a1a5bdd9d637dcfc3d0338..f0e6c06e89ac0448afd7580245353b12acbb3d4f 100644 (file)
@@ -1224,7 +1224,12 @@ static int rt298_i2c_probe(struct i2c_client *i2c,
        regmap_write(rt298->regmap, RT298_MISC_CTRL1, 0x0000);
        regmap_update_bits(rt298->regmap,
                                RT298_WIND_FILTER_CTRL, 0x0082, 0x0082);
-       regmap_update_bits(rt298->regmap, RT298_IRQ_CTRL, 0x2, 0x2);
+
+       regmap_write(rt298->regmap, RT298_UNSOLICITED_INLINE_CMD, 0x81);
+       regmap_write(rt298->regmap, RT298_UNSOLICITED_HP_OUT, 0x82);
+       regmap_write(rt298->regmap, RT298_UNSOLICITED_MIC1, 0x84);
+       regmap_update_bits(rt298->regmap, RT298_IRQ_FLAG_CTRL, 0x2, 0x2);
+
        rt298->is_hp_in = -1;
 
        if (rt298->i2c->irq) {
index 31da16265f2b14f5c14f63a4d61f8c24562a2051..d66f8847b67632354d8750f5fee8f7153aa3768a 100644 (file)
@@ -34,6 +34,7 @@
 #define RT298_HP_OUT                                   0x21
 #define RT298_MIXER_IN1                                        0x22
 #define RT298_MIXER_IN2                                        0x23
+#define RT298_INLINE_CMD                               0x55
 
 #define RT298_SET_PIN_SFT                              6
 #define RT298_SET_PIN_ENABLE                           0x40
        VERB_CMD(AC_VERB_SET_COEF_INDEX, RT298_VENDOR_REGISTERS, 0)
 #define RT298_PROC_COEF\
        VERB_CMD(AC_VERB_SET_PROC_COEF, RT298_VENDOR_REGISTERS, 0)
+#define RT298_UNSOLICITED_INLINE_CMD\
+       VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_INLINE_CMD, 0)
+#define RT298_UNSOLICITED_HP_OUT\
+       VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_HP_OUT, 0)
+#define RT298_UNSOLICITED_MIC1\
+       VERB_CMD(AC_VERB_SET_UNSOLICITED_ENABLE, RT298_MIC1, 0)
 
 /* Index registers */
 #define RT298_A_BIAS_CTRL1     0x01
 #define RT298_DEPOP_CTRL2      0x67
 #define RT298_DEPOP_CTRL3      0x68
 #define RT298_DEPOP_CTRL4      0x69
+#define RT298_IRQ_FLAG_CTRL    0x7c
 
 /* SPDIF (0x06) */
 #define RT298_SPDIF_SEL_SFT    0
diff --git a/sound/soc/codecs/rt5514.c b/sound/soc/codecs/rt5514.c
new file mode 100644 (file)
index 0000000..879bf60
--- /dev/null
@@ -0,0 +1,982 @@
+/*
+ * rt5514.c  --  RT5514 ALSA SoC audio codec driver
+ *
+ * Copyright 2015 Realtek Semiconductor Corp.
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#include <linux/fs.h>
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/regmap.h>
+#include <linux/i2c.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/gpio.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include "rl6231.h"
+#include "rt5514.h"
+
+static const struct reg_sequence rt5514_i2c_patch[] = {
+       {0x1800101c, 0x00000000},
+       {0x18001100, 0x0000031f},
+       {0x18001104, 0x00000007},
+       {0x18001108, 0x00000000},
+       {0x1800110c, 0x00000000},
+       {0x18001110, 0x00000000},
+       {0x18001114, 0x00000001},
+       {0x18001118, 0x00000000},
+       {0x18002f08, 0x00000006},
+       {0x18002f00, 0x00055149},
+       {0x18002f00, 0x0005514b},
+       {0x18002f00, 0x00055149},
+       {0xfafafafa, 0x00000001},
+       {0x18002f10, 0x00000001},
+       {0x18002f10, 0x00000000},
+       {0x18002f10, 0x00000001},
+       {0xfafafafa, 0x00000001},
+       {0x18002000, 0x000010ec},
+       {0xfafafafa, 0x00000000},
+};
+
+static const struct reg_sequence rt5514_patch[] = {
+       {RT5514_DIG_IO_CTRL,            0x00000040},
+       {RT5514_CLK_CTRL1,              0x38020041},
+       {RT5514_SRC_CTRL,               0x44000eee},
+       {RT5514_ANA_CTRL_LDO10,         0x00028604},
+       {RT5514_ANA_CTRL_ADCFED,        0x00000800},
+};
+
+static const struct reg_default rt5514_reg[] = {
+       {RT5514_RESET,                  0x00000000},
+       {RT5514_PWR_ANA1,               0x00808880},
+       {RT5514_PWR_ANA2,               0x00220000},
+       {RT5514_I2S_CTRL1,              0x00000330},
+       {RT5514_I2S_CTRL2,              0x20000000},
+       {RT5514_VAD_CTRL6,              0xc00007d2},
+       {RT5514_EXT_VAD_CTRL,           0x80000080},
+       {RT5514_DIG_IO_CTRL,            0x00000040},
+       {RT5514_PAD_CTRL1,              0x00804000},
+       {RT5514_DMIC_DATA_CTRL,         0x00000005},
+       {RT5514_DIG_SOURCE_CTRL,        0x00000002},
+       {RT5514_SRC_CTRL,               0x44000eee},
+       {RT5514_DOWNFILTER2_CTRL1,      0x0000882f},
+       {RT5514_PLL_SOURCE_CTRL,        0x00000004},
+       {RT5514_CLK_CTRL1,              0x38020041},
+       {RT5514_CLK_CTRL2,              0x00000000},
+       {RT5514_PLL3_CALIB_CTRL1,       0x00400200},
+       {RT5514_PLL3_CALIB_CTRL5,       0x40220012},
+       {RT5514_DELAY_BUF_CTRL1,        0x7fff006a},
+       {RT5514_DELAY_BUF_CTRL3,        0x00000000},
+       {RT5514_DOWNFILTER0_CTRL1,      0x00020c2f},
+       {RT5514_DOWNFILTER0_CTRL2,      0x00020c2f},
+       {RT5514_DOWNFILTER0_CTRL3,      0x00000362},
+       {RT5514_DOWNFILTER1_CTRL1,      0x00020c2f},
+       {RT5514_DOWNFILTER1_CTRL2,      0x00020c2f},
+       {RT5514_DOWNFILTER1_CTRL3,      0x00000362},
+       {RT5514_ANA_CTRL_LDO10,         0x00028604},
+       {RT5514_ANA_CTRL_LDO18_16,      0x02000345},
+       {RT5514_ANA_CTRL_ADC12,         0x0000a2a8},
+       {RT5514_ANA_CTRL_ADC21,         0x00001180},
+       {RT5514_ANA_CTRL_ADC22,         0x0000aaa8},
+       {RT5514_ANA_CTRL_ADC23,         0x00151427},
+       {RT5514_ANA_CTRL_MICBST,        0x00002000},
+       {RT5514_ANA_CTRL_ADCFED,        0x00000800},
+       {RT5514_ANA_CTRL_INBUF,         0x00000143},
+       {RT5514_ANA_CTRL_VREF,          0x00008d50},
+       {RT5514_ANA_CTRL_PLL3,          0x0000000e},
+       {RT5514_ANA_CTRL_PLL1_1,        0x00000000},
+       {RT5514_ANA_CTRL_PLL1_2,        0x00030220},
+       {RT5514_DMIC_LP_CTRL,           0x00000000},
+       {RT5514_MISC_CTRL_DSP,          0x00000000},
+       {RT5514_DSP_CTRL1,              0x00055149},
+       {RT5514_DSP_CTRL3,              0x00000006},
+       {RT5514_DSP_CTRL4,              0x00000001},
+       {RT5514_VENDOR_ID1,             0x00000001},
+       {RT5514_VENDOR_ID2,             0x10ec5514},
+};
+
+static bool rt5514_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case RT5514_VENDOR_ID1:
+       case RT5514_VENDOR_ID2:
+               return true;
+
+       default:
+               return false;
+       }
+}
+
+static bool rt5514_readable_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case RT5514_RESET:
+       case RT5514_PWR_ANA1:
+       case RT5514_PWR_ANA2:
+       case RT5514_I2S_CTRL1:
+       case RT5514_I2S_CTRL2:
+       case RT5514_VAD_CTRL6:
+       case RT5514_EXT_VAD_CTRL:
+       case RT5514_DIG_IO_CTRL:
+       case RT5514_PAD_CTRL1:
+       case RT5514_DMIC_DATA_CTRL:
+       case RT5514_DIG_SOURCE_CTRL:
+       case RT5514_SRC_CTRL:
+       case RT5514_DOWNFILTER2_CTRL1:
+       case RT5514_PLL_SOURCE_CTRL:
+       case RT5514_CLK_CTRL1:
+       case RT5514_CLK_CTRL2:
+       case RT5514_PLL3_CALIB_CTRL1:
+       case RT5514_PLL3_CALIB_CTRL5:
+       case RT5514_DELAY_BUF_CTRL1:
+       case RT5514_DELAY_BUF_CTRL3:
+       case RT5514_DOWNFILTER0_CTRL1:
+       case RT5514_DOWNFILTER0_CTRL2:
+       case RT5514_DOWNFILTER0_CTRL3:
+       case RT5514_DOWNFILTER1_CTRL1:
+       case RT5514_DOWNFILTER1_CTRL2:
+       case RT5514_DOWNFILTER1_CTRL3:
+       case RT5514_ANA_CTRL_LDO10:
+       case RT5514_ANA_CTRL_LDO18_16:
+       case RT5514_ANA_CTRL_ADC12:
+       case RT5514_ANA_CTRL_ADC21:
+       case RT5514_ANA_CTRL_ADC22:
+       case RT5514_ANA_CTRL_ADC23:
+       case RT5514_ANA_CTRL_MICBST:
+       case RT5514_ANA_CTRL_ADCFED:
+       case RT5514_ANA_CTRL_INBUF:
+       case RT5514_ANA_CTRL_VREF:
+       case RT5514_ANA_CTRL_PLL3:
+       case RT5514_ANA_CTRL_PLL1_1:
+       case RT5514_ANA_CTRL_PLL1_2:
+       case RT5514_DMIC_LP_CTRL:
+       case RT5514_MISC_CTRL_DSP:
+       case RT5514_DSP_CTRL1:
+       case RT5514_DSP_CTRL3:
+       case RT5514_DSP_CTRL4:
+       case RT5514_VENDOR_ID1:
+       case RT5514_VENDOR_ID2:
+               return true;
+
+       default:
+               return false;
+       }
+}
+
+static bool rt5514_i2c_readable_register(struct device *dev,
+       unsigned int reg)
+{
+       switch (reg) {
+       case RT5514_DSP_MAPPING | RT5514_RESET:
+       case RT5514_DSP_MAPPING | RT5514_PWR_ANA1:
+       case RT5514_DSP_MAPPING | RT5514_PWR_ANA2:
+       case RT5514_DSP_MAPPING | RT5514_I2S_CTRL1:
+       case RT5514_DSP_MAPPING | RT5514_I2S_CTRL2:
+       case RT5514_DSP_MAPPING | RT5514_VAD_CTRL6:
+       case RT5514_DSP_MAPPING | RT5514_EXT_VAD_CTRL:
+       case RT5514_DSP_MAPPING | RT5514_DIG_IO_CTRL:
+       case RT5514_DSP_MAPPING | RT5514_PAD_CTRL1:
+       case RT5514_DSP_MAPPING | RT5514_DMIC_DATA_CTRL:
+       case RT5514_DSP_MAPPING | RT5514_DIG_SOURCE_CTRL:
+       case RT5514_DSP_MAPPING | RT5514_SRC_CTRL:
+       case RT5514_DSP_MAPPING | RT5514_DOWNFILTER2_CTRL1:
+       case RT5514_DSP_MAPPING | RT5514_PLL_SOURCE_CTRL:
+       case RT5514_DSP_MAPPING | RT5514_CLK_CTRL1:
+       case RT5514_DSP_MAPPING | RT5514_CLK_CTRL2:
+       case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL1:
+       case RT5514_DSP_MAPPING | RT5514_PLL3_CALIB_CTRL5:
+       case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL1:
+       case RT5514_DSP_MAPPING | RT5514_DELAY_BUF_CTRL3:
+       case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL1:
+       case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL2:
+       case RT5514_DSP_MAPPING | RT5514_DOWNFILTER0_CTRL3:
+       case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL1:
+       case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL2:
+       case RT5514_DSP_MAPPING | RT5514_DOWNFILTER1_CTRL3:
+       case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO10:
+       case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_LDO18_16:
+       case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC12:
+       case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC21:
+       case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC22:
+       case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADC23:
+       case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_MICBST:
+       case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_ADCFED:
+       case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_INBUF:
+       case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_VREF:
+       case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL3:
+       case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_1:
+       case RT5514_DSP_MAPPING | RT5514_ANA_CTRL_PLL1_2:
+       case RT5514_DSP_MAPPING | RT5514_DMIC_LP_CTRL:
+       case RT5514_DSP_MAPPING | RT5514_MISC_CTRL_DSP:
+       case RT5514_DSP_MAPPING | RT5514_DSP_CTRL1:
+       case RT5514_DSP_MAPPING | RT5514_DSP_CTRL3:
+       case RT5514_DSP_MAPPING | RT5514_DSP_CTRL4:
+       case RT5514_DSP_MAPPING | RT5514_VENDOR_ID1:
+       case RT5514_DSP_MAPPING | RT5514_VENDOR_ID2:
+               return true;
+
+       default:
+               return false;
+       }
+}
+
+/* {-3, 0, +3, +4.5, +7.5, +9.5, +12, +14, +17} dB */
+static const DECLARE_TLV_DB_RANGE(bst_tlv,
+       0, 2, TLV_DB_SCALE_ITEM(-300, 300, 0),
+       3, 3, TLV_DB_SCALE_ITEM(450, 0, 0),
+       4, 4, TLV_DB_SCALE_ITEM(750, 0, 0),
+       5, 5, TLV_DB_SCALE_ITEM(950, 0, 0),
+       6, 6, TLV_DB_SCALE_ITEM(1200, 0, 0),
+       7, 7, TLV_DB_SCALE_ITEM(1400, 0, 0),
+       8, 8, TLV_DB_SCALE_ITEM(1700, 0, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+
+static const struct snd_kcontrol_new rt5514_snd_controls[] = {
+       SOC_DOUBLE_TLV("MIC Boost Volume", RT5514_ANA_CTRL_MICBST,
+               RT5514_SEL_BSTL_SFT, RT5514_SEL_BSTR_SFT, 8, 0, bst_tlv),
+       SOC_DOUBLE_R_TLV("ADC1 Capture Volume", RT5514_DOWNFILTER0_CTRL1,
+               RT5514_DOWNFILTER0_CTRL2, RT5514_AD_GAIN_SFT, 127, 0,
+               adc_vol_tlv),
+       SOC_DOUBLE_R_TLV("ADC2 Capture Volume", RT5514_DOWNFILTER1_CTRL1,
+               RT5514_DOWNFILTER1_CTRL2, RT5514_AD_GAIN_SFT, 127, 0,
+               adc_vol_tlv),
+};
+
+/* ADC Mixer*/
+static const struct snd_kcontrol_new rt5514_sto1_adc_l_mix[] = {
+       SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL1,
+               RT5514_AD_DMIC_MIX_BIT, 1, 1),
+       SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL1,
+               RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5514_sto1_adc_r_mix[] = {
+       SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER0_CTRL2,
+               RT5514_AD_DMIC_MIX_BIT, 1, 1),
+       SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER0_CTRL2,
+               RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5514_sto2_adc_l_mix[] = {
+       SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL1,
+               RT5514_AD_DMIC_MIX_BIT, 1, 1),
+       SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL1,
+               RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+static const struct snd_kcontrol_new rt5514_sto2_adc_r_mix[] = {
+       SOC_DAPM_SINGLE("DMIC Switch", RT5514_DOWNFILTER1_CTRL2,
+               RT5514_AD_DMIC_MIX_BIT, 1, 1),
+       SOC_DAPM_SINGLE("ADC Switch", RT5514_DOWNFILTER1_CTRL2,
+               RT5514_AD_AD_MIX_BIT, 1, 1),
+};
+
+/* DMIC Source */
+static const char * const rt5514_dmic_src[] = {
+       "DMIC1", "DMIC2"
+};
+
+static const SOC_ENUM_SINGLE_DECL(
+       rt5514_stereo1_dmic_enum, RT5514_DIG_SOURCE_CTRL,
+       RT5514_AD0_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
+
+static const struct snd_kcontrol_new rt5514_sto1_dmic_mux =
+       SOC_DAPM_ENUM("Stereo1 DMIC Source", rt5514_stereo1_dmic_enum);
+
+static const SOC_ENUM_SINGLE_DECL(
+       rt5514_stereo2_dmic_enum, RT5514_DIG_SOURCE_CTRL,
+       RT5514_AD1_DMIC_INPUT_SEL_SFT, rt5514_dmic_src);
+
+static const struct snd_kcontrol_new rt5514_sto2_dmic_mux =
+       SOC_DAPM_ENUM("Stereo2 DMIC Source", rt5514_stereo2_dmic_enum);
+
+/**
+ * rt5514_calc_dmic_clk - Calculate the frequency divider parameter of dmic.
+ *
+ * @rate: base clock rate.
+ *
+ * Choose divider parameter that gives the highest possible DMIC frequency in
+ * 1MHz - 3MHz range.
+ */
+static int rt5514_calc_dmic_clk(struct snd_soc_codec *codec, int rate)
+{
+       int div[] = {2, 3, 4, 8, 12, 16, 24, 32};
+       int i;
+
+       if (rate < 1000000 * div[0]) {
+               pr_warn("Base clock rate %d is too low\n", rate);
+               return -EINVAL;
+       }
+
+       for (i = 0; i < ARRAY_SIZE(div); i++) {
+               /* find divider that gives DMIC frequency below 3.072MHz */
+               if (3072000 * div[i] >= rate)
+                       return i;
+       }
+
+       dev_warn(codec->dev, "Base clock rate %d is too high\n", rate);
+       return -EINVAL;
+}
+
+static int rt5514_set_dmic_clk(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+       int idx;
+
+       idx = rt5514_calc_dmic_clk(codec, rt5514->sysclk);
+       if (idx < 0)
+               dev_err(codec->dev, "Failed to set DMIC clock\n");
+       else
+               regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL1,
+                       RT5514_CLK_DMIC_OUT_SEL_MASK,
+                       idx << RT5514_CLK_DMIC_OUT_SEL_SFT);
+
+       return idx;
+}
+
+static int rt5514_is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
+       struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+
+       if (rt5514->sysclk_src == RT5514_SCLK_S_PLL1)
+               return 1;
+       else
+               return 0;
+}
+
+static const struct snd_soc_dapm_widget rt5514_dapm_widgets[] = {
+       /* Input Lines */
+       SND_SOC_DAPM_INPUT("DMIC1L"),
+       SND_SOC_DAPM_INPUT("DMIC1R"),
+       SND_SOC_DAPM_INPUT("DMIC2L"),
+       SND_SOC_DAPM_INPUT("DMIC2R"),
+
+       SND_SOC_DAPM_INPUT("AMICL"),
+       SND_SOC_DAPM_INPUT("AMICR"),
+
+       SND_SOC_DAPM_PGA("DMIC1", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("DMIC2", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("DMIC CLK", SND_SOC_NOPM, 0, 0,
+               rt5514_set_dmic_clk, SND_SOC_DAPM_PRE_PMU),
+
+       SND_SOC_DAPM_SUPPLY("ADC CLK", RT5514_CLK_CTRL1,
+               RT5514_CLK_AD_ANA1_EN_BIT, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("LDO18 IN", RT5514_PWR_ANA1,
+               RT5514_POW_LDO18_IN_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("LDO18 ADC", RT5514_PWR_ANA1,
+               RT5514_POW_LDO18_ADC_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("LDO21", RT5514_PWR_ANA1, RT5514_POW_LDO21_BIT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("BG LDO18 IN", RT5514_PWR_ANA1,
+               RT5514_POW_BG_LDO18_IN_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("BG LDO21", RT5514_PWR_ANA1,
+               RT5514_POW_BG_LDO21_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("BG MBIAS", RT5514_PWR_ANA2,
+               RT5514_POW_BG_MBIAS_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("MBIAS", RT5514_PWR_ANA2, RT5514_POW_MBIAS_BIT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("VREF2", RT5514_PWR_ANA2, RT5514_POW_VREF2_BIT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("VREF1", RT5514_PWR_ANA2, RT5514_POW_VREF1_BIT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC Power", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+
+       SND_SOC_DAPM_SUPPLY("LDO16L", RT5514_PWR_ANA2, RT5514_POWL_LDO16_BIT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC1L", RT5514_PWR_ANA2, RT5514_POW_ADC1_L_BIT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("BSTL2", RT5514_PWR_ANA2, RT5514_POW2_BSTL_BIT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("BSTL", RT5514_PWR_ANA2, RT5514_POW_BSTL_BIT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADCFEDL", RT5514_PWR_ANA2, RT5514_POW_ADCFEDL_BIT,
+               0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADCL Power", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("LDO16R", RT5514_PWR_ANA2, RT5514_POWR_LDO16_BIT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC1R", RT5514_PWR_ANA2, RT5514_POW_ADC1_R_BIT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("BSTR2", RT5514_PWR_ANA2, RT5514_POW2_BSTR_BIT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("BSTR", RT5514_PWR_ANA2, RT5514_POW_BSTR_BIT, 0,
+               NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADCFEDR", RT5514_PWR_ANA2, RT5514_POW_ADCFEDR_BIT,
+               0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADCR Power", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_SUPPLY("PLL1 LDO ENABLE", RT5514_ANA_CTRL_PLL1_2,
+               RT5514_EN_LDO_PLL1_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("PLL1 LDO", RT5514_PWR_ANA2,
+               RT5514_POW_PLL1_LDO_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("PLL1", RT5514_PWR_ANA2, RT5514_POW_PLL1_BIT, 0,
+               NULL, 0),
+
+       /* ADC Mux */
+       SND_SOC_DAPM_MUX("Stereo1 DMIC Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5514_sto1_dmic_mux),
+       SND_SOC_DAPM_MUX("Stereo2 DMIC Mux", SND_SOC_NOPM, 0, 0,
+                               &rt5514_sto2_dmic_mux),
+
+       /* ADC Mixer */
+       SND_SOC_DAPM_SUPPLY("adc stereo1 filter", RT5514_CLK_CTRL1,
+               RT5514_CLK_AD0_EN_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("adc stereo2 filter", RT5514_CLK_CTRL1,
+               RT5514_CLK_AD1_EN_BIT, 0, NULL, 0),
+
+       SND_SOC_DAPM_MIXER("Sto1 ADC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5514_sto1_adc_l_mix, ARRAY_SIZE(rt5514_sto1_adc_l_mix)),
+       SND_SOC_DAPM_MIXER("Sto1 ADC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5514_sto1_adc_r_mix, ARRAY_SIZE(rt5514_sto1_adc_r_mix)),
+       SND_SOC_DAPM_MIXER("Sto2 ADC MIXL", SND_SOC_NOPM, 0, 0,
+               rt5514_sto2_adc_l_mix, ARRAY_SIZE(rt5514_sto2_adc_l_mix)),
+       SND_SOC_DAPM_MIXER("Sto2 ADC MIXR", SND_SOC_NOPM, 0, 0,
+               rt5514_sto2_adc_r_mix, ARRAY_SIZE(rt5514_sto2_adc_r_mix)),
+
+       SND_SOC_DAPM_ADC("Stereo1 ADC MIXL", NULL, RT5514_DOWNFILTER0_CTRL1,
+               RT5514_AD_AD_MUTE_BIT, 1),
+       SND_SOC_DAPM_ADC("Stereo1 ADC MIXR", NULL, RT5514_DOWNFILTER0_CTRL2,
+               RT5514_AD_AD_MUTE_BIT, 1),
+       SND_SOC_DAPM_ADC("Stereo2 ADC MIXL", NULL, RT5514_DOWNFILTER1_CTRL1,
+               RT5514_AD_AD_MUTE_BIT, 1),
+       SND_SOC_DAPM_ADC("Stereo2 ADC MIXR", NULL, RT5514_DOWNFILTER1_CTRL2,
+               RT5514_AD_AD_MUTE_BIT, 1),
+
+       /* ADC PGA */
+       SND_SOC_DAPM_PGA("Stereo1 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Stereo2 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
+
+       /* Audio Interface */
+       SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
+};
+
+static const struct snd_soc_dapm_route rt5514_dapm_routes[] = {
+       { "DMIC1", NULL, "DMIC1L" },
+       { "DMIC1", NULL, "DMIC1R" },
+       { "DMIC2", NULL, "DMIC2L" },
+       { "DMIC2", NULL, "DMIC2R" },
+
+       { "DMIC1L", NULL, "DMIC CLK" },
+       { "DMIC1R", NULL, "DMIC CLK" },
+       { "DMIC2L", NULL, "DMIC CLK" },
+       { "DMIC2R", NULL, "DMIC CLK" },
+
+       { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
+       { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
+
+       { "Sto1 ADC MIXL", "DMIC Switch", "Stereo1 DMIC Mux" },
+       { "Sto1 ADC MIXL", "ADC Switch", "AMICL" },
+       { "Sto1 ADC MIXR", "DMIC Switch", "Stereo1 DMIC Mux" },
+       { "Sto1 ADC MIXR", "ADC Switch", "AMICR" },
+
+       { "ADC Power", NULL, "LDO18 IN" },
+       { "ADC Power", NULL, "LDO18 ADC" },
+       { "ADC Power", NULL, "LDO21" },
+       { "ADC Power", NULL, "BG LDO18 IN" },
+       { "ADC Power", NULL, "BG LDO21" },
+       { "ADC Power", NULL, "BG MBIAS" },
+       { "ADC Power", NULL, "MBIAS" },
+       { "ADC Power", NULL, "VREF2" },
+       { "ADC Power", NULL, "VREF1" },
+
+       { "ADCL Power", NULL, "LDO16L" },
+       { "ADCL Power", NULL, "ADC1L" },
+       { "ADCL Power", NULL, "BSTL2" },
+       { "ADCL Power", NULL, "BSTL" },
+       { "ADCL Power", NULL, "ADCFEDL" },
+
+       { "ADCR Power", NULL, "LDO16R" },
+       { "ADCR Power", NULL, "ADC1R" },
+       { "ADCR Power", NULL, "BSTR2" },
+       { "ADCR Power", NULL, "BSTR" },
+       { "ADCR Power", NULL, "ADCFEDR" },
+
+       { "AMICL", NULL, "ADC CLK" },
+       { "AMICL", NULL, "ADC Power" },
+       { "AMICL", NULL, "ADCL Power" },
+       { "AMICR", NULL, "ADC CLK" },
+       { "AMICR", NULL, "ADC Power" },
+       { "AMICR", NULL, "ADCR Power" },
+
+       { "PLL1 LDO", NULL, "PLL1 LDO ENABLE" },
+       { "PLL1", NULL, "PLL1 LDO" },
+
+       { "Stereo1 ADC MIXL", NULL, "Sto1 ADC MIXL" },
+       { "Stereo1 ADC MIXR", NULL, "Sto1 ADC MIXR" },
+
+       { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXL" },
+       { "Stereo1 ADC MIX", NULL, "Stereo1 ADC MIXR" },
+       { "Stereo1 ADC MIX", NULL, "adc stereo1 filter" },
+       { "adc stereo1 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll },
+
+       { "Stereo2 DMIC Mux", "DMIC1", "DMIC1" },
+       { "Stereo2 DMIC Mux", "DMIC2", "DMIC2" },
+
+       { "Sto2 ADC MIXL", "DMIC Switch", "Stereo2 DMIC Mux" },
+       { "Sto2 ADC MIXL", "ADC Switch", "AMICL" },
+       { "Sto2 ADC MIXR", "DMIC Switch", "Stereo2 DMIC Mux" },
+       { "Sto2 ADC MIXR", "ADC Switch", "AMICR" },
+
+       { "Stereo2 ADC MIXL", NULL, "Sto2 ADC MIXL" },
+       { "Stereo2 ADC MIXR", NULL, "Sto2 ADC MIXR" },
+
+       { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXL" },
+       { "Stereo2 ADC MIX", NULL, "Stereo2 ADC MIXR" },
+       { "Stereo2 ADC MIX", NULL, "adc stereo2 filter" },
+       { "adc stereo2 filter", NULL, "PLL1", rt5514_is_sys_clk_from_pll },
+
+       { "AIF1TX", NULL, "Stereo1 ADC MIX"},
+       { "AIF1TX", NULL, "Stereo2 ADC MIX"},
+};
+
+static int rt5514_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+       int pre_div, bclk_ms, frame_size;
+       unsigned int val_len = 0;
+
+       rt5514->lrck = params_rate(params);
+       pre_div = rl6231_get_clk_info(rt5514->sysclk, rt5514->lrck);
+       if (pre_div < 0) {
+               dev_err(codec->dev, "Unsupported clock setting\n");
+               return -EINVAL;
+       }
+
+       frame_size = snd_soc_params_to_frame_size(params);
+       if (frame_size < 0) {
+               dev_err(codec->dev, "Unsupported frame size: %d\n", frame_size);
+               return -EINVAL;
+       }
+
+       bclk_ms = frame_size > 32;
+       rt5514->bclk = rt5514->lrck * (32 << bclk_ms);
+
+       dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
+               rt5514->bclk, rt5514->lrck);
+       dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
+                               bclk_ms, pre_div, dai->id);
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               val_len = RT5514_I2S_DL_20;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               val_len = RT5514_I2S_DL_24;
+               break;
+       case SNDRV_PCM_FORMAT_S8:
+               val_len = RT5514_I2S_DL_8;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_I2S_DL_MASK,
+               val_len);
+       regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
+               RT5514_CLK_SYS_DIV_OUT_MASK | RT5514_SEL_ADC_OSR_MASK,
+               pre_div << RT5514_CLK_SYS_DIV_OUT_SFT |
+               pre_div << RT5514_SEL_ADC_OSR_SFT);
+
+       return 0;
+}
+
+static int rt5514_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+       unsigned int reg_val = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+
+       case SND_SOC_DAIFMT_NB_IF:
+               reg_val |= RT5514_I2S_LR_INV;
+               break;
+
+       case SND_SOC_DAIFMT_IB_NF:
+               reg_val |= RT5514_I2S_BP_INV;
+               break;
+
+       case SND_SOC_DAIFMT_IB_IF:
+               reg_val |= RT5514_I2S_BP_INV | RT5514_I2S_LR_INV;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               break;
+
+       case SND_SOC_DAIFMT_LEFT_J:
+               reg_val |= RT5514_I2S_DF_LEFT;
+               break;
+
+       case SND_SOC_DAIFMT_DSP_A:
+               reg_val |= RT5514_I2S_DF_PCM_A;
+               break;
+
+       case SND_SOC_DAIFMT_DSP_B:
+               reg_val |= RT5514_I2S_DF_PCM_B;
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1,
+               RT5514_I2S_DF_MASK | RT5514_I2S_BP_MASK | RT5514_I2S_LR_MASK,
+               reg_val);
+
+       return 0;
+}
+
+static int rt5514_set_dai_sysclk(struct snd_soc_dai *dai,
+               int clk_id, unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+       unsigned int reg_val = 0;
+
+       if (freq == rt5514->sysclk && clk_id == rt5514->sysclk_src)
+               return 0;
+
+       switch (clk_id) {
+       case RT5514_SCLK_S_MCLK:
+               reg_val |= RT5514_CLK_SYS_PRE_SEL_MCLK;
+               break;
+
+       case RT5514_SCLK_S_PLL1:
+               reg_val |= RT5514_CLK_SYS_PRE_SEL_PLL;
+               break;
+
+       default:
+               dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
+               return -EINVAL;
+       }
+
+       regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
+               RT5514_CLK_SYS_PRE_SEL_MASK, reg_val);
+
+       rt5514->sysclk = freq;
+       rt5514->sysclk_src = clk_id;
+
+       dev_dbg(dai->dev, "Sysclk is %dHz and clock id is %d\n", freq, clk_id);
+
+       return 0;
+}
+
+static int rt5514_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
+                       unsigned int freq_in, unsigned int freq_out)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+       struct rl6231_pll_code pll_code;
+       int ret;
+
+       if (!freq_in || !freq_out) {
+               dev_dbg(codec->dev, "PLL disabled\n");
+
+               rt5514->pll_in = 0;
+               rt5514->pll_out = 0;
+               regmap_update_bits(rt5514->regmap, RT5514_CLK_CTRL2,
+                       RT5514_CLK_SYS_PRE_SEL_MASK,
+                       RT5514_CLK_SYS_PRE_SEL_MCLK);
+
+               return 0;
+       }
+
+       if (source == rt5514->pll_src && freq_in == rt5514->pll_in &&
+           freq_out == rt5514->pll_out)
+               return 0;
+
+       switch (source) {
+       case RT5514_PLL1_S_MCLK:
+               regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL,
+                       RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_MCLK);
+               break;
+
+       case RT5514_PLL1_S_BCLK:
+               regmap_update_bits(rt5514->regmap, RT5514_PLL_SOURCE_CTRL,
+                       RT5514_PLL_1_SEL_MASK, RT5514_PLL_1_SEL_SCLK);
+               break;
+
+       default:
+               dev_err(codec->dev, "Unknown PLL source %d\n", source);
+               return -EINVAL;
+       }
+
+       ret = rl6231_pll_calc(freq_in, freq_out, &pll_code);
+       if (ret < 0) {
+               dev_err(codec->dev, "Unsupport input clock %d\n", freq_in);
+               return ret;
+       }
+
+       dev_dbg(codec->dev, "bypass=%d m=%d n=%d k=%d\n",
+               pll_code.m_bp, (pll_code.m_bp ? 0 : pll_code.m_code),
+               pll_code.n_code, pll_code.k_code);
+
+       regmap_write(rt5514->regmap, RT5514_ANA_CTRL_PLL1_1,
+               pll_code.k_code << RT5514_PLL_K_SFT |
+               pll_code.n_code << RT5514_PLL_N_SFT |
+               (pll_code.m_bp ? 0 : pll_code.m_code) << RT5514_PLL_M_SFT);
+       regmap_update_bits(rt5514->regmap, RT5514_ANA_CTRL_PLL1_2,
+               RT5514_PLL_M_BP, pll_code.m_bp << RT5514_PLL_M_BP_SFT);
+
+       rt5514->pll_in = freq_in;
+       rt5514->pll_out = freq_out;
+       rt5514->pll_src = source;
+
+       return 0;
+}
+
+static int rt5514_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+                       unsigned int rx_mask, int slots, int slot_width)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val = 0;
+
+       if (rx_mask || tx_mask)
+               val |= RT5514_TDM_MODE;
+
+       if (slots == 4)
+               val |= RT5514_TDMSLOT_SEL_RX_4CH | RT5514_TDMSLOT_SEL_TX_4CH;
+
+
+       switch (slot_width) {
+       case 20:
+               val |= RT5514_CH_LEN_RX_20 | RT5514_CH_LEN_TX_20;
+               break;
+
+       case 24:
+               val |= RT5514_CH_LEN_RX_24 | RT5514_CH_LEN_TX_24;
+               break;
+
+       case 32:
+               val |= RT5514_CH_LEN_RX_32 | RT5514_CH_LEN_TX_32;
+               break;
+
+       case 16:
+       default:
+               break;
+       }
+
+       regmap_update_bits(rt5514->regmap, RT5514_I2S_CTRL1, RT5514_TDM_MODE |
+               RT5514_TDMSLOT_SEL_RX_MASK | RT5514_TDMSLOT_SEL_TX_MASK |
+               RT5514_CH_LEN_RX_MASK | RT5514_CH_LEN_TX_MASK, val);
+
+       return 0;
+}
+
+static int rt5514_probe(struct snd_soc_codec *codec)
+{
+       struct rt5514_priv *rt5514 = snd_soc_codec_get_drvdata(codec);
+
+       rt5514->codec = codec;
+
+       return 0;
+}
+
+static int rt5514_i2c_read(void *context, unsigned int reg, unsigned int *val)
+{
+       struct i2c_client *client = context;
+       struct rt5514_priv *rt5514 = i2c_get_clientdata(client);
+
+       regmap_read(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val);
+
+       return 0;
+}
+
+static int rt5514_i2c_write(void *context, unsigned int reg, unsigned int val)
+{
+       struct i2c_client *client = context;
+       struct rt5514_priv *rt5514 = i2c_get_clientdata(client);
+
+       regmap_write(rt5514->i2c_regmap, reg | RT5514_DSP_MAPPING, val);
+
+       return 0;
+}
+
+#define RT5514_STEREO_RATES SNDRV_PCM_RATE_8000_192000
+#define RT5514_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
+                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
+
+struct snd_soc_dai_ops rt5514_aif_dai_ops = {
+       .hw_params = rt5514_hw_params,
+       .set_fmt = rt5514_set_dai_fmt,
+       .set_sysclk = rt5514_set_dai_sysclk,
+       .set_pll = rt5514_set_dai_pll,
+       .set_tdm_slot = rt5514_set_tdm_slot,
+};
+
+struct snd_soc_dai_driver rt5514_dai[] = {
+       {
+               .name = "rt5514-aif1",
+               .id = 0,
+               .capture = {
+                       .stream_name = "AIF1 Capture",
+                       .channels_min = 1,
+                       .channels_max = 4,
+                       .rates = RT5514_STEREO_RATES,
+                       .formats = RT5514_FORMATS,
+               },
+               .ops = &rt5514_aif_dai_ops,
+       }
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_rt5514 = {
+       .probe = rt5514_probe,
+       .idle_bias_off = true,
+       .controls = rt5514_snd_controls,
+       .num_controls = ARRAY_SIZE(rt5514_snd_controls),
+       .dapm_widgets = rt5514_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(rt5514_dapm_widgets),
+       .dapm_routes = rt5514_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(rt5514_dapm_routes),
+};
+
+static const struct regmap_config rt5514_i2c_regmap = {
+       .name = "i2c",
+       .reg_bits = 32,
+       .val_bits = 32,
+
+       .max_register = RT5514_DSP_MAPPING | RT5514_VENDOR_ID2,
+       .readable_reg = rt5514_i2c_readable_register,
+
+       .cache_type = REGCACHE_NONE,
+};
+
+static const struct regmap_config rt5514_regmap = {
+       .reg_bits = 16,
+       .val_bits = 32,
+
+       .max_register = RT5514_VENDOR_ID2,
+       .volatile_reg = rt5514_volatile_register,
+       .readable_reg = rt5514_readable_register,
+       .reg_read = rt5514_i2c_read,
+       .reg_write = rt5514_i2c_write,
+
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = rt5514_reg,
+       .num_reg_defaults = ARRAY_SIZE(rt5514_reg),
+       .use_single_rw = true,
+};
+
+static const struct i2c_device_id rt5514_i2c_id[] = {
+       { "rt5514", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, rt5514_i2c_id);
+
+#if defined(CONFIG_OF)
+static const struct of_device_id rt5514_of_match[] = {
+       { .compatible = "realtek,rt5514", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rt5514_of_match);
+#endif
+
+static int rt5514_i2c_probe(struct i2c_client *i2c,
+                   const struct i2c_device_id *id)
+{
+       struct rt5514_priv *rt5514;
+       int ret;
+       unsigned int val;
+
+       rt5514 = devm_kzalloc(&i2c->dev, sizeof(struct rt5514_priv),
+                               GFP_KERNEL);
+       if (rt5514 == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, rt5514);
+
+       rt5514->i2c_regmap = devm_regmap_init_i2c(i2c, &rt5514_i2c_regmap);
+       if (IS_ERR(rt5514->i2c_regmap)) {
+               ret = PTR_ERR(rt5514->i2c_regmap);
+               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       rt5514->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5514_regmap);
+       if (IS_ERR(rt5514->regmap)) {
+               ret = PTR_ERR(rt5514->regmap);
+               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       regmap_read(rt5514->regmap, RT5514_VENDOR_ID2, &val);
+       if (val != RT5514_DEVICE_ID) {
+               dev_err(&i2c->dev,
+                       "Device with ID register %x is not rt5514\n", val);
+               return -ENODEV;
+       }
+
+       ret = regmap_register_patch(rt5514->i2c_regmap, rt5514_i2c_patch,
+                                   ARRAY_SIZE(rt5514_i2c_patch));
+       if (ret != 0)
+               dev_warn(&i2c->dev, "Failed to apply i2c_regmap patch: %d\n",
+                       ret);
+
+       ret = regmap_register_patch(rt5514->regmap, rt5514_patch,
+                                   ARRAY_SIZE(rt5514_patch));
+       if (ret != 0)
+               dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
+
+       return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5514,
+                       rt5514_dai, ARRAY_SIZE(rt5514_dai));
+}
+
+static int rt5514_i2c_remove(struct i2c_client *i2c)
+{
+       snd_soc_unregister_codec(&i2c->dev);
+
+       return 0;
+}
+
+struct i2c_driver rt5514_i2c_driver = {
+       .driver = {
+               .name = "rt5514",
+               .of_match_table = of_match_ptr(rt5514_of_match),
+       },
+       .probe = rt5514_i2c_probe,
+       .remove   = rt5514_i2c_remove,
+       .id_table = rt5514_i2c_id,
+};
+module_i2c_driver(rt5514_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC RT5514 driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5514.h b/sound/soc/codecs/rt5514.h
new file mode 100644 (file)
index 0000000..6ad8a61
--- /dev/null
@@ -0,0 +1,252 @@
+/*
+ * rt5514.h  --  RT5514 ALSA SoC audio driver
+ *
+ * Copyright 2015 Realtek Microelectronics
+ * Author: Oder Chiou <oder_chiou@realtek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 as
+ * published by the Free Software Foundation.
+ */
+
+#ifndef __RT5514_H__
+#define __RT5514_H__
+
+#define RT5514_DEVICE_ID                       0x10ec5514
+
+#define RT5514_RESET                           0x2000
+#define RT5514_PWR_ANA1                                0x2004
+#define RT5514_PWR_ANA2                                0x2008
+#define RT5514_I2S_CTRL1                       0x2010
+#define RT5514_I2S_CTRL2                       0x2014
+#define RT5514_VAD_CTRL6                       0x2030
+#define RT5514_EXT_VAD_CTRL                    0x206c
+#define RT5514_DIG_IO_CTRL                     0x2070
+#define RT5514_PAD_CTRL1                       0x2080
+#define RT5514_DMIC_DATA_CTRL                  0x20a0
+#define RT5514_DIG_SOURCE_CTRL                 0x20a4
+#define RT5514_SRC_CTRL                                0x20ac
+#define RT5514_DOWNFILTER2_CTRL1               0x20d0
+#define RT5514_PLL_SOURCE_CTRL                 0x2100
+#define RT5514_CLK_CTRL1                       0x2104
+#define RT5514_CLK_CTRL2                       0x2108
+#define RT5514_PLL3_CALIB_CTRL1                        0x2110
+#define RT5514_PLL3_CALIB_CTRL5                        0x2124
+#define RT5514_DELAY_BUF_CTRL1                 0x2140
+#define RT5514_DELAY_BUF_CTRL3                 0x2148
+#define RT5514_DOWNFILTER0_CTRL1               0x2190
+#define RT5514_DOWNFILTER0_CTRL2               0x2194
+#define RT5514_DOWNFILTER0_CTRL3               0x2198
+#define RT5514_DOWNFILTER1_CTRL1               0x21a0
+#define RT5514_DOWNFILTER1_CTRL2               0x21a4
+#define RT5514_DOWNFILTER1_CTRL3               0x21a8
+#define RT5514_ANA_CTRL_LDO10                  0x2200
+#define RT5514_ANA_CTRL_LDO18_16               0x2204
+#define RT5514_ANA_CTRL_ADC12                  0x2210
+#define RT5514_ANA_CTRL_ADC21                  0x2214
+#define RT5514_ANA_CTRL_ADC22                  0x2218
+#define RT5514_ANA_CTRL_ADC23                  0x221c
+#define RT5514_ANA_CTRL_MICBST                 0x2220
+#define RT5514_ANA_CTRL_ADCFED                 0x2224
+#define RT5514_ANA_CTRL_INBUF                  0x2228
+#define RT5514_ANA_CTRL_VREF                   0x222c
+#define RT5514_ANA_CTRL_PLL3                   0x2240
+#define RT5514_ANA_CTRL_PLL1_1                 0x2260
+#define RT5514_ANA_CTRL_PLL1_2                 0x2264
+#define RT5514_DMIC_LP_CTRL                    0x2e00
+#define RT5514_MISC_CTRL_DSP                   0x2e04
+#define RT5514_DSP_CTRL1                       0x2f00
+#define RT5514_DSP_CTRL3                       0x2f08
+#define RT5514_DSP_CTRL4                       0x2f10
+#define RT5514_VENDOR_ID1                      0x2ff0
+#define RT5514_VENDOR_ID2                      0x2ff4
+
+#define RT5514_DSP_MAPPING                     0x18000000
+
+/* RT5514_PWR_ANA1 (0x2004) */
+#define RT5514_POW_LDO18_IN                    (0x1 << 5)
+#define RT5514_POW_LDO18_IN_BIT                        5
+#define RT5514_POW_LDO18_ADC                   (0x1 << 4)
+#define RT5514_POW_LDO18_ADC_BIT               4
+#define RT5514_POW_LDO21                       (0x1 << 3)
+#define RT5514_POW_LDO21_BIT                   3
+#define RT5514_POW_BG_LDO18_IN                 (0x1 << 2)
+#define RT5514_POW_BG_LDO18_IN_BIT             2
+#define RT5514_POW_BG_LDO21                    (0x1 << 1)
+#define RT5514_POW_BG_LDO21_BIT                        1
+
+/* RT5514_PWR_ANA2 (0x2008) */
+#define RT5514_POW_PLL1                                (0x1 << 18)
+#define RT5514_POW_PLL1_BIT                    18
+#define RT5514_POW_PLL1_LDO                    (0x1 << 16)
+#define RT5514_POW_PLL1_LDO_BIT                        16
+#define RT5514_POW_BG_MBIAS                    (0x1 << 15)
+#define RT5514_POW_BG_MBIAS_BIT                        15
+#define RT5514_POW_MBIAS                       (0x1 << 14)
+#define RT5514_POW_MBIAS_BIT                   14
+#define RT5514_POW_VREF2                       (0x1 << 13)
+#define RT5514_POW_VREF2_BIT                   13
+#define RT5514_POW_VREF1                       (0x1 << 12)
+#define RT5514_POW_VREF1_BIT                   12
+#define RT5514_POWR_LDO16                      (0x1 << 11)
+#define RT5514_POWR_LDO16_BIT                  11
+#define RT5514_POWL_LDO16                      (0x1 << 10)
+#define RT5514_POWL_LDO16_BIT                  10
+#define RT5514_POW_ADC2                                (0x1 << 9)
+#define RT5514_POW_ADC2_BIT                    9
+#define RT5514_POW_INPUT_BUF                   (0x1 << 8)
+#define RT5514_POW_INPUT_BUF_BIT               8
+#define RT5514_POW_ADC1_R                      (0x1 << 7)
+#define RT5514_POW_ADC1_R_BIT                  7
+#define RT5514_POW_ADC1_L                      (0x1 << 6)
+#define RT5514_POW_ADC1_L_BIT                  6
+#define RT5514_POW2_BSTR                       (0x1 << 5)
+#define RT5514_POW2_BSTR_BIT                   5
+#define RT5514_POW2_BSTL                       (0x1 << 4)
+#define RT5514_POW2_BSTL_BIT                   4
+#define RT5514_POW_BSTR                                (0x1 << 3)
+#define RT5514_POW_BSTR_BIT                    3
+#define RT5514_POW_BSTL                                (0x1 << 2)
+#define RT5514_POW_BSTL_BIT                    2
+#define RT5514_POW_ADCFEDR                     (0x1 << 1)
+#define RT5514_POW_ADCFEDR_BIT                 1
+#define RT5514_POW_ADCFEDL                     (0x1 << 0)
+#define RT5514_POW_ADCFEDL_BIT                 0
+
+/* RT5514_I2S_CTRL1 (0x2010) */
+#define RT5514_TDM_MODE                                (0x1 << 28)
+#define RT5514_TDM_MODE_SFT                    28
+#define RT5514_I2S_LR_MASK                     (0x1 << 26)
+#define RT5514_I2S_LR_SFT                      26
+#define RT5514_I2S_LR_NOR                      (0x0 << 26)
+#define RT5514_I2S_LR_INV                      (0x1 << 26)
+#define RT5514_I2S_BP_MASK                     (0x1 << 25)
+#define RT5514_I2S_BP_SFT                      25
+#define RT5514_I2S_BP_NOR                      (0x0 << 25)
+#define RT5514_I2S_BP_INV                      (0x1 << 25)
+#define RT5514_I2S_DF_MASK                     (0x7 << 16)
+#define RT5514_I2S_DF_SFT                      16
+#define RT5514_I2S_DF_I2S                      (0x0 << 16)
+#define RT5514_I2S_DF_LEFT                     (0x1 << 16)
+#define RT5514_I2S_DF_PCM_A                    (0x2 << 16)
+#define RT5514_I2S_DF_PCM_B                    (0x3 << 16)
+#define RT5514_TDMSLOT_SEL_RX_MASK             (0x3 << 10)
+#define RT5514_TDMSLOT_SEL_RX_SFT              10
+#define RT5514_TDMSLOT_SEL_RX_4CH              (0x1 << 10)
+#define RT5514_CH_LEN_RX_MASK                  (0x3 << 8)
+#define RT5514_CH_LEN_RX_SFT                   8
+#define RT5514_CH_LEN_RX_16                    (0x0 << 8)
+#define RT5514_CH_LEN_RX_20                    (0x1 << 8)
+#define RT5514_CH_LEN_RX_24                    (0x2 << 8)
+#define RT5514_CH_LEN_RX_32                    (0x3 << 8)
+#define RT5514_TDMSLOT_SEL_TX_MASK             (0x3 << 6)
+#define RT5514_TDMSLOT_SEL_TX_SFT              6
+#define RT5514_TDMSLOT_SEL_TX_4CH              (0x1 << 6)
+#define RT5514_CH_LEN_TX_MASK                  (0x3 << 4)
+#define RT5514_CH_LEN_TX_SFT                   4
+#define RT5514_CH_LEN_TX_16                    (0x0 << 4)
+#define RT5514_CH_LEN_TX_20                    (0x1 << 4)
+#define RT5514_CH_LEN_TX_24                    (0x2 << 4)
+#define RT5514_CH_LEN_TX_32                    (0x3 << 4)
+#define RT5514_I2S_DL_MASK                     (0x3 << 0)
+#define RT5514_I2S_DL_SFT                      0
+#define RT5514_I2S_DL_16                       (0x0 << 0)
+#define RT5514_I2S_DL_20                       (0x1 << 0)
+#define RT5514_I2S_DL_24                       (0x2 << 0)
+#define RT5514_I2S_DL_8                                (0x3 << 0)
+
+/* RT5514_DIG_SOURCE_CTRL (0x20a4) */
+#define RT5514_AD1_DMIC_INPUT_SEL              (0x1 << 1)
+#define RT5514_AD1_DMIC_INPUT_SEL_SFT          1
+#define RT5514_AD0_DMIC_INPUT_SEL              (0x1 << 0)
+#define RT5514_AD0_DMIC_INPUT_SEL_SFT          0
+
+/* RT5514_PLL_SOURCE_CTRL (0x2100) */
+#define RT5514_PLL_1_SEL_MASK                  (0x7 << 12)
+#define RT5514_PLL_1_SEL_SFT                   12
+#define RT5514_PLL_1_SEL_SCLK                  (0x3 << 12)
+#define RT5514_PLL_1_SEL_MCLK                  (0x4 << 12)
+
+/* RT5514_CLK_CTRL1 (0x2104) */
+#define RT5514_CLK_AD_ANA1_EN                  (0x1 << 31)
+#define RT5514_CLK_AD_ANA1_EN_BIT              31
+#define RT5514_CLK_AD1_EN                      (0x1 << 24)
+#define RT5514_CLK_AD1_EN_BIT                  24
+#define RT5514_CLK_AD0_EN                      (0x1 << 23)
+#define RT5514_CLK_AD0_EN_BIT                  23
+#define RT5514_CLK_DMIC_OUT_SEL_MASK           (0x7 << 8)
+#define RT5514_CLK_DMIC_OUT_SEL_SFT            8
+
+/* RT5514_CLK_CTRL2 (0x2108) */
+#define RT5514_CLK_SYS_DIV_OUT_MASK            (0x7 << 8)
+#define RT5514_CLK_SYS_DIV_OUT_SFT             8
+#define RT5514_SEL_ADC_OSR_MASK                        (0x7 << 4)
+#define RT5514_SEL_ADC_OSR_SFT                 4
+#define RT5514_CLK_SYS_PRE_SEL_MASK            (0x3 << 0)
+#define RT5514_CLK_SYS_PRE_SEL_SFT             0
+#define RT5514_CLK_SYS_PRE_SEL_MCLK            (0x2 << 0)
+#define RT5514_CLK_SYS_PRE_SEL_PLL             (0x3 << 0)
+
+/*  RT5514_DOWNFILTER_CTRL (0x2190 0x2194 0x21a0 0x21a4) */
+#define RT5514_AD_DMIC_MIX                     (0x1 << 11)
+#define RT5514_AD_DMIC_MIX_BIT                 11
+#define RT5514_AD_AD_MIX                       (0x1 << 10)
+#define RT5514_AD_AD_MIX_BIT                   10
+#define RT5514_AD_AD_MUTE                      (0x1 << 7)
+#define RT5514_AD_AD_MUTE_BIT                  7
+#define RT5514_AD_GAIN_MASK                    (0x7f << 0)
+#define RT5514_AD_GAIN_SFT                     0
+
+/*  RT5514_ANA_CTRL_MICBST (0x2220) */
+#define RT5514_SEL_BSTL_MASK                   (0xf << 4)
+#define RT5514_SEL_BSTL_SFT                    4
+#define RT5514_SEL_BSTR_MASK                   (0xf << 0)
+#define RT5514_SEL_BSTR_SFT                    0
+
+/*  RT5514_ANA_CTRL_PLL1_1 (0x2260) */
+#define RT5514_PLL_K_MAX                       0x1f
+#define RT5514_PLL_K_MASK                      (RT5514_PLL_K_MAX << 16)
+#define RT5514_PLL_K_SFT                       16
+#define RT5514_PLL_N_MAX                       0x1ff
+#define RT5514_PLL_N_MASK                      (RT5514_PLL_N_MAX << 7)
+#define RT5514_PLL_N_SFT                       4
+#define RT5514_PLL_M_MAX                       0xf
+#define RT5514_PLL_M_MASK                      (RT5514_PLL_M_MAX << 0)
+#define RT5514_PLL_M_SFT                       0
+
+/*  RT5514_ANA_CTRL_PLL1_2 (0x2264) */
+#define RT5514_PLL_M_BP                                (0x1 << 2)
+#define RT5514_PLL_M_BP_SFT                    2
+#define RT5514_PLL_K_BP                                (0x1 << 1)
+#define RT5514_PLL_K_BP_SFT                    1
+#define RT5514_EN_LDO_PLL1                     (0x1 << 0)
+#define RT5514_EN_LDO_PLL1_BIT                 0
+
+#define RT5514_PLL_INP_MAX                     40000000
+#define RT5514_PLL_INP_MIN                     256000
+
+/* System Clock Source */
+enum {
+       RT5514_SCLK_S_MCLK,
+       RT5514_SCLK_S_PLL1,
+};
+
+/* PLL1 Source */
+enum {
+       RT5514_PLL1_S_MCLK,
+       RT5514_PLL1_S_BCLK,
+};
+
+struct rt5514_priv {
+       struct snd_soc_codec *codec;
+       struct regmap *i2c_regmap, *regmap;
+       int sysclk;
+       int sysclk_src;
+       int lrck;
+       int bclk;
+       int pll_src;
+       int pll_in;
+       int pll_out;
+};
+
+#endif /* __RT5514_H__ */
index 1c10d8ed39d2f342a47f610c7be36a25b1ef8a51..f527b5b2817ba4f432766acd677c1e84c23174d5 100644 (file)
@@ -12,6 +12,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>
@@ -53,6 +54,7 @@ static const struct reg_sequence init_list[] = {
        {RT5616_PR_BASE + 0x21, 0x4040},
        {RT5616_PR_BASE + 0x23, 0x0004},
 };
+
 #define RT5616_INIT_REG_LEN ARRAY_SIZE(init_list)
 
 static const struct reg_default rt5616_reg[] = {
@@ -143,6 +145,7 @@ struct rt5616_priv {
        struct snd_soc_codec *codec;
        struct delayed_work patch_work;
        struct regmap *regmap;
+       struct clk *mclk;
 
        int sysclk;
        int sysclk_src;
@@ -162,9 +165,8 @@ static bool rt5616_volatile_register(struct device *dev, unsigned int reg)
 
        for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) {
                if (reg >= rt5616_ranges[i].range_min &&
-                       reg <= rt5616_ranges[i].range_max) {
+                   reg <= rt5616_ranges[i].range_max)
                        return true;
-               }
        }
 
        switch (reg) {
@@ -190,9 +192,8 @@ static bool rt5616_readable_register(struct device *dev, unsigned int reg)
 
        for (i = 0; i < ARRAY_SIZE(rt5616_ranges); i++) {
                if (reg >= rt5616_ranges[i].range_min &&
-                       reg <= rt5616_ranges[i].range_max) {
+                   reg <= rt5616_ranges[i].range_max)
                        return true;
-               }
        }
 
        switch (reg) {
@@ -307,45 +308,47 @@ static unsigned int bst_tlv[] = {
 static const struct snd_kcontrol_new rt5616_snd_controls[] = {
        /* Headphone Output Volume */
        SOC_DOUBLE("HP Playback Switch", RT5616_HP_VOL,
-               RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+                  RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+       SOC_DOUBLE("HPVOL Playback Switch", RT5616_HP_VOL,
+                  RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
        SOC_DOUBLE_TLV("HP Playback Volume", RT5616_HP_VOL,
-               RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
+                      RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
        /* OUTPUT Control */
        SOC_DOUBLE("OUT Playback Switch", RT5616_LOUT_CTRL1,
-               RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+                  RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
        SOC_DOUBLE("OUT Channel Switch", RT5616_LOUT_CTRL1,
-               RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
+                  RT5616_VOL_L_SFT, RT5616_VOL_R_SFT, 1, 1),
        SOC_DOUBLE_TLV("OUT Playback Volume", RT5616_LOUT_CTRL1,
-               RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
+                      RT5616_L_VOL_SFT, RT5616_R_VOL_SFT, 39, 1, out_vol_tlv),
 
        /* DAC Digital Volume */
        SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5616_DAC1_DIG_VOL,
-                       RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
-                       175, 0, dac_vol_tlv),
+                      RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
+                      175, 0, dac_vol_tlv),
        /* IN1/IN2 Control */
        SOC_SINGLE_TLV("IN1 Boost Volume", RT5616_IN1_IN2,
-               RT5616_BST_SFT1, 8, 0, bst_tlv),
+                      RT5616_BST_SFT1, 8, 0, bst_tlv),
        SOC_SINGLE_TLV("IN2 Boost Volume", RT5616_IN1_IN2,
-               RT5616_BST_SFT2, 8, 0, bst_tlv),
+                      RT5616_BST_SFT2, 8, 0, bst_tlv),
        /* INL/INR Volume Control */
        SOC_DOUBLE_TLV("IN Capture Volume", RT5616_INL1_INR1_VOL,
-                       RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT,
-                       31, 1, in_vol_tlv),
+                      RT5616_INL_VOL_SFT, RT5616_INR_VOL_SFT,
+                      31, 1, in_vol_tlv),
        /* ADC Digital Volume Control */
        SOC_DOUBLE("ADC Capture Switch", RT5616_ADC_DIG_VOL,
-               RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
+                  RT5616_L_MUTE_SFT, RT5616_R_MUTE_SFT, 1, 1),
        SOC_DOUBLE_TLV("ADC Capture Volume", RT5616_ADC_DIG_VOL,
-                       RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
-                       127, 0, adc_vol_tlv),
+                      RT5616_L_VOL_SFT, RT5616_R_VOL_SFT,
+                      127, 0, adc_vol_tlv),
 
        /* ADC Boost Volume Control */
        SOC_DOUBLE_TLV("ADC Boost Volume", RT5616_ADC_BST_VOL,
-                       RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT,
-                       3, 0, adc_bst_tlv),
+                      RT5616_ADC_L_BST_SFT, RT5616_ADC_R_BST_SFT,
+                      3, 0, adc_bst_tlv),
 };
 
 static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
-                        struct snd_soc_dapm_widget *sink)
+                              struct snd_soc_dapm_widget *sink)
 {
        unsigned int val;
 
@@ -462,20 +465,20 @@ static const struct snd_kcontrol_new rt5616_lout_mix[] = {
 };
 
 static int rt5616_adc_event(struct snd_soc_dapm_widget *w,
-       struct snd_kcontrol *kcontrol, int event)
+                           struct snd_kcontrol *kcontrol, int event)
 {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
                snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL,
-                               RT5616_L_MUTE | RT5616_R_MUTE, 0);
+                                   RT5616_L_MUTE | RT5616_R_MUTE, 0);
                break;
 
        case SND_SOC_DAPM_POST_PMD:
                snd_soc_update_bits(codec, RT5616_ADC_DIG_VOL,
-                               RT5616_L_MUTE | RT5616_R_MUTE,
-                               RT5616_L_MUTE | RT5616_R_MUTE);
+                                   RT5616_L_MUTE | RT5616_R_MUTE,
+                                   RT5616_L_MUTE | RT5616_R_MUTE);
                break;
 
        default:
@@ -486,7 +489,7 @@ static int rt5616_adc_event(struct snd_soc_dapm_widget *w,
 }
 
 static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
-                          struct snd_kcontrol *kcontrol, int event)
+                                   struct snd_kcontrol *kcontrol, int event)
 {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
@@ -494,54 +497,55 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
        case SND_SOC_DAPM_POST_PMU:
                /* depop parameters */
                snd_soc_update_bits(codec, RT5616_DEPOP_M2,
-                       RT5616_DEPOP_MASK, RT5616_DEPOP_MAN);
+                                   RT5616_DEPOP_MASK, RT5616_DEPOP_MAN);
                snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-                       RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
-                       RT5616_HP_CB_MASK, RT5616_HP_CP_PU |
-                       RT5616_HP_SG_DIS | RT5616_HP_CB_PU);
+                                   RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
+                                   RT5616_HP_CB_MASK, RT5616_HP_CP_PU |
+                                   RT5616_HP_SG_DIS | RT5616_HP_CB_PU);
                snd_soc_write(codec, RT5616_PR_BASE +
-                       RT5616_HP_DCC_INT1, 0x9f00);
+                             RT5616_HP_DCC_INT1, 0x9f00);
                /* headphone amp power on */
                snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-                       RT5616_PWR_FV1 | RT5616_PWR_FV2, 0);
+                                   RT5616_PWR_FV1 | RT5616_PWR_FV2, 0);
                snd_soc_update_bits(codec, RT5616_PWR_VOL,
-                       RT5616_PWR_HV_L | RT5616_PWR_HV_R,
-                       RT5616_PWR_HV_L | RT5616_PWR_HV_R);
+                                   RT5616_PWR_HV_L | RT5616_PWR_HV_R,
+                                   RT5616_PWR_HV_L | RT5616_PWR_HV_R);
                snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-                       RT5616_PWR_HP_L | RT5616_PWR_HP_R |
-                       RT5616_PWR_HA, RT5616_PWR_HP_L |
-                       RT5616_PWR_HP_R | RT5616_PWR_HA);
+                                   RT5616_PWR_HP_L | RT5616_PWR_HP_R |
+                                   RT5616_PWR_HA, RT5616_PWR_HP_L |
+                                   RT5616_PWR_HP_R | RT5616_PWR_HA);
                msleep(50);
                snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-                       RT5616_PWR_FV1 | RT5616_PWR_FV2,
-                       RT5616_PWR_FV1 | RT5616_PWR_FV2);
+                                   RT5616_PWR_FV1 | RT5616_PWR_FV2,
+                                   RT5616_PWR_FV1 | RT5616_PWR_FV2);
 
                snd_soc_update_bits(codec, RT5616_CHARGE_PUMP,
-                       RT5616_PM_HP_MASK, RT5616_PM_HP_HV);
+                                   RT5616_PM_HP_MASK, RT5616_PM_HP_HV);
                snd_soc_update_bits(codec, RT5616_PR_BASE +
-                       RT5616_CHOP_DAC_ADC, 0x0200, 0x0200);
+                                   RT5616_CHOP_DAC_ADC, 0x0200, 0x0200);
                snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-                       RT5616_HP_CO_MASK | RT5616_HP_SG_MASK,
-                       RT5616_HP_CO_EN | RT5616_HP_SG_EN);
+                                   RT5616_HP_CO_MASK | RT5616_HP_SG_MASK,
+                                   RT5616_HP_CO_EN | RT5616_HP_SG_EN);
                break;
        case SND_SOC_DAPM_PRE_PMD:
                snd_soc_update_bits(codec, RT5616_PR_BASE +
-                       RT5616_CHOP_DAC_ADC, 0x0200, 0x0);
+                                   RT5616_CHOP_DAC_ADC, 0x0200, 0x0);
                snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-                       RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
-                       RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
-                       RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
+                                   RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
+                                   RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
+                                   RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
                /* headphone amp power down */
                snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-                       RT5616_SMT_TRIG_MASK | RT5616_HP_CD_PD_MASK |
-                       RT5616_HP_CO_MASK | RT5616_HP_CP_MASK |
-                       RT5616_HP_SG_MASK | RT5616_HP_CB_MASK,
-                       RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN |
-                       RT5616_HP_CO_DIS | RT5616_HP_CP_PD |
-                       RT5616_HP_SG_EN | RT5616_HP_CB_PD);
+                                   RT5616_SMT_TRIG_MASK |
+                                   RT5616_HP_CD_PD_MASK | RT5616_HP_CO_MASK |
+                                   RT5616_HP_CP_MASK | RT5616_HP_SG_MASK |
+                                   RT5616_HP_CB_MASK,
+                                   RT5616_SMT_TRIG_DIS | RT5616_HP_CD_PD_EN |
+                                   RT5616_HP_CO_DIS | RT5616_HP_CP_PD |
+                                   RT5616_HP_SG_EN | RT5616_HP_CB_PD);
                snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-                       RT5616_PWR_HP_L | RT5616_PWR_HP_R |
-                       RT5616_PWR_HA, 0);
+                                   RT5616_PWR_HP_L | RT5616_PWR_HP_R |
+                                   RT5616_PWR_HA, 0);
                break;
        default:
                return 0;
@@ -551,7 +555,7 @@ static int rt5616_charge_pump_event(struct snd_soc_dapm_widget *w,
 }
 
 static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
-       struct snd_kcontrol *kcontrol, int event)
+                          struct snd_kcontrol *kcontrol, int event)
 {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
@@ -559,57 +563,57 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
        case SND_SOC_DAPM_POST_PMU:
                /* headphone unmute sequence */
                snd_soc_update_bits(codec, RT5616_DEPOP_M3,
-                       RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
-                       RT5616_CP_FQ3_MASK,
-                       (RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT) |
-                       (RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) |
-                       (RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT));
+                                   RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
+                                   RT5616_CP_FQ3_MASK,
+                                   RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ1_SFT |
+                                   RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT |
+                                   RT5616_CP_FQ_192_KHZ << RT5616_CP_FQ3_SFT);
                snd_soc_write(codec, RT5616_PR_BASE +
-                       RT5616_MAMP_INT_REG2, 0xfc00);
+                             RT5616_MAMP_INT_REG2, 0xfc00);
                snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-                       RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN);
+                                   RT5616_SMT_TRIG_MASK, RT5616_SMT_TRIG_EN);
                snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-                       RT5616_RSTN_MASK, RT5616_RSTN_EN);
+                                   RT5616_RSTN_MASK, RT5616_RSTN_EN);
                snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-                       RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK |
-                       RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS |
-                       RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
+                                   RT5616_RSTN_MASK | RT5616_HP_L_SMT_MASK |
+                                   RT5616_HP_R_SMT_MASK, RT5616_RSTN_DIS |
+                                   RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
                snd_soc_update_bits(codec, RT5616_HP_VOL,
-                       RT5616_L_MUTE | RT5616_R_MUTE, 0);
+                                   RT5616_L_MUTE | RT5616_R_MUTE, 0);
                msleep(100);
                snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-                       RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
-                       RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
-                       RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
+                                   RT5616_HP_SG_MASK | RT5616_HP_L_SMT_MASK |
+                                   RT5616_HP_R_SMT_MASK, RT5616_HP_SG_DIS |
+                                   RT5616_HP_L_SMT_DIS | RT5616_HP_R_SMT_DIS);
                msleep(20);
                snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET,
-                       RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN);
+                                   RT5616_HPD_PS_MASK, RT5616_HPD_PS_EN);
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
                /* headphone mute sequence */
                snd_soc_update_bits(codec, RT5616_DEPOP_M3,
-                       RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
-                       RT5616_CP_FQ3_MASK,
-                       (RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT) |
-                       (RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT) |
-                       (RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT));
+                                   RT5616_CP_FQ1_MASK | RT5616_CP_FQ2_MASK |
+                                   RT5616_CP_FQ3_MASK,
+                                   RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ1_SFT |
+                                   RT5616_CP_FQ_12_KHZ << RT5616_CP_FQ2_SFT |
+                                   RT5616_CP_FQ_96_KHZ << RT5616_CP_FQ3_SFT);
                snd_soc_write(codec, RT5616_PR_BASE +
-                       RT5616_MAMP_INT_REG2, 0xfc00);
+                             RT5616_MAMP_INT_REG2, 0xfc00);
                snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-                       RT5616_HP_SG_MASK, RT5616_HP_SG_EN);
+                                   RT5616_HP_SG_MASK, RT5616_HP_SG_EN);
                snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-                       RT5616_RSTP_MASK, RT5616_RSTP_EN);
+                                   RT5616_RSTP_MASK, RT5616_RSTP_EN);
                snd_soc_update_bits(codec, RT5616_DEPOP_M1,
-                       RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK |
-                       RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS |
-                       RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
+                                   RT5616_RSTP_MASK | RT5616_HP_L_SMT_MASK |
+                                   RT5616_HP_R_SMT_MASK, RT5616_RSTP_DIS |
+                                   RT5616_HP_L_SMT_EN | RT5616_HP_R_SMT_EN);
                snd_soc_update_bits(codec, RT5616_HP_CALIB_AMP_DET,
-                       RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS);
+                                   RT5616_HPD_PS_MASK, RT5616_HPD_PS_DIS);
                msleep(90);
                snd_soc_update_bits(codec, RT5616_HP_VOL,
-                       RT5616_L_MUTE | RT5616_R_MUTE,
-                       RT5616_L_MUTE | RT5616_R_MUTE);
+                                   RT5616_L_MUTE | RT5616_R_MUTE,
+                                   RT5616_L_MUTE | RT5616_R_MUTE);
                msleep(30);
                break;
 
@@ -621,24 +625,24 @@ static int rt5616_hp_event(struct snd_soc_dapm_widget *w,
 }
 
 static int rt5616_lout_event(struct snd_soc_dapm_widget *w,
-       struct snd_kcontrol *kcontrol, int event)
+                            struct snd_kcontrol *kcontrol, int event)
 {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
                snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-                       RT5616_PWR_LM, RT5616_PWR_LM);
+                                   RT5616_PWR_LM, RT5616_PWR_LM);
                snd_soc_update_bits(codec, RT5616_LOUT_CTRL1,
-                       RT5616_L_MUTE | RT5616_R_MUTE, 0);
+                                   RT5616_L_MUTE | RT5616_R_MUTE, 0);
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
                snd_soc_update_bits(codec, RT5616_LOUT_CTRL1,
-                       RT5616_L_MUTE | RT5616_R_MUTE,
-                       RT5616_L_MUTE | RT5616_R_MUTE);
+                                   RT5616_L_MUTE | RT5616_R_MUTE,
+                                   RT5616_L_MUTE | RT5616_R_MUTE);
                snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-                       RT5616_PWR_LM, 0);
+                                   RT5616_PWR_LM, 0);
                break;
 
        default:
@@ -649,19 +653,19 @@ static int rt5616_lout_event(struct snd_soc_dapm_widget *w,
 }
 
 static int rt5616_bst1_event(struct snd_soc_dapm_widget *w,
-       struct snd_kcontrol *kcontrol, int event)
+                            struct snd_kcontrol *kcontrol, int event)
 {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
                snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
-                       RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2);
+                                   RT5616_PWR_BST1_OP2, RT5616_PWR_BST1_OP2);
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
                snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
-                       RT5616_PWR_BST1_OP2, 0);
+                                   RT5616_PWR_BST1_OP2, 0);
                break;
 
        default:
@@ -672,19 +676,19 @@ static int rt5616_bst1_event(struct snd_soc_dapm_widget *w,
 }
 
 static int rt5616_bst2_event(struct snd_soc_dapm_widget *w,
-       struct snd_kcontrol *kcontrol, int event)
+                            struct snd_kcontrol *kcontrol, int event)
 {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
                snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
-                       RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2);
+                                   RT5616_PWR_BST2_OP2, RT5616_PWR_BST2_OP2);
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
                snd_soc_update_bits(codec, RT5616_PWR_ANLG2,
-                       RT5616_PWR_BST2_OP2, 0);
+                                   RT5616_PWR_BST2_OP2, 0);
                break;
 
        default:
@@ -696,13 +700,13 @@ static int rt5616_bst2_event(struct snd_soc_dapm_widget *w,
 
 static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
        SND_SOC_DAPM_SUPPLY("PLL1", RT5616_PWR_ANLG2,
-                       RT5616_PWR_PLL_BIT, 0, NULL, 0),
+                           RT5616_PWR_PLL_BIT, 0, NULL, 0),
        /* Input Side */
        /* micbias */
        SND_SOC_DAPM_SUPPLY("LDO", RT5616_PWR_ANLG1,
-                       RT5616_PWR_LDO_BIT, 0, NULL, 0),
+                           RT5616_PWR_LDO_BIT, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY("micbias1", RT5616_PWR_ANLG2,
-                       RT5616_PWR_MB1_BIT, 0, NULL, 0),
+                           RT5616_PWR_MB1_BIT, 0, NULL, 0),
 
        /* Input Lines */
        SND_SOC_DAPM_INPUT("MIC1"),
@@ -714,45 +718,47 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
 
        /* Boost */
        SND_SOC_DAPM_PGA_E("BST1", RT5616_PWR_ANLG2,
-               RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event,
-               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+                          RT5616_PWR_BST1_BIT, 0, NULL, 0, rt5616_bst1_event,
+                          SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
        SND_SOC_DAPM_PGA_E("BST2", RT5616_PWR_ANLG2,
-               RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event,
-               SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+                          RT5616_PWR_BST2_BIT, 0, NULL, 0, rt5616_bst2_event,
+                          SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
        /* Input Volume */
        SND_SOC_DAPM_PGA("INL1 VOL", RT5616_PWR_VOL,
-               RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
+                        RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
        SND_SOC_DAPM_PGA("INR1 VOL", RT5616_PWR_VOL,
-               RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
+                        RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
        SND_SOC_DAPM_PGA("INL2 VOL", RT5616_PWR_VOL,
-               RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
+                        RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
        SND_SOC_DAPM_PGA("INR2 VOL", RT5616_PWR_VOL,
-               RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
+                        RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
 
        /* REC Mixer */
        SND_SOC_DAPM_MIXER("RECMIXL", RT5616_PWR_MIXER, RT5616_PWR_RM_L_BIT, 0,
-                       rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)),
+                          rt5616_rec_l_mix, ARRAY_SIZE(rt5616_rec_l_mix)),
        SND_SOC_DAPM_MIXER("RECMIXR", RT5616_PWR_MIXER, RT5616_PWR_RM_R_BIT, 0,
-                       rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)),
+                          rt5616_rec_r_mix, ARRAY_SIZE(rt5616_rec_r_mix)),
        /* ADCs */
        SND_SOC_DAPM_ADC_E("ADC L", NULL, RT5616_PWR_DIG1,
-               RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event,
-               SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+                          RT5616_PWR_ADC_L_BIT, 0, rt5616_adc_event,
+                          SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
        SND_SOC_DAPM_ADC_E("ADC R", NULL, RT5616_PWR_DIG1,
-               RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event,
-               SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
+                          RT5616_PWR_ADC_R_BIT, 0, rt5616_adc_event,
+                          SND_SOC_DAPM_POST_PMD | SND_SOC_DAPM_POST_PMU),
 
        /* ADC Mixer */
        SND_SOC_DAPM_SUPPLY("stereo1 filter", RT5616_PWR_DIG2,
-               RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0),
+                           RT5616_PWR_ADC_STO1_F_BIT, 0, NULL, 0),
        SND_SOC_DAPM_MIXER("Stereo1 ADC MIXL", SND_SOC_NOPM, 0, 0,
-               rt5616_sto1_adc_l_mix, ARRAY_SIZE(rt5616_sto1_adc_l_mix)),
+                          rt5616_sto1_adc_l_mix,
+                          ARRAY_SIZE(rt5616_sto1_adc_l_mix)),
        SND_SOC_DAPM_MIXER("Stereo1 ADC MIXR", SND_SOC_NOPM, 0, 0,
-               rt5616_sto1_adc_r_mix, ARRAY_SIZE(rt5616_sto1_adc_r_mix)),
+                          rt5616_sto1_adc_r_mix,
+                          ARRAY_SIZE(rt5616_sto1_adc_r_mix)),
 
        /* Digital Interface */
        SND_SOC_DAPM_SUPPLY("I2S1", RT5616_PWR_DIG1,
-               RT5616_PWR_I2S1_BIT, 0, NULL, 0),
+                           RT5616_PWR_I2S1_BIT, 0, NULL, 0),
        SND_SOC_DAPM_PGA("IF1 DAC", SND_SOC_NOPM, 0, 0, NULL, 0),
        SND_SOC_DAPM_PGA("IF1 DAC1 L", SND_SOC_NOPM, 0, 0, NULL, 0),
        SND_SOC_DAPM_PGA("IF1 DAC1 R", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -770,68 +776,70 @@ static const struct snd_soc_dapm_widget rt5616_dapm_widgets[] = {
        /* Output Side */
        /* DAC mixer before sound effect  */
        SND_SOC_DAPM_MIXER("DAC MIXL", SND_SOC_NOPM, 0, 0,
-               rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)),
+                          rt5616_dac_l_mix, ARRAY_SIZE(rt5616_dac_l_mix)),
        SND_SOC_DAPM_MIXER("DAC MIXR", SND_SOC_NOPM, 0, 0,
-               rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)),
+                          rt5616_dac_r_mix, ARRAY_SIZE(rt5616_dac_r_mix)),
 
        SND_SOC_DAPM_SUPPLY("Stero1 DAC Power", RT5616_PWR_DIG2,
-                       RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0),
+                           RT5616_PWR_DAC_STO1_F_BIT, 0, NULL, 0),
 
        /* DAC Mixer */
        SND_SOC_DAPM_MIXER("Stereo DAC MIXL", SND_SOC_NOPM, 0, 0,
-               rt5616_sto_dac_l_mix, ARRAY_SIZE(rt5616_sto_dac_l_mix)),
+                          rt5616_sto_dac_l_mix,
+                          ARRAY_SIZE(rt5616_sto_dac_l_mix)),
        SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
-               rt5616_sto_dac_r_mix, ARRAY_SIZE(rt5616_sto_dac_r_mix)),
+                          rt5616_sto_dac_r_mix,
+                          ARRAY_SIZE(rt5616_sto_dac_r_mix)),
 
        /* DACs */
        SND_SOC_DAPM_DAC("DAC L1", NULL, RT5616_PWR_DIG1,
-                       RT5616_PWR_DAC_L1_BIT, 0),
+                        RT5616_PWR_DAC_L1_BIT, 0),
        SND_SOC_DAPM_DAC("DAC R1", NULL, RT5616_PWR_DIG1,
-                       RT5616_PWR_DAC_R1_BIT, 0),
+                        RT5616_PWR_DAC_R1_BIT, 0),
        /* OUT Mixer */
        SND_SOC_DAPM_MIXER("OUT MIXL", RT5616_PWR_MIXER, RT5616_PWR_OM_L_BIT,
-               0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)),
+                          0, rt5616_out_l_mix, ARRAY_SIZE(rt5616_out_l_mix)),
        SND_SOC_DAPM_MIXER("OUT MIXR", RT5616_PWR_MIXER, RT5616_PWR_OM_R_BIT,
-               0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)),
+                          0, rt5616_out_r_mix, ARRAY_SIZE(rt5616_out_r_mix)),
        /* Output Volume */
        SND_SOC_DAPM_PGA("OUTVOL L", RT5616_PWR_VOL,
-               RT5616_PWR_OV_L_BIT, 0, NULL, 0),
+                        RT5616_PWR_OV_L_BIT, 0, NULL, 0),
        SND_SOC_DAPM_PGA("OUTVOL R", RT5616_PWR_VOL,
-               RT5616_PWR_OV_R_BIT, 0, NULL, 0),
+                        RT5616_PWR_OV_R_BIT, 0, NULL, 0),
        SND_SOC_DAPM_PGA("HPOVOL L", RT5616_PWR_VOL,
-               RT5616_PWR_HV_L_BIT, 0, NULL, 0),
+                        RT5616_PWR_HV_L_BIT, 0, NULL, 0),
        SND_SOC_DAPM_PGA("HPOVOL R", RT5616_PWR_VOL,
-               RT5616_PWR_HV_R_BIT, 0, NULL, 0),
+                        RT5616_PWR_HV_R_BIT, 0, NULL, 0),
        SND_SOC_DAPM_PGA("DAC 1", SND_SOC_NOPM,
-               0, 0, NULL, 0),
+                        0, 0, NULL, 0),
        SND_SOC_DAPM_PGA("DAC 2", SND_SOC_NOPM,
-               0, 0, NULL, 0),
+                        0, 0, NULL, 0),
        SND_SOC_DAPM_PGA("HPOVOL", SND_SOC_NOPM,
-               0, 0, NULL, 0),
+                        0, 0, NULL, 0),
        SND_SOC_DAPM_PGA("INL1", RT5616_PWR_VOL,
-               RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
+                        RT5616_PWR_IN1_L_BIT, 0, NULL, 0),
        SND_SOC_DAPM_PGA("INR1", RT5616_PWR_VOL,
-               RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
+                        RT5616_PWR_IN1_R_BIT, 0, NULL, 0),
        SND_SOC_DAPM_PGA("INL2", RT5616_PWR_VOL,
-               RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
+                        RT5616_PWR_IN2_L_BIT, 0, NULL, 0),
        SND_SOC_DAPM_PGA("INR2", RT5616_PWR_VOL,
-               RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
+                        RT5616_PWR_IN2_R_BIT, 0, NULL, 0),
        /* HPO/LOUT/Mono Mixer */
        SND_SOC_DAPM_MIXER("HPO MIX", SND_SOC_NOPM, 0, 0,
-               rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)),
+                          rt5616_hpo_mix, ARRAY_SIZE(rt5616_hpo_mix)),
        SND_SOC_DAPM_MIXER("LOUT MIX", SND_SOC_NOPM, 0, 0,
-               rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)),
+                          rt5616_lout_mix, ARRAY_SIZE(rt5616_lout_mix)),
 
        SND_SOC_DAPM_PGA_S("HP amp", 1, SND_SOC_NOPM, 0, 0,
-               rt5616_hp_event, SND_SOC_DAPM_PRE_PMD |
-               SND_SOC_DAPM_POST_PMU),
+                          rt5616_hp_event, SND_SOC_DAPM_PRE_PMD |
+                          SND_SOC_DAPM_POST_PMU),
        SND_SOC_DAPM_PGA_S("LOUT amp", 1, SND_SOC_NOPM, 0, 0,
-               rt5616_lout_event, SND_SOC_DAPM_PRE_PMD |
-               SND_SOC_DAPM_POST_PMU),
+                          rt5616_lout_event, SND_SOC_DAPM_PRE_PMD |
+                          SND_SOC_DAPM_POST_PMU),
 
        SND_SOC_DAPM_SUPPLY_S("Charge Pump", 1, SND_SOC_NOPM, 0, 0,
-               rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU |
-               SND_SOC_DAPM_PRE_PMD),
+                             rt5616_charge_pump_event, SND_SOC_DAPM_POST_PMU |
+                             SND_SOC_DAPM_PRE_PMD),
 
        /* Output Lines */
        SND_SOC_DAPM_OUTPUT("HPOL"),
@@ -950,7 +958,8 @@ static const struct snd_soc_dapm_route rt5616_dapm_routes[] = {
 };
 
 static int rt5616_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *dai)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_codec *codec = rtd->codec;
@@ -977,7 +986,7 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
        dev_dbg(dai->dev, "bclk is %dHz and lrck is %dHz\n",
                rt5616->bclk[dai->id], rt5616->lrck[dai->id]);
        dev_dbg(dai->dev, "bclk_ms is %d and pre_div is %d for iis %d\n",
-                               bclk_ms, pre_div, dai->id);
+               bclk_ms, pre_div, dai->id);
 
        switch (params_format(params)) {
        case SNDRV_PCM_FORMAT_S16_LE:
@@ -998,10 +1007,9 @@ static int rt5616_hw_params(struct snd_pcm_substream *substream,
        mask_clk = RT5616_I2S_PD1_MASK;
        val_clk = pre_div << RT5616_I2S_PD1_SFT;
        snd_soc_update_bits(codec, RT5616_I2S1_SDP,
-               RT5616_I2S_DL_MASK, val_len);
+                           RT5616_I2S_DL_MASK, val_len);
        snd_soc_update_bits(codec, RT5616_ADDA_CLK1, mask_clk, val_clk);
 
-
        return 0;
 }
 
@@ -1050,15 +1058,14 @@ static int rt5616_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        }
 
        snd_soc_update_bits(codec, RT5616_I2S1_SDP,
-               RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK |
-               RT5616_I2S_DF_MASK, reg_val);
-
+                           RT5616_I2S_MS_MASK | RT5616_I2S_BP_MASK |
+                           RT5616_I2S_DF_MASK, reg_val);
 
        return 0;
 }
 
 static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
-               int clk_id, unsigned int freq, int dir)
+                                int clk_id, unsigned int freq, int dir)
 {
        struct snd_soc_codec *codec = dai->codec;
        struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
@@ -1078,8 +1085,9 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
                dev_err(codec->dev, "Invalid clock id (%d)\n", clk_id);
                return -EINVAL;
        }
+
        snd_soc_update_bits(codec, RT5616_GLB_CLK,
-               RT5616_SCLK_SRC_MASK, reg_val);
+                           RT5616_SCLK_SRC_MASK, reg_val);
        rt5616->sysclk = freq;
        rt5616->sysclk_src = clk_id;
 
@@ -1089,7 +1097,7 @@ static int rt5616_set_dai_sysclk(struct snd_soc_dai *dai,
 }
 
 static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
-                       unsigned int freq_in, unsigned int freq_out)
+                             unsigned int freq_in, unsigned int freq_out)
 {
        struct snd_soc_codec *codec = dai->codec;
        struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
@@ -1106,19 +1114,22 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
                rt5616->pll_in = 0;
                rt5616->pll_out = 0;
                snd_soc_update_bits(codec, RT5616_GLB_CLK,
-                       RT5616_SCLK_SRC_MASK, RT5616_SCLK_SRC_MCLK);
+                                   RT5616_SCLK_SRC_MASK,
+                                   RT5616_SCLK_SRC_MCLK);
                return 0;
        }
 
        switch (source) {
        case RT5616_PLL1_S_MCLK:
                snd_soc_update_bits(codec, RT5616_GLB_CLK,
-                       RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_MCLK);
+                                   RT5616_PLL1_SRC_MASK,
+                                   RT5616_PLL1_SRC_MCLK);
                break;
        case RT5616_PLL1_S_BCLK1:
        case RT5616_PLL1_S_BCLK2:
                snd_soc_update_bits(codec, RT5616_GLB_CLK,
-                       RT5616_PLL1_SRC_MASK, RT5616_PLL1_SRC_BCLK1);
+                                   RT5616_PLL1_SRC_MASK,
+                                   RT5616_PLL1_SRC_BCLK1);
                break;
        default:
                dev_err(codec->dev, "Unknown PLL source %d\n", source);
@@ -1136,10 +1147,11 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
                pll_code.n_code, pll_code.k_code);
 
        snd_soc_write(codec, RT5616_PLL_CTRL1,
-               pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code);
+                     pll_code.n_code << RT5616_PLL_N_SFT | pll_code.k_code);
        snd_soc_write(codec, RT5616_PLL_CTRL2,
-               (pll_code.m_bp ? 0 : pll_code.m_code) << RT5616_PLL_M_SFT |
-               pll_code.m_bp << RT5616_PLL_M_BP_SFT);
+                     (pll_code.m_bp ? 0 : pll_code.m_code) <<
+                     RT5616_PLL_M_SFT |
+                     pll_code.m_bp << RT5616_PLL_M_BP_SFT);
 
        rt5616->pll_in = freq_in;
        rt5616->pll_out = freq_out;
@@ -1149,22 +1161,50 @@ static int rt5616_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
 }
 
 static int rt5616_set_bias_level(struct snd_soc_codec *codec,
-                       enum snd_soc_bias_level level)
+                                enum snd_soc_bias_level level)
 {
+       struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
        switch (level) {
+
+       case SND_SOC_BIAS_ON:
+               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(rt5616->mclk))
+                       break;
+
+               if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+                       clk_disable_unprepare(rt5616->mclk);
+               } else {
+                       ret = clk_prepare_enable(rt5616->mclk);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
        case SND_SOC_BIAS_STANDBY:
                if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
                        snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-                               RT5616_PWR_VREF1 | RT5616_PWR_MB |
-                               RT5616_PWR_BG | RT5616_PWR_VREF2,
-                               RT5616_PWR_VREF1 | RT5616_PWR_MB |
-                               RT5616_PWR_BG | RT5616_PWR_VREF2);
+                                           RT5616_PWR_VREF1 | RT5616_PWR_MB |
+                                           RT5616_PWR_BG | RT5616_PWR_VREF2,
+                                           RT5616_PWR_VREF1 | RT5616_PWR_MB |
+                                           RT5616_PWR_BG | RT5616_PWR_VREF2);
                        mdelay(10);
                        snd_soc_update_bits(codec, RT5616_PWR_ANLG1,
-                               RT5616_PWR_FV1 | RT5616_PWR_FV2,
-                               RT5616_PWR_FV1 | RT5616_PWR_FV2);
+                                           RT5616_PWR_FV1 | RT5616_PWR_FV2,
+                                           RT5616_PWR_FV1 | RT5616_PWR_FV2);
                        snd_soc_update_bits(codec, RT5616_D_MISC,
-                               RT5616_D_GATE_EN, RT5616_D_GATE_EN);
+                                           RT5616_D_GATE_EN,
+                                           RT5616_D_GATE_EN);
                }
                break;
 
@@ -1189,6 +1229,11 @@ static int rt5616_probe(struct snd_soc_codec *codec)
 {
        struct rt5616_priv *rt5616 = snd_soc_codec_get_drvdata(codec);
 
+       /* Check if MCLK provided */
+       rt5616->mclk = devm_clk_get(codec->dev, "mclk");
+       if (PTR_ERR(rt5616->mclk) == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
        rt5616->codec = codec;
 
        return 0;
@@ -1218,11 +1263,10 @@ static int rt5616_resume(struct snd_soc_codec *codec)
 #define rt5616_resume NULL
 #endif
 
-#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_96000
+#define RT5616_STEREO_RATES SNDRV_PCM_RATE_8000_192000
 #define RT5616_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
 
-
 struct snd_soc_dai_ops rt5616_aif_dai_ops = {
        .hw_params = rt5616_hw_params,
        .set_fmt = rt5616_set_dai_fmt,
@@ -1296,15 +1340,15 @@ MODULE_DEVICE_TABLE(of, rt5616_of_match);
 #endif
 
 static int rt5616_i2c_probe(struct i2c_client *i2c,
-                   const struct i2c_device_id *id)
+                           const struct i2c_device_id *id)
 {
        struct rt5616_priv *rt5616;
        unsigned int val;
        int ret;
 
        rt5616 = devm_kzalloc(&i2c->dev, sizeof(struct rt5616_priv),
-                               GFP_KERNEL);
-       if (rt5616 == NULL)
+                             GFP_KERNEL);
+       if (!rt5616)
                return -ENOMEM;
 
        i2c_set_clientdata(i2c, rt5616);
@@ -1326,14 +1370,14 @@ static int rt5616_i2c_probe(struct i2c_client *i2c,
        }
        regmap_write(rt5616->regmap, RT5616_RESET, 0);
        regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
-               RT5616_PWR_VREF1 | RT5616_PWR_MB |
-               RT5616_PWR_BG | RT5616_PWR_VREF2,
-               RT5616_PWR_VREF1 | RT5616_PWR_MB |
-               RT5616_PWR_BG | RT5616_PWR_VREF2);
+                          RT5616_PWR_VREF1 | RT5616_PWR_MB |
+                          RT5616_PWR_BG | RT5616_PWR_VREF2,
+                          RT5616_PWR_VREF1 | RT5616_PWR_MB |
+                          RT5616_PWR_BG | RT5616_PWR_VREF2);
        mdelay(10);
        regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
-               RT5616_PWR_FV1 | RT5616_PWR_FV2,
-               RT5616_PWR_FV1 | RT5616_PWR_FV2);
+                          RT5616_PWR_FV1 | RT5616_PWR_FV2,
+                          RT5616_PWR_FV1 | RT5616_PWR_FV2);
 
        ret = regmap_register_patch(rt5616->regmap, init_list,
                                    ARRAY_SIZE(init_list));
@@ -1341,11 +1385,10 @@ static int rt5616_i2c_probe(struct i2c_client *i2c,
                dev_warn(&i2c->dev, "Failed to apply regmap patch: %d\n", ret);
 
        regmap_update_bits(rt5616->regmap, RT5616_PWR_ANLG1,
-               RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V);
+                          RT5616_PWR_LDO_DVO_MASK, RT5616_PWR_LDO_DVO_1_2V);
 
        return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5616,
-                       rt5616_dai, ARRAY_SIZE(rt5616_dai));
-
+                                     rt5616_dai, ARRAY_SIZE(rt5616_dai));
 }
 
 static int rt5616_i2c_remove(struct i2c_client *i2c)
@@ -1361,7 +1404,6 @@ static void rt5616_i2c_shutdown(struct i2c_client *client)
 
        regmap_write(rt5616->regmap, RT5616_HP_VOL, 0xc8c8);
        regmap_write(rt5616->regmap, RT5616_LOUT_CTRL1, 0xc8c8);
-
 }
 
 static struct i2c_driver rt5616_i2c_driver = {
index 11d032cdc65814126b426544e7fe0dd0214786ac..e8b5ba04417a9dee6ace5d136fb1c97c752254a2 100644 (file)
@@ -1217,11 +1217,14 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
        SND_SOC_DAPM_MIXER("DIG MIXR", SND_SOC_NOPM, 0, 0,
                rt5640_dig_r_mix, ARRAY_SIZE(rt5640_dig_r_mix)),
        /* DACs */
-       SND_SOC_DAPM_DAC("DAC L1", NULL, RT5640_PWR_DIG1,
-                       RT5640_PWR_DAC_L1_BIT, 0),
-       SND_SOC_DAPM_DAC("DAC R1", NULL, RT5640_PWR_DIG1,
-                       RT5640_PWR_DAC_R1_BIT, 0),
-
+       SND_SOC_DAPM_DAC("DAC L1", NULL, SND_SOC_NOPM,
+                       0, 0),
+       SND_SOC_DAPM_DAC("DAC R1", NULL, SND_SOC_NOPM,
+                       0, 0),
+       SND_SOC_DAPM_SUPPLY("DAC L1 Power", RT5640_PWR_DIG1,
+               RT5640_PWR_DAC_L1_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DAC R1 Power", RT5640_PWR_DIG1,
+               RT5640_PWR_DAC_R1_BIT, 0, NULL, 0),
        /* SPK/OUT Mixer */
        SND_SOC_DAPM_MIXER("SPK MIXL", RT5640_PWR_MIXER, RT5640_PWR_SM_L_BIT,
                0, rt5640_spk_l_mix, ARRAY_SIZE(rt5640_spk_l_mix)),
@@ -1298,9 +1301,9 @@ static const struct snd_soc_dapm_widget rt5640_specific_dapm_widgets[] = {
        SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
                rt5640_sto_dac_r_mix, ARRAY_SIZE(rt5640_sto_dac_r_mix)),
 
-       SND_SOC_DAPM_DAC("DAC R2", NULL, RT5640_PWR_DIG1, RT5640_PWR_DAC_R2_BIT,
+       SND_SOC_DAPM_DAC("DAC R2", NULL, SND_SOC_NOPM, 0,
                0),
-       SND_SOC_DAPM_DAC("DAC L2", NULL, RT5640_PWR_DIG1, RT5640_PWR_DAC_L2_BIT,
+       SND_SOC_DAPM_DAC("DAC L2", NULL, SND_SOC_NOPM, 0,
                0),
 
        SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
@@ -1317,6 +1320,10 @@ static const struct snd_soc_dapm_widget rt5640_specific_dapm_widgets[] = {
                rt5640_mono_mix, ARRAY_SIZE(rt5640_mono_mix)),
        SND_SOC_DAPM_SUPPLY("Improve MONO Amp Drv", RT5640_PWR_ANLG1,
                RT5640_PWR_MA_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DAC L2 Power", RT5640_PWR_DIG1,
+               RT5640_PWR_DAC_L2_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("DAC R2 Power", RT5640_PWR_DIG1,
+               RT5640_PWR_DAC_R2_BIT, 0, NULL, 0),
 
        SND_SOC_DAPM_OUTPUT("MONOP"),
        SND_SOC_DAPM_OUTPUT("MONON"),
@@ -1328,11 +1335,6 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
        SND_SOC_DAPM_MIXER("Stereo DAC MIXR", SND_SOC_NOPM, 0, 0,
                rt5639_sto_dac_r_mix, ARRAY_SIZE(rt5639_sto_dac_r_mix)),
 
-       SND_SOC_DAPM_SUPPLY("DAC L2 Filter", RT5640_PWR_DIG1,
-               RT5640_PWR_DAC_L2_BIT, 0, NULL, 0),
-       SND_SOC_DAPM_SUPPLY("DAC R2 Filter", RT5640_PWR_DIG1,
-               RT5640_PWR_DAC_R2_BIT, 0, NULL, 0),
-
        SND_SOC_DAPM_MIXER("OUT MIXL", RT5640_PWR_MIXER, RT5640_PWR_OM_L_BIT,
                0, rt5639_out_l_mix, ARRAY_SIZE(rt5639_out_l_mix)),
        SND_SOC_DAPM_MIXER("OUT MIXR", RT5640_PWR_MIXER, RT5640_PWR_OM_R_BIT,
@@ -1493,8 +1495,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
 
        {"DAC MIXL", "Stereo ADC Switch", "Stereo ADC MIXL"},
        {"DAC MIXL", "INF1 Switch", "IF1 DAC L"},
+       {"DAC MIXL", NULL, "DAC L1 Power"},
        {"DAC MIXR", "Stereo ADC Switch", "Stereo ADC MIXR"},
        {"DAC MIXR", "INF1 Switch", "IF1 DAC R"},
+       {"DAC MIXR", NULL, "DAC R1 Power"},
 
        {"Stereo DAC MIXL", "DAC L1 Switch", "DAC MIXL"},
        {"Stereo DAC MIXR", "DAC R1 Switch", "DAC MIXR"},
@@ -1507,8 +1511,10 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
 
        {"DAC L1", NULL, "Stereo DAC MIXL"},
        {"DAC L1", NULL, "PLL1", is_sys_clk_from_pll},
+       {"DAC L1", NULL, "DAC L1 Power"},
        {"DAC R1", NULL, "Stereo DAC MIXR"},
        {"DAC R1", NULL, "PLL1", is_sys_clk_from_pll},
+       {"DAC R1", NULL, "DAC R1 Power"},
 
        {"SPK MIXL", "REC MIXL Switch", "RECMIXL"},
        {"SPK MIXL", "INL Switch", "INL VOL"},
@@ -1595,8 +1601,9 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = {
 
        {"DAC L2 Mux", "IF2", "IF2 DAC L"},
        {"DAC L2 Mux", "Base L/R", "Audio DSP"},
-
+       {"DAC L2 Mux", NULL, "DAC L2 Power"},
        {"DAC R2 Mux", "IF2", "IF2 DAC R"},
+       {"DAC R2 Mux", NULL, "DAC R2 Power"},
 
        {"Stereo DAC MIXL", "DAC L2 Switch", "DAC L2 Mux"},
        {"Stereo DAC MIXL", "ANC Switch", "ANC"},
@@ -1614,8 +1621,10 @@ static const struct snd_soc_dapm_route rt5640_specific_dapm_routes[] = {
 
        {"DAC L2", NULL, "Mono DAC MIXL"},
        {"DAC L2", NULL, "PLL1", is_sys_clk_from_pll},
+       {"DAC L2", NULL, "DAC L2 Power"},
        {"DAC R2", NULL, "Mono DAC MIXR"},
        {"DAC R2", NULL, "PLL1", is_sys_clk_from_pll},
+       {"DAC R2", NULL, "DAC R2 Power"},
 
        {"SPK MIXL", "DAC L2 Switch", "DAC L2"},
        {"SPK MIXR", "DAC R2 Switch", "DAC R2"},
@@ -1656,8 +1665,8 @@ static const struct snd_soc_dapm_route rt5639_specific_dapm_routes[] = {
        {"DIG MIXL", "DAC L2 Switch", "IF2 DAC L"},
        {"DIG MIXR", "DAC R2 Switch", "IF2 DAC R"},
 
-       {"IF2 DAC L", NULL, "DAC L2 Filter"},
-       {"IF2 DAC R", NULL, "DAC R2 Filter"},
+       {"IF2 DAC L", NULL, "DAC L2 Power"},
+       {"IF2 DAC R", NULL, "DAC R2 Power"},
 };
 
 static int get_sdp_info(struct snd_soc_codec *codec, int dai_id)
@@ -1880,7 +1889,7 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
        struct snd_soc_codec *codec = dai->codec;
        struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
        struct rl6231_pll_code pll_code;
-       int ret, dai_sel;
+       int ret;
 
        if (source == rt5640->pll_src && freq_in == rt5640->pll_in &&
            freq_out == rt5640->pll_out)
@@ -1902,21 +1911,12 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
                        RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_MCLK);
                break;
        case RT5640_PLL1_S_BCLK1:
+               snd_soc_update_bits(codec, RT5640_GLB_CLK,
+                       RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1);
+               break;
        case RT5640_PLL1_S_BCLK2:
-               dai_sel = get_sdp_info(codec, dai->id);
-               if (dai_sel < 0) {
-                       dev_err(codec->dev,
-                               "Failed to get sdp info: %d\n", dai_sel);
-                       return -EINVAL;
-               }
-               if (dai_sel & RT5640_U_IF1) {
-                       snd_soc_update_bits(codec, RT5640_GLB_CLK,
-                               RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK1);
-               }
-               if (dai_sel & RT5640_U_IF2) {
-                       snd_soc_update_bits(codec, RT5640_GLB_CLK,
-                               RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2);
-               }
+               snd_soc_update_bits(codec, RT5640_GLB_CLK,
+                       RT5640_PLL1_SRC_MASK, RT5640_PLL1_SRC_BCLK2);
                break;
        default:
                dev_err(codec->dev, "Unknown PLL source %d\n", source);
@@ -1949,7 +1949,33 @@ static int rt5640_set_dai_pll(struct snd_soc_dai *dai, int pll_id, int source,
 static int rt5640_set_bias_level(struct snd_soc_codec *codec,
                        enum snd_soc_bias_level level)
 {
+       struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
        switch (level) {
+       case SND_SOC_BIAS_ON:
+               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(rt5640->mclk))
+                       break;
+
+               if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_ON) {
+                       clk_disable_unprepare(rt5640->mclk);
+               } else {
+                       ret = clk_prepare_enable(rt5640->mclk);
+                       if (ret)
+                               return ret;
+               }
+               break;
+
        case SND_SOC_BIAS_STANDBY:
                if (SND_SOC_BIAS_OFF == snd_soc_codec_get_bias_level(codec)) {
                        snd_soc_update_bits(codec, RT5640_PWR_ANLG1,
@@ -2088,6 +2114,11 @@ static int rt5640_probe(struct snd_soc_codec *codec)
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct rt5640_priv *rt5640 = snd_soc_codec_get_drvdata(codec);
 
+       /* Check if MCLK provided */
+       rt5640->mclk = devm_clk_get(codec->dev, "mclk");
+       if (PTR_ERR(rt5640->mclk) == -EPROBE_DEFER)
+               return -EPROBE_DEFER;
+
        rt5640->codec = codec;
 
        snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
index 83a7150ddc24eb637de10d1670eb8614ef0063c2..1761c3a98b76234f7d0ffd2edac3c38fa1119148 100644 (file)
@@ -12,6 +12,7 @@
 #ifndef _RT5640_H
 #define _RT5640_H
 
+#include <linux/clk.h>
 #include <sound/rt5640.h>
 
 /* Info */
@@ -2097,6 +2098,7 @@ struct rt5640_priv {
        struct snd_soc_codec *codec;
        struct rt5640_platform_data pdata;
        struct regmap *regmap;
+       struct clk *mclk;
 
        int sysclk;
        int sysclk_src;
index 93e8c9017633f97ed264cd9e54c4aaab606d54f3..7af5e7380d61e45658feb4ba190b2e2a7cd8efec 100644 (file)
@@ -1674,7 +1674,7 @@ static void hp_amp_power(struct snd_soc_codec *codec, int on)
                                regmap_write(rt5645->regmap, RT5645_PR_BASE +
                                        RT5645_MAMP_INT_REG2, 0xfc00);
                                snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
-                               msleep(70);
+                               msleep(90);
                                rt5645->hp_on = true;
                        } else {
                                /* depop parameters */
@@ -3029,13 +3029,18 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
                        RT5645_PWR_BG | RT5645_PWR_VREF2,
                        RT5645_PWR_VREF1 | RT5645_PWR_MB |
                        RT5645_PWR_BG | RT5645_PWR_VREF2);
+               mdelay(10);
                snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
                        RT5645_PWR_FV1 | RT5645_PWR_FV2,
                        RT5645_PWR_FV1 | RT5645_PWR_FV2);
-               if (rt5645->en_button_func &&
-                       snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
-                       queue_delayed_work(system_power_efficient_wq,
-                               &rt5645->jack_detect_work, msecs_to_jiffies(0));
+               if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+                       snd_soc_write(codec, RT5645_DEPOP_M2, 0x1140);
+                       msleep(40);
+                       if (rt5645->en_button_func)
+                               queue_delayed_work(system_power_efficient_wq,
+                                       &rt5645->jack_detect_work,
+                                       msecs_to_jiffies(0));
+               }
                break;
 
        case SND_SOC_BIAS_OFF:
index fb8ea05c0de1d9ddb60134f68c1dc3b180a39c29..1b30914c2d916ddd70db50b6e6203eeb0ef486e3 100644 (file)
@@ -4176,7 +4176,7 @@ static int rt5659_i2c_remove(struct i2c_client *i2c)
        return 0;
 }
 
-void rt5659_i2c_shutdown(struct i2c_client *client)
+static void rt5659_i2c_shutdown(struct i2c_client *client)
 {
        struct rt5659_priv *rt5659 = i2c_get_clientdata(client);
 
index e619d5651b09bbd6401cdea6734acf0a4a6f17dc..080c78e88e102199cb9b8923f0556ce084ad22d8 100644 (file)
@@ -352,6 +352,11 @@ static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable)
        regcache_cache_only(ssm4567->regmap, !enable);
 
        if (enable) {
+               ret = regmap_write(ssm4567->regmap, SSM4567_REG_SOFT_RESET,
+                       0x00);
+               if (ret)
+                       return ret;
+
                ret = regmap_update_bits(ssm4567->regmap,
                        SSM4567_REG_POWER_CTRL,
                        SSM4567_POWER_SPWDN, 0x00);
index 64637d1cf4e5673f17145b13d7c12681bed21d48..a8b3e3f701f964bdd3b54869a655d9cbfec74264 100644 (file)
@@ -619,7 +619,7 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w,
 {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
-       unsigned int v;
+       unsigned int v = 0;
        int ret;
 
        switch (event) {
@@ -654,7 +654,7 @@ static int wm5102_adsp_power_ev(struct snd_soc_dapm_widget *w,
                break;
        }
 
-       return wm_adsp2_early_event(w, kcontrol, event);
+       return wm_adsp2_early_event(w, kcontrol, event, v);
 }
 
 static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
@@ -1408,7 +1408,7 @@ ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
 ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
 ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
 
-WM_ADSP2_E("DSP1", 0, wm5102_adsp_power_ev),
+WM_ADSP2("DSP1", 0, wm5102_adsp_power_ev),
 
 SND_SOC_DAPM_OUTPUT("HPOUT1L"),
 SND_SOC_DAPM_OUTPUT("HPOUT1R"),
@@ -1599,6 +1599,9 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
        { "Slim2 Capture", NULL, "SYSCLK" },
        { "Slim3 Capture", NULL, "SYSCLK" },
 
+       { "Audio Trace DSP", NULL, "DSP1" },
+       { "Audio Trace DSP", NULL, "SYSCLK" },
+
        { "IN1L PGA", NULL, "IN1L" },
        { "IN1R PGA", NULL, "IN1R" },
 
@@ -1735,7 +1738,7 @@ static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
        }
 }
 
-#define WM5102_RATES SNDRV_PCM_RATE_8000_192000
+#define WM5102_RATES SNDRV_PCM_RATE_KNOT
 
 #define WM5102_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -1864,14 +1867,67 @@ static struct snd_soc_dai_driver wm5102_dai[] = {
                 },
                .ops = &arizona_simple_dai_ops,
        },
+       {
+               .name = "wm5102-cpu-trace",
+               .capture = {
+                       .stream_name = "Audio Trace CPU",
+                       .channels_min = 1,
+                       .channels_max = 6,
+                       .rates = WM5102_RATES,
+                       .formats = WM5102_FORMATS,
+               },
+               .compress_new = snd_soc_new_compress,
+       },
+       {
+               .name = "wm5102-dsp-trace",
+               .capture = {
+                       .stream_name = "Audio Trace DSP",
+                       .channels_min = 1,
+                       .channels_max = 4,
+                       .rates = WM5102_RATES,
+                       .formats = WM5102_FORMATS,
+               },
+       },
 };
 
+static int wm5102_open(struct snd_compr_stream *stream)
+{
+       struct snd_soc_pcm_runtime *rtd = stream->private_data;
+       struct wm5102_priv *priv = snd_soc_codec_get_drvdata(rtd->codec);
+
+       return wm_adsp_compr_open(&priv->core.adsp[0], stream);
+}
+
+static irqreturn_t wm5102_adsp2_irq(int irq, void *data)
+{
+       struct wm5102_priv *priv = data;
+       struct arizona *arizona = priv->core.arizona;
+       int ret;
+
+       ret = wm_adsp_compr_handle_irq(&priv->core.adsp[0]);
+       if (ret == -ENODEV) {
+               dev_err(arizona->dev, "Spurious compressed data IRQ\n");
+               return IRQ_NONE;
+       }
+
+       return IRQ_HANDLED;
+}
+
 static int wm5102_codec_probe(struct snd_soc_codec *codec)
 {
        struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
        struct wm5102_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->core.arizona;
        int ret;
 
+       ret = arizona_request_irq(arizona, ARIZONA_IRQ_DSP_IRQ1,
+                                 "ADSP2 Compressed IRQ", wm5102_adsp2_irq,
+                                 priv);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to request DSP IRQ: %d\n", ret);
+               return ret;
+       }
+
        ret = wm_adsp2_codec_probe(&priv->core.adsp[0], codec);
        if (ret)
                return ret;
@@ -1946,6 +2002,20 @@ static struct snd_soc_codec_driver soc_codec_dev_wm5102 = {
        .num_dapm_routes = ARRAY_SIZE(wm5102_dapm_routes),
 };
 
+static struct snd_compr_ops wm5102_compr_ops = {
+       .open = wm5102_open,
+       .free = wm_adsp_compr_free,
+       .set_params = wm_adsp_compr_set_params,
+       .get_caps = wm_adsp_compr_get_caps,
+       .trigger = wm_adsp_compr_trigger,
+       .pointer = wm_adsp_compr_pointer,
+       .copy = wm_adsp_compr_copy,
+};
+
+static struct snd_soc_platform_driver wm5102_compr_platform = {
+       .compr_ops = &wm5102_compr_ops,
+};
+
 static int wm5102_probe(struct platform_device *pdev)
 {
        struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
@@ -2005,12 +2075,25 @@ static int wm5102_probe(struct platform_device *pdev)
        pm_runtime_enable(&pdev->dev);
        pm_runtime_idle(&pdev->dev);
 
-       return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102,
+       ret = snd_soc_register_platform(&pdev->dev, &wm5102_compr_platform);
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5102,
                                      wm5102_dai, ARRAY_SIZE(wm5102_dai));
+       if (ret < 0) {
+               dev_err(&pdev->dev, "Failed to register codec: %d\n", ret);
+               snd_soc_unregister_platform(&pdev->dev);
+       }
+
+       return ret;
 }
 
 static int wm5102_remove(struct platform_device *pdev)
 {
+       snd_soc_unregister_platform(&pdev->dev);
        snd_soc_unregister_codec(&pdev->dev);
        pm_runtime_disable(&pdev->dev);
 
index 97c0f1e2388637dcdc686da1f1a5cf06037a6944..83ba70fe16e69488a473a68bcc97d4cc2408874f 100644 (file)
@@ -191,6 +191,25 @@ static int wm5110_sysclk_ev(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int wm5110_adsp_power_ev(struct snd_soc_dapm_widget *w,
+                               struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+       unsigned int v;
+       int ret;
+
+       ret = regmap_read(arizona->regmap, ARIZONA_SYSTEM_CLOCK_1, &v);
+       if (ret != 0) {
+               dev_err(codec->dev, "Failed to read SYSCLK state: %d\n", ret);
+               return ret;
+       }
+
+       v = (v & ARIZONA_SYSCLK_FREQ_MASK) >> ARIZONA_SYSCLK_FREQ_SHIFT;
+
+       return wm_adsp2_early_event(w, kcontrol, event, v);
+}
+
 static const struct reg_sequence wm5110_no_dre_left_enable[] = {
        { 0x3024, 0xE410 },
        { 0x3025, 0x0056 },
@@ -1179,10 +1198,10 @@ SND_SOC_DAPM_PGA("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
 SND_SOC_DAPM_PGA("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
                 NULL, 0),
 
-WM_ADSP2("DSP1", 0),
-WM_ADSP2("DSP2", 1),
-WM_ADSP2("DSP3", 2),
-WM_ADSP2("DSP4", 3),
+WM_ADSP2("DSP1", 0, wm5110_adsp_power_ev),
+WM_ADSP2("DSP2", 1, wm5110_adsp_power_ev),
+WM_ADSP2("DSP3", 2, wm5110_adsp_power_ev),
+WM_ADSP2("DSP4", 3, wm5110_adsp_power_ev),
 
 SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
                 ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
@@ -1809,6 +1828,9 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
        { "Voice Control DSP", NULL, "DSP3" },
        { "Voice Control DSP", NULL, "SYSCLK" },
 
+       { "Audio Trace DSP", NULL, "DSP1" },
+       { "Audio Trace DSP", NULL, "SYSCLK" },
+
        { "IN1L PGA", NULL, "IN1L" },
        { "IN1R PGA", NULL, "IN1R" },
 
@@ -2002,7 +2024,7 @@ static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
        }
 }
 
-#define WM5110_RATES SNDRV_PCM_RATE_8000_192000
+#define WM5110_RATES SNDRV_PCM_RATE_KNOT
 
 #define WM5110_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
@@ -2152,6 +2174,27 @@ static struct snd_soc_dai_driver wm5110_dai[] = {
                        .formats = WM5110_FORMATS,
                },
        },
+       {
+               .name = "wm5110-cpu-trace",
+               .capture = {
+                       .stream_name = "Audio Trace CPU",
+                       .channels_min = 1,
+                       .channels_max = 6,
+                       .rates = WM5110_RATES,
+                       .formats = WM5110_FORMATS,
+               },
+               .compress_new = snd_soc_new_compress,
+       },
+       {
+               .name = "wm5110-dsp-trace",
+               .capture = {
+                       .stream_name = "Audio Trace DSP",
+                       .channels_min = 1,
+                       .channels_max = 6,
+                       .rates = WM5110_RATES,
+                       .formats = WM5110_FORMATS,
+               },
+       },
 };
 
 static int wm5110_open(struct snd_compr_stream *stream)
@@ -2163,6 +2206,8 @@ static int wm5110_open(struct snd_compr_stream *stream)
 
        if (strcmp(rtd->codec_dai->name, "wm5110-dsp-voicectrl") == 0) {
                n_adsp = 2;
+       } else if (strcmp(rtd->codec_dai->name, "wm5110-dsp-trace") == 0) {
+               n_adsp = 0;
        } else {
                dev_err(arizona->dev,
                        "No suitable compressed stream for DAI '%s'\n",
@@ -2175,12 +2220,21 @@ static int wm5110_open(struct snd_compr_stream *stream)
 
 static irqreturn_t wm5110_adsp2_irq(int irq, void *data)
 {
-       struct wm5110_priv *florida = data;
-       int ret;
+       struct wm5110_priv *priv = data;
+       struct arizona *arizona = priv->core.arizona;
+       int serviced = 0;
+       int i, ret;
 
-       ret = wm_adsp_compr_handle_irq(&florida->core.adsp[2]);
-       if (ret == -ENODEV)
+       for (i = 0; i < WM5110_NUM_ADSP; ++i) {
+               ret = wm_adsp_compr_handle_irq(&priv->core.adsp[i]);
+               if (ret != -ENODEV)
+                       serviced++;
+       }
+
+       if (!serviced) {
+               dev_err(arizona->dev, "Spurious compressed data IRQ\n");
                return IRQ_NONE;
+       }
 
        return IRQ_HANDLED;
 }
@@ -2366,7 +2420,7 @@ static int wm5110_probe(struct platform_device *pdev)
        ret = snd_soc_register_platform(&pdev->dev, &wm5110_compr_platform);
        if (ret < 0) {
                dev_err(&pdev->dev, "Failed to register platform: %d\n", ret);
-               goto error;
+               return ret;
        }
 
        ret = snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm5110,
@@ -2376,7 +2430,6 @@ static int wm5110_probe(struct platform_device *pdev)
                snd_soc_unregister_platform(&pdev->dev);
        }
 
-error:
        return ret;
 }
 
index c284c7b6db8bfa028846936e82a624de8c1a95af..dc8c3b1ebb6fe9a98b3468daa5cc6d9e06f6ae6a 100644 (file)
 
 #include "wm8974.h"
 
+struct wm8974_priv {
+       unsigned int mclk;
+       unsigned int fs;
+};
+
 static const struct reg_default wm8974_reg_defaults[] = {
        {  0, 0x0000 }, {  1, 0x0000 }, {  2, 0x0000 }, {  3, 0x0000 },
        {  4, 0x0050 }, {  5, 0x0000 }, {  6, 0x0140 }, {  7, 0x0000 },
@@ -379,6 +384,79 @@ static int wm8974_set_dai_clkdiv(struct snd_soc_dai *codec_dai,
        return 0;
 }
 
+static unsigned int wm8974_get_mclkdiv(unsigned int f_in, unsigned int f_out,
+                                      int *mclkdiv)
+{
+       unsigned int ratio = 2 * f_in / f_out;
+
+       if (ratio <= 2) {
+               *mclkdiv = WM8974_MCLKDIV_1;
+               ratio = 2;
+       } else if (ratio == 3) {
+               *mclkdiv = WM8974_MCLKDIV_1_5;
+       } else if (ratio == 4) {
+               *mclkdiv = WM8974_MCLKDIV_2;
+       } else if (ratio <= 6) {
+               *mclkdiv = WM8974_MCLKDIV_3;
+               ratio = 6;
+       } else if (ratio <= 8) {
+               *mclkdiv = WM8974_MCLKDIV_4;
+               ratio = 8;
+       } else if (ratio <= 12) {
+               *mclkdiv = WM8974_MCLKDIV_6;
+               ratio = 12;
+       } else if (ratio <= 16) {
+               *mclkdiv = WM8974_MCLKDIV_8;
+               ratio = 16;
+       } else {
+               *mclkdiv = WM8974_MCLKDIV_12;
+               ratio = 24;
+       }
+
+       return f_out * ratio / 2;
+}
+
+static int wm8974_update_clocks(struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec);
+       unsigned int fs256;
+       unsigned int fpll = 0;
+       unsigned int f;
+       int mclkdiv;
+
+       if (!priv->mclk || !priv->fs)
+               return 0;
+
+       fs256 = 256 * priv->fs;
+
+       f = wm8974_get_mclkdiv(priv->mclk, fs256, &mclkdiv);
+
+       if (f != priv->mclk) {
+               /* The PLL performs best around 90MHz */
+               fpll = wm8974_get_mclkdiv(22500000, fs256, &mclkdiv);
+       }
+
+       wm8974_set_dai_pll(dai, 0, 0, priv->mclk, fpll);
+       wm8974_set_dai_clkdiv(dai, WM8974_MCLKDIV, mclkdiv);
+
+       return 0;
+}
+
+static int wm8974_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
+                                unsigned int freq, int dir)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       if (dir != SND_SOC_CLOCK_IN)
+               return -EINVAL;
+
+       priv->mclk = freq;
+
+       return wm8974_update_clocks(dai);
+}
+
 static int wm8974_set_dai_fmt(struct snd_soc_dai *codec_dai,
                unsigned int fmt)
 {
@@ -441,8 +519,15 @@ static int wm8974_pcm_hw_params(struct snd_pcm_substream *substream,
                                struct snd_soc_dai *dai)
 {
        struct snd_soc_codec *codec = dai->codec;
+       struct wm8974_priv *priv = snd_soc_codec_get_drvdata(codec);
        u16 iface = snd_soc_read(codec, WM8974_IFACE) & 0x19f;
        u16 adn = snd_soc_read(codec, WM8974_ADD) & 0x1f1;
+       int err;
+
+       priv->fs = params_rate(params);
+       err = wm8974_update_clocks(dai);
+       if (err)
+               return err;
 
        /* bit size */
        switch (params_width(params)) {
@@ -547,6 +632,7 @@ static const struct snd_soc_dai_ops wm8974_ops = {
        .set_fmt = wm8974_set_dai_fmt,
        .set_clkdiv = wm8974_set_dai_clkdiv,
        .set_pll = wm8974_set_dai_pll,
+       .set_sysclk = wm8974_set_dai_sysclk,
 };
 
 static struct snd_soc_dai_driver wm8974_dai = {
@@ -606,9 +692,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
 static int wm8974_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
+       struct wm8974_priv *priv;
        struct regmap *regmap;
        int ret;
 
+       priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, priv);
+
        regmap = devm_regmap_init_i2c(i2c, &wm8974_regmap);
        if (IS_ERR(regmap))
                return PTR_ERR(regmap);
index b4dba3a02abafd73044724146601fade748637b4..52d766efe14f0d318844d43bc19df2dcf16bee0d 100644 (file)
@@ -943,7 +943,7 @@ static int wm8997_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
        }
 }
 
-#define WM8997_RATES SNDRV_PCM_RATE_8000_192000
+#define WM8997_RATES SNDRV_PCM_RATE_KNOT
 
 #define WM8997_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
index 7719bc509e50b182a0c63b3280154bac4de277fe..012396074a8a6da52d416d5edf10cc092bed16e5 100644 (file)
@@ -1170,7 +1170,7 @@ static const struct snd_soc_dapm_route wm8998_dapm_routes[] = {
        { "DRC1 Signal Activity", NULL, "DRC1R" },
 };
 
-#define WM8998_RATES SNDRV_PCM_RATE_8000_192000
+#define WM8998_RATES SNDRV_PCM_RATE_KNOT
 
 #define WM8998_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
index b9195b9c2b05175278d4e779f7db87aad20d9f63..d3b1cb15e7f0766111b6ced6fbe36d65571703aa 100644 (file)
@@ -32,9 +32,6 @@
 #include <sound/initval.h>
 #include <sound/tlv.h>
 
-#include <linux/mfd/arizona/registers.h>
-
-#include "arizona.h"
 #include "wm_adsp.h"
 
 #define adsp_crit(_dsp, fmt, ...) \
@@ -295,6 +292,8 @@ struct wm_adsp_compr {
 
        u32 *raw_buf;
        unsigned int copied_total;
+
+       unsigned int sample_rate;
 };
 
 #define WM_ADSP_DATA_WORD_SIZE         3
@@ -328,7 +327,7 @@ struct wm_adsp_buffer_region_def {
        unsigned int size_offset;
 };
 
-static struct wm_adsp_buffer_region_def ez2control_regions[] = {
+static const struct wm_adsp_buffer_region_def default_regions[] = {
        {
                .mem_type = WMFW_ADSP2_XM,
                .base_offset = HOST_BUFFER_FIELD(X_buf_base),
@@ -350,10 +349,10 @@ struct wm_adsp_fw_caps {
        u32 id;
        struct snd_codec_desc desc;
        int num_regions;
-       struct wm_adsp_buffer_region_def *region_defs;
+       const struct wm_adsp_buffer_region_def *region_defs;
 };
 
-static const struct wm_adsp_fw_caps ez2control_caps[] = {
+static const struct wm_adsp_fw_caps ctrl_caps[] = {
        {
                .id = SND_AUDIOCODEC_BESPOKE,
                .desc = {
@@ -362,8 +361,26 @@ static const struct wm_adsp_fw_caps ez2control_caps[] = {
                        .num_sample_rates = 1,
                        .formats = SNDRV_PCM_FMTBIT_S16_LE,
                },
-               .num_regions = ARRAY_SIZE(ez2control_regions),
-               .region_defs = ez2control_regions,
+               .num_regions = ARRAY_SIZE(default_regions),
+               .region_defs = default_regions,
+       },
+};
+
+static const struct wm_adsp_fw_caps trace_caps[] = {
+       {
+               .id = SND_AUDIOCODEC_BESPOKE,
+               .desc = {
+                       .max_ch = 8,
+                       .sample_rates = {
+                               4000, 8000, 11025, 12000, 16000, 22050,
+                               24000, 32000, 44100, 48000, 64000, 88200,
+                               96000, 176400, 192000
+                       },
+                       .num_sample_rates = 15,
+                       .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               },
+               .num_regions = ARRAY_SIZE(default_regions),
+               .region_defs = default_regions,
        },
 };
 
@@ -382,11 +399,16 @@ static const struct {
        [WM_ADSP_FW_CTRL] =     {
                .file = "ctrl",
                .compr_direction = SND_COMPRESS_CAPTURE,
-               .num_caps = ARRAY_SIZE(ez2control_caps),
-               .caps = ez2control_caps,
+               .num_caps = ARRAY_SIZE(ctrl_caps),
+               .caps = ctrl_caps,
        },
        [WM_ADSP_FW_ASR] =      { .file = "asr" },
-       [WM_ADSP_FW_TRACE] =    { .file = "trace" },
+       [WM_ADSP_FW_TRACE] =    {
+               .file = "trace",
+               .compr_direction = SND_COMPRESS_CAPTURE,
+               .num_caps = ARRAY_SIZE(trace_caps),
+               .caps = trace_caps,
+       },
        [WM_ADSP_FW_SPK_PROT] = { .file = "spk-prot" },
        [WM_ADSP_FW_MISC] =     { .file = "misc" },
 };
@@ -719,19 +741,19 @@ static int wm_coeff_write_control(struct wm_coeff_ctl *ctl,
        reg = ctl->alg_region.base + ctl->offset;
        reg = wm_adsp_region_to_reg(mem, reg);
 
-       scratch = kmemdup(buf, ctl->len, GFP_KERNEL | GFP_DMA);
+       scratch = kmemdup(buf, len, GFP_KERNEL | GFP_DMA);
        if (!scratch)
                return -ENOMEM;
 
        ret = regmap_raw_write(dsp->regmap, reg, scratch,
-                              ctl->len);
+                              len);
        if (ret) {
                adsp_err(dsp, "Failed to write %zu bytes to %x: %d\n",
-                        ctl->len, reg, ret);
+                        len, reg, ret);
                kfree(scratch);
                return ret;
        }
-       adsp_dbg(dsp, "Wrote %zu bytes to %x\n", ctl->len, reg);
+       adsp_dbg(dsp, "Wrote %zu bytes to %x\n", len, reg);
 
        kfree(scratch);
 
@@ -778,20 +800,20 @@ static int wm_coeff_read_control(struct wm_coeff_ctl *ctl,
        reg = ctl->alg_region.base + ctl->offset;
        reg = wm_adsp_region_to_reg(mem, reg);
 
-       scratch = kmalloc(ctl->len, GFP_KERNEL | GFP_DMA);
+       scratch = kmalloc(len, GFP_KERNEL | GFP_DMA);
        if (!scratch)
                return -ENOMEM;
 
-       ret = regmap_raw_read(dsp->regmap, reg, scratch, ctl->len);
+       ret = regmap_raw_read(dsp->regmap, reg, scratch, len);
        if (ret) {
                adsp_err(dsp, "Failed to read %zu bytes from %x: %d\n",
-                        ctl->len, reg, ret);
+                        len, reg, ret);
                kfree(scratch);
                return ret;
        }
-       adsp_dbg(dsp, "Read %zu bytes from %x\n", ctl->len, reg);
+       adsp_dbg(dsp, "Read %zu bytes from %x\n", len, reg);
 
-       memcpy(buf, scratch, ctl->len);
+       memcpy(buf, scratch, len);
        kfree(scratch);
 
        return 0;
@@ -855,17 +877,18 @@ static int wmfw_add_ctl(struct wm_adsp *dsp, struct wm_coeff_ctl *ctl)
                        kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_READ;
                if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
                        kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
+       } else {
+               kcontrol->access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+               kcontrol->access |= SNDRV_CTL_ELEM_ACCESS_VOLATILE;
        }
 
-       ret = snd_soc_add_card_controls(dsp->card,
-                                       kcontrol, 1);
+       ret = snd_soc_add_card_controls(dsp->card, kcontrol, 1);
        if (ret < 0)
                goto err_kcontrol;
 
        kfree(kcontrol);
 
-       ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card,
-                                                 ctl->name);
+       ctl->kcontrol = snd_soc_card_get_kcontrol(dsp->card, ctl->name);
 
        return 0;
 
@@ -885,9 +908,7 @@ static int wm_coeff_init_control_caches(struct wm_adsp *dsp)
                if (ctl->flags & WMFW_CTL_FLAG_VOLATILE)
                        continue;
 
-               ret = wm_coeff_read_control(ctl,
-                                           ctl->cache,
-                                           ctl->len);
+               ret = wm_coeff_read_control(ctl, ctl->cache, ctl->len);
                if (ret < 0)
                        return ret;
        }
@@ -904,9 +925,7 @@ static int wm_coeff_sync_controls(struct wm_adsp *dsp)
                if (!ctl->enabled)
                        continue;
                if (ctl->set && !(ctl->flags & WMFW_CTL_FLAG_VOLATILE)) {
-                       ret = wm_coeff_write_control(ctl,
-                                                    ctl->cache,
-                                                    ctl->len);
+                       ret = wm_coeff_write_control(ctl, ctl->cache, ctl->len);
                        if (ret < 0)
                                return ret;
                }
@@ -1502,8 +1521,7 @@ static void *wm_adsp_read_algs(struct wm_adsp *dsp, size_t n_algs,
 
        ret = regmap_raw_read(dsp->regmap, pos, alg, len * 2);
        if (ret != 0) {
-               adsp_err(dsp, "Failed to read algorithm list: %d\n",
-                       ret);
+               adsp_err(dsp, "Failed to read algorithm list: %d\n", ret);
                kfree(alg);
                return ERR_PTR(ret);
        }
@@ -2002,8 +2020,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
                                goto err_mutex;
                        }
 
-                       val = (val & dsp->sysclk_mask)
-                               >> dsp->sysclk_shift;
+                       val = (val & dsp->sysclk_mask) >> dsp->sysclk_shift;
 
                        ret = regmap_update_bits(dsp->regmap,
                                                 dsp->base + ADSP1_CONTROL_31,
@@ -2096,8 +2113,7 @@ static int wm_adsp2_ena(struct wm_adsp *dsp)
 
        /* Wait for the RAM to start, should be near instantaneous */
        for (count = 0; count < 10; ++count) {
-               ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1,
-                                 &val);
+               ret = regmap_read(dsp->regmap, dsp->base + ADSP2_STATUS1, &val);
                if (ret != 0)
                        return ret;
 
@@ -2123,30 +2139,9 @@ static void wm_adsp2_boot_work(struct work_struct *work)
                                           struct wm_adsp,
                                           boot_work);
        int ret;
-       unsigned int val;
 
        mutex_lock(&dsp->pwr_lock);
 
-       /*
-        * For simplicity set the DSP clock rate to be the
-        * SYSCLK rate rather than making it configurable.
-        */
-       ret = regmap_read(dsp->regmap, ARIZONA_SYSTEM_CLOCK_1, &val);
-       if (ret != 0) {
-               adsp_err(dsp, "Failed to read SYSCLK state: %d\n", ret);
-               goto err_mutex;
-       }
-       val = (val & ARIZONA_SYSCLK_FREQ_MASK)
-               >> ARIZONA_SYSCLK_FREQ_SHIFT;
-
-       ret = regmap_update_bits_async(dsp->regmap,
-                                      dsp->base + ADSP2_CLOCKING,
-                                      ADSP2_CLK_SEL_MASK, val);
-       if (ret != 0) {
-               adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
-               goto err_mutex;
-       }
-
        ret = wm_adsp2_ena(dsp);
        if (ret != 0)
                goto err_mutex;
@@ -2186,8 +2181,21 @@ static void wm_adsp2_boot_work(struct work_struct *work)
        mutex_unlock(&dsp->pwr_lock);
 }
 
+static void wm_adsp2_set_dspclk(struct wm_adsp *dsp, unsigned int freq)
+{
+       int ret;
+
+       ret = regmap_update_bits_async(dsp->regmap,
+                                      dsp->base + ADSP2_CLOCKING,
+                                      ADSP2_CLK_SEL_MASK,
+                                      freq << ADSP2_CLK_SEL_SHIFT);
+       if (ret != 0)
+               adsp_err(dsp, "Failed to set clock rate: %d\n", ret);
+}
+
 int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
-                  struct snd_kcontrol *kcontrol, int event)
+                        struct snd_kcontrol *kcontrol, int event,
+                        unsigned int freq)
 {
        struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct wm_adsp *dsps = snd_soc_codec_get_drvdata(codec);
@@ -2197,6 +2205,7 @@ int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
 
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
+               wm_adsp2_set_dspclk(dsp, freq);
                queue_work(system_unbound_wq, &dsp->boot_work);
                break;
        default:
@@ -2471,6 +2480,8 @@ int wm_adsp_compr_set_params(struct snd_compr_stream *stream,
        if (!compr->raw_buf)
                return -ENOMEM;
 
+       compr->sample_rate = params->codec.sample_rate;
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(wm_adsp_compr_set_params);
@@ -2810,7 +2821,6 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
        mutex_lock(&dsp->pwr_lock);
 
        if (!buf) {
-               adsp_err(dsp, "Spurious buffer IRQ\n");
                ret = -ENODEV;
                goto out;
        }
@@ -2841,7 +2851,7 @@ int wm_adsp_compr_handle_irq(struct wm_adsp *dsp)
                goto out;
        }
 
-       if (compr->stream)
+       if (compr && compr->stream)
                snd_compr_fragment_elapsed(compr->stream);
 
 out:
@@ -2911,6 +2921,7 @@ int wm_adsp_compr_pointer(struct snd_compr_stream *stream,
 
        tstamp->copied_total = compr->copied_total;
        tstamp->copied_total += buf->avail * WM_ADSP_DATA_WORD_SIZE;
+       tstamp->sampling_rate = compr->sample_rate;
 
 out:
        mutex_unlock(&dsp->pwr_lock);
index 1a928ec547411b6d38bcec2718d2dcc09d1f6cac..b61cb57e600fb6447798e5cf9684c51f641d094e 100644 (file)
@@ -80,7 +80,7 @@ struct wm_adsp {
        SND_SOC_DAPM_PGA_E(wname, SND_SOC_NOPM, num, 0, NULL, 0, \
                wm_adsp1_event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
 
-#define WM_ADSP2_E(wname, num, event_fn) \
+#define WM_ADSP2(wname, num, event_fn) \
 {      .id = snd_soc_dapm_dai_link, .name = wname " Preloader", \
        .reg = SND_SOC_NOPM, .shift = num, .event = event_fn, \
        .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD }, \
@@ -88,9 +88,6 @@ struct wm_adsp {
        .reg = SND_SOC_NOPM, .shift = num, .event = wm_adsp2_event, \
        .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD }
 
-#define WM_ADSP2(wname, num) \
-       WM_ADSP2_E(wname, num, wm_adsp2_early_event)
-
 extern const struct snd_kcontrol_new wm_adsp_fw_controls[];
 
 int wm_adsp1_init(struct wm_adsp *dsp);
@@ -100,7 +97,8 @@ int wm_adsp2_codec_remove(struct wm_adsp *dsp, struct snd_soc_codec *codec);
 int wm_adsp1_event(struct snd_soc_dapm_widget *w,
                   struct snd_kcontrol *kcontrol, int event);
 int wm_adsp2_early_event(struct snd_soc_dapm_widget *w,
-                        struct snd_kcontrol *kcontrol, int event);
+                        struct snd_kcontrol *kcontrol, int event,
+                        unsigned int freq);
 int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                   struct snd_kcontrol *kcontrol, int event);
 
index 3736d9aabc563c9b006c9c00a6abea196e3e5daf..50ca291cc225321fb82090c31a5edae523ee9b0d 100644 (file)
@@ -5,7 +5,7 @@ config SND_DAVINCI_SOC
 
 config SND_EDMA_SOC
        tristate "SoC Audio for Texas Instruments chips using eDMA"
-       depends on SOC_AM33XX || SOC_AM43XX || ARCH_DAVINCI
+       depends on TI_EDMA
        select SND_SOC_GENERIC_DMAENGINE_PCM
        help
          Say Y or M here if you want audio support for TI SoC which uses eDMA.
@@ -13,6 +13,7 @@ config SND_EDMA_SOC
          - daVinci devices
          - AM335x
          - AM437x/AM438x
+         - DRA7xx family
 
 config SND_DAVINCI_SOC_I2S
        tristate
index 2ccb8bccc9d4cc4a79c6a94abe4dfb1566872737..e1324989bd6b9a498d35698f9748f5dae14ffe07 100644 (file)
@@ -77,6 +77,7 @@ struct davinci_mcasp {
        u32 fifo_base;
        struct device *dev;
        struct snd_pcm_substream *substreams[2];
+       unsigned int dai_fmt;
 
        /* McASP specific data */
        int     tdm_slots;
@@ -398,6 +399,9 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
        bool fs_pol_rising;
        bool inv_fs = false;
 
+       if (!fmt)
+               return 0;
+
        pm_runtime_get_sync(mcasp->dev);
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_DSP_A:
@@ -529,6 +533,8 @@ static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG, FSXPOL);
                mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG, FSRPOL);
        }
+
+       mcasp->dai_fmt = fmt;
 out:
        pm_runtime_put(mcasp->dev);
        return ret;
@@ -1026,6 +1032,10 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
        int period_size = params_period_size(params);
        int ret;
 
+       ret = davinci_mcasp_set_dai_fmt(cpu_dai, mcasp->dai_fmt);
+       if (ret)
+               return ret;
+
        /*
         * If mcasp is BCLK master, and a BCLK divider was not provided by
         * the machine driver, we need to calculate the ratio.
@@ -1517,6 +1527,8 @@ static int mcasp_reparent_fck(struct platform_device *pdev)
        if (!parent_name)
                return 0;
 
+       dev_warn(&pdev->dev, "Update the bindings to use assigned-clocks!\n");
+
        gfclk = clk_get(&pdev->dev, "fck");
        if (IS_ERR(gfclk)) {
                dev_err(&pdev->dev, "failed to get fck\n");
index 14dfdee05fd5ae842bebdd0092e47bd4bc88f605..35aabf9dc503b8f4ac78bba4df86343585aaf8fb 100644 (file)
@@ -292,8 +292,8 @@ config SND_SOC_FSL_ASOC_CARD
        select SND_SOC_FSL_SSI
        help
         ALSA SoC Audio support with ASRC feature for Freescale SoCs that have
-        ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888
-        and SGTL5000.
+        ESAI/SAI/SSI and connect with external CODECs such as WM8962, CS42888,
+        CS4271, CS4272 and SGTL5000.
         Say Y if you want to add support for Freescale Generic ASoC Sound Card.
 
 endif # SND_IMX_SOC
index 562b3bd22d9ac1f86de0a7d08fe64d8145445db8..dffd549a0e2abdc05d0b218669d34ec13f136ee6 100644 (file)
@@ -28,6 +28,8 @@
 #include "../codecs/wm8962.h"
 #include "../codecs/wm8960.h"
 
+#define CS427x_SYSCLK_MCLK 0
+
 #define RX 0
 #define TX 1
 
@@ -99,19 +101,26 @@ struct fsl_asoc_card_priv {
 /**
  * This dapm route map exsits for DPCM link only.
  * The other routes shall go through Device Tree.
+ *
+ * Note: keep all ASRC routes in the second half
+ *      to drop them easily for non-ASRC cases.
  */
 static const struct snd_soc_dapm_route audio_map[] = {
-       {"CPU-Playback",  NULL, "ASRC-Playback"},
+       /* 1st half -- Normal DAPM routes */
        {"Playback",  NULL, "CPU-Playback"},
-       {"ASRC-Capture",  NULL, "CPU-Capture"},
        {"CPU-Capture",  NULL, "Capture"},
+       /* 2nd half -- ASRC DAPM routes */
+       {"CPU-Playback",  NULL, "ASRC-Playback"},
+       {"ASRC-Capture",  NULL, "CPU-Capture"},
 };
 
 static const struct snd_soc_dapm_route audio_map_ac97[] = {
-       {"AC97 Playback",  NULL, "ASRC-Playback"},
+       /* 1st half -- Normal DAPM routes */
        {"Playback",  NULL, "AC97 Playback"},
-       {"ASRC-Capture",  NULL, "AC97 Capture"},
        {"AC97 Capture",  NULL, "Capture"},
+       /* 2nd half -- ASRC DAPM routes */
+       {"AC97 Playback",  NULL, "ASRC-Playback"},
+       {"ASRC-Capture",  NULL, "AC97 Capture"},
 };
 
 /* Add all possible widgets into here without being redundant */
@@ -528,6 +537,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
                priv->cpu_priv.sysclk_dir[RX] = SND_SOC_CLOCK_OUT;
                priv->cpu_priv.slot_width = 32;
                priv->dai_fmt |= SND_SOC_DAIFMT_CBS_CFS;
+       } else if (of_device_is_compatible(np, "fsl,imx-audio-cs427x")) {
+               codec_dai_name = "cs4271-hifi";
+               priv->codec_priv.mclk_id = CS427x_SYSCLK_MCLK;
+               priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
        } else if (of_device_is_compatible(np, "fsl,imx-audio-sgtl5000")) {
                codec_dai_name = "sgtl5000";
                priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
@@ -593,6 +606,10 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
        priv->card.dapm_widgets = fsl_asoc_card_dapm_widgets;
        priv->card.num_dapm_widgets = ARRAY_SIZE(fsl_asoc_card_dapm_widgets);
 
+       /* Drop the second half of DAPM routes -- ASRC */
+       if (!asrc_pdev)
+               priv->card.num_dapm_routes /= 2;
+
        memcpy(priv->dai_link, fsl_asoc_card_dai,
               sizeof(struct snd_soc_dai_link) * ARRAY_SIZE(priv->dai_link));
 
@@ -681,6 +698,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
 static const struct of_device_id fsl_asoc_card_dt_ids[] = {
        { .compatible = "fsl,imx-audio-ac97", },
        { .compatible = "fsl,imx-audio-cs42888", },
+       { .compatible = "fsl,imx-audio-cs427x", },
        { .compatible = "fsl,imx-audio-sgtl5000", },
        { .compatible = "fsl,imx-audio-wm8962", },
        { .compatible = "fsl,imx-audio-wm8960", },
index fef264d27fd3378d726a77209647ccc8098ec6a0..0754df771e3b4c8a945c29e8c3c99f5c23403719 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/of_address.h>
 #include <linux/regmap.h>
 #include <linux/slab.h>
+#include <linux/time.h>
 #include <sound/core.h>
 #include <sound/dmaengine_pcm.h>
 #include <sound/pcm_params.h>
@@ -919,7 +920,7 @@ static int fsl_sai_resume(struct device *dev)
        regcache_cache_only(sai->regmap, false);
        regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR);
        regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR);
-       msleep(1);
+       usleep_range(1000, 2000);
        regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
        regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
        return regcache_sync(sai->regmap);
index 40dfd8a3648408a2cc76bb6e4c4c3872c8e28ac3..ed8de1035cda159d0d186f2cded0fb7a97fbceb4 100644 (file)
@@ -112,20 +112,6 @@ struct fsl_ssi_rxtx_reg_val {
        struct fsl_ssi_reg_val tx;
 };
 
-static const struct reg_default fsl_ssi_reg_defaults[] = {
-       {CCSR_SSI_SCR,     0x00000000},
-       {CCSR_SSI_SIER,    0x00003003},
-       {CCSR_SSI_STCR,    0x00000200},
-       {CCSR_SSI_SRCR,    0x00000200},
-       {CCSR_SSI_STCCR,   0x00040000},
-       {CCSR_SSI_SRCCR,   0x00040000},
-       {CCSR_SSI_SACNT,   0x00000000},
-       {CCSR_SSI_STMSK,   0x00000000},
-       {CCSR_SSI_SRMSK,   0x00000000},
-       {CCSR_SSI_SACCEN,  0x00000000},
-       {CCSR_SSI_SACCDIS, 0x00000000},
-};
-
 static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
@@ -190,8 +176,7 @@ static const struct regmap_config fsl_ssi_regconfig = {
        .val_bits = 32,
        .reg_stride = 4,
        .val_format_endian = REGMAP_ENDIAN_NATIVE,
-       .reg_defaults = fsl_ssi_reg_defaults,
-       .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults),
+       .num_reg_defaults_raw = CCSR_SSI_SACCDIS / sizeof(uint32_t) + 1,
        .readable_reg = fsl_ssi_readable_reg,
        .volatile_reg = fsl_ssi_volatile_reg,
        .precious_reg = fsl_ssi_precious_reg,
@@ -201,6 +186,7 @@ static const struct regmap_config fsl_ssi_regconfig = {
 
 struct fsl_ssi_soc_data {
        bool imx;
+       bool imx21regs; /* imx21-class SSI - no SACC{ST,EN,DIS} regs */
        bool offline_config;
        u32 sisr_write_mask;
 };
@@ -303,6 +289,7 @@ static struct fsl_ssi_soc_data fsl_ssi_mpc8610 = {
 
 static struct fsl_ssi_soc_data fsl_ssi_imx21 = {
        .imx = true,
+       .imx21regs = true,
        .offline_config = true,
        .sisr_write_mask = 0,
 };
@@ -586,8 +573,12 @@ static void fsl_ssi_setup_ac97(struct fsl_ssi_private *ssi_private)
         */
        regmap_write(regs, CCSR_SSI_SACNT,
                        CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV);
-       regmap_write(regs, CCSR_SSI_SACCDIS, 0xff);
-       regmap_write(regs, CCSR_SSI_SACCEN, 0x300);
+
+       /* no SACC{ST,EN,DIS} regs on imx21-class SSI */
+       if (!ssi_private->soc->imx21regs) {
+               regmap_write(regs, CCSR_SSI_SACCDIS, 0xff);
+               regmap_write(regs, CCSR_SSI_SACCEN, 0x300);
+       }
 
        /*
         * Enable SSI, Transmit and Receive. AC97 has to communicate with the
@@ -1397,6 +1388,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        struct resource *res;
        void __iomem *iomem;
        char name[64];
+       struct regmap_config regconfig = fsl_ssi_regconfig;
 
        of_id = of_match_device(fsl_ssi_ids, &pdev->dev);
        if (!of_id || !of_id->data)
@@ -1444,15 +1436,25 @@ static int fsl_ssi_probe(struct platform_device *pdev)
                return PTR_ERR(iomem);
        ssi_private->ssi_phys = res->start;
 
+       if (ssi_private->soc->imx21regs) {
+               /*
+                * According to datasheet imx21-class SSI
+                * don't have SACC{ST,EN,DIS} regs.
+                */
+               regconfig.max_register = CCSR_SSI_SRMSK;
+               regconfig.num_reg_defaults_raw =
+                       CCSR_SSI_SRMSK / sizeof(uint32_t) + 1;
+       }
+
        ret = of_property_match_string(np, "clock-names", "ipg");
        if (ret < 0) {
                ssi_private->has_ipg_clk_name = false;
                ssi_private->regs = devm_regmap_init_mmio(&pdev->dev, iomem,
-                       &fsl_ssi_regconfig);
+                       &regconfig);
        } else {
                ssi_private->has_ipg_clk_name = true;
                ssi_private->regs = devm_regmap_init_mmio_clk(&pdev->dev,
-                       "ipg", iomem, &fsl_ssi_regconfig);
+                       "ipg", iomem, &regconfig);
        }
        if (IS_ERR(ssi_private->regs)) {
                dev_err(&pdev->dev, "Failed to init register map\n");
index 0bab76051fd830dd0bc0a3d993341ad8cd759afe..243700cc29e618417d2e3561f6c554031229d531 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/of_device.h>
 #include <linux/of_platform.h>
 #include <linux/delay.h>
+#include <linux/time.h>
 
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -127,7 +128,7 @@ static void psc_ac97_cold_reset(struct snd_ac97 *ac97)
 
        mutex_unlock(&psc_dma->mutex);
 
-       msleep(1);
+       usleep_range(1000, 2000);
        psc_ac97_warm_reset(ac97);
 }
 
index 7d7c872c280dbd62b0b1b66209e1e1cfe75791ee..b3e6c230045792555fcb7f5a6cab38edd65266d4 100644 (file)
@@ -163,6 +163,7 @@ config SND_SOC_INTEL_SKYLAKE
        tristate
        select SND_HDA_EXT_CORE
        select SND_SOC_TOPOLOGY
+       select SND_HDA_I915
        select SND_SOC_INTEL_SST
 
 config SND_SOC_INTEL_SKL_RT286_MACH
@@ -172,6 +173,7 @@ config SND_SOC_INTEL_SKL_RT286_MACH
        select SND_SOC_INTEL_SKYLAKE
        select SND_SOC_RT286
        select SND_SOC_DMIC
+       select SND_SOC_HDAC_HDMI
        help
           This adds support for ASoC machine driver for Skylake platforms
           with RT286 I2S audio codec.
@@ -186,6 +188,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_SSM4567_MACH
        select SND_SOC_NAU8825
        select SND_SOC_SSM4567
        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 NAU88L25 + SSM4567.
@@ -200,6 +203,7 @@ config SND_SOC_INTEL_SKL_NAU88L25_MAX98357A_MACH
        select SND_SOC_NAU8825
        select SND_SOC_MAX98357A
        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 NAU88L25 + MAX98357A.
index 4fce03fc1870b55ba0a44c4999e266ed19ae7bbf..3bc4b63b2f9daaefe7d135dd2100e1ea94f479b4 100644 (file)
@@ -342,6 +342,10 @@ static struct sst_acpi_mach sst_acpi_chv[] = {
                                                &chv_platform_data },
        {"193C9890", "cht-bsw-max98090", "intel/fw_sst_22a8.bin", "cht-bsw", NULL,
                                                &chv_platform_data },
+       /* some CHT-T platforms rely on RT5640, use Baytrail machine driver */
+       {"10EC5640", "bytcr_rt5640", "intel/fw_sst_22a8.bin", "bytcr_rt5640", NULL,
+                                               &chv_platform_data },
+
        {},
 };
 
index 3dc7358828b374a6a4f5e8c5f45f588a1d1daaa5..8afa6fe7b0b0023e7877717bad8bbbcfbcce2ec4 100644 (file)
@@ -318,7 +318,6 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
        union ipc_header_high msg_high;
        u32 msg_low;
        struct ipc_dsp_hdr *dsp_hdr;
-       unsigned int cmd_id;
 
        msg_high = msg->mrfld_header.p.header_high;
        msg_low = msg->mrfld_header.p.header_low_payload;
@@ -357,7 +356,6 @@ void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
                        return;
                /* Copy command id so that we can use to put sst to reset */
                dsp_hdr = (struct ipc_dsp_hdr *)data;
-               cmd_id = dsp_hdr->cmd_id;
                dev_dbg(sst_drv_ctx->dev, "cmd_id %d\n", dsp_hdr->cmd_id);
                if (sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
                                msg_high.part.drv_id,
index 9a1752df45a9feacf7687fdd65d8a276c7c9d9ab..032a2e753f0bfbdbee9814af8dd173bf8be51cf4 100644 (file)
 #include "../atom/sst-atom-controls.h"
 #include "../common/sst-acpi.h"
 
+enum {
+       BYT_RT5640_DMIC1_MAP,
+       BYT_RT5640_DMIC2_MAP,
+       BYT_RT5640_IN1_MAP,
+};
+
+#define BYT_RT5640_MAP(quirk)  ((quirk) & 0xff)
+#define BYT_RT5640_DMIC_EN     BIT(16)
+
+static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
+                                       BYT_RT5640_DMIC_EN;
+
 static const struct snd_soc_dapm_widget byt_rt5640_widgets[] = {
        SND_SOC_DAPM_HP("Headphone", NULL),
        SND_SOC_DAPM_MIC("Headset Mic", NULL),
@@ -70,18 +82,6 @@ static const struct snd_soc_dapm_route byt_rt5640_intmic_in1_map[] = {
        {"IN1P", NULL, "Internal Mic"},
 };
 
-enum {
-       BYT_RT5640_DMIC1_MAP,
-       BYT_RT5640_DMIC2_MAP,
-       BYT_RT5640_IN1_MAP,
-};
-
-#define BYT_RT5640_MAP(quirk)  ((quirk) & 0xff)
-#define BYT_RT5640_DMIC_EN     BIT(16)
-
-static unsigned long byt_rt5640_quirk = BYT_RT5640_DMIC1_MAP |
-                                       BYT_RT5640_DMIC_EN;
-
 static const struct snd_kcontrol_new byt_rt5640_controls[] = {
        SOC_DAPM_PIN_SWITCH("Headphone"),
        SOC_DAPM_PIN_SWITCH("Headset Mic"),
@@ -174,7 +174,6 @@ static int byt_rt5640_init(struct snd_soc_pcm_runtime *runtime)
                return ret;
        }
 
-       dmi_check_system(byt_rt5640_quirk_table);
        switch (BYT_RT5640_MAP(byt_rt5640_quirk)) {
        case BYT_RT5640_IN1_MAP:
                custom_map = byt_rt5640_intmic_in1_map;
@@ -341,15 +340,34 @@ static int snd_byt_rt5640_mc_probe(struct platform_device *pdev)
 {
        int ret_val = 0;
        struct sst_acpi_mach *mach;
+       const char *i2c_name = NULL;
+       int i;
+       int dai_index;
 
        /* register the soc card */
        byt_rt5640_card.dev = &pdev->dev;
        mach = byt_rt5640_card.dev->platform_data;
 
+       /* fix index of codec dai */
+       dai_index = MERR_DPCM_COMPR + 1;
+       for (i = 0; i < ARRAY_SIZE(byt_rt5640_dais); i++) {
+               if (!strcmp(byt_rt5640_dais[i].codec_name, "i2c-10EC5640:00")) {
+                       dai_index = i;
+                       break;
+               }
+       }
+
        /* fixup codec name based on HID */
-       snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name),
-                "%s%s%s", "i2c-", mach->id, ":00");
-       byt_rt5640_dais[MERR_DPCM_COMPR+1].codec_name = byt_rt5640_codec_name;
+       i2c_name = sst_acpi_find_name_from_hid(mach->id);
+       if (i2c_name != NULL) {
+               snprintf(byt_rt5640_codec_name, sizeof(byt_rt5640_codec_name),
+                       "%s%s", "i2c-", i2c_name);
+
+               byt_rt5640_dais[dai_index].codec_name = byt_rt5640_codec_name;
+       }
+
+       /* check quirks before creating card */
+       dmi_check_system(byt_rt5640_quirk_table);
 
        ret_val = devm_snd_soc_register_card(&pdev->dev, &byt_rt5640_card);
 
index 90588d6e64fc7869a4a4a210b0f188a01e7907b8..e609f089593a935984c101cb6e78fe7a0966e314 100644 (file)
@@ -287,33 +287,20 @@ static struct snd_soc_card snd_soc_card_cht = {
        .num_controls = ARRAY_SIZE(cht_mc_controls),
 };
 
-static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level,
-                                               void *context, void **ret)
-{
-       *(bool *)context = true;
-       return AE_OK;
-}
-
 static int snd_cht_mc_probe(struct platform_device *pdev)
 {
        int ret_val = 0;
-       bool found = false;
        struct cht_mc_private *drv;
 
        drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
        if (!drv)
                return -ENOMEM;
 
-       if (ACPI_SUCCESS(acpi_get_devices(
-                                       "104C227E",
-                                       snd_acpi_codec_match,
-                                       &found, NULL)) && found) {
-               drv->ts3a227e_present = true;
-       } else {
+       drv->ts3a227e_present = acpi_dev_present("104C227E");
+       if (!drv->ts3a227e_present) {
                /* no need probe TI jack detection chip */
                snd_soc_card_cht.aux_dev = NULL;
                snd_soc_card_cht.num_aux_devs = 0;
-               drv->ts3a227e_present = false;
        }
 
        /* register the soc card */
index a7b96a9a4e0ecb57876413c848cfa91359d42a92..2a6f80843bc9656b8303a4b9f1d67e4f9b49df45 100644 (file)
@@ -147,6 +147,17 @@ static const struct snd_kcontrol_new cht_mc_controls[] = {
        SOC_DAPM_PIN_SWITCH("Ext Spk"),
 };
 
+static struct snd_soc_jack_pin cht_bsw_jack_pins[] = {
+       {
+               .pin    = "Headphone",
+               .mask   = SND_JACK_HEADPHONE,
+       },
+       {
+               .pin    = "Headset Mic",
+               .mask   = SND_JACK_MICROPHONE,
+       },
+};
+
 static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *params)
 {
@@ -202,9 +213,9 @@ static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
        else
                jack_type = SND_JACK_HEADPHONE | SND_JACK_MICROPHONE;
 
-       ret = snd_soc_card_jack_new(runtime->card, "Headset Jack",
+       ret = snd_soc_card_jack_new(runtime->card, "Headset",
                                    jack_type, &ctx->jack,
-                                   NULL, 0);
+                                   cht_bsw_jack_pins, ARRAY_SIZE(cht_bsw_jack_pins));
        if (ret) {
                dev_err(runtime->dev, "Headset jack creation failed %d\n", ret);
                return ret;
@@ -333,20 +344,12 @@ static struct cht_acpi_card snd_soc_cards[] = {
        {"10EC5650", CODEC_TYPE_RT5650, &snd_soc_card_chtrt5650},
 };
 
-static acpi_status snd_acpi_codec_match(acpi_handle handle, u32 level,
-                                      void *context, void **ret)
-{
-       *(bool *)context = true;
-       return AE_OK;
-}
-
 static int snd_cht_mc_probe(struct platform_device *pdev)
 {
        int ret_val = 0;
        int i;
        struct cht_mc_private *drv;
        struct snd_soc_card *card = snd_soc_cards[0].soc_card;
-       bool found = false;
        char codec_name[16];
 
        drv = devm_kzalloc(&pdev->dev, sizeof(*drv), GFP_ATOMIC);
@@ -354,10 +357,7 @@ static int snd_cht_mc_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        for (i = 0; i < ARRAY_SIZE(snd_soc_cards); i++) {
-               if (ACPI_SUCCESS(acpi_get_devices(
-                                               snd_soc_cards[i].codec_id,
-                                               snd_acpi_codec_match,
-                                               &found, NULL)) && found) {
+               if (acpi_dev_present(snd_soc_cards[i].codec_id)) {
                        dev_dbg(&pdev->dev,
                                "found codec %s\n", snd_soc_cards[i].codec_id);
                        card = snd_soc_cards[i].soc_card;
index ab7da9c304b2049ceec9c06eb53ce25e1a809382..72176b79a18d6c61c02afabdcfab6bf5b3f4f932 100644 (file)
@@ -22,6 +22,7 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include "../../codecs/nau8825.h"
+#include "../../codecs/hdac_hdmi.h"
 
 #define SKL_NUVOTON_CODEC_DAI  "nau8825-hifi"
 #define SKL_MAXIM_CODEC_DAI "HiFi"
 static struct snd_soc_jack skylake_headset;
 static struct snd_soc_card skylake_audio_card;
 
+enum {
+       SKL_DPCM_AUDIO_PB = 0,
+       SKL_DPCM_AUDIO_CP,
+       SKL_DPCM_AUDIO_REF_CP,
+       SKL_DPCM_AUDIO_DMIC_CP,
+       SKL_DPCM_AUDIO_HDMI1_PB,
+       SKL_DPCM_AUDIO_HDMI2_PB,
+       SKL_DPCM_AUDIO_HDMI3_PB,
+};
+
 static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card)
 {
        struct snd_soc_pcm_runtime *rtd;
@@ -87,7 +98,6 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {
        SND_SOC_DAPM_MIC("Headset Mic", NULL),
        SND_SOC_DAPM_SPK("Spk", NULL),
        SND_SOC_DAPM_MIC("SoC DMIC", NULL),
-       SND_SOC_DAPM_SINK("WoV Sink"),
        SND_SOC_DAPM_SPK("DP", NULL),
        SND_SOC_DAPM_SPK("HDMI", NULL),
        SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
@@ -107,7 +117,6 @@ static const struct snd_soc_dapm_route skylake_map[] = {
        { "MIC", NULL, "Headset Mic" },
        { "DMic", NULL, "SoC DMIC" },
 
-       {"WoV Sink", NULL, "hwd_in sink"},
        {"HDMI", NULL, "hif5 Output"},
        {"DP", NULL, "hif6 Output"},
 
@@ -124,8 +133,14 @@ static const struct snd_soc_dapm_route skylake_map[] = {
        /* DMIC */
        { "dmic01_hifi", NULL, "DMIC01 Rx" },
        { "DMIC01 Rx", NULL, "DMIC AIF" },
-       { "hifi1", NULL, "iDisp Tx"},
-       { "iDisp Tx", NULL, "iDisp_out"},
+
+       { "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"},
+
        { "Headphone Jack", NULL, "Platform Clock" },
        { "Headset Mic", NULL, "Platform Clock" },
 };
@@ -171,11 +186,31 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
        nau8825_enable_jack_detect(codec, &skylake_headset);
 
        snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
-       snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
 
        return ret;
 }
 
+static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *dai = rtd->codec_dai;
+
+       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
+}
+
+static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *dai = rtd->codec_dai;
+
+       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
+}
+
+static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *dai = rtd->codec_dai;
+
+       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
+}
+
 static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_dapm_context *dapm;
@@ -318,7 +353,7 @@ static struct snd_soc_ops skylaye_refcap_ops = {
 /* skylake digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link skylake_dais[] = {
        /* Front End DAI links */
-       {
+       [SKL_DPCM_AUDIO_PB] = {
                .name = "Skl Audio Port",
                .stream_name = "Audio",
                .cpu_dai_name = "System Pin",
@@ -333,7 +368,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
                .dpcm_playback = 1,
                .ops = &skylake_nau8825_fe_ops,
        },
-       {
+       [SKL_DPCM_AUDIO_CP] = {
                .name = "Skl Audio Capture Port",
                .stream_name = "Audio Record",
                .cpu_dai_name = "System Pin",
@@ -347,7 +382,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
                .dpcm_capture = 1,
                .ops = &skylake_nau8825_fe_ops,
        },
-       {
+       [SKL_DPCM_AUDIO_REF_CP] = {
                .name = "Skl Audio Reference cap",
                .stream_name = "Wake on Voice",
                .cpu_dai_name = "Reference Pin",
@@ -361,7 +396,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
                .dynamic = 1,
                .ops = &skylaye_refcap_ops,
        },
-       {
+       [SKL_DPCM_AUDIO_DMIC_CP] = {
                .name = "Skl Audio DMIC cap",
                .stream_name = "dmiccap",
                .cpu_dai_name = "DMIC Pin",
@@ -374,15 +409,45 @@ static struct snd_soc_dai_link skylake_dais[] = {
                .dynamic = 1,
                .ops = &skylake_dmic_ops,
        },
-       {
-               .name = "Skl HDMI Port",
-               .stream_name = "Hdmi",
-               .cpu_dai_name = "HDMI Pin",
+       [SKL_DPCM_AUDIO_HDMI1_PB] = {
+               .name = "Skl 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,
+       },
+       [SKL_DPCM_AUDIO_HDMI2_PB] = {
+               .name = "Skl 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,
+       },
+       [SKL_DPCM_AUDIO_HDMI3_PB] = {
+               .name = "Skl 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,
        },
@@ -407,7 +472,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        {
                /* SSP1 - Codec */
                .name = "SSP1-Codec",
-               .be_id = 0,
+               .be_id = 1,
                .cpu_dai_name = "SSP1 Pin",
                .platform_name = "0000:00:1f.3",
                .no_pcm = 1,
@@ -424,7 +489,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
        {
                .name = "dmic01",
-               .be_id = 1,
+               .be_id = 2,
                .cpu_dai_name = "DMIC01 Pin",
                .codec_name = "dmic-codec",
                .codec_dai_name = "dmic-hifi",
@@ -435,13 +500,36 @@ static struct snd_soc_dai_link skylake_dais[] = {
                .no_pcm = 1,
        },
        {
-               .name = "iDisp",
+               .name = "iDisp1",
                .be_id = 3,
-               .cpu_dai_name = "iDisp Pin",
+               .cpu_dai_name = "iDisp1 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi1",
                .platform_name = "0000:00:1f.3",
                .dpcm_playback = 1,
+               .init = skylake_hdmi1_init,
+               .no_pcm = 1,
+       },
+       {
+               .name = "iDisp2",
+               .be_id = 4,
+               .cpu_dai_name = "iDisp2 Pin",
+               .codec_name = "ehdaudio0D2",
+               .codec_dai_name = "intel-hdmi-hifi2",
+               .platform_name = "0000:00:1f.3",
+               .init = skylake_hdmi2_init,
+               .dpcm_playback = 1,
+               .no_pcm = 1,
+       },
+       {
+               .name = "iDisp3",
+               .be_id = 5,
+               .cpu_dai_name = "iDisp3 Pin",
+               .codec_name = "ehdaudio0D2",
+               .codec_dai_name = "intel-hdmi-hifi3",
+               .platform_name = "0000:00:1f.3",
+               .init = skylake_hdmi3_init,
+               .dpcm_playback = 1,
                .no_pcm = 1,
        },
 };
index c071812f31e5a01ecd899f02024f60a428567e10..5f1ca99ae9b0d03dc51045af7a3489ee3a20b830 100644 (file)
@@ -26,6 +26,7 @@
 #include <sound/jack.h>
 #include <sound/pcm_params.h>
 #include "../../codecs/nau8825.h"
+#include "../../codecs/hdac_hdmi.h"
 
 #define SKL_NUVOTON_CODEC_DAI  "nau8825-hifi"
 #define SKL_SSM_CODEC_DAI      "ssm4567-hifi"
 static struct snd_soc_jack skylake_headset;
 static struct snd_soc_card skylake_audio_card;
 
+enum {
+       SKL_DPCM_AUDIO_PB = 0,
+       SKL_DPCM_AUDIO_CP,
+       SKL_DPCM_AUDIO_REF_CP,
+       SKL_DPCM_AUDIO_DMIC_CP,
+       SKL_DPCM_AUDIO_HDMI1_PB,
+       SKL_DPCM_AUDIO_HDMI2_PB,
+       SKL_DPCM_AUDIO_HDMI3_PB,
+};
+
 static inline struct snd_soc_dai *skl_get_codec_dai(struct snd_soc_card *card)
 {
        struct snd_soc_pcm_runtime *rtd;
@@ -92,7 +103,6 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {
        SND_SOC_DAPM_SPK("Left Speaker", NULL),
        SND_SOC_DAPM_SPK("Right Speaker", NULL),
        SND_SOC_DAPM_MIC("SoC DMIC", NULL),
-       SND_SOC_DAPM_SINK("WoV Sink"),
        SND_SOC_DAPM_SPK("DP", NULL),
        SND_SOC_DAPM_SPK("HDMI", NULL),
        SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
@@ -113,8 +123,6 @@ static const struct snd_soc_dapm_route skylake_map[] = {
        {"MIC", NULL, "Headset Mic"},
        {"DMic", NULL, "SoC DMIC"},
 
-       {"WoV Sink", NULL, "hwd_in sink"},
-
        {"HDMI", NULL, "hif5 Output"},
        {"DP", NULL, "hif6 Output"},
        /* CODEC BE connections */
@@ -122,6 +130,11 @@ static const struct snd_soc_dapm_route skylake_map[] = {
        { "Right Playback", NULL, "ssp0 Tx"},
        { "ssp0 Tx", NULL, "codec0_out"},
 
+       /* IV feedback path */
+       { "codec0_lp_in", NULL, "ssp0 Rx"},
+       { "ssp0 Rx", NULL, "Left Capture Sense" },
+       { "ssp0 Rx", NULL, "Right Capture Sense" },
+
        { "Playback", NULL, "ssp1 Tx"},
        { "ssp1 Tx", NULL, "codec1_out"},
 
@@ -131,8 +144,14 @@ static const struct snd_soc_dapm_route skylake_map[] = {
        /* DMIC */
        { "dmic01_hifi", NULL, "DMIC01 Rx" },
        { "DMIC01 Rx", NULL, "DMIC AIF" },
-       { "hifi1", NULL, "iDisp Tx"},
-       { "iDisp Tx", NULL, "iDisp_out"},
+
+       { "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"},
+
        { "Headphone Jack", NULL, "Platform Clock" },
        { "Headset Mic", NULL, "Platform Clock" },
 };
@@ -197,11 +216,32 @@ static int skylake_nau8825_codec_init(struct snd_soc_pcm_runtime *rtd)
        nau8825_enable_jack_detect(codec, &skylake_headset);
 
        snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
-       snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
 
        return ret;
 }
 
+static int skylake_hdmi1_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *dai = rtd->codec_dai;
+
+       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB);
+}
+
+static int skylake_hdmi2_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *dai = rtd->codec_dai;
+
+       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI2_PB);
+}
+
+
+static int skylake_hdmi3_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *dai = rtd->codec_dai;
+
+       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI3_PB);
+}
+
 static int skylake_nau8825_fe_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_dapm_context *dapm;
@@ -362,7 +402,7 @@ static struct snd_soc_ops skylaye_refcap_ops = {
 /* skylake digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link skylake_dais[] = {
        /* Front End DAI links */
-       {
+       [SKL_DPCM_AUDIO_PB] = {
                .name = "Skl Audio Port",
                .stream_name = "Audio",
                .cpu_dai_name = "System Pin",
@@ -377,7 +417,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
                .dpcm_playback = 1,
                .ops = &skylake_nau8825_fe_ops,
        },
-       {
+       [SKL_DPCM_AUDIO_CP] = {
                .name = "Skl Audio Capture Port",
                .stream_name = "Audio Record",
                .cpu_dai_name = "System Pin",
@@ -391,7 +431,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
                .dpcm_capture = 1,
                .ops = &skylake_nau8825_fe_ops,
        },
-       {
+       [SKL_DPCM_AUDIO_REF_CP] = {
                .name = "Skl Audio Reference cap",
                .stream_name = "Wake on Voice",
                .cpu_dai_name = "Reference Pin",
@@ -405,7 +445,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
                .dynamic = 1,
                .ops = &skylaye_refcap_ops,
        },
-       {
+       [SKL_DPCM_AUDIO_DMIC_CP] = {
                .name = "Skl Audio DMIC cap",
                .stream_name = "dmiccap",
                .cpu_dai_name = "DMIC Pin",
@@ -418,13 +458,43 @@ static struct snd_soc_dai_link skylake_dais[] = {
                .dynamic = 1,
                .ops = &skylake_dmic_ops,
        },
-       {
-               .name = "Skl HDMI Port",
-               .stream_name = "Hdmi",
-               .cpu_dai_name = "HDMI Pin",
+       [SKL_DPCM_AUDIO_HDMI1_PB] = {
+               .name = "Skl 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,
+       },
+       [SKL_DPCM_AUDIO_HDMI2_PB] = {
+               .name = "Skl 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,
+       },
+       [SKL_DPCM_AUDIO_HDMI3_PB] = {
+               .name = "Skl 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,
@@ -448,11 +518,12 @@ static struct snd_soc_dai_link skylake_dais[] = {
                .ignore_pmdown_time = 1,
                .be_hw_params_fixup = skylake_ssp_fixup,
                .dpcm_playback = 1,
+               .dpcm_capture = 1,
        },
        {
                /* SSP1 - Codec */
                .name = "SSP1-Codec",
-               .be_id = 0,
+               .be_id = 1,
                .cpu_dai_name = "SSP1 Pin",
                .platform_name = "0000:00:1f.3",
                .no_pcm = 1,
@@ -469,7 +540,7 @@ static struct snd_soc_dai_link skylake_dais[] = {
        },
        {
                .name = "dmic01",
-               .be_id = 1,
+               .be_id = 2,
                .cpu_dai_name = "DMIC01 Pin",
                .codec_name = "dmic-codec",
                .codec_dai_name = "dmic-hifi",
@@ -480,13 +551,36 @@ static struct snd_soc_dai_link skylake_dais[] = {
                .no_pcm = 1,
        },
        {
-               .name = "iDisp",
+               .name = "iDisp1",
                .be_id = 3,
-               .cpu_dai_name = "iDisp Pin",
+               .cpu_dai_name = "iDisp1 Pin",
                .codec_name = "ehdaudio0D2",
                .codec_dai_name = "intel-hdmi-hifi1",
                .platform_name = "0000:00:1f.3",
                .dpcm_playback = 1,
+               .init = skylake_hdmi1_init,
+               .no_pcm = 1,
+       },
+       {
+               .name = "iDisp2",
+               .be_id = 4,
+               .cpu_dai_name = "iDisp2 Pin",
+               .codec_name = "ehdaudio0D2",
+               .codec_dai_name = "intel-hdmi-hifi2",
+               .platform_name = "0000:00:1f.3",
+               .init = skylake_hdmi2_init,
+               .dpcm_playback = 1,
+               .no_pcm = 1,
+       },
+       {
+               .name = "iDisp3",
+               .be_id = 5,
+               .cpu_dai_name = "iDisp3 Pin",
+               .codec_name = "ehdaudio0D2",
+               .codec_dai_name = "intel-hdmi-hifi3",
+               .platform_name = "0000:00:1f.3",
+               .init = skylake_hdmi3_init,
+               .dpcm_playback = 1,
                .no_pcm = 1,
        },
 };
index 2cbcbe4126611d0d1660d86279329da5bec504b1..2016397a8e755e5cdeb72b26b270c0203aaec4b9 100644 (file)
 #include <sound/jack.h>
 #include <sound/pcm_params.h>
 #include "../../codecs/rt286.h"
+#include "../../codecs/hdac_hdmi.h"
 
 static struct snd_soc_jack skylake_headset;
+
+enum {
+       SKL_DPCM_AUDIO_PB = 0,
+       SKL_DPCM_AUDIO_CP,
+       SKL_DPCM_AUDIO_REF_CP,
+       SKL_DPCM_AUDIO_DMIC_CP,
+       SKL_DPCM_AUDIO_HDMI1_PB,
+       SKL_DPCM_AUDIO_HDMI2_PB,
+       SKL_DPCM_AUDIO_HDMI3_PB,
+};
+
 /* Headset jack detection DAPM pins */
 static struct snd_soc_jack_pin skylake_headset_pins[] = {
        {
@@ -52,7 +64,9 @@ static const struct snd_soc_dapm_widget skylake_widgets[] = {
        SND_SOC_DAPM_MIC("Mic Jack", NULL),
        SND_SOC_DAPM_MIC("DMIC2", NULL),
        SND_SOC_DAPM_MIC("SoC DMIC", NULL),
-       SND_SOC_DAPM_SINK("WoV Sink"),
+       SND_SOC_DAPM_SPK("HDMI1", NULL),
+       SND_SOC_DAPM_SPK("HDMI2", NULL),
+       SND_SOC_DAPM_SPK("HDMI3", NULL),
 };
 
 static const struct snd_soc_dapm_route skylake_rt286_map[] = {
@@ -70,7 +84,9 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = {
        {"DMIC1 Pin", NULL, "DMIC2"},
        {"DMic", NULL, "SoC DMIC"},
 
-       {"WoV Sink", NULL, "hwd_in sink"},
+       {"HDMI1", NULL, "hif5 Output"},
+       {"HDMI2", NULL, "hif6 Output"},
+       {"HDMI3", NULL, "hif7 Output"},
 
        /* CODEC BE connections */
        { "AIF1 Playback", NULL, "ssp0 Tx"},
@@ -84,8 +100,12 @@ static const struct snd_soc_dapm_route skylake_rt286_map[] = {
        { "dmic01_hifi", NULL, "DMIC01 Rx" },
        { "DMIC01 Rx", NULL, "DMIC AIF" },
 
-       { "hif1", NULL, "iDisp Tx"},
-       { "iDisp Tx", NULL, "iDisp_out"},
+       { "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"},
 
 };
 
@@ -116,11 +136,17 @@ static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
        rt286_mic_detect(codec, &skylake_headset);
 
        snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "SoC DMIC");
-       snd_soc_dapm_ignore_suspend(&rtd->card->dapm, "WoV Sink");
 
        return 0;
 }
 
+static int skylake_hdmi_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *dai = rtd->codec_dai;
+
+       return hdac_hdmi_jack_init(dai, SKL_DPCM_AUDIO_HDMI1_PB + dai->id);
+}
+
 static unsigned int rates[] = {
        48000,
 };
@@ -249,7 +275,7 @@ static struct snd_soc_ops skylake_dmic_ops = {
 /* skylake digital audio interface glue - connects codec <--> CPU */
 static struct snd_soc_dai_link skylake_rt286_dais[] = {
        /* Front End DAI links */
-       {
+       [SKL_DPCM_AUDIO_PB] = {
                .name = "Skl Audio Port",
                .stream_name = "Audio",
                .cpu_dai_name = "System Pin",
@@ -266,7 +292,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
                .dpcm_playback = 1,
                .ops = &skylake_rt286_fe_ops,
        },
-       {
+       [SKL_DPCM_AUDIO_CP] = {
                .name = "Skl Audio Capture Port",
                .stream_name = "Audio Record",
                .cpu_dai_name = "System Pin",
@@ -282,7 +308,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
                .dpcm_capture = 1,
                .ops = &skylake_rt286_fe_ops,
        },
-       {
+       [SKL_DPCM_AUDIO_REF_CP] = {
                .name = "Skl Audio Reference cap",
                .stream_name = "refcap",
                .cpu_dai_name = "Reference Pin",
@@ -295,7 +321,7 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
                .nonatomic = 1,
                .dynamic = 1,
        },
-       {
+       [SKL_DPCM_AUDIO_DMIC_CP] = {
                .name = "Skl Audio DMIC cap",
                .stream_name = "dmiccap",
                .cpu_dai_name = "DMIC Pin",
@@ -308,6 +334,42 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
                .dynamic = 1,
                .ops = &skylake_dmic_ops,
        },
+       [SKL_DPCM_AUDIO_HDMI1_PB] = {
+               .name = "Skl 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,
+               .nonatomic = 1,
+               .dynamic = 1,
+       },
+       [SKL_DPCM_AUDIO_HDMI2_PB] = {
+               .name = "Skl 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,
+               .nonatomic = 1,
+               .dynamic = 1,
+       },
+       [SKL_DPCM_AUDIO_HDMI3_PB] = {
+               .name = "Skl 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",
+               .dpcm_playback = 1,
+               .init = NULL,
+               .nonatomic = 1,
+               .dynamic = 1,
+       },
 
        /* Back End DAI links */
        {
@@ -341,6 +403,39 @@ static struct snd_soc_dai_link skylake_rt286_dais[] = {
                .dpcm_capture = 1,
                .no_pcm = 1,
        },
+       {
+               .name = "iDisp1",
+               .be_id = 2,
+               .cpu_dai_name = "iDisp1 Pin",
+               .codec_name = "ehdaudio0D2",
+               .codec_dai_name = "intel-hdmi-hifi1",
+               .platform_name = "0000:00:1f.3",
+               .init = skylake_hdmi_init,
+               .dpcm_playback = 1,
+               .no_pcm = 1,
+       },
+       {
+               .name = "iDisp2",
+               .be_id = 3,
+               .cpu_dai_name = "iDisp2 Pin",
+               .codec_name = "ehdaudio0D2",
+               .codec_dai_name = "intel-hdmi-hifi2",
+               .platform_name = "0000:00:1f.3",
+               .init = skylake_hdmi_init,
+               .dpcm_playback = 1,
+               .no_pcm = 1,
+       },
+       {
+               .name = "iDisp3",
+               .be_id = 4,
+               .cpu_dai_name = "iDisp3 Pin",
+               .codec_name = "ehdaudio0D2",
+               .codec_dai_name = "intel-hdmi-hifi3",
+               .platform_name = "0000:00:1f.3",
+               .init = skylake_hdmi_init,
+               .dpcm_playback = 1,
+               .no_pcm = 1,
+       },
 };
 
 /* skylake audio machine driver for SPT + RT286S */
index 3ee3b7ab5d037b053e2f862ffbaa19c25b3f8e11..4dcfb7e5ed7099abc8436e4156e2a552c4137f18 100644 (file)
@@ -14,6 +14,9 @@
 
 #include <linux/acpi.h>
 
+/* translation fron HID to I2C name, needed for DAI codec_name */
+const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN]);
+
 /* acpi match */
 struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines);
 
index 81aa1ed6420116dce01d6a5f12fbf36fa5bb961b..97dc1ae05e69d60858f78ab1c6702dee629aaadf 100644 (file)
@@ -317,6 +317,7 @@ struct sst_dsp {
        struct skl_cl_dev cl_dev;
        u32 intr_status;
        const struct firmware *fw;
+       struct snd_dma_buffer dmab;
 };
 
 /* Size optimised DRAM/IRAM memcpy */
index 3b4539d21492484ac2ba15cec78a36f4e135aee6..789843307a4950ad503d3cabda82300489d8e8d0 100644 (file)
  * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
  * more details.
  */
-#include <linux/acpi.h>
-#include <linux/device.h>
-#include <linux/module.h>
-#include <linux/platform_device.h>
 
 #include "sst-acpi.h"
 
+static acpi_status sst_acpi_find_name(acpi_handle handle, u32 level,
+                                     void *context, void **ret)
+{
+       struct acpi_device *adev;
+       const char *name = NULL;
+
+       if (acpi_bus_get_device(handle, &adev))
+               return AE_OK;
+
+       if (adev->status.present && adev->status.functional) {
+               name = acpi_dev_name(adev);
+               *(const char **)ret = name;
+               return AE_CTRL_TERMINATE;
+       }
+
+       return AE_OK;
+}
+
+const char *sst_acpi_find_name_from_hid(const u8 hid[ACPI_ID_LEN])
+{
+       const char *name = NULL;
+       acpi_status status;
+
+       status = acpi_get_devices(hid, sst_acpi_find_name, NULL,
+                                 (void **)&name);
+
+       if (ACPI_FAILURE(status) || name[0] == '\0')
+               return NULL;
+
+       return name;
+}
+EXPORT_SYMBOL_GPL(sst_acpi_find_name_from_hid);
+
 static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
                                       void *context, void **ret)
 {
+       unsigned long long sta;
+       acpi_status status;
+
        *(bool *)context = true;
+       status = acpi_evaluate_integer(handle, "_STA", NULL, &sta);
+       if (ACPI_FAILURE(status) || !(sta & ACPI_STA_DEVICE_PRESENT))
+               *(bool *)context = false;
+
        return AE_OK;
 }
 
@@ -37,7 +73,6 @@ struct sst_acpi_mach *sst_acpi_find_machine(struct sst_acpi_mach *machines)
                                                  sst_acpi_mach_match,
                                                  &found, NULL)) && found)
                        return mach;
-
        return NULL;
 }
 EXPORT_SYMBOL_GPL(sst_acpi_find_machine);
index 4629372d7c8e0b728c9ec0467ae34208532fa48b..79c5089b85d6483efab2ffb25b2959bf8210ab5e 100644 (file)
@@ -72,17 +72,47 @@ static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
        skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask);
 }
 
+static struct skl_dsp_loader_ops skl_get_loader_ops(void)
+{
+       struct skl_dsp_loader_ops loader_ops;
+
+       memset(&loader_ops, 0, sizeof(struct skl_dsp_loader_ops));
+
+       loader_ops.alloc_dma_buf = skl_alloc_dma_buf;
+       loader_ops.free_dma_buf = skl_free_dma_buf;
+
+       return loader_ops;
+};
+
+static const struct skl_dsp_ops dsp_ops[] = {
+       {
+               .id = 0x9d70,
+               .loader_ops = skl_get_loader_ops,
+               .init = skl_sst_dsp_init,
+               .cleanup = skl_sst_dsp_cleanup
+       },
+};
+
+static int skl_get_dsp_ops(int pci_id)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(dsp_ops); i++) {
+               if (dsp_ops[i].id == pci_id)
+                       return i;
+       }
+
+       return -EINVAL;
+}
+
 int skl_init_dsp(struct skl *skl)
 {
        void __iomem *mmio_base;
        struct hdac_ext_bus *ebus = &skl->ebus;
        struct hdac_bus *bus = ebus_to_hbus(ebus);
-       int irq = bus->irq;
        struct skl_dsp_loader_ops loader_ops;
-       int ret;
-
-       loader_ops.alloc_dma_buf = skl_alloc_dma_buf;
-       loader_ops.free_dma_buf = skl_free_dma_buf;
+       int irq = bus->irq;
+       int ret, index;
 
        /* enable ppcap interrupt */
        snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
@@ -95,8 +125,14 @@ int skl_init_dsp(struct skl *skl)
                return -ENXIO;
        }
 
-       ret = skl_sst_dsp_init(bus->dev, mmio_base, irq,
+       index  = skl_get_dsp_ops(skl->pci->device);
+       if (index  < 0)
+               return -EINVAL;
+
+       loader_ops = dsp_ops[index].loader_ops();
+       ret = dsp_ops[index].init(bus->dev, mmio_base, irq,
                        skl->fw_name, loader_ops, &skl->skl_sst);
+
        if (ret < 0)
                return ret;
 
@@ -106,18 +142,26 @@ int skl_init_dsp(struct skl *skl)
        return ret;
 }
 
-void skl_free_dsp(struct skl *skl)
+int skl_free_dsp(struct skl *skl)
 {
        struct hdac_ext_bus *ebus = &skl->ebus;
        struct hdac_bus *bus = ebus_to_hbus(ebus);
-       struct skl_sst *ctx =  skl->skl_sst;
+       struct skl_sst *ctx = skl->skl_sst;
+       int index;
 
        /* disable  ppcap interrupt */
        snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, false);
 
-       skl_sst_dsp_cleanup(bus->dev, ctx);
+       index = skl_get_dsp_ops(skl->pci->device);
+       if (index  < 0)
+               return -EIO;
+
+       dsp_ops[index].cleanup(bus->dev, ctx);
+
        if (ctx->dsp->addr.lpe)
                iounmap(ctx->dsp->addr.lpe);
+
+       return 0;
 }
 
 int skl_suspend_dsp(struct skl *skl)
@@ -238,9 +282,8 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig,
  * Calculate the gatewat settings required for copier module, type of
  * gateway and index of gateway to use
  */
-static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
-                       struct skl_module_cfg *mconfig,
-                       struct skl_cpr_cfg *cpr_mconfig)
+static u32 skl_get_node_id(struct skl_sst *ctx,
+                       struct skl_module_cfg *mconfig)
 {
        union skl_connector_node_id node_id = {0};
        union skl_ssp_dma_node ssp_node  = {0};
@@ -289,13 +332,24 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
                break;
 
        default:
-               cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID;
+               node_id.val = 0xFFFFFFFF;
+               break;
+       }
+
+       return node_id.val;
+}
+
+static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
+                       struct skl_module_cfg *mconfig,
+                       struct skl_cpr_cfg *cpr_mconfig)
+{
+       cpr_mconfig->gtw_cfg.node_id = skl_get_node_id(ctx, mconfig);
+
+       if (cpr_mconfig->gtw_cfg.node_id == SKL_NON_GATEWAY_CPR_NODE_ID) {
                cpr_mconfig->cpr_feature_mask = 0;
                return;
        }
 
-       cpr_mconfig->gtw_cfg.node_id = node_id.val;
-
        if (SKL_CONN_SOURCE == mconfig->hw_conn_type)
                cpr_mconfig->gtw_cfg.dma_buffer_size = 2 * mconfig->obs;
        else
@@ -307,6 +361,46 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
        skl_copy_copier_caps(mconfig, cpr_mconfig);
 }
 
+#define DMA_CONTROL_ID 5
+
+int skl_dsp_set_dma_control(struct skl_sst *ctx, struct skl_module_cfg *mconfig)
+{
+       struct skl_dma_control *dma_ctrl;
+       struct skl_i2s_config_blob config_blob;
+       struct skl_ipc_large_config_msg msg = {0};
+       int err = 0;
+
+
+       /*
+        * if blob size is same as capablity size, then no dma control
+        * present so return
+        */
+       if (mconfig->formats_config.caps_size == sizeof(config_blob))
+               return 0;
+
+       msg.large_param_id = DMA_CONTROL_ID;
+       msg.param_data_size = sizeof(struct skl_dma_control) +
+                               mconfig->formats_config.caps_size;
+
+       dma_ctrl = kzalloc(msg.param_data_size, GFP_KERNEL);
+       if (dma_ctrl == NULL)
+               return -ENOMEM;
+
+       dma_ctrl->node_id = skl_get_node_id(ctx, mconfig);
+
+       /* size in dwords */
+       dma_ctrl->config_length = sizeof(config_blob) / 4;
+
+       memcpy(dma_ctrl->config_data, mconfig->formats_config.caps,
+                               mconfig->formats_config.caps_size);
+
+       err = skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)dma_ctrl);
+
+       kfree(dma_ctrl);
+
+       return err;
+}
+
 static void skl_setup_out_format(struct skl_sst *ctx,
                        struct skl_module_cfg *mconfig,
                        struct skl_audio_data_format *out_fmt)
index 6e4b21cdb1bd9bff4b6606103bd41629c0ab2b7f..14d1916ea9f843af75622cd50c93dcf5d426fd68 100644 (file)
@@ -145,3 +145,37 @@ struct nhlt_specific_cfg
 
        return NULL;
 }
+
+static void skl_nhlt_trim_space(struct skl *skl)
+{
+       char *s = skl->tplg_name;
+       int cnt;
+       int i;
+
+       cnt = 0;
+       for (i = 0; s[i]; i++) {
+               if (!isspace(s[i]))
+                       s[cnt++] = s[i];
+       }
+
+       s[cnt] = '\0';
+}
+
+int skl_nhlt_update_topology_bin(struct skl *skl)
+{
+       struct nhlt_acpi_table *nhlt = (struct nhlt_acpi_table *)skl->nhlt;
+       struct hdac_bus *bus = ebus_to_hbus(&skl->ebus);
+       struct device *dev = bus->dev;
+
+       dev_dbg(dev, "oem_id %.6s, oem_table_id %8s oem_revision %d\n",
+               nhlt->header.oem_id, nhlt->header.oem_table_id,
+               nhlt->header.oem_revision);
+
+       snprintf(skl->tplg_name, sizeof(skl->tplg_name), "%x-%.6s-%.8s-%d%s",
+               skl->pci_id, nhlt->header.oem_id, nhlt->header.oem_table_id,
+               nhlt->header.oem_revision, "-tplg.bin");
+
+       skl_nhlt_trim_space(skl);
+
+       return 0;
+}
index b6e6b61d10ec22349650ee5c1db3074068a180e4..dab0900eef26c13f6f9c8ac85d5f6b6a40cdf35b 100644 (file)
@@ -206,6 +206,23 @@ static int skl_get_format(struct snd_pcm_substream *substream,
        return format_val;
 }
 
+static int skl_be_prepare(struct snd_pcm_substream *substream,
+               struct snd_soc_dai *dai)
+{
+       struct skl *skl = get_skl_ctx(dai->dev);
+       struct skl_sst *ctx = skl->skl_sst;
+       struct skl_module_cfg *mconfig;
+
+       if ((dai->playback_active > 1) || (dai->capture_active > 1))
+               return 0;
+
+       mconfig = skl_tplg_be_get_cpr_module(dai, substream->stream);
+       if (mconfig == NULL)
+               return -EINVAL;
+
+       return skl_dsp_set_dma_control(ctx, mconfig);
+}
+
 static int skl_pcm_prepare(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
@@ -458,7 +475,7 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
        struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
        struct hdac_ext_stream *link_dev;
        struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
-       struct skl_dma_params *dma_params;
+       struct hdac_ext_dma_params *dma_params;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct skl_pipe_params p_params = {0};
 
@@ -470,11 +487,9 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
        snd_soc_dai_set_dma_data(dai, substream, (void *)link_dev);
 
        /* set the stream tag in the codec dai dma params  */
-       dma_params = (struct skl_dma_params *)
-                       snd_soc_dai_get_dma_data(codec_dai, substream);
+       dma_params = snd_soc_dai_get_dma_data(codec_dai, substream);
        if (dma_params)
                dma_params->stream_tag =  hdac_stream(link_dev)->stream_tag;
-       snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params);
 
        p_params.s_fmt = snd_pcm_format_width(params_format(params));
        p_params.ch = params_channels(params);
@@ -588,6 +603,7 @@ static struct snd_soc_dai_ops skl_dmic_dai_ops = {
 
 static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {
        .hw_params = skl_be_hw_params,
+       .prepare = skl_be_prepare,
 };
 
 static struct snd_soc_dai_ops skl_link_dai_ops = {
@@ -660,6 +676,51 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
                .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
        },
 },
+{
+       .name = "HDMI1 Pin",
+       .ops = &skl_pcm_dai_ops,
+       .playback = {
+               .stream_name = "HDMI1 Playback",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+                       SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+                       SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+                       SNDRV_PCM_RATE_192000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+                       SNDRV_PCM_FMTBIT_S32_LE,
+       },
+},
+{
+       .name = "HDMI2 Pin",
+       .ops = &skl_pcm_dai_ops,
+       .playback = {
+               .stream_name = "HDMI2 Playback",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+                       SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+                       SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+                       SNDRV_PCM_RATE_192000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+                       SNDRV_PCM_FMTBIT_S32_LE,
+       },
+},
+{
+       .name = "HDMI3 Pin",
+       .ops = &skl_pcm_dai_ops,
+       .playback = {
+               .stream_name = "HDMI3 Playback",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |
+                       SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200 |
+                       SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_176400 |
+                       SNDRV_PCM_RATE_192000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE |
+                       SNDRV_PCM_FMTBIT_S32_LE,
+       },
+},
 
 /* BE CPU  Dais */
 {
@@ -699,14 +760,41 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
        },
 },
 {
-       .name = "iDisp Pin",
+       .name = "iDisp1 Pin",
        .ops = &skl_link_dai_ops,
        .playback = {
-               .stream_name = "iDisp Tx",
+               .stream_name = "iDisp1 Tx",
                .channels_min = HDA_STEREO,
                .channels_max = HDA_STEREO,
                .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S24_LE,
+       },
+},
+{
+       .name = "iDisp2 Pin",
+       .ops = &skl_link_dai_ops,
+       .playback = {
+               .stream_name = "iDisp2 Tx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
+                       SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S24_LE,
+       },
+},
+{
+       .name = "iDisp3 Pin",
+       .ops = &skl_link_dai_ops,
+       .playback = {
+               .stream_name = "iDisp3 Tx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|
+                       SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S32_LE |
+                       SNDRV_PCM_FMTBIT_S24_LE,
        },
 },
 {
@@ -863,7 +951,9 @@ static int skl_get_delay_from_lpib(struct hdac_ext_bus *ebus,
                else
                        delay += hstream->bufsize;
        }
-       delay = (hstream->bufsize == delay) ? 0 : delay;
+
+       if (hstream->bufsize == delay)
+               delay = 0;
 
        if (delay >= hstream->period_bytes) {
                dev_info(bus->dev,
index 1bfb7f63b5724d98e8acf1446364fafec01e115f..a5267e8a96e0533bd8671f595f35ce53268b0a7e 100644 (file)
@@ -34,7 +34,7 @@ void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state)
        mutex_unlock(&ctx->mutex);
 }
 
-static int skl_dsp_core_set_reset_state(struct sst_dsp  *ctx)
+static int skl_dsp_core_set_reset_state(struct sst_dsp *ctx)
 {
        int ret;
 
@@ -60,7 +60,7 @@ static int skl_dsp_core_set_reset_state(struct sst_dsp  *ctx)
        return ret;
 }
 
-static int skl_dsp_core_unset_reset_state(struct sst_dsp  *ctx)
+static int skl_dsp_core_unset_reset_state(struct sst_dsp *ctx)
 {
        int ret;
 
@@ -87,7 +87,7 @@ static int skl_dsp_core_unset_reset_state(struct sst_dsp  *ctx)
        return ret;
 }
 
-static bool is_skl_dsp_core_enable(struct sst_dsp  *ctx)
+static bool is_skl_dsp_core_enable(struct sst_dsp *ctx)
 {
        int val;
        bool is_enable;
@@ -140,7 +140,7 @@ static int skl_dsp_start_core(struct sst_dsp *ctx)
        return ret;
 }
 
-static int skl_dsp_core_power_up(struct sst_dsp  *ctx)
+static int skl_dsp_core_power_up(struct sst_dsp *ctx)
 {
        int ret;
 
@@ -166,7 +166,7 @@ static int skl_dsp_core_power_up(struct sst_dsp  *ctx)
        return ret;
 }
 
-static int skl_dsp_core_power_down(struct sst_dsp  *ctx)
+static int skl_dsp_core_power_down(struct sst_dsp *ctx)
 {
        /* update bits */
        sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_ADSPCS,
@@ -181,7 +181,7 @@ static int skl_dsp_core_power_down(struct sst_dsp  *ctx)
                        "Power down");
 }
 
-static int skl_dsp_enable_core(struct sst_dsp  *ctx)
+int skl_dsp_enable_core(struct sst_dsp *ctx)
 {
        int ret;
 
@@ -195,7 +195,7 @@ static int skl_dsp_enable_core(struct sst_dsp  *ctx)
        return skl_dsp_start_core(ctx);
 }
 
-int skl_dsp_disable_core(struct sst_dsp  *ctx)
+int skl_dsp_disable_core(struct sst_dsp *ctx)
 {
        int ret;
 
index cbb40751c37e297227edff2a9f411df2ec998c53..b6e310d49dd682c239dcfcbeacebe875b365de40 100644 (file)
@@ -53,6 +53,10 @@ struct sst_dsp_device;
 /* HIPCT */
 #define SKL_ADSP_REG_HIPCT_BUSY                BIT(31)
 
+/* FW base IDs */
+#define SKL_INSTANCE_ID                        0
+#define SKL_BASE_FW_MODULE_ID          0
+
 /* Intel HD Audio SRAM Window 1 */
 #define SKL_ADSP_SRAM1_BASE            0xA000
 
@@ -144,7 +148,8 @@ int skl_cldma_prepare(struct sst_dsp *ctx);
 void skl_dsp_set_state_locked(struct sst_dsp *ctx, int state);
 struct sst_dsp *skl_dsp_ctx_init(struct device *dev,
                struct sst_dsp_device *sst_dev, int irq);
-int skl_dsp_disable_core(struct sst_dsp  *ctx);
+int skl_dsp_enable_core(struct sst_dsp *ctx);
+int skl_dsp_disable_core(struct sst_dsp *ctx);
 bool is_skl_dsp_running(struct sst_dsp *ctx);
 irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id);
 int skl_dsp_wake(struct sst_dsp *ctx);
index e26f4746afb7e58a306ea453da996c77571a5431..348a734f8e24258a1782392a4d324b3b2a6dc794 100644 (file)
@@ -35,9 +35,6 @@
 #define SKL_ADSP_FW_STATUS     SKL_ADSP_SRAM0_BASE
 #define SKL_ADSP_ERROR_CODE    (SKL_ADSP_FW_STATUS + 0x4)
 
-#define SKL_INSTANCE_ID                0
-#define SKL_BASE_FW_MODULE_ID  0
-
 #define SKL_NUM_MODULES                1
 
 static bool skl_check_fw_status(struct sst_dsp *ctx, u32 status)
index 5a4837dcfce3e615eedf47049ac3923332c8a9bf..545b4e77b8aaeaaaa72662d1953281e0d3440c7a 100644 (file)
@@ -260,6 +260,65 @@ static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
                                multiplier;
 }
 
+static int skl_tplg_update_be_blob(struct snd_soc_dapm_widget *w,
+                                               struct skl_sst *ctx)
+{
+       struct skl_module_cfg *m_cfg = w->priv;
+       int link_type, dir;
+       u32 ch, s_freq, s_fmt;
+       struct nhlt_specific_cfg *cfg;
+       struct skl *skl = get_skl_ctx(ctx->dev);
+
+       /* check if we already have blob */
+       if (m_cfg->formats_config.caps_size > 0)
+               return 0;
+
+       dev_dbg(ctx->dev, "Applying default cfg blob\n");
+       switch (m_cfg->dev_type) {
+       case SKL_DEVICE_DMIC:
+               link_type = NHLT_LINK_DMIC;
+               dir = SNDRV_PCM_STREAM_CAPTURE;
+               s_freq = m_cfg->in_fmt[0].s_freq;
+               s_fmt = m_cfg->in_fmt[0].bit_depth;
+               ch = m_cfg->in_fmt[0].channels;
+               break;
+
+       case SKL_DEVICE_I2S:
+               link_type = NHLT_LINK_SSP;
+               if (m_cfg->hw_conn_type == SKL_CONN_SOURCE) {
+                       dir = SNDRV_PCM_STREAM_PLAYBACK;
+                       s_freq = m_cfg->out_fmt[0].s_freq;
+                       s_fmt = m_cfg->out_fmt[0].bit_depth;
+                       ch = m_cfg->out_fmt[0].channels;
+               } else {
+                       dir = SNDRV_PCM_STREAM_CAPTURE;
+                       s_freq = m_cfg->in_fmt[0].s_freq;
+                       s_fmt = m_cfg->in_fmt[0].bit_depth;
+                       ch = m_cfg->in_fmt[0].channels;
+               }
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       /* update the blob based on virtual bus_id and default params */
+       cfg = skl_get_ep_blob(skl, m_cfg->vbus_id, link_type,
+                                       s_fmt, ch, s_freq, dir);
+       if (cfg) {
+               m_cfg->formats_config.caps_size = cfg->size;
+               m_cfg->formats_config.caps = (u32 *) &cfg->caps;
+       } else {
+               dev_err(ctx->dev, "Blob NULL for id %x type %d dirn %d\n",
+                                       m_cfg->vbus_id, link_type, dir);
+               dev_err(ctx->dev, "PCM: ch %d, freq %d, fmt %d\n",
+                                       ch, s_freq, s_fmt);
+               return -EIO;
+       }
+
+       return 0;
+}
+
 static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w,
                                                        struct skl_sst *ctx)
 {
@@ -433,6 +492,9 @@ skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
                                return ret;
                }
 
+               /* update blob if blob is null for be with default value */
+               skl_tplg_update_be_blob(w, ctx);
+
                /*
                 * apply fix/conversion to module params based on
                 * FE/BE params
@@ -545,6 +607,66 @@ static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+/*
+ * Some modules require params to be set after the module is bound to
+ * all pins connected.
+ *
+ * The module provider initializes set_param flag for such modules and we
+ * send params after binding
+ */
+static int skl_tplg_set_module_bind_params(struct snd_soc_dapm_widget *w,
+                       struct skl_module_cfg *mcfg, struct skl_sst *ctx)
+{
+       int i, ret;
+       struct skl_module_cfg *mconfig = w->priv;
+       const struct snd_kcontrol_new *k;
+       struct soc_bytes_ext *sb;
+       struct skl_algo_data *bc;
+       struct skl_specific_cfg *sp_cfg;
+
+       /*
+        * check all out/in pins are in bind state.
+        * if so set the module param
+        */
+       for (i = 0; i < mcfg->max_out_queue; i++) {
+               if (mcfg->m_out_pin[i].pin_state != SKL_PIN_BIND_DONE)
+                       return 0;
+       }
+
+       for (i = 0; i < mcfg->max_in_queue; i++) {
+               if (mcfg->m_in_pin[i].pin_state != SKL_PIN_BIND_DONE)
+                       return 0;
+       }
+
+       if (mconfig->formats_config.caps_size > 0 &&
+               mconfig->formats_config.set_params == SKL_PARAM_BIND) {
+               sp_cfg = &mconfig->formats_config;
+               ret = skl_set_module_params(ctx, sp_cfg->caps,
+                                       sp_cfg->caps_size,
+                                       sp_cfg->param_id, mconfig);
+               if (ret < 0)
+                       return ret;
+       }
+
+       for (i = 0; i < w->num_kcontrols; i++) {
+               k = &w->kcontrol_news[i];
+               if (k->access & SNDRV_CTL_ELEM_ACCESS_TLV_CALLBACK) {
+                       sb = (void *) k->private_value;
+                       bc = (struct skl_algo_data *)sb->dobj.private;
+
+                       if (bc->set_params == SKL_PARAM_BIND) {
+                               ret = skl_set_module_params(ctx,
+                                               (u32 *)bc->params, bc->max,
+                                               bc->param_id, mconfig);
+                               if (ret < 0)
+                                       return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
                                struct skl *skl,
                                struct snd_soc_dapm_widget *src_w,
@@ -579,11 +701,19 @@ static int skl_tplg_bind_sinks(struct snd_soc_dapm_widget *w,
                        sink = p->sink;
                        sink_mconfig = sink->priv;
 
+                       if (src_mconfig->m_state == SKL_MODULE_UNINIT ||
+                               sink_mconfig->m_state == SKL_MODULE_UNINIT)
+                               continue;
+
                        /* Bind source to sink, mixin is always source */
                        ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
                        if (ret)
                                return ret;
 
+                       /* set module params after bind */
+                       skl_tplg_set_module_bind_params(src_w, src_mconfig, ctx);
+                       skl_tplg_set_module_bind_params(sink, sink_mconfig, ctx);
+
                        /* Start sinks pipe first */
                        if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
                                if (sink_mconfig->pipe->conn_type !=
@@ -714,6 +844,10 @@ static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
                if (ret)
                        return ret;
 
+               /* set module params after bind */
+               skl_tplg_set_module_bind_params(source, src_mconfig, ctx);
+               skl_tplg_set_module_bind_params(sink, sink_mconfig, ctx);
+
                if (sink_mconfig->pipe->conn_type != SKL_PIPE_CONN_TYPE_FE)
                        ret = skl_run_pipe(ctx, sink_mconfig->pipe);
        }
@@ -1091,6 +1225,66 @@ skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
        return NULL;
 }
 
+static struct skl_module_cfg *skl_get_mconfig_pb_cpr(
+               struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_dapm_path *p;
+       struct skl_module_cfg *mconfig = NULL;
+
+       snd_soc_dapm_widget_for_each_source_path(w, p) {
+               if (w->endpoints[SND_SOC_DAPM_DIR_OUT] > 0) {
+                       if (p->connect &&
+                                   (p->sink->id == snd_soc_dapm_aif_out) &&
+                                   p->source->priv) {
+                               mconfig = p->source->priv;
+                               return mconfig;
+                       }
+                       mconfig = skl_get_mconfig_pb_cpr(dai, p->source);
+                       if (mconfig)
+                               return mconfig;
+               }
+       }
+       return mconfig;
+}
+
+static struct skl_module_cfg *skl_get_mconfig_cap_cpr(
+               struct snd_soc_dai *dai, struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_dapm_path *p;
+       struct skl_module_cfg *mconfig = NULL;
+
+       snd_soc_dapm_widget_for_each_sink_path(w, p) {
+               if (w->endpoints[SND_SOC_DAPM_DIR_IN] > 0) {
+                       if (p->connect &&
+                                   (p->source->id == snd_soc_dapm_aif_in) &&
+                                   p->sink->priv) {
+                               mconfig = p->sink->priv;
+                               return mconfig;
+                       }
+                       mconfig = skl_get_mconfig_cap_cpr(dai, p->sink);
+                       if (mconfig)
+                               return mconfig;
+               }
+       }
+       return mconfig;
+}
+
+struct skl_module_cfg *
+skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai, int stream)
+{
+       struct snd_soc_dapm_widget *w;
+       struct skl_module_cfg *mconfig;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               w = dai->playback_widget;
+               mconfig = skl_get_mconfig_pb_cpr(dai, w);
+       } else {
+               w = dai->capture_widget;
+               mconfig = skl_get_mconfig_cap_cpr(dai, w);
+       }
+       return mconfig;
+}
+
 static u8 skl_tplg_be_link_type(int dev_type)
 {
        int ret;
@@ -1464,8 +1658,7 @@ static int skl_init_algo_data(struct device *dev, struct soc_bytes_ext *be,
                if (!ac->params)
                        return -ENOMEM;
 
-               if (dfw_ac->params)
-                       memcpy(ac->params, dfw_ac->params, ac->max);
+               memcpy(ac->params, dfw_ac->params, ac->max);
        }
 
        be->dobj.private  = ac;
@@ -1523,11 +1716,16 @@ int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
        struct hdac_bus *bus = ebus_to_hbus(ebus);
        struct skl *skl = ebus_to_skl(ebus);
 
-       ret = request_firmware(&fw, "dfw_sst.bin", bus->dev);
+       ret = request_firmware(&fw, skl->tplg_name, bus->dev);
        if (ret < 0) {
                dev_err(bus->dev, "tplg fw %s load failed with %d\n",
-                               "dfw_sst.bin", ret);
-               return ret;
+                               skl->tplg_name, ret);
+               ret = request_firmware(&fw, "dfw_sst.bin", bus->dev);
+               if (ret < 0) {
+                       dev_err(bus->dev, "Fallback tplg fw %s load failed with %d\n",
+                                       "dfw_sst.bin", ret);
+                       return ret;
+               }
        }
 
        /*
index 9aa2a2b6598a3ede5dff38f0570c5b16e30bcb70..de3c401284d9df312b13d8a1549f274a08f5a754 100644 (file)
@@ -113,6 +113,29 @@ struct skl_cpr_gtw_cfg {
        u32 config_data[1];
 } __packed;
 
+struct skl_i2s_config_blob {
+       u32 gateway_attrib;
+       u32 tdm_ts_group[8];
+       u32 ssc0;
+       u32 ssc1;
+       u32 sscto;
+       u32 sspsp;
+       u32 sstsa;
+       u32 ssrsa;
+       u32 ssc2;
+       u32 sspsp2;
+       u32 ssc3;
+       u32 ssioc;
+       u32 mdivc;
+       u32 mdivr;
+} __packed;
+
+struct skl_dma_control {
+       u32 node_id;
+       u32 config_length;
+       u32 config_data[1];
+} __packed;
+
 struct skl_cpr_cfg {
        struct skl_base_cfg base_cfg;
        struct skl_audio_data_format out_fmt;
@@ -313,6 +336,8 @@ static inline struct skl *get_skl_ctx(struct device *dev)
 
 int skl_tplg_be_update_params(struct snd_soc_dai *dai,
        struct skl_pipe_params *params);
+int skl_dsp_set_dma_control(struct skl_sst *ctx,
+               struct skl_module_cfg *mconfig);
 void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai,
        struct skl_pipe_params *params, int stream);
 int skl_tplg_init(struct snd_soc_platform *platform,
@@ -345,5 +370,7 @@ int skl_set_module_params(struct skl_sst *ctx, u32 *params, int size,
 int skl_get_module_params(struct skl_sst *ctx, u32 *params, int size,
                          u32 param_id, struct skl_module_cfg *mcfg);
 
+struct skl_module_cfg *skl_tplg_be_get_cpr_module(struct snd_soc_dai *dai,
+                                                               int stream);
 enum skl_bitdepth skl_get_bit_depth(int params);
 #endif
index c9ae010b3cc8dc1df5a359a67987a44186e588cc..1db88a63ac1787f6862d62b53a141481dbb60cb8 100644 (file)
@@ -144,7 +144,8 @@ enum module_pin_type {
 enum skl_module_param_type {
        SKL_PARAM_DEFAULT = 0,
        SKL_PARAM_INIT,
-       SKL_PARAM_SET
+       SKL_PARAM_SET,
+       SKL_PARAM_BIND
 };
 
 struct skl_dfw_module_pin {
index 092705e73db497fb1d75c1c9b45a3c2aaaa34507..ab5e25aaeee38f1284701873946a81d48163fd23 100644 (file)
@@ -28,6 +28,9 @@
 #include <linux/firmware.h>
 #include <sound/pcm.h>
 #include "../common/sst-acpi.h"
+#include <sound/hda_register.h>
+#include <sound/hdaudio.h>
+#include <sound/hda_i915.h>
 #include "skl.h"
 #include "skl-sst-dsp.h"
 #include "skl-sst-ipc.h"
@@ -243,6 +246,16 @@ static int skl_resume(struct device *dev)
        struct hdac_bus *bus = ebus_to_hbus(ebus);
        int ret;
 
+       /* Turned OFF in HDMI codec driver after codec reconfiguration */
+       if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
+               ret = snd_hdac_display_power(bus, true);
+               if (ret < 0) {
+                       dev_err(bus->dev,
+                               "Cannot turn on display power on i915\n");
+                       return ret;
+               }
+       }
+
        /*
         * resume only when we are not in suspend active, otherwise need to
         * restore the device
@@ -481,6 +494,27 @@ static int skl_create(struct pci_dev *pci,
        return 0;
 }
 
+static int skl_i915_init(struct hdac_bus *bus)
+{
+       int err;
+
+       /*
+        * The HDMI codec is in GPU so we need to ensure that it is powered
+        * up and ready for probe
+        */
+       err = snd_hdac_i915_init(bus);
+       if (err < 0)
+               return err;
+
+       err = snd_hdac_display_power(bus, true);
+       if (err < 0) {
+               dev_err(bus->dev, "Cannot turn on display power on i915\n");
+               return err;
+       }
+
+       return err;
+}
+
 static int skl_first_init(struct hdac_ext_bus *ebus)
 {
        struct skl *skl = ebus_to_skl(ebus);
@@ -543,6 +577,12 @@ static int skl_first_init(struct hdac_ext_bus *ebus)
        /* initialize chip */
        skl_init_pci(skl);
 
+       if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
+               err = skl_i915_init(bus);
+               if (err < 0)
+                       return err;
+       }
+
        skl_init_chip(bus, true);
 
        /* codec detection */
@@ -573,11 +613,15 @@ static int skl_probe(struct pci_dev *pci,
        if (err < 0)
                goto out_free;
 
+       skl->pci_id = pci->device;
+
        skl->nhlt = skl_nhlt_init(bus->dev);
 
        if (skl->nhlt == NULL)
                goto out_free;
 
+       skl_nhlt_update_topology_bin(skl);
+
        pci_set_drvdata(skl->pci, ebus);
 
        /* check if dsp is there */
@@ -613,6 +657,14 @@ static int skl_probe(struct pci_dev *pci,
        if (err < 0)
                goto out_unregister;
 
+       if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI)) {
+               err = snd_hdac_display_power(bus, false);
+               if (err < 0) {
+                       dev_err(bus->dev, "Cannot turn off display power on i915\n");
+                       return err;
+               }
+       }
+
        /*configure PM */
        pm_runtime_put_noidle(bus->dev);
        pm_runtime_allow(bus->dev);
@@ -634,6 +686,31 @@ static int skl_probe(struct pci_dev *pci,
        return err;
 }
 
+static void skl_shutdown(struct pci_dev *pci)
+{
+       struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+       struct hdac_stream *s;
+       struct hdac_ext_stream *stream;
+       struct skl *skl;
+
+       if (ebus == NULL)
+               return;
+
+       skl = ebus_to_skl(ebus);
+
+       if (skl->init_failed)
+               return;
+
+       snd_hdac_ext_stop_streams(ebus);
+       list_for_each_entry(s, &bus->stream_list, list) {
+               stream = stream_to_hdac_ext_stream(s);
+               snd_hdac_ext_stream_decouple(ebus, stream, false);
+       }
+
+       snd_hdac_bus_stop_chip(bus);
+}
+
 static void skl_remove(struct pci_dev *pci)
 {
        struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
@@ -642,6 +719,9 @@ static void skl_remove(struct pci_dev *pci)
        if (skl->tplg)
                release_firmware(skl->tplg);
 
+       if (IS_ENABLED(CONFIG_SND_SOC_HDAC_HDMI))
+               snd_hdac_i915_exit(&ebus->bus);
+
        if (pci_dev_run_wake(pci))
                pm_runtime_get_noresume(&pci->dev);
        pci_dev_put(pci);
@@ -662,11 +742,18 @@ static struct sst_acpi_mach sst_skl_devdata[] = {
        {}
 };
 
+static struct sst_acpi_mach sst_bxtp_devdata[] = {
+       { "INT343A", "bxt_alc298s_i2s", "intel/dsp_fw_bxtn.bin", NULL, NULL, NULL },
+};
+
 /* PCI IDs */
 static const struct pci_device_id skl_ids[] = {
        /* Sunrise Point-LP */
        { PCI_DEVICE(0x8086, 0x9d70),
                .driver_data = (unsigned long)&sst_skl_devdata},
+       /* BXT-P */
+       { PCI_DEVICE(0x8086, 0x5a98),
+               .driver_data = (unsigned long)&sst_bxtp_devdata},
        { 0, }
 };
 MODULE_DEVICE_TABLE(pci, skl_ids);
@@ -677,6 +764,7 @@ static struct pci_driver skl_driver = {
        .id_table = skl_ids,
        .probe = skl_probe,
        .remove = skl_remove,
+       .shutdown = skl_shutdown,
        .driver = {
                .pm = &skl_pm,
        },
index 4d18293b5537e78b286921bddcedb5b18407b5ee..39e16fa7a92b1a6921d8032ed279e88319061a4e 100644 (file)
@@ -73,6 +73,8 @@ struct skl {
        struct list_head ppl_list;
 
        const char *fw_name;
+       char tplg_name[64];
+       unsigned short pci_id;
        const struct firmware *tplg;
 
        int supend_active;
@@ -88,6 +90,16 @@ struct skl_dma_params {
        u8 stream_tag;
 };
 
+struct skl_dsp_ops {
+       int id;
+       struct skl_dsp_loader_ops (*loader_ops)(void);
+       int (*init)(struct device *dev, void __iomem *mmio_base,
+                       int irq, const char *fw_name,
+                       struct skl_dsp_loader_ops loader_ops,
+                       struct skl_sst **skl_sst);
+       void (*cleanup)(struct device *dev, struct skl_sst *ctx);
+};
+
 int skl_platform_unregister(struct device *dev);
 int skl_platform_register(struct device *dev);
 
@@ -96,8 +108,9 @@ void skl_nhlt_free(void *addr);
 struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
                        u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);
 
+int skl_nhlt_update_topology_bin(struct skl *skl);
 int skl_init_dsp(struct skl *skl);
-void skl_free_dsp(struct skl *skl);
+int skl_free_dsp(struct skl *skl);
 int skl_suspend_dsp(struct skl *skl);
 int skl_resume_dsp(struct skl *skl);
 #endif /* __SOUND_SOC_SKL_H */
index 9769676753878b833513d0e863dab776bdaae635..f7e789e97fbca5fcefafb4c7fa061b0dfd3ef6c8 100644 (file)
@@ -17,6 +17,27 @@ config SND_SOC_MT8173_MAX98090
          Select Y if you have such device.
          If unsure select "N".
 
+config SND_SOC_MT8173_RT5650
+       tristate "ASoC Audio driver for MT8173 with RT5650 codec"
+       depends on SND_SOC_MEDIATEK && I2C
+       select SND_SOC_RT5645
+       help
+         This adds ASoC driver for Mediatek MT8173 boards
+         with the RT5650 audio codec.
+         Select Y if you have such device.
+         If unsure select "N".
+
+config SND_SOC_MT8173_RT5650_RT5514
+       tristate "ASoC Audio driver for MT8173 with RT5650 RT5514 codecs"
+       depends on SND_SOC_MEDIATEK && I2C
+       select SND_SOC_RT5645
+       select SND_SOC_RT5514
+       help
+         This adds ASoC driver for Mediatek MT8173 boards
+         with the RT5650 and RT5514 codecs.
+         Select Y if you have such device.
+         If unsure select "N".
+
 config SND_SOC_MT8173_RT5650_RT5676
        tristate "ASoC Audio driver for MT8173 with RT5650 RT5676 codecs"
        depends on SND_SOC_MEDIATEK && I2C
@@ -27,4 +48,3 @@ config SND_SOC_MT8173_RT5650_RT5676
          with the RT5650 and RT5676 codecs.
          Select Y if you have such device.
          If unsure select "N".
-
index 75effbec438d4ebbcc9b6551755c5c171b169477..d486860c0a88e832f44b78f02d1579b550844d0b 100644 (file)
@@ -2,4 +2,6 @@
 obj-$(CONFIG_SND_SOC_MEDIATEK) += mtk-afe-pcm.o
 # Machine support
 obj-$(CONFIG_SND_SOC_MT8173_MAX98090) += mt8173-max98090.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650) += mt8173-rt5650.o
+obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5514) += mt8173-rt5650-rt5514.o
 obj-$(CONFIG_SND_SOC_MT8173_RT5650_RT5676) += mt8173-rt5650-rt5676.o
diff --git a/sound/soc/mediatek/mt8173-rt5650-rt5514.c b/sound/soc/mediatek/mt8173-rt5650-rt5514.c
new file mode 100644 (file)
index 0000000..58e0836
--- /dev/null
@@ -0,0 +1,258 @@
+/*
+ * mt8173-rt5650-rt5514.c  --  MT8173 machine driver with RT5650/5514 codecs
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Koro Chen <koro.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../codecs/rt5645.h"
+
+#define MCLK_FOR_CODECS                12288000
+
+static const struct snd_soc_dapm_widget mt8173_rt5650_rt5514_widgets[] = {
+       SND_SOC_DAPM_SPK("Speaker", NULL),
+       SND_SOC_DAPM_MIC("Int Mic", NULL),
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route mt8173_rt5650_rt5514_routes[] = {
+       {"Speaker", NULL, "SPOL"},
+       {"Speaker", NULL, "SPOR"},
+       {"Sub DMIC1L", NULL, "Int Mic"},
+       {"Sub DMIC1R", NULL, "Int Mic"},
+       {"Headphone", NULL, "HPOL"},
+       {"Headphone", NULL, "HPOR"},
+       {"Headset Mic", NULL, "micbias1"},
+       {"Headset Mic", NULL, "micbias2"},
+       {"IN1P", NULL, "Headset Mic"},
+       {"IN1N", NULL, "Headset Mic"},
+};
+
+static const struct snd_kcontrol_new mt8173_rt5650_rt5514_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Speaker"),
+       SOC_DAPM_PIN_SWITCH("Int Mic"),
+       SOC_DAPM_PIN_SWITCH("Headphone"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+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;
+       int i, ret;
+
+       for (i = 0; i < rtd->num_codecs; i++) {
+               struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
+
+               /* pll from mclk 12.288M */
+               ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
+                                         params_rate(params) * 512);
+               if (ret)
+                       return ret;
+
+               /* sysclk from pll */
+               ret = snd_soc_dai_set_sysclk(codec_dai, 1,
+                                            params_rate(params) * 512,
+                                            SND_SOC_CLOCK_IN);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+static struct snd_soc_ops mt8173_rt5650_rt5514_ops = {
+       .hw_params = mt8173_rt5650_rt5514_hw_params,
+};
+
+static struct snd_soc_jack mt8173_rt5650_rt5514_jack;
+
+static int mt8173_rt5650_rt5514_init(struct snd_soc_pcm_runtime *runtime)
+{
+       struct snd_soc_card *card = runtime->card;
+       struct snd_soc_codec *codec = runtime->codec_dais[0]->codec;
+       int ret;
+
+       rt5645_sel_asrc_clk_src(codec,
+                               RT5645_DA_STEREO_FILTER |
+                               RT5645_AD_STEREO_FILTER,
+                               RT5645_CLK_SEL_I2S1_ASRC);
+
+       /* enable jack detection */
+       ret = snd_soc_card_jack_new(card, "Headset Jack",
+                                   SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+                                   SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+                                   SND_JACK_BTN_2 | SND_JACK_BTN_3,
+                                   &mt8173_rt5650_rt5514_jack, NULL, 0);
+       if (ret) {
+               dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
+               return ret;
+       }
+
+       return rt5645_set_jack_detect(codec,
+                                     &mt8173_rt5650_rt5514_jack,
+                                     &mt8173_rt5650_rt5514_jack,
+                                     &mt8173_rt5650_rt5514_jack);
+}
+
+static struct snd_soc_dai_link_component mt8173_rt5650_rt5514_codecs[] = {
+       {
+               .dai_name = "rt5645-aif1",
+       },
+       {
+               .dai_name = "rt5514-aif1",
+       },
+};
+
+enum {
+       DAI_LINK_PLAYBACK,
+       DAI_LINK_CAPTURE,
+       DAI_LINK_CODEC_I2S,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link mt8173_rt5650_rt5514_dais[] = {
+       /* Front End DAI links */
+       [DAI_LINK_PLAYBACK] = {
+               .name = "rt5650_rt5514 Playback",
+               .stream_name = "rt5650_rt5514 Playback",
+               .cpu_dai_name = "DL1",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+               .dynamic = 1,
+               .dpcm_playback = 1,
+       },
+       [DAI_LINK_CAPTURE] = {
+               .name = "rt5650_rt5514 Capture",
+               .stream_name = "rt5650_rt5514 Capture",
+               .cpu_dai_name = "VUL",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+               .dynamic = 1,
+               .dpcm_capture = 1,
+       },
+       /* Back End DAI links */
+       [DAI_LINK_CODEC_I2S] = {
+               .name = "Codec",
+               .cpu_dai_name = "I2S",
+               .no_pcm = 1,
+               .codecs = mt8173_rt5650_rt5514_codecs,
+               .num_codecs = 2,
+               .init = mt8173_rt5650_rt5514_init,
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+                          SND_SOC_DAIFMT_CBS_CFS,
+               .ops = &mt8173_rt5650_rt5514_ops,
+               .ignore_pmdown_time = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+};
+
+static struct snd_soc_codec_conf mt8173_rt5650_rt5514_codec_conf[] = {
+       {
+               .name_prefix = "Sub",
+       },
+};
+
+static struct snd_soc_card mt8173_rt5650_rt5514_card = {
+       .name = "mtk-rt5650-rt5514",
+       .owner = THIS_MODULE,
+       .dai_link = mt8173_rt5650_rt5514_dais,
+       .num_links = ARRAY_SIZE(mt8173_rt5650_rt5514_dais),
+       .codec_conf = mt8173_rt5650_rt5514_codec_conf,
+       .num_configs = ARRAY_SIZE(mt8173_rt5650_rt5514_codec_conf),
+       .controls = mt8173_rt5650_rt5514_controls,
+       .num_controls = ARRAY_SIZE(mt8173_rt5650_rt5514_controls),
+       .dapm_widgets = mt8173_rt5650_rt5514_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_rt5514_widgets),
+       .dapm_routes = mt8173_rt5650_rt5514_routes,
+       .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_rt5514_routes),
+};
+
+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;
+       int i, ret;
+
+       platform_node = of_parse_phandle(pdev->dev.of_node,
+                                        "mediatek,platform", 0);
+       if (!platform_node) {
+               dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < card->num_links; i++) {
+               if (mt8173_rt5650_rt5514_dais[i].platform_name)
+                       continue;
+               mt8173_rt5650_rt5514_dais[i].platform_of_node = platform_node;
+       }
+
+       mt8173_rt5650_rt5514_codecs[0].of_node =
+               of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);
+       if (!mt8173_rt5650_rt5514_codecs[0].of_node) {
+               dev_err(&pdev->dev,
+                       "Property 'audio-codec' missing or invalid\n");
+               return -EINVAL;
+       }
+       mt8173_rt5650_rt5514_codecs[1].of_node =
+               of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 1);
+       if (!mt8173_rt5650_rt5514_codecs[1].of_node) {
+               dev_err(&pdev->dev,
+                       "Property 'audio-codec' missing or invalid\n");
+               return -EINVAL;
+       }
+       mt8173_rt5650_rt5514_codec_conf[0].of_node =
+               mt8173_rt5650_rt5514_codecs[1].of_node;
+
+       card->dev = &pdev->dev;
+       platform_set_drvdata(pdev, card);
+
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
+       if (ret)
+               dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+                       __func__, ret);
+       return ret;
+}
+
+static const struct of_device_id mt8173_rt5650_rt5514_dt_match[] = {
+       { .compatible = "mediatek,mt8173-rt5650-rt5514", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, mt8173_rt5650_rt5514_dt_match);
+
+static struct platform_driver mt8173_rt5650_rt5514_driver = {
+       .driver = {
+                  .name = "mtk-rt5650-rt5514",
+                  .of_match_table = mt8173_rt5650_rt5514_dt_match,
+#ifdef CONFIG_PM
+                  .pm = &snd_soc_pm_ops,
+#endif
+       },
+       .probe = mt8173_rt5650_rt5514_dev_probe,
+};
+
+module_platform_driver(mt8173_rt5650_rt5514_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8173 RT5650 and RT5514 SoC machine driver");
+MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mtk-rt5650-rt5514");
+
index 50ba538eccb3f771da9c08685d4e33aa0cbfa80b..5c4c58c69c516aa32b76ae1e141f01685a6f0f62 100644 (file)
@@ -131,10 +131,17 @@ static struct snd_soc_dai_link_component mt8173_rt5650_rt5676_codecs[] = {
        },
 };
 
+enum {
+       DAI_LINK_PLAYBACK,
+       DAI_LINK_CAPTURE,
+       DAI_LINK_CODEC_I2S,
+       DAI_LINK_INTERCODEC
+};
+
 /* Digital audio interface glue - connects codec <---> CPU */
 static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
        /* Front End DAI links */
-       {
+       [DAI_LINK_PLAYBACK] = {
                .name = "rt5650_rt5676 Playback",
                .stream_name = "rt5650_rt5676 Playback",
                .cpu_dai_name = "DL1",
@@ -144,7 +151,7 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
                .dynamic = 1,
                .dpcm_playback = 1,
        },
-       {
+       [DAI_LINK_CAPTURE] = {
                .name = "rt5650_rt5676 Capture",
                .stream_name = "rt5650_rt5676 Capture",
                .cpu_dai_name = "VUL",
@@ -156,7 +163,7 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
        },
 
        /* Back End DAI links */
-       {
+       [DAI_LINK_CODEC_I2S] = {
                .name = "Codec",
                .cpu_dai_name = "I2S",
                .no_pcm = 1,
@@ -170,7 +177,8 @@ static struct snd_soc_dai_link mt8173_rt5650_rt5676_dais[] = {
                .dpcm_playback = 1,
                .dpcm_capture = 1,
        },
-       { /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */
+       /* rt5676 <-> rt5650 intercodec link: Sets rt5676 I2S2 as master */
+       [DAI_LINK_INTERCODEC] = {
                .name = "rt5650_rt5676 intercodec",
                .stream_name = "rt5650_rt5676 intercodec",
                .cpu_dai_name = "snd-soc-dummy-dai",
@@ -240,7 +248,7 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
        mt8173_rt5650_rt5676_codec_conf[0].of_node =
                mt8173_rt5650_rt5676_codecs[1].of_node;
 
-       mt8173_rt5650_rt5676_dais[3].codec_of_node =
+       mt8173_rt5650_rt5676_dais[DAI_LINK_INTERCODEC].codec_of_node =
                mt8173_rt5650_rt5676_codecs[1].of_node;
 
        card->dev = &pdev->dev;
diff --git a/sound/soc/mediatek/mt8173-rt5650.c b/sound/soc/mediatek/mt8173-rt5650.c
new file mode 100644 (file)
index 0000000..bb09bb1
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * mt8173-rt5650.c  --  MT8173 machine driver with RT5650 codecs
+ *
+ * Copyright (c) 2016 MediaTek Inc.
+ * Author: Koro Chen <koro.chen@mediatek.com>
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License version 2 and
+ * only version 2 as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include "../codecs/rt5645.h"
+
+#define MCLK_FOR_CODECS                12288000
+
+static const struct snd_soc_dapm_widget mt8173_rt5650_widgets[] = {
+       SND_SOC_DAPM_SPK("Speaker", NULL),
+       SND_SOC_DAPM_MIC("Int Mic", NULL),
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+};
+
+static const struct snd_soc_dapm_route mt8173_rt5650_routes[] = {
+       {"Speaker", NULL, "SPOL"},
+       {"Speaker", NULL, "SPOR"},
+       {"DMIC L1", NULL, "Int Mic"},
+       {"DMIC R1", NULL, "Int Mic"},
+       {"Headphone", NULL, "HPOL"},
+       {"Headphone", NULL, "HPOR"},
+       {"Headset Mic", NULL, "micbias1"},
+       {"Headset Mic", NULL, "micbias2"},
+       {"IN1P", NULL, "Headset Mic"},
+       {"IN1N", NULL, "Headset Mic"},
+};
+
+static const struct snd_kcontrol_new mt8173_rt5650_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Speaker"),
+       SOC_DAPM_PIN_SWITCH("Int Mic"),
+       SOC_DAPM_PIN_SWITCH("Headphone"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+};
+
+static int mt8173_rt5650_hw_params(struct snd_pcm_substream *substream,
+                                  struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       int i, ret;
+
+       for (i = 0; i < rtd->num_codecs; i++) {
+               struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
+
+               /* pll from mclk 12.288M */
+               ret = snd_soc_dai_set_pll(codec_dai, 0, 0, MCLK_FOR_CODECS,
+                                         params_rate(params) * 512);
+               if (ret)
+                       return ret;
+
+               /* sysclk from pll */
+               ret = snd_soc_dai_set_sysclk(codec_dai, 1,
+                                            params_rate(params) * 512,
+                                            SND_SOC_CLOCK_IN);
+               if (ret)
+                       return ret;
+       }
+       return 0;
+}
+
+static struct snd_soc_ops mt8173_rt5650_ops = {
+       .hw_params = mt8173_rt5650_hw_params,
+};
+
+static struct snd_soc_jack mt8173_rt5650_jack;
+
+static int mt8173_rt5650_init(struct snd_soc_pcm_runtime *runtime)
+{
+       struct snd_soc_card *card = runtime->card;
+       struct snd_soc_codec *codec = runtime->codec_dais[0]->codec;
+       int ret;
+
+       rt5645_sel_asrc_clk_src(codec,
+                               RT5645_DA_STEREO_FILTER |
+                               RT5645_AD_STEREO_FILTER,
+                               RT5645_CLK_SEL_I2S1_ASRC);
+       /* enable jack detection */
+       ret = snd_soc_card_jack_new(card, "Headset Jack",
+                                   SND_JACK_HEADPHONE | SND_JACK_MICROPHONE |
+                                   SND_JACK_BTN_0 | SND_JACK_BTN_1 |
+                                   SND_JACK_BTN_2 | SND_JACK_BTN_3,
+                                   &mt8173_rt5650_jack, NULL, 0);
+       if (ret) {
+               dev_err(card->dev, "Can't new Headset Jack %d\n", ret);
+               return ret;
+       }
+
+       return rt5645_set_jack_detect(codec,
+                                     &mt8173_rt5650_jack,
+                                     &mt8173_rt5650_jack,
+                                     &mt8173_rt5650_jack);
+}
+
+static struct snd_soc_dai_link_component mt8173_rt5650_codecs[] = {
+       {
+               .dai_name = "rt5645-aif1",
+       },
+};
+
+enum {
+       DAI_LINK_PLAYBACK,
+       DAI_LINK_CAPTURE,
+       DAI_LINK_CODEC_I2S,
+};
+
+/* Digital audio interface glue - connects codec <---> CPU */
+static struct snd_soc_dai_link mt8173_rt5650_dais[] = {
+       /* Front End DAI links */
+       [DAI_LINK_PLAYBACK] = {
+               .name = "rt5650 Playback",
+               .stream_name = "rt5650 Playback",
+               .cpu_dai_name = "DL1",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+               .dynamic = 1,
+               .dpcm_playback = 1,
+       },
+       [DAI_LINK_CAPTURE] = {
+               .name = "rt5650 Capture",
+               .stream_name = "rt5650 Capture",
+               .cpu_dai_name = "VUL",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
+               .dynamic = 1,
+               .dpcm_capture = 1,
+       },
+       /* Back End DAI links */
+       [DAI_LINK_CODEC_I2S] = {
+               .name = "Codec",
+               .cpu_dai_name = "I2S",
+               .no_pcm = 1,
+               .codecs = mt8173_rt5650_codecs,
+               .num_codecs = 1,
+               .init = mt8173_rt5650_init,
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+                          SND_SOC_DAIFMT_CBS_CFS,
+               .ops = &mt8173_rt5650_ops,
+               .ignore_pmdown_time = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+};
+
+static struct snd_soc_card mt8173_rt5650_card = {
+       .name = "mtk-rt5650",
+       .owner = THIS_MODULE,
+       .dai_link = mt8173_rt5650_dais,
+       .num_links = ARRAY_SIZE(mt8173_rt5650_dais),
+       .controls = mt8173_rt5650_controls,
+       .num_controls = ARRAY_SIZE(mt8173_rt5650_controls),
+       .dapm_widgets = mt8173_rt5650_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(mt8173_rt5650_widgets),
+       .dapm_routes = mt8173_rt5650_routes,
+       .num_dapm_routes = ARRAY_SIZE(mt8173_rt5650_routes),
+};
+
+static int mt8173_rt5650_dev_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = &mt8173_rt5650_card;
+       struct device_node *platform_node;
+       int i, ret;
+
+       platform_node = of_parse_phandle(pdev->dev.of_node,
+                                        "mediatek,platform", 0);
+       if (!platform_node) {
+               dev_err(&pdev->dev, "Property 'platform' missing or invalid\n");
+               return -EINVAL;
+       }
+
+       for (i = 0; i < card->num_links; i++) {
+               if (mt8173_rt5650_dais[i].platform_name)
+                       continue;
+               mt8173_rt5650_dais[i].platform_of_node = platform_node;
+       }
+
+       mt8173_rt5650_codecs[0].of_node =
+               of_parse_phandle(pdev->dev.of_node, "mediatek,audio-codec", 0);
+       if (!mt8173_rt5650_codecs[0].of_node) {
+               dev_err(&pdev->dev,
+                       "Property 'audio-codec' missing or invalid\n");
+               return -EINVAL;
+       }
+       card->dev = &pdev->dev;
+       platform_set_drvdata(pdev, card);
+
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
+       if (ret)
+               dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
+                       __func__, ret);
+       return ret;
+}
+
+static const struct of_device_id mt8173_rt5650_dt_match[] = {
+       { .compatible = "mediatek,mt8173-rt5650", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, mt8173_rt5650_dt_match);
+
+static struct platform_driver mt8173_rt5650_driver = {
+       .driver = {
+                  .name = "mtk-rt5650",
+                  .of_match_table = mt8173_rt5650_dt_match,
+#ifdef CONFIG_PM
+                  .pm = &snd_soc_pm_ops,
+#endif
+       },
+       .probe = mt8173_rt5650_dev_probe,
+};
+
+module_platform_driver(mt8173_rt5650_driver);
+
+/* Module information */
+MODULE_DESCRIPTION("MT8173 RT5650 SoC machine driver");
+MODULE_AUTHOR("Koro Chen <koro.chen@mediatek.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:mtk-rt5650");
+
index 9b1af1a7087480c95f931a3b9e020d0f33ac8e46..f341f623e887f2d2d169e33ef89903a6705c186c 100644 (file)
@@ -87,6 +87,7 @@ struct mtk_afe_memif_data {
        int irq_en_shift;
        int irq_fs_shift;
        int irq_clr_shift;
+       int msb_shift;
 };
 
 struct mtk_afe_memif {
index 08af9f5dc4ab2117bb81501b0381930f9cf483b5..f1c58a2c12fbbd8420614c576f75a19cf90c08e6 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/of_address.h>
+#include <linux/dma-mapping.h>
 #include <linux/pm_runtime.h>
 #include <sound/soc.h>
 #include "mtk-afe-common.h"
 #define AFE_I2S_CON1           0x0034
 #define AFE_I2S_CON2           0x0038
 #define AFE_CONN_24BIT         0x006c
+#define AFE_MEMIF_MSB          0x00cc
 
 #define AFE_CONN1              0x0024
 #define AFE_CONN2              0x0028
+#define AFE_CONN3              0x002c
 #define AFE_CONN7              0x0460
 #define AFE_CONN8              0x0464
 #define AFE_HDMI_CONN0         0x0390
@@ -61,6 +64,7 @@
 #define AFE_HDMI_OUT_CUR       0x0378
 #define AFE_HDMI_OUT_END       0x037c
 
+#define AFE_ADDA_TOP_CON0      0x0120
 #define AFE_ADDA2_TOP_CON0     0x0600
 
 #define AFE_HDMI_OUT_CON0      0x0370
@@ -257,6 +261,7 @@ static int mtk_afe_set_i2s(struct mtk_afe *afe, unsigned int rate)
                return -EINVAL;
 
        /* from external ADC */
+       regmap_update_bits(afe->regmap, AFE_ADDA_TOP_CON0, 0x1, 0x1);
        regmap_update_bits(afe->regmap, AFE_ADDA2_TOP_CON0, 0x1, 0x1);
 
        /* set input */
@@ -281,20 +286,13 @@ static void mtk_afe_set_i2s_enable(struct mtk_afe *afe, bool enable)
 
        regmap_read(afe->regmap, AFE_I2S_CON2, &val);
        if (!!(val & AFE_I2S_CON2_EN) == enable)
-               return; /* must skip soft reset */
-
-       /* I2S soft reset begin */
-       regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0x4);
+               return;
 
        /* input */
        regmap_update_bits(afe->regmap, AFE_I2S_CON2, 0x1, enable);
 
        /* output */
        regmap_update_bits(afe->regmap, AFE_I2S_CON1, 0x1, enable);
-
-       /* I2S soft reset end */
-       udelay(1);
-       regmap_update_bits(afe->regmap, AUDIO_TOP_CON1, 0x4, 0);
 }
 
 static int mtk_afe_dais_enable_clks(struct mtk_afe *afe,
@@ -363,6 +361,7 @@ static int mtk_afe_i2s_startup(struct snd_pcm_substream *substream,
                return 0;
 
        mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL);
+       mtk_afe_dais_enable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL);
        regmap_update_bits(afe->regmap, AUDIO_TOP_CON0,
                           AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M, 0);
        return 0;
@@ -382,6 +381,7 @@ static void mtk_afe_i2s_shutdown(struct snd_pcm_substream *substream,
                           AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M,
                           AUD_TCON0_PDN_22M | AUD_TCON0_PDN_24M);
        mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S1_M], NULL);
+       mtk_afe_dais_disable_clks(afe, afe->clocks[MTK_CLK_I2S2_M], NULL);
 }
 
 static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream,
@@ -395,6 +395,9 @@ static int mtk_afe_i2s_prepare(struct snd_pcm_substream *substream,
        mtk_afe_dais_set_clks(afe,
                              afe->clocks[MTK_CLK_I2S1_M], runtime->rate * 256,
                              NULL, 0);
+       mtk_afe_dais_set_clks(afe,
+                             afe->clocks[MTK_CLK_I2S2_M], runtime->rate * 256,
+                             NULL, 0);
        /* config I2S */
        ret = mtk_afe_set_i2s(afe, substream->runtime->rate);
        if (ret)
@@ -592,6 +595,7 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct mtk_afe *afe = snd_soc_platform_get_drvdata(rtd->platform);
        struct mtk_afe_memif *memif = &afe->memif[rtd->cpu_dai->id];
+       int msb_at_bit33 = 0;
        int ret;
 
        dev_dbg(afe->dev,
@@ -603,7 +607,8 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
        if (ret < 0)
                return ret;
 
-       memif->phys_buf_addr = substream->runtime->dma_addr;
+       msb_at_bit33 = upper_32_bits(substream->runtime->dma_addr) ? 1 : 0;
+       memif->phys_buf_addr = lower_32_bits(substream->runtime->dma_addr);
        memif->buffer_size = substream->runtime->dma_bytes;
 
        /* start */
@@ -614,6 +619,11 @@ static int mtk_afe_dais_hw_params(struct snd_pcm_substream *substream,
                     memif->data->reg_ofs_base + AFE_BASE_END_OFFSET,
                     memif->phys_buf_addr + memif->buffer_size - 1);
 
+       /* set MSB to 33-bit */
+       regmap_update_bits(afe->regmap, AFE_MEMIF_MSB,
+                          1 << memif->data->msb_shift,
+                          msb_at_bit33 << memif->data->msb_shift);
+
        /* set channel */
        if (memif->data->mono_shift >= 0) {
                unsigned int mono = (params_channels(params) == 1) ? 1 : 0;
@@ -894,15 +904,19 @@ static const struct snd_kcontrol_new mtk_afe_o04_mix[] = {
 };
 
 static const struct snd_kcontrol_new mtk_afe_o09_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I03 Switch", AFE_CONN3, 0, 1, 0),
        SOC_DAPM_SINGLE_AUTODISABLE("I17 Switch", AFE_CONN7, 30, 1, 0),
 };
 
 static const struct snd_kcontrol_new mtk_afe_o10_mix[] = {
+       SOC_DAPM_SINGLE_AUTODISABLE("I04 Switch", AFE_CONN3, 3, 1, 0),
        SOC_DAPM_SINGLE_AUTODISABLE("I18 Switch", AFE_CONN8, 0, 1, 0),
 };
 
 static const struct snd_soc_dapm_widget mtk_afe_pcm_widgets[] = {
        /* inter-connections */
+       SND_SOC_DAPM_MIXER("I03", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_MIXER("I04", SND_SOC_NOPM, 0, 0, NULL, 0),
        SND_SOC_DAPM_MIXER("I05", SND_SOC_NOPM, 0, 0, NULL, 0),
        SND_SOC_DAPM_MIXER("I06", SND_SOC_NOPM, 0, 0, NULL, 0),
        SND_SOC_DAPM_MIXER("I17", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -925,12 +939,16 @@ static const struct snd_soc_dapm_route mtk_afe_pcm_routes[] = {
        {"I2S Playback", NULL, "O04"},
        {"VUL", NULL, "O09"},
        {"VUL", NULL, "O10"},
+       {"I03", NULL, "I2S Capture"},
+       {"I04", NULL, "I2S Capture"},
        {"I17", NULL, "I2S Capture"},
        {"I18", NULL, "I2S Capture"},
        { "O03", "I05 Switch", "I05" },
        { "O04", "I06 Switch", "I06" },
        { "O09", "I17 Switch", "I17" },
+       { "O09", "I03 Switch", "I03" },
        { "O10", "I18 Switch", "I18" },
+       { "O10", "I04 Switch", "I04" },
 };
 
 static const struct snd_soc_dapm_route mtk_afe_hdmi_routes[] = {
@@ -978,6 +996,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
                .irq_en_shift = 0,
                .irq_fs_shift = 4,
                .irq_clr_shift = 0,
+               .msb_shift = 0,
        }, {
                .name = "DL2",
                .id = MTK_AFE_MEMIF_DL2,
@@ -991,6 +1010,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
                .irq_en_shift = 2,
                .irq_fs_shift = 16,
                .irq_clr_shift = 2,
+               .msb_shift = 1,
        }, {
                .name = "VUL",
                .id = MTK_AFE_MEMIF_VUL,
@@ -1004,6 +1024,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
                .irq_en_shift = 1,
                .irq_fs_shift = 8,
                .irq_clr_shift = 1,
+               .msb_shift = 6,
        }, {
                .name = "DAI",
                .id = MTK_AFE_MEMIF_DAI,
@@ -1017,6 +1038,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
                .irq_en_shift = 3,
                .irq_fs_shift = 20,
                .irq_clr_shift = 3,
+               .msb_shift = 5,
        }, {
                .name = "AWB",
                .id = MTK_AFE_MEMIF_AWB,
@@ -1030,6 +1052,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
                .irq_en_shift = 14,
                .irq_fs_shift = 24,
                .irq_clr_shift = 6,
+               .msb_shift = 3,
        }, {
                .name = "MOD_DAI",
                .id = MTK_AFE_MEMIF_MOD_DAI,
@@ -1043,6 +1066,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
                .irq_en_shift = 3,
                .irq_fs_shift = 20,
                .irq_clr_shift = 3,
+               .msb_shift = 4,
        }, {
                .name = "HDMI",
                .id = MTK_AFE_MEMIF_HDMI,
@@ -1056,6 +1080,7 @@ static const struct mtk_afe_memif_data memif_data[MTK_AFE_MEMIF_NUM] = {
                .irq_en_shift = 12,
                .irq_fs_shift = -1,
                .irq_clr_shift = 4,
+               .msb_shift = 8,
        },
 };
 
@@ -1189,6 +1214,10 @@ static int mtk_afe_pcm_dev_probe(struct platform_device *pdev)
        struct mtk_afe *afe;
        struct resource *res;
 
+       ret = dma_set_mask_and_coherent(&pdev->dev, DMA_BIT_MASK(33));
+       if (ret)
+               return ret;
+
        afe = devm_kzalloc(&pdev->dev, sizeof(*afe), GFP_KERNEL);
        if (!afe)
                return -ENOMEM;
index a6c7b8d87cd2f15198334840b9013980f87313c0..13631003cb7c6796d49f5278834eb5924aea294c 100644 (file)
@@ -418,7 +418,7 @@ static int mxs_saif_hw_params(struct snd_pcm_substream *substream,
        }
 
        stat = __raw_readl(saif->base + SAIF_STAT);
-       if (stat & BM_SAIF_STAT_BUSY) {
+       if (!saif->mclk_in_use && (stat & BM_SAIF_STAT_BUSY)) {
                dev_err(cpu_dai->dev, "error: busy\n");
                return -EBUSY;
        }
index f83cc2bc0fc41a3434e0dca238569e613e3bad22..64425d352962f889989ad59d73a71f18cfae53e2 100644 (file)
@@ -345,6 +345,7 @@ static int omap_hdmi_audio_probe(struct platform_device *pdev)
                dai_drv = &omap4_hdmi_dai;
                break;
        case OMAPDSS_VER_OMAP5:
+       case OMAPDSS_VER_DRA7xx:
                dai_drv = &omap5_hdmi_dai;
                break;
        default:
index 416ea646c3b1606b7bbc499c50bd447046f6a23b..ec522e94b0e219408c359a0059b7ee0ce6c7b79c 100644 (file)
@@ -52,7 +52,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        int freq_out, sspa_mclk, sysclk;
-       int sspa_div;
 
        if (params_rate(params) > 11025) {
                freq_out  = params_rate(params) * 512;
@@ -63,7 +62,6 @@ static int brownstone_wm8994_hw_params(struct snd_pcm_substream *substream,
                sysclk    = params_rate(params) * 512;
                sspa_mclk = params_rate(params) * 64;
        }
-       sspa_div = freq_out / sspa_mclk;
 
        snd_soc_dai_set_sysclk(cpu_dai, MMP_SSPA_CLK_AUDIO, freq_out, 0);
        snd_soc_dai_set_pll(cpu_dai, MMP_SYSCLK, 0, freq_out, sysclk);
index 3cc252e55468eaabf529d60978eaa3c52158b25e..8ec9a074b38bd7025a2f39e2e66062a83cdad6df 100644 (file)
@@ -11,21 +11,24 @@ config SND_SOC_LPASS_CPU
 
 config SND_SOC_LPASS_PLATFORM
        tristate
+       depends on HAS_DMA
        select REGMAP_MMIO
 
 config SND_SOC_LPASS_IPQ806X
        tristate
+       depends on HAS_DMA
        select SND_SOC_LPASS_CPU
        select SND_SOC_LPASS_PLATFORM
 
 config SND_SOC_LPASS_APQ8016
        tristate
+       depends on HAS_DMA
        select SND_SOC_LPASS_CPU
        select SND_SOC_LPASS_PLATFORM
 
 config SND_SOC_STORM
        tristate "ASoC I2S support for Storm boards"
-       depends on SND_SOC_QCOM
+       depends on SND_SOC_QCOM && HAS_DMA
        select SND_SOC_LPASS_IPQ806X
        select SND_SOC_MAX98357A
        help
@@ -34,7 +37,7 @@ config SND_SOC_STORM
 
 config SND_SOC_APQ8016_SBC
        tristate "SoC Audio support for APQ8016 SBC platforms"
-       depends on SND_SOC_QCOM
+       depends on SND_SOC_QCOM && HAS_DMA
        select SND_SOC_LPASS_APQ8016
        help
           Support for Qualcomm Technologies LPASS audio block in
index 1efdf0088ecdff4eac700492dfc5bf90270e288c..1289543c8fb2594dfa4a9d6f9be16f70d0e67c5c 100644 (file)
@@ -30,6 +30,7 @@ struct apq8016_sbc_data {
        struct snd_soc_dai_link dai_link[];     /* dynamically allocated */
 };
 
+#define MIC_CTRL_TER_WS_SLAVE_SEL      BIT(21)
 #define MIC_CTRL_QUA_WS_SLAVE_SEL_10   BIT(17)
 #define MIC_CTRL_TLMM_SCLK_EN          BIT(1)
 #define        SPKR_CTL_PRI_WS_SLAVE_SEL_11    (BIT(17) | BIT(16))
@@ -53,6 +54,12 @@ static int apq8016_sbc_dai_init(struct snd_soc_pcm_runtime *rtd)
                        MIC_CTRL_TLMM_SCLK_EN,
                        pdata->mic_iomux);
                break;
+       case MI2S_TERTIARY:
+               writel(readl(pdata->mic_iomux) | MIC_CTRL_TER_WS_SLAVE_SEL |
+                       MIC_CTRL_TLMM_SCLK_EN,
+                       pdata->mic_iomux);
+
+               break;
 
        default:
                dev_err(card->dev, "unsupported cpu dai configuration\n");
@@ -126,9 +133,6 @@ static struct apq8016_sbc_data *apq8016_sbc_parse_of(struct snd_soc_card *card)
                }
 
                link->platform_of_node = link->cpu_of_node;
-               /* For now we only support playback */
-               link->playback_only = true;
-
                ret = of_property_read_string(np, "link-name", &link->name);
                if (ret) {
                        dev_err(card->dev, "error getting codec dai_link name\n");
index 94efc01020c442087a453489ce7f7e7261cd1716..3eef0c37ba50d6d4d568727917f4d93348584247 100644 (file)
@@ -133,23 +133,36 @@ static struct snd_soc_dai_driver apq8016_lpass_cpu_dai_driver[] = {
        },
 };
 
-static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+static int apq8016_lpass_alloc_dma_channel(struct lpass_data *drvdata,
+                                          int direction)
 {
        struct lpass_variant *v = drvdata->variant;
-       int chan = find_first_zero_bit(&drvdata->rdma_ch_bit_map,
+       int chan = 0;
+
+       if (direction == SNDRV_PCM_STREAM_PLAYBACK) {
+               chan = find_first_zero_bit(&drvdata->dma_ch_bit_map,
                                        v->rdma_channels);
 
-       if (chan >= v->rdma_channels)
-               return -EBUSY;
+               if (chan >= v->rdma_channels)
+                       return -EBUSY;
+       } else {
+               chan = find_next_zero_bit(&drvdata->dma_ch_bit_map,
+                                       v->wrdma_channel_start +
+                                       v->wrdma_channels,
+                                       v->wrdma_channel_start);
+
+               if (chan >=  v->wrdma_channel_start + v->wrdma_channels)
+                       return -EBUSY;
+       }
 
-       set_bit(chan, &drvdata->rdma_ch_bit_map);
+       set_bit(chan, &drvdata->dma_ch_bit_map);
 
        return chan;
 }
 
 static int apq8016_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
 {
-       clear_bit(chan, &drvdata->rdma_ch_bit_map);
+       clear_bit(chan, &drvdata->dma_ch_bit_map);
 
        return 0;
 }
@@ -212,7 +225,11 @@ static struct lpass_variant apq8016_data = {
        .rdma_reg_base          = 0x8400,
        .rdma_reg_stride        = 0x1000,
        .rdma_channels          = 2,
-       .rdmactl_audif_start    = 1,
+       .dmactl_audif_start     = 1,
+       .wrdma_reg_base         = 0xB000,
+       .wrdma_reg_stride       = 0x1000,
+       .wrdma_channel_start    = 5,
+       .wrdma_channels         = 2,
        .dai_driver             = apq8016_lpass_cpu_dai_driver,
        .num_dai                = ARRAY_SIZE(apq8016_lpass_cpu_dai_driver),
        .init                   = apq8016_lpass_init,
index e5101e0d2d372262f8ecf7d0498240cba971b86f..3cde9fb977fa72779a2ff26530fbaafae29a6386 100644 (file)
@@ -120,31 +120,60 @@ static int lpass_cpu_daiops_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       switch (channels) {
-       case 1:
-               regval |= LPAIF_I2SCTL_SPKMODE_SD0;
-               regval |= LPAIF_I2SCTL_SPKMONO_MONO;
-               break;
-       case 2:
-               regval |= LPAIF_I2SCTL_SPKMODE_SD0;
-               regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
-               break;
-       case 4:
-               regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
-               regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
-               break;
-       case 6:
-               regval |= LPAIF_I2SCTL_SPKMODE_6CH;
-               regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
-               break;
-       case 8:
-               regval |= LPAIF_I2SCTL_SPKMODE_8CH;
-               regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
-               break;
-       default:
-               dev_err(dai->dev, "%s() invalid channels given: %u\n",
-                               __func__, channels);
-               return -EINVAL;
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               switch (channels) {
+               case 1:
+                       regval |= LPAIF_I2SCTL_SPKMODE_SD0;
+                       regval |= LPAIF_I2SCTL_SPKMONO_MONO;
+                       break;
+               case 2:
+                       regval |= LPAIF_I2SCTL_SPKMODE_SD0;
+                       regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+                       break;
+               case 4:
+                       regval |= LPAIF_I2SCTL_SPKMODE_QUAD01;
+                       regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+                       break;
+               case 6:
+                       regval |= LPAIF_I2SCTL_SPKMODE_6CH;
+                       regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+                       break;
+               case 8:
+                       regval |= LPAIF_I2SCTL_SPKMODE_8CH;
+                       regval |= LPAIF_I2SCTL_SPKMONO_STEREO;
+                       break;
+               default:
+                       dev_err(dai->dev, "%s() invalid channels given: %u\n",
+                                       __func__, channels);
+                       return -EINVAL;
+               }
+       } else {
+               switch (channels) {
+               case 1:
+                       regval |= LPAIF_I2SCTL_MICMODE_SD0;
+                       regval |= LPAIF_I2SCTL_MICMONO_MONO;
+                       break;
+               case 2:
+                       regval |= LPAIF_I2SCTL_MICMODE_SD0;
+                       regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+                       break;
+               case 4:
+                       regval |= LPAIF_I2SCTL_MICMODE_QUAD01;
+                       regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+                       break;
+               case 6:
+                       regval |= LPAIF_I2SCTL_MICMODE_6CH;
+                       regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+                       break;
+               case 8:
+                       regval |= LPAIF_I2SCTL_MICMODE_8CH;
+                       regval |= LPAIF_I2SCTL_MICMONO_STEREO;
+                       break;
+               default:
+                       dev_err(dai->dev, "%s() invalid channels given: %u\n",
+                                       __func__, channels);
+                       return -EINVAL;
+               }
        }
 
        ret = regmap_write(drvdata->lpaif_map,
@@ -188,10 +217,19 @@ static int lpass_cpu_daiops_prepare(struct snd_pcm_substream *substream,
 {
        struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
        int ret;
+       unsigned int val, mask;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               val = LPAIF_I2SCTL_SPKEN_ENABLE;
+               mask = LPAIF_I2SCTL_SPKEN_MASK;
+       } else  {
+               val = LPAIF_I2SCTL_MICEN_ENABLE;
+               mask = LPAIF_I2SCTL_MICEN_MASK;
+       }
 
        ret = regmap_update_bits(drvdata->lpaif_map,
                        LPAIF_I2SCTL_REG(drvdata->variant, dai->driver->id),
-                       LPAIF_I2SCTL_SPKEN_MASK, LPAIF_I2SCTL_SPKEN_ENABLE);
+                       mask, val);
        if (ret)
                dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
                                __func__, ret);
@@ -204,16 +242,24 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
 {
        struct lpass_data *drvdata = snd_soc_dai_get_drvdata(dai);
        int ret = -EINVAL;
+       unsigned int val, mask;
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
        case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       val = LPAIF_I2SCTL_SPKEN_ENABLE;
+                       mask = LPAIF_I2SCTL_SPKEN_MASK;
+               } else  {
+                       val = LPAIF_I2SCTL_MICEN_ENABLE;
+                       mask = LPAIF_I2SCTL_MICEN_MASK;
+               }
+
                ret = regmap_update_bits(drvdata->lpaif_map,
                                LPAIF_I2SCTL_REG(drvdata->variant,
                                                dai->driver->id),
-                               LPAIF_I2SCTL_SPKEN_MASK,
-                               LPAIF_I2SCTL_SPKEN_ENABLE);
+                               mask, val);
                if (ret)
                        dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
                                        __func__, ret);
@@ -221,11 +267,18 @@ static int lpass_cpu_daiops_trigger(struct snd_pcm_substream *substream,
        case SNDRV_PCM_TRIGGER_STOP:
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       val = LPAIF_I2SCTL_SPKEN_DISABLE;
+                       mask = LPAIF_I2SCTL_SPKEN_MASK;
+               } else  {
+                       val = LPAIF_I2SCTL_MICEN_DISABLE;
+                       mask = LPAIF_I2SCTL_MICEN_MASK;
+               }
+
                ret = regmap_update_bits(drvdata->lpaif_map,
                                LPAIF_I2SCTL_REG(drvdata->variant,
                                                dai->driver->id),
-                               LPAIF_I2SCTL_SPKEN_MASK,
-                               LPAIF_I2SCTL_SPKEN_DISABLE);
+                               mask, val);
                if (ret)
                        dev_err(dai->dev, "%s() error writing to i2sctl reg: %d\n",
                                        __func__, ret);
@@ -294,6 +347,17 @@ static bool lpass_cpu_regmap_writeable(struct device *dev, unsigned int reg)
                        return true;
        }
 
+       for (i = 0; i < v->wrdma_channels; ++i) {
+               if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
+                       return true;
+       }
+
        return false;
 }
 
@@ -327,6 +391,19 @@ static bool lpass_cpu_regmap_readable(struct device *dev, unsigned int reg)
                        return true;
        }
 
+       for (i = 0; i < v->wrdma_channels; ++i) {
+               if (reg == LPAIF_WRDMACTL_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMABASE_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMABUFF_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
+                       return true;
+               if (reg == LPAIF_WRDMAPER_REG(v, i + v->wrdma_channel_start))
+                       return true;
+       }
+
        return false;
 }
 
@@ -344,6 +421,10 @@ static bool lpass_cpu_regmap_volatile(struct device *dev, unsigned int reg)
                if (reg == LPAIF_RDMACURR_REG(v, i))
                        return true;
 
+       for (i = 0; i < v->wrdma_channels; ++i)
+               if (reg == LPAIF_WRDMACURR_REG(v, i + v->wrdma_channel_start))
+                       return true;
+
        return false;
 }
 
@@ -398,8 +479,9 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
                return PTR_ERR((void const __force *)drvdata->lpaif);
        }
 
-       lpass_cpu_regmap_config.max_register = LPAIF_RDMAPER_REG(variant,
-                                               variant->rdma_channels);
+       lpass_cpu_regmap_config.max_register = LPAIF_WRDMAPER_REG(variant,
+                                               variant->wrdma_channels +
+                                               variant->wrdma_channel_start);
 
        drvdata->lpaif_map = devm_regmap_init_mmio(&pdev->dev, drvdata->lpaif,
                        &lpass_cpu_regmap_config);
index 7a416795271130113a875cbf5b4fbea70e813cd9..608c1a92af8a801d8364343540d8cc85cdc6f640 100644 (file)
@@ -63,9 +63,12 @@ static struct snd_soc_dai_driver ipq806x_lpass_cpu_dai_driver = {
        .ops    = &asoc_qcom_lpass_cpu_dai_ops,
 };
 
-static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata)
+static int ipq806x_lpass_alloc_dma_channel(struct lpass_data *drvdata, int dir)
 {
-       return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
+       if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+               return IPQ806X_LPAIF_RDMA_CHAN_MI2S;
+       else    /* Capture currently not implemented */
+               return -EINVAL;
 }
 
 static int ipq806x_lpass_free_dma_channel(struct lpass_data *drvdata, int chan)
@@ -83,6 +86,10 @@ static struct lpass_variant ipq806x_data = {
        .rdma_reg_base          = 0x6000,
        .rdma_reg_stride        = 0x1000,
        .rdma_channels          = 4,
+       .wrdma_reg_base         = 0xB000,
+       .wrdma_reg_stride       = 0x1000,
+       .wrdma_channel_start    = 5,
+       .wrdma_channels         = 4,
        .dai_driver             = &ipq806x_lpass_cpu_dai_driver,
        .num_dai                = 1,
        .alloc_dma_channel      = ipq806x_lpass_alloc_dma_channel,
index 95e22f1310529f09ae5b0e7205845a164cb2b248..2240bc68e6ecf1d8e1b013e06c7b6cb46ca2bce0 100644 (file)
 #define LPAIF_I2SCTL_SPKMONO_STEREO    (0 << LPAIF_I2SCTL_SPKMONO_SHIFT)
 #define LPAIF_I2SCTL_SPKMONO_MONO      (1 << LPAIF_I2SCTL_SPKMONO_SHIFT)
 
+#define LPAIF_I2SCTL_MICEN_MASK                GENMASK(8, 8)
+#define LPAIF_I2SCTL_MICEN_SHIFT       8
+#define LPAIF_I2SCTL_MICEN_DISABLE     (0 << LPAIF_I2SCTL_MICEN_SHIFT)
+#define LPAIF_I2SCTL_MICEN_ENABLE      (1 << LPAIF_I2SCTL_MICEN_SHIFT)
+
+#define LPAIF_I2SCTL_MICMODE_MASK      GENMASK(7, 4)
+#define LPAIF_I2SCTL_MICMODE_SHIFT     4
+#define LPAIF_I2SCTL_MICMODE_NONE      (0 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_SD0       (1 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_SD1       (2 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_SD2       (3 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_SD3       (4 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_QUAD01    (5 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_QUAD23    (6 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_6CH       (7 << LPAIF_I2SCTL_MICMODE_SHIFT)
+#define LPAIF_I2SCTL_MICMODE_8CH       (8 << LPAIF_I2SCTL_MICMODE_SHIFT)
+
+#define LPAIF_I2SCTL_MIMONO_MASK       GENMASK(3, 3)
+#define LPAIF_I2SCTL_MICMONO_SHIFT     3
+#define LPAIF_I2SCTL_MICMONO_STEREO    (0 << LPAIF_I2SCTL_MICMONO_SHIFT)
+#define LPAIF_I2SCTL_MICMONO_MONO      (1 << LPAIF_I2SCTL_MICMONO_SHIFT)
+
 #define LPAIF_I2SCTL_WSSRC_MASK                0x0004
 #define LPAIF_I2SCTL_WSSRC_SHIFT       2
 #define LPAIF_I2SCTL_WSSRC_INTERNAL    (0 << LPAIF_I2SCTL_WSSRC_SHIFT)
 #define        LPAIF_RDMAPER_REG(v, chan)      LPAIF_RDMA_REG_ADDR(v, 0x10, (chan))
 #define        LPAIF_RDMAPERCNT_REG(v, chan)   LPAIF_RDMA_REG_ADDR(v, 0x14, (chan))
 
-#define LPAIF_RDMACTL_BURSTEN_MASK     0x800
-#define LPAIF_RDMACTL_BURSTEN_SHIFT    11
-#define LPAIF_RDMACTL_BURSTEN_SINGLE   (0 << LPAIF_RDMACTL_BURSTEN_SHIFT)
-#define LPAIF_RDMACTL_BURSTEN_INCR4    (1 << LPAIF_RDMACTL_BURSTEN_SHIFT)
-
-#define LPAIF_RDMACTL_WPSCNT_MASK      0x700
-#define LPAIF_RDMACTL_WPSCNT_SHIFT     8
-#define LPAIF_RDMACTL_WPSCNT_ONE       (0 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_TWO       (1 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_THREE     (2 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_FOUR      (3 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_SIX       (5 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-#define LPAIF_RDMACTL_WPSCNT_EIGHT     (7 << LPAIF_RDMACTL_WPSCNT_SHIFT)
-
-#define LPAIF_RDMACTL_AUDINTF_MASK     0x0F0
-#define LPAIF_RDMACTL_AUDINTF_SHIFT    4
-
-#define LPAIF_RDMACTL_FIFOWM_MASK      0x00E
-#define LPAIF_RDMACTL_FIFOWM_SHIFT     1
-#define LPAIF_RDMACTL_FIFOWM_1         (0 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_2         (1 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_3         (2 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_4         (3 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_5         (4 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_6         (5 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_7         (6 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-#define LPAIF_RDMACTL_FIFOWM_8         (7 << LPAIF_RDMACTL_FIFOWM_SHIFT)
-
-#define LPAIF_RDMACTL_ENABLE_MASK      0x1
-#define LPAIF_RDMACTL_ENABLE_SHIFT     0
-#define LPAIF_RDMACTL_ENABLE_OFF       (0 << LPAIF_RDMACTL_ENABLE_SHIFT)
-#define LPAIF_RDMACTL_ENABLE_ON                (1 << LPAIF_RDMACTL_ENABLE_SHIFT)
-
+#define LPAIF_WRDMA_REG_ADDR(v, addr, chan) \
+       (v->wrdma_reg_base + (addr) + \
+        v->wrdma_reg_stride * (chan - v->wrdma_channel_start))
+
+#define LPAIF_WRDMACTL_REG(v, chan)    LPAIF_WRDMA_REG_ADDR(v, 0x00, (chan))
+#define LPAIF_WRDMABASE_REG(v, chan)   LPAIF_WRDMA_REG_ADDR(v, 0x04, (chan))
+#define        LPAIF_WRDMABUFF_REG(v, chan)    LPAIF_WRDMA_REG_ADDR(v, 0x08, (chan))
+#define LPAIF_WRDMACURR_REG(v, chan)   LPAIF_WRDMA_REG_ADDR(v, 0x0C, (chan))
+#define        LPAIF_WRDMAPER_REG(v, chan)     LPAIF_WRDMA_REG_ADDR(v, 0x10, (chan))
+#define        LPAIF_WRDMAPERCNT_REG(v, chan)  LPAIF_WRDMA_REG_ADDR(v, 0x14, (chan))
+
+#define __LPAIF_DMA_REG(v, chan, dir, reg)  \
+       (dir ==  SNDRV_PCM_STREAM_PLAYBACK) ? \
+               LPAIF_RDMA##reg##_REG(v, chan) : \
+               LPAIF_WRDMA##reg##_REG(v, chan)
+
+#define LPAIF_DMACTL_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CTL)
+#define LPAIF_DMABASE_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BASE)
+#define        LPAIF_DMABUFF_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, BUFF)
+#define LPAIF_DMACURR_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, CURR)
+#define        LPAIF_DMAPER_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PER)
+#define        LPAIF_DMAPERCNT_REG(v, chan, dir) __LPAIF_DMA_REG(v, chan, dir, PERCNT)
+
+#define LPAIF_DMACTL_BURSTEN_MASK      0x800
+#define LPAIF_DMACTL_BURSTEN_SHIFT     11
+#define LPAIF_DMACTL_BURSTEN_SINGLE    (0 << LPAIF_DMACTL_BURSTEN_SHIFT)
+#define LPAIF_DMACTL_BURSTEN_INCR4     (1 << LPAIF_DMACTL_BURSTEN_SHIFT)
+
+#define LPAIF_DMACTL_WPSCNT_MASK       0x700
+#define LPAIF_DMACTL_WPSCNT_SHIFT      8
+#define LPAIF_DMACTL_WPSCNT_ONE        (0 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_TWO        (1 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_THREE      (2 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_FOUR       (3 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_SIX        (5 << LPAIF_DMACTL_WPSCNT_SHIFT)
+#define LPAIF_DMACTL_WPSCNT_EIGHT      (7 << LPAIF_DMACTL_WPSCNT_SHIFT)
+
+#define LPAIF_DMACTL_AUDINTF_MASK      0x0F0
+#define LPAIF_DMACTL_AUDINTF_SHIFT     4
+#define LPAIF_DMACTL_AUDINTF(id)       (id << LPAIF_DMACTL_AUDINTF_SHIFT)
+
+#define LPAIF_DMACTL_FIFOWM_MASK       0x00E
+#define LPAIF_DMACTL_FIFOWM_SHIFT      1
+#define LPAIF_DMACTL_FIFOWM_1          (0 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_2          (1 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_3          (2 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_4          (3 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_5          (4 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_6          (5 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_7          (6 << LPAIF_DMACTL_FIFOWM_SHIFT)
+#define LPAIF_DMACTL_FIFOWM_8          (7 << LPAIF_DMACTL_FIFOWM_SHIFT)
+
+#define LPAIF_DMACTL_ENABLE_MASK       0x1
+#define LPAIF_DMACTL_ENABLE_SHIFT      0
+#define LPAIF_DMACTL_ENABLE_OFF        (0 << LPAIF_DMACTL_ENABLE_SHIFT)
+#define LPAIF_DMACTL_ENABLE_ON         (1 << LPAIF_DMACTL_ENABLE_SHIFT)
+
+#define LPAIF_DMACTL_DYNCLK_MASK       BIT(12)
+#define LPAIF_DMACTL_DYNCLK_SHIFT      12
+#define LPAIF_DMACTL_DYNCLK_OFF        (0 << LPAIF_DMACTL_DYNCLK_SHIFT)
+#define LPAIF_DMACTL_DYNCLK_ON         (1 << LPAIF_DMACTL_DYNCLK_SHIFT)
 #endif /* __LPASS_LPAIF_REG_H__ */
index 4aeb8e1a7160b812a6ab119cfec657b9f55b16cb..6e8665430bd5efc22e06f6934a7133d1b62306ea 100644 (file)
@@ -26,6 +26,7 @@
 
 struct lpass_pcm_data {
        int rdma_ch;
+       int wrdma_ch;
        int i2s_port;
 };
 
@@ -90,8 +91,14 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
        snd_pcm_format_t format = params_format(params);
        unsigned int channels = params_channels(params);
        unsigned int regval;
+       int ch, dir = substream->stream;
        int bitwidth;
-       int ret, rdma_port = pcm_data->i2s_port + v->rdmactl_audif_start;
+       int ret, dma_port = pcm_data->i2s_port + v->dmactl_audif_start;
+
+       if (dir ==  SNDRV_PCM_STREAM_PLAYBACK)
+               ch = pcm_data->rdma_ch;
+       else
+               ch = pcm_data->wrdma_ch;
 
        bitwidth = snd_pcm_format_width(format);
        if (bitwidth < 0) {
@@ -100,25 +107,25 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
                return bitwidth;
        }
 
-       regval = LPAIF_RDMACTL_BURSTEN_INCR4 |
-                       LPAIF_RDMACTL_AUDINTF(rdma_port) |
-                       LPAIF_RDMACTL_FIFOWM_8;
+       regval = LPAIF_DMACTL_BURSTEN_INCR4 |
+                       LPAIF_DMACTL_AUDINTF(dma_port) |
+                       LPAIF_DMACTL_FIFOWM_8;
 
        switch (bitwidth) {
        case 16:
                switch (channels) {
                case 1:
                case 2:
-                       regval |= LPAIF_RDMACTL_WPSCNT_ONE;
+                       regval |= LPAIF_DMACTL_WPSCNT_ONE;
                        break;
                case 4:
-                       regval |= LPAIF_RDMACTL_WPSCNT_TWO;
+                       regval |= LPAIF_DMACTL_WPSCNT_TWO;
                        break;
                case 6:
-                       regval |= LPAIF_RDMACTL_WPSCNT_THREE;
+                       regval |= LPAIF_DMACTL_WPSCNT_THREE;
                        break;
                case 8:
-                       regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
+                       regval |= LPAIF_DMACTL_WPSCNT_FOUR;
                        break;
                default:
                        dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
@@ -130,19 +137,19 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
        case 32:
                switch (channels) {
                case 1:
-                       regval |= LPAIF_RDMACTL_WPSCNT_ONE;
+                       regval |= LPAIF_DMACTL_WPSCNT_ONE;
                        break;
                case 2:
-                       regval |= LPAIF_RDMACTL_WPSCNT_TWO;
+                       regval |= LPAIF_DMACTL_WPSCNT_TWO;
                        break;
                case 4:
-                       regval |= LPAIF_RDMACTL_WPSCNT_FOUR;
+                       regval |= LPAIF_DMACTL_WPSCNT_FOUR;
                        break;
                case 6:
-                       regval |= LPAIF_RDMACTL_WPSCNT_SIX;
+                       regval |= LPAIF_DMACTL_WPSCNT_SIX;
                        break;
                case 8:
-                       regval |= LPAIF_RDMACTL_WPSCNT_EIGHT;
+                       regval |= LPAIF_DMACTL_WPSCNT_EIGHT;
                        break;
                default:
                        dev_err(soc_runtime->dev, "%s() invalid PCM config given: bw=%d, ch=%u\n",
@@ -157,7 +164,7 @@ static int lpass_platform_pcmops_hw_params(struct snd_pcm_substream *substream,
        }
 
        ret = regmap_write(drvdata->lpaif_map,
-                       LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), regval);
+                       LPAIF_DMACTL_REG(v, ch, dir), regval);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
                                __func__, ret);
@@ -174,10 +181,15 @@ static int lpass_platform_pcmops_hw_free(struct snd_pcm_substream *substream)
        struct lpass_data *drvdata =
                snd_soc_platform_get_drvdata(soc_runtime->platform);
        struct lpass_variant *v = drvdata->variant;
+       unsigned int reg;
        int ret;
 
-       ret = regmap_write(drvdata->lpaif_map,
-                       LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch), 0);
+       if (substream->stream ==  SNDRV_PCM_STREAM_PLAYBACK)
+               reg = LPAIF_RDMACTL_REG(v, pcm_data->rdma_ch);
+       else
+               reg = LPAIF_WRDMACTL_REG(v, pcm_data->wrdma_ch);
+
+       ret = regmap_write(drvdata->lpaif_map, reg, 0);
        if (ret)
                dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
                                __func__, ret);
@@ -193,10 +205,15 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
        struct lpass_data *drvdata =
                snd_soc_platform_get_drvdata(soc_runtime->platform);
        struct lpass_variant *v = drvdata->variant;
-       int ret, ch = pcm_data->rdma_ch;
+       int ret, ch, dir = substream->stream;
+
+       if (dir ==  SNDRV_PCM_STREAM_PLAYBACK)
+               ch = pcm_data->rdma_ch;
+       else
+               ch = pcm_data->wrdma_ch;
 
        ret = regmap_write(drvdata->lpaif_map,
-                       LPAIF_RDMABASE_REG(v, ch),
+                       LPAIF_DMABASE_REG(v, ch, dir),
                        runtime->dma_addr);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error writing to rdmabase reg: %d\n",
@@ -205,7 +222,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
        }
 
        ret = regmap_write(drvdata->lpaif_map,
-                       LPAIF_RDMABUFF_REG(v, ch),
+                       LPAIF_DMABUFF_REG(v, ch, dir),
                        (snd_pcm_lib_buffer_bytes(substream) >> 2) - 1);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error writing to rdmabuff reg: %d\n",
@@ -214,7 +231,7 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
        }
 
        ret = regmap_write(drvdata->lpaif_map,
-                       LPAIF_RDMAPER_REG(v, ch),
+                       LPAIF_DMAPER_REG(v, ch, dir),
                        (snd_pcm_lib_period_bytes(substream) >> 2) - 1);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error writing to rdmaper reg: %d\n",
@@ -223,8 +240,8 @@ static int lpass_platform_pcmops_prepare(struct snd_pcm_substream *substream)
        }
 
        ret = regmap_update_bits(drvdata->lpaif_map,
-                       LPAIF_RDMACTL_REG(v, ch),
-                       LPAIF_RDMACTL_ENABLE_MASK, LPAIF_RDMACTL_ENABLE_ON);
+                       LPAIF_DMACTL_REG(v, ch, dir),
+                       LPAIF_DMACTL_ENABLE_MASK, LPAIF_DMACTL_ENABLE_ON);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
                                __func__, ret);
@@ -242,7 +259,12 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
        struct lpass_data *drvdata =
                snd_soc_platform_get_drvdata(soc_runtime->platform);
        struct lpass_variant *v = drvdata->variant;
-       int ret, ch = pcm_data->rdma_ch;
+       int ret, ch, dir = substream->stream;
+
+       if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+               ch = pcm_data->rdma_ch;
+       else
+               ch = pcm_data->wrdma_ch;
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
@@ -269,9 +291,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
                }
 
                ret = regmap_update_bits(drvdata->lpaif_map,
-                               LPAIF_RDMACTL_REG(v, ch),
-                               LPAIF_RDMACTL_ENABLE_MASK,
-                               LPAIF_RDMACTL_ENABLE_ON);
+                               LPAIF_DMACTL_REG(v, ch, dir),
+                               LPAIF_DMACTL_ENABLE_MASK,
+                               LPAIF_DMACTL_ENABLE_ON);
                if (ret) {
                        dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
                                        __func__, ret);
@@ -282,9 +304,9 @@ static int lpass_platform_pcmops_trigger(struct snd_pcm_substream *substream,
        case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
                ret = regmap_update_bits(drvdata->lpaif_map,
-                               LPAIF_RDMACTL_REG(v, ch),
-                               LPAIF_RDMACTL_ENABLE_MASK,
-                               LPAIF_RDMACTL_ENABLE_OFF);
+                               LPAIF_DMACTL_REG(v, ch, dir),
+                               LPAIF_DMACTL_ENABLE_MASK,
+                               LPAIF_DMACTL_ENABLE_OFF);
                if (ret) {
                        dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
                                        __func__, ret);
@@ -314,10 +336,15 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
                        snd_soc_platform_get_drvdata(soc_runtime->platform);
        struct lpass_variant *v = drvdata->variant;
        unsigned int base_addr, curr_addr;
-       int ret, ch = pcm_data->rdma_ch;
+       int ret, ch, dir = substream->stream;
+
+       if (dir == SNDRV_PCM_STREAM_PLAYBACK)
+               ch = pcm_data->rdma_ch;
+       else
+               ch = pcm_data->wrdma_ch;
 
        ret = regmap_read(drvdata->lpaif_map,
-                       LPAIF_RDMABASE_REG(v, ch), &base_addr);
+                       LPAIF_DMABASE_REG(v, ch, dir), &base_addr);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error reading from rdmabase reg: %d\n",
                                __func__, ret);
@@ -325,7 +352,7 @@ static snd_pcm_uframes_t lpass_platform_pcmops_pointer(
        }
 
        ret = regmap_read(drvdata->lpaif_map,
-                       LPAIF_RDMACURR_REG(v, ch), &curr_addr);
+                       LPAIF_DMACURR_REG(v, ch, dir), &curr_addr);
        if (ret) {
                dev_err(soc_runtime->dev, "%s() error reading from rdmacurr reg: %d\n",
                                __func__, ret);
@@ -439,101 +466,124 @@ static irqreturn_t lpass_platform_lpaif_irq(int irq, void *data)
        return IRQ_HANDLED;
 }
 
-static int lpass_platform_alloc_buffer(struct snd_pcm_substream *substream,
-               struct snd_soc_pcm_runtime *rt)
-{
-       struct snd_dma_buffer *buf = &substream->dma_buffer;
-       size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
-
-       buf->dev.type = SNDRV_DMA_TYPE_DEV;
-       buf->dev.dev = rt->platform->dev;
-       buf->private_data = NULL;
-       buf->area = dma_alloc_coherent(rt->platform->dev, size, &buf->addr,
-                       GFP_KERNEL);
-       if (!buf->area) {
-               dev_err(rt->platform->dev, "%s: Could not allocate DMA buffer\n",
-                               __func__);
-               return -ENOMEM;
-       }
-       buf->bytes = size;
-
-       return 0;
-}
-
-static void lpass_platform_free_buffer(struct snd_pcm_substream *substream,
-               struct snd_soc_pcm_runtime *rt)
-{
-       struct snd_dma_buffer *buf = &substream->dma_buffer;
-
-       if (buf->area) {
-               dma_free_coherent(rt->dev, buf->bytes, buf->area,
-                               buf->addr);
-       }
-       buf->area = NULL;
-}
-
 static int lpass_platform_pcm_new(struct snd_soc_pcm_runtime *soc_runtime)
 {
        struct snd_pcm *pcm = soc_runtime->pcm;
-       struct snd_pcm_substream *substream =
-               pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+       struct snd_pcm_substream *psubstream, *csubstream;
        struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
        struct lpass_data *drvdata =
                snd_soc_platform_get_drvdata(soc_runtime->platform);
        struct lpass_variant *v = drvdata->variant;
        int ret;
        struct lpass_pcm_data *data;
+       size_t size = lpass_platform_pcm_hardware.buffer_bytes_max;
 
        data = devm_kzalloc(soc_runtime->dev, sizeof(*data), GFP_KERNEL);
        if (!data)
                return -ENOMEM;
 
-       if (v->alloc_dma_channel)
-               data->rdma_ch = v->alloc_dma_channel(drvdata);
+       data->i2s_port = cpu_dai->driver->id;
+       snd_soc_pcm_set_drvdata(soc_runtime, data);
 
-       if (IS_ERR_VALUE(data->rdma_ch))
-               return data->rdma_ch;
+       psubstream = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
+       if (psubstream) {
+               if (v->alloc_dma_channel)
+                       data->rdma_ch = v->alloc_dma_channel(drvdata,
+                                               SNDRV_PCM_STREAM_PLAYBACK);
 
-       drvdata->substream[data->rdma_ch] = substream;
-       data->i2s_port = cpu_dai->driver->id;
+               if (IS_ERR_VALUE(data->rdma_ch))
+                       return data->rdma_ch;
 
-       snd_soc_pcm_set_drvdata(soc_runtime, data);
+               drvdata->substream[data->rdma_ch] = psubstream;
 
-       ret = lpass_platform_alloc_buffer(substream, soc_runtime);
-       if (ret)
-               return ret;
+               ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+                                       soc_runtime->platform->dev,
+                                       size, &psubstream->dma_buffer);
+               if (ret)
+                       goto playback_alloc_err;
 
-       ret = regmap_write(drvdata->lpaif_map,
+               ret = regmap_write(drvdata->lpaif_map,
                        LPAIF_RDMACTL_REG(v, data->rdma_ch), 0);
-       if (ret) {
-               dev_err(soc_runtime->dev, "%s() error writing to rdmactl reg: %d\n",
+               if (ret) {
+                       dev_err(soc_runtime->dev,
+                               "%s() error writing to rdmactl reg: %d\n",
+                               __func__, ret);
+                       goto capture_alloc_err;
+               }
+       }
+
+       csubstream = pcm->streams[SNDRV_PCM_STREAM_CAPTURE].substream;
+       if (csubstream) {
+               if (v->alloc_dma_channel)
+                       data->wrdma_ch = v->alloc_dma_channel(drvdata,
+                                               SNDRV_PCM_STREAM_CAPTURE);
+
+               if (IS_ERR_VALUE(data->wrdma_ch))
+                       goto capture_alloc_err;
+
+               drvdata->substream[data->wrdma_ch] = csubstream;
+
+               ret = snd_dma_alloc_pages(SNDRV_DMA_TYPE_DEV,
+                                       soc_runtime->platform->dev,
+                                       size, &csubstream->dma_buffer);
+               if (ret)
+                       goto capture_alloc_err;
+
+               ret = regmap_write(drvdata->lpaif_map,
+                       LPAIF_WRDMACTL_REG(v, data->wrdma_ch), 0);
+               if (ret) {
+                       dev_err(soc_runtime->dev,
+                               "%s() error writing to wrdmactl reg: %d\n",
                                __func__, ret);
-               goto err_buf;
+                       goto capture_reg_err;
+               }
        }
 
        return 0;
 
-err_buf:
-       lpass_platform_free_buffer(substream, soc_runtime);
+capture_reg_err:
+       if (csubstream)
+               snd_dma_free_pages(&csubstream->dma_buffer);
+
+capture_alloc_err:
+       if (psubstream)
+               snd_dma_free_pages(&psubstream->dma_buffer);
+
+ playback_alloc_err:
+       dev_err(soc_runtime->dev, "Cannot allocate buffer(s)\n");
+
        return ret;
 }
 
 static void lpass_platform_pcm_free(struct snd_pcm *pcm)
 {
-       struct snd_pcm_substream *substream =
-               pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
-       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
-       struct lpass_data *drvdata =
-               snd_soc_platform_get_drvdata(soc_runtime->platform);
-       struct lpass_pcm_data *data = snd_soc_pcm_get_drvdata(soc_runtime);
-       struct lpass_variant *v = drvdata->variant;
-
-       drvdata->substream[data->rdma_ch] = NULL;
-
-       if (v->free_dma_channel)
-               v->free_dma_channel(drvdata, data->rdma_ch);
-
-       lpass_platform_free_buffer(substream, soc_runtime);
+       struct snd_soc_pcm_runtime *rt;
+       struct lpass_data *drvdata;
+       struct lpass_pcm_data *data;
+       struct lpass_variant *v;
+       struct snd_pcm_substream *substream;
+       int ch, i;
+
+       for (i = 0; i < ARRAY_SIZE(pcm->streams); i++) {
+               substream = pcm->streams[i].substream;
+               if (substream) {
+                       rt = substream->private_data;
+                       data = snd_soc_pcm_get_drvdata(rt);
+                       drvdata = snd_soc_platform_get_drvdata(rt->platform);
+
+                       ch = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                               ? data->rdma_ch
+                               : data->wrdma_ch;
+                       v = drvdata->variant;
+                       drvdata->substream[ch] = NULL;
+                       if (v->free_dma_channel)
+                               v->free_dma_channel(drvdata, ch);
+
+                       snd_dma_free_pages(&substream->dma_buffer);
+                       substream->dma_buffer.area = NULL;
+                       substream->dma_buffer.addr = 0;
+               }
+       }
 }
 
 static struct snd_soc_platform_driver lpass_platform_driver = {
index 0b63e2e5bcc925c3054096e530d454da838d5b1d..30714ad1e138d405a89643351c4ff30e5b5e2c1f 100644 (file)
@@ -50,7 +50,7 @@ struct lpass_data {
        struct lpass_variant *variant;
 
        /* bit map to keep track of static channel allocations */
-       unsigned long rdma_ch_bit_map;
+       unsigned long dma_ch_bit_map;
 
        /* used it for handling interrupt per dma channel */
        struct snd_pcm_substream *substream[LPASS_MAX_DMA_CHANNELS];
@@ -71,16 +71,20 @@ struct lpass_variant {
        u32     rdma_reg_base;
        u32     rdma_reg_stride;
        u32     rdma_channels;
+       u32     wrdma_reg_base;
+       u32     wrdma_reg_stride;
+       u32     wrdma_channels;
 
        /**
         * on SOCs like APQ8016 the channel control bits start
         * at different offset to ipq806x
         **/
-       u32     rdmactl_audif_start;
+       u32     dmactl_audif_start;
+       u32     wrdma_channel_start;
        /* SOC specific intialization like clocks */
        int (*init)(struct platform_device *pdev);
        int (*exit)(struct platform_device *pdev);
-       int (*alloc_dma_channel)(struct lpass_data *data);
+       int (*alloc_dma_channel)(struct lpass_data *data, int direction);
        int (*free_dma_channel)(struct lpass_data *data, int ch);
 
        /* SOC specific dais */
index 6561c4cc2eddbac035cec09ddd51dead9d3d8c22..2f8e20416bd3aa08bf9178949c010905472a2414 100644 (file)
@@ -440,11 +440,21 @@ static bool rockchip_i2s_precious_reg(struct device *dev, unsigned int reg)
        }
 }
 
+static const struct reg_default rockchip_i2s_reg_defaults[] = {
+       {0x00, 0x0000000f},
+       {0x04, 0x0000000f},
+       {0x08, 0x00071f1f},
+       {0x10, 0x001f0000},
+       {0x14, 0x01f00000},
+};
+
 static const struct regmap_config rockchip_i2s_regmap_config = {
        .reg_bits = 32,
        .reg_stride = 4,
        .val_bits = 32,
        .max_register = I2S_RXDR,
+       .reg_defaults = rockchip_i2s_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(rockchip_i2s_reg_defaults),
        .writeable_reg = rockchip_i2s_wr_reg,
        .readable_reg = rockchip_i2s_rd_reg,
        .volatile_reg = rockchip_i2s_volatile_reg,
@@ -575,6 +585,9 @@ static int rockchip_i2s_remove(struct platform_device *pdev)
 
 static const struct of_device_id rockchip_i2s_match[] = {
        { .compatible = "rockchip,rk3066-i2s", },
+       { .compatible = "rockchip,rk3188-i2s", },
+       { .compatible = "rockchip,rk3288-i2s", },
+       { .compatible = "rockchip,rk3399-i2s", },
        {},
 };
 
index 5a806da89f42b49967ea52d15eac0f3348b7b873..100781e37848a7b7c716cde4ddae69501867150c 100644 (file)
@@ -28,6 +28,7 @@ enum rk_spdif_type {
        RK_SPDIF_RK3066,
        RK_SPDIF_RK3188,
        RK_SPDIF_RK3288,
+       RK_SPDIF_RK3366,
 };
 
 #define RK3288_GRF_SOC_CON2 0x24c
@@ -45,16 +46,22 @@ struct rk_spdif_dev {
 
 static const struct of_device_id rk_spdif_match[] = {
        { .compatible = "rockchip,rk3066-spdif",
-         .data = (void *) RK_SPDIF_RK3066 },
+         .data = (void *)RK_SPDIF_RK3066 },
        { .compatible = "rockchip,rk3188-spdif",
-         .data = (void *) RK_SPDIF_RK3188 },
+         .data = (void *)RK_SPDIF_RK3188 },
        { .compatible = "rockchip,rk3288-spdif",
-         .data = (void *) RK_SPDIF_RK3288 },
+         .data = (void *)RK_SPDIF_RK3288 },
+       { .compatible = "rockchip,rk3366-spdif",
+         .data = (void *)RK_SPDIF_RK3366 },
+       { .compatible = "rockchip,rk3368-spdif",
+         .data = (void *)RK_SPDIF_RK3366 },
+       { .compatible = "rockchip,rk3399-spdif",
+         .data = (void *)RK_SPDIF_RK3366 },
        {},
 };
 MODULE_DEVICE_TABLE(of, rk_spdif_match);
 
-static int rk_spdif_runtime_suspend(struct device *dev)
+static int __maybe_unused rk_spdif_runtime_suspend(struct device *dev)
 {
        struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
 
@@ -64,7 +71,7 @@ static int rk_spdif_runtime_suspend(struct device *dev)
        return 0;
 }
 
-static int rk_spdif_runtime_resume(struct device *dev)
+static int __maybe_unused rk_spdif_runtime_resume(struct device *dev)
 {
        struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
        int ret;
index df65c5b494b17aad482c7821586b6ed6832bf9f6..b6ab3fc5789e89a92b937e1b2c95a1c6fa2c0063 100644 (file)
@@ -709,7 +709,7 @@ static int s3c2412_i2s_resume(struct snd_soc_dai *dai)
 #endif
 
 int s3c_i2sv2_register_component(struct device *dev, int id,
-                          struct snd_soc_component_driver *cmp_drv,
+                          const struct snd_soc_component_driver *cmp_drv,
                           struct snd_soc_dai_driver *dai_drv)
 {
        struct snd_soc_dai_ops *ops = (struct snd_soc_dai_ops *)dai_drv->ops;
index 90abab364b495fd18d11ed421a37bc123e3fa45e..d0684145ed1fd50967c1ce27075af3636aec15aa 100644 (file)
@@ -101,7 +101,7 @@ extern int s3c_i2sv2_probe(struct snd_soc_dai *dai,
  * soc core.
  */
 extern int s3c_i2sv2_register_component(struct device *dev, int id,
-                                       struct snd_soc_component_driver *cmp_drv,
+                                       const struct snd_soc_component_driver *cmp_drv,
                                        struct snd_soc_dai_driver *dai_drv);
 
 #endif /* __SND_SOC_S3C24XX_S3C_I2SV2_I2S_H */
index 6d3ef366d53692c222b311662a83feb6a5b6d8e4..606399de684d72c26550bdca02d36b07cd7015d1 100644 (file)
@@ -90,6 +90,108 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
        return (0x6 + ws) << 8;
 }
 
+static void __rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
+                                      struct rsnd_dai_stream *io,
+                                      unsigned int target_rate,
+                                      unsigned int *target_val,
+                                      unsigned int *target_en)
+{
+       struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       int idx, sel, div, step;
+       unsigned int val, en;
+       unsigned int min, diff;
+       unsigned int sel_rate[] = {
+               clk_get_rate(adg->clk[CLKA]),   /* 0000: CLKA */
+               clk_get_rate(adg->clk[CLKB]),   /* 0001: CLKB */
+               clk_get_rate(adg->clk[CLKC]),   /* 0010: CLKC */
+               adg->rbga_rate_for_441khz,      /* 0011: RBGA */
+               adg->rbgb_rate_for_48khz,       /* 0100: RBGB */
+       };
+
+       min = ~0;
+       val = 0;
+       en = 0;
+       for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
+               idx = 0;
+               step = 2;
+
+               if (!sel_rate[sel])
+                       continue;
+
+               for (div = 2; div <= 98304; div += step) {
+                       diff = abs(target_rate - sel_rate[sel] / div);
+                       if (min > diff) {
+                               val = (sel << 8) | idx;
+                               min = diff;
+                               en = 1 << (sel + 1); /* fixme */
+                       }
+
+                       /*
+                        * step of 0_0000 / 0_0001 / 0_1101
+                        * are out of order
+                        */
+                       if ((idx > 2) && (idx % 2))
+                               step *= 2;
+                       if (idx == 0x1c) {
+                               div += step;
+                               step *= 2;
+                       }
+                       idx++;
+               }
+       }
+
+       if (min == ~0) {
+               dev_err(dev, "no Input clock\n");
+               return;
+       }
+
+       *target_val = val;
+       if (target_en)
+               *target_en = en;
+}
+
+static void rsnd_adg_get_timesel_ratio(struct rsnd_priv *priv,
+                                      struct rsnd_dai_stream *io,
+                                      unsigned int in_rate,
+                                      unsigned int out_rate,
+                                      u32 *in, u32 *out, u32 *en)
+{
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+       unsigned int target_rate;
+       u32 *target_val;
+       u32 _in;
+       u32 _out;
+       u32 _en;
+
+       /* default = SSI WS */
+       _in =
+       _out = rsnd_adg_ssi_ws_timing_gen2(io);
+
+       target_rate = 0;
+       target_val = NULL;
+       _en = 0;
+       if (runtime->rate != in_rate) {
+               target_rate = out_rate;
+               target_val  = &_out;
+       } else if (runtime->rate != out_rate) {
+               target_rate = in_rate;
+               target_val  = &_in;
+       }
+
+       if (target_rate)
+               __rsnd_adg_get_timesel_ratio(priv, io,
+                                            target_rate,
+                                            target_val, &_en);
+
+       if (in)
+               *in = _in;
+       if (out)
+               *out = _out;
+       if (en)
+               *en = _en;
+}
+
 int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
                                 struct rsnd_dai_stream *io)
 {
@@ -100,7 +202,10 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
        int shift = (id % 2) ? 16 : 0;
        u32 mask, val;
 
-       val = rsnd_adg_ssi_ws_timing_gen2(io);
+       rsnd_adg_get_timesel_ratio(priv, io,
+                                  rsnd_src_get_in_rate(priv, io),
+                                  rsnd_src_get_out_rate(priv, io),
+                                  NULL, &val, NULL);
 
        val  = val      << shift;
        mask = 0xffff   << shift;
@@ -110,25 +215,24 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *cmd_mod,
        return 0;
 }
 
-static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
-                                       struct rsnd_dai_stream *io,
-                                       u32 timsel)
+int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
+                                 struct rsnd_dai_stream *io,
+                                 unsigned int in_rate,
+                                 unsigned int out_rate)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
        struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
        struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
-       int is_play = rsnd_io_is_play(io);
+       u32 in, out;
+       u32 mask, en;
        int id = rsnd_mod_id(src_mod);
        int shift = (id % 2) ? 16 : 0;
-       u32 mask, ws;
-       u32 in, out;
 
        rsnd_mod_confirm_src(src_mod);
 
-       ws = rsnd_adg_ssi_ws_timing_gen2(io);
-
-       in  = (is_play) ? timsel : ws;
-       out = (is_play) ? ws     : timsel;
+       rsnd_adg_get_timesel_ratio(priv, io,
+                                  in_rate, out_rate,
+                                  &in, &out, &en);
 
        in   = in       << shift;
        out  = out      << shift;
@@ -157,91 +261,12 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
                break;
        }
 
-       return 0;
-}
-
-int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod,
-                                 struct rsnd_dai_stream *io,
-                                 unsigned int src_rate,
-                                 unsigned int dst_rate)
-{
-       struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
-       struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
-       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
-       struct device *dev = rsnd_priv_to_dev(priv);
-       int idx, sel, div, step, ret;
-       u32 val, en;
-       unsigned int min, diff;
-       unsigned int sel_rate [] = {
-               clk_get_rate(adg->clk[CLKA]),   /* 0000: CLKA */
-               clk_get_rate(adg->clk[CLKB]),   /* 0001: CLKB */
-               clk_get_rate(adg->clk[CLKC]),   /* 0010: CLKC */
-               adg->rbga_rate_for_441khz,      /* 0011: RBGA */
-               adg->rbgb_rate_for_48khz,       /* 0100: RBGB */
-       };
-
-       rsnd_mod_confirm_src(src_mod);
-
-       min = ~0;
-       val = 0;
-       en = 0;
-       for (sel = 0; sel < ARRAY_SIZE(sel_rate); sel++) {
-               idx = 0;
-               step = 2;
-
-               if (!sel_rate[sel])
-                       continue;
-
-               for (div = 2; div <= 98304; div += step) {
-                       diff = abs(src_rate - sel_rate[sel] / div);
-                       if (min > diff) {
-                               val = (sel << 8) | idx;
-                               min = diff;
-                               en = 1 << (sel + 1); /* fixme */
-                       }
-
-                       /*
-                        * step of 0_0000 / 0_0001 / 0_1101
-                        * are out of order
-                        */
-                       if ((idx > 2) && (idx % 2))
-                               step *= 2;
-                       if (idx == 0x1c) {
-                               div += step;
-                               step *= 2;
-                       }
-                       idx++;
-               }
-       }
-
-       if (min == ~0) {
-               dev_err(dev, "no Input clock\n");
-               return -EIO;
-       }
-
-       ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
-       if (ret < 0) {
-               dev_err(dev, "timsel error\n");
-               return ret;
-       }
-
-       rsnd_mod_bset(adg_mod, DIV_EN, en, en);
-
-       dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate);
+       if (en)
+               rsnd_mod_bset(adg_mod, DIV_EN, en, en);
 
        return 0;
 }
 
-int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod,
-                                    struct rsnd_dai_stream *io)
-{
-       u32 val = rsnd_adg_ssi_ws_timing_gen2(io);
-
-       rsnd_mod_confirm_src(src_mod);
-
-       return rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
-}
-
 static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
@@ -518,13 +543,8 @@ int rsnd_adg_probe(struct rsnd_priv *priv)
                return -ENOMEM;
        }
 
-       /*
-        * ADG is special module.
-        * Use ADG mod without rsnd_mod_init() to make debug easy
-        * for rsnd_write/rsnd_read
-        */
-       adg->mod.ops = &adg_ops;
-       adg->mod.priv = priv;
+       rsnd_mod_init(priv, &adg->mod, &adg_ops,
+                     NULL, NULL, 0, 0);
 
        rsnd_adg_get_clkin(priv, adg);
        rsnd_adg_get_clkout(priv, adg);
index cd1f064e63c4344af914e2629b92e0d326f47376..abb5eaac854a9b9c47027e278cd5204c18aceace 100644 (file)
@@ -29,7 +29,6 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
 {
        struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
        struct rsnd_mod *mix = rsnd_io_to_mod_mix(io);
-       struct rsnd_mod *src = rsnd_io_to_mod_src(io);
        struct device *dev = rsnd_priv_to_dev(priv);
        u32 data;
 
@@ -38,6 +37,8 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
 
        if (mix) {
                struct rsnd_dai *rdai;
+               struct rsnd_mod *src;
+               struct rsnd_dai_stream *tio;
                int i;
                u32 path[] = {
                        [0] = 0,
@@ -55,16 +56,20 @@ static int rsnd_cmd_init(struct rsnd_mod *mod,
                 */
                data = 0;
                for_each_rsnd_dai(rdai, priv, i) {
-                       io = &rdai->playback;
-                       if (mix == rsnd_io_to_mod_mix(io))
+                       tio = &rdai->playback;
+                       src = rsnd_io_to_mod_src(tio);
+                       if (mix == rsnd_io_to_mod_mix(tio))
                                data |= path[rsnd_mod_id(src)];
 
-                       io = &rdai->capture;
-                       if (mix == rsnd_io_to_mod_mix(io))
+                       tio = &rdai->capture;
+                       src = rsnd_io_to_mod_src(tio);
+                       if (mix == rsnd_io_to_mod_mix(tio))
                                data |= path[rsnd_mod_id(src)];
                }
 
        } else {
+               struct rsnd_mod *src = rsnd_io_to_mod_src(io);
+
                u32 path[] = {
                        [0] = 0x30000,
                        [1] = 0x30001,
@@ -152,7 +157,8 @@ int rsnd_cmd_probe(struct rsnd_priv *priv)
 
        for_each_rsnd_cmd(cmd, priv, i) {
                ret = rsnd_mod_init(priv, rsnd_mod_get(cmd),
-                                   &rsnd_cmd_ops, NULL, RSND_MOD_CMD, i);
+                                   &rsnd_cmd_ops, NULL,
+                                   rsnd_mod_get_status, RSND_MOD_CMD, i);
                if (ret)
                        return ret;
        }
index 02b4b085b8d77f57398a13c35214a49445d1ed77..3351a701c60ee3525b1b441908d7f8bcb628e8f0 100644 (file)
@@ -138,12 +138,22 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
        return mod->ops->dma_req(io, mod);
 }
 
+u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io,
+                        struct rsnd_mod *mod,
+                        enum rsnd_mod_type type)
+{
+       return &mod->status;
+}
+
 int rsnd_mod_init(struct rsnd_priv *priv,
                  struct rsnd_mod *mod,
-                  struct rsnd_mod_ops *ops,
-                  struct clk *clk,
-                  enum rsnd_mod_type type,
-                  int id)
+                 struct rsnd_mod_ops *ops,
+                 struct clk *clk,
+                 u32* (*get_status)(struct rsnd_dai_stream *io,
+                                    struct rsnd_mod *mod,
+                                    enum rsnd_mod_type type),
+                 enum rsnd_mod_type type,
+                 int id)
 {
        int ret = clk_prepare(clk);
 
@@ -155,6 +165,7 @@ int rsnd_mod_init(struct rsnd_priv *priv,
        mod->type       = type;
        mod->clk        = clk;
        mod->priv       = priv;
+       mod->get_status = get_status;
 
        return ret;
 }
@@ -163,6 +174,7 @@ void rsnd_mod_quit(struct rsnd_mod *mod)
 {
        if (mod->clk)
                clk_unprepare(mod->clk);
+       mod->clk = NULL;
 }
 
 void rsnd_mod_interrupt(struct rsnd_mod *mod,
@@ -212,13 +224,36 @@ int rsnd_get_slot_num(struct rsnd_dai_stream *io)
        return rdai->slots_num;
 }
 
-int rsnd_get_slot_width(struct rsnd_dai_stream *io)
+int rsnd_runtime_channel_original(struct rsnd_dai_stream *io)
 {
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
-       int chan = runtime->channels;
 
-       /* Multi channel Mode */
-       if (rsnd_ssi_multi_slaves(io))
+       return runtime->channels;
+}
+
+int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io)
+{
+       int chan = rsnd_runtime_channel_original(io);
+       struct rsnd_mod *ctu_mod = rsnd_io_to_mod_ctu(io);
+
+       if (ctu_mod) {
+               u32 converted_chan = rsnd_ctu_converted_channel(ctu_mod);
+
+               if (converted_chan)
+                       return converted_chan;
+       }
+
+       return chan;
+}
+
+int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io)
+{
+       int chan = rsnd_io_is_play(io) ?
+               rsnd_runtime_channel_after_ctu(io) :
+               rsnd_runtime_channel_original(io);
+
+       /* Use Multi SSI */
+       if (rsnd_runtime_is_ssi_multi(io))
                chan /= rsnd_get_slot_num(io);
 
        /* TDM Extend Mode needs 8ch */
@@ -228,6 +263,21 @@ int rsnd_get_slot_width(struct rsnd_dai_stream *io)
        return chan;
 }
 
+int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io)
+{
+       int slots = rsnd_get_slot_num(io);
+       int chan = rsnd_io_is_play(io) ?
+               rsnd_runtime_channel_after_ctu(io) :
+               rsnd_runtime_channel_original(io);
+
+       return (chan >= 6) && (slots > 1);
+}
+
+int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io)
+{
+       return rsnd_runtime_channel_for_ssi(io) >= 6;
+}
+
 /*
  *     ADINR function
  */
@@ -249,29 +299,6 @@ u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
        return 0;
 }
 
-u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
-{
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
-       struct device *dev = rsnd_priv_to_dev(priv);
-       u32 chan = runtime->channels;
-
-       switch (chan) {
-       case 1:
-       case 2:
-       case 4:
-       case 6:
-       case 8:
-               break;
-       default:
-               dev_warn(dev, "not supported channel\n");
-               chan = 0;
-               break;
-       }
-
-       return chan;
-}
-
 /*
  *     DALIGN function
  */
@@ -324,31 +351,73 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);         \
        struct rsnd_mod *mod = (io)->mod[idx];                  \
        struct device *dev = rsnd_priv_to_dev(priv);            \
-       u32 *status = (io)->mod_status + idx;                   \
+       u32 *status = mod->get_status(io, mod, idx);                    \
        u32 mask = 0xF << __rsnd_mod_shift_##func;                      \
        u8 val  = (*status >> __rsnd_mod_shift_##func) & 0xF;           \
        u8 add  = ((val + __rsnd_mod_add_##func) & 0xF);                \
        int ret = 0;                                                    \
        int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
-       *status = (*status & ~mask) +                                   \
-               (add << __rsnd_mod_shift_##func);                       \
+       if (add == 0xF)                                                 \
+               call = 0;                                               \
+       else                                                            \
+               *status = (*status & ~mask) +                           \
+                       (add << __rsnd_mod_shift_##func);               \
        dev_dbg(dev, "%s[%d]\t0x%08x %s\n",                             \
                rsnd_mod_name(mod), rsnd_mod_id(mod),                   \
                *status, call ? #func : "");                            \
        if (call)                                                       \
                ret = (mod)->ops->func(mod, io, param);                 \
+       if (ret)                                                        \
+               dev_dbg(dev, "%s[%d] : rsnd_mod_call error %d\n",       \
+                       rsnd_mod_name(mod), rsnd_mod_id(mod), ret);     \
        ret;                                                            \
 })
 
+static enum rsnd_mod_type rsnd_mod_sequence[][RSND_MOD_MAX] = {
+       {
+               /* CAPTURE */
+               RSND_MOD_AUDMAPP,
+               RSND_MOD_AUDMA,
+               RSND_MOD_DVC,
+               RSND_MOD_MIX,
+               RSND_MOD_CTU,
+               RSND_MOD_CMD,
+               RSND_MOD_SRC,
+               RSND_MOD_SSIU,
+               RSND_MOD_SSIM3,
+               RSND_MOD_SSIM2,
+               RSND_MOD_SSIM1,
+               RSND_MOD_SSIP,
+               RSND_MOD_SSI,
+       }, {
+               /* PLAYBACK */
+               RSND_MOD_AUDMAPP,
+               RSND_MOD_AUDMA,
+               RSND_MOD_SSIM3,
+               RSND_MOD_SSIM2,
+               RSND_MOD_SSIM1,
+               RSND_MOD_SSIP,
+               RSND_MOD_SSI,
+               RSND_MOD_SSIU,
+               RSND_MOD_DVC,
+               RSND_MOD_MIX,
+               RSND_MOD_CTU,
+               RSND_MOD_CMD,
+               RSND_MOD_SRC,
+       },
+};
+
 #define rsnd_dai_call(fn, io, param...)                                \
 ({                                                             \
        struct rsnd_mod *mod;                                   \
+       int type, is_play = rsnd_io_is_play(io);                \
        int ret = 0, i;                                         \
        for (i = 0; i < RSND_MOD_MAX; i++) {                    \
-               mod = (io)->mod[i];                             \
+               type = rsnd_mod_sequence[is_play][i];           \
+               mod = (io)->mod[type];                          \
                if (!mod)                                       \
                        continue;                               \
-               ret |= rsnd_mod_call(i, io, fn, param);         \
+               ret |= rsnd_mod_call(type, io, fn, param);      \
        }                                                       \
        ret;                                                    \
 })
@@ -363,6 +432,9 @@ int rsnd_dai_connect(struct rsnd_mod *mod,
        if (!mod)
                return -EIO;
 
+       if (io->mod[type] == mod)
+               return 0;
+
        if (io->mod[type])
                return -EINVAL;
 
@@ -511,9 +583,16 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
                ret = rsnd_dai_call(start, io, priv);
                if (ret < 0)
                        goto dai_trigger_end;
+
+               ret = rsnd_dai_call(irq, io, priv, 1);
+               if (ret < 0)
+                       goto dai_trigger_end;
+
                break;
        case SNDRV_PCM_TRIGGER_STOP:
-               ret = rsnd_dai_call(stop, io, priv);
+               ret = rsnd_dai_call(irq, io, priv, 0);
+
+               ret |= rsnd_dai_call(stop, io, priv);
 
                ret |= rsnd_dai_call(quit, io, priv);
 
@@ -863,7 +942,7 @@ static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
                }
        }
 
-       if (change)
+       if (change && cfg->update)
                cfg->update(cfg->io, mod);
 
        return change;
@@ -923,7 +1002,7 @@ int rsnd_kctrl_new_m(struct rsnd_mod *mod,
                     int ch_size,
                     u32 max)
 {
-       if (ch_size > RSND_DVC_CHANNELS)
+       if (ch_size > RSND_MAX_CHANNELS)
                return -EINVAL;
 
        _cfg->cfg.max   = max;
@@ -1055,7 +1134,6 @@ static int rsnd_probe(struct platform_device *pdev)
        struct rsnd_priv *priv;
        struct device *dev = &pdev->dev;
        struct rsnd_dai *rdai;
-       const struct of_device_id *of_id = of_match_device(rsnd_of_match, dev);
        int (*probe_func[])(struct rsnd_priv *priv) = {
                rsnd_gen_probe,
                rsnd_dma_probe,
@@ -1081,7 +1159,7 @@ static int rsnd_probe(struct platform_device *pdev)
        }
 
        priv->pdev      = pdev;
-       priv->flags     = (unsigned long)of_id->data;
+       priv->flags     = (unsigned long)of_device_get_match_data(dev);
        spin_lock_init(&priv->lock);
 
        /*
index d53a225d19e9b724b2b326a32f5746c822e3c4b8..9dcc1f9db026f6b76d7849ff3108ac3335543116 100644 (file)
 #define CTU_NAME_SIZE  16
 #define CTU_NAME "ctu"
 
+/*
+ * User needs to setup CTU by amixer, and its settings are
+ * based on below registers
+ *
+ * CTUn_CPMDR : amixser set "CTU Pass"
+ * CTUn_SV0xR : amixser set "CTU SV0"
+ * CTUn_SV1xR : amixser set "CTU SV1"
+ * CTUn_SV2xR : amixser set "CTU SV2"
+ * CTUn_SV3xR : amixser set "CTU SV3"
+ *
+ * [CTU Pass]
+ * 0000: default
+ * 0001: Connect input data of channel 0
+ * 0010: Connect input data of channel 1
+ * 0011: Connect input data of channel 2
+ * 0100: Connect input data of channel 3
+ * 0101: Connect input data of channel 4
+ * 0110: Connect input data of channel 5
+ * 0111: Connect input data of channel 6
+ * 1000: Connect input data of channel 7
+ * 1001: Connect calculated data by scale values of matrix row 0
+ * 1010: Connect calculated data by scale values of matrix row 1
+ * 1011: Connect calculated data by scale values of matrix row 2
+ * 1100: Connect calculated data by scale values of matrix row 3
+ *
+ * [CTU SVx]
+ * [Output0] = [SV00, SV01, SV02, SV03, SV04, SV05, SV06, SV07]
+ * [Output1] = [SV10, SV11, SV12, SV13, SV14, SV15, SV16, SV17]
+ * [Output2] = [SV20, SV21, SV22, SV23, SV24, SV25, SV26, SV27]
+ * [Output3] = [SV30, SV31, SV32, SV33, SV34, SV35, SV36, SV37]
+ * [Output4] = [ 0,   0,    0,    0,    0,    0,    0,    0   ]
+ * [Output5] = [ 0,   0,    0,    0,    0,    0,    0,    0   ]
+ * [Output6] = [ 0,   0,    0,    0,    0,    0,    0,    0   ]
+ * [Output7] = [ 0,   0,    0,    0,    0,    0,    0,    0   ]
+ *
+ * [SVxx]
+ * Plus                                        Minus
+ * value       time            dB      value           time            dB
+ * -----------------------------------------------------------------------
+ * H'7F_FFFF   2               6       H'80_0000       2               6
+ * ...
+ * H'40_0000   1               0       H'C0_0000       1               0
+ * ...
+ * H'00_0001   2.38 x 10^-7    -132
+ * H'00_0000   0               Mute    H'FF_FFFF       2.38 x 10^-7    -132
+ *
+ *
+ * Ex) Input ch -> Output ch
+ *     1ch     ->  0ch
+ *     0ch     ->  1ch
+ *
+ *     amixer set "CTU Reset" on
+ *     amixer set "CTU Pass" 9,10
+ *     amixer set "CTU SV0" 0,4194304
+ *     amixer set "CTU SV1" 4194304,0
+ * or
+ *     amixer set "CTU Reset" on
+ *     amixer set "CTU Pass" 2,1
+ */
+
 struct rsnd_ctu {
        struct rsnd_mod mod;
+       struct rsnd_kctrl_cfg_m pass;
+       struct rsnd_kctrl_cfg_m sv0;
+       struct rsnd_kctrl_cfg_m sv1;
+       struct rsnd_kctrl_cfg_m sv2;
+       struct rsnd_kctrl_cfg_m sv3;
+       struct rsnd_kctrl_cfg_s reset;
+       int channels;
 };
 
 #define rsnd_ctu_nr(priv) ((priv)->ctu_nr)
@@ -23,12 +90,28 @@ struct rsnd_ctu {
                     ((pos) = (struct rsnd_ctu *)(priv)->ctu + i);      \
             i++)
 
+#define rsnd_mod_to_ctu(_mod)  \
+       container_of((_mod), struct rsnd_ctu, mod)
+
 #define rsnd_ctu_get(priv, id) ((struct rsnd_ctu *)(priv->ctu) + id)
-#define rsnd_ctu_initialize_lock(mod)  __rsnd_ctu_initialize_lock(mod, 1)
-#define rsnd_ctu_initialize_unlock(mod)        __rsnd_ctu_initialize_lock(mod, 0)
-static void __rsnd_ctu_initialize_lock(struct rsnd_mod *mod, u32 enable)
+
+static void rsnd_ctu_activation(struct rsnd_mod *mod)
+{
+       rsnd_mod_write(mod, CTU_SWRSR, 0);
+       rsnd_mod_write(mod, CTU_SWRSR, 1);
+}
+
+static void rsnd_ctu_halt(struct rsnd_mod *mod)
+{
+       rsnd_mod_write(mod, CTU_CTUIR, 1);
+       rsnd_mod_write(mod, CTU_SWRSR, 0);
+}
+
+int rsnd_ctu_converted_channel(struct rsnd_mod *mod)
 {
-       rsnd_mod_write(mod, CTU_CTUIR, enable);
+       struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+
+       return ctu->channels;
 }
 
 static int rsnd_ctu_probe_(struct rsnd_mod *mod,
@@ -38,17 +121,103 @@ static int rsnd_ctu_probe_(struct rsnd_mod *mod,
        return rsnd_cmd_attach(io, rsnd_mod_id(mod) / 4);
 }
 
+static void rsnd_ctu_value_init(struct rsnd_dai_stream *io,
+                              struct rsnd_mod *mod)
+{
+       struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+       u32 cpmdr = 0;
+       u32 scmdr = 0;
+       int i;
+
+       for (i = 0; i < RSND_MAX_CHANNELS; i++) {
+               u32 val = ctu->pass.val[i];
+
+               cpmdr |= val << (28 - (i * 4));
+
+               if ((val > 0x8) && (scmdr < (val - 0x8)))
+                       scmdr = val - 0x8;
+       }
+
+       rsnd_mod_write(mod, CTU_CTUIR, 1);
+
+       rsnd_mod_write(mod, CTU_ADINR, rsnd_runtime_channel_original(io));
+
+       rsnd_mod_write(mod, CTU_CPMDR, cpmdr);
+
+       rsnd_mod_write(mod, CTU_SCMDR, scmdr);
+
+       if (scmdr > 0) {
+               rsnd_mod_write(mod, CTU_SV00R, ctu->sv0.val[0]);
+               rsnd_mod_write(mod, CTU_SV01R, ctu->sv0.val[1]);
+               rsnd_mod_write(mod, CTU_SV02R, ctu->sv0.val[2]);
+               rsnd_mod_write(mod, CTU_SV03R, ctu->sv0.val[3]);
+               rsnd_mod_write(mod, CTU_SV04R, ctu->sv0.val[4]);
+               rsnd_mod_write(mod, CTU_SV05R, ctu->sv0.val[5]);
+               rsnd_mod_write(mod, CTU_SV06R, ctu->sv0.val[6]);
+               rsnd_mod_write(mod, CTU_SV07R, ctu->sv0.val[7]);
+       }
+       if (scmdr > 1) {
+               rsnd_mod_write(mod, CTU_SV10R, ctu->sv1.val[0]);
+               rsnd_mod_write(mod, CTU_SV11R, ctu->sv1.val[1]);
+               rsnd_mod_write(mod, CTU_SV12R, ctu->sv1.val[2]);
+               rsnd_mod_write(mod, CTU_SV13R, ctu->sv1.val[3]);
+               rsnd_mod_write(mod, CTU_SV14R, ctu->sv1.val[4]);
+               rsnd_mod_write(mod, CTU_SV15R, ctu->sv1.val[5]);
+               rsnd_mod_write(mod, CTU_SV16R, ctu->sv1.val[6]);
+               rsnd_mod_write(mod, CTU_SV17R, ctu->sv1.val[7]);
+       }
+       if (scmdr > 2) {
+               rsnd_mod_write(mod, CTU_SV20R, ctu->sv2.val[0]);
+               rsnd_mod_write(mod, CTU_SV21R, ctu->sv2.val[1]);
+               rsnd_mod_write(mod, CTU_SV22R, ctu->sv2.val[2]);
+               rsnd_mod_write(mod, CTU_SV23R, ctu->sv2.val[3]);
+               rsnd_mod_write(mod, CTU_SV24R, ctu->sv2.val[4]);
+               rsnd_mod_write(mod, CTU_SV25R, ctu->sv2.val[5]);
+               rsnd_mod_write(mod, CTU_SV26R, ctu->sv2.val[6]);
+               rsnd_mod_write(mod, CTU_SV27R, ctu->sv2.val[7]);
+       }
+       if (scmdr > 3) {
+               rsnd_mod_write(mod, CTU_SV30R, ctu->sv3.val[0]);
+               rsnd_mod_write(mod, CTU_SV31R, ctu->sv3.val[1]);
+               rsnd_mod_write(mod, CTU_SV32R, ctu->sv3.val[2]);
+               rsnd_mod_write(mod, CTU_SV33R, ctu->sv3.val[3]);
+               rsnd_mod_write(mod, CTU_SV34R, ctu->sv3.val[4]);
+               rsnd_mod_write(mod, CTU_SV35R, ctu->sv3.val[5]);
+               rsnd_mod_write(mod, CTU_SV36R, ctu->sv3.val[6]);
+               rsnd_mod_write(mod, CTU_SV37R, ctu->sv3.val[7]);
+       }
+
+       rsnd_mod_write(mod, CTU_CTUIR, 0);
+}
+
+static void rsnd_ctu_value_reset(struct rsnd_dai_stream *io,
+                                struct rsnd_mod *mod)
+{
+       struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+       int i;
+
+       if (!ctu->reset.val)
+               return;
+
+       for (i = 0; i < RSND_MAX_CHANNELS; i++) {
+               ctu->pass.val[i] = 0;
+               ctu->sv0.val[i] = 0;
+               ctu->sv1.val[i] = 0;
+               ctu->sv2.val[i] = 0;
+               ctu->sv3.val[i] = 0;
+       }
+       ctu->reset.val = 0;
+}
+
 static int rsnd_ctu_init(struct rsnd_mod *mod,
                         struct rsnd_dai_stream *io,
                         struct rsnd_priv *priv)
 {
        rsnd_mod_power_on(mod);
 
-       rsnd_ctu_initialize_lock(mod);
-
-       rsnd_mod_write(mod, CTU_ADINR, rsnd_get_adinr_chan(mod, io));
+       rsnd_ctu_activation(mod);
 
-       rsnd_ctu_initialize_unlock(mod);
+       rsnd_ctu_value_init(io, mod);
 
        return 0;
 }
@@ -57,16 +226,110 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod,
                         struct rsnd_dai_stream *io,
                         struct rsnd_priv *priv)
 {
+       rsnd_ctu_halt(mod);
+
        rsnd_mod_power_off(mod);
 
        return 0;
 }
 
+static int rsnd_ctu_hw_params(struct rsnd_mod *mod,
+                             struct rsnd_dai_stream *io,
+                             struct snd_pcm_substream *substream,
+                             struct snd_pcm_hw_params *fe_params)
+{
+       struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+       struct snd_soc_pcm_runtime *fe = substream->private_data;
+
+       /*
+        * CTU assumes that it is used under DPCM if user want to use
+        * channel transfer. Then, CTU should be FE.
+        * And then, this function will be called *after* BE settings.
+        * this means, each BE already has fixuped hw_params.
+        * see
+        *      dpcm_fe_dai_hw_params()
+        *      dpcm_be_dai_hw_params()
+        */
+       ctu->channels = 0;
+       if (fe->dai_link->dynamic) {
+               struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+               struct device *dev = rsnd_priv_to_dev(priv);
+               struct snd_soc_dpcm *dpcm;
+               struct snd_pcm_hw_params *be_params;
+               int stream = substream->stream;
+
+               list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be) {
+                       be_params = &dpcm->hw_params;
+                       if (params_channels(fe_params) != params_channels(be_params))
+                               ctu->channels = params_channels(be_params);
+               }
+
+               dev_dbg(dev, "CTU convert channels %d\n", ctu->channels);
+       }
+
+       return 0;
+}
+
+static int rsnd_ctu_pcm_new(struct rsnd_mod *mod,
+                           struct rsnd_dai_stream *io,
+                           struct snd_soc_pcm_runtime *rtd)
+{
+       struct rsnd_ctu *ctu = rsnd_mod_to_ctu(mod);
+       int ret;
+
+       /* CTU Pass */
+       ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU Pass",
+                              NULL,
+                              &ctu->pass, RSND_MAX_CHANNELS,
+                              0xC);
+
+       /* ROW0 */
+       ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV0",
+                              NULL,
+                              &ctu->sv0, RSND_MAX_CHANNELS,
+                              0x00FFFFFF);
+       if (ret < 0)
+               return ret;
+
+       /* ROW1 */
+       ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV1",
+                              NULL,
+                              &ctu->sv1, RSND_MAX_CHANNELS,
+                              0x00FFFFFF);
+       if (ret < 0)
+               return ret;
+
+       /* ROW2 */
+       ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV2",
+                              NULL,
+                              &ctu->sv2, RSND_MAX_CHANNELS,
+                              0x00FFFFFF);
+       if (ret < 0)
+               return ret;
+
+       /* ROW3 */
+       ret = rsnd_kctrl_new_m(mod, io, rtd, "CTU SV3",
+                              NULL,
+                              &ctu->sv3, RSND_MAX_CHANNELS,
+                              0x00FFFFFF);
+       if (ret < 0)
+               return ret;
+
+       /* Reset */
+       ret = rsnd_kctrl_new_s(mod, io, rtd, "CTU Reset",
+                              rsnd_ctu_value_reset,
+                              &ctu->reset, 1);
+
+       return ret;
+}
+
 static struct rsnd_mod_ops rsnd_ctu_ops = {
        .name           = CTU_NAME,
        .probe          = rsnd_ctu_probe_,
        .init           = rsnd_ctu_init,
        .quit           = rsnd_ctu_quit,
+       .hw_params      = rsnd_ctu_hw_params,
+       .pcm_new        = rsnd_ctu_pcm_new,
 };
 
 struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
@@ -129,7 +392,7 @@ int rsnd_ctu_probe(struct rsnd_priv *priv)
                }
 
                ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
-                                   clk, RSND_MOD_CTU, i);
+                                   clk, rsnd_mod_get_status, RSND_MOD_CTU, i);
                if (ret)
                        goto rsnd_ctu_probe_done;
 
index 418e6fdd06a338b1aac03d585e1a353bd21bfc26..7658e8fd7bdce5032df9210b5662048a88ebf329 100644 (file)
@@ -622,15 +622,13 @@ static void rsnd_dma_of_path(struct rsnd_mod *this,
        }
 }
 
-struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
-                                struct rsnd_mod *mod, int id)
+int rsnd_dma_attach(struct rsnd_dai_stream *io, struct rsnd_mod *mod,
+                   struct rsnd_mod **dma_mod, int id)
 {
-       struct rsnd_mod *dma_mod;
        struct rsnd_mod *mod_from = NULL;
        struct rsnd_mod *mod_to = NULL;
        struct rsnd_priv *priv = rsnd_io_to_priv(io);
        struct rsnd_dma_ctrl *dmac = rsnd_priv_to_dmac(priv);
-       struct rsnd_dma *dma;
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_mod_ops *ops;
        enum rsnd_mod_type type;
@@ -646,17 +644,10 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
         *      rsnd_rdai_continuance_probe()
         */
        if (!dmac)
-               return ERR_PTR(-EAGAIN);
-
-       dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
-       if (!dma)
-               return ERR_PTR(-ENOMEM);
+               return -EAGAIN;
 
        rsnd_dma_of_path(mod, io, is_play, &mod_from, &mod_to);
 
-       dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
-       dma->dst_addr = rsnd_dma_addr(io, mod_to,   is_play, 0);
-
        /* for Gen2 */
        if (mod_from && mod_to) {
                ops     = &rsnd_dmapp_ops;
@@ -678,27 +669,38 @@ struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
                type    = RSND_MOD_AUDMA;
        }
 
-       dma_mod = rsnd_mod_get(dma);
+       if (!(*dma_mod)) {
+               struct rsnd_dma *dma;
 
-       ret = rsnd_mod_init(priv, dma_mod,
-                           ops, NULL, type, dma_id);
-       if (ret < 0)
-               return ERR_PTR(ret);
+               dma = devm_kzalloc(dev, sizeof(*dma), GFP_KERNEL);
+               if (!dma)
+                       return -ENOMEM;
 
-       dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
-               rsnd_mod_name(dma_mod), rsnd_mod_id(dma_mod),
-               rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
-               rsnd_mod_name(mod_to),   rsnd_mod_id(mod_to));
+               *dma_mod = rsnd_mod_get(dma);
 
-       ret = attach(io, dma, id, mod_from, mod_to);
-       if (ret < 0)
-               return ERR_PTR(ret);
+               dma->src_addr = rsnd_dma_addr(io, mod_from, is_play, 1);
+               dma->dst_addr = rsnd_dma_addr(io, mod_to,   is_play, 0);
+
+               ret = rsnd_mod_init(priv, *dma_mod, ops, NULL,
+                                   rsnd_mod_get_status, type, dma_id);
+               if (ret < 0)
+                       return ret;
 
-       ret = rsnd_dai_connect(dma_mod, io, type);
+               dev_dbg(dev, "%s[%d] %s[%d] -> %s[%d]\n",
+                       rsnd_mod_name(*dma_mod), rsnd_mod_id(*dma_mod),
+                       rsnd_mod_name(mod_from), rsnd_mod_id(mod_from),
+                       rsnd_mod_name(mod_to),   rsnd_mod_id(mod_to));
+
+               ret = attach(io, dma, id, mod_from, mod_to);
+               if (ret < 0)
+                       return ret;
+       }
+
+       ret = rsnd_dai_connect(*dma_mod, io, type);
        if (ret < 0)
-               return ERR_PTR(ret);
+               return ret;
 
-       return rsnd_mod_get(dma);
+       return 0;
 }
 
 int rsnd_dma_probe(struct rsnd_priv *priv)
index d45ffe496397f84b4f68adc3377699f2e94e2308..02d971f69eff78731df462937bcc69a50149dff3 100644 (file)
@@ -8,6 +8,29 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+
+/*
+ * Playback Volume
+ *     amixer set "DVC Out" 100%
+ *
+ * Capture Volume
+ *     amixer set "DVC In" 100%
+ *
+ * Playback Mute
+ *     amixer set "DVC Out Mute" on
+ *
+ * Capture Mute
+ *     amixer set "DVC In Mute" on
+ *
+ * Volume Ramp
+ *     amixer set "DVC Out Ramp Up Rate"   "0.125 dB/64 steps"
+ *     amixer set "DVC Out Ramp Down Rate" "0.125 dB/512 steps"
+ *     amixer set "DVC Out Ramp" on
+ *     aplay xxx.wav &
+ *     amixer set "DVC Out"  80%  // Volume Down
+ *     amixer set "DVC Out" 100%  // Volume Up
+ */
+
 #include "rsnd.h"
 
 #define RSND_DVC_NAME_SIZE     16
@@ -83,15 +106,15 @@ static void rsnd_dvc_volume_parameter(struct rsnd_dai_stream *io,
                                              struct rsnd_mod *mod)
 {
        struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
-       u32 val[RSND_DVC_CHANNELS];
+       u32 val[RSND_MAX_CHANNELS];
        int i;
 
        /* Enable Ramp */
        if (dvc->ren.val)
-               for (i = 0; i < RSND_DVC_CHANNELS; i++)
+               for (i = 0; i < RSND_MAX_CHANNELS; i++)
                        val[i] = dvc->volume.cfg.max;
        else
-               for (i = 0; i < RSND_DVC_CHANNELS; i++)
+               for (i = 0; i < RSND_MAX_CHANNELS; i++)
                        val[i] = dvc->volume.val[i];
 
        /* Enable Digital Volume */
@@ -116,7 +139,7 @@ static void rsnd_dvc_volume_init(struct rsnd_dai_stream *io,
        u32 vrdbr = 0;
 
        adinr = rsnd_get_adinr_bit(mod, io) |
-               rsnd_get_adinr_chan(mod, io);
+               rsnd_runtime_channel_after_ctu(io);
 
        /* Enable Digital Volume, Zero Cross Mute Mode */
        dvucr |= 0x101;
@@ -373,7 +396,7 @@ int rsnd_dvc_probe(struct rsnd_priv *priv)
                }
 
                ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
-                             clk, RSND_MOD_DVC, i);
+                                   clk, rsnd_mod_get_status, RSND_MOD_DVC, i);
                if (ret)
                        goto rsnd_dvc_probe_done;
 
index ea24247eba73c2fffc8bfdb4c56f2c6d7c9268c5..46c0ba7b6414944ad6d5887735ec7ccce93c80c5 100644 (file)
@@ -101,23 +101,6 @@ void rsnd_write(struct rsnd_priv *priv,
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
 
-       if (!rsnd_is_accessible_reg(priv, gen, reg))
-               return;
-
-       regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
-
-       dev_dbg(dev, "w %s[%d] - %-18s (%4d) : %08x\n",
-               rsnd_mod_name(mod), rsnd_mod_id(mod),
-               rsnd_reg_name(gen, reg), reg, data);
-}
-
-void rsnd_force_write(struct rsnd_priv *priv,
-                     struct rsnd_mod *mod,
-                     enum rsnd_reg reg, u32 data)
-{
-       struct device *dev = rsnd_priv_to_dev(priv);
-       struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
-
        if (!rsnd_is_accessible_reg(priv, gen, reg))
                return;
 
@@ -137,8 +120,8 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
        if (!rsnd_is_accessible_reg(priv, gen, reg))
                return;
 
-       regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
-                                 mask, data);
+       regmap_fields_force_update_bits(gen->regs[reg],
+                                       rsnd_mod_id(mod), mask, data);
 
        dev_dbg(dev, "b %s[%d] - %-18s (%4d) : %08x/%08x\n",
                rsnd_mod_name(mod), rsnd_mod_id(mod),
@@ -260,8 +243,43 @@ static int rsnd_gen2_probe(struct rsnd_priv *priv)
                RSND_GEN_M_REG(SRC_SRCCR,       0x224,  0x40),
                RSND_GEN_M_REG(SRC_BSDSR,       0x22c,  0x40),
                RSND_GEN_M_REG(SRC_BSISR,       0x238,  0x40),
+               RSND_GEN_M_REG(CTU_SWRSR,       0x500,  0x100),
                RSND_GEN_M_REG(CTU_CTUIR,       0x504,  0x100),
                RSND_GEN_M_REG(CTU_ADINR,       0x508,  0x100),
+               RSND_GEN_M_REG(CTU_CPMDR,       0x510,  0x100),
+               RSND_GEN_M_REG(CTU_SCMDR,       0x514,  0x100),
+               RSND_GEN_M_REG(CTU_SV00R,       0x518,  0x100),
+               RSND_GEN_M_REG(CTU_SV01R,       0x51c,  0x100),
+               RSND_GEN_M_REG(CTU_SV02R,       0x520,  0x100),
+               RSND_GEN_M_REG(CTU_SV03R,       0x524,  0x100),
+               RSND_GEN_M_REG(CTU_SV04R,       0x528,  0x100),
+               RSND_GEN_M_REG(CTU_SV05R,       0x52c,  0x100),
+               RSND_GEN_M_REG(CTU_SV06R,       0x530,  0x100),
+               RSND_GEN_M_REG(CTU_SV07R,       0x534,  0x100),
+               RSND_GEN_M_REG(CTU_SV10R,       0x538,  0x100),
+               RSND_GEN_M_REG(CTU_SV11R,       0x53c,  0x100),
+               RSND_GEN_M_REG(CTU_SV12R,       0x540,  0x100),
+               RSND_GEN_M_REG(CTU_SV13R,       0x544,  0x100),
+               RSND_GEN_M_REG(CTU_SV14R,       0x548,  0x100),
+               RSND_GEN_M_REG(CTU_SV15R,       0x54c,  0x100),
+               RSND_GEN_M_REG(CTU_SV16R,       0x550,  0x100),
+               RSND_GEN_M_REG(CTU_SV17R,       0x554,  0x100),
+               RSND_GEN_M_REG(CTU_SV20R,       0x558,  0x100),
+               RSND_GEN_M_REG(CTU_SV21R,       0x55c,  0x100),
+               RSND_GEN_M_REG(CTU_SV22R,       0x560,  0x100),
+               RSND_GEN_M_REG(CTU_SV23R,       0x564,  0x100),
+               RSND_GEN_M_REG(CTU_SV24R,       0x568,  0x100),
+               RSND_GEN_M_REG(CTU_SV25R,       0x56c,  0x100),
+               RSND_GEN_M_REG(CTU_SV26R,       0x570,  0x100),
+               RSND_GEN_M_REG(CTU_SV27R,       0x574,  0x100),
+               RSND_GEN_M_REG(CTU_SV30R,       0x578,  0x100),
+               RSND_GEN_M_REG(CTU_SV31R,       0x57c,  0x100),
+               RSND_GEN_M_REG(CTU_SV32R,       0x580,  0x100),
+               RSND_GEN_M_REG(CTU_SV33R,       0x584,  0x100),
+               RSND_GEN_M_REG(CTU_SV34R,       0x588,  0x100),
+               RSND_GEN_M_REG(CTU_SV35R,       0x58c,  0x100),
+               RSND_GEN_M_REG(CTU_SV36R,       0x590,  0x100),
+               RSND_GEN_M_REG(CTU_SV37R,       0x594,  0x100),
                RSND_GEN_M_REG(MIX_SWRSR,       0xd00,  0x40),
                RSND_GEN_M_REG(MIX_MIXIR,       0xd04,  0x40),
                RSND_GEN_M_REG(MIX_ADINR,       0xd08,  0x40),
index 65542b6a89e9e3452b312369035983c98b70077d..195fc7bb22afb6519fb574af7b28832d2defda6b 100644 (file)
@@ -51,7 +51,7 @@ static void rsnd_mix_volume_init(struct rsnd_dai_stream *io,
        rsnd_mod_write(mod, MIX_MIXIR, 1);
 
        /* General Information */
-       rsnd_mod_write(mod, MIX_ADINR, rsnd_get_adinr_chan(mod, io));
+       rsnd_mod_write(mod, MIX_ADINR, rsnd_runtime_channel_after_ctu(io));
 
        /* volume step */
        rsnd_mod_write(mod, MIX_MIXMR, 0);
@@ -172,7 +172,7 @@ int rsnd_mix_probe(struct rsnd_priv *priv)
                }
 
                ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
-                                   clk, RSND_MOD_MIX, i);
+                                   clk, rsnd_mod_get_status, RSND_MOD_MIX, i);
                if (ret)
                        goto rsnd_mix_probe_done;
 
index 317dd793149a7feb5279e2f665385a564c594469..fc89a67258cac6306eb73e1835d2dd2bf995559d 100644 (file)
@@ -86,8 +86,43 @@ enum rsnd_reg {
        RSND_REG_CMD_BUSIF_DALIGN,      /* Gen2 only */
        RSND_REG_CMD_ROUTE_SLCT,
        RSND_REG_CMDOUT_TIMSEL,         /* Gen2 only */
+       RSND_REG_CTU_SWRSR,
        RSND_REG_CTU_CTUIR,
        RSND_REG_CTU_ADINR,
+       RSND_REG_CTU_CPMDR,
+       RSND_REG_CTU_SCMDR,
+       RSND_REG_CTU_SV00R,
+       RSND_REG_CTU_SV01R,
+       RSND_REG_CTU_SV02R,
+       RSND_REG_CTU_SV03R,
+       RSND_REG_CTU_SV04R,
+       RSND_REG_CTU_SV05R,
+       RSND_REG_CTU_SV06R,
+       RSND_REG_CTU_SV07R,
+       RSND_REG_CTU_SV10R,
+       RSND_REG_CTU_SV11R,
+       RSND_REG_CTU_SV12R,
+       RSND_REG_CTU_SV13R,
+       RSND_REG_CTU_SV14R,
+       RSND_REG_CTU_SV15R,
+       RSND_REG_CTU_SV16R,
+       RSND_REG_CTU_SV17R,
+       RSND_REG_CTU_SV20R,
+       RSND_REG_CTU_SV21R,
+       RSND_REG_CTU_SV22R,
+       RSND_REG_CTU_SV23R,
+       RSND_REG_CTU_SV24R,
+       RSND_REG_CTU_SV25R,
+       RSND_REG_CTU_SV26R,
+       RSND_REG_CTU_SV27R,
+       RSND_REG_CTU_SV30R,
+       RSND_REG_CTU_SV31R,
+       RSND_REG_CTU_SV32R,
+       RSND_REG_CTU_SV33R,
+       RSND_REG_CTU_SV34R,
+       RSND_REG_CTU_SV35R,
+       RSND_REG_CTU_SV36R,
+       RSND_REG_CTU_SV37R,
        RSND_REG_MIX_SWRSR,
        RSND_REG_MIX_MIXIR,
        RSND_REG_MIX_ADINR,
@@ -147,8 +182,6 @@ struct rsnd_dai_stream;
        rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r)
 #define rsnd_mod_write(m, r, d) \
        rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
-#define rsnd_mod_force_write(m, r, d) \
-       rsnd_force_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
 #define rsnd_mod_bset(m, r, s, d) \
        rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d)
 
@@ -160,14 +193,13 @@ void rsnd_force_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
 void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
                    u32 mask, u32 data);
 u32 rsnd_get_adinr_bit(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
-u32 rsnd_get_adinr_chan(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
 u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io);
 
 /*
  *     R-Car DMA
  */
-struct rsnd_mod *rsnd_dma_attach(struct rsnd_dai_stream *io,
-                              struct rsnd_mod *mod, int id);
+int rsnd_dma_attach(struct rsnd_dai_stream *io,
+                   struct rsnd_mod *mod, struct rsnd_mod **dma_mod, int id);
 int rsnd_dma_probe(struct rsnd_priv *priv);
 struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
                                          struct rsnd_mod *mod, char *name);
@@ -214,6 +246,9 @@ struct rsnd_mod_ops {
        int (*stop)(struct rsnd_mod *mod,
                    struct rsnd_dai_stream *io,
                    struct rsnd_priv *priv);
+       int (*irq)(struct rsnd_mod *mod,
+                  struct rsnd_dai_stream *io,
+                  struct rsnd_priv *priv, int enable);
        int (*pcm_new)(struct rsnd_mod *mod,
                       struct rsnd_dai_stream *io,
                       struct snd_soc_pcm_runtime *rtd);
@@ -233,47 +268,54 @@ struct rsnd_mod {
        struct rsnd_mod_ops *ops;
        struct rsnd_priv *priv;
        struct clk *clk;
+       u32 *(*get_status)(struct rsnd_dai_stream *io,
+                          struct rsnd_mod *mod,
+                          enum rsnd_mod_type type);
+       u32 status;
 };
 /*
  * status
  *
- * 0xH0000CBA
+ * 0xH0000CB0
  *
- * A   0: probe        1: remove
  * B   0: init         1: quit
  * C   0: start        1: stop
  *
  * H is always called (see __rsnd_mod_call)
+ * H   0: probe        1: remove
  * H   0: pcm_new
  * H   0: fallback
  * H   0: hw_params
  */
-#define __rsnd_mod_shift_probe         0
-#define __rsnd_mod_shift_remove                0
 #define __rsnd_mod_shift_init          4
 #define __rsnd_mod_shift_quit          4
 #define __rsnd_mod_shift_start         8
 #define __rsnd_mod_shift_stop          8
+#define __rsnd_mod_shift_probe         28 /* always called */
+#define __rsnd_mod_shift_remove                28 /* always called */
+#define __rsnd_mod_shift_irq           28 /* always called */
 #define __rsnd_mod_shift_pcm_new       28 /* always called */
 #define __rsnd_mod_shift_fallback      28 /* always called */
 #define __rsnd_mod_shift_hw_params     28 /* always called */
 
-#define __rsnd_mod_add_probe            1
-#define __rsnd_mod_add_remove          -1
+#define __rsnd_mod_add_probe           0
+#define __rsnd_mod_add_remove          0
 #define __rsnd_mod_add_init             1
 #define __rsnd_mod_add_quit            -1
 #define __rsnd_mod_add_start            1
 #define __rsnd_mod_add_stop            -1
+#define __rsnd_mod_add_irq             0
 #define __rsnd_mod_add_pcm_new         0
 #define __rsnd_mod_add_fallback                0
 #define __rsnd_mod_add_hw_params       0
 
 #define __rsnd_mod_call_probe          0
-#define __rsnd_mod_call_remove         1
+#define __rsnd_mod_call_remove         0
 #define __rsnd_mod_call_init           0
 #define __rsnd_mod_call_quit           1
 #define __rsnd_mod_call_start          0
 #define __rsnd_mod_call_stop           1
+#define __rsnd_mod_call_irq            0
 #define __rsnd_mod_call_pcm_new                0
 #define __rsnd_mod_call_fallback       0
 #define __rsnd_mod_call_hw_params      0
@@ -286,10 +328,13 @@ struct rsnd_mod {
 
 int rsnd_mod_init(struct rsnd_priv *priv,
                  struct rsnd_mod *mod,
-                  struct rsnd_mod_ops *ops,
-                  struct clk *clk,
-                  enum rsnd_mod_type type,
-                  int id);
+                 struct rsnd_mod_ops *ops,
+                 struct clk *clk,
+                 u32* (*get_status)(struct rsnd_dai_stream *io,
+                                    struct rsnd_mod *mod,
+                                    enum rsnd_mod_type type),
+                 enum rsnd_mod_type type,
+                 int id);
 void rsnd_mod_quit(struct rsnd_mod *mod);
 char *rsnd_mod_name(struct rsnd_mod *mod);
 struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
@@ -297,6 +342,10 @@ struct dma_chan *rsnd_mod_dma_req(struct rsnd_dai_stream *io,
 void rsnd_mod_interrupt(struct rsnd_mod *mod,
                        void (*callback)(struct rsnd_mod *mod,
                                         struct rsnd_dai_stream *io));
+u32 *rsnd_mod_get_status(struct rsnd_dai_stream *io,
+                        struct rsnd_mod *mod,
+                        enum rsnd_mod_type type);
+
 void rsnd_parse_connect_common(struct rsnd_dai *rdai,
                struct rsnd_mod* (*mod_get)(struct rsnd_priv *priv, int id),
                struct device_node *node,
@@ -306,9 +355,14 @@ void rsnd_parse_connect_common(struct rsnd_dai *rdai,
 void rsnd_set_slot(struct rsnd_dai *rdai,
                   int slots, int slots_total);
 int rsnd_get_slot(struct rsnd_dai_stream *io);
-int rsnd_get_slot_width(struct rsnd_dai_stream *io);
 int rsnd_get_slot_num(struct rsnd_dai_stream *io);
 
+int rsnd_runtime_channel_original(struct rsnd_dai_stream *io);
+int rsnd_runtime_channel_after_ctu(struct rsnd_dai_stream *io);
+int rsnd_runtime_channel_for_ssi(struct rsnd_dai_stream *io);
+int rsnd_runtime_is_ssi_multi(struct rsnd_dai_stream *io);
+int rsnd_runtime_is_ssi_tdm(struct rsnd_dai_stream *io);
+
 /*
  *     R-Car sound DAI
  */
@@ -319,7 +373,7 @@ struct rsnd_dai_stream {
        struct rsnd_mod *mod[RSND_MOD_MAX];
        struct rsnd_dai_path_info *info; /* rcar_snd.h */
        struct rsnd_dai *rdai;
-       u32 mod_status[RSND_MOD_MAX];
+       u32 parent_ssi_status;
        int byte_pos;
        int period_pos;
        int byte_per_period;
@@ -392,12 +446,10 @@ int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
 int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
 int rsnd_adg_probe(struct rsnd_priv *priv);
 void rsnd_adg_remove(struct rsnd_priv *priv);
-int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_src_timesel_gen2(struct rsnd_mod *src_mod,
                                  struct rsnd_dai_stream *io,
-                                 unsigned int src_rate,
-                                 unsigned int dst_rate);
-int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
-                                    struct rsnd_dai_stream *io);
+                                 unsigned int in_rate,
+                                 unsigned int out_rate);
 int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
                                 struct rsnd_dai_stream *io);
 
@@ -498,10 +550,10 @@ struct rsnd_kctrl_cfg {
        struct snd_kcontrol *kctrl;
 };
 
-#define RSND_DVC_CHANNELS      8
+#define RSND_MAX_CHANNELS      8
 struct rsnd_kctrl_cfg_m {
        struct rsnd_kctrl_cfg cfg;
-       u32 val[RSND_DVC_CHANNELS];
+       u32 val[RSND_MAX_CHANNELS];
 };
 
 struct rsnd_kctrl_cfg_s {
@@ -547,7 +599,7 @@ void rsnd_ssi_remove(struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
 int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
 int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
-u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io);
+u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io);
 
 #define rsnd_ssi_is_pin_sharing(io)    \
        __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
@@ -573,9 +625,13 @@ void rsnd_ssiu_remove(struct rsnd_priv *priv);
 int rsnd_src_probe(struct rsnd_priv *priv);
 void rsnd_src_remove(struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id);
-unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
-                                  struct rsnd_dai_stream *io,
-                                  struct snd_pcm_runtime *runtime);
+
+#define rsnd_src_get_in_rate(priv, io) rsnd_src_get_rate(priv, io, 1)
+#define rsnd_src_get_out_rate(priv, io) rsnd_src_get_rate(priv, io, 0)
+unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
+                              struct rsnd_dai_stream *io,
+                              int is_in);
+
 #define rsnd_src_of_node(priv)                                         \
        of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,src")
 #define rsnd_parse_connect_src(rdai, playback, capture)                        \
@@ -588,6 +644,7 @@ unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
  */
 int rsnd_ctu_probe(struct rsnd_priv *priv);
 void rsnd_ctu_remove(struct rsnd_priv *priv);
+int rsnd_ctu_converted_channel(struct rsnd_mod *mod);
 struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id);
 #define rsnd_ctu_of_node(priv)                                         \
        of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ctu")
index 8a357fdf1077c91822d6699dcf79e7aa96574809..1bc7ecfc42a9e18004d7726e2de45ca06f1542ca 100644 (file)
@@ -66,12 +66,12 @@ struct rsrc_card_priv {
        struct snd_soc_dai_link *dai_link;
        int dai_num;
        u32 convert_rate;
+       u32 convert_channels;
 };
 
 #define rsrc_priv_to_dev(priv) ((priv)->snd_card.dev)
 #define rsrc_priv_to_link(priv, i) ((priv)->snd_card.dai_link + (i))
 #define rsrc_priv_to_props(priv, i) ((priv)->dai_props + (i))
-#define rsrc_dev_to_of_data(dev) (of_match_device(rsrc_card_of_match, (dev))->data)
 
 static int rsrc_card_startup(struct snd_pcm_substream *substream)
 {
@@ -145,11 +145,16 @@ static int rsrc_card_be_hw_params_fixup(struct snd_soc_pcm_runtime *rtd,
        struct rsrc_card_priv *priv = snd_soc_card_get_drvdata(rtd->card);
        struct snd_interval *rate = hw_param_interval(params,
                                                      SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels = hw_param_interval(params,
+                                               SNDRV_PCM_HW_PARAM_CHANNELS);
 
-       if (!priv->convert_rate)
-               return 0;
+       if (priv->convert_rate)
+               rate->min =
+               rate->max = priv->convert_rate;
 
-       rate->min = rate->max = priv->convert_rate;
+       if (priv->convert_channels)
+               channels->min =
+               channels->max = priv->convert_channels;
 
        return 0;
 }
@@ -246,7 +251,7 @@ static int rsrc_card_parse_links(struct device_node *np,
                struct device *dev = rsrc_priv_to_dev(priv);
                const struct rsrc_card_of_data *of_data;
 
-               of_data = rsrc_dev_to_of_data(dev);
+               of_data = of_device_get_match_data(dev);
 
                /* FE is dummy */
                dai_link->cpu_of_node           = NULL;
@@ -396,7 +401,7 @@ static int rsrc_card_parse_of(struct device_node *node,
                              struct rsrc_card_priv *priv,
                              struct device *dev)
 {
-       const struct rsrc_card_of_data *of_data = rsrc_dev_to_of_data(dev);
+       const struct rsrc_card_of_data *of_data = of_device_get_match_data(dev);
        struct rsrc_card_dai *props;
        struct snd_soc_dai_link *links;
        int ret;
@@ -437,9 +442,13 @@ static int rsrc_card_parse_of(struct device_node *node,
        /* sampling rate convert */
        of_property_read_u32(node, "convert-rate", &priv->convert_rate);
 
-       dev_dbg(dev, "New rsrc-audio-card: %s (%d)\n",
-               priv->snd_card.name ? priv->snd_card.name : "",
-               priv->convert_rate);
+       /* channels transfer */
+       of_property_read_u32(node, "convert-channels", &priv->convert_channels);
+
+       dev_dbg(dev, "New rsrc-audio-card: %s\n",
+               priv->snd_card.name ? priv->snd_card.name : "");
+       dev_dbg(dev, "SRC : convert_rate     %d\n", priv->convert_rate);
+       dev_dbg(dev, "CTU : convert_channels %d\n", priv->convert_channels);
 
        ret = rsrc_card_dai_link_of(node, priv);
        if (ret < 0)
index 5eda056d9f20ed8ef3137da612c63ae9d2cba067..15d6ffe8be74da83b61e4cbe28f10bcc669cd845 100644 (file)
@@ -25,7 +25,6 @@ struct rsnd_src {
        struct rsnd_kctrl_cfg_s sen;  /* sync convert enable */
        struct rsnd_kctrl_cfg_s sync; /* sync convert */
        u32 convert_rate; /* sampling rate convert */
-       int err;
        int irq;
 };
 
@@ -34,7 +33,7 @@ struct rsnd_src {
 #define rsnd_src_get(priv, id) ((struct rsnd_src *)(priv->src) + id)
 #define rsnd_src_to_dma(src) ((src)->dma)
 #define rsnd_src_nr(priv) ((priv)->src_nr)
-#define rsnd_enable_sync_convert(src) ((src)->sen.val)
+#define rsnd_src_sync_is_enabled(mod) (rsnd_mod_to_src(mod)->sen.val)
 
 #define rsnd_mod_to_src(_mod)                          \
        container_of((_mod), struct rsnd_src, mod)
@@ -94,15 +93,16 @@ static struct dma_chan *rsnd_src_dma_req(struct rsnd_dai_stream *io,
 }
 
 static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
-                                struct rsnd_src *src)
+                                struct rsnd_mod *mod)
 {
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+       struct rsnd_src *src = rsnd_mod_to_src(mod);
        u32 convert_rate;
 
        if (!runtime)
                return 0;
 
-       if (!rsnd_enable_sync_convert(src))
+       if (!rsnd_src_sync_is_enabled(mod))
                return src->convert_rate;
 
        convert_rate = src->sync.val;
@@ -116,23 +116,33 @@ static u32 rsnd_src_convert_rate(struct rsnd_dai_stream *io,
        return convert_rate;
 }
 
-unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
-                                  struct rsnd_dai_stream *io,
-                                  struct snd_pcm_runtime *runtime)
+unsigned int rsnd_src_get_rate(struct rsnd_priv *priv,
+                              struct rsnd_dai_stream *io,
+                              int is_in)
 {
        struct rsnd_mod *src_mod = rsnd_io_to_mod_src(io);
-       struct rsnd_src *src;
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        unsigned int rate = 0;
+       int is_play = rsnd_io_is_play(io);
 
-       if (src_mod) {
-               src = rsnd_mod_to_src(src_mod);
+       /*
+        *
+        * Playback
+        * runtime_rate -> [SRC] -> convert_rate
+        *
+        * Capture
+        * convert_rate -> [SRC] -> runtime_rate
+        */
 
-               /*
-                * return convert rate if SRC is used,
-                * otherwise, return runtime->rate as usual
-                */
-               rate = rsnd_src_convert_rate(io, src);
-       }
+       if (is_play == is_in)
+               return runtime->rate;
+
+       /*
+        * return convert rate if SRC is used,
+        * otherwise, return runtime->rate as usual
+        */
+       if (src_mod)
+               rate = rsnd_src_convert_rate(io, src_mod);
 
        if (!rate)
                rate = runtime->rate;
@@ -179,8 +189,7 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-       u32 convert_rate = rsnd_src_convert_rate(io, src);
+       u32 fin, fout;
        u32 ifscr, fsrate, adinr;
        u32 cr, route;
        u32 bsdsr, bsisr;
@@ -189,13 +198,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
        if (!runtime)
                return;
 
+       fin  = rsnd_src_get_in_rate(priv, io);
+       fout = rsnd_src_get_out_rate(priv, io);
+
        /* 6 - 1/6 are very enough ratio for SRC_BSDSR */
-       if (!convert_rate)
+       if (fin == fout)
                ratio = 0;
-       else if (convert_rate > runtime->rate)
-               ratio = 100 * convert_rate / runtime->rate;
+       else if (fin > fout)
+               ratio = 100 * fin / fout;
        else
-               ratio = 100 * runtime->rate / convert_rate;
+               ratio = 100 * fout / fin;
 
        if (ratio > 600) {
                dev_err(dev, "FSO/FSI ratio error\n");
@@ -206,16 +218,16 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
         *      SRC_ADINR
         */
        adinr = rsnd_get_adinr_bit(mod, io) |
-               rsnd_get_adinr_chan(mod, io);
+               rsnd_runtime_channel_original(io);
 
        /*
         *      SRC_IFSCR / SRC_IFSVR
         */
        ifscr = 0;
        fsrate = 0;
-       if (convert_rate) {
+       if (fin != fout) {
                ifscr = 1;
-               fsrate = 0x0400000 / convert_rate * runtime->rate;
+               fsrate = 0x0400000 / fout * fin;
        }
 
        /*
@@ -223,10 +235,10 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
         */
        cr      = 0x00011110;
        route   = 0x0;
-       if (convert_rate) {
+       if (fin != fout) {
                route   = 0x1;
 
-               if (rsnd_enable_sync_convert(src)) {
+               if (rsnd_src_sync_is_enabled(mod)) {
                        cr |= 0x1;
                        route |= rsnd_io_is_play(io) ?
                                (0x1 << 24) : (0x1 << 25);
@@ -250,6 +262,8 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
                break;
        }
 
+       rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
+
        rsnd_mod_write(mod, SRC_SRCIR, 1);      /* initialize */
        rsnd_mod_write(mod, SRC_ADINR, adinr);
        rsnd_mod_write(mod, SRC_IFSCR, ifscr);
@@ -259,22 +273,17 @@ static void rsnd_src_set_convert_rate(struct rsnd_dai_stream *io,
        rsnd_mod_write(mod, SRC_BSISR, bsisr);
        rsnd_mod_write(mod, SRC_SRCIR, 0);      /* cancel initialize */
 
-       rsnd_mod_write(mod, SRC_ROUTE_MODE0, route);
        rsnd_mod_write(mod, SRC_I_BUSIF_MODE, 1);
        rsnd_mod_write(mod, SRC_O_BUSIF_MODE, 1);
        rsnd_mod_write(mod, SRC_BUSIF_DALIGN, rsnd_get_dalign(mod, io));
 
-       if (convert_rate)
-               rsnd_adg_set_convert_clk_gen2(mod, io,
-                                             runtime->rate,
-                                             convert_rate);
-       else
-               rsnd_adg_set_convert_timing_gen2(mod, io);
+       rsnd_adg_set_src_timesel_gen2(mod, io, fin, fout);
 }
 
-#define rsnd_src_irq_enable(mod)  rsnd_src_irq_ctrol(mod, 1)
-#define rsnd_src_irq_disable(mod) rsnd_src_irq_ctrol(mod, 0)
-static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable)
+static int rsnd_src_irq(struct rsnd_mod *mod,
+                       struct rsnd_dai_stream *io,
+                       struct rsnd_priv *priv,
+                       int enable)
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
        u32 sys_int_val, int_val, sys_int_mask;
@@ -298,14 +307,16 @@ static void rsnd_src_irq_ctrol(struct rsnd_mod *mod, int enable)
        /*
         * WORKAROUND
         *
-        * ignore over flow error when rsnd_enable_sync_convert()
+        * ignore over flow error when rsnd_src_sync_is_enabled()
         */
-       if (rsnd_enable_sync_convert(src))
+       if (rsnd_src_sync_is_enabled(mod))
                sys_int_val = sys_int_val & 0xffff;
 
        rsnd_mod_write(mod, SRC_INT_ENABLE0, int_val);
        rsnd_mod_bset(mod, SCU_SYS_INT_EN0, sys_int_mask, sys_int_val);
        rsnd_mod_bset(mod, SCU_SYS_INT_EN1, sys_int_mask, sys_int_val);
+
+       return 0;
 }
 
 static void rsnd_src_status_clear(struct rsnd_mod *mod)
@@ -316,9 +327,8 @@ static void rsnd_src_status_clear(struct rsnd_mod *mod)
        rsnd_mod_bset(mod, SCU_SYS_STATUS1, val, val);
 }
 
-static bool rsnd_src_record_error(struct rsnd_mod *mod)
+static bool rsnd_src_error_occurred(struct rsnd_mod *mod)
 {
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
        u32 val0, val1;
        bool ret = false;
 
@@ -327,18 +337,14 @@ static bool rsnd_src_record_error(struct rsnd_mod *mod)
        /*
         * WORKAROUND
         *
-        * ignore over flow error when rsnd_enable_sync_convert()
+        * ignore over flow error when rsnd_src_sync_is_enabled()
         */
-       if (rsnd_enable_sync_convert(src))
+       if (rsnd_src_sync_is_enabled(mod))
                val0 = val0 & 0xffff;
 
        if ((rsnd_mod_read(mod, SCU_SYS_STATUS0) & val0) ||
-           (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1)) {
-               struct rsnd_src *src = rsnd_mod_to_src(mod);
-
-               src->err++;
+           (rsnd_mod_read(mod, SCU_SYS_STATUS1) & val1))
                ret = true;
-       }
 
        return ret;
 }
@@ -347,7 +353,6 @@ static int rsnd_src_start(struct rsnd_mod *mod,
                          struct rsnd_dai_stream *io,
                          struct rsnd_priv *priv)
 {
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
        u32 val;
 
        /*
@@ -355,7 +360,7 @@ static int rsnd_src_start(struct rsnd_mod *mod,
         *
         * Enable SRC output if you want to use sync convert together with DVC
         */
-       val = (rsnd_io_to_mod_dvc(io) && !rsnd_enable_sync_convert(src)) ?
+       val = (rsnd_io_to_mod_dvc(io) && !rsnd_src_sync_is_enabled(mod)) ?
                0x01 : 0x11;
 
        rsnd_mod_write(mod, SRC_CTRL, val);
@@ -367,11 +372,7 @@ static int rsnd_src_stop(struct rsnd_mod *mod,
                         struct rsnd_dai_stream *io,
                         struct rsnd_priv *priv)
 {
-       /*
-        * stop SRC output only
-        * see rsnd_src_quit
-        */
-       rsnd_mod_write(mod, SRC_CTRL, 0x01);
+       rsnd_mod_write(mod, SRC_CTRL, 0);
 
        return 0;
 }
@@ -390,10 +391,6 @@ static int rsnd_src_init(struct rsnd_mod *mod,
 
        rsnd_src_status_clear(mod);
 
-       rsnd_src_irq_enable(mod);
-
-       src->err = 0;
-
        /* reset sync convert_rate */
        src->sync.val = 0;
 
@@ -405,21 +402,11 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
                         struct rsnd_priv *priv)
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
-       struct device *dev = rsnd_priv_to_dev(priv);
-
-       rsnd_src_irq_disable(mod);
-
-       /* stop both out/in */
-       rsnd_mod_write(mod, SRC_CTRL, 0);
 
        rsnd_src_halt(mod);
 
        rsnd_mod_power_off(mod);
 
-       if (src->err)
-               dev_warn(dev, "%s[%d] under/over flow err = %d\n",
-                        rsnd_mod_name(mod), rsnd_mod_id(mod), src->err);
-
        src->convert_rate = 0;
 
        /* reset sync convert_rate */
@@ -432,8 +419,7 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod,
                                 struct rsnd_dai_stream *io)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-       struct device *dev = rsnd_priv_to_dev(priv);
+       bool stop = false;
 
        spin_lock(&priv->lock);
 
@@ -441,26 +427,16 @@ static void __rsnd_src_interrupt(struct rsnd_mod *mod,
        if (!rsnd_io_is_working(io))
                goto rsnd_src_interrupt_out;
 
-       if (rsnd_src_record_error(mod)) {
-
-               dev_dbg(dev, "%s[%d] restart\n",
-                       rsnd_mod_name(mod), rsnd_mod_id(mod));
-
-               rsnd_src_stop(mod, io, priv);
-               rsnd_src_start(mod, io, priv);
-       }
-
-       if (src->err > 1024) {
-               rsnd_src_irq_disable(mod);
-
-               dev_warn(dev, "no more %s[%d] restart\n",
-                        rsnd_mod_name(mod), rsnd_mod_id(mod));
-       }
+       if (rsnd_src_error_occurred(mod))
+               stop = true;
 
        rsnd_src_status_clear(mod);
 rsnd_src_interrupt_out:
 
        spin_unlock(&priv->lock);
+
+       if (stop)
+               snd_pcm_stop_xrun(io->substream);
 }
 
 static irqreturn_t rsnd_src_interrupt(int irq, void *data)
@@ -485,7 +461,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod,
                /*
                 * IRQ is not supported on non-DT
                 * see
-                *      rsnd_src_irq_enable()
+                *      rsnd_src_irq()
                 */
                ret = devm_request_irq(dev, irq,
                                       rsnd_src_interrupt,
@@ -495,9 +471,7 @@ static int rsnd_src_probe_(struct rsnd_mod *mod,
                        return ret;
        }
 
-       src->dma = rsnd_dma_attach(io, mod, 0);
-       if (IS_ERR(src->dma))
-               return PTR_ERR(src->dma);
+       ret = rsnd_dma_attach(io, mod, &src->dma, 0);
 
        return ret;
 }
@@ -506,8 +480,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
                            struct rsnd_dai_stream *io,
                            struct snd_soc_pcm_runtime *rtd)
 {
-       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
-       struct rsnd_mod *dvc = rsnd_io_to_mod_dvc(io);
        struct rsnd_src *src = rsnd_mod_to_src(mod);
        int ret;
 
@@ -516,15 +488,10 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
         */
 
        /*
-        * SRC sync convert needs clock master
-        */
-       if (!rsnd_rdai_is_clk_master(rdai))
-               return 0;
-
-       /*
-        * SRC In doesn't work if DVC was enabled
+        * It can't use SRC Synchronous convert
+        * when Capture if it uses CMD
         */
-       if (dvc && !rsnd_io_is_play(io))
+       if (rsnd_io_to_mod_cmd(io) && !rsnd_io_is_play(io))
                return 0;
 
        /*
@@ -557,6 +524,7 @@ static struct rsnd_mod_ops rsnd_src_ops = {
        .quit   = rsnd_src_quit,
        .start  = rsnd_src_start,
        .stop   = rsnd_src_stop,
+       .irq    = rsnd_src_irq,
        .hw_params = rsnd_src_hw_params,
        .pcm_new = rsnd_src_pcm_new,
 };
@@ -622,7 +590,8 @@ int rsnd_src_probe(struct rsnd_priv *priv)
                }
 
                ret = rsnd_mod_init(priv, rsnd_mod_get(src),
-                                   &rsnd_src_ops, clk, RSND_MOD_SRC, i);
+                                   &rsnd_src_ops, clk, rsnd_mod_get_status,
+                                   RSND_MOD_SRC, i);
                if (ret)
                        goto rsnd_src_probe_done;
 
index 7ee89da4dd5fc3a36b62147acd95fc6b4724661b..5f848f0547457338e122f5309914f3036c308836 100644 (file)
@@ -64,7 +64,6 @@
 #define SSI_NAME "ssi"
 
 struct rsnd_ssi {
-       struct rsnd_ssi *parent;
        struct rsnd_mod mod;
        struct rsnd_mod *dma;
 
@@ -75,7 +74,6 @@ struct rsnd_ssi {
        u32 wsr;
        int chan;
        int rate;
-       int err;
        int irq;
        unsigned int usrcnt;
 };
@@ -96,7 +94,10 @@ struct rsnd_ssi {
 #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
 #define rsnd_ssi_mode_flags(p) ((p)->flags)
 #define rsnd_ssi_is_parent(ssi, io) ((ssi) == rsnd_io_to_mod_ssip(io))
-#define rsnd_ssi_is_multi_slave(ssi, io) ((mod) != rsnd_io_to_mod_ssi(io))
+#define rsnd_ssi_is_multi_slave(mod, io) \
+       (rsnd_ssi_multi_slaves(io) & (1 << rsnd_mod_id(mod)))
+#define rsnd_ssi_is_run_mods(mod, io) \
+       (rsnd_ssi_run_mods(io) & (1 << rsnd_mod_id(mod)))
 
 int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
 {
@@ -141,43 +142,13 @@ static void rsnd_ssi_status_check(struct rsnd_mod *mod,
                udelay(50);
        }
 
-       dev_warn(dev, "status check failed\n");
-}
-
-static int rsnd_ssi_irq_enable(struct rsnd_mod *ssi_mod)
-{
-       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
-
-       if (rsnd_is_gen1(priv))
-               return 0;
-
-       /* enable SSI interrupt if Gen2 */
-       rsnd_mod_write(ssi_mod, SSI_INT_ENABLE,
-                      rsnd_ssi_is_dma_mode(ssi_mod) ?
-                      0x0e000000 : 0x0f000000);
-
-       return 0;
-}
-
-static int rsnd_ssi_irq_disable(struct rsnd_mod *ssi_mod)
-{
-       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
-
-       if (rsnd_is_gen1(priv))
-               return 0;
-
-       /* disable SSI interrupt if Gen2 */
-       rsnd_mod_write(ssi_mod, SSI_INT_ENABLE, 0x00000000);
-
-       return 0;
+       dev_warn(dev, "%s[%d] status check failed\n",
+                rsnd_mod_name(mod), rsnd_mod_id(mod));
 }
 
-u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
+static u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
 {
        struct rsnd_mod *mod;
-       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
-       struct rsnd_priv *priv = rsnd_io_to_priv(io);
-       struct device *dev = rsnd_priv_to_dev(priv);
        enum rsnd_mod_type types[] = {
                RSND_MOD_SSIM1,
                RSND_MOD_SSIM2,
@@ -185,16 +156,6 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
        };
        int i, mask;
 
-       switch (runtime->channels) {
-       case 2: /* Multi channel is not needed for Stereo */
-               return 0;
-       case 6:
-               break;
-       default:
-               dev_err(dev, "unsupported channel\n");
-               return 0;
-       }
-
        mask = 0;
        for (i = 0; i < ARRAY_SIZE(types); i++) {
                mod = rsnd_io_to_mod(io, types[i]);
@@ -207,22 +168,41 @@ u32 rsnd_ssi_multi_slaves(struct rsnd_dai_stream *io)
        return mask;
 }
 
-static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
+static u32 rsnd_ssi_run_mods(struct rsnd_dai_stream *io)
+{
+       struct rsnd_mod *ssi_mod = rsnd_io_to_mod_ssi(io);
+       struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
+
+       return rsnd_ssi_multi_slaves_runtime(io) |
+               1 << rsnd_mod_id(ssi_mod) |
+               1 << rsnd_mod_id(ssi_parent_mod);
+}
+
+u32 rsnd_ssi_multi_slaves_runtime(struct rsnd_dai_stream *io)
+{
+       if (rsnd_runtime_is_ssi_multi(io))
+               return rsnd_ssi_multi_slaves(io);
+
+       return 0;
+}
+
+static int rsnd_ssi_master_clk_start(struct rsnd_mod *mod,
                                     struct rsnd_dai_stream *io)
 {
        struct rsnd_priv *priv = rsnd_io_to_priv(io);
-       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
-       struct rsnd_mod *mod = rsnd_mod_get(ssi);
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
-       int slots = rsnd_get_slot_width(io);
+       int chan = rsnd_runtime_channel_for_ssi(io);
        int j, ret;
        int ssi_clk_mul_table[] = {
                1, 2, 4, 8, 16, 6, 12,
        };
        unsigned int main_rate;
-       unsigned int rate = rsnd_src_get_ssi_rate(priv, io, runtime);
+       unsigned int rate = rsnd_io_is_play(io) ?
+               rsnd_src_get_out_rate(priv, io) :
+               rsnd_src_get_in_rate(priv, io);
 
        if (!rsnd_rdai_is_clk_master(rdai))
                return 0;
@@ -249,10 +229,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
 
                /*
                 * this driver is assuming that
-                * system word is 32bit x slots
+                * system word is 32bit x chan
                 * see rsnd_ssi_init()
                 */
-               main_rate = rate * 32 * slots * ssi_clk_mul_table[j];
+               main_rate = rate * 32 * chan * ssi_clk_mul_table[j];
 
                ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
                if (0 == ret) {
@@ -274,11 +254,11 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
        return -EIO;
 }
 
-static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
+static void rsnd_ssi_master_clk_stop(struct rsnd_mod *mod,
                                     struct rsnd_dai_stream *io)
 {
        struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
-       struct rsnd_mod *mod = rsnd_mod_get(ssi);
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        struct rsnd_mod *ssi_parent_mod = rsnd_io_to_mod_ssip(io);
 
        if (!rsnd_rdai_is_clk_master(rdai))
@@ -296,17 +276,18 @@ static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi,
        rsnd_adg_ssi_clk_stop(mod);
 }
 
-static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
+static void rsnd_ssi_config_init(struct rsnd_mod *mod,
                                struct rsnd_dai_stream *io)
 {
        struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        u32 cr_own;
        u32 cr_mode;
        u32 wsr;
        int is_tdm;
 
-       is_tdm = (rsnd_get_slot_width(io) >= 6) ? 1 : 0;
+       is_tdm = rsnd_runtime_is_ssi_tdm(io);
 
        /*
         * always use 32bit system word.
@@ -332,11 +313,9 @@ static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
        case 32:
                cr_own |= DWL_24;
                break;
-       default:
-               return -EINVAL;
        }
 
-       if (rsnd_ssi_is_dma_mode(rsnd_mod_get(ssi))) {
+       if (rsnd_ssi_is_dma_mode(mod)) {
                cr_mode = UIEN | OIEN | /* over/under run */
                          DMEN;         /* DMA : enable DMA */
        } else {
@@ -357,8 +336,16 @@ static int rsnd_ssi_config_init(struct rsnd_ssi *ssi,
        ssi->cr_own     = cr_own;
        ssi->cr_mode    = cr_mode;
        ssi->wsr        = wsr;
+}
 
-       return 0;
+static void rsnd_ssi_register_setup(struct rsnd_mod *mod)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+       rsnd_mod_write(mod, SSIWSR,     ssi->wsr);
+       rsnd_mod_write(mod, SSICR,      ssi->cr_own     |
+                                       ssi->cr_clk     |
+                                       ssi->cr_mode); /* without EN */
 }
 
 /*
@@ -371,28 +358,25 @@ static int rsnd_ssi_init(struct rsnd_mod *mod,
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        int ret;
 
+       if (!rsnd_ssi_is_run_mods(mod, io))
+               return 0;
+
        ssi->usrcnt++;
 
        rsnd_mod_power_on(mod);
 
-       ret = rsnd_ssi_master_clk_start(ssi, io);
+       ret = rsnd_ssi_master_clk_start(mod, io);
        if (ret < 0)
                return ret;
 
-       if (rsnd_ssi_is_parent(mod, io))
-               return 0;
-
-       ret = rsnd_ssi_config_init(ssi, io);
-       if (ret < 0)
-               return ret;
+       if (!rsnd_ssi_is_parent(mod, io))
+               rsnd_ssi_config_init(mod, io);
 
-       ssi->err        = -1; /* ignore 1st error */
+       rsnd_ssi_register_setup(mod);
 
        /* clear error status */
        rsnd_ssi_status_clear(mod);
 
-       rsnd_ssi_irq_enable(mod);
-
        return 0;
 }
 
@@ -403,25 +387,19 @@ static int rsnd_ssi_quit(struct rsnd_mod *mod,
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
 
+       if (!rsnd_ssi_is_run_mods(mod, io))
+               return 0;
+
        if (!ssi->usrcnt) {
                dev_err(dev, "%s[%d] usrcnt error\n",
                        rsnd_mod_name(mod), rsnd_mod_id(mod));
                return -EIO;
        }
 
-       if (!rsnd_ssi_is_parent(mod, io)) {
-               if (ssi->err > 0)
-                       dev_warn(dev, "%s[%d] under/over flow err = %d\n",
-                                rsnd_mod_name(mod), rsnd_mod_id(mod),
-                                ssi->err);
-
+       if (!rsnd_ssi_is_parent(mod, io))
                ssi->cr_own     = 0;
-               ssi->err        = 0;
 
-               rsnd_ssi_irq_disable(mod);
-       }
-
-       rsnd_ssi_master_clk_stop(ssi, io);
+       rsnd_ssi_master_clk_stop(mod, io);
 
        rsnd_mod_power_off(mod);
 
@@ -456,61 +434,43 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
        return 0;
 }
 
-static u32 rsnd_ssi_record_error(struct rsnd_ssi *ssi)
-{
-       struct rsnd_mod *mod = rsnd_mod_get(ssi);
-       u32 status = rsnd_ssi_status_get(mod);
-
-       /* under/over flow error */
-       if (status & (UIRQ | OIRQ))
-               ssi->err++;
-
-       return status;
-}
-
-static int __rsnd_ssi_start(struct rsnd_mod *mod,
-                           struct rsnd_dai_stream *io,
-                           struct rsnd_priv *priv)
+static int rsnd_ssi_start(struct rsnd_mod *mod,
+                         struct rsnd_dai_stream *io,
+                         struct rsnd_priv *priv)
 {
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       u32 cr;
-
-       cr  =   ssi->cr_own     |
-               ssi->cr_clk     |
-               ssi->cr_mode;
+       if (!rsnd_ssi_is_run_mods(mod, io))
+               return 0;
 
        /*
         * EN will be set via SSIU :: SSI_CONTROL
         * if Multi channel mode
         */
-       if (!rsnd_ssi_multi_slaves(io))
-               cr |= EN;
+       if (rsnd_ssi_multi_slaves_runtime(io))
+               return 0;
 
-       rsnd_mod_write(mod, SSICR, cr);
-       rsnd_mod_write(mod, SSIWSR, ssi->wsr);
+       rsnd_mod_bset(mod, SSICR, EN, EN);
 
        return 0;
 }
 
-static int rsnd_ssi_start(struct rsnd_mod *mod,
-                         struct rsnd_dai_stream *io,
-                         struct rsnd_priv *priv)
+static int rsnd_ssi_stop(struct rsnd_mod *mod,
+                        struct rsnd_dai_stream *io,
+                        struct rsnd_priv *priv)
 {
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       u32 cr;
+
+       if (!rsnd_ssi_is_run_mods(mod, io))
+               return 0;
+
        /*
-        * no limit to start
+        * don't stop if not last user
         * see also
-        *      rsnd_ssi_stop
+        *      rsnd_ssi_start
         *      rsnd_ssi_interrupt
         */
-       return __rsnd_ssi_start(mod, io, priv);
-}
-
-static int __rsnd_ssi_stop(struct rsnd_mod *mod,
-                          struct rsnd_dai_stream *io,
-                          struct rsnd_priv *priv)
-{
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       u32 cr;
+       if (ssi->usrcnt > 1)
+               return 0;
 
        /*
         * disable all IRQ,
@@ -532,33 +492,38 @@ static int __rsnd_ssi_stop(struct rsnd_mod *mod,
        return 0;
 }
 
-static int rsnd_ssi_stop(struct rsnd_mod *mod,
-                        struct rsnd_dai_stream *io,
-                        struct rsnd_priv *priv)
+static int rsnd_ssi_irq(struct rsnd_mod *mod,
+                       struct rsnd_dai_stream *io,
+                       struct rsnd_priv *priv,
+                       int enable)
 {
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       u32 val = 0;
 
-       /*
-        * don't stop if not last user
-        * see also
-        *      rsnd_ssi_start
-        *      rsnd_ssi_interrupt
-        */
-       if (ssi->usrcnt > 1)
+       if (rsnd_is_gen1(priv))
                return 0;
 
-       return __rsnd_ssi_stop(mod, io, priv);
+       if (rsnd_ssi_is_parent(mod, io))
+               return 0;
+
+       if (!rsnd_ssi_is_run_mods(mod, io))
+               return 0;
+
+       if (enable)
+               val = rsnd_ssi_is_dma_mode(mod) ? 0x0e000000 : 0x0f000000;
+
+       rsnd_mod_write(mod, SSI_INT_ENABLE, val);
+
+       return 0;
 }
 
 static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
                                 struct rsnd_dai_stream *io)
 {
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
-       struct device *dev = rsnd_priv_to_dev(priv);
        int is_dma = rsnd_ssi_is_dma_mode(mod);
        u32 status;
        bool elapsed = false;
+       bool stop = false;
 
        spin_lock(&priv->lock);
 
@@ -566,7 +531,7 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
        if (!rsnd_io_is_working(io))
                goto rsnd_ssi_interrupt_out;
 
-       status = rsnd_ssi_record_error(ssi);
+       status = rsnd_ssi_status_get(mod);
 
        /* PIO only */
        if (!is_dma && (status & DIRQ)) {
@@ -588,23 +553,8 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
        }
 
        /* DMA only */
-       if (is_dma && (status & (UIRQ | OIRQ))) {
-               /*
-                * restart SSI
-                */
-               dev_dbg(dev, "%s[%d] restart\n",
-                       rsnd_mod_name(mod), rsnd_mod_id(mod));
-
-               __rsnd_ssi_stop(mod, io, priv);
-               __rsnd_ssi_start(mod, io, priv);
-       }
-
-       if (ssi->err > 1024) {
-               rsnd_ssi_irq_disable(mod);
-
-               dev_warn(dev, "no more %s[%d] restart\n",
-                        rsnd_mod_name(mod), rsnd_mod_id(mod));
-       }
+       if (is_dma && (status & (UIRQ | OIRQ)))
+               stop = true;
 
        rsnd_ssi_status_clear(mod);
 rsnd_ssi_interrupt_out:
@@ -612,6 +562,10 @@ static void __rsnd_ssi_interrupt(struct rsnd_mod *mod,
 
        if (elapsed)
                rsnd_dai_period_elapsed(io);
+
+       if (stop)
+               snd_pcm_stop_xrun(io->substream);
+
 }
 
 static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
@@ -627,12 +581,17 @@ static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
  *             SSI PIO
  */
 static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
-                                  struct rsnd_dai_stream *io,
-                                  struct rsnd_priv *priv)
+                                  struct rsnd_dai_stream *io)
 {
+       struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+
        if (!__rsnd_ssi_is_pin_sharing(mod))
                return;
 
+       if (!rsnd_rdai_is_clk_master(rdai))
+               return;
+
        switch (rsnd_mod_id(mod)) {
        case 1:
        case 2:
@@ -647,6 +606,20 @@ static void rsnd_ssi_parent_attach(struct rsnd_mod *mod,
        }
 }
 
+static int rsnd_ssi_pcm_new(struct rsnd_mod *mod,
+                           struct rsnd_dai_stream *io,
+                           struct snd_soc_pcm_runtime *rtd)
+{
+       /*
+        * rsnd_rdai_is_clk_master() will be enabled after set_fmt,
+        * and, pcm_new will be called after it.
+        * This function reuse pcm_new at this point.
+        */
+       rsnd_ssi_parent_attach(mod, io);
+
+       return 0;
+}
+
 static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
                                 struct rsnd_dai_stream *io,
                                 struct rsnd_priv *priv)
@@ -662,7 +635,10 @@ static int rsnd_ssi_common_probe(struct rsnd_mod *mod,
        if (rsnd_ssi_is_multi_slave(mod, io))
                return 0;
 
-       rsnd_ssi_parent_attach(mod, io, priv);
+       /*
+        * It can't judge ssi parent at this point
+        * see rsnd_ssi_pcm_new()
+        */
 
        ret = rsnd_ssiu_attach(io, mod);
        if (ret < 0)
@@ -683,6 +659,8 @@ static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
        .quit   = rsnd_ssi_quit,
        .start  = rsnd_ssi_start,
        .stop   = rsnd_ssi_stop,
+       .irq    = rsnd_ssi_irq,
+       .pcm_new = rsnd_ssi_pcm_new,
        .hw_params = rsnd_ssi_hw_params,
 };
 
@@ -705,9 +683,8 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
        if (ret)
                return ret;
 
-       ssi->dma = rsnd_dma_attach(io, mod, dma_id);
-       if (IS_ERR(ssi->dma))
-               return PTR_ERR(ssi->dma);
+       /* SSI probe might be called many times in MUX multi path */
+       ret = rsnd_dma_attach(io, mod, &ssi->dma, dma_id);
 
        return ret;
 }
@@ -772,6 +749,8 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
        .quit   = rsnd_ssi_quit,
        .start  = rsnd_ssi_start,
        .stop   = rsnd_ssi_stop,
+       .irq    = rsnd_ssi_irq,
+       .pcm_new = rsnd_ssi_pcm_new,
        .fallback = rsnd_ssi_fallback,
        .hw_params = rsnd_ssi_hw_params,
 };
@@ -858,6 +837,41 @@ int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
        return !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_CLK_PIN_SHARE);
 }
 
+static u32 *rsnd_ssi_get_status(struct rsnd_dai_stream *io,
+                               struct rsnd_mod *mod,
+                               enum rsnd_mod_type type)
+{
+       /*
+        * SSIP (= SSI parent) needs to be special, otherwise,
+        * 2nd SSI might doesn't start. see also rsnd_mod_call()
+        *
+        * We can't include parent SSI status on SSI, because we don't know
+        * how many SSI requests parent SSI. Thus, it is localed on "io" now.
+        * ex) trouble case
+        *      Playback: SSI0
+        *      Capture : SSI1 (needs SSI0)
+        *
+        * 1) start Capture  -> SSI0/SSI1 are started.
+        * 2) start Playback -> SSI0 doesn't work, because it is already
+        *                      marked as "started" on 1)
+        *
+        * OTOH, using each mod's status is good for MUX case.
+        * It doesn't need to start in 2nd start
+        * ex)
+        *      IO-0: SRC0 -> CTU1 -+-> MUX -> DVC -> SSIU -> SSI0
+        *                          |
+        *      IO-1: SRC1 -> CTU2 -+
+        *
+        * 1) start IO-0 ->     start SSI0
+        * 2) start IO-1 ->     SSI0 doesn't need to start, because it is
+        *                      already started on 1)
+        */
+       if (type == RSND_MOD_SSIP)
+               return &io->parent_ssi_status;
+
+       return rsnd_mod_get_status(io, mod, type);
+}
+
 int rsnd_ssi_probe(struct rsnd_priv *priv)
 {
        struct device_node *node;
@@ -920,7 +934,7 @@ int rsnd_ssi_probe(struct rsnd_priv *priv)
                        ops = &rsnd_ssi_dma_ops;
 
                ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
-                                   RSND_MOD_SSI, i);
+                                   rsnd_ssi_get_status, RSND_MOD_SSI, i);
                if (ret)
                        goto rsnd_ssi_probe_done;
 
index 06d72828e5bcdc210ceb1234880abd4d7a50186b..6f9b388ec5a8f0c2095a2a88e33d725244706b2e 100644 (file)
@@ -27,7 +27,7 @@ static int rsnd_ssiu_init(struct rsnd_mod *mod,
                          struct rsnd_priv *priv)
 {
        struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
-       u32 multi_ssi_slaves = rsnd_ssi_multi_slaves(io);
+       u32 multi_ssi_slaves = rsnd_ssi_multi_slaves_runtime(io);
        int use_busif = rsnd_ssi_use_busif(io);
        int id = rsnd_mod_id(mod);
        u32 mask1, val1;
@@ -105,7 +105,7 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
        if (ret < 0)
                return ret;
 
-       if (rsnd_get_slot_width(io) >= 6) {
+       if (rsnd_runtime_is_ssi_tdm(io)) {
                /*
                 * TDM Extend Mode
                 * see
@@ -115,13 +115,14 @@ static int rsnd_ssiu_init_gen2(struct rsnd_mod *mod,
        }
 
        if (rsnd_ssi_use_busif(io)) {
-               u32 val = rsnd_get_dalign(mod, io);
-
                rsnd_mod_write(mod, SSI_BUSIF_ADINR,
                               rsnd_get_adinr_bit(mod, io) |
-                              rsnd_get_adinr_chan(mod, io));
+                              (rsnd_io_is_play(io) ?
+                               rsnd_runtime_channel_after_ctu(io) :
+                               rsnd_runtime_channel_original(io)));
                rsnd_mod_write(mod, SSI_BUSIF_MODE,  1);
-               rsnd_mod_write(mod, SSI_BUSIF_DALIGN, val);
+               rsnd_mod_write(mod, SSI_BUSIF_DALIGN,
+                              rsnd_get_dalign(mod, io));
        }
 
        return 0;
@@ -136,7 +137,7 @@ static int rsnd_ssiu_start_gen2(struct rsnd_mod *mod,
 
        rsnd_mod_write(mod, SSI_CTRL, 0x1);
 
-       if (rsnd_ssi_multi_slaves(io))
+       if (rsnd_ssi_multi_slaves_runtime(io))
                rsnd_mod_write(mod, SSI_CONTROL, 0x1);
 
        return 0;
@@ -151,7 +152,7 @@ static int rsnd_ssiu_stop_gen2(struct rsnd_mod *mod,
 
        rsnd_mod_write(mod, SSI_CTRL, 0);
 
-       if (rsnd_ssi_multi_slaves(io))
+       if (rsnd_ssi_multi_slaves_runtime(io))
                rsnd_mod_write(mod, SSI_CONTROL, 0);
 
        return 0;
@@ -206,7 +207,8 @@ int rsnd_ssiu_probe(struct rsnd_priv *priv)
 
        for_each_rsnd_ssiu(ssiu, priv, i) {
                ret = rsnd_mod_init(priv, rsnd_mod_get(ssiu),
-                                   ops, NULL, RSND_MOD_SSIU, i);
+                                   ops, NULL, rsnd_mod_get_status,
+                                   RSND_MOD_SSIU, i);
                if (ret)
                        return ret;
        }
index 790ee2bf1a474b394a286f650b2772c8d3436c05..d2e62b159610bba4dce327a259affc62a92a6cde 100644 (file)
@@ -986,16 +986,16 @@ static int soc_bind_dai_link(struct snd_soc_card *card,
 
        dev_dbg(card->dev, "ASoC: binding %s\n", dai_link->name);
 
-       rtd = soc_new_pcm_runtime(card, dai_link);
-       if (!rtd)
-               return -ENOMEM;
-
        if (soc_is_dai_link_bound(card, dai_link)) {
                dev_dbg(card->dev, "ASoC: dai link %s already bound\n",
                        dai_link->name);
                return 0;
        }
 
+       rtd = soc_new_pcm_runtime(card, dai_link);
+       if (!rtd)
+               return -ENOMEM;
+
        cpu_dai_component.name = dai_link->cpu_name;
        cpu_dai_component.of_node = dai_link->cpu_of_node;
        cpu_dai_component.dai_name = dai_link->cpu_dai_name;
index 581175a51ecf730bb8c6a2271d137f049c2a445d..801ae1a81dfd8eab907b1273b641493c74f9ff58 100644 (file)
@@ -2805,7 +2805,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_add_routes);
 int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
                            const struct snd_soc_dapm_route *route, int num)
 {
-       int i, ret = 0;
+       int i;
 
        mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
        for (i = 0; i < num; i++) {
@@ -2814,7 +2814,7 @@ int snd_soc_dapm_del_routes(struct snd_soc_dapm_context *dapm,
        }
        mutex_unlock(&dapm->card->dapm_mutex);
 
-       return ret;
+       return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_del_routes);
 
index 1af4f23697a781416f89794050ab41fb631f0171..aa99dac31b3bff3ce6bb510b4b07bba637363371 100644 (file)
@@ -1867,18 +1867,6 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
                if (!snd_soc_dpcm_be_can_update(fe, be, stream))
                        continue;
 
-               /* only allow hw_params() if no connected FEs are running */
-               if (!snd_soc_dpcm_can_be_params(fe, be, stream))
-                       continue;
-
-               if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
-                   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
-                   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE))
-                       continue;
-
-               dev_dbg(be->dev, "ASoC: hw_params BE %s\n",
-                       dpcm->fe->dai_link->name);
-
                /* copy params for each dpcm */
                memcpy(&dpcm->hw_params, &fe->dpcm[stream].hw_params,
                                sizeof(struct snd_pcm_hw_params));
@@ -1895,6 +1883,18 @@ int dpcm_be_dai_hw_params(struct snd_soc_pcm_runtime *fe, int stream)
                        }
                }
 
+               /* only allow hw_params() if no connected FEs are running */
+               if (!snd_soc_dpcm_can_be_params(fe, be, stream))
+                       continue;
+
+               if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_OPEN) &&
+                   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
+                   (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE))
+                       continue;
+
+               dev_dbg(be->dev, "ASoC: hw_params BE %s\n",
+                       dpcm->fe->dai_link->name);
+
                ret = soc_pcm_hw_params(be_substream, &dpcm->hw_params);
                if (ret < 0) {
                        dev_err(dpcm->be->dev,
index 6963ba20991c10066fdad2ad65f3102fc5752424..1cf94d7fb9f4534801b83565227408b594f2ee59 100644 (file)
@@ -223,51 +223,6 @@ static int get_widget_id(int tplg_type)
        return -EINVAL;
 }
 
-static enum snd_soc_dobj_type get_dobj_mixer_type(
-       struct snd_soc_tplg_ctl_hdr *control_hdr)
-{
-       if (control_hdr == NULL)
-               return SND_SOC_DOBJ_NONE;
-
-       switch (control_hdr->ops.info) {
-       case SND_SOC_TPLG_CTL_VOLSW:
-       case SND_SOC_TPLG_CTL_VOLSW_SX:
-       case SND_SOC_TPLG_CTL_VOLSW_XR_SX:
-       case SND_SOC_TPLG_CTL_RANGE:
-       case SND_SOC_TPLG_CTL_STROBE:
-               return SND_SOC_DOBJ_MIXER;
-       case SND_SOC_TPLG_CTL_ENUM:
-       case SND_SOC_TPLG_CTL_ENUM_VALUE:
-               return SND_SOC_DOBJ_ENUM;
-       case SND_SOC_TPLG_CTL_BYTES:
-               return SND_SOC_DOBJ_BYTES;
-       default:
-               return SND_SOC_DOBJ_NONE;
-       }
-}
-
-static enum snd_soc_dobj_type get_dobj_type(struct snd_soc_tplg_hdr *hdr,
-       struct snd_soc_tplg_ctl_hdr *control_hdr)
-{
-       switch (hdr->type) {
-       case SND_SOC_TPLG_TYPE_MIXER:
-               return get_dobj_mixer_type(control_hdr);
-       case SND_SOC_TPLG_TYPE_DAPM_GRAPH:
-       case SND_SOC_TPLG_TYPE_MANIFEST:
-               return SND_SOC_DOBJ_NONE;
-       case SND_SOC_TPLG_TYPE_DAPM_WIDGET:
-               return SND_SOC_DOBJ_WIDGET;
-       case SND_SOC_TPLG_TYPE_DAI_LINK:
-               return SND_SOC_DOBJ_DAI_LINK;
-       case SND_SOC_TPLG_TYPE_PCM:
-               return SND_SOC_DOBJ_PCM;
-       case SND_SOC_TPLG_TYPE_CODEC_LINK:
-               return SND_SOC_DOBJ_CODEC_LINK;
-       default:
-               return SND_SOC_DOBJ_NONE;
-       }
-}
-
 static inline void soc_bind_err(struct soc_tplg *tplg,
        struct snd_soc_tplg_ctl_hdr *hdr, int index)
 {
@@ -330,12 +285,22 @@ static int soc_tplg_widget_load(struct soc_tplg *tplg,
        return 0;
 }
 
-/* pass dynamic FEs configurations to component driver */
-static int soc_tplg_pcm_dai_load(struct soc_tplg *tplg,
-       struct snd_soc_tplg_pcm_dai *pcm_dai, int num_pcm_dai)
+/* pass DAI configurations to component driver for extra intialization */
+static int soc_tplg_dai_load(struct soc_tplg *tplg,
+       struct snd_soc_dai_driver *dai_drv)
+{
+       if (tplg->comp && tplg->ops && tplg->ops->dai_load)
+               return tplg->ops->dai_load(tplg->comp, dai_drv);
+
+       return 0;
+}
+
+/* pass link configurations to component driver for extra intialization */
+static int soc_tplg_dai_link_load(struct soc_tplg *tplg,
+       struct snd_soc_dai_link *link)
 {
-       if (tplg->comp && tplg->ops && tplg->ops->pcm_dai_load)
-               return tplg->ops->pcm_dai_load(tplg->comp, pcm_dai, num_pcm_dai);
+       if (tplg->comp && tplg->ops && tplg->ops->link_load)
+               return tplg->ops->link_load(tplg->comp, link);
 
        return 0;
 }
@@ -495,18 +460,39 @@ static void remove_widget(struct snd_soc_component *comp,
        /* widget w is freed by soc-dapm.c */
 }
 
-/* remove PCM DAI configurations */
-static void remove_pcm_dai(struct snd_soc_component *comp,
+/* remove DAI configurations */
+static void remove_dai(struct snd_soc_component *comp,
+       struct snd_soc_dobj *dobj, int pass)
+{
+       struct snd_soc_dai_driver *dai_drv =
+               container_of(dobj, struct snd_soc_dai_driver, dobj);
+
+       if (pass != SOC_TPLG_PASS_PCM_DAI)
+               return;
+
+       if (dobj->ops && dobj->ops->dai_unload)
+               dobj->ops->dai_unload(comp, dobj);
+
+       list_del(&dobj->list);
+       kfree(dai_drv);
+}
+
+/* remove link configurations */
+static void remove_link(struct snd_soc_component *comp,
        struct snd_soc_dobj *dobj, int pass)
 {
+       struct snd_soc_dai_link *link =
+               container_of(dobj, struct snd_soc_dai_link, dobj);
+
        if (pass != SOC_TPLG_PASS_PCM_DAI)
                return;
 
-       if (dobj->ops && dobj->ops->pcm_dai_unload)
-               dobj->ops->pcm_dai_unload(comp, dobj);
+       if (dobj->ops && dobj->ops->link_unload)
+               dobj->ops->link_unload(comp, dobj);
 
        list_del(&dobj->list);
-       kfree(dobj);
+       snd_soc_remove_dai_link(comp->card, link);
+       kfree(link);
 }
 
 /* bind a kcontrol to it's IO handlers */
@@ -1544,18 +1530,116 @@ static int soc_tplg_dapm_complete(struct soc_tplg *tplg)
        return 0;
 }
 
-static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg,
+static void set_stream_info(struct snd_soc_pcm_stream *stream,
+       struct snd_soc_tplg_stream_caps *caps)
+{
+       stream->stream_name = kstrdup(caps->name, GFP_KERNEL);
+       stream->channels_min = caps->channels_min;
+       stream->channels_max = caps->channels_max;
+       stream->rates = caps->rates;
+       stream->rate_min = caps->rate_min;
+       stream->rate_max = caps->rate_max;
+       stream->formats = caps->formats;
+}
+
+static int soc_tplg_dai_create(struct soc_tplg *tplg,
+       struct snd_soc_tplg_pcm *pcm)
+{
+       struct snd_soc_dai_driver *dai_drv;
+       struct snd_soc_pcm_stream *stream;
+       struct snd_soc_tplg_stream_caps *caps;
+       int ret;
+
+       dai_drv = kzalloc(sizeof(struct snd_soc_dai_driver), GFP_KERNEL);
+       if (dai_drv == NULL)
+               return -ENOMEM;
+
+       dai_drv->name = pcm->dai_name;
+       dai_drv->id = pcm->dai_id;
+
+       if (pcm->playback) {
+               stream = &dai_drv->playback;
+               caps = &pcm->caps[SND_SOC_TPLG_STREAM_PLAYBACK];
+               set_stream_info(stream, caps);
+       }
+
+       if (pcm->capture) {
+               stream = &dai_drv->capture;
+               caps = &pcm->caps[SND_SOC_TPLG_STREAM_CAPTURE];
+               set_stream_info(stream, caps);
+       }
+
+       /* pass control to component driver for optional further init */
+       ret = soc_tplg_dai_load(tplg, dai_drv);
+       if (ret < 0) {
+               dev_err(tplg->comp->dev, "ASoC: DAI loading failed\n");
+               kfree(dai_drv);
+               return ret;
+       }
+
+       dai_drv->dobj.index = tplg->index;
+       dai_drv->dobj.ops = tplg->ops;
+       dai_drv->dobj.type = SND_SOC_DOBJ_PCM;
+       list_add(&dai_drv->dobj.list, &tplg->comp->dobj_list);
+
+       /* register the DAI to the component */
+       return snd_soc_register_dai(tplg->comp, dai_drv);
+}
+
+static int soc_tplg_link_create(struct soc_tplg *tplg,
+       struct snd_soc_tplg_pcm *pcm)
+{
+       struct snd_soc_dai_link *link;
+       int ret;
+
+       link = kzalloc(sizeof(struct snd_soc_dai_link), GFP_KERNEL);
+       if (link == NULL)
+               return -ENOMEM;
+
+       link->name = pcm->pcm_name;
+       link->stream_name = pcm->pcm_name;
+
+       /* pass control to component driver for optional further init */
+       ret = soc_tplg_dai_link_load(tplg, link);
+       if (ret < 0) {
+               dev_err(tplg->comp->dev, "ASoC: FE link loading failed\n");
+               kfree(link);
+               return ret;
+       }
+
+       link->dobj.index = tplg->index;
+       link->dobj.ops = tplg->ops;
+       link->dobj.type = SND_SOC_DOBJ_DAI_LINK;
+       list_add(&link->dobj.list, &tplg->comp->dobj_list);
+
+       snd_soc_add_dai_link(tplg->comp->card, link);
+       return 0;
+}
+
+/* create a FE DAI and DAI link from the PCM object */
+static int soc_tplg_pcm_create(struct soc_tplg *tplg,
+       struct snd_soc_tplg_pcm *pcm)
+{
+       int ret;
+
+       ret = soc_tplg_dai_create(tplg, pcm);
+       if (ret < 0)
+               return ret;
+
+       return  soc_tplg_link_create(tplg, pcm);
+}
+
+static int soc_tplg_pcm_elems_load(struct soc_tplg *tplg,
        struct snd_soc_tplg_hdr *hdr)
 {
-       struct snd_soc_tplg_pcm_dai *pcm_dai;
-       struct snd_soc_dobj *dobj;
+       struct snd_soc_tplg_pcm *pcm;
        int count = hdr->count;
-       int ret;
+       int i;
 
        if (tplg->pass != SOC_TPLG_PASS_PCM_DAI)
                return 0;
 
-       pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos;
+       pcm = (struct snd_soc_tplg_pcm *)tplg->pos;
 
        if (soc_tplg_check_elem_count(tplg,
                sizeof(struct snd_soc_tplg_pcm), count,
@@ -1565,31 +1649,16 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg,
                return -EINVAL;
        }
 
+       /* create the FE DAIs and DAI links */
+       for (i = 0; i < count; i++) {
+               soc_tplg_pcm_create(tplg, pcm);
+               pcm++;
+       }
+
        dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count);
        tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count;
 
-       dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL);
-       if (dobj == NULL)
-               return -ENOMEM;
-
-       /* Call the platform driver call back to register the dais */
-       ret = soc_tplg_pcm_dai_load(tplg, pcm_dai, count);
-       if (ret < 0) {
-               dev_err(tplg->comp->dev, "ASoC: PCM DAI loading failed\n");
-               goto err;
-       }
-
-       dobj->type = get_dobj_type(hdr, NULL);
-       dobj->pcm_dai.count = count;
-       dobj->pcm_dai.pd = pcm_dai;
-       dobj->ops = tplg->ops;
-       dobj->index = tplg->index;
-       list_add(&dobj->list, &tplg->comp->dobj_list);
        return 0;
-
-err:
-       kfree(dobj);
-       return ret;
 }
 
 static int soc_tplg_manifest_load(struct soc_tplg *tplg,
@@ -1681,9 +1750,7 @@ static int soc_tplg_load_header(struct soc_tplg *tplg,
        case SND_SOC_TPLG_TYPE_DAPM_WIDGET:
                return soc_tplg_dapm_widget_elems_load(tplg, hdr);
        case SND_SOC_TPLG_TYPE_PCM:
-       case SND_SOC_TPLG_TYPE_DAI_LINK:
-       case SND_SOC_TPLG_TYPE_CODEC_LINK:
-               return soc_tplg_pcm_dai_elems_load(tplg, hdr);
+               return soc_tplg_pcm_elems_load(tplg, hdr);
        case SND_SOC_TPLG_TYPE_MANIFEST:
                return soc_tplg_manifest_load(tplg, hdr);
        default:
@@ -1841,9 +1908,10 @@ int snd_soc_tplg_component_remove(struct snd_soc_component *comp, u32 index)
                                remove_widget(comp, dobj, pass);
                                break;
                        case SND_SOC_DOBJ_PCM:
+                               remove_dai(comp, dobj, pass);
+                               break;
                        case SND_SOC_DOBJ_DAI_LINK:
-                       case SND_SOC_DOBJ_CODEC_LINK:
-                               remove_pcm_dai(comp, dobj, pass);
+                               remove_link(comp, dobj, pass);
                                break;
                        default:
                                dev_err(comp->dev, "ASoC: invalid component type %d for removal\n",
index 84c72ec6ad732152d7798f0da01d3ceed17d31cd..ae42294ef688c4422b063fda841181a4b55fb349 100644 (file)
@@ -8,4 +8,12 @@ config SND_SUN4I_CODEC
          Select Y or M to add support for the Codec embedded in the Allwinner
          A10 and affiliated SoCs.
 
+config SND_SUN4I_SPDIF
+       tristate "Allwinner A10 SPDIF Support"
+       depends on OF
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+       select REGMAP_MMIO
+       help
+         Say Y or M to add support for the S/PDIF audio block in the Allwinner
+         A10 and affiliated SoCs.
 endmenu
index ea8a08c881d65666c228955cbf364c734805f64a..8f5e889667f11986d2e837be43c7173fff66774f 100644 (file)
@@ -1,2 +1,3 @@
 obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
 
+obj-$(CONFIG_SND_SUN4I_SPDIF) += sun4i-spdif.o
diff --git a/sound/soc/sunxi/sun4i-spdif.c b/sound/soc/sunxi/sun4i-spdif.c
new file mode 100644 (file)
index 0000000..0b04fb0
--- /dev/null
@@ -0,0 +1,550 @@
+/*
+ * ALSA SoC SPDIF Audio Layer
+ *
+ * Copyright 2015 Andrea Venturi <be17068@iperbole.bo.it>
+ * Copyright 2015 Marcus Cooper <codekipper@gmail.com>
+ *
+ * Based on the Allwinner SDK driver, released under the GPL.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/clk.h>
+#include <linux/delay.h>
+#include <linux/device.h>
+#include <linux/kernel.h>
+#include <linux/init.h>
+#include <linux/regmap.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/ioport.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/pm_runtime.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+
+#define        SUN4I_SPDIF_CTL         (0x00)
+       #define SUN4I_SPDIF_CTL_MCLKDIV(v)              ((v) << 4) /* v even */
+       #define SUN4I_SPDIF_CTL_MCLKOUTEN               BIT(2)
+       #define SUN4I_SPDIF_CTL_GEN                     BIT(1)
+       #define SUN4I_SPDIF_CTL_RESET                   BIT(0)
+
+#define SUN4I_SPDIF_TXCFG      (0x04)
+       #define SUN4I_SPDIF_TXCFG_SINGLEMOD             BIT(31)
+       #define SUN4I_SPDIF_TXCFG_ASS                   BIT(17)
+       #define SUN4I_SPDIF_TXCFG_NONAUDIO              BIT(16)
+       #define SUN4I_SPDIF_TXCFG_TXRATIO(v)            ((v) << 4)
+       #define SUN4I_SPDIF_TXCFG_TXRATIO_MASK          GENMASK(8, 4)
+       #define SUN4I_SPDIF_TXCFG_FMTRVD                GENMASK(3, 2)
+       #define SUN4I_SPDIF_TXCFG_FMT16BIT              (0 << 2)
+       #define SUN4I_SPDIF_TXCFG_FMT20BIT              (1 << 2)
+       #define SUN4I_SPDIF_TXCFG_FMT24BIT              (2 << 2)
+       #define SUN4I_SPDIF_TXCFG_CHSTMODE              BIT(1)
+       #define SUN4I_SPDIF_TXCFG_TXEN                  BIT(0)
+
+#define SUN4I_SPDIF_RXCFG      (0x08)
+       #define SUN4I_SPDIF_RXCFG_LOCKFLAG              BIT(4)
+       #define SUN4I_SPDIF_RXCFG_CHSTSRC               BIT(3)
+       #define SUN4I_SPDIF_RXCFG_CHSTCP                BIT(1)
+       #define SUN4I_SPDIF_RXCFG_RXEN                  BIT(0)
+
+#define SUN4I_SPDIF_TXFIFO     (0x0C)
+
+#define SUN4I_SPDIF_RXFIFO     (0x10)
+
+#define SUN4I_SPDIF_FCTL       (0x14)
+       #define SUN4I_SPDIF_FCTL_FIFOSRC                BIT(31)
+       #define SUN4I_SPDIF_FCTL_FTX                    BIT(17)
+       #define SUN4I_SPDIF_FCTL_FRX                    BIT(16)
+       #define SUN4I_SPDIF_FCTL_TXTL(v)                ((v) << 8)
+       #define SUN4I_SPDIF_FCTL_TXTL_MASK              GENMASK(12, 8)
+       #define SUN4I_SPDIF_FCTL_RXTL(v)                ((v) << 3)
+       #define SUN4I_SPDIF_FCTL_RXTL_MASK              GENMASK(7, 3)
+       #define SUN4I_SPDIF_FCTL_TXIM                   BIT(2)
+       #define SUN4I_SPDIF_FCTL_RXOM(v)                ((v) << 0)
+       #define SUN4I_SPDIF_FCTL_RXOM_MASK              GENMASK(1, 0)
+
+#define SUN4I_SPDIF_FSTA       (0x18)
+       #define SUN4I_SPDIF_FSTA_TXE                    BIT(14)
+       #define SUN4I_SPDIF_FSTA_TXECNTSHT              (8)
+       #define SUN4I_SPDIF_FSTA_RXA                    BIT(6)
+       #define SUN4I_SPDIF_FSTA_RXACNTSHT              (0)
+
+#define SUN4I_SPDIF_INT                (0x1C)
+       #define SUN4I_SPDIF_INT_RXLOCKEN                BIT(18)
+       #define SUN4I_SPDIF_INT_RXUNLOCKEN              BIT(17)
+       #define SUN4I_SPDIF_INT_RXPARERREN              BIT(16)
+       #define SUN4I_SPDIF_INT_TXDRQEN                 BIT(7)
+       #define SUN4I_SPDIF_INT_TXUIEN                  BIT(6)
+       #define SUN4I_SPDIF_INT_TXOIEN                  BIT(5)
+       #define SUN4I_SPDIF_INT_TXEIEN                  BIT(4)
+       #define SUN4I_SPDIF_INT_RXDRQEN                 BIT(2)
+       #define SUN4I_SPDIF_INT_RXOIEN                  BIT(1)
+       #define SUN4I_SPDIF_INT_RXAIEN                  BIT(0)
+
+#define SUN4I_SPDIF_ISTA       (0x20)
+       #define SUN4I_SPDIF_ISTA_RXLOCKSTA              BIT(18)
+       #define SUN4I_SPDIF_ISTA_RXUNLOCKSTA            BIT(17)
+       #define SUN4I_SPDIF_ISTA_RXPARERRSTA            BIT(16)
+       #define SUN4I_SPDIF_ISTA_TXUSTA                 BIT(6)
+       #define SUN4I_SPDIF_ISTA_TXOSTA                 BIT(5)
+       #define SUN4I_SPDIF_ISTA_TXESTA                 BIT(4)
+       #define SUN4I_SPDIF_ISTA_RXOSTA                 BIT(1)
+       #define SUN4I_SPDIF_ISTA_RXASTA                 BIT(0)
+
+#define SUN4I_SPDIF_TXCNT      (0x24)
+
+#define SUN4I_SPDIF_RXCNT      (0x28)
+
+#define SUN4I_SPDIF_TXCHSTA0   (0x2C)
+       #define SUN4I_SPDIF_TXCHSTA0_CLK(v)             ((v) << 28)
+       #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ(v)         ((v) << 24)
+       #define SUN4I_SPDIF_TXCHSTA0_SAMFREQ_MASK       GENMASK(27, 24)
+       #define SUN4I_SPDIF_TXCHSTA0_CHNUM(v)           ((v) << 20)
+       #define SUN4I_SPDIF_TXCHSTA0_CHNUM_MASK         GENMASK(23, 20)
+       #define SUN4I_SPDIF_TXCHSTA0_SRCNUM(v)          ((v) << 16)
+       #define SUN4I_SPDIF_TXCHSTA0_CATACOD(v)         ((v) << 8)
+       #define SUN4I_SPDIF_TXCHSTA0_MODE(v)            ((v) << 6)
+       #define SUN4I_SPDIF_TXCHSTA0_EMPHASIS(v)        ((v) << 3)
+       #define SUN4I_SPDIF_TXCHSTA0_CP                 BIT(2)
+       #define SUN4I_SPDIF_TXCHSTA0_AUDIO              BIT(1)
+       #define SUN4I_SPDIF_TXCHSTA0_PRO                BIT(0)
+
+#define SUN4I_SPDIF_TXCHSTA1   (0x30)
+       #define SUN4I_SPDIF_TXCHSTA1_CGMSA(v)           ((v) << 8)
+       #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ(v)      ((v) << 4)
+       #define SUN4I_SPDIF_TXCHSTA1_ORISAMFREQ_MASK    GENMASK(7, 4)
+       #define SUN4I_SPDIF_TXCHSTA1_SAMWORDLEN(v)      ((v) << 1)
+       #define SUN4I_SPDIF_TXCHSTA1_MAXWORDLEN         BIT(0)
+
+#define SUN4I_SPDIF_RXCHSTA0   (0x34)
+       #define SUN4I_SPDIF_RXCHSTA0_CLK(v)             ((v) << 28)
+       #define SUN4I_SPDIF_RXCHSTA0_SAMFREQ(v)         ((v) << 24)
+       #define SUN4I_SPDIF_RXCHSTA0_CHNUM(v)           ((v) << 20)
+       #define SUN4I_SPDIF_RXCHSTA0_SRCNUM(v)          ((v) << 16)
+       #define SUN4I_SPDIF_RXCHSTA0_CATACOD(v)         ((v) << 8)
+       #define SUN4I_SPDIF_RXCHSTA0_MODE(v)            ((v) << 6)
+       #define SUN4I_SPDIF_RXCHSTA0_EMPHASIS(v)        ((v) << 3)
+       #define SUN4I_SPDIF_RXCHSTA0_CP                 BIT(2)
+       #define SUN4I_SPDIF_RXCHSTA0_AUDIO              BIT(1)
+       #define SUN4I_SPDIF_RXCHSTA0_PRO                BIT(0)
+
+#define SUN4I_SPDIF_RXCHSTA1   (0x38)
+       #define SUN4I_SPDIF_RXCHSTA1_CGMSA(v)           ((v) << 8)
+       #define SUN4I_SPDIF_RXCHSTA1_ORISAMFREQ(v)      ((v) << 4)
+       #define SUN4I_SPDIF_RXCHSTA1_SAMWORDLEN(v)      ((v) << 1)
+       #define SUN4I_SPDIF_RXCHSTA1_MAXWORDLEN         BIT(0)
+
+/* Defines for Sampling Frequency */
+#define SUN4I_SPDIF_SAMFREQ_44_1KHZ            0x0
+#define SUN4I_SPDIF_SAMFREQ_NOT_INDICATED      0x1
+#define SUN4I_SPDIF_SAMFREQ_48KHZ              0x2
+#define SUN4I_SPDIF_SAMFREQ_32KHZ              0x3
+#define SUN4I_SPDIF_SAMFREQ_22_05KHZ           0x4
+#define SUN4I_SPDIF_SAMFREQ_24KHZ              0x6
+#define SUN4I_SPDIF_SAMFREQ_88_2KHZ            0x8
+#define SUN4I_SPDIF_SAMFREQ_76_8KHZ            0x9
+#define SUN4I_SPDIF_SAMFREQ_96KHZ              0xa
+#define SUN4I_SPDIF_SAMFREQ_176_4KHZ           0xc
+#define SUN4I_SPDIF_SAMFREQ_192KHZ             0xe
+
+struct sun4i_spdif_dev {
+       struct platform_device *pdev;
+       struct clk *spdif_clk;
+       struct clk *apb_clk;
+       struct snd_soc_dai_driver cpu_dai_drv;
+       struct regmap *regmap;
+       struct snd_dmaengine_dai_dma_data dma_params_tx;
+};
+
+static void sun4i_spdif_configure(struct sun4i_spdif_dev *host)
+{
+       /* soft reset SPDIF */
+       regmap_write(host->regmap, SUN4I_SPDIF_CTL, SUN4I_SPDIF_CTL_RESET);
+
+       /* flush TX FIFO */
+       regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL,
+                          SUN4I_SPDIF_FCTL_FTX, SUN4I_SPDIF_FCTL_FTX);
+
+       /* clear TX counter */
+       regmap_write(host->regmap, SUN4I_SPDIF_TXCNT, 0);
+}
+
+static void sun4i_snd_txctrl_on(struct snd_pcm_substream *substream,
+                               struct sun4i_spdif_dev *host)
+{
+       if (substream->runtime->channels == 1)
+               regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
+                                  SUN4I_SPDIF_TXCFG_SINGLEMOD,
+                                  SUN4I_SPDIF_TXCFG_SINGLEMOD);
+
+       /* SPDIF TX ENABLE */
+       regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
+                          SUN4I_SPDIF_TXCFG_TXEN, SUN4I_SPDIF_TXCFG_TXEN);
+
+       /* DRQ ENABLE */
+       regmap_update_bits(host->regmap, SUN4I_SPDIF_INT,
+                          SUN4I_SPDIF_INT_TXDRQEN, SUN4I_SPDIF_INT_TXDRQEN);
+
+       /* Global enable */
+       regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL,
+                          SUN4I_SPDIF_CTL_GEN, SUN4I_SPDIF_CTL_GEN);
+}
+
+static void sun4i_snd_txctrl_off(struct snd_pcm_substream *substream,
+                                struct sun4i_spdif_dev *host)
+{
+       /* SPDIF TX DISABLE */
+       regmap_update_bits(host->regmap, SUN4I_SPDIF_TXCFG,
+                          SUN4I_SPDIF_TXCFG_TXEN, 0);
+
+       /* DRQ DISABLE */
+       regmap_update_bits(host->regmap, SUN4I_SPDIF_INT,
+                          SUN4I_SPDIF_INT_TXDRQEN, 0);
+
+       /* Global disable */
+       regmap_update_bits(host->regmap, SUN4I_SPDIF_CTL,
+                          SUN4I_SPDIF_CTL_GEN, 0);
+}
+
+static int sun4i_spdif_startup(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *cpu_dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+               return -EINVAL;
+
+       sun4i_spdif_configure(host);
+
+       return 0;
+}
+
+static int sun4i_spdif_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *cpu_dai)
+{
+       int ret = 0;
+       int fmt;
+       unsigned long rate = params_rate(params);
+       u32 mclk_div = 0;
+       unsigned int mclk = 0;
+       u32 reg_val;
+       struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(cpu_dai);
+       struct platform_device *pdev = host->pdev;
+
+       /* Add the PCM and raw data select interface */
+       switch (params_channels(params)) {
+       case 1: /* PCM mode */
+       case 2:
+               fmt = 0;
+               break;
+       case 4: /* raw data mode */
+               fmt = SUN4I_SPDIF_TXCFG_NONAUDIO;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               fmt |= SUN4I_SPDIF_TXCFG_FMT16BIT;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               fmt |= SUN4I_SPDIF_TXCFG_FMT20BIT;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               fmt |= SUN4I_SPDIF_TXCFG_FMT24BIT;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (rate) {
+       case 22050:
+       case 44100:
+       case 88200:
+       case 176400:
+               mclk = 22579200;
+               break;
+       case 24000:
+       case 32000:
+       case 48000:
+       case 96000:
+       case 192000:
+               mclk = 24576000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = clk_set_rate(host->spdif_clk, mclk);
+       if (ret < 0) {
+               dev_err(&pdev->dev,
+                       "Setting SPDIF clock rate for %d Hz failed!\n", mclk);
+               return ret;
+       }
+
+       regmap_update_bits(host->regmap, SUN4I_SPDIF_FCTL,
+                          SUN4I_SPDIF_FCTL_TXIM, SUN4I_SPDIF_FCTL_TXIM);
+
+       switch (rate) {
+       case 22050:
+       case 24000:
+               mclk_div = 8;
+               break;
+       case 32000:
+               mclk_div = 6;
+               break;
+       case 44100:
+       case 48000:
+               mclk_div = 4;
+               break;
+       case 88200:
+       case 96000:
+               mclk_div = 2;
+               break;
+       case 176400:
+       case 192000:
+               mclk_div = 1;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       reg_val = 0;
+       reg_val |= SUN4I_SPDIF_TXCFG_ASS;
+       reg_val |= fmt; /* set non audio and bit depth */
+       reg_val |= SUN4I_SPDIF_TXCFG_CHSTMODE;
+       reg_val |= SUN4I_SPDIF_TXCFG_TXRATIO(mclk_div - 1);
+       regmap_write(host->regmap, SUN4I_SPDIF_TXCFG, reg_val);
+
+       return 0;
+}
+
+static int sun4i_spdif_trigger(struct snd_pcm_substream *substream, int cmd,
+                              struct snd_soc_dai *dai)
+{
+       int ret = 0;
+       struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai);
+
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+               return -EINVAL;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               sun4i_snd_txctrl_on(substream, host);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               sun4i_snd_txctrl_off(substream, host);
+               break;
+
+       default:
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
+static int sun4i_spdif_soc_dai_probe(struct snd_soc_dai *dai)
+{
+       struct sun4i_spdif_dev *host = snd_soc_dai_get_drvdata(dai);
+
+       snd_soc_dai_init_dma_data(dai, &host->dma_params_tx, NULL);
+       return 0;
+}
+
+static const struct snd_soc_dai_ops sun4i_spdif_dai_ops = {
+       .startup        = sun4i_spdif_startup,
+       .trigger        = sun4i_spdif_trigger,
+       .hw_params      = sun4i_spdif_hw_params,
+};
+
+static const struct regmap_config sun4i_spdif_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = SUN4I_SPDIF_RXCHSTA1,
+};
+
+#define SUN4I_RATES    SNDRV_PCM_RATE_8000_192000
+
+#define SUN4I_FORMATS  (SNDRV_PCM_FORMAT_S16_LE | \
+                               SNDRV_PCM_FORMAT_S20_3LE | \
+                               SNDRV_PCM_FORMAT_S24_LE)
+
+static struct snd_soc_dai_driver sun4i_spdif_dai = {
+       .playback = {
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SUN4I_RATES,
+               .formats = SUN4I_FORMATS,
+       },
+       .probe = sun4i_spdif_soc_dai_probe,
+       .ops = &sun4i_spdif_dai_ops,
+       .name = "spdif",
+};
+
+static const struct snd_soc_dapm_widget dit_widgets[] = {
+       SND_SOC_DAPM_OUTPUT("spdif-out"),
+};
+
+static const struct snd_soc_dapm_route dit_routes[] = {
+       { "spdif-out", NULL, "Playback" },
+};
+
+static const struct of_device_id sun4i_spdif_of_match[] = {
+       { .compatible = "allwinner,sun4i-a10-spdif", },
+       { /* sentinel */ }
+};
+MODULE_DEVICE_TABLE(of, sun4i_spdif_of_match);
+
+static const struct snd_soc_component_driver sun4i_spdif_component = {
+       .name           = "sun4i-spdif",
+};
+
+static int sun4i_spdif_runtime_suspend(struct device *dev)
+{
+       struct sun4i_spdif_dev *host  = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(host->spdif_clk);
+       clk_disable_unprepare(host->apb_clk);
+
+       return 0;
+}
+
+static int sun4i_spdif_runtime_resume(struct device *dev)
+{
+       struct sun4i_spdif_dev *host  = dev_get_drvdata(dev);
+
+       clk_prepare_enable(host->spdif_clk);
+       clk_prepare_enable(host->apb_clk);
+
+       return 0;
+}
+
+static int sun4i_spdif_probe(struct platform_device *pdev)
+{
+       struct sun4i_spdif_dev *host;
+       struct resource *res;
+       int ret;
+       void __iomem *base;
+
+       dev_dbg(&pdev->dev, "Entered %s\n", __func__);
+
+       host = devm_kzalloc(&pdev->dev, sizeof(*host), GFP_KERNEL);
+       if (!host)
+               return -ENOMEM;
+
+       host->pdev = pdev;
+
+       /* Initialize this copy of the CPU DAI driver structure */
+       memcpy(&host->cpu_dai_drv, &sun4i_spdif_dai, sizeof(sun4i_spdif_dai));
+       host->cpu_dai_drv.name = dev_name(&pdev->dev);
+
+       /* Get the addresses */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base))
+               return PTR_ERR(base);
+
+       host->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+                                               &sun4i_spdif_regmap_config);
+
+       /* Clocks */
+       host->apb_clk = devm_clk_get(&pdev->dev, "apb");
+       if (IS_ERR(host->apb_clk)) {
+               dev_err(&pdev->dev, "failed to get a apb clock.\n");
+               return PTR_ERR(host->apb_clk);
+       }
+
+       host->spdif_clk = devm_clk_get(&pdev->dev, "spdif");
+       if (IS_ERR(host->spdif_clk)) {
+               dev_err(&pdev->dev, "failed to get a spdif clock.\n");
+               ret = PTR_ERR(host->spdif_clk);
+               goto err_disable_apb_clk;
+       }
+
+       host->dma_params_tx.addr = res->start + SUN4I_SPDIF_TXFIFO;
+       host->dma_params_tx.maxburst = 4;
+       host->dma_params_tx.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+       platform_set_drvdata(pdev, host);
+
+       ret = devm_snd_soc_register_component(&pdev->dev,
+                               &sun4i_spdif_component, &sun4i_spdif_dai, 1);
+       if (ret)
+               goto err_disable_apb_clk;
+
+       pm_runtime_enable(&pdev->dev);
+       if (!pm_runtime_enabled(&pdev->dev)) {
+               ret = sun4i_spdif_runtime_resume(&pdev->dev);
+               if (ret)
+                       goto err_unregister;
+       }
+
+       ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+       if (ret)
+               goto err_suspend;
+       return 0;
+err_suspend:
+       if (!pm_runtime_status_suspended(&pdev->dev))
+               sun4i_spdif_runtime_suspend(&pdev->dev);
+err_unregister:
+       pm_runtime_disable(&pdev->dev);
+       snd_soc_unregister_component(&pdev->dev);
+err_disable_apb_clk:
+       clk_disable_unprepare(host->apb_clk);
+       return ret;
+}
+
+static int sun4i_spdif_remove(struct platform_device *pdev)
+{
+       pm_runtime_disable(&pdev->dev);
+       if (!pm_runtime_status_suspended(&pdev->dev))
+               sun4i_spdif_runtime_suspend(&pdev->dev);
+
+       snd_soc_unregister_platform(&pdev->dev);
+       snd_soc_unregister_component(&pdev->dev);
+
+       return 0;
+}
+
+static const struct dev_pm_ops sun4i_spdif_pm = {
+       SET_RUNTIME_PM_OPS(sun4i_spdif_runtime_suspend,
+                          sun4i_spdif_runtime_resume, NULL)
+};
+
+static struct platform_driver sun4i_spdif_driver = {
+       .driver         = {
+               .name   = "sun4i-spdif",
+               .of_match_table = of_match_ptr(sun4i_spdif_of_match),
+               .pm     = &sun4i_spdif_pm,
+       },
+       .probe          = sun4i_spdif_probe,
+       .remove         = sun4i_spdif_remove,
+};
+
+module_platform_driver(sun4i_spdif_driver);
+
+MODULE_AUTHOR("Marcus Cooper <codekipper@gmail.com>");
+MODULE_AUTHOR("Andrea Venturi <be17068@iperbole.bo.it>");
+MODULE_DESCRIPTION("Allwinner sun4i SPDIF SoC Interface");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:sun4i-spdif");
index 258cf7015ce2f443e97c933dd397780de43cf6e7..63244bbba8c76d7e738a3a0c962b3f623cdd9d7f 100644 (file)
@@ -83,6 +83,7 @@ static int pid[SNDRV_CARDS] = { [0 ... (SNDRV_CARDS-1)] = -1 };
 static int device_setup[SNDRV_CARDS]; /* device parameter for this card */
 static bool ignore_ctl_error;
 static bool autoclock = true;
+static char *quirk_alias[SNDRV_CARDS];
 
 module_param_array(index, int, NULL, 0444);
 MODULE_PARM_DESC(index, "Index value for the USB audio adapter.");
@@ -101,6 +102,8 @@ MODULE_PARM_DESC(ignore_ctl_error,
                 "Ignore errors from USB controller for mixer interfaces.");
 module_param(autoclock, bool, 0444);
 MODULE_PARM_DESC(autoclock, "Enable auto-clock selection for UAC2 devices (default: yes).");
+module_param_array(quirk_alias, charp, NULL, 0444);
+MODULE_PARM_DESC(quirk_alias, "Quirk aliases, e.g. 0123abcd:5678beef.");
 
 /*
  * we keep the snd_usb_audio_t instances by ourselves for merging
@@ -172,8 +175,9 @@ static int snd_usb_create_stream(struct snd_usb_audio *chip, int ctrlif, int int
        if ((altsd->bInterfaceClass == USB_CLASS_AUDIO ||
             altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC) &&
            altsd->bInterfaceSubClass == USB_SUBCLASS_MIDISTREAMING) {
-               int err = snd_usbmidi_create(chip->card, iface,
-                                            &chip->midi_list, NULL);
+               int err = __snd_usbmidi_create(chip->card, iface,
+                                            &chip->midi_list, NULL,
+                                            chip->usb_id);
                if (err < 0) {
                        dev_err(&dev->dev,
                                "%u:%d: cannot create sequencer device\n",
@@ -312,6 +316,7 @@ static int snd_usb_audio_free(struct snd_usb_audio *chip)
                snd_usb_endpoint_free(ep);
 
        mutex_destroy(&chip->mutex);
+       dev_set_drvdata(&chip->dev->dev, NULL);
        kfree(chip);
        return 0;
 }
@@ -456,6 +461,48 @@ static int snd_usb_audio_create(struct usb_interface *intf,
        return 0;
 }
 
+/* look for a matching quirk alias id */
+static bool get_alias_id(struct usb_device *dev, unsigned int *id)
+{
+       int i;
+       unsigned int src, dst;
+
+       for (i = 0; i < ARRAY_SIZE(quirk_alias); i++) {
+               if (!quirk_alias[i] ||
+                   sscanf(quirk_alias[i], "%x:%x", &src, &dst) != 2 ||
+                   src != *id)
+                       continue;
+               dev_info(&dev->dev,
+                        "device (%04x:%04x): applying quirk alias %04x:%04x\n",
+                        USB_ID_VENDOR(*id), USB_ID_PRODUCT(*id),
+                        USB_ID_VENDOR(dst), USB_ID_PRODUCT(dst));
+               *id = dst;
+               return true;
+       }
+
+       return false;
+}
+
+static struct usb_device_id usb_audio_ids[]; /* defined below */
+
+/* look for the corresponding quirk */
+static const struct snd_usb_audio_quirk *
+get_alias_quirk(struct usb_device *dev, unsigned int id)
+{
+       const struct usb_device_id *p;
+
+       for (p = usb_audio_ids; p->match_flags; p++) {
+               /* FIXME: this checks only vendor:product pair in the list */
+               if ((p->match_flags & USB_DEVICE_ID_MATCH_DEVICE) ==
+                   USB_DEVICE_ID_MATCH_DEVICE &&
+                   p->idVendor == USB_ID_VENDOR(id) &&
+                   p->idProduct == USB_ID_PRODUCT(id))
+                       return (const struct snd_usb_audio_quirk *)p->driver_info;
+       }
+
+       return NULL;
+}
+
 /*
  * probe the active usb device
  *
@@ -482,10 +529,12 @@ static int usb_audio_probe(struct usb_interface *intf,
        ifnum = get_iface_desc(alts)->bInterfaceNumber;
        id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
                    le16_to_cpu(dev->descriptor.idProduct));
+       if (get_alias_id(dev, &id))
+               quirk = get_alias_quirk(dev, id);
        if (quirk && quirk->ifnum >= 0 && ifnum != quirk->ifnum)
                return -ENXIO;
 
-       err = snd_usb_apply_boot_quirk(dev, intf, quirk);
+       err = snd_usb_apply_boot_quirk(dev, intf, quirk, id);
        if (err < 0)
                return err;
 
@@ -504,6 +553,7 @@ static int usb_audio_probe(struct usb_interface *intf,
                                goto __error;
                        }
                        chip = usb_chip[i];
+                       dev_set_drvdata(&dev->dev, chip);
                        atomic_inc(&chip->active); /* avoid autopm */
                        break;
                }
index 2ed260b10f6dc02cd129550ba1067c878034cb07..7ccbcaf6a1476423e0d2becf0bf91f3463a47b46 100644 (file)
@@ -285,6 +285,8 @@ static int set_sample_rate_v1(struct snd_usb_audio *chip, int iface,
        unsigned char data[3];
        int err, crate;
 
+       if (get_iface_desc(alts)->bNumEndpoints < 1)
+               return -EINVAL;
        ep = get_endpoint(alts, 0)->bEndpointAddress;
 
        /* if endpoint doesn't have sampling rate control, bail out */
index 7b1cb365ffab74d6028206adb8012226da963bac..c07a7eda42a28eee533752e28f787ccfd2230ed2 100644 (file)
@@ -438,6 +438,9 @@ static void snd_complete_urb(struct urb *urb)
  *
  * New endpoints will be added to chip->ep_list and must be freed by
  * calling snd_usb_endpoint_free().
+ *
+ * For SND_USB_ENDPOINT_TYPE_SYNC, the caller needs to guarantee that
+ * bNumEndpoints > 1 beforehand.
  */
 struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
                                              struct usb_host_interface *alts,
index 007cf583112154bc56616234d25e92f4e5e8212c..47de8af42f16d031b8075dd10ebff204109fc391 100644 (file)
@@ -2320,10 +2320,11 @@ EXPORT_SYMBOL(snd_usbmidi_resume);
 /*
  * Creates and registers everything needed for a MIDI streaming interface.
  */
-int snd_usbmidi_create(struct snd_card *card,
-                      struct usb_interface *iface,
-                      struct list_head *midi_list,
-                      const struct snd_usb_audio_quirk *quirk)
+int __snd_usbmidi_create(struct snd_card *card,
+                        struct usb_interface *iface,
+                        struct list_head *midi_list,
+                        const struct snd_usb_audio_quirk *quirk,
+                        unsigned int usb_id)
 {
        struct snd_usb_midi *umidi;
        struct snd_usb_midi_endpoint_info endpoints[MIDI_MAX_ENDPOINTS];
@@ -2341,8 +2342,10 @@ int snd_usbmidi_create(struct snd_card *card,
        spin_lock_init(&umidi->disc_lock);
        init_rwsem(&umidi->disc_rwsem);
        mutex_init(&umidi->mutex);
-       umidi->usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
+       if (!usb_id)
+               usb_id = USB_ID(le16_to_cpu(umidi->dev->descriptor.idVendor),
                               le16_to_cpu(umidi->dev->descriptor.idProduct));
+       umidi->usb_id = usb_id;
        setup_timer(&umidi->error_timer, snd_usbmidi_error_timer,
                    (unsigned long)umidi);
 
@@ -2463,4 +2466,4 @@ int snd_usbmidi_create(struct snd_card *card,
        list_add_tail(&umidi->list, midi_list);
        return 0;
 }
-EXPORT_SYMBOL(snd_usbmidi_create);
+EXPORT_SYMBOL(__snd_usbmidi_create);
index ad8a3211f8e7b284342058592f796b6ff5e0a279..5e25a3fd6c1d8647eff2850732963a1ec7f225b3 100644 (file)
@@ -39,10 +39,20 @@ struct snd_usb_midi_endpoint_info {
 
 /* for QUIRK_MIDI_AKAI, data is NULL */
 
-int snd_usbmidi_create(struct snd_card *card,
+int __snd_usbmidi_create(struct snd_card *card,
+                        struct usb_interface *iface,
+                        struct list_head *midi_list,
+                        const struct snd_usb_audio_quirk *quirk,
+                        unsigned int usb_id);
+
+static inline int snd_usbmidi_create(struct snd_card *card,
                       struct usb_interface *iface,
                       struct list_head *midi_list,
-                      const struct snd_usb_audio_quirk *quirk);
+                      const struct snd_usb_audio_quirk *quirk)
+{
+       return __snd_usbmidi_create(card, iface, midi_list, quirk, 0);
+}
+
 void snd_usbmidi_input_stop(struct list_head *p);
 void snd_usbmidi_input_start(struct list_head *p);
 void snd_usbmidi_disconnect(struct list_head *p);
index 279025650568ec2936e270d33a1737fd5f504cd6..f6c3bf79af9a7d9f7cabe6b83c3573a59964b826 100644 (file)
@@ -1519,7 +1519,11 @@ static int snd_microii_spdif_default_get(struct snd_kcontrol *kcontrol,
 
        /* use known values for that card: interface#1 altsetting#1 */
        iface = usb_ifnum_to_if(chip->dev, 1);
+       if (!iface || iface->num_altsetting < 2)
+               return -EINVAL;
        alts = &iface->altsetting[1];
+       if (get_iface_desc(alts)->bNumEndpoints < 1)
+               return -EINVAL;
        ep = get_endpoint(alts, 0)->bEndpointAddress;
 
        err = snd_usb_ctl_msg(chip->dev,
index b0370d5f33f819755053aabebe28547890795add..0e4e0640c50486b608b4a3df4c83b18fd11cae17 100644 (file)
@@ -160,6 +160,8 @@ static int init_pitch_v1(struct snd_usb_audio *chip, int iface,
        unsigned char data[1];
        int err;
 
+       if (get_iface_desc(alts)->bNumEndpoints < 1)
+               return -EINVAL;
        ep = get_endpoint(alts, 0)->bEndpointAddress;
 
        data[0] = 1;
index c458d60d50300823e03e39109b715c94bf44cbbf..a889d433cbdffe2a4e5dee72991730fa30844670 100644 (file)
@@ -167,19 +167,20 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
        stream = (fp->endpoint & USB_DIR_IN)
                ? SNDRV_PCM_STREAM_CAPTURE : SNDRV_PCM_STREAM_PLAYBACK;
        err = snd_usb_add_audio_stream(chip, stream, fp);
-       if (err < 0) {
-               kfree(fp);
-               kfree(rate_table);
-               return err;
-       }
+       if (err < 0)
+               goto error;
        if (fp->iface != get_iface_desc(&iface->altsetting[0])->bInterfaceNumber ||
            fp->altset_idx >= iface->num_altsetting) {
-               kfree(fp);
-               kfree(rate_table);
-               return -EINVAL;
+               err = -EINVAL;
+               goto error;
        }
        alts = &iface->altsetting[fp->altset_idx];
        altsd = get_iface_desc(alts);
+       if (altsd->bNumEndpoints < 1) {
+               err = -EINVAL;
+               goto error;
+       }
+
        fp->protocol = altsd->bInterfaceProtocol;
 
        if (fp->datainterval == 0)
@@ -190,6 +191,11 @@ static int create_fixed_stream_quirk(struct snd_usb_audio *chip,
        snd_usb_init_pitch(chip, fp->iface, alts, fp);
        snd_usb_init_sample_rate(chip, fp->iface, alts, fp, fp->rate_max);
        return 0;
+
+ error:
+       kfree(fp);
+       kfree(rate_table);
+       return err;
 }
 
 static int create_auto_pcm_quirk(struct snd_usb_audio *chip,
@@ -446,8 +452,9 @@ static int create_uaxx_quirk(struct snd_usb_audio *chip,
                const struct snd_usb_audio_quirk *quirk =
                        chip->usb_id == USB_ID(0x0582, 0x002b)
                        ? &ua700_quirk : &uaxx_quirk;
-               return snd_usbmidi_create(chip->card, iface,
-                                         &chip->midi_list, quirk);
+               return __snd_usbmidi_create(chip->card, iface,
+                                         &chip->midi_list, quirk,
+                                         chip->usb_id);
        }
 
        if (altsd->bNumEndpoints != 1)
@@ -974,11 +981,9 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
 
 int snd_usb_apply_boot_quirk(struct usb_device *dev,
                             struct usb_interface *intf,
-                            const struct snd_usb_audio_quirk *quirk)
+                            const struct snd_usb_audio_quirk *quirk,
+                            unsigned int id)
 {
-       u32 id = USB_ID(le16_to_cpu(dev->descriptor.idVendor),
-                       le16_to_cpu(dev->descriptor.idProduct));
-
        switch (id) {
        case USB_ID(0x041e, 0x3000):
                /* SB Extigy needs special boot-up sequence */
@@ -1184,7 +1189,7 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
         * "Playback Design" products send bogus feedback data at the start
         * of the stream. Ignore them.
         */
-       if ((le16_to_cpu(ep->chip->dev->descriptor.idVendor) == 0x23ba) &&
+       if (USB_ID_VENDOR(ep->chip->usb_id) == 0x23ba &&
            ep->type == SND_USB_ENDPOINT_TYPE_SYNC)
                ep->skip_packets = 4;
 
@@ -1203,11 +1208,15 @@ void snd_usb_endpoint_start_quirk(struct snd_usb_endpoint *ep)
 
 void snd_usb_set_interface_quirk(struct usb_device *dev)
 {
+       struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev);
+
+       if (!chip)
+               return;
        /*
         * "Playback Design" products need a 50ms delay after setting the
         * USB interface.
         */
-       switch (le16_to_cpu(dev->descriptor.idVendor)) {
+       switch (USB_ID_VENDOR(chip->usb_id)) {
        case 0x23ba: /* Playback Design */
        case 0x0644: /* TEAC Corp. */
                mdelay(50);
@@ -1215,15 +1224,20 @@ void snd_usb_set_interface_quirk(struct usb_device *dev)
        }
 }
 
+/* quirk applied after snd_usb_ctl_msg(); not applied during boot quirks */
 void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
                           __u8 request, __u8 requesttype, __u16 value,
                           __u16 index, void *data, __u16 size)
 {
+       struct snd_usb_audio *chip = dev_get_drvdata(&dev->dev);
+
+       if (!chip)
+               return;
        /*
         * "Playback Design" products need a 20ms delay after each
         * class compliant request
         */
-       if ((le16_to_cpu(dev->descriptor.idVendor) == 0x23ba) &&
+       if (USB_ID_VENDOR(chip->usb_id) == 0x23ba &&
            (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
                mdelay(20);
 
@@ -1231,23 +1245,21 @@ void snd_usb_ctl_msg_quirk(struct usb_device *dev, unsigned int pipe,
         * "TEAC Corp." products need a 20ms delay after each
         * class compliant request
         */
-       if ((le16_to_cpu(dev->descriptor.idVendor) == 0x0644) &&
+       if (USB_ID_VENDOR(chip->usb_id) == 0x0644 &&
            (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
                mdelay(20);
 
        /* Marantz/Denon devices with USB DAC functionality need a delay
         * after each class compliant request
         */
-       if (is_marantz_denon_dac(USB_ID(le16_to_cpu(dev->descriptor.idVendor),
-                                       le16_to_cpu(dev->descriptor.idProduct)))
+       if (is_marantz_denon_dac(chip->usb_id)
            && (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
                mdelay(20);
 
        /* Zoom R16/24 needs a tiny delay here, otherwise requests like
         * get/set frequency return as failed despite actually succeeding.
         */
-       if ((le16_to_cpu(dev->descriptor.idVendor) == 0x1686) &&
-           (le16_to_cpu(dev->descriptor.idProduct) == 0x00dd) &&
+       if (chip->usb_id == USB_ID(0x1686, 0x00dd) &&
            (requesttype & USB_TYPE_MASK) == USB_TYPE_CLASS)
                mdelay(1);
 }
@@ -1264,7 +1276,7 @@ u64 snd_usb_interface_dsd_format_quirks(struct snd_usb_audio *chip,
                                        unsigned int sample_bytes)
 {
        /* Playback Designs */
-       if (le16_to_cpu(chip->dev->descriptor.idVendor) == 0x23ba) {
+       if (USB_ID_VENDOR(chip->usb_id) == 0x23ba) {
                switch (fp->altsetting) {
                case 1:
                        fp->dsd_dop = true;
index 2cd71ed1201f93ea8e6b54e1d487a84751e09885..192ff5ce94521b35ae09e6adc624f12239f48003 100644 (file)
@@ -16,7 +16,8 @@ int snd_usb_apply_interface_quirk(struct snd_usb_audio *chip,
 
 int snd_usb_apply_boot_quirk(struct usb_device *dev,
                             struct usb_interface *intf,
-                            const struct snd_usb_audio_quirk *quirk);
+                            const struct snd_usb_audio_quirk *quirk,
+                            unsigned int usb_id);
 
 void snd_usb_set_format_quirk(struct snd_usb_substream *subs,
                              struct audioformat *fmt);