Merge remote-tracking branches 'asoc/fix/atmel', 'asoc/fix/intel', 'asoc/fix/rt5645...
authorMark Brown <broonie@kernel.org>
Tue, 16 Dec 2014 11:52:34 +0000 (11:52 +0000)
committerMark Brown <broonie@kernel.org>
Tue, 16 Dec 2014 11:52:34 +0000 (11:52 +0000)
245 files changed:
Documentation/devicetree/bindings/sound/arndale.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/davinci-mcasp-audio.txt
Documentation/devicetree/bindings/sound/eukrea-tlv320.txt
Documentation/devicetree/bindings/sound/fsl,esai.txt
Documentation/devicetree/bindings/sound/fsl,spdif.txt
Documentation/devicetree/bindings/sound/fsl-sai.txt
Documentation/devicetree/bindings/sound/imx-audio-sgtl5000.txt
Documentation/devicetree/bindings/sound/imx-audio-spdif.txt
Documentation/devicetree/bindings/sound/imx-audio-wm8962.txt
Documentation/devicetree/bindings/sound/imx-audmux.txt
Documentation/devicetree/bindings/sound/max98090.txt
Documentation/devicetree/bindings/sound/renesas,fsi.txt
Documentation/devicetree/bindings/sound/renesas,rsnd.txt
Documentation/devicetree/bindings/sound/rt5631.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/rt5677.txt
Documentation/devicetree/bindings/sound/samsung-i2s.txt
Documentation/devicetree/bindings/sound/sgtl5000.txt
Documentation/devicetree/bindings/sound/ts3a227e.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/wm8960.txt [new file with mode: 0644]
MAINTAINERS
arch/arm/mach-pxa/spitz.c
arch/x86/include/asm/platform_sst_audio.h
drivers/base/regmap/Kconfig
drivers/base/regmap/Makefile
drivers/base/regmap/regmap-ac97.c [new file with mode: 0644]
include/linux/mfd/arizona/core.h
include/linux/mfd/davinci_voicecodec.h
include/linux/platform_data/asoc-s3c.h
include/linux/regmap.h
include/sound/rcar_snd.h
include/sound/rt5645.h
include/sound/rt5677.h
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc.h
include/sound/uda134x.h
include/trace/events/asoc.h
sound/soc/Makefile
sound/soc/atmel/Kconfig
sound/soc/atmel/Makefile
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/atmel/snd-soc-afeb9260.c [deleted file]
sound/soc/au1x/ac97c.c
sound/soc/au1x/psc-ac97.c
sound/soc/blackfin/bf5xx-ac97.c
sound/soc/blackfin/bf5xx-ad1980.c
sound/soc/cirrus/Kconfig
sound/soc/cirrus/ep93xx-ac97.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ab8500-codec.c
sound/soc/codecs/ac97.c
sound/soc/codecs/ad193x.c
sound/soc/codecs/ad1980.c
sound/soc/codecs/ad1980.h [deleted file]
sound/soc/codecs/adau1373.c
sound/soc/codecs/adau1701.c
sound/soc/codecs/adau1761.c
sound/soc/codecs/adau1781.c
sound/soc/codecs/adau17x1.c
sound/soc/codecs/adau17x1.h
sound/soc/codecs/adav80x.c
sound/soc/codecs/ak4535.c
sound/soc/codecs/ak4641.c
sound/soc/codecs/ak4642.c
sound/soc/codecs/ak4671.c
sound/soc/codecs/alc5623.c
sound/soc/codecs/alc5632.c
sound/soc/codecs/arizona.c
sound/soc/codecs/cq93vc.c
sound/soc/codecs/cs4265.c
sound/soc/codecs/cs4271-i2c.c [new file with mode: 0644]
sound/soc/codecs/cs4271-spi.c [new file with mode: 0644]
sound/soc/codecs/cs4271.c
sound/soc/codecs/cs4271.h [new file with mode: 0644]
sound/soc/codecs/cs42l51.c
sound/soc/codecs/cs42l73.c
sound/soc/codecs/hdmi.c
sound/soc/codecs/lm49453.c
sound/soc/codecs/max98088.c
sound/soc/codecs/max98090.c
sound/soc/codecs/max98090.h
sound/soc/codecs/max98095.c
sound/soc/codecs/max9850.c
sound/soc/codecs/pcm512x-i2c.c
sound/soc/codecs/rt286.c
sound/soc/codecs/rt5631.c
sound/soc/codecs/rt5645.c
sound/soc/codecs/rt5645.h
sound/soc/codecs/rt5670.c
sound/soc/codecs/rt5670.h
sound/soc/codecs/rt5677-spi.c [new file with mode: 0644]
sound/soc/codecs/rt5677-spi.h [new file with mode: 0644]
sound/soc/codecs/rt5677.c
sound/soc/codecs/rt5677.h
sound/soc/codecs/sgtl5000.c
sound/soc/codecs/sigmadsp-i2c.c
sound/soc/codecs/sigmadsp-regmap.c
sound/soc/codecs/sigmadsp.c
sound/soc/codecs/sigmadsp.h
sound/soc/codecs/sirf-audio-codec.c
sound/soc/codecs/sn95031.c
sound/soc/codecs/ssm4567.c
sound/soc/codecs/sta32x.c
sound/soc/codecs/sta350.c
sound/soc/codecs/sta529.c
sound/soc/codecs/stac9766.c
sound/soc/codecs/tas2552.c
sound/soc/codecs/tfa9879.c [new file with mode: 0644]
sound/soc/codecs/tfa9879.h [new file with mode: 0644]
sound/soc/codecs/tlv320aic23.c
sound/soc/codecs/tlv320aic31xx.c
sound/soc/codecs/tlv320aic32x4.c
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/tlv320aic3x.h
sound/soc/codecs/tlv320dac33.c
sound/soc/codecs/ts3a227e.c [new file with mode: 0644]
sound/soc/codecs/ts3a227e.h [new file with mode: 0644]
sound/soc/codecs/twl4030.c
sound/soc/codecs/twl6040.c
sound/soc/codecs/uda134x.c
sound/soc/codecs/uda1380.c
sound/soc/codecs/wl1273.c
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8400.c
sound/soc/codecs/wm8510.c
sound/soc/codecs/wm8523.c
sound/soc/codecs/wm8580.c
sound/soc/codecs/wm8711.c
sound/soc/codecs/wm8728.c
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8737.c
sound/soc/codecs/wm8750.c
sound/soc/codecs/wm8776.c
sound/soc/codecs/wm8804.c
sound/soc/codecs/wm8900.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8940.c
sound/soc/codecs/wm8955.c
sound/soc/codecs/wm8958-dsp2.c
sound/soc/codecs/wm8960.c
sound/soc/codecs/wm8961.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8974.c
sound/soc/codecs/wm8978.c
sound/soc/codecs/wm8983.c
sound/soc/codecs/wm8985.c
sound/soc/codecs/wm8988.c
sound/soc/codecs/wm8990.c
sound/soc/codecs/wm8991.c
sound/soc/codecs/wm8993.c
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8994.h
sound/soc/codecs/wm8995.c
sound/soc/codecs/wm9081.c
sound/soc/codecs/wm9090.c
sound/soc/codecs/wm9705.c
sound/soc/codecs/wm9712.c
sound/soc/codecs/wm9713.c
sound/soc/codecs/wm_adsp.c
sound/soc/davinci/davinci-mcasp.c
sound/soc/davinci/davinci-mcasp.h
sound/soc/dwc/designware_i2s.c
sound/soc/fsl/eukrea-tlv320.c
sound/soc/fsl/fsl-asoc-card.c
sound/soc/fsl/fsl_esai.c
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/imx-sgtl5000.c
sound/soc/fsl/imx-spdif.c
sound/soc/fsl/imx-ssi.c
sound/soc/fsl/imx-wm8962.c
sound/soc/fsl/mpc5200_dma.c
sound/soc/fsl/mpc5200_psc_ac97.c
sound/soc/generic/simple-card.c
sound/soc/intel/Kconfig
sound/soc/intel/Makefile
sound/soc/intel/broadwell.c
sound/soc/intel/bytcr_dpcm_rt5640.c [new file with mode: 0644]
sound/soc/intel/cht_bsw_rt5672.c [new file with mode: 0644]
sound/soc/intel/haswell.c
sound/soc/intel/sst-atom-controls.c
sound/soc/intel/sst-atom-controls.h
sound/soc/intel/sst-baytrail-dsp.c
sound/soc/intel/sst-dsp-priv.h
sound/soc/intel/sst-dsp.c
sound/soc/intel/sst-dsp.h
sound/soc/intel/sst-firmware.c
sound/soc/intel/sst-haswell-dsp.c
sound/soc/intel/sst-haswell-ipc.c
sound/soc/intel/sst-haswell-ipc.h
sound/soc/intel/sst-haswell-pcm.c
sound/soc/intel/sst-mfld-platform-compress.c
sound/soc/intel/sst-mfld-platform-pcm.c
sound/soc/intel/sst-mfld-platform.h
sound/soc/intel/sst/Makefile [new file with mode: 0644]
sound/soc/intel/sst/sst.c [new file with mode: 0644]
sound/soc/intel/sst/sst.h [new file with mode: 0644]
sound/soc/intel/sst/sst_acpi.c [new file with mode: 0644]
sound/soc/intel/sst/sst_drv_interface.c [new file with mode: 0644]
sound/soc/intel/sst/sst_ipc.c [new file with mode: 0644]
sound/soc/intel/sst/sst_loader.c [new file with mode: 0644]
sound/soc/intel/sst/sst_pci.c [new file with mode: 0644]
sound/soc/intel/sst/sst_pvt.c [new file with mode: 0644]
sound/soc/intel/sst/sst_stream.c [new file with mode: 0644]
sound/soc/jz4740/qi_lb60.c
sound/soc/mxs/mxs-saif.c
sound/soc/mxs/mxs-sgtl5000.c
sound/soc/nuc900/nuc900-ac97.c
sound/soc/omap/Kconfig
sound/soc/omap/mcbsp.c
sound/soc/pxa/mioa701_wm9713.c
sound/soc/pxa/pxa-ssp.c
sound/soc/pxa/pxa2xx-ac97.c
sound/soc/pxa/spitz.c
sound/soc/rockchip/Kconfig
sound/soc/samsung/Kconfig
sound/soc/samsung/Makefile
sound/soc/samsung/ac97.c
sound/soc/samsung/arndale_rt5631.c [new file with mode: 0644]
sound/soc/samsung/i2s-regs.h
sound/soc/samsung/i2s.c
sound/soc/samsung/odroidx2_max98090.c
sound/soc/sh/fsi.c
sound/soc/sh/hac.c
sound/soc/sh/rcar/adg.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/dvc.c
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/src.c
sound/soc/sh/rcar/ssi.c
sound/soc/soc-ac97.c [new file with mode: 0644]
sound/soc/soc-cache.c
sound/soc/soc-compress.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-jack.c
sound/soc/soc-ops.c [new file with mode: 0644]
sound/soc/soc-pcm.c
sound/soc/tegra/tegra20_ac97.c
sound/soc/tegra/tegra_rt5640.c
sound/soc/txx9/txx9aclc-ac97.c
sound/soc/txx9/txx9aclc.c
sound/soc/ux500/mop500.c

diff --git a/Documentation/devicetree/bindings/sound/arndale.txt b/Documentation/devicetree/bindings/sound/arndale.txt
new file mode 100644 (file)
index 0000000..0e76946
--- /dev/null
@@ -0,0 +1,24 @@
+Audio Binding for Arndale boards
+
+Required properties:
+- compatible : Can be the following,
+                       "samsung,arndale-rt5631"
+
+- samsung,audio-cpu: The phandle of the Samsung I2S controller
+- samsung,audio-codec: The phandle of the audio codec
+
+Optional:
+- samsung,model: The name of the sound-card
+
+Arndale Boards has many audio daughter cards, one of them is
+rt5631/alc5631. Below example shows audio bindings for rt5631/
+alc5631 based codec.
+
+Example:
+
+sound {
+               compatible = "samsung,arndale-rt5631";
+
+               samsung,audio-cpu = <&i2s0>
+               samsung,audio-codec = <&rt5631>;
+};
index 60ca079..46bc982 100644 (file)
@@ -32,7 +32,7 @@ Optional properties:
 - rx-num-evt : FIFO levels.
 - sram-size-playback : size of sram to be allocated during playback
 - sram-size-capture  : size of sram to be allocated during capture
-- interrupts : Interrupt numbers for McASP, currently not used by the driver
+- interrupts : Interrupt numbers for McASP
 - interrupt-names : Known interrupt names are "tx" and "rx"
 - pinctrl-0: Should specify pin control group used for this controller.
 - pinctrl-names: Should contain only one value - "default", for more details
index 0d7985c..6dfa88c 100644 (file)
@@ -1,11 +1,16 @@
 Audio complex for Eukrea boards with tlv320aic23 codec.
 
 Required properties:
-- compatible : "eukrea,asoc-tlv320"
-- eukrea,model : The user-visible name of this sound complex.
-- ssi-controller : The phandle of the SSI controller.
-- fsl,mux-int-port : The internal port of the i.MX audio muxer (AUDMUX).
-- fsl,mux-ext-port : The external port of the i.MX audio muxer.
+
+  - compatible         : "eukrea,asoc-tlv320"
+
+  - eukrea,model       : The user-visible name of this sound complex.
+
+  - ssi-controller     : The phandle of the SSI controller.
+
+  - fsl,mux-int-port   : The internal port of the i.MX audio muxer (AUDMUX).
+
+  - fsl,mux-ext-port   : The external port of the i.MX audio muxer.
 
 Note: The AUDMUX port numbering should start at 1, which is consistent with
 hardware manual.
index 52f5b6b..d3b6b5f 100644 (file)
@@ -7,37 +7,39 @@ other DSPs. It has up to six transmitters and four receivers.
 
 Required properties:
 
-  - compatible : Compatible list, must contain "fsl,imx35-esai" or
-                "fsl,vf610-esai"
+  - compatible         : Compatible list, must contain "fsl,imx35-esai" or
+                         "fsl,vf610-esai"
 
-  - reg : Offset and length of the register set for the device.
+  - reg                        : Offset and length of the register set for the device.
 
-  - interrupts : Contains the spdif interrupt.
+  - interrupts         : Contains the spdif interrupt.
 
-  - dmas : Generic dma devicetree binding as described in
-  Documentation/devicetree/bindings/dma/dma.txt.
+  - 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".
+  - dma-names          : Two dmas have to be defined, "tx" and "rx".
 
-  - clocks: Contains an entry for each entry in clock-names.
+  - clocks             : Contains an entry for each entry in clock-names.
 
-  - clock-names : Includes the following entries:
-       "core"          The core clock used to access registers
-       "extal"         The esai baud clock for esai controller used to derive
-                       HCK, SCK and FS.
-       "fsys"          The system clock derived from ahb clock used to derive
-                       HCK, SCK and FS.
+  - clock-names                : Includes the following entries:
+       "core"            The core clock used to access registers
+       "extal"           The esai baud clock for esai controller used to
+                         derive HCK, SCK and FS.
+       "fsys"            The system clock derived from ahb clock used to
+                         derive HCK, SCK and FS.
 
-  - fsl,fifo-depth: The number of elements in the transmit and receive FIFOs.
-    This number is the maximum allowed value for TFCR[TFWM] or RFCR[RFWM].
+  - fsl,fifo-depth     : The number of elements in the transmit and receive
+                         FIFOs. This number is the maximum allowed value for
+                         TFCR[TFWM] or RFCR[RFWM].
 
   - fsl,esai-synchronous: This is a boolean property. If present, indicating
-    that ESAI would work in the synchronous mode, which means all the settings
-    for Receiving would be duplicated from Transmition related registers.
+                         that ESAI would work in the synchronous mode, which
+                         means all the settings for Receiving would be
+                         duplicated from Transmition related registers.
 
-  - big-endian : If this property is absent, the native endian mode will
-    be in use as default, or the big endian mode will be in use for all the
-    device registers.
+  - big-endian         : If this property is absent, the native endian mode
+                         will be in use as default, or the big endian mode
+                         will be in use for all the device registers.
 
 Example:
 
index 3e9e82c..b5ee32e 100644 (file)
@@ -6,32 +6,31 @@ a fibre cable.
 
 Required properties:
 
-  - compatible : Compatible list, must contain "fsl,imx35-spdif".
+  - compatible         : Compatible list, must contain "fsl,imx35-spdif".
 
-  - reg : Offset and length of the register set for the device.
+  - reg                        : Offset and length of the register set for the device.
 
-  - interrupts : Contains the spdif interrupt.
+  - interrupts         : Contains the spdif interrupt.
 
-  - dmas : Generic dma devicetree binding as described in
-  Documentation/devicetree/bindings/dma/dma.txt.
+  - 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".
+  - dma-names          : Two dmas have to be defined, "tx" and "rx".
 
-  - clocks : Contains an entry for each entry in clock-names.
+  - clocks             : Contains an entry for each entry in clock-names.
 
-  - clock-names : Includes the following entries:
-       "core"          The core clock of spdif controller
-       "rxtx<0-7>"     Clock source list for tx and rx clock.
-                       This clock list should be identical to
-                       the source list connecting to the spdif
-                       clock mux in "SPDIF Transceiver Clock
-                       Diagram" of SoC reference manual. It
-                       can also be referred to TxClk_Source
-                       bit of register SPDIF_STC.
+  - clock-names                : Includes the following entries:
+       "core"            The core clock of spdif controller.
+       "rxtx<0-7>"       Clock source list for tx and rx clock.
+                         This clock list should be identical to the source
+                         list connecting to the spdif clock mux in "SPDIF
+                         Transceiver Clock Diagram" of SoC reference manual.
+                         It can also be referred to TxClk_Source bit of
+                         register SPDIF_STC.
 
-   - big-endian : If this property is absent, the native endian mode will
-   be in use as default, or the big endian mode will be in use for all the
-   device registers.
+   - big-endian                : If this property is absent, the native endian mode
+                         will be in use as default, or the big endian mode
+                         will be in use for all the device registers.
 
 Example:
 
index 4956b14..044e5d7 100644 (file)
@@ -5,32 +5,48 @@ which provides a synchronous audio interface that supports fullduplex
 serial interfaces with frame synchronization such as I2S, AC97, TDM, and
 codec/DSP interfaces.
 
-
 Required properties:
-- compatible: Compatible list, contains "fsl,vf610-sai" or "fsl,imx6sx-sai".
-- reg: Offset and length of the register set for the device.
-- clocks: Must contain an entry for each entry in clock-names.
-- clock-names : Must include the "bus" for register access and "mclk1" "mclk2"
-  "mclk3" for bit clock and frame clock providing.
-- 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".
-- pinctrl-names: Must contain a "default" entry.
-- pinctrl-NNN: One property must exist for each entry in pinctrl-names.
-  See ../pinctrl/pinctrl-bindings.txt for details of the property values.
-- big-endian: Boolean property, required if all the FTM_PWM registers
-  are big-endian rather than little-endian.
-- lsb-first: Configures whether the LSB or the MSB is transmitted first for
-  the fifo data. If this property is absent, the MSB is transmitted first as
-  default, or the LSB is transmitted first.
-- fsl,sai-synchronous-rx: This is a boolean property. If present, indicating
-  that SAI will work in the synchronous mode (sync Tx with Rx) which means
-  both the transimitter and receiver will send and receive data by following
-  receiver's bit clocks and frame sync clocks.
-- fsl,sai-asynchronous: This is a boolean property. If present, indicating
-  that SAI will work in the asynchronous mode, which means both transimitter
-  and receiver will send and receive data by following their own bit clocks
-  and frame sync clocks separately.
+
+  - compatible         : Compatible list, contains "fsl,vf610-sai" or
+                         "fsl,imx6sx-sai".
+
+  - reg                        : Offset and length of the register set for the device.
+
+  - clocks             : Must contain an entry for each entry in clock-names.
+
+  - clock-names                : Must include the "bus" for register access and
+                         "mclk1", "mclk2", "mclk3" for bit clock and frame
+                         clock providing.
+  - 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".
+
+  - pinctrl-names      : Must contain a "default" entry.
+
+  - pinctrl-NNN                : One property must exist for each entry in
+                         pinctrl-names. See ../pinctrl/pinctrl-bindings.txt
+                         for details of the property values.
+
+  - big-endian         : Boolean property, required if all the FTM_PWM
+                         registers are big-endian rather than little-endian.
+
+  - lsb-first          : Configures whether the LSB or the MSB is transmitted
+                         first for the fifo data. If this property is absent,
+                         the MSB is transmitted first as default, or the LSB
+                         is transmitted first.
+
+  - fsl,sai-synchronous-rx: This is a boolean property. If present, indicating
+                         that SAI will work in the synchronous mode (sync Tx
+                         with Rx) which means both the transimitter and the
+                         receiver will send and receive data by following
+                         receiver's bit clocks and frame sync clocks.
+
+  - fsl,sai-asynchronous: This is a boolean property. If present, indicating
+                         that SAI will work in the asynchronous mode, which
+                         means both transimitter and receiver will send and
+                         receive data by following their own bit clocks and
+                         frame sync clocks separately.
 
 Note:
 - If both fsl,sai-asynchronous and fsl,sai-synchronous-rx are absent, the
index e4acdd8..2f89db8 100644 (file)
@@ -1,33 +1,40 @@
 Freescale i.MX audio complex with SGTL5000 codec
 
 Required properties:
-- compatible : "fsl,imx-audio-sgtl5000"
-- model : The user-visible name of this sound complex
-- ssi-controller : The phandle of the i.MX SSI controller
-- audio-codec : The phandle of the SGTL5000 audio codec
-- audio-routing : A list of the connections between audio components.
-  Each entry is a pair of strings, the first being the connection's sink,
-  the second being the connection's source. Valid names could be power
-  supplies, SGTL5000 pins, and the jacks on the board:
-
-  Power supplies:
-   * Mic Bias
-
-  SGTL5000 pins:
-   * MIC_IN
-   * LINE_IN
-   * HP_OUT
-   * LINE_OUT
-
-  Board connectors:
-   * Mic Jack
-   * Line In Jack
-   * Headphone Jack
-   * Line Out Jack
-   * Ext Spk
-
-- 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
+
+  - compatible         : "fsl,imx-audio-sgtl5000"
+
+  - model              : The user-visible name of this sound complex
+
+  - ssi-controller     : The phandle of the i.MX SSI controller
+
+  - audio-codec                : The phandle of the SGTL5000 audio codec
+
+  - audio-routing      : A list of the connections between audio components.
+                         Each entry is a pair of strings, the first being the
+                         connection's sink, the second being the connection's
+                         source. Valid names could be power supplies, SGTL5000
+                         pins, and the jacks on the board:
+
+                         Power supplies:
+                          * Mic Bias
+
+                         SGTL5000 pins:
+                          * MIC_IN
+                          * LINE_IN
+                          * HP_OUT
+                          * LINE_OUT
+
+                         Board connectors:
+                          * Mic Jack
+                          * Line In Jack
+                          * Headphone Jack
+                          * Line Out Jack
+                          * Ext Spk
+
+  - 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
 
 Note: The AUDMUX port numbering should start at 1, which is consistent with
 hardware manual.
index 7d13479..da84a44 100644 (file)
@@ -2,23 +2,25 @@ Freescale i.MX audio complex with S/PDIF transceiver
 
 Required properties:
 
-  - compatible : "fsl,imx-audio-spdif"
+  - compatible         : "fsl,imx-audio-spdif"
 
-  - model : The user-visible name of this sound complex
+  - model              : The user-visible name of this sound complex
 
-  - spdif-controller : The phandle of the i.MX S/PDIF controller
+  - spdif-controller   : The phandle of the i.MX S/PDIF controller
 
 
 Optional properties:
 
-  - spdif-out : This is a boolean property. If present, the transmitting
-    function of S/PDIF will be enabled, indicating there's a physical
-    S/PDIF out connector/jack on the board or it's connecting to some
-    other IP block, such as an HDMI encoder/display-controller.
+  - spdif-out          : This is a boolean property. If present, the
+                         transmitting function of S/PDIF will be enabled,
+                         indicating there's a physical S/PDIF out connector
+                         or jack on the board or it's connecting to some
+                         other IP block, such as an HDMI encoder or
+                         display-controller.
 
-  - spdif-in : This is a boolean property. If present, the receiving
-    function of S/PDIF will be enabled, indicating there's a physical
-    S/PDIF in connector/jack on the board.
+  - spdif-in           : This is a boolean property. If present, the receiving
+                         function of S/PDIF will be enabled, indicating there
+                         is a physical S/PDIF in connector/jack on the board.
 
 * Note: At least one of these two properties should be set in the DT binding.
 
index f49450a..acea71b 100644 (file)
@@ -1,25 +1,32 @@
 Freescale i.MX audio complex with WM8962 codec
 
 Required properties:
-- compatible : "fsl,imx-audio-wm8962"
-- model : The user-visible name of this sound complex
-- ssi-controller : The phandle of the i.MX SSI controller
-- audio-codec : The phandle of the WM8962 audio codec
-- audio-routing : A list of the connections between audio components.
-  Each entry is a pair of strings, the first being the connection's sink,
-  the second being the connection's source. Valid names could be power
-  supplies, WM8962 pins, and the jacks on the board:
-
-  Power supplies:
-   * Mic Bias
-
-  Board connectors:
-   * Mic Jack
-   * Headphone Jack
-   * Ext Spk
-
-- 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
+
+  - compatible         : "fsl,imx-audio-wm8962"
+
+  - model              : The user-visible name of this sound complex
+
+  - ssi-controller     : The phandle of the i.MX SSI controller
+
+  - audio-codec                : The phandle of the WM8962 audio codec
+
+  - audio-routing      : A list of the connections between audio components.
+                         Each entry is a pair of strings, the first being the
+                         connection's sink, the second being the connection's
+                         source. Valid names could be power supplies, WM8962
+                         pins, and the jacks on the board:
+
+                         Power supplies:
+                          * Mic Bias
+
+                         Board connectors:
+                          * Mic Jack
+                          * Headphone Jack
+                          * Ext Spk
+
+  - 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
 
 Note: The AUDMUX port numbering should start at 1, which is consistent with
 hardware manual.
index f88a00e..b30a737 100644 (file)
@@ -1,18 +1,24 @@
 Freescale Digital Audio Mux (AUDMUX) device
 
 Required properties:
-- compatible : "fsl,imx21-audmux" for AUDMUX version firstly used on i.MX21,
-  or "fsl,imx31-audmux" for the version firstly used on i.MX31.
-- reg : Should contain AUDMUX registers location and length
+
+  - compatible         : "fsl,imx21-audmux" for AUDMUX version firstly used
+                         on i.MX21, or "fsl,imx31-audmux" for the version
+                         firstly used on i.MX31.
+
+  - reg                        : Should contain AUDMUX registers location and length.
 
 An initial configuration can be setup using child nodes.
 
 Required properties of optional child nodes:
-- fsl,audmux-port : Integer of the audmux port that is configured by this
-  child node.
-- fsl,port-config : List of configuration options for the specific port. For
-  imx31-audmux and above, it is a list of tuples <ptcr pdcr>. For
-  imx21-audmux it is a list of pcr values.
+
+  - fsl,audmux-port    : Integer of the audmux port that is configured by this
+                         child node.
+
+  - fsl,port-config    : List of configuration options for the specific port.
+                         For imx31-audmux and above, it is a list of tuples
+                         <ptcr pdcr>. For imx21-audmux it is a list of pcr
+                         values.
 
 Example:
 
index c454e67..aa802a2 100644 (file)
@@ -16,6 +16,8 @@ Optional properties:
 
 - clock-names: Should be "mclk"
 
+- maxim,dmic-freq: Frequency at which to clock DMIC
+
 Pins on the device (for linking into audio routes):
 
   * MIC1
index c5be003..0d0ab51 100644 (file)
@@ -1,11 +1,16 @@
 Renesas FSI
 
 Required properties:
-- compatible                   : "renesas,sh_fsi2" or "renesas,sh_fsi"
+- compatible                   : "renesas,fsi2-<soctype>",
+                                 "renesas,sh_fsi2" or "renesas,sh_fsi" as
+                                 fallback.
+                                 Examples with soctypes are:
+                                   - "renesas,fsi2-r8a7740" (R-Mobile A1)
+                                   - "renesas,fsi2-sh73a0" (SH-Mobile AG5)
 - reg                          : Should contain the register physical address and length
 - interrupts                   : Should contain FSI interrupt
 
-- fsia,spdif-connection                : FSI is connected by S/PDFI
+- fsia,spdif-connection                : FSI is connected by S/PDIF
 - fsia,stream-mode-support     : FSI supports 16bit stream mode.
 - fsia,use-internal-clock      : FSI uses internal clock when master mode.
 
index aa697ab..2dd690b 100644 (file)
@@ -1,8 +1,12 @@
 Renesas R-Car sound
 
 Required properties:
-- compatible                   : "renesas,rcar_sound-gen1" if generation1
+- compatible                   : "renesas,rcar_sound-<soctype>", fallbacks
+                                 "renesas,rcar_sound-gen1" if generation1, and
                                  "renesas,rcar_sound-gen2" if generation2
+                                 Examples with soctypes are:
+                                   - "renesas,rcar_sound-r8a7790" (R-Car H2)
+                                   - "renesas,rcar_sound-r8a7791" (R-Car M2-W)
 - reg                          : Should contain the register physical address.
                                  required register is
                                   SRU/ADG/SSI      if generation1
@@ -35,9 +39,9 @@ DAI subnode properties:
 
 Example:
 
-rcar_sound: rcar_sound@0xffd90000 {
+rcar_sound: rcar_sound@ec500000 {
        #sound-dai-cells = <1>;
-       compatible = "renesas,rcar_sound-gen2";
+       compatible = "renesas,rcar_sound-r8a7791", "renesas,rcar_sound-gen2";
        reg =   <0 0xec500000 0 0x1000>, /* SCU */
                <0 0xec5a0000 0 0x100>,  /* ADG */
                <0 0xec540000 0 0x1000>, /* SSIU */
diff --git a/Documentation/devicetree/bindings/sound/rt5631.txt b/Documentation/devicetree/bindings/sound/rt5631.txt
new file mode 100644 (file)
index 0000000..92b986c
--- /dev/null
@@ -0,0 +1,48 @@
+ALC5631/RT5631 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+  - compatible : "realtek,alc5631" or "realtek,rt5631"
+
+  - reg : the I2C address of the device.
+
+Pins on the device (for linking into audio routes):
+
+  * SPK_OUT_R_P
+  * SPK_OUT_R_N
+  * SPK_OUT_L_P
+  * SPK_OUT_L_N
+  * HP_OUT_L
+  * HP_OUT_R
+  * AUX_OUT2_LP
+  * AUX_OUT2_RN
+  * AUX_OUT1_LP
+  * AUX_OUT1_RN
+  * AUX_IN_L_JD
+  * AUX_IN_R_JD
+  * MONO_IN_P
+  * MONO_IN_N
+  * MIC1_P
+  * MIC1_N
+  * MIC2_P
+  * MIC2_N
+  * MONO_OUT_P
+  * MONO_OUT_N
+  * MICBIAS1
+  * MICBIAS2
+
+Example:
+
+alc5631: alc5631@1a {
+       compatible = "realtek,alc5631";
+       reg = <0x1a>;
+};
+
+or
+
+rt5631: rt5631@1a {
+       compatible = "realtek,rt5631";
+       reg = <0x1a>;
+};
index 0701b83..740ff77 100644 (file)
@@ -27,6 +27,21 @@ Optional properties:
   Boolean. Indicate MIC1/2 input and LOUT1/2/3 outputs are differential,
   rather than single-ended.
 
+- realtek,gpio-config
+  Array of six 8bit elements that configures GPIO.
+    0 - floating (reset value)
+    1 - pull down
+    2 - pull up
+
+- realtek,jd1-gpio
+  Configures GPIO Mic Jack detection 1.
+  Select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively.
+
+- realtek,jd2-gpio
+- realtek,jd3-gpio
+  Configures GPIO Mic Jack detection 2 and 3.
+  Select 0 ~ 3 as OFF, GPIO4, GPIO5 and GPIO6 respectively.
+
 Pins on the device (for linking into audio routes):
 
   * IN1P
@@ -56,4 +71,6 @@ rt5677 {
        realtek,pow-ldo2-gpio =
                <&gpio TEGRA_GPIO(V, 3) GPIO_ACTIVE_HIGH>;
        realtek,in1-differential = "true";
+       realtek,gpio-config = /bits/ 8  <0 0 0 0 0 2>;   /* pull up GPIO6 */
+       realtek,jd2-gpio = <3>;  /* Enables Jack detection for GPIO6 */
 };
index 7386d44..d188296 100644 (file)
@@ -6,10 +6,17 @@ Required SoC Specific Properties:
    - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S.
    - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with
      secondary fifo, s/w reset control and internal mux for root clk src.
-   - samsung,exynos5420-i2s: for 8/16/24bit multichannel(7.1) I2S with
-     secondary fifo, s/w reset control, internal mux for root clk src and
-     TDM support. TDM (Time division multiplexing) is to allow transfer of
-     multiple channel audio data on single data line.
+   - samsung,exynos5420-i2s: for 8/16/24bit multichannel(5.1) I2S for
+     playback, sterio channel capture, secondary fifo using internal
+     or external dma, s/w reset control, internal mux for root clk src
+     and 7.1 channel TDM support for playback. TDM (Time division multiplexing)
+     is to allow transfer of multiple channel audio data on single data line.
+   - samsung,exynos7-i2s: with all the available features of exynos5 i2s,
+     exynos7 I2S has 7.1 channel TDM support for capture, secondary fifo
+     with only external dma and more no.of root clk sampling frequencies.
+   - samsung,exynos7-i2s1: I2S1 on previous samsung platforms supports
+     stereo channels. exynos7 i2s1 upgraded to 5.1 multichannel with
+     slightly modified bit offsets.
 
 - reg: physical base address of the controller and length of memory mapped
   region.
index d556dcb..0e5e4eb 100644 (file)
@@ -7,6 +7,17 @@ Required properties:
 
 - clocks : the clock provider of SYS_MCLK
 
+- micbias-resistor-k-ohms : the bias resistor to be used in kOmhs
+       The resistor can take values of 2k, 4k or 8k.
+       If set to 0 it will be off.
+       If this node is not mentioned or if the value is unknown, then
+       micbias resistor is set to 4K.
+
+- micbias-voltage-m-volts : the bias voltage to be used in mVolts
+       The voltage can take values from 1.25V to 3V by 250mV steps
+       If this node is not mentionned or the value is unknown, then
+       the value is set to 1.25V.
+
 - VDDA-supply : the regulator provider of VDDA
 
 - VDDIO-supply: the regulator provider of VDDIO
@@ -21,6 +32,8 @@ codec: sgtl5000@0a {
        compatible = "fsl,sgtl5000";
        reg = <0x0a>;
        clocks = <&clks 150>;
+       micbias-resistor-k-ohms = <2>;
+       micbias-voltage-m-volts = <2250>;
        VDDA-supply = <&reg_3p3v>;
        VDDIO-supply = <&reg_3p3v>;
 };
diff --git a/Documentation/devicetree/bindings/sound/ts3a227e.txt b/Documentation/devicetree/bindings/sound/ts3a227e.txt
new file mode 100644 (file)
index 0000000..e8bf23e
--- /dev/null
@@ -0,0 +1,26 @@
+Texas Instruments TS3A227E
+Autonomous Audio Accessory Detection and Configuration Switch
+
+The TS3A227E detect headsets of 3-ring and 4-ring standards and
+switches automatically to route the microphone correctly.  It also
+handles key press detection in accordance with the Android audio
+headset specification v1.0.
+
+Required properties:
+
+ - compatible:         Should contain "ti,ts3a227e".
+ - reg:                        The i2c address. Should contain <0x3b>.
+ - interrupt-parent:   The parent interrupt controller
+ - interrupts:         Interrupt number for /INT pin from the 227e
+
+
+Examples:
+
+       i2c {
+               ts3a227e@3b {
+                       compatible = "ti,ts3a227e";
+                       reg = <0x3b>;
+                       interrupt-parent = <&gpio>;
+                       interrupts = <3 IRQ_TYPE_LEVEL_LOW>;
+               };
+       };
diff --git a/Documentation/devicetree/bindings/sound/wm8960.txt b/Documentation/devicetree/bindings/sound/wm8960.txt
new file mode 100644 (file)
index 0000000..2deb8a3
--- /dev/null
@@ -0,0 +1,31 @@
+WM8960 audio CODEC
+
+This device supports I2C only.
+
+Required properties:
+
+  - compatible : "wlf,wm8960"
+
+  - reg : the I2C address of the device.
+
+Optional properties:
+  - wlf,shared-lrclk: This is a boolean property. If present, the LRCM bit of
+       R24 (Additional control 2) gets set, indicating that ADCLRC and DACLRC pins
+       will be disabled only when ADC (Left and Right) and DAC (Left and Right)
+       are disabled.
+       When wm8960 works on synchronize mode and DACLRC pin is used to supply
+       frame clock, it will no frame clock for captrue unless enable DAC to enable
+       DACLRC pin. If shared-lrclk is present, no need to enable DAC for captrue.
+
+  - wlf,capless: This is a boolean property. If present, OUT3 pin will be
+       enabled and disabled together with HP_L and HP_R pins in response to jack
+       detect events.
+
+Example:
+
+codec: wm8960@1a {
+       compatible = "wlf,wm8960";
+       reg = <0x1a>;
+
+       wlf,shared-lrclk;
+};
index c721042..dd9a4e9 100644 (file)
@@ -6601,6 +6601,12 @@ S:       Supported
 F:     drivers/gpu/drm/i2c/tda998x_drv.c
 F:     include/drm/i2c/tda998x.h
 
+NXP TFA9879 DRIVER
+M:     Peter Rosin <peda@axentia.se>
+L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
+S:     Maintained
+F:     sound/soc/codecs/tfa9879*
+
 OMAP SUPPORT
 M:     Tony Lindgren <tony@atomide.com>
 L:     linux-omap@vger.kernel.org
index 840c3a4..962a7f3 100644 (file)
@@ -923,6 +923,14 @@ static void __init spitz_i2c_init(void)
 static inline void spitz_i2c_init(void) {}
 #endif
 
+/******************************************************************************
+ * Audio devices
+ ******************************************************************************/
+static inline void spitz_audio_init(void)
+{
+       platform_device_register_simple("spitz-audio", -1, NULL, 0);
+}
+
 /******************************************************************************
  * Machine init
  ******************************************************************************/
@@ -970,6 +978,7 @@ static void __init spitz_init(void)
        spitz_nor_init();
        spitz_nand_init();
        spitz_i2c_init();
+       spitz_audio_init();
 }
 
 static void __init spitz_fixup(struct tag *tags, char **cmdline)
index 0a4e140..7249e6d 100644 (file)
@@ -16,6 +16,9 @@
 
 #include <linux/sfi.h>
 
+#define MAX_NUM_STREAMS_MRFLD  25
+#define MAX_NUM_STREAMS        MAX_NUM_STREAMS_MRFLD
+
 enum sst_audio_task_id_mrfld {
        SST_TASK_ID_NONE = 0,
        SST_TASK_ID_SBA = 1,
@@ -73,6 +76,65 @@ struct sst_platform_data {
        unsigned int strm_map_size;
 };
 
+struct sst_info {
+       u32 iram_start;
+       u32 iram_end;
+       bool iram_use;
+       u32 dram_start;
+       u32 dram_end;
+       bool dram_use;
+       u32 imr_start;
+       u32 imr_end;
+       bool imr_use;
+       u32 mailbox_start;
+       bool use_elf;
+       bool lpe_viewpt_rqd;
+       unsigned int max_streams;
+       u32 dma_max_len;
+       u8 num_probes;
+};
+
+struct sst_lib_dnld_info {
+       unsigned int mod_base;
+       unsigned int mod_end;
+       unsigned int mod_table_offset;
+       unsigned int mod_table_size;
+       bool mod_ddr_dnld;
+};
+
+struct sst_res_info {
+       unsigned int shim_offset;
+       unsigned int shim_size;
+       unsigned int shim_phy_addr;
+       unsigned int ssp0_offset;
+       unsigned int ssp0_size;
+       unsigned int dma0_offset;
+       unsigned int dma0_size;
+       unsigned int dma1_offset;
+       unsigned int dma1_size;
+       unsigned int iram_offset;
+       unsigned int iram_size;
+       unsigned int dram_offset;
+       unsigned int dram_size;
+       unsigned int mbox_offset;
+       unsigned int mbox_size;
+       unsigned int acpi_lpe_res_index;
+       unsigned int acpi_ddr_index;
+       unsigned int acpi_ipc_irq_index;
+};
+
+struct sst_ipc_info {
+       int ipc_offset;
+       unsigned int mbox_recv_off;
+};
+
+struct sst_platform_info {
+       const struct sst_info *probe_data;
+       const struct sst_ipc_info *ipc_info;
+       const struct sst_res_info *res_info;
+       const struct sst_lib_dnld_info *lib_info;
+       const char *platform;
+};
 int add_sst_platform_device(void);
 #endif
 
index 8a3f51f..db9d00c 100644 (file)
@@ -3,12 +3,15 @@
 # subsystems should select the appropriate symbols.
 
 config REGMAP
-       default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_MMIO || REGMAP_IRQ)
+       default y if (REGMAP_I2C || REGMAP_SPI || REGMAP_SPMI || REGMAP_AC97 || REGMAP_MMIO || REGMAP_IRQ)
        select LZO_COMPRESS
        select LZO_DECOMPRESS
        select IRQ_DOMAIN if REGMAP_IRQ
        bool
 
+config REGMAP_AC97
+       tristate
+
 config REGMAP_I2C
        tristate
        depends on I2C
index a7c670b..0a53365 100644 (file)
@@ -1,6 +1,7 @@
 obj-$(CONFIG_REGMAP) += regmap.o regcache.o
 obj-$(CONFIG_REGMAP) += regcache-rbtree.o regcache-lzo.o regcache-flat.o
 obj-$(CONFIG_DEBUG_FS) += regmap-debugfs.o
+obj-$(CONFIG_REGMAP_AC97) += regmap-ac97.o
 obj-$(CONFIG_REGMAP_I2C) += regmap-i2c.o
 obj-$(CONFIG_REGMAP_SPI) += regmap-spi.o
 obj-$(CONFIG_REGMAP_SPMI) += regmap-spmi.o
diff --git a/drivers/base/regmap/regmap-ac97.c b/drivers/base/regmap/regmap-ac97.c
new file mode 100644 (file)
index 0000000..e4c45d2
--- /dev/null
@@ -0,0 +1,114 @@
+/*
+ * Register map access API - AC'97 support
+ *
+ * Copyright 2013 Linaro Ltd.  All rights reserved.
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope it will be useful, but WITHOUT
+ * ANY WARRANTY; without even the implied warranty of MERCHANTABILITY or
+ * FITNESS FOR A PARTICULAR PURPOSE.  See the GNU General Public License for
+ * more details.
+ *
+ * You should have received a copy of the GNU General Public License
+ * along with this program.  If not, see <http://www.gnu.org/licenses/>.
+ */
+
+#include <linux/clk.h>
+#include <linux/err.h>
+#include <linux/init.h>
+#include <linux/io.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+
+#include <sound/ac97_codec.h>
+
+bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case AC97_RESET:
+       case AC97_POWERDOWN:
+       case AC97_INT_PAGING:
+       case AC97_EXTENDED_ID:
+       case AC97_EXTENDED_STATUS:
+       case AC97_EXTENDED_MID:
+       case AC97_EXTENDED_MSTATUS:
+       case AC97_GPIO_STATUS:
+       case AC97_MISC_AFE:
+       case AC97_VENDOR_ID1:
+       case AC97_VENDOR_ID2:
+       case AC97_CODEC_CLASS_REV:
+       case AC97_PCI_SVID:
+       case AC97_PCI_SID:
+       case AC97_FUNC_SELECT:
+       case AC97_FUNC_INFO:
+       case AC97_SENSE_INFO:
+               return true;
+       default:
+               return false;
+       }
+}
+EXPORT_SYMBOL_GPL(regmap_ac97_default_volatile);
+
+static int regmap_ac97_reg_read(void *context, unsigned int reg,
+       unsigned int *val)
+{
+       struct snd_ac97 *ac97 = context;
+
+       *val = ac97->bus->ops->read(ac97, reg);
+
+       return 0;
+}
+
+static int regmap_ac97_reg_write(void *context, unsigned int reg,
+       unsigned int val)
+{
+       struct snd_ac97 *ac97 = context;
+
+       ac97->bus->ops->write(ac97, reg, val);
+
+       return 0;
+}
+
+static const struct regmap_bus ac97_regmap_bus = {
+               .reg_write = regmap_ac97_reg_write,
+               .reg_read = regmap_ac97_reg_read,
+};
+
+/**
+ * regmap_init_ac97(): Initialise AC'97 register map
+ *
+ * @ac97: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer to
+ * a struct regmap.
+ */
+struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
+                               const struct regmap_config *config)
+{
+       return regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
+}
+EXPORT_SYMBOL_GPL(regmap_init_ac97);
+
+/**
+ * devm_regmap_init_ac97(): Initialise AC'97 register map
+ *
+ * @ac97: Device that will be interacted with
+ * @config: Configuration for register map
+ *
+ * The return value will be an ERR_PTR() on error or a valid pointer
+ * to a struct regmap.  The regmap will be automatically freed by the
+ * device management code.
+ */
+struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
+                                    const struct regmap_config *config)
+{
+       return devm_regmap_init(&ac97->dev, &ac97_regmap_bus, ac97, config);
+}
+EXPORT_SYMBOL_GPL(devm_regmap_init_ac97);
+
+MODULE_LICENSE("GPL v2");
index f34723f..910e3aa 100644 (file)
@@ -141,6 +141,7 @@ struct arizona {
 
        uint16_t dac_comp_coeff;
        uint8_t dac_comp_enabled;
+       struct mutex dac_comp_lock;
 };
 
 int arizona_clk32k_enable(struct arizona *arizona);
index cb01496..8e1cdbe 100644 (file)
@@ -99,12 +99,6 @@ struct davinci_vcif {
        dma_addr_t dma_rx_addr;
 };
 
-struct cq93vc {
-       struct platform_device *pdev;
-       struct snd_soc_codec *codec;
-       u32 sysclk;
-};
-
 struct davinci_vc;
 
 struct davinci_vc {
@@ -122,7 +116,6 @@ struct davinci_vc {
 
        /* Client devices */
        struct davinci_vcif davinci_vcif;
-       struct cq93vc cq93vc;
 };
 
 #endif
index a6591c6..5e0bc77 100644 (file)
@@ -27,6 +27,7 @@ struct samsung_i2s {
 #define QUIRK_NO_MUXPSR                (1 << 2)
 #define QUIRK_NEED_RSTCLR      (1 << 3)
 #define QUIRK_SUPPORTS_TDM     (1 << 4)
+#define QUIRK_SUPPORTS_IDMA    (1 << 5)
        /* Quirks of the I2S controller */
        u32 quirks;
        dma_addr_t idma_addr;
index c5ed83f..4419b99 100644 (file)
@@ -27,6 +27,7 @@ struct spmi_device;
 struct regmap;
 struct regmap_range_cfg;
 struct regmap_field;
+struct snd_ac97;
 
 /* An enum of all the supported cache types */
 enum regcache_type {
@@ -340,6 +341,8 @@ struct regmap *regmap_init_spmi_ext(struct spmi_device *dev,
 struct regmap *regmap_init_mmio_clk(struct device *dev, const char *clk_id,
                                    void __iomem *regs,
                                    const struct regmap_config *config);
+struct regmap *regmap_init_ac97(struct snd_ac97 *ac97,
+                               const struct regmap_config *config);
 
 struct regmap *devm_regmap_init(struct device *dev,
                                const struct regmap_bus *bus,
@@ -356,6 +359,10 @@ struct regmap *devm_regmap_init_spmi_ext(struct spmi_device *dev,
 struct regmap *devm_regmap_init_mmio_clk(struct device *dev, const char *clk_id,
                                         void __iomem *regs,
                                         const struct regmap_config *config);
+struct regmap *devm_regmap_init_ac97(struct snd_ac97 *ac97,
+                                    const struct regmap_config *config);
+
+bool regmap_ac97_default_volatile(struct device *dev, unsigned int reg);
 
 /**
  * regmap_init_mmio(): Initialise register map
index d76412b..83284ca 100644 (file)
 #define RSND_SSI_CLK_PIN_SHARE         (1 << 31)
 #define RSND_SSI_NO_BUSIF              (1 << 30) /* SSI+DMA without BUSIF */
 
-#define RSND_SSI(_dma_id, _pio_irq, _flags)            \
-{ .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags }
+#define RSND_SSI(_dma_id, _irq, _flags)                \
+{ .dma_id = _dma_id, .irq = _irq, .flags = _flags }
 #define RSND_SSI_UNUSED \
-{ .dma_id = -1, .pio_irq = -1, .flags = 0 }
+{ .dma_id = -1, .irq = -1, .flags = 0 }
 
 struct rsnd_ssi_platform_info {
        int dma_id;
-       int pio_irq;
+       int irq;
        u32 flags;
 };
 
index a535271..120d961 100644 (file)
@@ -23,6 +23,10 @@ struct rt5645_platform_data {
 
        unsigned int hp_det_gpio;
        bool gpio_hp_det_active_high;
+
+       /* true if codec's jd function is used */
+       bool en_jd_func;
+       unsigned int jd_mode;
 };
 
 #endif
index 082670e..d9eb7d8 100644 (file)
@@ -27,6 +27,16 @@ struct rt5677_platform_data {
        bool lout3_diff;
        /* DMIC2 clock source selection */
        enum rt5677_dmic2_clk dmic2_clk_pin;
+
+       /* configures GPIO, 0 - floating, 1 - pulldown, 2 - pullup */
+       u8 gpio_config[6];
+
+       /* jd1 can select 0 ~ 3 as OFF, GPIO1, GPIO2 and GPIO3 respectively */
+       unsigned int jd1_gpio;
+       /* jd2 and jd3 can select 0 ~ 3 as
+               OFF, GPIO4, GPIO5 and GPIO6 respectively */
+       unsigned int jd2_gpio;
+       unsigned int jd3_gpio;
 };
 
 #endif
index e8b3080..2df96b1 100644 (file)
@@ -206,7 +206,6 @@ struct snd_soc_dai_driver {
        /* DAI description */
        const char *name;
        unsigned int id;
-       int ac97_control;
        unsigned int base;
 
        /* DAI driver callbacks */
@@ -216,6 +215,8 @@ struct snd_soc_dai_driver {
        int (*resume)(struct snd_soc_dai *dai);
        /* compress dai */
        bool compress_dai;
+       /* DAI is also used for the control bus */
+       bool bus_control;
 
        /* ops */
        const struct snd_soc_dai_ops *ops;
@@ -241,7 +242,6 @@ struct snd_soc_dai {
        const char *name;
        int id;
        struct device *dev;
-       void *ac97_pdata;       /* platform_data for the ac97 codec */
 
        /* driver ops */
        struct snd_soc_dai_driver *driver;
@@ -268,7 +268,6 @@ struct snd_soc_dai {
        unsigned int sample_bits;
 
        /* parent platform/codec */
-       struct snd_soc_platform *platform;
        struct snd_soc_codec *codec;
        struct snd_soc_component *component;
 
@@ -276,8 +275,6 @@ struct snd_soc_dai {
        unsigned int tx_mask;
        unsigned int rx_mask;
 
-       struct snd_soc_card *card;
-
        struct list_head list;
 };
 
index 3a4d7da..89823cf 100644 (file)
@@ -435,7 +435,7 @@ void snd_soc_dapm_auto_nc_pins(struct snd_soc_card *card);
 unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol);
 
 /* Mostly internal - should not normally be used */
-void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm);
+void dapm_mark_endpoints_dirty(struct snd_soc_card *card);
 
 /* dapm path query */
 int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
@@ -508,9 +508,9 @@ struct snd_soc_dapm_path {
 
        /* status */
        u32 connect:1;  /* source and sink widgets are connected */
-       u32 walked:1;   /* path has been walked */
        u32 walking:1;  /* path is in the process of being walked */
        u32 weak:1;     /* path ignored for power management */
+       u32 is_supply:1;        /* At least one of the connected widgets is a supply */
 
        int (*connected)(struct snd_soc_dapm_widget *source,
                         struct snd_soc_dapm_widget *sink);
@@ -544,11 +544,13 @@ struct snd_soc_dapm_widget {
        unsigned char active:1;                 /* active stream on DAC, ADC's */
        unsigned char connected:1;              /* connected codec pin */
        unsigned char new:1;                    /* cnew complete */
-       unsigned char ext:1;                    /* has external widgets */
        unsigned char force:1;                  /* force state */
        unsigned char ignore_suspend:1;         /* kept enabled over suspend */
        unsigned char new_power:1;              /* power from this run */
        unsigned char power_checked:1;          /* power checked this run */
+       unsigned char is_supply:1;              /* Widget is a supply type widget */
+       unsigned char is_sink:1;                /* Widget is a sink type widget */
+       unsigned char is_source:1;              /* Widget is a source type widget */
        int subseq;                             /* sort within widget type */
 
        int (*power_check)(struct snd_soc_dapm_widget *w);
@@ -567,6 +569,7 @@ struct snd_soc_dapm_widget {
        struct list_head sinks;
 
        /* used during DAPM updates */
+       struct list_head work_list;
        struct list_head power_list;
        struct list_head dirty;
        int inputs;
index 7ba7130..b4fca9a 100644 (file)
        {.reg = xreg, .rreg = xreg, .shift = shift_left, \
        .rshift = shift_right, .max = xmax, .platform_max = xmax, \
        .invert = xinvert, .autodisable = xautodisable})
+#define SOC_DOUBLE_S_VALUE(xreg, shift_left, shift_right, xmin, xmax, xsign_bit, xinvert, xautodisable) \
+       ((unsigned long)&(struct soc_mixer_control) \
+       {.reg = xreg, .rreg = xreg, .shift = shift_left, \
+       .rshift = shift_right, .min = xmin, .max = xmax, .platform_max = xmax, \
+       .sign_bit = xsign_bit, .invert = xinvert, .autodisable = xautodisable})
 #define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \
        SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable)
 #define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \
        .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
                  SNDRV_CTL_ELEM_ACCESS_READWRITE, \
        .tlv.p  = (tlv_array), \
-       .info   = snd_soc_info_volsw_s8, .get = snd_soc_get_volsw_s8, \
-       .put    = snd_soc_put_volsw_s8, \
-       .private_value = (unsigned long)&(struct soc_mixer_control) \
-               {.reg = xreg, .min = xmin, .max = xmax, \
-                .platform_max = xmax} }
+       .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
+       .put = snd_soc_put_volsw, \
+       .private_value = SOC_DOUBLE_S_VALUE(xreg, 0, 8, xmin, xmax, 7, 0, 0) }
 #define SOC_ENUM_DOUBLE(xreg, xshift_l, xshift_r, xitems, xtexts) \
 {      .reg = xreg, .shift_l = xshift_l, .shift_r = xshift_r, \
        .items = xitems, .texts = xtexts, \
@@ -366,8 +369,6 @@ struct snd_soc_jack_gpio;
 
 typedef int (*hw_write_t)(void *,const char* ,int);
 
-extern struct snd_ac97_bus_ops *soc_ac97_ops;
-
 enum snd_soc_pcm_subclass {
        SND_SOC_PCM_CLASS_PCM   = 0,
        SND_SOC_PCM_CLASS_BE    = 1,
@@ -409,13 +410,9 @@ int devm_snd_soc_register_component(struct device *dev,
                         const struct snd_soc_component_driver *cmpnt_drv,
                         struct snd_soc_dai_driver *dai_drv, int num_dai);
 void snd_soc_unregister_component(struct device *dev);
-int snd_soc_cache_sync(struct snd_soc_codec *codec);
 int snd_soc_cache_init(struct snd_soc_codec *codec);
 int snd_soc_cache_exit(struct snd_soc_codec *codec);
-int snd_soc_cache_write(struct snd_soc_codec *codec,
-                       unsigned int reg, unsigned int value);
-int snd_soc_cache_read(struct snd_soc_codec *codec,
-                      unsigned int reg, unsigned int *value);
+
 int snd_soc_platform_read(struct snd_soc_platform *platform,
                                        unsigned int reg);
 int snd_soc_platform_write(struct snd_soc_platform *platform,
@@ -500,14 +497,28 @@ int snd_soc_update_bits_locked(struct snd_soc_codec *codec,
 int snd_soc_test_bits(struct snd_soc_codec *codec, unsigned int reg,
                                unsigned int mask, unsigned int value);
 
-int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
-       struct snd_ac97_bus_ops *ops, int num);
-void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
+#ifdef CONFIG_SND_SOC_AC97_BUS
+struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec);
+void snd_soc_free_ac97_codec(struct snd_ac97 *ac97);
 
 int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops);
 int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
                struct platform_device *pdev);
 
+extern struct snd_ac97_bus_ops *soc_ac97_ops;
+#else
+static inline int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
+       struct platform_device *pdev)
+{
+       return 0;
+}
+
+static inline int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
+{
+       return 0;
+}
+#endif
+
 /*
  *Controls
  */
@@ -545,12 +556,6 @@ int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
-int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo);
-int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol);
-int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol);
 int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_info *uinfo);
 int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
@@ -780,24 +785,18 @@ struct snd_soc_codec {
        struct device *dev;
        const struct snd_soc_codec_driver *driver;
 
-       struct mutex mutex;
        struct list_head list;
        struct list_head card_list;
 
        /* runtime */
-       struct snd_ac97 *ac97;  /* for ad-hoc ac97 devices */
        unsigned int cache_bypass:1; /* Suppress access to the cache */
        unsigned int suspended:1; /* Codec is in suspend PM state */
-       unsigned int ac97_registered:1; /* Codec has been AC97 registered */
-       unsigned int ac97_created:1; /* Codec has been created by SoC */
        unsigned int cache_init:1; /* codec cache has been initialized */
-       u32 cache_sync; /* Cache needs to be synced to hardware */
 
        /* codec IO */
        void *control_data; /* codec control (i2c/3wire) data */
        hw_write_t hw_write;
        void *reg_cache;
-       struct mutex cache_rw_mutex;
 
        /* component */
        struct snd_soc_component component;
@@ -860,8 +859,6 @@ struct snd_soc_platform_driver {
 
        int (*probe)(struct snd_soc_platform *);
        int (*remove)(struct snd_soc_platform *);
-       int (*suspend)(struct snd_soc_dai *dai);
-       int (*resume)(struct snd_soc_dai *dai);
        struct snd_soc_component_driver component_driver;
 
        /* pcm creation and destruction */
@@ -886,7 +883,7 @@ struct snd_soc_platform_driver {
 
 struct snd_soc_dai_link_component {
        const char *name;
-       const struct device_node *of_node;
+       struct device_node *of_node;
        const char *dai_name;
 };
 
@@ -894,8 +891,6 @@ struct snd_soc_platform {
        struct device *dev;
        const struct snd_soc_platform_driver *driver;
 
-       unsigned int suspended:1; /* platform is suspended */
-
        struct list_head list;
 
        struct snd_soc_component component;
@@ -990,7 +985,7 @@ struct snd_soc_codec_conf {
         * DT/OF node, but not both.
         */
        const char *dev_name;
-       const struct device_node *of_node;
+       struct device_node *of_node;
 
        /*
         * optional map of kcontrol, widget and path name prefixes that are
@@ -1007,7 +1002,7 @@ struct snd_soc_aux_dev {
         * DT/OF node, but not both.
         */
        const char *codec_name;
-       const struct device_node *codec_of_node;
+       struct device_node *codec_of_node;
 
        /* codec/machine specific init - e.g. add machine controls */
        int (*init)(struct snd_soc_component *component);
@@ -1264,6 +1259,17 @@ unsigned int snd_soc_read(struct snd_soc_codec *codec, unsigned int reg);
 int snd_soc_write(struct snd_soc_codec *codec, unsigned int reg,
        unsigned int val);
 
+/**
+ * snd_soc_cache_sync() - Sync the register cache with the hardware
+ * @codec: CODEC to sync
+ *
+ * Note: This function will call regcache_sync()
+ */
+static inline int snd_soc_cache_sync(struct snd_soc_codec *codec)
+{
+       return regcache_sync(codec->component.regmap);
+}
+
 /* component IO */
 int snd_soc_component_read(struct snd_soc_component *component,
        unsigned int reg, unsigned int *val);
@@ -1277,6 +1283,45 @@ void snd_soc_component_async_complete(struct snd_soc_component *component);
 int snd_soc_component_test_bits(struct snd_soc_component *component,
        unsigned int reg, unsigned int mask, unsigned int value);
 
+#ifdef CONFIG_REGMAP
+
+void snd_soc_component_init_regmap(struct snd_soc_component *component,
+       struct regmap *regmap);
+void snd_soc_component_exit_regmap(struct snd_soc_component *component);
+
+/**
+ * snd_soc_codec_init_regmap() - Initialize regmap instance for the CODEC
+ * @codec: The CODEC for which to initialize the regmap instance
+ * @regmap: The regmap instance that should be used by the CODEC
+ *
+ * This function allows deferred assignment of the regmap instance that is
+ * associated with the CODEC. Only use this if the regmap instance is not yet
+ * ready when the CODEC is registered. The function must also be called before
+ * the first IO attempt of the CODEC.
+ */
+static inline void snd_soc_codec_init_regmap(struct snd_soc_codec *codec,
+       struct regmap *regmap)
+{
+       snd_soc_component_init_regmap(&codec->component, regmap);
+}
+
+/**
+ * snd_soc_codec_exit_regmap() - De-initialize regmap instance for the CODEC
+ * @codec: The CODEC for which to de-initialize the regmap instance
+ *
+ * Calls regmap_exit() on the regmap instance associated to the CODEC and
+ * removes the regmap instance from the CODEC.
+ *
+ * This function should only be used if snd_soc_codec_init_regmap() was used to
+ * initialize the regmap instance.
+ */
+static inline void snd_soc_codec_exit_regmap(struct snd_soc_codec *codec)
+{
+       snd_soc_component_exit_regmap(&codec->component);
+}
+
+#endif
+
 /* device driver data */
 
 static inline void snd_soc_card_set_drvdata(struct snd_soc_card *card,
@@ -1451,6 +1496,9 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
                                     struct device_node **framemaster);
 int snd_soc_of_get_dai_name(struct device_node *of_node,
                            const char **dai_name);
+int snd_soc_of_get_dai_link_codecs(struct device *dev,
+                                  struct device_node *of_node,
+                                  struct snd_soc_dai_link *dai_link);
 
 #include <sound/soc-dai.h>
 
index e475659..509efb0 100644 (file)
@@ -18,18 +18,6 @@ struct uda134x_platform_data {
        struct l3_pins l3;
        void (*power) (int);
        int model;
-       /*
-         ALSA SOC usually puts the device in standby mode when it's not used
-         for sometime. If you unset is_powered_on_standby the driver will
-         turn off the ADC/DAC when this callback is invoked and turn it back
-         on when needed. Unfortunately this will result in a very light bump
-         (it can be audible only with good earphones). If this bothers you
-         set is_powered_on_standby, you will have slightly higher power
-         consumption. Please note that sending the L3 command for ADC is
-         enough to make the bump, so it doesn't make difference if you
-         completely take off power from the codec.
-       */
-       int is_powered_on_standby;
 #define UDA134X_UDA1340 1
 #define UDA134X_UDA1341 2
 #define UDA134X_UDA1344 3
index b04ee7e..88cf39d 100644 (file)
@@ -288,31 +288,6 @@ TRACE_EVENT(snd_soc_jack_notify,
        TP_printk("jack=%s %x", __get_str(name), (int)__entry->val)
 );
 
-TRACE_EVENT(snd_soc_cache_sync,
-
-       TP_PROTO(struct snd_soc_codec *codec, const char *type,
-                const char *status),
-
-       TP_ARGS(codec, type, status),
-
-       TP_STRUCT__entry(
-               __string(       name,           codec->component.name)
-               __string(       status,         status          )
-               __string(       type,           type            )
-               __field(        int,            id              )
-       ),
-
-       TP_fast_assign(
-               __assign_str(name, codec->component.name);
-               __assign_str(status, status);
-               __assign_str(type, type);
-               __entry->id = codec->component.id;
-       ),
-
-       TP_printk("codec=%s.%d type=%s status=%s", __get_str(name),
-                 (int)__entry->id, __get_str(type), __get_str(status))
-);
-
 #endif /* _TRACE_ASOC_H */
 
 /* This part must be outside protection */
index d88edfc..865e090 100644 (file)
@@ -1,10 +1,14 @@
 snd-soc-core-objs := soc-core.o soc-dapm.o soc-jack.o soc-cache.o soc-utils.o
-snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o
+snd-soc-core-objs += soc-pcm.o soc-compress.o soc-io.o soc-devres.o soc-ops.o
 
 ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
 snd-soc-core-objs += soc-generic-dmaengine-pcm.o
 endif
 
+ifneq ($(CONFIG_SND_SOC_AC97_BUS),)
+snd-soc-core-objs += soc-ac97.o
+endif
+
 obj-$(CONFIG_SND_SOC)  += snd-soc-core.o
 obj-$(CONFIG_SND_SOC)  += codecs/
 obj-$(CONFIG_SND_SOC)  += generic/
index 27e3fc4..fb38783 100644 (file)
@@ -52,12 +52,3 @@ config SND_AT91_SOC_SAM9X5_WM8731
        help
          Say Y if you want to add support for audio SoC on an
          at91sam9x5 based board that is using WM8731 codec.
-
-config SND_AT91_SOC_AFEB9260
-       tristate "SoC Audio support for AFEB9260 board"
-       depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
-       select SND_ATMEL_SOC_PDC
-       select SND_ATMEL_SOC_SSC
-       select SND_SOC_TLV320AIC23_I2C
-       help
-         Say Y here to support sound on AFEB9260 board.
index 5baabc8..466a821 100644 (file)
@@ -17,4 +17,3 @@ snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
 obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
 obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
 obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
-obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
index f403f39..99ff35e 100644 (file)
@@ -267,7 +267,7 @@ static void atmel_ssc_shutdown(struct snd_pcm_substream *substream,
        if (!ssc_p->dir_mask) {
                if (ssc_p->initialized) {
                        /* Shutdown the SSC clock. */
-                       pr_debug("atmel_ssc_dau: Stopping clock\n");
+                       pr_debug("atmel_ssc_dai: Stopping clock\n");
                        clk_disable(ssc_p->ssc->clk);
 
                        free_irq(ssc_p->ssc->irq, ssc_p);
@@ -310,7 +310,10 @@ static int atmel_ssc_set_dai_clkdiv(struct snd_soc_dai *cpu_dai,
                 * transmit and receive, so if a value has already
                 * been set, it must match this value.
                 */
-               if (ssc_p->cmr_div == 0)
+               if (ssc_p->dir_mask !=
+                       (SSC_DIR_MASK_PLAYBACK | SSC_DIR_MASK_CAPTURE))
+                       ssc_p->cmr_div = div;
+               else if (ssc_p->cmr_div == 0)
                        ssc_p->cmr_div = div;
                else
                        if (div != ssc_p->cmr_div)
diff --git a/sound/soc/atmel/snd-soc-afeb9260.c b/sound/soc/atmel/snd-soc-afeb9260.c
deleted file mode 100644 (file)
index 9579799..0000000
+++ /dev/null
@@ -1,151 +0,0 @@
-/*
- * afeb9260.c  --  SoC audio for AFEB9260
- *
- * Copyright (C) 2009 Sergey Lapin <slapin@ossfans.org>
- *
- * This program is free software; you can redistribute it and/or
- * modify it under the terms of the GNU General Public License
- * version 2 as published by the Free Software Foundation.
- *
- * This program is distributed in the hope that it will be useful, but
- * WITHOUT ANY WARRANTY; without even the implied warranty of
- * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
- * General Public License for more details.
- *
- * 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., 51 Franklin St, Fifth Floor, Boston, MA
- * 02110-1301 USA
- *
- */
-
-#include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/kernel.h>
-#include <linux/clk.h>
-#include <linux/platform_device.h>
-
-#include <linux/atmel-ssc.h>
-#include <sound/core.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-
-#include <asm/mach-types.h>
-#include <mach/hardware.h>
-#include <linux/gpio.h>
-
-#include "../codecs/tlv320aic23.h"
-#include "atmel-pcm.h"
-#include "atmel_ssc_dai.h"
-
-#define CODEC_CLOCK    12000000
-
-static int afeb9260_hw_params(struct snd_pcm_substream *substream,
-                        struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int err;
-
-       /* Set the codec system clock for DAC and ADC */
-       err =
-           snd_soc_dai_set_sysclk(codec_dai, 0, CODEC_CLOCK, SND_SOC_CLOCK_IN);
-
-       if (err < 0) {
-               printk(KERN_ERR "can't set codec system clock\n");
-               return err;
-       }
-
-       return err;
-}
-
-static struct snd_soc_ops afeb9260_ops = {
-       .hw_params = afeb9260_hw_params,
-};
-
-static const struct snd_soc_dapm_widget tlv320aic23_dapm_widgets[] = {
-       SND_SOC_DAPM_HP("Headphone Jack", NULL),
-       SND_SOC_DAPM_LINE("Line In", NULL),
-       SND_SOC_DAPM_MIC("Mic Jack", NULL),
-};
-
-static const struct snd_soc_dapm_route afeb9260_audio_map[] = {
-       {"Headphone Jack", NULL, "LHPOUT"},
-       {"Headphone Jack", NULL, "RHPOUT"},
-
-       {"LLINEIN", NULL, "Line In"},
-       {"RLINEIN", NULL, "Line In"},
-
-       {"MICIN", NULL, "Mic Jack"},
-};
-
-
-/* Digital audio interface glue - connects codec <--> CPU */
-static struct snd_soc_dai_link afeb9260_dai = {
-       .name = "TLV320AIC23",
-       .stream_name = "AIC23",
-       .cpu_dai_name = "atmel-ssc-dai.0",
-       .codec_dai_name = "tlv320aic23-hifi",
-       .platform_name = "atmel_pcm-audio",
-       .codec_name = "tlv320aic23-codec.0-001a",
-       .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_IF |
-                  SND_SOC_DAIFMT_CBM_CFM,
-       .ops = &afeb9260_ops,
-};
-
-/* Audio machine driver */
-static struct snd_soc_card snd_soc_machine_afeb9260 = {
-       .name = "AFEB9260",
-       .owner = THIS_MODULE,
-       .dai_link = &afeb9260_dai,
-       .num_links = 1,
-
-       .dapm_widgets = tlv320aic23_dapm_widgets,
-       .num_dapm_widgets = ARRAY_SIZE(tlv320aic23_dapm_widgets),
-       .dapm_routes = afeb9260_audio_map,
-       .num_dapm_routes = ARRAY_SIZE(afeb9260_audio_map),
-};
-
-static struct platform_device *afeb9260_snd_device;
-
-static int __init afeb9260_soc_init(void)
-{
-       int err;
-       struct device *dev;
-
-       if (!(machine_is_afeb9260()))
-               return -ENODEV;
-
-
-       afeb9260_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!afeb9260_snd_device) {
-               printk(KERN_ERR "ASoC: Platform device allocation failed\n");
-               return -ENOMEM;
-       }
-
-       platform_set_drvdata(afeb9260_snd_device, &snd_soc_machine_afeb9260);
-       err = platform_device_add(afeb9260_snd_device);
-       if (err)
-               goto err1;
-
-       dev = &afeb9260_snd_device->dev;
-
-       return 0;
-err1:
-       platform_device_put(afeb9260_snd_device);
-       return err;
-}
-
-static void __exit afeb9260_soc_exit(void)
-{
-       platform_device_unregister(afeb9260_snd_device);
-}
-
-module_init(afeb9260_soc_init);
-module_exit(afeb9260_soc_exit);
-
-MODULE_AUTHOR("Sergey Lapin <slapin@ossfans.org>");
-MODULE_DESCRIPTION("ALSA SoC for AFEB9260");
-MODULE_LICENSE("GPL");
-
index c8a2de1..5159a50 100644 (file)
@@ -205,7 +205,7 @@ static int au1xac97c_dai_probe(struct snd_soc_dai *dai)
 
 static struct snd_soc_dai_driver au1xac97c_dai_driver = {
        .name                   = "alchemy-ac97c",
-       .ac97_control           = 1,
+       .bus_control            = true,
        .probe                  = au1xac97c_dai_probe,
        .playback = {
                .rates          = AC97_RATES,
index 84f31e1..c6daec9 100644 (file)
@@ -343,7 +343,7 @@ static const struct snd_soc_dai_ops au1xpsc_ac97_dai_ops = {
 };
 
 static const struct snd_soc_dai_driver au1xpsc_ac97_dai_template = {
-       .ac97_control           = 1,
+       .bus_control            = true,
        .probe                  = au1xpsc_ac97_probe,
        .playback = {
                .rates          = AC97_RATES,
index e82eb37..6bf21a6 100644 (file)
@@ -260,7 +260,7 @@ static int bf5xx_ac97_resume(struct snd_soc_dai *dai)
 #endif
 
 static struct snd_soc_dai_driver bfin_ac97_dai = {
-       .ac97_control = 1,
+       .bus_control = true,
        .suspend = bf5xx_ac97_suspend,
        .resume = bf5xx_ac97_resume,
        .playback = {
index 3450e8f..0fa81a5 100644 (file)
@@ -46,8 +46,6 @@
 #include <linux/gpio.h>
 #include <asm/portmux.h>
 
-#include "../codecs/ad1980.h"
-
 #include "bf5xx-ac97.h"
 
 static struct snd_soc_card bf5xx_board;
index 5477c54..7b7fbcd 100644 (file)
@@ -36,7 +36,8 @@ config SND_EP93XX_SOC_EDB93XX
        tristate "SoC Audio support for Cirrus Logic EDB93xx boards"
        depends on SND_EP93XX_SOC && (MACH_EDB9301 || MACH_EDB9302 || MACH_EDB9302A || MACH_EDB9307A || MACH_EDB9315A)
        select SND_EP93XX_SOC_I2S
-       select SND_SOC_CS4271
+       select SND_SOC_CS4271_I2C if I2C
+       select SND_SOC_CS4271_SPI if SPI_MASTER
        help
          Say Y or M here if you want to add support for I2S audio on the
          Cirrus Logic EDB93xx boards.
index f30dadf..6b8a366 100644 (file)
@@ -338,7 +338,7 @@ static const struct snd_soc_dai_ops ep93xx_ac97_dai_ops = {
 static struct snd_soc_dai_driver ep93xx_ac97_dai = {
        .name           = "ep93xx-ac97",
        .id             = 0,
-       .ac97_control   = 1,
+       .bus_control    = true,
        .probe          = ep93xx_ac97_dai_probe,
        .playback       = {
                .stream_name    = "AC97 Playback",
index a68d173..8349f98 100644 (file)
@@ -50,7 +50,8 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CS42L73 if I2C
        select SND_SOC_CS4265 if I2C
        select SND_SOC_CS4270 if I2C
-       select SND_SOC_CS4271 if SND_SOC_I2C_AND_SPI
+       select SND_SOC_CS4271_I2C if I2C
+       select SND_SOC_CS4271_SPI if SPI_MASTER
        select SND_SOC_CS42XX8_I2C if I2C
        select SND_SOC_CX20442 if TTY
        select SND_SOC_DA7210 if I2C
@@ -85,7 +86,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_RT5645 if I2C
        select SND_SOC_RT5651 if I2C
        select SND_SOC_RT5670 if I2C
-       select SND_SOC_RT5677 if I2C
+       select SND_SOC_RT5677 if I2C && SPI_MASTER
        select SND_SOC_SGTL5000 if I2C
        select SND_SOC_SI476X if MFD_SI476X_CORE
        select SND_SOC_SIRF_AUDIO_CODEC
@@ -101,6 +102,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_STAC9766 if SND_SOC_AC97_BUS
        select SND_SOC_TAS2552 if I2C
        select SND_SOC_TAS5086 if I2C
+       select SND_SOC_TFA9879 if I2C
        select SND_SOC_TLV320AIC23_I2C if I2C
        select SND_SOC_TLV320AIC23_SPI if SPI_MASTER
        select SND_SOC_TLV320AIC26 if SPI_MASTER
@@ -109,6 +111,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_TLV320AIC3X if I2C
        select SND_SOC_TPA6130A2 if I2C
        select SND_SOC_TLV320DAC33 if I2C
+       select SND_SOC_TS3A227E if I2C
        select SND_SOC_TWL4030 if TWL4030_CORE
        select SND_SOC_TWL6040 if TWL6040_CORE
        select SND_SOC_UDA134X
@@ -223,6 +226,7 @@ config SND_SOC_AD193X_I2C
        select SND_SOC_AD193X
 
 config SND_SOC_AD1980
+       select REGMAP_AC97
        tristate
 
 config SND_SOC_AD73311
@@ -336,7 +340,8 @@ config SND_SOC_CS42L51
        tristate
 
 config SND_SOC_CS42L51_I2C
-       tristate
+       tristate "Cirrus Logic CS42L51 CODEC (I2C)"
+       depends on I2C
        select SND_SOC_CS42L51
 
 config SND_SOC_CS42L52
@@ -370,8 +375,19 @@ config SND_SOC_CS4270_VD33_ERRATA
        depends on SND_SOC_CS4270
 
 config SND_SOC_CS4271
-       tristate "Cirrus Logic CS4271 CODEC"
-       depends on SND_SOC_I2C_AND_SPI
+       tristate
+
+config SND_SOC_CS4271_I2C
+       tristate "Cirrus Logic CS4271 CODEC (I2C)"
+       depends on I2C
+       select SND_SOC_CS4271
+       select REGMAP_I2C
+
+config SND_SOC_CS4271_SPI
+       tristate "Cirrus Logic CS4271 CODEC (SPI)"
+       depends on SPI_MASTER
+       select SND_SOC_CS4271
+       select REGMAP_SPI
 
 config SND_SOC_CS42XX8
        tristate
@@ -487,7 +503,8 @@ config SND_SOC_RT286
        depends on I2C
 
 config SND_SOC_RT5631
-       tristate
+       tristate "Realtek ALC5631/RT5631 CODEC"
+       depends on I2C
 
 config SND_SOC_RT5640
        tristate
@@ -503,6 +520,12 @@ config SND_SOC_RT5670
 
 config SND_SOC_RT5677
        tristate
+       select REGMAP_I2C
+       select REGMAP_IRQ
+
+config SND_SOC_RT5677_SPI
+       tristate
+       default SND_SOC_RT5677
 
 #Freescale sgtl5000 codec
 config SND_SOC_SGTL5000
@@ -577,15 +600,21 @@ config SND_SOC_TAS5086
        tristate "Texas Instruments TAS5086 speaker amplifier"
        depends on I2C
 
+config SND_SOC_TFA9879
+       tristate "NXP Semiconductors TFA9879 amplifier"
+       depends on I2C
+
 config SND_SOC_TLV320AIC23
        tristate
 
 config SND_SOC_TLV320AIC23_I2C
-       tristate
+       tristate "Texas Instruments TLV320AIC23 audio CODEC - I2C"
+       depends on I2C
        select SND_SOC_TLV320AIC23
 
 config SND_SOC_TLV320AIC23_SPI
-       tristate
+       tristate "Texas Instruments TLV320AIC23 audio CODEC - SPI"
+       depends on SPI_MASTER
        select SND_SOC_TLV320AIC23
 
 config SND_SOC_TLV320AIC26
@@ -607,6 +636,10 @@ config SND_SOC_TLV320AIC3X
 config SND_SOC_TLV320DAC33
        tristate
 
+config SND_SOC_TS3A227E
+       tristate "TI Headset/Mic detect and keypress chip"
+       depends on I2C
+
 config SND_SOC_TWL4030
        select MFD_TWL4030_AUDIO
        tristate
index 5dce451..bbdfd1e 100644 (file)
@@ -41,6 +41,8 @@ snd-soc-cs42l73-objs := cs42l73.o
 snd-soc-cs4265-objs := cs4265.o
 snd-soc-cs4270-objs := cs4270.o
 snd-soc-cs4271-objs := cs4271.o
+snd-soc-cs4271-i2c-objs := cs4271-i2c.o
+snd-soc-cs4271-spi-objs := cs4271-spi.o
 snd-soc-cs42xx8-objs := cs42xx8.o
 snd-soc-cs42xx8-i2c-objs := cs42xx8-i2c.o
 snd-soc-cx20442-objs := cx20442.o
@@ -80,6 +82,7 @@ snd-soc-rt5645-objs := rt5645.o
 snd-soc-rt5651-objs := rt5651.o
 snd-soc-rt5670-objs := rt5670.o
 snd-soc-rt5677-objs := rt5677.o
+snd-soc-rt5677-spi-objs := rt5677-spi.o
 snd-soc-sgtl5000-objs := sgtl5000.o
 snd-soc-alc5623-objs := alc5623.o
 snd-soc-alc5632-objs := alc5632.o
@@ -101,6 +104,7 @@ snd-soc-sta350-objs := sta350.o
 snd-soc-sta529-objs := sta529.o
 snd-soc-stac9766-objs := stac9766.o
 snd-soc-tas5086-objs := tas5086.o
+snd-soc-tfa9879-objs := tfa9879.o
 snd-soc-tlv320aic23-objs := tlv320aic23.o
 snd-soc-tlv320aic23-i2c-objs := tlv320aic23-i2c.o
 snd-soc-tlv320aic23-spi-objs := tlv320aic23-spi.o
@@ -109,6 +113,7 @@ snd-soc-tlv320aic31xx-objs := tlv320aic31xx.o
 snd-soc-tlv320aic32x4-objs := tlv320aic32x4.o
 snd-soc-tlv320aic3x-objs := tlv320aic3x.o
 snd-soc-tlv320dac33-objs := tlv320dac33.o
+snd-soc-ts3a227e-objs := ts3a227e.o
 snd-soc-twl4030-objs := twl4030.o
 snd-soc-twl6040-objs := twl6040.o
 snd-soc-uda134x-objs := uda134x.o
@@ -217,6 +222,8 @@ obj-$(CONFIG_SND_SOC_CS42L73)       += snd-soc-cs42l73.o
 obj-$(CONFIG_SND_SOC_CS4265)   += snd-soc-cs4265.o
 obj-$(CONFIG_SND_SOC_CS4270)   += snd-soc-cs4270.o
 obj-$(CONFIG_SND_SOC_CS4271)   += snd-soc-cs4271.o
+obj-$(CONFIG_SND_SOC_CS4271_I2C)       += snd-soc-cs4271-i2c.o
+obj-$(CONFIG_SND_SOC_CS4271_SPI)       += snd-soc-cs4271-spi.o
 obj-$(CONFIG_SND_SOC_CS42XX8)  += snd-soc-cs42xx8.o
 obj-$(CONFIG_SND_SOC_CS42XX8_I2C) += snd-soc-cs42xx8-i2c.o
 obj-$(CONFIG_SND_SOC_CX20442)  += snd-soc-cx20442.o
@@ -256,6 +263,7 @@ obj-$(CONFIG_SND_SOC_RT5645)        += snd-soc-rt5645.o
 obj-$(CONFIG_SND_SOC_RT5651)   += snd-soc-rt5651.o
 obj-$(CONFIG_SND_SOC_RT5670)   += snd-soc-rt5670.o
 obj-$(CONFIG_SND_SOC_RT5677)   += snd-soc-rt5677.o
+obj-$(CONFIG_SND_SOC_RT5677_SPI)       += snd-soc-rt5677-spi.o
 obj-$(CONFIG_SND_SOC_SGTL5000)  += snd-soc-sgtl5000.o
 obj-$(CONFIG_SND_SOC_SIGMADSP) += snd-soc-sigmadsp.o
 obj-$(CONFIG_SND_SOC_SIGMADSP_I2C)     += snd-soc-sigmadsp-i2c.o
@@ -274,6 +282,7 @@ obj-$(CONFIG_SND_SOC_STA529)   += snd-soc-sta529.o
 obj-$(CONFIG_SND_SOC_STAC9766) += snd-soc-stac9766.o
 obj-$(CONFIG_SND_SOC_TAS2552)  += snd-soc-tas2552.o
 obj-$(CONFIG_SND_SOC_TAS5086)  += snd-soc-tas5086.o
+obj-$(CONFIG_SND_SOC_TFA9879)  += snd-soc-tfa9879.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23)      += snd-soc-tlv320aic23.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23_I2C)  += snd-soc-tlv320aic23-i2c.o
 obj-$(CONFIG_SND_SOC_TLV320AIC23_SPI)  += snd-soc-tlv320aic23-spi.o
@@ -282,6 +291,7 @@ obj-$(CONFIG_SND_SOC_TLV320AIC31XX)     += snd-soc-tlv320aic31xx.o
 obj-$(CONFIG_SND_SOC_TLV320AIC32X4)     += snd-soc-tlv320aic32x4.o
 obj-$(CONFIG_SND_SOC_TLV320AIC3X)      += snd-soc-tlv320aic3x.o
 obj-$(CONFIG_SND_SOC_TLV320DAC33)      += snd-soc-tlv320dac33.o
+obj-$(CONFIG_SND_SOC_TS3A227E) += snd-soc-ts3a227e.o
 obj-$(CONFIG_SND_SOC_TWL4030)  += snd-soc-twl4030.o
 obj-$(CONFIG_SND_SOC_TWL6040)  += snd-soc-twl6040.o
 obj-$(CONFIG_SND_SOC_UDA134X)  += snd-soc-uda134x.o
index fd43827..7dfbc99 100644 (file)
@@ -126,13 +126,13 @@ struct ab8500_codec_drvdata_dbg {
 /* Private data for AB8500 device-driver */
 struct ab8500_codec_drvdata {
        struct regmap *regmap;
+       struct mutex ctrl_lock;
 
        /* Sidetone */
        long *sid_fir_values;
        enum sid_state sid_status;
 
        /* ANC */
-       struct mutex anc_lock;
        long *anc_fir_values;
        long *anc_iir_values;
        enum anc_state anc_status;
@@ -1129,9 +1129,9 @@ static int sid_status_control_get(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&drvdata->ctrl_lock);
        ucontrol->value.integer.value[0] = drvdata->sid_status;
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&drvdata->ctrl_lock);
 
        return 0;
 }
@@ -1154,7 +1154,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
                return -EIO;
        }
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&drvdata->ctrl_lock);
 
        sidconf = snd_soc_read(codec, AB8500_SIDFIRCONF);
        if (((sidconf & BIT(AB8500_SIDFIRCONF_FIRSIDBUSY)) != 0)) {
@@ -1185,7 +1185,7 @@ static int sid_status_control_put(struct snd_kcontrol *kcontrol,
        drvdata->sid_status = SID_FIR_CONFIGURED;
 
 out:
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&drvdata->ctrl_lock);
 
        dev_dbg(codec->dev, "%s: Exit\n", __func__);
 
@@ -1198,9 +1198,9 @@ static int anc_status_control_get(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct ab8500_codec_drvdata *drvdata = dev_get_drvdata(codec->dev);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&drvdata->ctrl_lock);
        ucontrol->value.integer.value[0] = drvdata->anc_status;
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&drvdata->ctrl_lock);
 
        return 0;
 }
@@ -1217,7 +1217,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
 
        dev_dbg(dev, "%s: Enter.\n", __func__);
 
-       mutex_lock(&drvdata->anc_lock);
+       mutex_lock(&drvdata->ctrl_lock);
 
        req = ucontrol->value.integer.value[0];
        if (req >= ARRAY_SIZE(enum_anc_state)) {
@@ -1244,9 +1244,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
        }
        snd_soc_dapm_sync(&codec->dapm);
 
-       mutex_lock(&codec->mutex);
        anc_configure(codec, apply_fir, apply_iir);
-       mutex_unlock(&codec->mutex);
 
        if (apply_fir) {
                if (drvdata->anc_status == ANC_IIR_CONFIGURED)
@@ -1265,7 +1263,7 @@ static int anc_status_control_put(struct snd_kcontrol *kcontrol,
        snd_soc_dapm_sync(&codec->dapm);
 
 cleanup:
-       mutex_unlock(&drvdata->anc_lock);
+       mutex_unlock(&drvdata->ctrl_lock);
 
        if (status < 0)
                dev_err(dev, "%s: Unable to configure ANC! (status = %d)\n",
@@ -1294,14 +1292,15 @@ static int filter_control_get(struct snd_kcontrol *kcontrol,
                        struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
        struct filter_control *fc =
                        (struct filter_control *)kcontrol->private_value;
        unsigned int i;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&drvdata->ctrl_lock);
        for (i = 0; i < fc->count; i++)
                ucontrol->value.integer.value[i] = fc->value[i];
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&drvdata->ctrl_lock);
 
        return 0;
 }
@@ -1310,14 +1309,15 @@ static int filter_control_put(struct snd_kcontrol *kcontrol,
                struct snd_ctl_elem_value *ucontrol)
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct ab8500_codec_drvdata *drvdata = snd_soc_codec_get_drvdata(codec);
        struct filter_control *fc =
                        (struct filter_control *)kcontrol->private_value;
        unsigned int i;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&drvdata->ctrl_lock);
        for (i = 0; i < fc->count; i++)
                fc->value[i] = ucontrol->value.integer.value[i];
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&drvdata->ctrl_lock);
 
        return 0;
 }
@@ -2545,7 +2545,7 @@ static int ab8500_codec_probe(struct snd_soc_codec *codec)
 
        (void)snd_soc_dapm_disable_pin(&codec->dapm, "ANC Configure Input");
 
-       mutex_init(&drvdata->anc_lock);
+       mutex_init(&drvdata->ctrl_lock);
 
        return status;
 }
index bd9b183..c6e5a31 100644 (file)
@@ -37,10 +37,11 @@ static int ac97_prepare(struct snd_pcm_substream *substream,
                        struct snd_soc_dai *dai)
 {
        struct snd_soc_codec *codec = dai->codec;
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
 
        int reg = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
                  AC97_PCM_FRONT_DAC_RATE : AC97_PCM_LR_ADC_RATE;
-       return snd_ac97_set_rate(codec->ac97, reg, substream->runtime->rate);
+       return snd_ac97_set_rate(ac97, reg, substream->runtime->rate);
 }
 
 #define STD_AC97_RATES (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_11025 |\
@@ -53,7 +54,6 @@ static const struct snd_soc_dai_ops ac97_dai_ops = {
 
 static struct snd_soc_dai_driver ac97_dai = {
        .name = "ac97-hifi",
-       .ac97_control = 1,
        .playback = {
                .stream_name = "AC97 Playback",
                .channels_min = 1,
@@ -71,6 +71,7 @@ static struct snd_soc_dai_driver ac97_dai = {
 
 static int ac97_soc_probe(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97;
        struct snd_ac97_bus *ac97_bus;
        struct snd_ac97_template ac97_template;
        int ret;
@@ -82,24 +83,31 @@ static int ac97_soc_probe(struct snd_soc_codec *codec)
                return ret;
 
        memset(&ac97_template, 0, sizeof(struct snd_ac97_template));
-       ret = snd_ac97_mixer(ac97_bus, &ac97_template, &codec->ac97);
+       ret = snd_ac97_mixer(ac97_bus, &ac97_template, &ac97);
        if (ret < 0)
                return ret;
 
+       snd_soc_codec_set_drvdata(codec, ac97);
+
        return 0;
 }
 
 #ifdef CONFIG_PM
 static int ac97_soc_suspend(struct snd_soc_codec *codec)
 {
-       snd_ac97_suspend(codec->ac97);
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+       snd_ac97_suspend(ac97);
 
        return 0;
 }
 
 static int ac97_soc_resume(struct snd_soc_codec *codec)
 {
-       snd_ac97_resume(codec->ac97);
+
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+       snd_ac97_resume(ac97);
 
        return 0;
 }
index 6844d0b..387530b 100644 (file)
@@ -72,11 +72,13 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
 };
 
 static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
-       SND_SOC_DAPM_DAC("DAC", "Playback", AD193X_DAC_CTRL0, 0, 1),
+       SND_SOC_DAPM_DAC("DAC", "Playback", SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_PGA("DAC Output", AD193X_DAC_CTRL0, 0, 1, NULL, 0),
        SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
        SND_SOC_DAPM_SUPPLY("PLL_PWR", AD193X_PLL_CLK_CTRL0, 0, 1, NULL, 0),
        SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
        SND_SOC_DAPM_SUPPLY("SYSCLK", AD193X_PLL_CLK_CTRL0, 7, 0, NULL, 0),
+       SND_SOC_DAPM_VMID("VMID"),
        SND_SOC_DAPM_OUTPUT("DAC1OUT"),
        SND_SOC_DAPM_OUTPUT("DAC2OUT"),
        SND_SOC_DAPM_OUTPUT("DAC3OUT"),
@@ -87,13 +89,15 @@ static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
 
 static const struct snd_soc_dapm_route audio_paths[] = {
        { "DAC", NULL, "SYSCLK" },
+       { "DAC Output", NULL, "DAC" },
+       { "DAC Output", NULL, "VMID" },
        { "ADC", NULL, "SYSCLK" },
        { "DAC", NULL, "ADC_PWR" },
        { "ADC", NULL, "ADC_PWR" },
-       { "DAC1OUT", NULL, "DAC" },
-       { "DAC2OUT", NULL, "DAC" },
-       { "DAC3OUT", NULL, "DAC" },
-       { "DAC4OUT", NULL, "DAC" },
+       { "DAC1OUT", NULL, "DAC Output" },
+       { "DAC2OUT", NULL, "DAC Output" },
+       { "DAC3OUT", NULL, "DAC Output" },
+       { "DAC4OUT", NULL, "DAC Output" },
        { "ADC", NULL, "ADC1IN" },
        { "ADC", NULL, "ADC2IN" },
        { "SYSCLK", NULL, "PLL_PWR" },
index 304d300..2860eef 100644 (file)
 #include <linux/module.h>
 #include <linux/kernel.h>
 #include <linux/device.h>
+#include <linux/regmap.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
 #include <sound/initval.h>
 #include <sound/soc.h>
 
-#include "ad1980.h"
+static const struct reg_default ad1980_reg_defaults[] = {
+       { 0x02, 0x8000 },
+       { 0x04, 0x8000 },
+       { 0x06, 0x8000 },
+       { 0x0c, 0x8008 },
+       { 0x0e, 0x8008 },
+       { 0x10, 0x8808 },
+       { 0x12, 0x8808 },
+       { 0x16, 0x8808 },
+       { 0x18, 0x8808 },
+       { 0x1a, 0x0000 },
+       { 0x1c, 0x8000 },
+       { 0x20, 0x0000 },
+       { 0x28, 0x03c7 },
+       { 0x2c, 0xbb80 },
+       { 0x2e, 0xbb80 },
+       { 0x30, 0xbb80 },
+       { 0x32, 0xbb80 },
+       { 0x36, 0x8080 },
+       { 0x38, 0x8080 },
+       { 0x3a, 0x2000 },
+       { 0x60, 0x0000 },
+       { 0x62, 0x0000 },
+       { 0x72, 0x0000 },
+       { 0x74, 0x1001 },
+       { 0x76, 0x0000 },
+};
 
-/*
- * AD1980 register cache
- */
-static const u16 ad1980_reg[] = {
-       0x0090, 0x8000, 0x8000, 0x8000, /* 0 - 6  */
-       0x0000, 0x0000, 0x8008, 0x8008, /* 8 - e  */
-       0x8808, 0x8808, 0x0000, 0x8808, /* 10 - 16 */
-       0x8808, 0x0000, 0x8000, 0x0000, /* 18 - 1e */
-       0x0000, 0x0000, 0x0000, 0x0000, /* 20 - 26 */
-       0x03c7, 0x0000, 0xbb80, 0xbb80, /* 28 - 2e */
-       0xbb80, 0xbb80, 0x0000, 0x8080, /* 30 - 36 */
-       0x8080, 0x2000, 0x0000, 0x0000, /* 38 - 3e */
-       0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-       0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-       0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-       0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-       0x8080, 0x0000, 0x0000, 0x0000, /* 60 - 66 */
-       0x0000, 0x0000, 0x0000, 0x0000, /* reserved */
-       0x0000, 0x0000, 0x1001, 0x0000, /* 70 - 76 */
-       0x0000, 0x0000, 0x4144, 0x5370  /* 78 - 7e */
+static bool ad1980_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case AC97_RESET ... AC97_MASTER_MONO:
+       case AC97_PHONE ... AC97_CD:
+       case AC97_AUX ... AC97_GENERAL_PURPOSE:
+       case AC97_POWERDOWN ... AC97_PCM_LR_ADC_RATE:
+       case AC97_SPDIF:
+       case AC97_CODEC_CLASS_REV:
+       case AC97_PCI_SVID:
+       case AC97_AD_CODEC_CFG:
+       case AC97_AD_JACK_SPDIF:
+       case AC97_AD_SERIAL_CFG:
+       case AC97_VENDOR_ID1:
+       case AC97_VENDOR_ID2:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool ad1980_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case AC97_VENDOR_ID1:
+       case AC97_VENDOR_ID2:
+               return false;
+       default:
+               return ad1980_readable_reg(dev, reg);
+       }
+}
+
+static const struct regmap_config ad1980_regmap_config = {
+       .reg_bits = 16,
+       .reg_stride = 2,
+       .val_bits = 16,
+       .max_register = 0x7e,
+       .cache_type = REGCACHE_RBTREE,
+
+       .volatile_reg = regmap_ac97_default_volatile,
+       .readable_reg = ad1980_readable_reg,
+       .writeable_reg = ad1980_writeable_reg,
+
+       .reg_defaults = ad1980_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(ad1980_reg_defaults),
 };
 
 static const char *ad1980_rec_sel[] = {"Mic", "CD", "NC", "AUX", "Line",
@@ -134,45 +186,8 @@ static const struct snd_soc_dapm_route ad1980_dapm_routes[] = {
        { "HP_OUT_R", NULL, "Playback" },
 };
 
-static unsigned int ac97_read(struct snd_soc_codec *codec,
-       unsigned int reg)
-{
-       u16 *cache = codec->reg_cache;
-
-       switch (reg) {
-       case AC97_RESET:
-       case AC97_INT_PAGING:
-       case AC97_POWERDOWN:
-       case AC97_EXTENDED_STATUS:
-       case AC97_VENDOR_ID1:
-       case AC97_VENDOR_ID2:
-               return soc_ac97_ops->read(codec->ac97, reg);
-       default:
-               reg = reg >> 1;
-
-               if (reg >= ARRAY_SIZE(ad1980_reg))
-                       return -EINVAL;
-
-               return cache[reg];
-       }
-}
-
-static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
-       unsigned int val)
-{
-       u16 *cache = codec->reg_cache;
-
-       soc_ac97_ops->write(codec->ac97, reg, val);
-       reg = reg >> 1;
-       if (reg < ARRAY_SIZE(ad1980_reg))
-               cache[reg] = val;
-
-       return 0;
-}
-
 static struct snd_soc_dai_driver ad1980_dai = {
        .name = "ad1980-hifi",
-       .ac97_control = 1,
        .playback = {
                .stream_name = "Playback",
                .channels_min = 2,
@@ -189,108 +204,115 @@ static struct snd_soc_dai_driver ad1980_dai = {
 
 static int ad1980_reset(struct snd_soc_codec *codec, int try_warm)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        unsigned int retry_cnt = 0;
 
        do {
                if (try_warm && soc_ac97_ops->warm_reset) {
-                       soc_ac97_ops->warm_reset(codec->ac97);
-                       if (ac97_read(codec, AC97_RESET) == 0x0090)
+                       soc_ac97_ops->warm_reset(ac97);
+                       if (snd_soc_read(codec, AC97_RESET) == 0x0090)
                                return 1;
                }
 
-               soc_ac97_ops->reset(codec->ac97);
+               soc_ac97_ops->reset(ac97);
                /*
                 * Set bit 16slot in register 74h, then every slot will has only
                 * 16 bits. This command is sent out in 20bit mode, in which
                 * case the first nibble of data is eaten by the addr. (Tag is
                 * always 16 bit)
                 */
-               ac97_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
+               snd_soc_write(codec, AC97_AD_SERIAL_CFG, 0x9900);
 
-               if (ac97_read(codec, AC97_RESET)  == 0x0090)
+               if (snd_soc_read(codec, AC97_RESET)  == 0x0090)
                        return 0;
        } while (retry_cnt++ < 10);
 
-       printk(KERN_ERR "AD1980 AC97 reset failed\n");
+       dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+
        return -EIO;
 }
 
 static int ad1980_soc_probe(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97;
+       struct regmap *regmap;
        int ret;
        u16 vendor_id2;
        u16 ext_status;
 
-       printk(KERN_INFO "AD1980 SoC Audio Codec\n");
-
-       ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-       if (ret < 0) {
-               printk(KERN_ERR "ad1980: failed to register AC97 codec\n");
+       ac97 = snd_soc_new_ac97_codec(codec);
+       if (IS_ERR(ac97)) {
+               ret = PTR_ERR(ac97);
+               dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
                return ret;
        }
 
+       regmap = regmap_init_ac97(ac97, &ad1980_regmap_config);
+       if (IS_ERR(regmap)) {
+               ret = PTR_ERR(regmap);
+               goto err_free_ac97;
+       }
+
+       snd_soc_codec_init_regmap(codec, regmap);
+       snd_soc_codec_set_drvdata(codec, ac97);
+
        ret = ad1980_reset(codec, 0);
-       if (ret < 0) {
-               printk(KERN_ERR "Failed to reset AD1980: AC97 link error\n");
+       if (ret < 0)
                goto reset_err;
-       }
 
        /* Read out vendor ID to make sure it is ad1980 */
-       if (ac97_read(codec, AC97_VENDOR_ID1) != 0x4144) {
+       if (snd_soc_read(codec, AC97_VENDOR_ID1) != 0x4144) {
                ret = -ENODEV;
                goto reset_err;
        }
 
-       vendor_id2 = ac97_read(codec, AC97_VENDOR_ID2);
+       vendor_id2 = snd_soc_read(codec, AC97_VENDOR_ID2);
 
        if (vendor_id2 != 0x5370) {
                if (vendor_id2 != 0x5374) {
                        ret = -ENODEV;
                        goto reset_err;
                } else {
-                       printk(KERN_WARNING "ad1980: "
-                               "Found AD1981 - only 2/2 IN/OUT Channels "
-                               "supported\n");
+                       dev_warn(codec->dev,
+                               "Found AD1981 - only 2/2 IN/OUT Channels supported\n");
                }
        }
 
        /* unmute captures and playbacks volume */
-       ac97_write(codec, AC97_MASTER, 0x0000);
-       ac97_write(codec, AC97_PCM, 0x0000);
-       ac97_write(codec, AC97_REC_GAIN, 0x0000);
-       ac97_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
-       ac97_write(codec, AC97_SURROUND_MASTER, 0x0000);
+       snd_soc_write(codec, AC97_MASTER, 0x0000);
+       snd_soc_write(codec, AC97_PCM, 0x0000);
+       snd_soc_write(codec, AC97_REC_GAIN, 0x0000);
+       snd_soc_write(codec, AC97_CENTER_LFE_MASTER, 0x0000);
+       snd_soc_write(codec, AC97_SURROUND_MASTER, 0x0000);
 
        /*power on LFE/CENTER/Surround DACs*/
-       ext_status = ac97_read(codec, AC97_EXTENDED_STATUS);
-       ac97_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
-
-       snd_soc_add_codec_controls(codec, ad1980_snd_ac97_controls,
-                               ARRAY_SIZE(ad1980_snd_ac97_controls));
+       ext_status = snd_soc_read(codec, AC97_EXTENDED_STATUS);
+       snd_soc_write(codec, AC97_EXTENDED_STATUS, ext_status&~0x3800);
 
        return 0;
 
 reset_err:
-       snd_soc_free_ac97_codec(codec);
+       snd_soc_codec_exit_regmap(codec);
+err_free_ac97:
+       snd_soc_free_ac97_codec(ac97);
        return ret;
 }
 
 static int ad1980_soc_remove(struct snd_soc_codec *codec)
 {
-       snd_soc_free_ac97_codec(codec);
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+       snd_soc_codec_exit_regmap(codec);
+       snd_soc_free_ac97_codec(ac97);
        return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
        .probe =        ad1980_soc_probe,
        .remove =       ad1980_soc_remove,
-       .reg_cache_size = ARRAY_SIZE(ad1980_reg),
-       .reg_word_size = sizeof(u16),
-       .reg_cache_default = ad1980_reg,
-       .reg_cache_step = 2,
-       .write = ac97_write,
-       .read = ac97_read,
 
+       .controls = ad1980_snd_ac97_controls,
+       .num_controls = ARRAY_SIZE(ad1980_snd_ac97_controls),
        .dapm_widgets = ad1980_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
        .dapm_routes = ad1980_dapm_routes,
diff --git a/sound/soc/codecs/ad1980.h b/sound/soc/codecs/ad1980.h
deleted file mode 100644 (file)
index eb0af44..0000000
+++ /dev/null
@@ -1,26 +0,0 @@
-/*
- * ad1980.h  --  ad1980 Soc Audio driver
- *
- * WARNING:
- *
- * Because Analog Devices Inc. discontinued the ad1980 sound chip since
- * Sep. 2009, this ad1980 driver is not maintained, tested and supported
- * by ADI now.
- */
-
-#ifndef _AD1980_H
-#define _AD1980_H
-/* Bit definition of Power-Down Control/Status Register */
-#define ADC            0x0001
-#define DAC            0x0002
-#define ANL            0x0004
-#define REF            0x0008
-#define PR0            0x0100
-#define PR1            0x0200
-#define PR2            0x0400
-#define PR3            0x0800
-#define PR4            0x1000
-#define PR5            0x2000
-#define PR6            0x4000
-
-#endif
index 7c784ad..783dcb5 100644 (file)
@@ -551,7 +551,7 @@ static const struct snd_kcontrol_new adau1373_drc_controls[] = {
 static int adau1373_pll_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
        unsigned int pll_id = w->name[3] - '1';
        unsigned int val;
@@ -823,7 +823,7 @@ static const struct snd_soc_dapm_widget adau1373_dapm_widgets[] = {
 static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
        struct snd_soc_dapm_widget *sink)
 {
-       struct snd_soc_codec *codec = source->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
        struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
        unsigned int dai;
        const char *clk;
@@ -844,7 +844,7 @@ static int adau1373_check_aif_clk(struct snd_soc_dapm_widget *source,
 static int adau1373_check_src(struct snd_soc_dapm_widget *source,
        struct snd_soc_dapm_widget *sink)
 {
-       struct snd_soc_codec *codec = source->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
        struct adau1373 *adau1373 = snd_soc_codec_get_drvdata(codec);
        unsigned int dai;
 
index 370b742..d4e219b 100644 (file)
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 
+#include <asm/unaligned.h>
+
 #include "sigmadsp.h"
 #include "adau1701.h"
 
+#define ADAU1701_SAFELOAD_DATA(i) (0x0810 + (i))
+#define ADAU1701_SAFELOAD_ADDR(i) (0x0815 + (i))
+
 #define ADAU1701_DSPCTRL       0x081c
 #define ADAU1701_SEROCTL       0x081e
 #define ADAU1701_SERICTL       0x081f
@@ -42,6 +47,7 @@
 #define ADAU1701_DSPCTRL_CR            (1 << 2)
 #define ADAU1701_DSPCTRL_DAM           (1 << 3)
 #define ADAU1701_DSPCTRL_ADM           (1 << 4)
+#define ADAU1701_DSPCTRL_IST           (1 << 5)
 #define ADAU1701_DSPCTRL_SR_48         0x00
 #define ADAU1701_DSPCTRL_SR_96         0x01
 #define ADAU1701_DSPCTRL_SR_192                0x02
@@ -102,7 +108,10 @@ struct adau1701 {
        unsigned int pll_clkdiv;
        unsigned int sysclk;
        struct regmap *regmap;
+       struct i2c_client *client;
        u8 pin_config[12];
+
+       struct sigmadsp *sigmadsp;
 };
 
 static const struct snd_kcontrol_new adau1701_controls[] = {
@@ -159,6 +168,7 @@ static bool adau1701_volatile_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
        case ADAU1701_DACSET:
+       case ADAU1701_DSPCTRL:
                return true;
        default:
                return false;
@@ -238,12 +248,58 @@ static int adau1701_reg_read(void *context, unsigned int reg,
        return 0;
 }
 
-static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
+static int adau1701_safeload(struct sigmadsp *sigmadsp, unsigned int addr,
+       const uint8_t bytes[], size_t len)
+{
+       struct i2c_client *client = to_i2c_client(sigmadsp->dev);
+       struct adau1701 *adau1701 = i2c_get_clientdata(client);
+       unsigned int val;
+       unsigned int i;
+       uint8_t buf[10];
+       int ret;
+
+       ret = regmap_read(adau1701->regmap, ADAU1701_DSPCTRL, &val);
+       if (ret)
+               return ret;
+
+       if (val & ADAU1701_DSPCTRL_IST)
+               msleep(50);
+
+       for (i = 0; i < len / 4; i++) {
+               put_unaligned_le16(ADAU1701_SAFELOAD_DATA(i), buf);
+               buf[2] = 0x00;
+               memcpy(buf + 3, bytes + i * 4, 4);
+               ret = i2c_master_send(client, buf, 7);
+               if (ret < 0)
+                       return ret;
+               else if (ret != 7)
+                       return -EIO;
+
+               put_unaligned_le16(ADAU1701_SAFELOAD_ADDR(i), buf);
+               put_unaligned_le16(addr + i, buf + 2);
+               ret = i2c_master_send(client, buf, 4);
+               if (ret < 0)
+                       return ret;
+               else if (ret != 4)
+                       return -EIO;
+       }
+
+       return regmap_update_bits(adau1701->regmap, ADAU1701_DSPCTRL,
+               ADAU1701_DSPCTRL_IST, ADAU1701_DSPCTRL_IST);
+}
+
+static const struct sigmadsp_ops adau1701_sigmadsp_ops = {
+       .safeload = adau1701_safeload,
+};
+
+static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv,
+       unsigned int rate)
 {
        struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
-       struct i2c_client *client = to_i2c_client(codec->dev);
        int ret;
 
+       sigmadsp_reset(adau1701->sigmadsp);
+
        if (clkdiv != ADAU1707_CLKDIV_UNSET &&
            gpio_is_valid(adau1701->gpio_pll_mode[0]) &&
            gpio_is_valid(adau1701->gpio_pll_mode[1])) {
@@ -284,7 +340,7 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
         * know the correct PLL setup
         */
        if (clkdiv != ADAU1707_CLKDIV_UNSET) {
-               ret = process_sigma_firmware(client, ADAU1701_FIRMWARE);
+               ret = sigmadsp_setup(adau1701->sigmadsp, rate);
                if (ret) {
                        dev_warn(codec->dev, "Failed to load firmware\n");
                        return ret;
@@ -385,7 +441,7 @@ static int adau1701_hw_params(struct snd_pcm_substream *substream,
         * firmware upload.
         */
        if (clkdiv != adau1701->pll_clkdiv) {
-               ret = adau1701_reset(codec, clkdiv);
+               ret = adau1701_reset(codec, clkdiv, params_rate(params));
                if (ret < 0)
                        return ret;
        }
@@ -554,6 +610,14 @@ static int adau1701_set_sysclk(struct snd_soc_codec *codec, int clk_id,
        return 0;
 }
 
+static int adau1701_startup(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai)
+{
+       struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(dai->codec);
+
+       return sigmadsp_restrict_params(adau1701->sigmadsp, substream);
+}
+
 #define ADAU1701_RATES (SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000 | \
        SNDRV_PCM_RATE_192000)
 
@@ -564,6 +628,7 @@ static const struct snd_soc_dai_ops adau1701_dai_ops = {
        .set_fmt        = adau1701_set_dai_fmt,
        .hw_params      = adau1701_hw_params,
        .digital_mute   = adau1701_digital_mute,
+       .startup        = adau1701_startup,
 };
 
 static struct snd_soc_dai_driver adau1701_dai = {
@@ -600,6 +665,10 @@ static int adau1701_probe(struct snd_soc_codec *codec)
        unsigned int val;
        struct adau1701 *adau1701 = snd_soc_codec_get_drvdata(codec);
 
+       ret = sigmadsp_attach(adau1701->sigmadsp, &codec->component);
+       if (ret)
+               return ret;
+
        /*
         * Let the pll_clkdiv variable default to something that won't happen
         * at runtime. That way, we can postpone the firmware download from
@@ -609,7 +678,7 @@ static int adau1701_probe(struct snd_soc_codec *codec)
        adau1701->pll_clkdiv = ADAU1707_CLKDIV_UNSET;
 
        /* initalize with pre-configured pll mode settings */
-       ret = adau1701_reset(codec, adau1701->pll_clkdiv);
+       ret = adau1701_reset(codec, adau1701->pll_clkdiv, 0);
        if (ret < 0)
                return ret;
 
@@ -667,6 +736,7 @@ static int adau1701_i2c_probe(struct i2c_client *client,
        if (!adau1701)
                return -ENOMEM;
 
+       adau1701->client = client;
        adau1701->regmap = devm_regmap_init(dev, NULL, client,
                                            &adau1701_regmap);
        if (IS_ERR(adau1701->regmap))
@@ -722,6 +792,12 @@ static int adau1701_i2c_probe(struct i2c_client *client,
        adau1701->gpio_pll_mode[1] = gpio_pll_mode[1];
 
        i2c_set_clientdata(client, adau1701);
+
+       adau1701->sigmadsp = devm_sigmadsp_init_i2c(client,
+               &adau1701_sigmadsp_ops, ADAU1701_FIRMWARE);
+       if (IS_ERR(adau1701->sigmadsp))
+               return PTR_ERR(adau1701->sigmadsp);
+
        ret = snd_soc_register_codec(&client->dev, &adau1701_codec_drv,
                        &adau1701_dai, 1);
        return ret;
index 91f6028..a1baeee 100644 (file)
@@ -255,7 +255,8 @@ static const struct snd_kcontrol_new adau1761_input_mux_control =
 static int adau1761_dejitter_fixup(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
 
        /* After any power changes have been made the dejitter circuit
         * has to be reinitialized. */
@@ -702,11 +703,6 @@ static int adau1761_codec_probe(struct snd_soc_codec *codec)
                        ARRAY_SIZE(adau1761_dapm_routes));
                if (ret)
                        return ret;
-
-               ret = adau17x1_load_firmware(adau, codec->dev,
-                       ADAU1761_FIRMWARE);
-               if (ret)
-                       dev_warn(codec->dev, "Failed to firmware\n");
        }
 
        ret = adau17x1_add_routes(codec);
@@ -775,16 +771,20 @@ int adau1761_probe(struct device *dev, struct regmap *regmap,
        enum adau17x1_type type, void (*switch_mode)(struct device *dev))
 {
        struct snd_soc_dai_driver *dai_drv;
+       const char *firmware_name;
        int ret;
 
-       ret = adau17x1_probe(dev, regmap, type, switch_mode);
-       if (ret)
-               return ret;
-
-       if (type == ADAU1361)
+       if (type == ADAU1361) {
                dai_drv = &adau1361_dai_driver;
-       else
+               firmware_name = NULL;
+       } else {
                dai_drv = &adau1761_dai_driver;
+               firmware_name = ADAU1761_FIRMWARE;
+       }
+
+       ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name);
+       if (ret)
+               return ret;
 
        return snd_soc_register_codec(dev, &adau1761_codec_driver, dai_drv, 1);
 }
@@ -798,6 +798,7 @@ const struct regmap_config adau1761_regmap_config = {
        .num_reg_defaults = ARRAY_SIZE(adau1761_reg_defaults),
        .readable_reg = adau1761_readable_register,
        .volatile_reg = adau17x1_volatile_register,
+       .precious_reg = adau17x1_precious_register,
        .cache_type = REGCACHE_RBTREE,
 };
 EXPORT_SYMBOL_GPL(adau1761_regmap_config);
index e9fc00f..35581f4 100644 (file)
@@ -174,7 +174,7 @@ static const struct snd_kcontrol_new adau1781_mono_mixer_controls[] = {
 static int adau1781_dejitter_fixup(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct adau *adau = snd_soc_codec_get_drvdata(codec);
 
        /* After any power changes have been made the dejitter circuit
@@ -385,7 +385,6 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec)
 {
        struct adau1781_platform_data *pdata = dev_get_platdata(codec->dev);
        struct adau *adau = snd_soc_codec_get_drvdata(codec);
-       const char *firmware;
        int ret;
 
        ret = adau17x1_add_widgets(codec);
@@ -422,25 +421,10 @@ static int adau1781_codec_probe(struct snd_soc_codec *codec)
                        return ret;
        }
 
-       switch (adau->type) {
-       case ADAU1381:
-               firmware = ADAU1381_FIRMWARE;
-               break;
-       case ADAU1781:
-               firmware = ADAU1781_FIRMWARE;
-               break;
-       default:
-               return -EINVAL;
-       }
-
        ret = adau17x1_add_routes(codec);
        if (ret < 0)
                return ret;
 
-       ret = adau17x1_load_firmware(adau, codec->dev, firmware);
-       if (ret)
-               dev_warn(codec->dev, "Failed to load firmware\n");
-
        return 0;
 }
 
@@ -488,6 +472,7 @@ const struct regmap_config adau1781_regmap_config = {
        .num_reg_defaults       = ARRAY_SIZE(adau1781_reg_defaults),
        .readable_reg           = adau1781_readable_register,
        .volatile_reg           = adau17x1_volatile_register,
+       .precious_reg           = adau17x1_precious_register,
        .cache_type             = REGCACHE_RBTREE,
 };
 EXPORT_SYMBOL_GPL(adau1781_regmap_config);
@@ -495,9 +480,21 @@ EXPORT_SYMBOL_GPL(adau1781_regmap_config);
 int adau1781_probe(struct device *dev, struct regmap *regmap,
        enum adau17x1_type type, void (*switch_mode)(struct device *dev))
 {
+       const char *firmware_name;
        int ret;
 
-       ret = adau17x1_probe(dev, regmap, type, switch_mode);
+       switch (type) {
+       case ADAU1381:
+               firmware_name = ADAU1381_FIRMWARE;
+               break;
+       case ADAU1781:
+               firmware_name = ADAU1781_FIRMWARE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = adau17x1_probe(dev, regmap, type, switch_mode, firmware_name);
        if (ret)
                return ret;
 
index 3e16c1c..fa2e690 100644 (file)
@@ -61,7 +61,8 @@ static const struct snd_kcontrol_new adau17x1_controls[] = {
 static int adau17x1_pll_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct adau *adau = snd_soc_codec_get_drvdata(w->codec);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct adau *adau = snd_soc_codec_get_drvdata(codec);
        int ret;
 
        if (SND_SOC_DAPM_EVENT_ON(event)) {
@@ -307,6 +308,7 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
        struct adau *adau = snd_soc_codec_get_drvdata(codec);
        unsigned int val, div, dsp_div;
        unsigned int freq;
+       int ret;
 
        if (adau->clk_src == ADAU17X1_CLK_SRC_PLL)
                freq = adau->pll_freq;
@@ -356,6 +358,12 @@ static int adau17x1_hw_params(struct snd_pcm_substream *substream,
                regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, dsp_div);
        }
 
+       if (adau->sigmadsp) {
+               ret = adau17x1_setup_firmware(adau, params_rate(params));
+               if (ret < 0)
+                       return ret;
+       }
+
        if (adau->dai_fmt != SND_SOC_DAIFMT_RIGHT_J)
                return 0;
 
@@ -661,12 +669,24 @@ static int adau17x1_set_dai_tdm_slot(struct snd_soc_dai *dai,
        return 0;
 }
 
+static int adau17x1_startup(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai)
+{
+       struct adau *adau = snd_soc_codec_get_drvdata(dai->codec);
+
+       if (adau->sigmadsp)
+               return sigmadsp_restrict_params(adau->sigmadsp, substream);
+
+       return 0;
+}
+
 const struct snd_soc_dai_ops adau17x1_dai_ops = {
        .hw_params      = adau17x1_hw_params,
        .set_sysclk     = adau17x1_set_dai_sysclk,
        .set_fmt        = adau17x1_set_dai_fmt,
        .set_pll        = adau17x1_set_dai_pll,
        .set_tdm_slot   = adau17x1_set_dai_tdm_slot,
+       .startup        = adau17x1_startup,
 };
 EXPORT_SYMBOL_GPL(adau17x1_dai_ops);
 
@@ -687,8 +707,22 @@ int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
 }
 EXPORT_SYMBOL_GPL(adau17x1_set_micbias_voltage);
 
+bool adau17x1_precious_register(struct device *dev, unsigned int reg)
+{
+       /* SigmaDSP parameter memory */
+       if (reg < 0x400)
+               return true;
+
+       return false;
+}
+EXPORT_SYMBOL_GPL(adau17x1_precious_register);
+
 bool adau17x1_readable_register(struct device *dev, unsigned int reg)
 {
+       /* SigmaDSP parameter memory */
+       if (reg < 0x400)
+               return true;
+
        switch (reg) {
        case ADAU17X1_CLOCK_CONTROL:
        case ADAU17X1_PLL_CONTROL:
@@ -745,8 +779,7 @@ bool adau17x1_volatile_register(struct device *dev, unsigned int reg)
 }
 EXPORT_SYMBOL_GPL(adau17x1_volatile_register);
 
-int adau17x1_load_firmware(struct adau *adau, struct device *dev,
-       const char *firmware)
+int adau17x1_setup_firmware(struct adau *adau, unsigned int rate)
 {
        int ret;
        int dspsr;
@@ -758,7 +791,7 @@ int adau17x1_load_firmware(struct adau *adau, struct device *dev,
        regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 1);
        regmap_write(adau->regmap, ADAU17X1_DSP_SAMPLING_RATE, 0xf);
 
-       ret = process_sigma_firmware_regmap(dev, adau->regmap, firmware);
+       ret = sigmadsp_setup(adau->sigmadsp, rate);
        if (ret) {
                regmap_write(adau->regmap, ADAU17X1_DSP_ENABLE, 0);
                return ret;
@@ -767,7 +800,7 @@ int adau17x1_load_firmware(struct adau *adau, struct device *dev,
 
        return 0;
 }
-EXPORT_SYMBOL_GPL(adau17x1_load_firmware);
+EXPORT_SYMBOL_GPL(adau17x1_setup_firmware);
 
 int adau17x1_add_widgets(struct snd_soc_codec *codec)
 {
@@ -787,8 +820,21 @@ int adau17x1_add_widgets(struct snd_soc_codec *codec)
                ret = snd_soc_dapm_new_controls(&codec->dapm,
                        adau17x1_dsp_dapm_widgets,
                        ARRAY_SIZE(adau17x1_dsp_dapm_widgets));
+               if (ret)
+                       return ret;
+
+               if (!adau->sigmadsp)
+                       return 0;
+
+               ret = sigmadsp_attach(adau->sigmadsp, &codec->component);
+               if (ret) {
+                       dev_err(codec->dev, "Failed to attach firmware: %d\n",
+                               ret);
+                       return ret;
+               }
        }
-       return ret;
+
+       return 0;
 }
 EXPORT_SYMBOL_GPL(adau17x1_add_widgets);
 
@@ -829,7 +875,8 @@ int adau17x1_resume(struct snd_soc_codec *codec)
 EXPORT_SYMBOL_GPL(adau17x1_resume);
 
 int adau17x1_probe(struct device *dev, struct regmap *regmap,
-       enum adau17x1_type type, void (*switch_mode)(struct device *dev))
+       enum adau17x1_type type, void (*switch_mode)(struct device *dev),
+       const char *firmware_name)
 {
        struct adau *adau;
 
@@ -846,6 +893,16 @@ int adau17x1_probe(struct device *dev, struct regmap *regmap,
 
        dev_set_drvdata(dev, adau);
 
+       if (firmware_name) {
+               adau->sigmadsp = devm_sigmadsp_init_regmap(dev, regmap, NULL,
+                       firmware_name);
+               if (IS_ERR(adau->sigmadsp)) {
+                       dev_warn(dev, "Could not find firmware file: %ld\n",
+                               PTR_ERR(adau->sigmadsp));
+                       adau->sigmadsp = NULL;
+               }
+       }
+
        if (switch_mode)
                switch_mode(dev);
 
index e4a557f..e13583e 100644 (file)
@@ -4,6 +4,8 @@
 #include <linux/regmap.h>
 #include <linux/platform_data/adau17x1.h>
 
+#include "sigmadsp.h"
+
 enum adau17x1_type {
        ADAU1361,
        ADAU1761,
@@ -42,22 +44,24 @@ struct adau {
        bool dsp_bypass[2];
 
        struct regmap *regmap;
+       struct sigmadsp *sigmadsp;
 };
 
 int adau17x1_add_widgets(struct snd_soc_codec *codec);
 int adau17x1_add_routes(struct snd_soc_codec *codec);
 int adau17x1_probe(struct device *dev, struct regmap *regmap,
-       enum adau17x1_type type, void (*switch_mode)(struct device *dev));
+       enum adau17x1_type type, void (*switch_mode)(struct device *dev),
+       const char *firmware_name);
 int adau17x1_set_micbias_voltage(struct snd_soc_codec *codec,
        enum adau17x1_micbias_voltage micbias);
 bool adau17x1_readable_register(struct device *dev, unsigned int reg);
 bool adau17x1_volatile_register(struct device *dev, unsigned int reg);
+bool adau17x1_precious_register(struct device *dev, unsigned int reg);
 int adau17x1_resume(struct snd_soc_codec *codec);
 
 extern const struct snd_soc_dai_ops adau17x1_dai_ops;
 
-int adau17x1_load_firmware(struct adau *adau, struct device *dev,
-       const char *firmware);
+int adau17x1_setup_firmware(struct adau *adau, unsigned int rate);
 bool adau17x1_has_dsp(struct adau *adau);
 
 #define ADAU17X1_CLOCK_CONTROL                 0x4000
index ce3cdca..b67480f 100644 (file)
@@ -212,7 +212,7 @@ static const struct snd_soc_dapm_widget adav80x_dapm_widgets[] = {
 static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
                         struct snd_soc_dapm_widget *sink)
 {
-       struct snd_soc_codec *codec = source->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
        struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
        const char *clk;
 
@@ -236,7 +236,7 @@ static int adav80x_dapm_sysclk_check(struct snd_soc_dapm_widget *source,
 static int adav80x_dapm_pll_check(struct snd_soc_dapm_widget *source,
                         struct snd_soc_dapm_widget *sink)
 {
-       struct snd_soc_codec *codec = source->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(source->dapm);
        struct adav80x *adav80x = snd_soc_codec_get_drvdata(codec);
 
        return adav80x->pll_src == ADAV80X_PLL_SRC_XTAL;
index 30e2978..9130d91 100644 (file)
@@ -373,33 +373,9 @@ static struct snd_soc_dai_driver ak4535_dai = {
        .ops = &ak4535_dai_ops,
 };
 
-static int ak4535_suspend(struct snd_soc_codec *codec)
-{
-       ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static int ak4535_resume(struct snd_soc_codec *codec)
 {
        snd_soc_cache_sync(codec);
-       ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
-static int ak4535_probe(struct snd_soc_codec *codec)
-{
-       /* power on device */
-       ak4535_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       snd_soc_add_codec_controls(codec, ak4535_snd_controls,
-                               ARRAY_SIZE(ak4535_snd_controls));
-       return 0;
-}
-
-/* power down chip */
-static int ak4535_remove(struct snd_soc_codec *codec)
-{
-       ak4535_set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
 }
 
@@ -416,11 +392,12 @@ static const struct regmap_config ak4535_regmap = {
 };
 
 static struct snd_soc_codec_driver soc_codec_dev_ak4535 = {
-       .probe =        ak4535_probe,
-       .remove =       ak4535_remove,
-       .suspend =      ak4535_suspend,
        .resume =       ak4535_resume,
        .set_bias_level = ak4535_set_bias_level,
+       .suspend_bias_off = true,
+
+       .controls = ak4535_snd_controls,
+       .num_controls = ARRAY_SIZE(ak4535_snd_controls),
        .dapm_widgets = ak4535_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(ak4535_dapm_widgets),
        .dapm_routes = ak4535_audio_map,
index 7afe8f4..70861c7 100644 (file)
@@ -505,39 +505,7 @@ static struct snd_soc_dai_driver ak4641_dai[] = {
 },
 };
 
-static int ak4641_suspend(struct snd_soc_codec *codec)
-{
-       ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int ak4641_resume(struct snd_soc_codec *codec)
-{
-       ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
-static int ak4641_probe(struct snd_soc_codec *codec)
-{
-       /* power on device */
-       ak4641_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static int ak4641_remove(struct snd_soc_codec *codec)
-{
-       ak4641_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-
 static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
-       .probe                  = ak4641_probe,
-       .remove                 = ak4641_remove,
-       .suspend                = ak4641_suspend,
-       .resume                 = ak4641_resume,
        .controls               = ak4641_snd_controls,
        .num_controls           = ARRAY_SIZE(ak4641_snd_controls),
        .dapm_widgets           = ak4641_dapm_widgets,
@@ -545,6 +513,7 @@ static struct snd_soc_codec_driver soc_codec_dev_ak4641 = {
        .dapm_routes            = ak4641_audio_map,
        .num_dapm_routes        = ARRAY_SIZE(ak4641_audio_map),
        .set_bias_level         = ak4641_set_bias_level,
+       .suspend_bias_off       = true,
 };
 
 static const struct regmap_config ak4641_regmap = {
index 0417125..dde8b49 100644 (file)
@@ -491,23 +491,7 @@ static int ak4642_resume(struct snd_soc_codec *codec)
        return 0;
 }
 
-
-static int ak4642_probe(struct snd_soc_codec *codec)
-{
-       ak4642_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static int ak4642_remove(struct snd_soc_codec *codec)
-{
-       ak4642_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
-       .probe                  = ak4642_probe,
-       .remove                 = ak4642_remove,
        .resume                 = ak4642_resume,
        .set_bias_level         = ak4642_set_bias_level,
        .controls               = ak4642_snd_controls,
index 998fa0c..686cacb 100644 (file)
@@ -611,20 +611,7 @@ static struct snd_soc_dai_driver ak4671_dai = {
        .ops = &ak4671_dai_ops,
 };
 
-static int ak4671_probe(struct snd_soc_codec *codec)
-{
-       return ak4671_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-}
-
-static int ak4671_remove(struct snd_soc_codec *codec)
-{
-       ak4671_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_ak4671 = {
-       .probe = ak4671_probe,
-       .remove = ak4671_remove,
        .set_bias_level = ak4671_set_bias_level,
        .controls = ak4671_snd_controls,
        .num_controls = ARRAY_SIZE(ak4671_snd_controls),
index 9d0755a..bdf8c5a 100644 (file)
@@ -866,7 +866,6 @@ static int alc5623_suspend(struct snd_soc_codec *codec)
 {
        struct alc5623_priv *alc5623 = snd_soc_codec_get_drvdata(codec);
 
-       alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF);
        regcache_cache_only(alc5623->regmap, true);
 
        return 0;
@@ -887,15 +886,6 @@ static int alc5623_resume(struct snd_soc_codec *codec)
                return ret;
        }
 
-       alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       /* charge alc5623 caps */
-       if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) {
-               alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-               codec->dapm.bias_level = SND_SOC_BIAS_ON;
-               alc5623_set_bias_level(codec, codec->dapm.bias_level);
-       }
-
        return 0;
 }
 
@@ -906,9 +896,6 @@ static int alc5623_probe(struct snd_soc_codec *codec)
 
        alc5623_reset(codec);
 
-       /* power on device */
-       alc5623_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        if (alc5623->add_ctrl) {
                snd_soc_write(codec, ALC5623_ADD_CTRL_REG,
                                alc5623->add_ctrl);
@@ -964,19 +951,12 @@ static int alc5623_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-/* power down chip */
-static int alc5623_remove(struct snd_soc_codec *codec)
-{
-       alc5623_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_device_alc5623 = {
        .probe = alc5623_probe,
-       .remove = alc5623_remove,
        .suspend = alc5623_suspend,
        .resume = alc5623_resume,
        .set_bias_level = alc5623_set_bias_level,
+       .suspend_bias_off = true,
 };
 
 static const struct regmap_config alc5623_regmap = {
index 85942ca..d1fdbc2 100644 (file)
@@ -1038,23 +1038,15 @@ static struct snd_soc_dai_driver alc5632_dai = {
 };
 
 #ifdef CONFIG_PM
-static int alc5632_suspend(struct snd_soc_codec *codec)
-{
-       alc5632_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static int alc5632_resume(struct snd_soc_codec *codec)
 {
        struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
 
        regcache_sync(alc5632->regmap);
 
-       alc5632_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        return 0;
 }
 #else
-#define        alc5632_suspend NULL
 #define        alc5632_resume  NULL
 #endif
 
@@ -1062,9 +1054,6 @@ static int alc5632_probe(struct snd_soc_codec *codec)
 {
        struct alc5632_priv *alc5632 = snd_soc_codec_get_drvdata(codec);
 
-       /* power on device  */
-       alc5632_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        switch (alc5632->id) {
        case 0x5c:
                snd_soc_add_codec_controls(codec, alc5632_vol_snd_controls,
@@ -1077,19 +1066,12 @@ static int alc5632_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-/* power down chip */
-static int alc5632_remove(struct snd_soc_codec *codec)
-{
-       alc5632_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_device_alc5632 = {
        .probe = alc5632_probe,
-       .remove = alc5632_remove,
-       .suspend = alc5632_suspend,
        .resume = alc5632_resume,
        .set_bias_level = alc5632_set_bias_level,
+       .suspend_bias_off = true,
+
        .controls = alc5632_snd_controls,
        .num_controls = ARRAY_SIZE(alc5632_snd_controls),
        .dapm_widgets = alc5632_dapm_widgets,
index 0c05e7a..9550d74 100644 (file)
 #define ARIZONA_FLL_MIN_OUTDIV 2
 #define ARIZONA_FLL_MAX_OUTDIV 7
 
+#define ARIZONA_FMT_DSP_MODE_A          0
+#define ARIZONA_FMT_DSP_MODE_B          1
+#define ARIZONA_FMT_I2S_MODE            2
+#define ARIZONA_FMT_LEFT_JUSTIFIED_MODE 3
+
 #define arizona_fll_err(_fll, fmt, ...) \
        dev_err(_fll->arizona->dev, "FLL%d: " fmt, _fll->id, ##__VA_ARGS__)
 #define arizona_fll_warn(_fll, fmt, ...) \
@@ -648,7 +653,7 @@ SOC_ENUM_SINGLE_DECL(arizona_in_hpf_cut_enum,
 EXPORT_SYMBOL_GPL(arizona_in_hpf_cut_enum);
 
 static const char * const arizona_in_dmic_osr_text[] = {
-       "1.536MHz", "3.072MHz", "6.144MHz",
+       "1.536MHz", "3.072MHz", "6.144MHz", "768kHz",
 };
 
 const struct soc_enum arizona_in_dmic_osr[] = {
@@ -946,10 +951,26 @@ static int arizona_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
 
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_DSP_A:
-               mode = 0;
+               mode = ARIZONA_FMT_DSP_MODE_A;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
+                               != SND_SOC_DAIFMT_CBM_CFM) {
+                       arizona_aif_err(dai, "DSP_B not valid in slave mode\n");
+                       return -EINVAL;
+               }
+               mode = ARIZONA_FMT_DSP_MODE_B;
                break;
        case SND_SOC_DAIFMT_I2S:
-               mode = 2;
+               mode = ARIZONA_FMT_I2S_MODE;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               if ((fmt & SND_SOC_DAIFMT_MASTER_MASK)
+                               != SND_SOC_DAIFMT_CBM_CFM) {
+                       arizona_aif_err(dai, "LEFT_J not valid in slave mode\n");
+                       return -EINVAL;
+               }
+               mode = ARIZONA_FMT_LEFT_JUSTIFIED_MODE;
                break;
        default:
                arizona_aif_err(dai, "Unsupported DAI format %d\n",
@@ -1164,13 +1185,13 @@ static void arizona_wm5102_set_dac_comp(struct snd_soc_codec *codec,
                { 0x80, 0x0 },
        };
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&arizona->dac_comp_lock);
 
        dac_comp[1].def = arizona->dac_comp_coeff;
        if (rate >= 176400)
                dac_comp[2].def = arizona->dac_comp_enabled;
 
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&arizona->dac_comp_lock);
 
        regmap_multi_reg_write(arizona->regmap,
                               dac_comp,
@@ -1298,7 +1319,8 @@ static int arizona_hw_params(struct snd_pcm_substream *substream,
 
        /* Force multiple of 2 channels for I2S mode */
        val = snd_soc_read(codec, base + ARIZONA_AIF_FORMAT);
-       if ((channels & 1) && (val & ARIZONA_AIF1_FMT_MASK)) {
+       val &= ARIZONA_AIF1_FMT_MASK;
+       if ((channels & 1) && (val == ARIZONA_FMT_I2S_MODE)) {
                arizona_aif_dbg(dai, "Forcing stereo mode\n");
                bclk_target /= channels;
                bclk_target *= channels + 1;
index 537327c..8d638e8 100644 (file)
@@ -62,14 +62,10 @@ static int cq93vc_mute(struct snd_soc_dai *dai, int mute)
 static int cq93vc_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 davinci_vc *davinci_vc = codec->dev->platform_data;
-
        switch (freq) {
        case 22579200:
        case 27000000:
        case 33868800:
-               davinci_vc->cq93vc.sysclk = freq;
                return 0;
        }
 
@@ -126,32 +122,6 @@ static struct snd_soc_dai_driver cq93vc_dai = {
        .ops = &cq93vc_dai_ops,
 };
 
-static int cq93vc_resume(struct snd_soc_codec *codec)
-{
-       cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static int cq93vc_probe(struct snd_soc_codec *codec)
-{
-       struct davinci_vc *davinci_vc = codec->dev->platform_data;
-
-       davinci_vc->cq93vc.codec = codec;
-
-       /* Off, with power on */
-       cq93vc_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static int cq93vc_remove(struct snd_soc_codec *codec)
-{
-       cq93vc_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
 static struct regmap *cq93vc_get_regmap(struct device *dev)
 {
        struct davinci_vc *davinci_vc = dev->platform_data;
@@ -161,9 +131,6 @@ static struct regmap *cq93vc_get_regmap(struct device *dev)
 
 static struct snd_soc_codec_driver soc_codec_dev_cq93vc = {
        .set_bias_level = cq93vc_set_bias_level,
-       .probe = cq93vc_probe,
-       .remove = cq93vc_remove,
-       .resume = cq93vc_resume,
        .get_regmap = cq93vc_get_regmap,
        .controls = cq93vc_snd_controls,
        .num_controls = ARRAY_SIZE(cq93vc_snd_controls),
index 4fdd47d..ce60868 100644 (file)
@@ -32,7 +32,6 @@
 #include "cs4265.h"
 
 struct cs4265_private {
-       struct device *dev;
        struct regmap *regmap;
        struct gpio_desc *reset_gpio;
        u8 format;
@@ -598,7 +597,6 @@ static int cs4265_i2c_probe(struct i2c_client *i2c_client,
                               GFP_KERNEL);
        if (cs4265 == NULL)
                return -ENOMEM;
-       cs4265->dev = &i2c_client->dev;
 
        cs4265->regmap = devm_regmap_init_i2c(i2c_client, &cs4265_regmap);
        if (IS_ERR(cs4265->regmap)) {
diff --git a/sound/soc/codecs/cs4271-i2c.c b/sound/soc/codecs/cs4271-i2c.c
new file mode 100644 (file)
index 0000000..b264da0
--- /dev/null
@@ -0,0 +1,62 @@
+/*
+ * CS4271 I2C audio driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * 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/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "cs4271.h"
+
+static int cs4271_i2c_probe(struct i2c_client *client,
+                            const struct i2c_device_id *id)
+{
+       struct regmap_config config;
+
+       config = cs4271_regmap_config;
+       config.reg_bits = 8;
+       config.val_bits = 8;
+
+       return cs4271_probe(&client->dev,
+                           devm_regmap_init_i2c(client, &config));
+}
+
+static int cs4271_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static const struct i2c_device_id cs4271_i2c_id[] = {
+       { "cs4271", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
+
+static struct i2c_driver cs4271_i2c_driver = {
+       .driver = {
+               .name = "cs4271",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(cs4271_dt_ids),
+       },
+       .probe = cs4271_i2c_probe,
+       .remove = cs4271_i2c_remove,
+       .id_table = cs4271_i2c_id,
+};
+module_i2c_driver(cs4271_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC CS4271 I2C Driver");
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/cs4271-spi.c b/sound/soc/codecs/cs4271-spi.c
new file mode 100644 (file)
index 0000000..acd49d8
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * CS4271 SPI audio driver
+ *
+ * Copyright (c) 2010 Alexander Sverdlin <subaparts@yandex.ru>
+ *
+ * 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/spi/spi.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include "cs4271.h"
+
+static int cs4271_spi_probe(struct spi_device *spi)
+{
+       struct regmap_config config;
+
+       config = cs4271_regmap_config;
+       config.reg_bits = 16;
+       config.val_bits = 8;
+       config.read_flag_mask = 0x21;
+       config.write_flag_mask = 0x20;
+
+       return cs4271_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+}
+
+static int cs4271_spi_remove(struct spi_device *spi)
+{
+       snd_soc_unregister_codec(&spi->dev);
+       return 0;
+}
+
+static struct spi_driver cs4271_spi_driver = {
+       .driver = {
+               .name   = "cs4271",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(cs4271_dt_ids),
+       },
+       .probe          = cs4271_spi_probe,
+       .remove         = cs4271_spi_remove,
+};
+module_spi_driver(cs4271_spi_driver);
+
+MODULE_DESCRIPTION("ASoC CS4271 SPI Driver");
+MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
+MODULE_LICENSE("GPL");
index 93cec52..79a4efc 100644 (file)
@@ -23,8 +23,6 @@
 #include <linux/slab.h>
 #include <linux/delay.h>
 #include <linux/gpio.h>
-#include <linux/i2c.h>
-#include <linux/spi/spi.h>
 #include <linux/of.h>
 #include <linux/of_device.h>
 #include <linux/of_gpio.h>
@@ -32,6 +30,7 @@
 #include <sound/soc.h>
 #include <sound/tlv.h>
 #include <sound/cs4271.h>
+#include "cs4271.h"
 
 #define CS4271_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
                            SNDRV_PCM_FMTBIT_S24_LE | \
@@ -527,14 +526,15 @@ static int cs4271_soc_resume(struct snd_soc_codec *codec)
 #endif /* CONFIG_PM */
 
 #ifdef CONFIG_OF
-static const struct of_device_id cs4271_dt_ids[] = {
+const struct of_device_id cs4271_dt_ids[] = {
        { .compatible = "cirrus,cs4271", },
        { }
 };
 MODULE_DEVICE_TABLE(of, cs4271_dt_ids);
+EXPORT_SYMBOL_GPL(cs4271_dt_ids);
 #endif
 
-static int cs4271_probe(struct snd_soc_codec *codec)
+static int cs4271_codec_probe(struct snd_soc_codec *codec)
 {
        struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
        struct cs4271_platform_data *cs4271plat = codec->dev->platform_data;
@@ -587,7 +587,7 @@ static int cs4271_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int cs4271_remove(struct snd_soc_codec *codec)
+static int cs4271_codec_remove(struct snd_soc_codec *codec)
 {
        struct cs4271_private *cs4271 = snd_soc_codec_get_drvdata(codec);
 
@@ -599,8 +599,8 @@ static int cs4271_remove(struct snd_soc_codec *codec)
 };
 
 static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
-       .probe                  = cs4271_probe,
-       .remove                 = cs4271_remove,
+       .probe                  = cs4271_codec_probe,
+       .remove                 = cs4271_codec_remove,
        .suspend                = cs4271_soc_suspend,
        .resume                 = cs4271_soc_resume,
 
@@ -642,14 +642,8 @@ static int cs4271_common_probe(struct device *dev,
        return 0;
 }
 
-#if defined(CONFIG_SPI_MASTER)
-
-static const struct regmap_config cs4271_spi_regmap = {
-       .reg_bits = 16,
-       .val_bits = 8,
+const struct regmap_config cs4271_regmap_config = {
        .max_register = CS4271_LASTREG,
-       .read_flag_mask = 0x21,
-       .write_flag_mask = 0x20,
 
        .reg_defaults = cs4271_reg_defaults,
        .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
@@ -657,140 +651,27 @@ static const struct regmap_config cs4271_spi_regmap = {
 
        .volatile_reg = cs4271_volatile_reg,
 };
+EXPORT_SYMBOL_GPL(cs4271_regmap_config);
 
-static int cs4271_spi_probe(struct spi_device *spi)
+int cs4271_probe(struct device *dev, struct regmap *regmap)
 {
        struct cs4271_private *cs4271;
        int ret;
 
-       ret = cs4271_common_probe(&spi->dev, &cs4271);
-       if (ret < 0)
-               return ret;
-
-       spi_set_drvdata(spi, cs4271);
-       cs4271->regmap = devm_regmap_init_spi(spi, &cs4271_spi_regmap);
-       if (IS_ERR(cs4271->regmap))
-               return PTR_ERR(cs4271->regmap);
-
-       return snd_soc_register_codec(&spi->dev, &soc_codec_dev_cs4271,
-               &cs4271_dai, 1);
-}
-
-static int cs4271_spi_remove(struct spi_device *spi)
-{
-       snd_soc_unregister_codec(&spi->dev);
-       return 0;
-}
-
-static struct spi_driver cs4271_spi_driver = {
-       .driver = {
-               .name   = "cs4271",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(cs4271_dt_ids),
-       },
-       .probe          = cs4271_spi_probe,
-       .remove         = cs4271_spi_remove,
-};
-#endif /* defined(CONFIG_SPI_MASTER) */
-
-#if IS_ENABLED(CONFIG_I2C)
-static const struct i2c_device_id cs4271_i2c_id[] = {
-       {"cs4271", 0},
-       {}
-};
-MODULE_DEVICE_TABLE(i2c, cs4271_i2c_id);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
 
-static const struct regmap_config cs4271_i2c_regmap = {
-       .reg_bits = 8,
-       .val_bits = 8,
-       .max_register = CS4271_LASTREG,
-
-       .reg_defaults = cs4271_reg_defaults,
-       .num_reg_defaults = ARRAY_SIZE(cs4271_reg_defaults),
-       .cache_type = REGCACHE_RBTREE,
-
-       .volatile_reg = cs4271_volatile_reg,
-};
-
-static int cs4271_i2c_probe(struct i2c_client *client,
-                           const struct i2c_device_id *id)
-{
-       struct cs4271_private *cs4271;
-       int ret;
-
-       ret = cs4271_common_probe(&client->dev, &cs4271);
+       ret = cs4271_common_probe(dev, &cs4271);
        if (ret < 0)
                return ret;
 
-       i2c_set_clientdata(client, cs4271);
-       cs4271->regmap = devm_regmap_init_i2c(client, &cs4271_i2c_regmap);
-       if (IS_ERR(cs4271->regmap))
-               return PTR_ERR(cs4271->regmap);
+       dev_set_drvdata(dev, cs4271);
+       cs4271->regmap = regmap;
 
-       return snd_soc_register_codec(&client->dev, &soc_codec_dev_cs4271,
-               &cs4271_dai, 1);
-}
-
-static int cs4271_i2c_remove(struct i2c_client *client)
-{
-       snd_soc_unregister_codec(&client->dev);
-       return 0;
-}
-
-static struct i2c_driver cs4271_i2c_driver = {
-       .driver = {
-               .name   = "cs4271",
-               .owner  = THIS_MODULE,
-               .of_match_table = of_match_ptr(cs4271_dt_ids),
-       },
-       .id_table       = cs4271_i2c_id,
-       .probe          = cs4271_i2c_probe,
-       .remove         = cs4271_i2c_remove,
-};
-#endif /* IS_ENABLED(CONFIG_I2C) */
-
-/*
- * We only register our serial bus driver here without
- * assignment to particular chip. So if any of the below
- * fails, there is some problem with I2C or SPI subsystem.
- * In most cases this module will be compiled with support
- * of only one serial bus.
- */
-static int __init cs4271_modinit(void)
-{
-       int ret;
-
-#if IS_ENABLED(CONFIG_I2C)
-       ret = i2c_add_driver(&cs4271_i2c_driver);
-       if (ret) {
-               pr_err("Failed to register CS4271 I2C driver: %d\n", ret);
-               return ret;
-       }
-#endif
-
-#if defined(CONFIG_SPI_MASTER)
-       ret = spi_register_driver(&cs4271_spi_driver);
-       if (ret) {
-               pr_err("Failed to register CS4271 SPI driver: %d\n", ret);
-               return ret;
-       }
-#endif
-
-       return 0;
-}
-module_init(cs4271_modinit);
-
-static void __exit cs4271_modexit(void)
-{
-#if defined(CONFIG_SPI_MASTER)
-       spi_unregister_driver(&cs4271_spi_driver);
-#endif
-
-#if IS_ENABLED(CONFIG_I2C)
-       i2c_del_driver(&cs4271_i2c_driver);
-#endif
+       return snd_soc_register_codec(dev, &soc_codec_dev_cs4271, &cs4271_dai,
+                                     1);
 }
-module_exit(cs4271_modexit);
+EXPORT_SYMBOL_GPL(cs4271_probe);
 
 MODULE_AUTHOR("Alexander Sverdlin <subaparts@yandex.ru>");
 MODULE_DESCRIPTION("Cirrus Logic CS4271 ALSA SoC Codec Driver");
diff --git a/sound/soc/codecs/cs4271.h b/sound/soc/codecs/cs4271.h
new file mode 100644 (file)
index 0000000..9adad8e
--- /dev/null
@@ -0,0 +1,11 @@
+#ifndef _CS4271_PRIV_H
+#define _CS4271_PRIV_H
+
+#include <linux/regmap.h>
+
+extern const struct of_device_id cs4271_dt_ids[];
+extern const struct regmap_config cs4271_regmap_config;
+
+int cs4271_probe(struct device *dev, struct regmap *regmap);
+
+#endif
index 669c38f..b395152 100644 (file)
@@ -153,15 +153,17 @@ static const struct snd_kcontrol_new cs42l51_snd_controls[] = {
 static int cs42l51_pdn_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);
+
        switch (event) {
        case SND_SOC_DAPM_PRE_PMD:
-               snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
+               snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
                                    CS42L51_POWER_CTL1_PDN,
                                    CS42L51_POWER_CTL1_PDN);
                break;
        default:
        case SND_SOC_DAPM_POST_PMD:
-               snd_soc_update_bits(w->codec, CS42L51_POWER_CTL1,
+               snd_soc_update_bits(codec, CS42L51_POWER_CTL1,
                                    CS42L51_POWER_CTL1_PDN, 0);
                break;
        }
index 2f8b946..7c55537 100644 (file)
@@ -584,7 +584,7 @@ static const struct snd_kcontrol_new cs42l73_snd_controls[] = {
 static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
        switch (event) {
        case SND_SOC_DAPM_POST_PMD:
@@ -600,7 +600,7 @@ static int cs42l73_spklo_spk_amp_event(struct snd_soc_dapm_widget *w,
 static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
        switch (event) {
        case SND_SOC_DAPM_POST_PMD:
@@ -618,7 +618,7 @@ static int cs42l73_ear_amp_event(struct snd_soc_dapm_widget *w,
 static int cs42l73_hp_amp_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct cs42l73_private *priv = snd_soc_codec_get_drvdata(codec);
        switch (event) {
        case SND_SOC_DAPM_POST_PMD:
index 1087fd5..1391ad5 100644 (file)
@@ -47,6 +47,7 @@ static struct snd_soc_dai_driver hdmi_codec_dai = {
                        SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE |
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
+               .sig_bits = 24,
        },
        .capture = {
                .stream_name = "Capture",
@@ -75,6 +76,7 @@ static struct snd_soc_codec_driver hdmi_codec = {
        .num_dapm_widgets = ARRAY_SIZE(hdmi_widgets),
        .dapm_routes = hdmi_routes,
        .num_dapm_routes = ARRAY_SIZE(hdmi_routes),
+       .ignore_pmdown_time = true,
 };
 
 static int hdmi_codec_probe(struct platform_device *pdev)
index c1ae576..c4dfde9 100644 (file)
@@ -1395,15 +1395,7 @@ static struct snd_soc_dai_driver lm49453_dai[] = {
        },
 };
 
-/* power down chip */
-static int lm49453_remove(struct snd_soc_codec *codec)
-{
-       lm49453_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_lm49453 = {
-       .remove = lm49453_remove,
        .set_bias_level = lm49453_set_bias_level,
        .controls = lm49453_snd_controls,
        .num_controls = ARRAY_SIZE(lm49453_snd_controls),
index 2cd3e54..805b3f8 100644 (file)
@@ -875,7 +875,7 @@ static const struct snd_kcontrol_new max98088_right_ADC_mixer_controls[] = {
 static int max98088_mic_event(struct snd_soc_dapm_widget *w,
                             struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
 
        switch (event) {
@@ -905,7 +905,7 @@ static int max98088_mic_event(struct snd_soc_dapm_widget *w,
 static int max98088_line_pga(struct snd_soc_dapm_widget *w,
                             int event, int line, u8 channel)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
        u8 *state;
 
@@ -1887,25 +1887,6 @@ static void max98088_handle_pdata(struct snd_soc_codec *codec)
                max98088_handle_eq_pdata(codec);
 }
 
-#ifdef CONFIG_PM
-static int max98088_suspend(struct snd_soc_codec *codec)
-{
-       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int max98088_resume(struct snd_soc_codec *codec)
-{
-       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-#else
-#define max98088_suspend NULL
-#define max98088_resume NULL
-#endif
-
 static int max98088_probe(struct snd_soc_codec *codec)
 {
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
@@ -1946,9 +1927,6 @@ static int max98088_probe(struct snd_soc_codec *codec)
 
        snd_soc_write(codec, M98088_REG_51_PWR_SYS, M98088_PWRSV);
 
-       /* initialize registers cache to hardware default */
-       max98088_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        snd_soc_write(codec, M98088_REG_0F_IRQ_ENABLE, 0x00);
 
        snd_soc_write(codec, M98088_REG_22_MIX_DAC,
@@ -1974,7 +1952,6 @@ static int max98088_remove(struct snd_soc_codec *codec)
 {
        struct max98088_priv *max98088 = snd_soc_codec_get_drvdata(codec);
 
-       max98088_set_bias_level(codec, SND_SOC_BIAS_OFF);
        kfree(max98088->eq_texts);
 
        return 0;
@@ -1983,9 +1960,9 @@ static int max98088_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_max98088 = {
        .probe   = max98088_probe,
        .remove  = max98088_remove,
-       .suspend = max98088_suspend,
-       .resume  = max98088_resume,
        .set_bias_level = max98088_set_bias_level,
+       .suspend_bias_off = true,
+
        .controls = max98088_snd_controls,
        .num_controls = ARRAY_SIZE(max98088_snd_controls),
        .dapm_widgets = max98088_dapm_widgets,
index 1229554..151f718 100644 (file)
@@ -806,7 +806,7 @@ static const struct snd_kcontrol_new max98091_snd_controls[] = {
 static int max98090_micinput_event(struct snd_soc_dapm_widget *w,
                                 struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
 
        unsigned int val = snd_soc_read(codec, w->reg);
@@ -1311,6 +1311,10 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
        {"MIC1 Input", NULL, "MIC1"},
        {"MIC2 Input", NULL, "MIC2"},
 
+       {"DMICL", NULL, "DMICL_ENA"},
+       {"DMICL", NULL, "DMICR_ENA"},
+       {"DMICR", NULL, "DMICL_ENA"},
+       {"DMICR", NULL, "DMICR_ENA"},
        {"DMICL", NULL, "AHPF"},
        {"DMICR", NULL, "AHPF"},
 
@@ -1368,8 +1372,6 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
        {"DMIC Mux", "ADC", "ADCR"},
        {"DMIC Mux", "DMIC", "DMICL"},
        {"DMIC Mux", "DMIC", "DMICR"},
-       {"DMIC Mux", "DMIC", "DMICL_ENA"},
-       {"DMIC Mux", "DMIC", "DMICR_ENA"},
 
        {"LBENL Mux", "Normal", "DMIC Mux"},
        {"LBENL Mux", "Loopback", "LTENL Mux"},
@@ -1395,8 +1397,8 @@ static const struct snd_soc_dapm_route max98090_dapm_routes[] = {
        {"STENL Mux", "Sidetone Left", "DMICL"},
        {"STENR Mux", "Sidetone Right", "ADCR"},
        {"STENR Mux", "Sidetone Right", "DMICR"},
-       {"DACL", "NULL", "STENL Mux"},
-       {"DACR", "NULL", "STENL Mux"},
+       {"DACL", NULL, "STENL Mux"},
+       {"DACR", NULL, "STENR Mux"},
 
        {"AIFINL", NULL, "SHDN"},
        {"AIFINR", NULL, "SHDN"},
@@ -1826,27 +1828,155 @@ static int max98090_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-static const int comp_pclk_rates[] = {
-       11289600, 12288000, 12000000, 13000000, 19200000
-};
-
-static const int dmic_micclk[] = {
-       2, 2, 2, 2, 4, 2
-};
+static const int dmic_divisors[] = { 2, 3, 4, 5, 6, 8 };
 
 static const int comp_lrclk_rates[] = {
        8000, 16000, 32000, 44100, 48000, 96000
 };
 
-static const int dmic_comp[6][6] = {
-       {7, 8, 3, 3, 3, 3},
-       {7, 8, 3, 3, 3, 3},
-       {7, 8, 3, 3, 3, 3},
-       {7, 8, 3, 1, 1, 1},
-       {7, 8, 3, 1, 2, 2},
-       {7, 8, 3, 3, 3, 3}
+struct dmic_table {
+       int pclk;
+       struct {
+               int freq;
+               int comp[6]; /* One each for 8, 16, 32, 44.1, 48, and 96 kHz */
+       } settings[6]; /* One for each dmic divisor. */
 };
 
+static const struct dmic_table dmic_table[] = { /* One for each pclk freq. */
+       {
+               .pclk = 11289600,
+               .settings = {
+                       { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+               },
+       },
+       {
+               .pclk = 12000000,
+               .settings = {
+                       { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+               }
+       },
+       {
+               .pclk = 12288000,
+               .settings = {
+                       { .freq = 2, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 1, .comp = { 7, 8, 2, 2, 2, 2 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 6, 6, 6, 6 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 3, 3, 3, 3 } },
+               }
+       },
+       {
+               .pclk = 13000000,
+               .settings = {
+                       { .freq = 2, .comp = { 7, 8, 1, 1, 1, 1 } },
+                       { .freq = 1, .comp = { 7, 8, 0, 0, 0, 0 } },
+                       { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+                       { .freq = 0, .comp = { 7, 8, 4, 4, 5, 5 } },
+                       { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+                       { .freq = 0, .comp = { 7, 8, 1, 1, 1, 1 } },
+               }
+       },
+       {
+               .pclk = 19200000,
+               .settings = {
+                       { .freq = 2, .comp = { 0, 0, 0, 0, 0, 0 } },
+                       { .freq = 1, .comp = { 7, 8, 1, 1, 1, 1 } },
+                       { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+                       { .freq = 0, .comp = { 7, 8, 2, 2, 3, 3 } },
+                       { .freq = 0, .comp = { 7, 8, 1, 1, 2, 2 } },
+                       { .freq = 0, .comp = { 7, 8, 5, 5, 6, 6 } },
+               }
+       },
+};
+
+static int max98090_find_divisor(int target_freq, int pclk)
+{
+       int current_diff = INT_MAX;
+       int test_diff = INT_MAX;
+       int divisor_index = 0;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(dmic_divisors); i++) {
+               test_diff = abs(target_freq - (pclk / dmic_divisors[i]));
+               if (test_diff < current_diff) {
+                       current_diff = test_diff;
+                       divisor_index = i;
+               }
+       }
+
+       return divisor_index;
+}
+
+static int max98090_find_closest_pclk(int pclk)
+{
+       int m1;
+       int m2;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(dmic_table); i++) {
+               if (pclk == dmic_table[i].pclk)
+                       return i;
+               if (pclk < dmic_table[i].pclk) {
+                       if (i == 0)
+                               return i;
+                       m1 = pclk - dmic_table[i-1].pclk;
+                       m2 = dmic_table[i].pclk - pclk;
+                       if (m1 < m2)
+                               return i - 1;
+                       else
+                               return i;
+               }
+       }
+
+       return -EINVAL;
+}
+
+static int max98090_configure_dmic(struct max98090_priv *max98090,
+                                  int target_dmic_clk, int pclk, int fs)
+{
+       int micclk_index;
+       int pclk_index;
+       int dmic_freq;
+       int dmic_comp;
+       int i;
+
+       pclk_index = max98090_find_closest_pclk(pclk);
+       if (pclk_index < 0)
+               return pclk_index;
+
+       micclk_index = max98090_find_divisor(target_dmic_clk, pclk);
+
+       for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) {
+               if (fs <= (comp_lrclk_rates[i] + comp_lrclk_rates[i+1]) / 2)
+                       break;
+       }
+
+       dmic_freq = dmic_table[pclk_index].settings[micclk_index].freq;
+       dmic_comp = dmic_table[pclk_index].settings[micclk_index].comp[i];
+
+       regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_ENABLE,
+                          M98090_MICCLK_MASK,
+                          micclk_index << M98090_MICCLK_SHIFT);
+
+       regmap_update_bits(max98090->regmap, M98090_REG_DIGITAL_MIC_CONFIG,
+                          M98090_DMIC_COMP_MASK | M98090_DMIC_FREQ_MASK,
+                          dmic_comp << M98090_DMIC_COMP_SHIFT |
+                          dmic_freq << M98090_DMIC_FREQ_SHIFT);
+
+       return 0;
+}
+
 static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
                                   struct snd_pcm_hw_params *params,
                                   struct snd_soc_dai *dai)
@@ -1854,7 +1984,6 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_codec *codec = dai->codec;
        struct max98090_priv *max98090 = snd_soc_codec_get_drvdata(codec);
        struct max98090_cdata *cdata;
-       int i, j;
 
        cdata = &max98090->dai[0];
        max98090->bclk = snd_soc_params_to_bclk(params);
@@ -1893,27 +2022,8 @@ static int max98090_dai_hw_params(struct snd_pcm_substream *substream,
                snd_soc_update_bits(codec, M98090_REG_FILTER_CONFIG,
                        M98090_DHF_MASK, M98090_DHF_MASK);
 
-       /* Check for supported PCLK to LRCLK ratios */
-       for (j = 0; j < ARRAY_SIZE(comp_pclk_rates); j++) {
-               if (comp_pclk_rates[j] == max98090->sysclk) {
-                       break;
-               }
-       }
-
-       for (i = 0; i < ARRAY_SIZE(comp_lrclk_rates) - 1; i++) {
-               if (max98090->lrclk <= (comp_lrclk_rates[i] +
-                       comp_lrclk_rates[i + 1]) / 2) {
-                       break;
-               }
-       }
-
-       snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_ENABLE,
-                       M98090_MICCLK_MASK,
-                       dmic_micclk[j] << M98090_MICCLK_SHIFT);
-
-       snd_soc_update_bits(codec, M98090_REG_DIGITAL_MIC_CONFIG,
-                       M98090_DMIC_COMP_MASK,
-                       dmic_comp[j][i] << M98090_DMIC_COMP_SHIFT);
+       max98090_configure_dmic(max98090, max98090->dmic_freq, max98090->pclk,
+                               max98090->lrclk);
 
        return 0;
 }
@@ -1944,12 +2054,15 @@ static int max98090_dai_set_sysclk(struct snd_soc_dai *dai,
        if ((freq >= 10000000) && (freq <= 20000000)) {
                snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
                        M98090_PSCLK_DIV1);
+               max98090->pclk = freq;
        } else if ((freq > 20000000) && (freq <= 40000000)) {
                snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
                        M98090_PSCLK_DIV2);
+               max98090->pclk = freq >> 1;
        } else if ((freq > 40000000) && (freq <= 60000000)) {
                snd_soc_write(codec, M98090_REG_SYSTEM_CLOCK,
                        M98090_PSCLK_DIV4);
+               max98090->pclk = freq >> 2;
        } else {
                dev_err(codec->dev, "Invalid master clock frequency\n");
                return -EINVAL;
@@ -2324,6 +2437,7 @@ static int max98090_probe(struct snd_soc_codec *codec)
        /* Initialize private data */
 
        max98090->sysclk = (unsigned)-1;
+       max98090->pclk = (unsigned)-1;
        max98090->master = false;
 
        cdata = &max98090->dai[0];
@@ -2463,6 +2577,11 @@ static int max98090_i2c_probe(struct i2c_client *i2c,
        i2c_set_clientdata(i2c, max98090);
        max98090->pdata = i2c->dev.platform_data;
 
+       ret = of_property_read_u32(i2c->dev.of_node, "maxim,dmic-freq",
+                                  &max98090->dmic_freq);
+       if (ret < 0)
+               max98090->dmic_freq = MAX98090_DEFAULT_DMIC_FREQ;
+
        max98090->regmap = devm_regmap_init_i2c(i2c, &max98090_regmap);
        if (IS_ERR(max98090->regmap)) {
                ret = PTR_ERR(max98090->regmap);
index a5f6bad..21ff743 100644 (file)
 #ifndef _MAX98090_H
 #define _MAX98090_H
 
+/*
+ * The default operating frequency for a DMIC attached to the codec.
+ * This can be overridden by a device tree property.
+ */
+#define MAX98090_DEFAULT_DMIC_FREQ             2500000
+
 /*
  * MAX98090 Register Definitions
  */
@@ -1518,8 +1524,10 @@ struct max98090_priv {
        struct max98090_pdata *pdata;
        struct clk *mclk;
        unsigned int sysclk;
+       unsigned int pclk;
        unsigned int bclk;
        unsigned int lrclk;
+       u32 dmic_freq;
        struct max98090_cdata dai[1];
        int jack_state;
        struct delayed_work jack_work;
index 0ee6797..8fba0c3 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/clk.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -57,6 +58,7 @@ struct max98095_priv {
        unsigned int mic2pre;
        struct snd_soc_jack *headphone_jack;
        struct snd_soc_jack *mic_jack;
+       struct mutex lock;
 };
 
 static const struct reg_default max98095_reg_def[] = {
@@ -864,7 +866,7 @@ static const struct snd_kcontrol_new max98095_right_ADC_mixer_controls[] = {
 static int max98095_mic_event(struct snd_soc_dapm_widget *w,
                             struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
 
        switch (event) {
@@ -894,7 +896,7 @@ static int max98095_mic_event(struct snd_soc_dapm_widget *w,
 static int max98095_line_pga(struct snd_soc_dapm_widget *w,
                             int event, u8 channel)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
        u8 *state;
 
@@ -942,7 +944,7 @@ static int max98095_pga_in2_event(struct snd_soc_dapm_widget *w,
 static int max98095_lineout_event(struct snd_soc_dapm_widget *w,
                             struct snd_kcontrol *kcontrol, int event)
 {
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
@@ -1803,7 +1805,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
        regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
        snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&max98095->lock);
        snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
        m98095_eq_band(codec, channel, 0, coef_set->band1);
        m98095_eq_band(codec, channel, 1, coef_set->band2);
@@ -1811,7 +1813,7 @@ static int max98095_put_eq_enum(struct snd_kcontrol *kcontrol,
        m98095_eq_band(codec, channel, 3, coef_set->band4);
        m98095_eq_band(codec, channel, 4, coef_set->band5);
        snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&max98095->lock);
 
        /* Restore the original on/off state */
        snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
@@ -1957,12 +1959,12 @@ static int max98095_put_bq_enum(struct snd_kcontrol *kcontrol,
        regsave = snd_soc_read(codec, M98095_088_CFG_LEVEL);
        snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, 0);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&max98095->lock);
        snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, M98095_SEG);
        m98095_biquad_band(codec, channel, 0, coef_set->band1);
        m98095_biquad_band(codec, channel, 1, coef_set->band2);
        snd_soc_update_bits(codec, M98095_00F_HOST_CFG, M98095_SEG, 0);
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&max98095->lock);
 
        /* Restore the original on/off state */
        snd_soc_update_bits(codec, M98095_088_CFG_LEVEL, regmask, regsave);
@@ -2317,9 +2319,6 @@ static int max98095_probe(struct snd_soc_codec *codec)
 
        snd_soc_write(codec, M98095_097_PWR_SYS, M98095_PWRSV);
 
-       /* initialize registers cache to hardware default */
-       max98095_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        snd_soc_write(codec, M98095_048_MIX_DAC_LR,
                M98095_DAI1L_TO_DACL|M98095_DAI1R_TO_DACR);
 
@@ -2359,8 +2358,6 @@ static int max98095_remove(struct snd_soc_codec *codec)
        struct max98095_priv *max98095 = snd_soc_codec_get_drvdata(codec);
        struct i2c_client *client = to_i2c_client(codec->dev);
 
-       max98095_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
        if (max98095->headphone_jack || max98095->mic_jack)
                max98095_jack_detect_disable(codec);
 
@@ -2395,6 +2392,8 @@ static int max98095_i2c_probe(struct i2c_client *i2c,
        if (max98095 == NULL)
                return -ENOMEM;
 
+       mutex_init(&max98095->lock);
+
        max98095->regmap = devm_regmap_init_i2c(i2c, &max98095_regmap);
        if (IS_ERR(max98095->regmap)) {
                ret = PTR_ERR(max98095->regmap);
index 4fdf5aa..10f8e47 100644 (file)
@@ -291,25 +291,6 @@ static struct snd_soc_dai_driver max9850_dai = {
        .ops = &max9850_dai_ops,
 };
 
-#ifdef CONFIG_PM
-static int max9850_suspend(struct snd_soc_codec *codec)
-{
-       max9850_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int max9850_resume(struct snd_soc_codec *codec)
-{
-       max9850_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-#else
-#define max9850_suspend NULL
-#define max9850_resume NULL
-#endif
-
 static int max9850_probe(struct snd_soc_codec *codec)
 {
        /* enable zero-detect */
@@ -324,9 +305,8 @@ static int max9850_probe(struct snd_soc_codec *codec)
 
 static struct snd_soc_codec_driver soc_codec_dev_max9850 = {
        .probe =        max9850_probe,
-       .suspend =      max9850_suspend,
-       .resume =       max9850_resume,
        .set_bias_level = max9850_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = max9850_controls,
        .num_controls = ARRAY_SIZE(max9850_controls),
index 4d62230..d0547fa 100644 (file)
@@ -24,8 +24,13 @@ static int pcm512x_i2c_probe(struct i2c_client *i2c,
                             const struct i2c_device_id *id)
 {
        struct regmap *regmap;
+       struct regmap_config config = pcm512x_regmap;
 
-       regmap = devm_regmap_init_i2c(i2c, &pcm512x_regmap);
+       /* msb needs to be set to enable auto-increment of addresses */
+       config.read_flag_mask = 0x80;
+       config.write_flag_mask = 0x80;
+
+       regmap = devm_regmap_init_i2c(i2c, &config);
        if (IS_ERR(regmap))
                return PTR_ERR(regmap);
 
index 4aa555c..2cd4fe4 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
+#include <linux/dmi.h>
 #include <linux/acpi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 
 struct rt286_priv {
        struct regmap *regmap;
+       struct snd_soc_codec *codec;
        struct rt286_platform_data pdata;
        struct i2c_client *i2c;
        struct snd_soc_jack *jack;
        struct delayed_work jack_detect_work;
        int sys_clk;
+       int clk_id;
        struct reg_default *index_cache;
 };
 
@@ -188,7 +191,7 @@ static int rt286_hw_write(void *context, unsigned int reg, unsigned int value)
        u8 data[4];
        int ret, i;
 
-       /*handle index registers*/
+       /* handle index registers */
        if (reg <= 0xff) {
                rt286_hw_write(client, RT286_COEF_INDEX, reg);
                for (i = 0; i < INDEX_CACHE_SIZE; i++) {
@@ -231,7 +234,7 @@ static int rt286_hw_read(void *context, unsigned int reg, unsigned int *value)
        __be32 be_reg;
        unsigned int index, vid, buf = 0x0;
 
-       /*handle index registers*/
+       /* handle index registers */
        if (reg <= 0xff) {
                rt286_hw_write(client, RT286_COEF_INDEX, reg);
                reg = RT286_PROC_COEF;
@@ -298,7 +301,6 @@ static int rt286_support_power_controls[] = {
 static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
 {
        unsigned int val, buf;
-       int i;
 
        *hp = false;
        *mic = false;
@@ -309,67 +311,44 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
                if (*hp) {
                        /* power on HV,VERF */
                        regmap_update_bits(rt286->regmap,
-                               RT286_POWER_CTRL1, 0x1001, 0x0);
+                               RT286_DC_GAIN, 0x200, 0x200);
+
+                       snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
+                                                       "HV");
+                       snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
+                                                       "VREF");
                        /* power LDO1 */
-                       regmap_update_bits(rt286->regmap,
-                               RT286_POWER_CTRL2, 0x4, 0x4);
-                       regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
-                       regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val);
+                       snd_soc_dapm_force_enable_pin(&rt286->codec->dapm,
+                                                       "LDO1");
+                       snd_soc_dapm_sync(&rt286->codec->dapm);
 
-                       msleep(200);
-                       i = 40;
-                       while (((val & 0x0800) == 0) && (i > 0)) {
-                               regmap_read(rt286->regmap,
-                                       RT286_CBJ_CTRL2, &val);
-                               i--;
-                               msleep(20);
-                       }
+                       regmap_write(rt286->regmap, RT286_SET_MIC1, 0x24);
+                       msleep(50);
 
-                       if (0x0400 == (val & 0x0700)) {
-                               *mic = false;
+                       regmap_update_bits(rt286->regmap,
+                               RT286_CBJ_CTRL1, 0xfcc0, 0xd400);
+                       msleep(300);
+                       regmap_read(rt286->regmap, RT286_CBJ_CTRL2, &val);
 
-                               regmap_write(rt286->regmap,
-                                       RT286_SET_MIC1, 0x20);
-                               /* power off HV,VERF */
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_POWER_CTRL1, 0x1001, 0x1001);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_CBJ_CTRL1, 0x0030, 0x0000);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
-                       } else if ((0x0200 == (val & 0x0700)) ||
-                               (0x0100 == (val & 0x0700))) {
+                       if (0x0070 == (val & 0x0070)) {
                                *mic = true;
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_CBJ_CTRL1, 0x0030, 0x0020);
-                               regmap_update_bits(rt286->regmap,
-                                       RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
                        } else {
-                               *mic = false;
+                               regmap_update_bits(rt286->regmap,
+                                       RT286_CBJ_CTRL1, 0xfcc0, 0xe400);
+                               msleep(300);
+                               regmap_read(rt286->regmap,
+                                       RT286_CBJ_CTRL2, &val);
+                               if (0x0070 == (val & 0x0070))
+                                       *mic = true;
+                               else
+                                       *mic = false;
                        }
-
-                       regmap_update_bits(rt286->regmap,
-                                               RT286_MISC_CTRL1,
-                                               0x0060, 0x0000);
-               } else {
-                       regmap_update_bits(rt286->regmap,
-                                               RT286_MISC_CTRL1,
-                                               0x0060, 0x0020);
                        regmap_update_bits(rt286->regmap,
-                                               RT286_A_BIAS_CTRL3,
-                                               0xc000, 0x8000);
-                       regmap_update_bits(rt286->regmap,
-                                               RT286_CBJ_CTRL1,
-                                               0x0030, 0x0020);
-                       regmap_update_bits(rt286->regmap,
-                                               RT286_A_BIAS_CTRL2,
-                                               0xc000, 0x8000);
+                               RT286_DC_GAIN, 0x200, 0x0);
 
+               } else {
                        *mic = false;
+                       regmap_write(rt286->regmap, RT286_SET_MIC1, 0x20);
                }
        } else {
                regmap_read(rt286->regmap, RT286_GET_HP_SENSE, &buf);
@@ -378,6 +357,12 @@ static int rt286_jack_detect(struct rt286_priv *rt286, bool *hp, bool *mic)
                *mic = buf & 0x80000000;
        }
 
+       snd_soc_dapm_disable_pin(&rt286->codec->dapm, "HV");
+       snd_soc_dapm_disable_pin(&rt286->codec->dapm, "VREF");
+       if (!*hp)
+               snd_soc_dapm_disable_pin(&rt286->codec->dapm, "LDO1");
+       snd_soc_dapm_sync(&rt286->codec->dapm);
+
        return 0;
 }
 
@@ -415,6 +400,17 @@ int rt286_mic_detect(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
 }
 EXPORT_SYMBOL_GPL(rt286_mic_detect);
 
+static int is_mclk_mode(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink)
+{
+       struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(source->codec);
+
+       if (rt286->clk_id == RT286_SCLK_S_MCLK)
+               return 1;
+       else
+               return 0;
+}
+
 static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -6350, 50, 0);
 static const DECLARE_TLV_DB_SCALE(mic_vol_tlv, 0, 1000, 0);
 
@@ -568,7 +564,84 @@ static int rt286_adc_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int rt286_vref_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_update_bits(codec,
+                       RT286_CBJ_CTRL1, 0x0400, 0x0000);
+               mdelay(50);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt286_ldo2_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x08);
+               break;
+       case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_update_bits(codec, RT286_POWER_CTRL2, 0x38, 0x30);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt286_mic1_event(struct snd_soc_dapm_widget *w,
+                            struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               snd_soc_update_bits(codec,
+                       RT286_A_BIAS_CTRL3, 0xc000, 0x8000);
+               snd_soc_update_bits(codec,
+                       RT286_A_BIAS_CTRL2, 0xc000, 0x8000);
+               break;
+       case SND_SOC_DAPM_POST_PMD:
+               snd_soc_update_bits(codec,
+                       RT286_A_BIAS_CTRL3, 0xc000, 0x0000);
+               snd_soc_update_bits(codec,
+                       RT286_A_BIAS_CTRL2, 0xc000, 0x0000);
+               break;
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
 static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY_S("HV", 1, RT286_POWER_CTRL1,
+               12, 1, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("VREF", RT286_POWER_CTRL1,
+               0, 1, rt286_vref_event, SND_SOC_DAPM_PRE_PMU),
+       SND_SOC_DAPM_SUPPLY_S("LDO1", 1, RT286_POWER_CTRL2,
+               2, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("LDO2", 2, RT286_POWER_CTRL1,
+               13, 1, rt286_ldo2_event, SND_SOC_DAPM_PRE_PMD |
+               SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_SUPPLY("MCLK MODE", RT286_PLL_CTRL1,
+               5, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("MIC1 Input Buffer", SND_SOC_NOPM,
+               0, 0, rt286_mic1_event, SND_SOC_DAPM_PRE_PMU |
+               SND_SOC_DAPM_POST_PMD),
+
        /* Input Lines */
        SND_SOC_DAPM_INPUT("DMIC1 Pin"),
        SND_SOC_DAPM_INPUT("DMIC2 Pin"),
@@ -642,6 +715,25 @@ static const struct snd_soc_dapm_widget rt286_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route rt286_dapm_routes[] = {
+       {"ADC 0", NULL, "MCLK MODE", is_mclk_mode},
+       {"ADC 1", NULL, "MCLK MODE", is_mclk_mode},
+       {"Front", NULL, "MCLK MODE", is_mclk_mode},
+       {"Surround", NULL, "MCLK MODE", is_mclk_mode},
+
+       {"HP Power", NULL, "LDO1"},
+       {"HP Power", NULL, "LDO2"},
+
+       {"MIC1", NULL, "LDO1"},
+       {"MIC1", NULL, "LDO2"},
+       {"MIC1", NULL, "HV"},
+       {"MIC1", NULL, "VREF"},
+       {"MIC1", NULL, "MIC1 Input Buffer"},
+
+       {"SPO", NULL, "LDO1"},
+       {"SPO", NULL, "LDO2"},
+       {"SPO", NULL, "HV"},
+       {"SPO", NULL, "VREF"},
+
        {"DMIC1", NULL, "DMIC1 Pin"},
        {"DMIC2", NULL, "DMIC2 Pin"},
        {"DMIC1", NULL, "DMIC Receiver"},
@@ -880,6 +972,7 @@ static int rt286_set_dai_sysclk(struct snd_soc_dai *dai,
        }
 
        rt286->sys_clk = freq;
+       rt286->clk_id = clk_id;
 
        return 0;
 }
@@ -915,13 +1008,18 @@ static int rt286_set_bias_level(struct snd_soc_codec *codec,
 
        case SND_SOC_BIAS_ON:
                mdelay(10);
+               snd_soc_update_bits(codec,
+                       RT286_CBJ_CTRL1, 0x0400, 0x0400);
+               snd_soc_update_bits(codec,
+                       RT286_DC_GAIN, 0x200, 0x0);
+
                break;
 
        case SND_SOC_BIAS_STANDBY:
                snd_soc_write(codec,
                        RT286_SET_AUDIO_POWER, AC_PWRST_D3);
                snd_soc_update_bits(codec,
-                       RT286_DC_GAIN, 0x200, 0x0);
+                       RT286_CBJ_CTRL1, 0x0400, 0x0000);
                break;
 
        default:
@@ -962,6 +1060,7 @@ static int rt286_probe(struct snd_soc_codec *codec)
 {
        struct rt286_priv *rt286 = snd_soc_codec_get_drvdata(codec);
 
+       rt286->codec = codec;
        codec->dapm.bias_level = SND_SOC_BIAS_OFF;
 
        if (rt286->i2c->irq) {
@@ -1107,6 +1206,16 @@ static const struct acpi_device_id rt286_acpi_match[] = {
 };
 MODULE_DEVICE_TABLE(acpi, rt286_acpi_match);
 
+static struct dmi_system_id force_combo_jack_table[] = {
+       {
+               .ident = "Intel Wilson Beach",
+               .matches = {
+                       DMI_MATCH(DMI_BOARD_NAME, "Wilson Beach SDS")
+               }
+       },
+       { }
+};
+
 static int rt286_i2c_probe(struct i2c_client *i2c,
                           const struct i2c_device_id *id)
 {
@@ -1142,6 +1251,9 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
        if (pdata)
                rt286->pdata = *pdata;
 
+       if (dmi_check_system(force_combo_jack_table))
+               rt286->pdata.cbj_en = true;
+
        regmap_write(rt286->regmap, RT286_SET_AUDIO_POWER, AC_PWRST_D3);
 
        for (i = 0; i < RT286_POWER_REG_LEN; i++)
@@ -1152,7 +1264,6 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
        if (!rt286->pdata.cbj_en) {
                regmap_write(rt286->regmap, RT286_CBJ_CTRL2, 0x0000);
                regmap_write(rt286->regmap, RT286_MIC1_DET_CTRL, 0x0816);
-               regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
                regmap_update_bits(rt286->regmap,
                                        RT286_CBJ_CTRL1, 0xf000, 0xb000);
        } else {
@@ -1169,10 +1280,12 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
 
        mdelay(10);
 
-       /*Power down LDO2*/
-       regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0x8, 0x0);
+       regmap_write(rt286->regmap, RT286_MISC_CTRL1, 0x0000);
+       /* Power down LDO, VREF */
+       regmap_update_bits(rt286->regmap, RT286_POWER_CTRL2, 0xc, 0x0);
+       regmap_update_bits(rt286->regmap, RT286_POWER_CTRL1, 0x1001, 0x1001);
 
-       /*Set depop parameter*/
+       /* Set depop parameter */
        regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL2, 0x403a, 0x401a);
        regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL3, 0xf777, 0x4737);
        regmap_update_bits(rt286->regmap, RT286_DEPOP_CTRL4, 0x00ff, 0x003f);
index 1ba27db..6d7b7ca 100644 (file)
@@ -1612,29 +1612,6 @@ static int rt5631_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int rt5631_remove(struct snd_soc_codec *codec)
-{
-       rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int rt5631_suspend(struct snd_soc_codec *codec)
-{
-       rt5631_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int rt5631_resume(struct snd_soc_codec *codec)
-{
-       rt5631_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-#else
-#define rt5631_suspend NULL
-#define rt5631_resume NULL
-#endif
-
 #define RT5631_STEREO_RATES SNDRV_PCM_RATE_8000_96000
 #define RT5631_FORMAT  (SNDRV_PCM_FMTBIT_S16_LE | \
                        SNDRV_PCM_FMTBIT_S20_3LE | \
@@ -1672,10 +1649,8 @@ static struct snd_soc_dai_driver rt5631_dai[] = {
 
 static struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
        .probe = rt5631_probe,
-       .remove = rt5631_remove,
-       .suspend = rt5631_suspend,
-       .resume = rt5631_resume,
        .set_bias_level = rt5631_set_bias_level,
+       .suspend_bias_off = true,
        .controls = rt5631_snd_controls,
        .num_controls = ARRAY_SIZE(rt5631_snd_controls),
        .dapm_widgets = rt5631_dapm_widgets,
@@ -1686,10 +1661,20 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5631 = {
 
 static const struct i2c_device_id rt5631_i2c_id[] = {
        { "rt5631", 0 },
+       { "alc5631", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, rt5631_i2c_id);
 
+#ifdef CONFIG_OF
+static struct of_device_id rt5631_i2c_dt_ids[] = {
+       { .compatible = "realtek,rt5631"},
+       { .compatible = "realtek,alc5631"},
+       { }
+};
+MODULE_DEVICE_TABLE(of, rt5631_i2c_dt_ids);
+#endif
+
 static const struct regmap_config rt5631_regmap_config = {
        .reg_bits = 8,
        .val_bits = 16,
@@ -1734,6 +1719,7 @@ static struct i2c_driver rt5631_i2c_driver = {
        .driver = {
                .name = "rt5631",
                .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(rt5631_i2c_dt_ids),
        },
        .probe = rt5631_i2c_probe,
        .remove   = rt5631_i2c_remove,
index d16331e..27141e2 100644 (file)
@@ -554,6 +554,53 @@ static int is_sys_clk_from_pll(struct snd_soc_dapm_widget *source,
                return 0;
 }
 
+static int is_using_asrc(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink)
+{
+       unsigned int reg, shift, val;
+
+       switch (source->shift) {
+       case 0:
+               reg = RT5645_ASRC_3;
+               shift = 0;
+               break;
+       case 1:
+               reg = RT5645_ASRC_3;
+               shift = 4;
+               break;
+       case 3:
+               reg = RT5645_ASRC_2;
+               shift = 0;
+               break;
+       case 8:
+               reg = RT5645_ASRC_2;
+               shift = 4;
+               break;
+       case 9:
+               reg = RT5645_ASRC_2;
+               shift = 8;
+               break;
+       case 10:
+               reg = RT5645_ASRC_2;
+               shift = 12;
+               break;
+       default:
+               return 0;
+       }
+
+       val = (snd_soc_read(source->codec, reg) >> shift) & 0xf;
+       switch (val) {
+       case 1:
+       case 2:
+       case 3:
+       case 4:
+               return 1;
+       default:
+               return 0;
+       }
+
+}
+
 /* Digital Mixer */
 static const struct snd_kcontrol_new rt5645_sto1_adc_l_mix[] = {
        SOC_DAPM_SINGLE("ADC1 Switch", RT5645_STO1_ADC_MIXER,
@@ -1246,6 +1293,30 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
        SND_SOC_DAPM_SUPPLY("Mic Det Power", RT5645_PWR_VOL,
                RT5645_PWR_MIC_DET_BIT, 0, NULL, 0),
 
+       /* ASRC */
+       SND_SOC_DAPM_SUPPLY_S("I2S1 ASRC", 1, RT5645_ASRC_1,
+                             11, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("I2S2 ASRC", 1, RT5645_ASRC_1,
+                             12, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DAC STO ASRC", 1, RT5645_ASRC_1,
+                             10, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DAC MONO L ASRC", 1, RT5645_ASRC_1,
+                             9, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5645_ASRC_1,
+                             8, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5645_ASRC_1,
+                             7, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5645_ASRC_1,
+                             5, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5645_ASRC_1,
+                             4, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5645_ASRC_1,
+                             3, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("ADC MONO L ASRC", 1, RT5645_ASRC_1,
+                             1, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("ADC MONO R ASRC", 1, RT5645_ASRC_1,
+                             0, 0, NULL, 0),
+
        /* Input Side */
        /* micbias */
        SND_SOC_DAPM_MICBIAS("micbias1", RT5645_PWR_ANLG2,
@@ -1504,6 +1575,17 @@ static const struct snd_soc_dapm_widget rt5645_dapm_widgets[] = {
 };
 
 static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
+       { "adc stereo1 filter", NULL, "ADC STO1 ASRC", is_using_asrc },
+       { "adc stereo2 filter", NULL, "ADC STO2 ASRC", is_using_asrc },
+       { "adc mono left filter", NULL, "ADC MONO L ASRC", is_using_asrc },
+       { "adc mono right filter", NULL, "ADC MONO R ASRC", is_using_asrc },
+       { "dac mono left filter", NULL, "DAC MONO L ASRC", is_using_asrc },
+       { "dac mono right filter", NULL, "DAC MONO R ASRC", is_using_asrc },
+       { "dac stereo1 filter", NULL, "DAC STO ASRC", is_using_asrc },
+
+       { "I2S1", NULL, "I2S1 ASRC" },
+       { "I2S2", NULL, "I2S2 ASRC" },
+
        { "IN1P", NULL, "LDO2" },
        { "IN2P", NULL, "LDO2" },
 
@@ -1550,12 +1632,15 @@ static const struct snd_soc_dapm_route rt5645_dapm_routes[] = {
 
        { "Stereo1 DMIC Mux", "DMIC1", "DMIC1" },
        { "Stereo1 DMIC Mux", "DMIC2", "DMIC2" },
+       { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC" },
 
        { "Mono DMIC L Mux", "DMIC1", "DMIC L1" },
        { "Mono DMIC L Mux", "DMIC2", "DMIC L2" },
+       { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC" },
 
        { "Mono DMIC R Mux", "DMIC1", "DMIC R1" },
        { "Mono DMIC R Mux", "DMIC2", "DMIC R2" },
+       { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC" },
 
        { "Stereo1 ADC L2 Mux", "DMIC", "Stereo1 DMIC Mux" },
        { "Stereo1 ADC L2 Mux", "DAC MIX", "DAC MIXL" },
@@ -2029,8 +2114,11 @@ static int rt5645_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
        struct snd_soc_codec *codec = dai->codec;
        unsigned int val = 0;
 
-       if (rx_mask || tx_mask)
+       if (rx_mask || tx_mask) {
                val |= (1 << 14);
+               snd_soc_update_bits(codec, RT5645_BASS_BACK,
+                       RT5645_G_BB_BST_MASK, RT5645_G_BB_BST_25DB);
+       }
 
        switch (slots) {
        case 4:
@@ -2071,8 +2159,8 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
                        enum snd_soc_bias_level level)
 {
        switch (level) {
-       case SND_SOC_BIAS_STANDBY:
-               if (SND_SOC_BIAS_OFF == codec->dapm.bias_level) {
+       case SND_SOC_BIAS_PREPARE:
+               if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
                        snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
                                RT5645_PWR_VREF1 | RT5645_PWR_MB |
                                RT5645_PWR_BG | RT5645_PWR_VREF2,
@@ -2087,15 +2175,24 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
                }
                break;
 
+       case SND_SOC_BIAS_STANDBY:
+               snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                       RT5645_PWR_VREF1 | RT5645_PWR_MB |
+                       RT5645_PWR_BG | RT5645_PWR_VREF2,
+                       RT5645_PWR_VREF1 | RT5645_PWR_MB |
+                       RT5645_PWR_BG | RT5645_PWR_VREF2);
+               snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                       RT5645_PWR_FV1 | RT5645_PWR_FV2,
+                       RT5645_PWR_FV1 | RT5645_PWR_FV2);
+               break;
+
        case SND_SOC_BIAS_OFF:
                snd_soc_write(codec, RT5645_DEPOP_M2, 0x1100);
                snd_soc_write(codec, RT5645_GEN_CTRL1, 0x0128);
-               snd_soc_write(codec, RT5645_PWR_DIG1, 0x0000);
-               snd_soc_write(codec, RT5645_PWR_DIG2, 0x0000);
-               snd_soc_write(codec, RT5645_PWR_VOL, 0x0000);
-               snd_soc_write(codec, RT5645_PWR_MIXER, 0x0000);
-               snd_soc_write(codec, RT5645_PWR_ANLG1, 0x0000);
-               snd_soc_write(codec, RT5645_PWR_ANLG2, 0x0000);
+               snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
+                               RT5645_PWR_VREF1 | RT5645_PWR_MB |
+                               RT5645_PWR_BG | RT5645_PWR_VREF2 |
+                               RT5645_PWR_FV1 | RT5645_PWR_FV2, 0x0);
                break;
 
        default:
@@ -2106,13 +2203,16 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-static int rt5645_jack_detect(struct snd_soc_codec *codec,
-       struct snd_soc_jack *jack)
+static int rt5645_jack_detect(struct snd_soc_codec *codec)
 {
        struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
        int gpio_state, jack_type = 0;
        unsigned int val;
 
+       if (!gpio_is_valid(rt5645->pdata.hp_det_gpio)) {
+               dev_err(codec->dev, "invalid gpio\n");
+               return -EINVAL;
+       }
        gpio_state = gpio_get_value(rt5645->pdata.hp_det_gpio);
 
        dev_dbg(codec->dev, "gpio = %d(%d)\n", rt5645->pdata.hp_det_gpio,
@@ -2145,34 +2245,44 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec,
 
                snd_soc_dapm_disable_pin(&codec->dapm, "micbias1");
                snd_soc_dapm_disable_pin(&codec->dapm, "micbias2");
-               snd_soc_dapm_disable_pin(&codec->dapm, "LDO2");
+               if (rt5645->pdata.jd_mode == 0)
+                       snd_soc_dapm_disable_pin(&codec->dapm, "LDO2");
                snd_soc_dapm_disable_pin(&codec->dapm, "Mic Det Power");
                snd_soc_dapm_sync(&codec->dapm);
        }
 
-       snd_soc_jack_report(rt5645->jack, jack_type, SND_JACK_HEADSET);
-
+       snd_soc_jack_report(rt5645->hp_jack, jack_type, SND_JACK_HEADPHONE);
+       snd_soc_jack_report(rt5645->mic_jack, jack_type, SND_JACK_MICROPHONE);
        return 0;
 }
 
 int rt5645_set_jack_detect(struct snd_soc_codec *codec,
-       struct snd_soc_jack *jack)
+       struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack)
 {
        struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
 
-       rt5645->jack = jack;
-
-       rt5645_jack_detect(codec, rt5645->jack);
+       rt5645->hp_jack = hp_jack;
+       rt5645->mic_jack = mic_jack;
+       rt5645_jack_detect(codec);
 
        return 0;
 }
 EXPORT_SYMBOL_GPL(rt5645_set_jack_detect);
 
+static void rt5645_jack_detect_work(struct work_struct *work)
+{
+       struct rt5645_priv *rt5645 =
+               container_of(work, struct rt5645_priv, jack_detect_work.work);
+
+       rt5645_jack_detect(rt5645->codec);
+}
+
 static irqreturn_t rt5645_irq(int irq, void *data)
 {
        struct rt5645_priv *rt5645 = data;
 
-       rt5645_jack_detect(rt5645->codec, rt5645->jack);
+       queue_delayed_work(system_power_efficient_wq,
+                          &rt5645->jack_detect_work, msecs_to_jiffies(250));
 
        return IRQ_HANDLED;
 }
@@ -2187,6 +2297,13 @@ static int rt5645_probe(struct snd_soc_codec *codec)
 
        snd_soc_update_bits(codec, RT5645_CHARGE_PUMP, 0x0300, 0x0200);
 
+       /* for JD function */
+       if (rt5645->pdata.en_jd_func) {
+               snd_soc_dapm_force_enable_pin(&codec->dapm, "JD Power");
+               snd_soc_dapm_force_enable_pin(&codec->dapm, "LDO2");
+               snd_soc_dapm_sync(&codec->dapm);
+       }
+
        return 0;
 }
 
@@ -2420,6 +2537,51 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
 
        }
 
+       if (rt5645->pdata.en_jd_func) {
+               regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
+                       RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU,
+                       RT5645_IRQ_CLK_GATE_CTRL | RT5645_MICINDET_MANU);
+               regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1,
+                       RT5645_CBJ_BST1_EN, RT5645_CBJ_BST1_EN);
+               regmap_update_bits(rt5645->regmap, RT5645_JD_CTRL3,
+                       RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL,
+                       RT5645_JD_CBJ_EN | RT5645_JD_CBJ_POL);
+               regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+                       RT5645_IRQ_CLK_INT, RT5645_IRQ_CLK_INT);
+       }
+
+       if (rt5645->pdata.jd_mode) {
+               regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+                                  RT5645_IRQ_JD_1_1_EN, RT5645_IRQ_JD_1_1_EN);
+               regmap_update_bits(rt5645->regmap, RT5645_GEN_CTRL3,
+                                  RT5645_JD_PSV_MODE, RT5645_JD_PSV_MODE);
+               regmap_update_bits(rt5645->regmap, RT5645_HPO_MIXER,
+                                  RT5645_IRQ_PSV_MODE, RT5645_IRQ_PSV_MODE);
+               regmap_update_bits(rt5645->regmap, RT5645_MICBIAS,
+                                  RT5645_MIC2_OVCD_EN, RT5645_MIC2_OVCD_EN);
+               regmap_update_bits(rt5645->regmap, RT5645_GPIO_CTRL1,
+                                  RT5645_GP1_PIN_IRQ, RT5645_GP1_PIN_IRQ);
+               switch (rt5645->pdata.jd_mode) {
+               case 1:
+                       regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
+                                          RT5645_JD1_MODE_MASK,
+                                          RT5645_JD1_MODE_0);
+                       break;
+               case 2:
+                       regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
+                                          RT5645_JD1_MODE_MASK,
+                                          RT5645_JD1_MODE_1);
+                       break;
+               case 3:
+                       regmap_update_bits(rt5645->regmap, RT5645_A_JD_CTRL1,
+                                          RT5645_JD1_MODE_MASK,
+                                          RT5645_JD1_MODE_2);
+                       break;
+               default:
+                       break;
+               }
+       }
+
        if (rt5645->i2c->irq) {
                ret = request_threaded_irq(rt5645->i2c->irq, NULL, rt5645_irq,
                        IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING
@@ -2438,6 +2600,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
                        dev_err(&i2c->dev, "Fail gpio_direction hp_det_gpio\n");
        }
 
+       INIT_DELAYED_WORK(&rt5645->jack_detect_work, rt5645_jack_detect_work);
+
        return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5645,
                                      rt5645_dai, ARRAY_SIZE(rt5645_dai));
 }
@@ -2449,6 +2613,8 @@ static int rt5645_i2c_remove(struct i2c_client *i2c)
        if (i2c->irq)
                free_irq(i2c->irq, rt5645);
 
+       cancel_delayed_work_sync(&rt5645->jack_detect_work);
+
        if (gpio_is_valid(rt5645->pdata.hp_det_gpio))
                gpio_free(rt5645->pdata.hp_det_gpio);
 
index 50c62c5..a815e36 100644 (file)
 #define RT5645_M_DAC1_HM_SFT                   14
 #define RT5645_M_HPVOL_HM                      (0x1 << 13)
 #define RT5645_M_HPVOL_HM_SFT                  13
+#define RT5645_IRQ_PSV_MODE                    (0x1 << 12)
 
 /* SPK Left Mixer Control (0x46) */
 #define RT5645_G_RM_L_SM_L_MASK                        (0x3 << 14)
 #define RT5645_PWR_CLK25M_SFT                  4
 #define RT5645_PWR_CLK25M_PD                   (0x0 << 4)
 #define RT5645_PWR_CLK25M_PU                   (0x1 << 4)
+#define RT5645_IRQ_CLK_MCLK                    (0x0 << 3)
+#define RT5645_IRQ_CLK_INT                     (0x1 << 3)
+#define RT5645_JD1_MODE_MASK                   (0x3 << 0)
+#define RT5645_JD1_MODE_0                      (0x0 << 0)
+#define RT5645_JD1_MODE_1                      (0x1 << 0)
+#define RT5645_JD1_MODE_2                      (0x2 << 0)
 
 /* VAD Control 4 (0x9d) */
 #define RT5645_VAD_SEL_MASK                    (0x3 << 8)
 #define RT5645_OT_P_SFT                                10
 #define RT5645_OT_P_NOR                                (0x0 << 10)
 #define RT5645_OT_P_INV                                (0x1 << 10)
+#define RT5645_IRQ_JD_1_1_EN                   (0x1 << 9)
 
 /* IRQ Control 2 (0xbe) */
 #define RT5645_IRQ_MB1_OC_MASK                 (0x1 << 15)
 #define RT5645_M_BB_HPF_R_SFT                  6
 #define RT5645_G_BB_BST_MASK                   (0x3f)
 #define RT5645_G_BB_BST_SFT                    0
+#define RT5645_G_BB_BST_25DB                   0x14
 
 /* MP3 Plus Control 1 (0xd0) */
 #define RT5645_M_MP3_L_MASK                    (0x1 << 15)
@@ -2116,6 +2125,10 @@ enum {
 #define RT5645_RXDP2_SEL_ADC                   (0x1 << 3)
 #define RT5645_RXDP2_SEL_SFT                   (3)
 
+/* General Control3 (0xfc) */
+#define RT5645_JD_PSV_MODE                     (0x1 << 12)
+#define RT5645_IRQ_CLK_GATE_CTRL               (0x1 << 11)
+#define RT5645_MICINDET_MANU                   (0x1 << 7)
 
 /* Vendor ID (0xfd) */
 #define RT5645_VER_C                           0x2
@@ -2167,7 +2180,9 @@ struct rt5645_priv {
        struct rt5645_platform_data pdata;
        struct regmap *regmap;
        struct i2c_client *i2c;
-       struct snd_soc_jack *jack;
+       struct snd_soc_jack *hp_jack;
+       struct snd_soc_jack *mic_jack;
+       struct delayed_work jack_detect_work;
 
        int sysclk;
        int sysclk_src;
@@ -2181,6 +2196,6 @@ struct rt5645_priv {
 };
 
 int rt5645_set_jack_detect(struct snd_soc_codec *codec,
-       struct snd_soc_jack *jack);
+       struct snd_soc_jack *hp_jack, struct snd_soc_jack *mic_jack);
 
 #endif /* __RT5645_H__ */
index 9bd8b4f..8a0833d 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
+#include <linux/acpi.h>
 #include <linux/spi/spi.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -575,6 +576,18 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
 
 }
 
+static int can_use_asrc(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 rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
+       if (rt5670->sysclk > rt5670->lrck[RT5670_AIF1] * 384)
+               return 1;
+
+       return 0;
+}
+
 /* Digital Mixer */
 static const struct snd_kcontrol_new rt5670_sto1_adc_l_mix[] = {
        SOC_DAPM_SINGLE("ADC1 Switch", RT5670_STO1_ADC_MIXER,
@@ -1281,6 +1294,14 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
                              9, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY_S("DAC MONO R ASRC", 1, RT5670_ASRC_1,
                              8, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC STO1 ASRC", 1, RT5670_ASRC_1,
+                             7, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC STO2 ASRC", 1, RT5670_ASRC_1,
+                             6, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC MONO L ASRC", 1, RT5670_ASRC_1,
+                             5, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY_S("DMIC MONO R ASRC", 1, RT5670_ASRC_1,
+                             4, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY_S("ADC STO1 ASRC", 1, RT5670_ASRC_1,
                              3, 0, NULL, 0),
        SND_SOC_DAPM_SUPPLY_S("ADC STO2 ASRC", 1, RT5670_ASRC_1,
@@ -1595,29 +1616,40 @@ static const struct snd_soc_dapm_widget rt5670_dapm_widgets[] = {
        /* PDM */
        SND_SOC_DAPM_SUPPLY("PDM1 Power", RT5670_PWR_DIG2,
                RT5670_PWR_PDM1_BIT, 0, NULL, 0),
-       SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5670_PWR_DIG2,
-               RT5670_PWR_PDM2_BIT, 0, NULL, 0),
 
        SND_SOC_DAPM_MUX("PDM1 L Mux", RT5670_PDM_OUT_CTRL,
                         RT5670_M_PDM1_L_SFT, 1, &rt5670_pdm1_l_mux),
        SND_SOC_DAPM_MUX("PDM1 R Mux", RT5670_PDM_OUT_CTRL,
                         RT5670_M_PDM1_R_SFT, 1, &rt5670_pdm1_r_mux),
-       SND_SOC_DAPM_MUX("PDM2 L Mux", RT5670_PDM_OUT_CTRL,
-                        RT5670_M_PDM2_L_SFT, 1, &rt5670_pdm2_l_mux),
-       SND_SOC_DAPM_MUX("PDM2 R Mux", RT5670_PDM_OUT_CTRL,
-                        RT5670_M_PDM2_R_SFT, 1, &rt5670_pdm2_r_mux),
 
        /* Output Lines */
        SND_SOC_DAPM_OUTPUT("HPOL"),
        SND_SOC_DAPM_OUTPUT("HPOR"),
        SND_SOC_DAPM_OUTPUT("LOUTL"),
        SND_SOC_DAPM_OUTPUT("LOUTR"),
+};
+
+static const struct snd_soc_dapm_widget rt5670_specific_dapm_widgets[] = {
+       SND_SOC_DAPM_SUPPLY("PDM2 Power", RT5670_PWR_DIG2,
+               RT5670_PWR_PDM2_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_MUX("PDM2 L Mux", RT5670_PDM_OUT_CTRL,
+                        RT5670_M_PDM2_L_SFT, 1, &rt5670_pdm2_l_mux),
+       SND_SOC_DAPM_MUX("PDM2 R Mux", RT5670_PDM_OUT_CTRL,
+                        RT5670_M_PDM2_R_SFT, 1, &rt5670_pdm2_r_mux),
        SND_SOC_DAPM_OUTPUT("PDM1L"),
        SND_SOC_DAPM_OUTPUT("PDM1R"),
        SND_SOC_DAPM_OUTPUT("PDM2L"),
        SND_SOC_DAPM_OUTPUT("PDM2R"),
 };
 
+static const struct snd_soc_dapm_widget rt5672_specific_dapm_widgets[] = {
+       SND_SOC_DAPM_PGA("SPO Amp", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_OUTPUT("SPOLP"),
+       SND_SOC_DAPM_OUTPUT("SPOLN"),
+       SND_SOC_DAPM_OUTPUT("SPORP"),
+       SND_SOC_DAPM_OUTPUT("SPORN"),
+};
+
 static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
        { "ADC Stereo1 Filter", NULL, "ADC STO1 ASRC", is_using_asrc },
        { "ADC Stereo2 Filter", NULL, "ADC STO2 ASRC", is_using_asrc },
@@ -1626,9 +1658,13 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
        { "DAC Mono Left Filter", NULL, "DAC MONO L ASRC", is_using_asrc },
        { "DAC Mono Right Filter", NULL, "DAC MONO R ASRC", is_using_asrc },
        { "DAC Stereo1 Filter", NULL, "DAC STO ASRC", is_using_asrc },
+       { "Stereo1 DMIC Mux", NULL, "DMIC STO1 ASRC", can_use_asrc },
+       { "Stereo2 DMIC Mux", NULL, "DMIC STO2 ASRC", can_use_asrc },
+       { "Mono DMIC L Mux", NULL, "DMIC MONO L ASRC", can_use_asrc },
+       { "Mono DMIC R Mux", NULL, "DMIC MONO R ASRC", can_use_asrc },
 
-       { "I2S1", NULL, "I2S1 ASRC" },
-       { "I2S2", NULL, "I2S2 ASRC" },
+       { "I2S1", NULL, "I2S1 ASRC", can_use_asrc},
+       { "I2S2", NULL, "I2S2 ASRC", can_use_asrc},
 
        { "DMIC1", NULL, "DMIC L1" },
        { "DMIC1", NULL, "DMIC R1" },
@@ -1970,12 +2006,6 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
        { "PDM1 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
        { "PDM1 R Mux", "Mono DAC", "Mono DAC MIXR" },
        { "PDM1 R Mux", NULL, "PDM1 Power" },
-       { "PDM2 L Mux", "Stereo DAC", "Stereo DAC MIXL" },
-       { "PDM2 L Mux", "Mono DAC", "Mono DAC MIXL" },
-       { "PDM2 L Mux", NULL, "PDM2 Power" },
-       { "PDM2 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
-       { "PDM2 R Mux", "Mono DAC", "Mono DAC MIXR" },
-       { "PDM2 R Mux", NULL, "PDM2 Power" },
 
        { "HP Amp", NULL, "HPO MIX" },
        { "HP Amp", NULL, "Mic Det Power" },
@@ -1993,13 +2023,30 @@ static const struct snd_soc_dapm_route rt5670_dapm_routes[] = {
        { "LOUTR", NULL, "LOUT R Playback" },
        { "LOUTL", NULL, "Improve HP Amp Drv" },
        { "LOUTR", NULL, "Improve HP Amp Drv" },
+};
 
+static const struct snd_soc_dapm_route rt5670_specific_dapm_routes[] = {
+       { "PDM2 L Mux", "Stereo DAC", "Stereo DAC MIXL" },
+       { "PDM2 L Mux", "Mono DAC", "Mono DAC MIXL" },
+       { "PDM2 L Mux", NULL, "PDM2 Power" },
+       { "PDM2 R Mux", "Stereo DAC", "Stereo DAC MIXR" },
+       { "PDM2 R Mux", "Mono DAC", "Mono DAC MIXR" },
+       { "PDM2 R Mux", NULL, "PDM2 Power" },
        { "PDM1L", NULL, "PDM1 L Mux" },
        { "PDM1R", NULL, "PDM1 R Mux" },
        { "PDM2L", NULL, "PDM2 L Mux" },
        { "PDM2R", NULL, "PDM2 R Mux" },
 };
 
+static const struct snd_soc_dapm_route rt5672_specific_dapm_routes[] = {
+       { "SPO Amp", NULL, "PDM1 L Mux" },
+       { "SPO Amp", NULL, "PDM1 R Mux" },
+       { "SPOLP", NULL, "SPO Amp" },
+       { "SPOLN", NULL, "SPO Amp" },
+       { "SPORP", NULL, "SPO Amp" },
+       { "SPORN", NULL, "SPO Amp" },
+};
+
 static int rt5670_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params, struct snd_soc_dai *dai)
 {
@@ -2287,6 +2334,8 @@ static int rt5670_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
 static int rt5670_set_bias_level(struct snd_soc_codec *codec,
                        enum snd_soc_bias_level level)
 {
+       struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
+
        switch (level) {
        case SND_SOC_BIAS_PREPARE:
                if (SND_SOC_BIAS_STANDBY == codec->dapm.bias_level) {
@@ -2308,16 +2357,27 @@ static int rt5670_set_bias_level(struct snd_soc_codec *codec,
                }
                break;
        case SND_SOC_BIAS_STANDBY:
-               snd_soc_write(codec, RT5670_PWR_DIG1, 0x0000);
-               snd_soc_write(codec, RT5670_PWR_DIG2, 0x0001);
-               snd_soc_write(codec, RT5670_PWR_VOL, 0x0000);
-               snd_soc_write(codec, RT5670_PWR_MIXER, 0x0001);
-               snd_soc_write(codec, RT5670_PWR_ANLG1, 0x2800);
-               snd_soc_write(codec, RT5670_PWR_ANLG2, 0x0004);
-               snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0);
+               snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+                               RT5670_PWR_VREF1 | RT5670_PWR_VREF2 |
+                               RT5670_PWR_FV1 | RT5670_PWR_FV2, 0);
                snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
                                RT5670_LDO_SEL_MASK, 0x1);
                break;
+       case SND_SOC_BIAS_OFF:
+               if (rt5670->pdata.jd_mode)
+                       snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+                               RT5670_PWR_VREF1 | RT5670_PWR_MB |
+                               RT5670_PWR_BG | RT5670_PWR_VREF2 |
+                               RT5670_PWR_FV1 | RT5670_PWR_FV2,
+                               RT5670_PWR_MB | RT5670_PWR_BG);
+               else
+                       snd_soc_update_bits(codec, RT5670_PWR_ANLG1,
+                               RT5670_PWR_VREF1 | RT5670_PWR_MB |
+                               RT5670_PWR_BG | RT5670_PWR_VREF2 |
+                               RT5670_PWR_FV1 | RT5670_PWR_FV2, 0);
+
+               snd_soc_update_bits(codec, RT5670_DIG_MISC, 0x1, 0x0);
+               break;
 
        default:
                break;
@@ -2331,6 +2391,29 @@ static int rt5670_probe(struct snd_soc_codec *codec)
 {
        struct rt5670_priv *rt5670 = snd_soc_codec_get_drvdata(codec);
 
+       switch (snd_soc_read(codec, RT5670_RESET) & RT5670_ID_MASK) {
+       case RT5670_ID_5670:
+       case RT5670_ID_5671:
+               snd_soc_dapm_new_controls(&codec->dapm,
+                       rt5670_specific_dapm_widgets,
+                       ARRAY_SIZE(rt5670_specific_dapm_widgets));
+               snd_soc_dapm_add_routes(&codec->dapm,
+                       rt5670_specific_dapm_routes,
+                       ARRAY_SIZE(rt5670_specific_dapm_routes));
+               break;
+       case RT5670_ID_5672:
+               snd_soc_dapm_new_controls(&codec->dapm,
+                       rt5672_specific_dapm_widgets,
+                       ARRAY_SIZE(rt5672_specific_dapm_widgets));
+               snd_soc_dapm_add_routes(&codec->dapm,
+                       rt5672_specific_dapm_routes,
+                       ARRAY_SIZE(rt5672_specific_dapm_routes));
+               break;
+       default:
+               dev_err(codec->dev,
+                       "The driver is for RT5670 RT5671 or RT5672 only\n");
+               return -ENODEV;
+       }
        rt5670->codec = codec;
 
        return 0;
@@ -2452,10 +2535,20 @@ static const struct regmap_config rt5670_regmap = {
 
 static const struct i2c_device_id rt5670_i2c_id[] = {
        { "rt5670", 0 },
+       { "rt5671", 0 },
+       { "rt5672", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, rt5670_i2c_id);
 
+#ifdef CONFIG_ACPI
+static struct acpi_device_id rt5670_acpi_match[] = {
+       { "10EC5670", 0},
+       { },
+};
+MODULE_DEVICE_TABLE(acpi, rt5670_acpi_match);
+#endif
+
 static int rt5670_i2c_probe(struct i2c_client *i2c,
                    const struct i2c_device_id *id)
 {
@@ -2644,6 +2737,7 @@ static struct i2c_driver rt5670_i2c_driver = {
        .driver = {
                .name = "rt5670",
                .owner = THIS_MODULE,
+               .acpi_match_table = ACPI_PTR(rt5670_acpi_match),
        },
        .probe = rt5670_i2c_probe,
        .remove   = rt5670_i2c_remove,
index a0b5c85..d11b9c2 100644 (file)
 #define RT5670_R_VOL_MASK                      (0x3f)
 #define RT5670_R_VOL_SFT                       0
 
+/* SW Reset & Device ID (0x00) */
+#define RT5670_ID_MASK                         (0x3 << 1)
+#define RT5670_ID_5670                         (0x0 << 1)
+#define RT5670_ID_5672                         (0x1 << 1)
+#define RT5670_ID_5671                         (0x2 << 1)
+
 /* Combo Jack Control 1 (0x0a) */
 #define RT5670_CBJ_BST1_MASK                   (0xf << 12)
 #define RT5670_CBJ_BST1_SFT                    (12)
diff --git a/sound/soc/codecs/rt5677-spi.c b/sound/soc/codecs/rt5677-spi.c
new file mode 100644 (file)
index 0000000..ef6348c
--- /dev/null
@@ -0,0 +1,130 @@
+/*
+ * rt5677-spi.c  --  RT5677 ALSA SoC audio codec driver
+ *
+ * Copyright 2013 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/module.h>
+#include <linux/input.h>
+#include <linux/spi/spi.h>
+#include <linux/device.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/interrupt.h>
+#include <linux/irq.h>
+#include <linux/slab.h>
+#include <linux/gpio.h>
+#include <linux/sched.h>
+#include <linux/kthread.h>
+#include <linux/uaccess.h>
+#include <linux/miscdevice.h>
+#include <linux/regulator/consumer.h>
+#include <linux/pm_qos.h>
+#include <linux/sysfs.h>
+#include <linux/clk.h>
+#include <linux/firmware.h>
+
+#include "rt5677-spi.h"
+
+static struct spi_device *g_spi;
+
+/**
+ * rt5677_spi_write - Write data to SPI.
+ * @txbuf: Data Buffer for writing.
+ * @len: Data length.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5677_spi_write(u8 *txbuf, size_t len)
+{
+       int status;
+
+       status = spi_write(g_spi, txbuf, len);
+
+       if (status)
+               dev_err(&g_spi->dev, "rt5677_spi_write error %d\n", status);
+
+       return status;
+}
+EXPORT_SYMBOL_GPL(rt5677_spi_write);
+
+/**
+ * rt5677_spi_burst_write - Write data to SPI by rt5677 dsp memory address.
+ * @addr: Start address.
+ * @txbuf: Data Buffer for writng.
+ * @len: Data length, it must be a multiple of 8.
+ *
+ *
+ * Returns true for success.
+ */
+int rt5677_spi_burst_write(u32 addr, const struct firmware *fw)
+{
+       u8 spi_cmd = RT5677_SPI_CMD_BURST_WRITE;
+       u8 *write_buf;
+       unsigned int i, end, offset = 0;
+
+       write_buf = kmalloc(RT5677_SPI_BUF_LEN + 6, GFP_KERNEL);
+
+       if (write_buf == NULL)
+               return -ENOMEM;
+
+       while (offset < fw->size) {
+               if (offset + RT5677_SPI_BUF_LEN <= fw->size)
+                       end = RT5677_SPI_BUF_LEN;
+               else
+                       end = fw->size % RT5677_SPI_BUF_LEN;
+
+               write_buf[0] = spi_cmd;
+               write_buf[1] = ((addr + offset) & 0xff000000) >> 24;
+               write_buf[2] = ((addr + offset) & 0x00ff0000) >> 16;
+               write_buf[3] = ((addr + offset) & 0x0000ff00) >> 8;
+               write_buf[4] = ((addr + offset) & 0x000000ff) >> 0;
+
+               for (i = 0; i < end; i += 8) {
+                       write_buf[i + 12] = fw->data[offset + i + 0];
+                       write_buf[i + 11] = fw->data[offset + i + 1];
+                       write_buf[i + 10] = fw->data[offset + i + 2];
+                       write_buf[i +  9] = fw->data[offset + i + 3];
+                       write_buf[i +  8] = fw->data[offset + i + 4];
+                       write_buf[i +  7] = fw->data[offset + i + 5];
+                       write_buf[i +  6] = fw->data[offset + i + 6];
+                       write_buf[i +  5] = fw->data[offset + i + 7];
+               }
+
+               write_buf[end + 5] = spi_cmd;
+
+               rt5677_spi_write(write_buf, end + 6);
+
+               offset += RT5677_SPI_BUF_LEN;
+       }
+
+       kfree(write_buf);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(rt5677_spi_burst_write);
+
+static int rt5677_spi_probe(struct spi_device *spi)
+{
+       g_spi = spi;
+       return 0;
+}
+
+static struct spi_driver rt5677_spi_driver = {
+       .driver = {
+               .name = "rt5677",
+               .owner = THIS_MODULE,
+       },
+       .probe = rt5677_spi_probe,
+};
+module_spi_driver(rt5677_spi_driver);
+
+MODULE_DESCRIPTION("ASoC RT5677 SPI driver");
+MODULE_AUTHOR("Oder Chiou <oder_chiou@realtek.com>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/rt5677-spi.h b/sound/soc/codecs/rt5677-spi.h
new file mode 100644 (file)
index 0000000..ec41b2b
--- /dev/null
@@ -0,0 +1,21 @@
+/*
+ * rt5677-spi.h  --  RT5677 ALSA SoC audio codec driver
+ *
+ * Copyright 2013 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.
+ */
+
+#ifndef __RT5677_SPI_H__
+#define __RT5677_SPI_H__
+
+#define RT5677_SPI_BUF_LEN 240
+#define RT5677_SPI_CMD_BURST_WRITE 0x05
+
+int rt5677_spi_write(u8 *txbuf, size_t len);
+int rt5677_spi_burst_write(u32 addr, const struct firmware *fw);
+
+#endif /* __RT5677_SPI_H__ */
index 16aa4d9..81fe146 100644 (file)
@@ -20,6 +20,7 @@
 #include <linux/i2c.h>
 #include <linux/platform_device.h>
 #include <linux/spi/spi.h>
+#include <linux/firmware.h>
 #include <linux/gpio.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -31,6 +32,7 @@
 
 #include "rl6231.h"
 #include "rt5677.h"
+#include "rt5677-spi.h"
 
 #define RT5677_DEVICE_ID 0x6327
 
@@ -53,12 +55,13 @@ static const struct regmap_range_cfg rt5677_ranges[] = {
 };
 
 static const struct reg_default init_list[] = {
+       {RT5677_ASRC_12,        0x0018},
        {RT5677_PR_BASE + 0x3d, 0x364d},
-       {RT5677_PR_BASE + 0x17, 0x4fc0},
-       {RT5677_PR_BASE + 0x13, 0x0312},
-       {RT5677_PR_BASE + 0x1e, 0x0000},
-       {RT5677_PR_BASE + 0x12, 0x0eaa},
-       {RT5677_PR_BASE + 0x14, 0x018a},
+       {RT5677_PR_BASE + 0x17, 0x4fc0},
+       {RT5677_PR_BASE + 0x13, 0x0312},
+       {RT5677_PR_BASE + 0x1e, 0x0000},
+       {RT5677_PR_BASE + 0x12, 0x0eaa},
+       {RT5677_PR_BASE + 0x14, 0x018a},
 };
 #define RT5677_INIT_REG_LEN ARRAY_SIZE(init_list)
 
@@ -171,7 +174,7 @@ static const struct reg_default rt5677_reg[] = {
        {RT5677_ASRC_9                  , 0x0000},
        {RT5677_ASRC_10                 , 0x0000},
        {RT5677_ASRC_11                 , 0x0000},
-       {RT5677_ASRC_12                 , 0x0008},
+       {RT5677_ASRC_12                 , 0x0018},
        {RT5677_ASRC_13                 , 0x0000},
        {RT5677_ASRC_14                 , 0x0000},
        {RT5677_ASRC_15                 , 0x0000},
@@ -537,10 +540,232 @@ static bool rt5677_readable_register(struct device *dev, unsigned int reg)
        }
 }
 
+/**
+ * rt5677_dsp_mode_i2c_write_addr - Write value to address on DSP mode.
+ * @rt5677: Private Data.
+ * @addr: Address index.
+ * @value: Address data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_write_addr(struct rt5677_priv *rt5677,
+               unsigned int addr, unsigned int value, unsigned int opcode)
+{
+       struct snd_soc_codec *codec = rt5677->codec;
+       int ret;
+
+       mutex_lock(&rt5677->dsp_cmd_lock);
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
+               addr >> 16);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret);
+               goto err;
+       }
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB,
+               addr & 0xffff);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret);
+               goto err;
+       }
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB,
+               value >> 16);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set data msb value: %d\n", ret);
+               goto err;
+       }
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB,
+               value & 0xffff);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set data lsb value: %d\n", ret);
+               goto err;
+       }
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE,
+               opcode);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set op code value: %d\n", ret);
+               goto err;
+       }
+
+err:
+       mutex_unlock(&rt5677->dsp_cmd_lock);
+
+       return ret;
+}
+
+/**
+ * rt5677_dsp_mode_i2c_read_addr - Read value from address on DSP mode.
+ * rt5677: Private Data.
+ * @addr: Address index.
+ * @value: Address data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_read_addr(
+       struct rt5677_priv *rt5677, unsigned int addr, unsigned int *value)
+{
+       struct snd_soc_codec *codec = rt5677->codec;
+       int ret;
+       unsigned int msb, lsb;
+
+       mutex_lock(&rt5677->dsp_cmd_lock);
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_MSB,
+               addr >> 16);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set addr msb value: %d\n", ret);
+               goto err;
+       }
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_ADDR_LSB,
+               addr & 0xffff);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set addr lsb value: %d\n", ret);
+               goto err;
+       }
+
+       ret = regmap_write(rt5677->regmap_physical, RT5677_DSP_I2C_OP_CODE,
+               0x0002);
+       if (ret < 0) {
+               dev_err(codec->dev, "Failed to set op code value: %d\n", ret);
+               goto err;
+       }
+
+       regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_MSB, &msb);
+       regmap_read(rt5677->regmap_physical, RT5677_DSP_I2C_DATA_LSB, &lsb);
+       *value = (msb << 16) | lsb;
+
+err:
+       mutex_unlock(&rt5677->dsp_cmd_lock);
+
+       return ret;
+}
+
+/**
+ * rt5677_dsp_mode_i2c_write - Write register on DSP mode.
+ * rt5677: Private Data.
+ * @reg: Register index.
+ * @value: Register data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_write(struct rt5677_priv *rt5677,
+               unsigned int reg, unsigned int value)
+{
+       return rt5677_dsp_mode_i2c_write_addr(rt5677, 0x18020000 + reg * 2,
+               value, 0x0001);
+}
+
+/**
+ * rt5677_dsp_mode_i2c_read - Read register on DSP mode.
+ * @codec: SoC audio codec device.
+ * @reg: Register index.
+ * @value: Register data.
+ *
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int rt5677_dsp_mode_i2c_read(
+       struct rt5677_priv *rt5677, unsigned int reg, unsigned int *value)
+{
+       int ret = rt5677_dsp_mode_i2c_read_addr(rt5677, 0x18020000 + reg * 2,
+               value);
+
+       *value &= 0xffff;
+
+       return ret;
+}
+
+static void rt5677_set_dsp_mode(struct snd_soc_codec *codec, bool on)
+{
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       if (on) {
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x2);
+               rt5677->is_dsp_mode = true;
+       } else {
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x2, 0x0);
+               rt5677->is_dsp_mode = false;
+       }
+}
+
+static int rt5677_set_dsp_vad(struct snd_soc_codec *codec, bool on)
+{
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+       static bool activity;
+       int ret;
+
+       if (on && !activity) {
+               activity = true;
+
+               regcache_cache_only(rt5677->regmap, false);
+               regcache_cache_bypass(rt5677->regmap, true);
+
+               regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x1, 0x1);
+               regmap_update_bits(rt5677->regmap,
+                       RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0f00);
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+                       RT5677_LDO1_SEL_MASK, 0x0);
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
+                       RT5677_PWR_LDO1, RT5677_PWR_LDO1);
+               regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK1,
+                       RT5677_MCLK_SRC_MASK, RT5677_MCLK2_SRC);
+               regmap_update_bits(rt5677->regmap, RT5677_GLB_CLK2,
+                       RT5677_PLL2_PR_SRC_MASK | RT5677_DSP_CLK_SRC_MASK,
+                       RT5677_PLL2_PR_SRC_MCLK2 | RT5677_DSP_CLK_SRC_BYPASS);
+               regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x07ff);
+               regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x07fd);
+               rt5677_set_dsp_mode(codec, true);
+
+               ret = request_firmware(&rt5677->fw1, RT5677_FIRMWARE1,
+                       codec->dev);
+               if (ret == 0) {
+                       rt5677_spi_burst_write(0x50000000, rt5677->fw1);
+                       release_firmware(rt5677->fw1);
+               }
+
+               ret = request_firmware(&rt5677->fw2, RT5677_FIRMWARE2,
+                       codec->dev);
+               if (ret == 0) {
+                       rt5677_spi_burst_write(0x60000000, rt5677->fw2);
+                       release_firmware(rt5677->fw2);
+               }
+
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x0);
+
+               regcache_cache_bypass(rt5677->regmap, false);
+               regcache_cache_only(rt5677->regmap, true);
+       } else if (!on && activity) {
+               activity = false;
+
+               regcache_cache_only(rt5677->regmap, false);
+               regcache_cache_bypass(rt5677->regmap, true);
+
+               regmap_update_bits(rt5677->regmap, RT5677_PWR_DSP1, 0x1, 0x1);
+               rt5677_set_dsp_mode(codec, false);
+               regmap_write(rt5677->regmap, RT5677_PWR_DSP1, 0x0001);
+
+               regmap_write(rt5677->regmap, RT5677_RESET, 0x10ec);
+
+               regcache_cache_bypass(rt5677->regmap, false);
+               regcache_mark_dirty(rt5677->regmap);
+               regcache_sync(rt5677->regmap);
+       }
+
+       return 0;
+}
+
 static const DECLARE_TLV_DB_SCALE(out_vol_tlv, -4650, 150, 0);
-static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -65625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(dac_vol_tlv, -6525, 75, 0);
 static const DECLARE_TLV_DB_SCALE(in_vol_tlv, -3450, 150, 0);
-static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -17625, 375, 0);
+static const DECLARE_TLV_DB_SCALE(adc_vol_tlv, -1725, 75, 0);
 static const DECLARE_TLV_DB_SCALE(adc_bst_tlv, 0, 1200, 0);
 static const DECLARE_TLV_DB_SCALE(st_vol_tlv, -4650, 150, 0);
 
@@ -556,6 +781,31 @@ static unsigned int bst_tlv[] = {
        8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0),
 };
 
+static int rt5677_dsp_vad_get(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.integer.value[0] = rt5677->dsp_vad_en;
+
+       return 0;
+}
+
+static int rt5677_dsp_vad_put(struct snd_kcontrol *kcontrol,
+               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       rt5677->dsp_vad_en = !!ucontrol->value.integer.value[0];
+
+       if (codec->dapm.bias_level == SND_SOC_BIAS_OFF)
+               rt5677_set_dsp_vad(codec, rt5677->dsp_vad_en);
+
+       return 0;
+}
+
 static const struct snd_kcontrol_new rt5677_snd_controls[] = {
        /* OUTPUT Control */
        SOC_SINGLE("OUT1 Playback Switch", RT5677_LOUT1,
@@ -567,13 +817,13 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
 
        /* DAC Digital Volume */
        SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5677_DAC1_DIG_VOL,
-               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
        SOC_DOUBLE_TLV("DAC2 Playback Volume", RT5677_DAC2_DIG_VOL,
-               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
        SOC_DOUBLE_TLV("DAC3 Playback Volume", RT5677_DAC3_DIG_VOL,
-               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
        SOC_DOUBLE_TLV("DAC4 Playback Volume", RT5677_DAC4_DIG_VOL,
-               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 175, 0, dac_vol_tlv),
+               RT5677_L_VOL_SFT, RT5677_R_VOL_SFT, 87, 0, dac_vol_tlv),
 
        /* IN1/IN2 Control */
        SOC_SINGLE_TLV("IN1 Boost", RT5677_IN1, RT5677_BST_SFT1, 8, 0, bst_tlv),
@@ -592,19 +842,19 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
                RT5677_L_MUTE_SFT, RT5677_R_MUTE_SFT, 1, 1),
 
        SOC_DOUBLE_TLV("ADC1 Capture Volume", RT5677_STO1_ADC_DIG_VOL,
-               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
                adc_vol_tlv),
        SOC_DOUBLE_TLV("ADC2 Capture Volume", RT5677_STO2_ADC_DIG_VOL,
-               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
                adc_vol_tlv),
        SOC_DOUBLE_TLV("ADC3 Capture Volume", RT5677_STO3_ADC_DIG_VOL,
-               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
                adc_vol_tlv),
        SOC_DOUBLE_TLV("ADC4 Capture Volume", RT5677_STO4_ADC_DIG_VOL,
-               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 127, 0,
+               RT5677_STO1_ADC_L_VOL_SFT, RT5677_STO1_ADC_R_VOL_SFT, 63, 0,
                adc_vol_tlv),
        SOC_DOUBLE_TLV("Mono ADC Capture Volume", RT5677_MONO_ADC_DIG_VOL,
-               RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 127, 0,
+               RT5677_MONO_ADC_L_VOL_SFT, RT5677_MONO_ADC_R_VOL_SFT, 63, 0,
                adc_vol_tlv),
 
        /* Sidetone Control */
@@ -627,6 +877,9 @@ static const struct snd_kcontrol_new rt5677_snd_controls[] = {
        SOC_DOUBLE_TLV("Mono ADC Boost Volume", RT5677_ADC_BST_CTRL2,
                RT5677_MONO_ADC_L_BST_SFT, RT5677_MONO_ADC_R_BST_SFT, 3, 0,
                adc_bst_tlv),
+
+       SOC_SINGLE_EXT("DSP VAD Switch", SND_SOC_NOPM, 0, 1, 0,
+               rt5677_dsp_vad_get, rt5677_dsp_vad_put),
 };
 
 /**
@@ -1086,7 +1339,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_ib45_bypass_src_mux =
        SOC_DAPM_ENUM("IB45 Bypass Source", rt5677_ib45_bypass_src_enum);
 
-/* Stereo ADC Source 2 */ /* MX-27 MX26  MX25 [11:10] */
+/* Stereo ADC Source 2 */ /* MX-27 MX26 MX25 [11:10] */
 static const char * const rt5677_stereo_adc2_src[] = {
        "DD MIX1", "DMIC", "Stereo DAC MIX"
 };
@@ -1171,7 +1424,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_sto2_adc_lr_mux =
        SOC_DAPM_ENUM("Stereo2 ADC LR Source", rt5677_stereo2_adc_lr_enum);
 
-/* Stereo1 ADC Source 1 */ /* MX-27 MX26  MX25 [13:12] */
+/* Stereo1 ADC Source 1 */ /* MX-27 MX26 MX25 [13:12] */
 static const char * const rt5677_stereo_adc1_src[] = {
        "DD MIX1", "ADC1/2", "Stereo DAC MIX"
 };
@@ -1443,7 +1696,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_pdm2_r_mux =
        SOC_DAPM_ENUM("PDM2 Source", rt5677_pdm2_r_enum);
 
-/* TDM IF1/2 SLB ADC1 Data Selection */ /* MX-3C MX-41 [5:4] MX-08 [1:0]*/
+/* TDM IF1/2 SLB ADC1 Data Selection */ /* MX-3C MX-41 [5:4] MX-08 [1:0] */
 static const char * const rt5677_if12_adc1_src[] = {
        "STO1 ADC MIX", "OB01", "VAD ADC"
 };
@@ -1521,7 +1774,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_slb_adc3_mux =
        SOC_DAPM_ENUM("SLB ADC3 Source", rt5677_slb_adc3_enum);
 
-/* TDM IF1/2 SLB ADC4 Data Selection */ /* MX-3C MX-41 [11:10]  MX-08 [7:6] */
+/* TDM IF1/2 SLB ADC4 Data Selection */ /* MX-3C MX-41 [11:10] MX-08 [7:6] */
 static const char * const rt5677_if12_adc4_src[] = {
        "STO4 ADC MIX", "OB67", "OB01"
 };
@@ -1547,7 +1800,7 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_slb_adc4_mux =
        SOC_DAPM_ENUM("SLB ADC4 Source", rt5677_slb_adc4_enum);
 
-/* Interface3/4 ADC Data Input */ /* MX-2F [3:0] MX-30 [7:4]*/
+/* Interface3/4 ADC Data Input */ /* MX-2F [3:0] MX-30 [7:4] */
 static const char * const rt5677_if34_adc_src[] = {
        "STO1 ADC MIX", "STO2 ADC MIX", "STO3 ADC MIX", "STO4 ADC MIX",
        "MONO ADC MIX", "OB01", "OB23", "VAD ADC"
@@ -1567,6 +1820,213 @@ static SOC_ENUM_SINGLE_DECL(
 static const struct snd_kcontrol_new rt5677_if4_adc_mux =
        SOC_DAPM_ENUM("IF4 ADC Source", rt5677_if4_adc_enum);
 
+/* TDM IF1/2 ADC Data Selection */ /* MX-3B MX-40 [7:6][5:4][3:2][1:0] */
+static const char * const rt5677_if12_adc_swap_src[] = {
+       "L/R", "R/L", "L/L", "R/R"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc1_swap_enum, RT5677_TDM1_CTRL1,
+       RT5677_IF1_ADC1_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc1_swap_mux =
+       SOC_DAPM_ENUM("IF1 ADC1 Swap Source", rt5677_if1_adc1_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc2_swap_enum, RT5677_TDM1_CTRL1,
+       RT5677_IF1_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc2_swap_mux =
+       SOC_DAPM_ENUM("IF1 ADC2 Swap Source", rt5677_if1_adc2_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc3_swap_enum, RT5677_TDM1_CTRL1,
+       RT5677_IF1_ADC3_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc3_swap_mux =
+       SOC_DAPM_ENUM("IF1 ADC3 Swap Source", rt5677_if1_adc3_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc4_swap_enum, RT5677_TDM1_CTRL1,
+       RT5677_IF1_ADC4_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc4_swap_mux =
+       SOC_DAPM_ENUM("IF1 ADC4 Swap Source", rt5677_if1_adc4_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc1_swap_enum, RT5677_TDM2_CTRL1,
+       RT5677_IF1_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc1_swap_mux =
+       SOC_DAPM_ENUM("IF1 ADC2 Swap Source", rt5677_if2_adc1_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc2_swap_enum, RT5677_TDM2_CTRL1,
+       RT5677_IF2_ADC2_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc2_swap_mux =
+       SOC_DAPM_ENUM("IF2 ADC2 Swap Source", rt5677_if2_adc2_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc3_swap_enum, RT5677_TDM2_CTRL1,
+       RT5677_IF2_ADC3_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc3_swap_mux =
+       SOC_DAPM_ENUM("IF2 ADC3 Swap Source", rt5677_if2_adc3_swap_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc4_swap_enum, RT5677_TDM2_CTRL1,
+       RT5677_IF2_ADC4_SWAP_SFT, rt5677_if12_adc_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc4_swap_mux =
+       SOC_DAPM_ENUM("IF2 ADC4 Swap Source", rt5677_if2_adc4_swap_enum);
+
+/* TDM IF1 ADC Data Selection */ /* MX-3C [2:0] */
+static const char * const rt5677_if1_adc_tdm_swap_src[] = {
+       "1/2/3/4", "2/1/3/4", "2/3/1/4", "4/1/2/3", "1/3/2/4", "1/4/2/3",
+       "3/1/2/4", "3/4/1/2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_adc_tdm_swap_enum, RT5677_TDM1_CTRL2,
+       RT5677_IF1_ADC_CTRL_SFT, rt5677_if1_adc_tdm_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if1_adc_tdm_swap_mux =
+       SOC_DAPM_ENUM("IF1 ADC TDM Swap Source", rt5677_if1_adc_tdm_swap_enum);
+
+/* TDM IF2 ADC Data Selection */ /* MX-41[2:0] */
+static const char * const rt5677_if2_adc_tdm_swap_src[] = {
+       "1/2/3/4", "2/1/3/4", "3/1/2/4", "4/1/2/3", "1/3/2/4", "1/4/2/3",
+       "2/3/1/4", "3/4/1/2"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_adc_tdm_swap_enum, RT5677_TDM2_CTRL2,
+       RT5677_IF2_ADC_CTRL_SFT, rt5677_if2_adc_tdm_swap_src);
+
+static const struct snd_kcontrol_new rt5677_if2_adc_tdm_swap_mux =
+       SOC_DAPM_ENUM("IF2 ADC TDM Swap Source", rt5677_if2_adc_tdm_swap_enum);
+
+/* TDM IF1/2 DAC Data Selection */ /* MX-3E[14:12][10:8][6:4][2:0]
+                                       MX-3F[14:12][10:8][6:4][2:0]
+                                       MX-43[14:12][10:8][6:4][2:0]
+                                       MX-44[14:12][10:8][6:4][2:0] */
+static const char * const rt5677_if12_dac_tdm_sel_src[] = {
+       "Slot0", "Slot1", "Slot2", "Slot3", "Slot4", "Slot5", "Slot6", "Slot7"
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac0_tdm_sel_enum, RT5677_TDM1_CTRL4,
+       RT5677_IF1_DAC0_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac0_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC0 TDM Source", rt5677_if1_dac0_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac1_tdm_sel_enum, RT5677_TDM1_CTRL4,
+       RT5677_IF1_DAC1_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac1_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC1 TDM Source", rt5677_if1_dac1_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac2_tdm_sel_enum, RT5677_TDM1_CTRL4,
+       RT5677_IF1_DAC2_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac2_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC2 TDM Source", rt5677_if1_dac2_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac3_tdm_sel_enum, RT5677_TDM1_CTRL4,
+       RT5677_IF1_DAC3_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac3_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC3 TDM Source", rt5677_if1_dac3_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac4_tdm_sel_enum, RT5677_TDM1_CTRL5,
+       RT5677_IF1_DAC4_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac4_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC4 TDM Source", rt5677_if1_dac4_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac5_tdm_sel_enum, RT5677_TDM1_CTRL5,
+       RT5677_IF1_DAC5_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac5_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC5 TDM Source", rt5677_if1_dac5_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac6_tdm_sel_enum, RT5677_TDM1_CTRL5,
+       RT5677_IF1_DAC6_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac6_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC6 TDM Source", rt5677_if1_dac6_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if1_dac7_tdm_sel_enum, RT5677_TDM1_CTRL5,
+       RT5677_IF1_DAC7_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if1_dac7_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF1 DAC7 TDM Source", rt5677_if1_dac7_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac0_tdm_sel_enum, RT5677_TDM2_CTRL4,
+       RT5677_IF2_DAC0_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac0_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC0 TDM Source", rt5677_if2_dac0_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac1_tdm_sel_enum, RT5677_TDM2_CTRL4,
+       RT5677_IF2_DAC1_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac1_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC1 TDM Source", rt5677_if2_dac1_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac2_tdm_sel_enum, RT5677_TDM2_CTRL4,
+       RT5677_IF2_DAC2_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac2_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC2 TDM Source", rt5677_if2_dac2_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac3_tdm_sel_enum, RT5677_TDM2_CTRL4,
+       RT5677_IF2_DAC3_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac3_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC3 TDM Source", rt5677_if2_dac3_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac4_tdm_sel_enum, RT5677_TDM2_CTRL5,
+       RT5677_IF2_DAC4_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac4_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC4 TDM Source", rt5677_if2_dac4_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac5_tdm_sel_enum, RT5677_TDM2_CTRL5,
+       RT5677_IF2_DAC5_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac5_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC5 TDM Source", rt5677_if2_dac5_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac6_tdm_sel_enum, RT5677_TDM2_CTRL5,
+       RT5677_IF2_DAC6_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac6_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC6 TDM Source", rt5677_if2_dac6_tdm_sel_enum);
+
+static SOC_ENUM_SINGLE_DECL(
+       rt5677_if2_dac7_tdm_sel_enum, RT5677_TDM2_CTRL5,
+       RT5677_IF2_DAC7_SFT, rt5677_if12_dac_tdm_sel_src);
+
+static const struct snd_kcontrol_new rt5677_if2_dac7_tdm_sel_mux =
+       SOC_DAPM_ENUM("IF2 DAC7 TDM Source", rt5677_if2_dac7_tdm_sel_enum);
+
 static int rt5677_bst1_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
@@ -1678,6 +2138,77 @@ static int rt5677_set_micbias1_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static int rt5677_if1_adc_tdm_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+       unsigned int value;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               regmap_read(rt5677->regmap, RT5677_TDM1_CTRL2, &value);
+               if (value & RT5677_IF1_ADC_CTRL_MASK)
+                       regmap_update_bits(rt5677->regmap, RT5677_TDM1_CTRL1,
+                               RT5677_IF1_ADC_MODE_MASK,
+                               RT5677_IF1_ADC_MODE_TDM);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5677_if2_adc_tdm_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+       unsigned int value;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               regmap_read(rt5677->regmap, RT5677_TDM2_CTRL2, &value);
+               if (value & RT5677_IF2_ADC_CTRL_MASK)
+                       regmap_update_bits(rt5677->regmap, RT5677_TDM2_CTRL1,
+                               RT5677_IF2_ADC_MODE_MASK,
+                               RT5677_IF2_ADC_MODE_TDM);
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
+static int rt5677_vref_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (codec->dapm.bias_level != SND_SOC_BIAS_ON &&
+                       !rt5677->is_vref_slow) {
+                       mdelay(20);
+                       regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+                               RT5677_PWR_FV1 | RT5677_PWR_FV2,
+                               RT5677_PWR_FV1 | RT5677_PWR_FV2);
+                       rt5677->is_vref_slow = true;
+               }
+               break;
+
+       default:
+               return 0;
+       }
+
+       return 0;
+}
+
 static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
        SND_SOC_DAPM_SUPPLY("PLL1", RT5677_PWR_ANLG2, RT5677_PWR_PLL1_BIT,
                0, rt5677_set_pll1_event, SND_SOC_DAPM_POST_PMU),
@@ -1837,10 +2368,8 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
        SND_SOC_DAPM_PGA("Stereo4 ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
        SND_SOC_DAPM_PGA("Sto2 ADC LR MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
        SND_SOC_DAPM_PGA("Mono ADC MIX", SND_SOC_NOPM, 0, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("IF1_ADC1", SND_SOC_NOPM, 0, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("IF1_ADC2", SND_SOC_NOPM, 0, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("IF1_ADC3", SND_SOC_NOPM, 0, 0, NULL, 0),
-       SND_SOC_DAPM_PGA("IF1_ADC4", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF1 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("IF2 ADC", SND_SOC_NOPM, 0, 0, NULL, 0),
 
        /* DSP */
        SND_SOC_DAPM_MUX("IB9 Mux", SND_SOC_NOPM, 0, 0,
@@ -1963,6 +2492,17 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
                        &rt5677_if1_adc3_mux),
        SND_SOC_DAPM_MUX("IF1 ADC4 Mux", SND_SOC_NOPM, 0, 0,
                        &rt5677_if1_adc4_mux),
+       SND_SOC_DAPM_MUX("IF1 ADC1 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc1_swap_mux),
+       SND_SOC_DAPM_MUX("IF1 ADC2 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc2_swap_mux),
+       SND_SOC_DAPM_MUX("IF1 ADC3 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc3_swap_mux),
+       SND_SOC_DAPM_MUX("IF1 ADC4 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc4_swap_mux),
+       SND_SOC_DAPM_MUX_E("IF1 ADC TDM Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_adc_tdm_swap_mux, rt5677_if1_adc_tdm_event,
+                       SND_SOC_DAPM_PRE_PMU),
        SND_SOC_DAPM_MUX("IF2 ADC1 Mux", SND_SOC_NOPM, 0, 0,
                        &rt5677_if2_adc1_mux),
        SND_SOC_DAPM_MUX("IF2 ADC2 Mux", SND_SOC_NOPM, 0, 0,
@@ -1971,6 +2511,17 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
                        &rt5677_if2_adc3_mux),
        SND_SOC_DAPM_MUX("IF2 ADC4 Mux", SND_SOC_NOPM, 0, 0,
                        &rt5677_if2_adc4_mux),
+       SND_SOC_DAPM_MUX("IF2 ADC1 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc1_swap_mux),
+       SND_SOC_DAPM_MUX("IF2 ADC2 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc2_swap_mux),
+       SND_SOC_DAPM_MUX("IF2 ADC3 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc3_swap_mux),
+       SND_SOC_DAPM_MUX("IF2 ADC4 Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc4_swap_mux),
+       SND_SOC_DAPM_MUX_E("IF2 ADC TDM Swap Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_adc_tdm_swap_mux, rt5677_if2_adc_tdm_event,
+                       SND_SOC_DAPM_PRE_PMU),
        SND_SOC_DAPM_MUX("IF3 ADC Mux", SND_SOC_NOPM, 0, 0,
                        &rt5677_if3_adc_mux),
        SND_SOC_DAPM_MUX("IF4 ADC Mux", SND_SOC_NOPM, 0, 0,
@@ -1984,6 +2535,40 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
        SND_SOC_DAPM_MUX("SLB ADC4 Mux", SND_SOC_NOPM, 0, 0,
                        &rt5677_slb_adc4_mux),
 
+       SND_SOC_DAPM_MUX("IF1 DAC0 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac0_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC1 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac1_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC2 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac2_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC3 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac3_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC4 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac4_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC5 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac5_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC6 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac6_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF1 DAC7 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if1_dac7_tdm_sel_mux),
+
+       SND_SOC_DAPM_MUX("IF2 DAC0 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac0_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC1 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac1_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC2 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac2_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC3 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac3_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC4 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac4_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC5 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac5_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC6 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac6_tdm_sel_mux),
+       SND_SOC_DAPM_MUX("IF2 DAC7 Mux", SND_SOC_NOPM, 0, 0,
+                       &rt5677_if2_dac7_tdm_sel_mux),
+
        /* Audio Interface */
        SND_SOC_DAPM_AIF_IN("AIF1RX", "AIF1 Playback", 0, SND_SOC_NOPM, 0, 0),
        SND_SOC_DAPM_AIF_OUT("AIF1TX", "AIF1 Capture", 0, SND_SOC_NOPM, 0, 0),
@@ -2022,7 +2607,7 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
                rt5677_ob_7_mix, ARRAY_SIZE(rt5677_ob_7_mix)),
 
        /* Output Side */
-       /* DAC mixer before sound effect  */
+       /* DAC mixer before sound effect */
        SND_SOC_DAPM_MIXER("DAC1 MIXL", SND_SOC_NOPM, 0, 0,
                rt5677_dac_l_mix, ARRAY_SIZE(rt5677_dac_l_mix)),
        SND_SOC_DAPM_MIXER("DAC1 MIXR", SND_SOC_NOPM, 0, 0,
@@ -2109,13 +2694,20 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
        SND_SOC_DAPM_MUX("PDM2 R Mux", RT5677_PDM_OUT_CTRL, RT5677_M_PDM2_R_SFT,
                1, &rt5677_pdm2_r_mux),
 
-       SND_SOC_DAPM_PGA_S("LOUT1 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO1_BIT,
+       SND_SOC_DAPM_PGA_S("LOUT1 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO1_BIT,
                0, NULL, 0),
-       SND_SOC_DAPM_PGA_S("LOUT2 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO2_BIT,
+       SND_SOC_DAPM_PGA_S("LOUT2 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO2_BIT,
                0, NULL, 0),
-       SND_SOC_DAPM_PGA_S("LOUT3 amp", 1, RT5677_PWR_ANLG1, RT5677_PWR_LO3_BIT,
+       SND_SOC_DAPM_PGA_S("LOUT3 amp", 0, RT5677_PWR_ANLG1, RT5677_PWR_LO3_BIT,
                0, NULL, 0),
 
+       SND_SOC_DAPM_PGA_S("LOUT1 vref", 1, SND_SOC_NOPM, 0, 0,
+               rt5677_vref_event, SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_S("LOUT2 vref", 1, SND_SOC_NOPM, 0, 0,
+               rt5677_vref_event, SND_SOC_DAPM_POST_PMU),
+       SND_SOC_DAPM_PGA_S("LOUT3 vref", 1, SND_SOC_NOPM, 0, 0,
+               rt5677_vref_event, SND_SOC_DAPM_POST_PMU),
+
        /* Output Lines */
        SND_SOC_DAPM_OUTPUT("LOUT1"),
        SND_SOC_DAPM_OUTPUT("LOUT2"),
@@ -2124,6 +2716,8 @@ static const struct snd_soc_dapm_widget rt5677_dapm_widgets[] = {
        SND_SOC_DAPM_OUTPUT("PDM1R"),
        SND_SOC_DAPM_OUTPUT("PDM2L"),
        SND_SOC_DAPM_OUTPUT("PDM2R"),
+
+       SND_SOC_DAPM_POST("vref", rt5677_vref_event),
 };
 
 static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
@@ -2354,11 +2948,42 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
        { "IF1 ADC4 Mux", "OB67", "OB67" },
        { "IF1 ADC4 Mux", "OB01", "OB01 Bypass Mux" },
 
+       { "IF1 ADC1 Swap Mux", "L/R", "IF1 ADC1 Mux" },
+       { "IF1 ADC1 Swap Mux", "R/L", "IF1 ADC1 Mux" },
+       { "IF1 ADC1 Swap Mux", "L/L", "IF1 ADC1 Mux" },
+       { "IF1 ADC1 Swap Mux", "R/R", "IF1 ADC1 Mux" },
+
+       { "IF1 ADC2 Swap Mux", "L/R", "IF1 ADC2 Mux" },
+       { "IF1 ADC2 Swap Mux", "R/L", "IF1 ADC2 Mux" },
+       { "IF1 ADC2 Swap Mux", "L/L", "IF1 ADC2 Mux" },
+       { "IF1 ADC2 Swap Mux", "R/R", "IF1 ADC2 Mux" },
+
+       { "IF1 ADC3 Swap Mux", "L/R", "IF1 ADC3 Mux" },
+       { "IF1 ADC3 Swap Mux", "R/L", "IF1 ADC3 Mux" },
+       { "IF1 ADC3 Swap Mux", "L/L", "IF1 ADC3 Mux" },
+       { "IF1 ADC3 Swap Mux", "R/R", "IF1 ADC3 Mux" },
+
+       { "IF1 ADC4 Swap Mux", "L/R", "IF1 ADC4 Mux" },
+       { "IF1 ADC4 Swap Mux", "R/L", "IF1 ADC4 Mux" },
+       { "IF1 ADC4 Swap Mux", "L/L", "IF1 ADC4 Mux" },
+       { "IF1 ADC4 Swap Mux", "R/R", "IF1 ADC4 Mux" },
+
+       { "IF1 ADC", NULL, "IF1 ADC1 Swap Mux" },
+       { "IF1 ADC", NULL, "IF1 ADC2 Swap Mux" },
+       { "IF1 ADC", NULL, "IF1 ADC3 Swap Mux" },
+       { "IF1 ADC", NULL, "IF1 ADC4 Swap Mux" },
+
+       { "IF1 ADC TDM Swap Mux", "1/2/3/4", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "2/1/3/4", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "2/3/1/4", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "4/1/2/3", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "1/3/2/4", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "1/4/2/3", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "3/1/2/4", "IF1 ADC" },
+       { "IF1 ADC TDM Swap Mux", "3/4/1/2", "IF1 ADC" },
+
        { "AIF1TX", NULL, "I2S1" },
-       { "AIF1TX", NULL, "IF1 ADC1 Mux" },
-       { "AIF1TX", NULL, "IF1 ADC2 Mux" },
-       { "AIF1TX", NULL, "IF1 ADC3 Mux" },
-       { "AIF1TX", NULL, "IF1 ADC4 Mux" },
+       { "AIF1TX", NULL, "IF1 ADC TDM Swap Mux" },
 
        { "IF2 ADC1 Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
        { "IF2 ADC1 Mux", "OB01", "OB01 Bypass Mux" },
@@ -2375,11 +3000,42 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
        { "IF2 ADC4 Mux", "OB67", "OB67" },
        { "IF2 ADC4 Mux", "OB01", "OB01 Bypass Mux" },
 
+       { "IF2 ADC1 Swap Mux", "L/R", "IF2 ADC1 Mux" },
+       { "IF2 ADC1 Swap Mux", "R/L", "IF2 ADC1 Mux" },
+       { "IF2 ADC1 Swap Mux", "L/L", "IF2 ADC1 Mux" },
+       { "IF2 ADC1 Swap Mux", "R/R", "IF2 ADC1 Mux" },
+
+       { "IF2 ADC2 Swap Mux", "L/R", "IF2 ADC2 Mux" },
+       { "IF2 ADC2 Swap Mux", "R/L", "IF2 ADC2 Mux" },
+       { "IF2 ADC2 Swap Mux", "L/L", "IF2 ADC2 Mux" },
+       { "IF2 ADC2 Swap Mux", "R/R", "IF2 ADC2 Mux" },
+
+       { "IF2 ADC3 Swap Mux", "L/R", "IF2 ADC3 Mux" },
+       { "IF2 ADC3 Swap Mux", "R/L", "IF2 ADC3 Mux" },
+       { "IF2 ADC3 Swap Mux", "L/L", "IF2 ADC3 Mux" },
+       { "IF2 ADC3 Swap Mux", "R/R", "IF2 ADC3 Mux" },
+
+       { "IF2 ADC4 Swap Mux", "L/R", "IF2 ADC4 Mux" },
+       { "IF2 ADC4 Swap Mux", "R/L", "IF2 ADC4 Mux" },
+       { "IF2 ADC4 Swap Mux", "L/L", "IF2 ADC4 Mux" },
+       { "IF2 ADC4 Swap Mux", "R/R", "IF2 ADC4 Mux" },
+
+       { "IF2 ADC", NULL, "IF2 ADC1 Swap Mux" },
+       { "IF2 ADC", NULL, "IF2 ADC2 Swap Mux" },
+       { "IF2 ADC", NULL, "IF2 ADC3 Swap Mux" },
+       { "IF2 ADC", NULL, "IF2 ADC4 Swap Mux" },
+
+       { "IF2 ADC TDM Swap Mux", "1/2/3/4", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "2/1/3/4", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "3/1/2/4", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "4/1/2/3", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "1/3/2/4", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "1/4/2/3", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "2/3/1/4", "IF2 ADC" },
+       { "IF2 ADC TDM Swap Mux", "3/4/1/2", "IF2 ADC" },
+
        { "AIF2TX", NULL, "I2S2" },
-       { "AIF2TX", NULL, "IF2 ADC1 Mux" },
-       { "AIF2TX", NULL, "IF2 ADC2 Mux" },
-       { "AIF2TX", NULL, "IF2 ADC3 Mux" },
-       { "AIF2TX", NULL, "IF2 ADC4 Mux" },
+       { "AIF2TX", NULL, "IF2 ADC TDM Swap Mux" },
 
        { "IF3 ADC Mux", "STO1 ADC MIX", "Stereo1 ADC MIX" },
        { "IF3 ADC Mux", "STO2 ADC MIX", "Stereo2 ADC MIX" },
@@ -2569,14 +3225,86 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
        { "IF1 DAC6", NULL, "I2S1" },
        { "IF1 DAC7", NULL, "I2S1" },
 
-       { "IF1 DAC01", NULL, "IF1 DAC0" },
-       { "IF1 DAC01", NULL, "IF1 DAC1" },
-       { "IF1 DAC23", NULL, "IF1 DAC2" },
-       { "IF1 DAC23", NULL, "IF1 DAC3" },
-       { "IF1 DAC45", NULL, "IF1 DAC4" },
-       { "IF1 DAC45", NULL, "IF1 DAC5" },
-       { "IF1 DAC67", NULL, "IF1 DAC6" },
-       { "IF1 DAC67", NULL, "IF1 DAC7" },
+       { "IF1 DAC0 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC0 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC0 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC0 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC0 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC0 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC0 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC0 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC1 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC1 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC1 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC1 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC1 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC1 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC1 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC1 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC2 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC2 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC2 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC2 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC2 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC2 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC2 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC2 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC3 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC3 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC3 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC3 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC3 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC3 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC3 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC3 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC4 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC4 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC4 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC4 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC4 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC4 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC4 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC4 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC5 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC5 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC5 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC5 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC5 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC5 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC5 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC5 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC6 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC6 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC6 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC6 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC6 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC6 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC6 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC6 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC7 Mux", "Slot0", "IF1 DAC0" },
+       { "IF1 DAC7 Mux", "Slot1", "IF1 DAC1" },
+       { "IF1 DAC7 Mux", "Slot2", "IF1 DAC2" },
+       { "IF1 DAC7 Mux", "Slot3", "IF1 DAC3" },
+       { "IF1 DAC7 Mux", "Slot4", "IF1 DAC4" },
+       { "IF1 DAC7 Mux", "Slot5", "IF1 DAC5" },
+       { "IF1 DAC7 Mux", "Slot6", "IF1 DAC6" },
+       { "IF1 DAC7 Mux", "Slot7", "IF1 DAC7" },
+
+       { "IF1 DAC01", NULL, "IF1 DAC0 Mux" },
+       { "IF1 DAC01", NULL, "IF1 DAC1 Mux" },
+       { "IF1 DAC23", NULL, "IF1 DAC2 Mux" },
+       { "IF1 DAC23", NULL, "IF1 DAC3 Mux" },
+       { "IF1 DAC45", NULL, "IF1 DAC4 Mux" },
+       { "IF1 DAC45", NULL, "IF1 DAC5 Mux" },
+       { "IF1 DAC67", NULL, "IF1 DAC6 Mux" },
+       { "IF1 DAC67", NULL, "IF1 DAC7 Mux" },
 
        { "IF2 DAC0", NULL, "AIF2RX" },
        { "IF2 DAC1", NULL, "AIF2RX" },
@@ -2595,14 +3323,86 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
        { "IF2 DAC6", NULL, "I2S2" },
        { "IF2 DAC7", NULL, "I2S2" },
 
-       { "IF2 DAC01", NULL, "IF2 DAC0" },
-       { "IF2 DAC01", NULL, "IF2 DAC1" },
-       { "IF2 DAC23", NULL, "IF2 DAC2" },
-       { "IF2 DAC23", NULL, "IF2 DAC3" },
-       { "IF2 DAC45", NULL, "IF2 DAC4" },
-       { "IF2 DAC45", NULL, "IF2 DAC5" },
-       { "IF2 DAC67", NULL, "IF2 DAC6" },
-       { "IF2 DAC67", NULL, "IF2 DAC7" },
+       { "IF2 DAC0 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC0 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC0 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC0 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC0 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC0 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC0 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC0 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC1 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC1 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC1 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC1 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC1 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC1 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC1 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC1 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC2 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC2 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC2 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC2 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC2 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC2 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC2 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC2 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC3 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC3 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC3 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC3 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC3 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC3 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC3 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC3 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC4 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC4 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC4 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC4 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC4 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC4 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC4 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC4 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC5 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC5 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC5 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC5 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC5 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC5 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC5 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC5 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC6 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC6 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC6 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC6 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC6 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC6 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC6 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC6 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC7 Mux", "Slot0", "IF2 DAC0" },
+       { "IF2 DAC7 Mux", "Slot1", "IF2 DAC1" },
+       { "IF2 DAC7 Mux", "Slot2", "IF2 DAC2" },
+       { "IF2 DAC7 Mux", "Slot3", "IF2 DAC3" },
+       { "IF2 DAC7 Mux", "Slot4", "IF2 DAC4" },
+       { "IF2 DAC7 Mux", "Slot5", "IF2 DAC5" },
+       { "IF2 DAC7 Mux", "Slot6", "IF2 DAC6" },
+       { "IF2 DAC7 Mux", "Slot7", "IF2 DAC7" },
+
+       { "IF2 DAC01", NULL, "IF2 DAC0 Mux" },
+       { "IF2 DAC01", NULL, "IF2 DAC1 Mux" },
+       { "IF2 DAC23", NULL, "IF2 DAC2 Mux" },
+       { "IF2 DAC23", NULL, "IF2 DAC3 Mux" },
+       { "IF2 DAC45", NULL, "IF2 DAC4 Mux" },
+       { "IF2 DAC45", NULL, "IF2 DAC5 Mux" },
+       { "IF2 DAC67", NULL, "IF2 DAC6 Mux" },
+       { "IF2 DAC67", NULL, "IF2 DAC7 Mux" },
 
        { "IF3 DAC", NULL, "AIF3RX" },
        { "IF3 DAC", NULL, "I2S3" },
@@ -2806,9 +3606,13 @@ static const struct snd_soc_dapm_route rt5677_dapm_routes[] = {
        { "LOUT2 amp", NULL, "DAC 2" },
        { "LOUT3 amp", NULL, "DAC 3" },
 
-       { "LOUT1", NULL, "LOUT1 amp" },
-       { "LOUT2", NULL, "LOUT2 amp" },
-       { "LOUT3", NULL, "LOUT3 amp" },
+       { "LOUT1 vref", NULL, "LOUT1 amp" },
+       { "LOUT2 vref", NULL, "LOUT2 amp" },
+       { "LOUT3 vref", NULL, "LOUT3 amp" },
+
+       { "LOUT1", NULL, "LOUT1 vref" },
+       { "LOUT2", NULL, "LOUT2 vref" },
+       { "LOUT3", NULL, "LOUT3 vref" },
 
        { "PDM1L", NULL, "PDM1 L Mux" },
        { "PDM1R", NULL, "PDM1 R Mux" },
@@ -2837,7 +3641,8 @@ static int rt5677_hw_params(struct snd_pcm_substream *substream,
        rt5677->lrck[dai->id] = params_rate(params);
        pre_div = rl6231_get_clk_info(rt5677->sysclk, rt5677->lrck[dai->id]);
        if (pre_div < 0) {
-               dev_err(codec->dev, "Unsupported clock setting\n");
+               dev_err(codec->dev, "Unsupported clock setting: sysclk=%dHz lrck=%dHz\n",
+                       rt5677->sysclk, rt5677->lrck[dai->id]);
                return -EINVAL;
        }
        frame_size = snd_soc_params_to_frame_size(params);
@@ -3181,6 +3986,8 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
 
        case SND_SOC_BIAS_PREPARE:
                if (codec->dapm.bias_level == SND_SOC_BIAS_STANDBY) {
+                       rt5677_set_dsp_vad(codec, false);
+
                        regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
                                RT5677_LDO1_SEL_MASK | RT5677_LDO2_SEL_MASK,
                                0x0055);
@@ -3188,14 +3995,12 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
                                RT5677_PR_BASE + RT5677_BIAS_CUR4,
                                0x0f00, 0x0f00);
                        regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
+                               RT5677_PWR_FV1 | RT5677_PWR_FV2 |
                                RT5677_PWR_VREF1 | RT5677_PWR_MB |
                                RT5677_PWR_BG | RT5677_PWR_VREF2,
                                RT5677_PWR_VREF1 | RT5677_PWR_MB |
                                RT5677_PWR_BG | RT5677_PWR_VREF2);
-                       mdelay(20);
-                       regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG1,
-                               RT5677_PWR_FV1 | RT5677_PWR_FV2,
-                               RT5677_PWR_FV1 | RT5677_PWR_FV2);
+                       rt5677->is_vref_slow = false;
                        regmap_update_bits(rt5677->regmap, RT5677_PWR_ANLG2,
                                RT5677_PWR_CORE, RT5677_PWR_CORE);
                        regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC,
@@ -3214,6 +4019,9 @@ static int rt5677_set_bias_level(struct snd_soc_codec *codec,
                regmap_write(rt5677->regmap, RT5677_PWR_ANLG2, 0x0000);
                regmap_update_bits(rt5677->regmap,
                        RT5677_PR_BASE + RT5677_BIAS_CUR4, 0x0f00, 0x0000);
+
+               if (rt5677->dsp_vad_en)
+                       rt5677_set_dsp_vad(codec, true);
                break;
 
        default:
@@ -3309,6 +4117,78 @@ static int rt5677_gpio_direction_in(struct gpio_chip *chip, unsigned offset)
        return 0;
 }
 
+/** Configures the gpio as
+ *   0 - floating
+ *   1 - pull down
+ *   2 - pull up
+ */
+static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
+               int value)
+{
+       int shift;
+
+       switch (offset) {
+       case RT5677_GPIO1 ... RT5677_GPIO2:
+               shift = 2 * (1 - offset);
+               regmap_update_bits(rt5677->regmap,
+                       RT5677_PR_BASE + RT5677_DIG_IN_PIN_ST_CTRL2,
+                       0x3 << shift,
+                       (value & 0x3) << shift);
+               break;
+
+       case RT5677_GPIO3 ... RT5677_GPIO6:
+               shift = 2 * (9 - offset);
+               regmap_update_bits(rt5677->regmap,
+                       RT5677_PR_BASE + RT5677_DIG_IN_PIN_ST_CTRL3,
+                       0x3 << shift,
+                       (value & 0x3) << shift);
+               break;
+
+       default:
+               break;
+       }
+}
+
+static int rt5677_to_irq(struct gpio_chip *chip, unsigned offset)
+{
+       struct rt5677_priv *rt5677 = gpio_to_rt5677(chip);
+       struct regmap_irq_chip_data *data = rt5677->irq_data;
+       int irq;
+
+       if (offset >= RT5677_GPIO1 && offset <= RT5677_GPIO3) {
+               if ((rt5677->pdata.jd1_gpio == 1 && offset == RT5677_GPIO1) ||
+                       (rt5677->pdata.jd1_gpio == 2 &&
+                               offset == RT5677_GPIO2) ||
+                       (rt5677->pdata.jd1_gpio == 3 &&
+                               offset == RT5677_GPIO3)) {
+                       irq = RT5677_IRQ_JD1;
+               } else {
+                       return -ENXIO;
+               }
+       }
+
+       if (offset >= RT5677_GPIO4 && offset <= RT5677_GPIO6) {
+               if ((rt5677->pdata.jd2_gpio == 1 && offset == RT5677_GPIO4) ||
+                       (rt5677->pdata.jd2_gpio == 2 &&
+                               offset == RT5677_GPIO5) ||
+                       (rt5677->pdata.jd2_gpio == 3 &&
+                               offset == RT5677_GPIO6)) {
+                       irq = RT5677_IRQ_JD2;
+               } else if ((rt5677->pdata.jd3_gpio == 1 &&
+                               offset == RT5677_GPIO4) ||
+                       (rt5677->pdata.jd3_gpio == 2 &&
+                               offset == RT5677_GPIO5) ||
+                       (rt5677->pdata.jd3_gpio == 3 &&
+                               offset == RT5677_GPIO6)) {
+                       irq = RT5677_IRQ_JD3;
+               } else {
+                       return -ENXIO;
+               }
+       }
+
+       return regmap_irq_get_virq(data, irq);
+}
+
 static struct gpio_chip rt5677_template_chip = {
        .label                  = "rt5677",
        .owner                  = THIS_MODULE,
@@ -3316,6 +4196,7 @@ static struct gpio_chip rt5677_template_chip = {
        .set                    = rt5677_gpio_set,
        .direction_input        = rt5677_gpio_direction_in,
        .get                    = rt5677_gpio_get,
+       .to_irq                 = rt5677_to_irq,
        .can_sleep              = 1,
 };
 
@@ -3341,6 +4222,11 @@ static void rt5677_free_gpio(struct i2c_client *i2c)
        gpiochip_remove(&rt5677->gpio_chip);
 }
 #else
+static void rt5677_gpio_config(struct rt5677_priv *rt5677, unsigned offset,
+               int value)
+{
+}
+
 static void rt5677_init_gpio(struct i2c_client *i2c)
 {
 }
@@ -3353,6 +4239,7 @@ static void rt5677_free_gpio(struct i2c_client *i2c)
 static int rt5677_probe(struct snd_soc_codec *codec)
 {
        struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
+       int i;
 
        rt5677->codec = codec;
 
@@ -3371,6 +4258,37 @@ static int rt5677_probe(struct snd_soc_codec *codec)
        regmap_write(rt5677->regmap, RT5677_DIG_MISC, 0x0020);
        regmap_write(rt5677->regmap, RT5677_PWR_DSP2, 0x0c00);
 
+       for (i = 0; i < RT5677_GPIO_NUM; i++)
+               rt5677_gpio_config(rt5677, i, rt5677->pdata.gpio_config[i]);
+
+       if (rt5677->irq_data) {
+               regmap_update_bits(rt5677->regmap, RT5677_GPIO_CTRL1, 0x8000,
+                       0x8000);
+               regmap_update_bits(rt5677->regmap, RT5677_DIG_MISC, 0x0018,
+                       0x0008);
+
+               if (rt5677->pdata.jd1_gpio)
+                       regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
+                               RT5677_SEL_GPIO_JD1_MASK,
+                               rt5677->pdata.jd1_gpio <<
+                               RT5677_SEL_GPIO_JD1_SFT);
+
+               if (rt5677->pdata.jd2_gpio)
+                       regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
+                               RT5677_SEL_GPIO_JD2_MASK,
+                               rt5677->pdata.jd2_gpio <<
+                               RT5677_SEL_GPIO_JD2_SFT);
+
+               if (rt5677->pdata.jd3_gpio)
+                       regmap_update_bits(rt5677->regmap, RT5677_JD_CTRL1,
+                               RT5677_SEL_GPIO_JD3_MASK,
+                               rt5677->pdata.jd3_gpio <<
+                               RT5677_SEL_GPIO_JD3_SFT);
+       }
+
+       mutex_init(&rt5677->dsp_cmd_lock);
+       mutex_init(&rt5677->dsp_pri_lock);
+
        return 0;
 }
 
@@ -3390,8 +4308,11 @@ static int rt5677_suspend(struct snd_soc_codec *codec)
 {
        struct rt5677_priv *rt5677 = snd_soc_codec_get_drvdata(codec);
 
-       regcache_cache_only(rt5677->regmap, true);
-       regcache_mark_dirty(rt5677->regmap);
+       if (!rt5677->dsp_vad_en) {
+               regcache_cache_only(rt5677->regmap, true);
+               regcache_mark_dirty(rt5677->regmap);
+       }
+
        if (gpio_is_valid(rt5677->pow_ldo2))
                gpio_set_value_cansleep(rt5677->pow_ldo2, 0);
 
@@ -3406,8 +4327,11 @@ static int rt5677_resume(struct snd_soc_codec *codec)
                gpio_set_value_cansleep(rt5677->pow_ldo2, 1);
                msleep(10);
        }
-       regcache_cache_only(rt5677->regmap, false);
-       regcache_sync(rt5677->regmap);
+
+       if (!rt5677->dsp_vad_en) {
+               regcache_cache_only(rt5677->regmap, false);
+               regcache_sync(rt5677->regmap);
+       }
 
        return 0;
 }
@@ -3416,6 +4340,51 @@ static int rt5677_resume(struct snd_soc_codec *codec)
 #define rt5677_resume NULL
 #endif
 
+static int rt5677_read(void *context, unsigned int reg, unsigned int *val)
+{
+       struct i2c_client *client = context;
+       struct rt5677_priv *rt5677 = i2c_get_clientdata(client);
+
+       if (rt5677->is_dsp_mode) {
+               if (reg > 0xff) {
+                       mutex_lock(&rt5677->dsp_pri_lock);
+                       rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX,
+                               reg & 0xff);
+                       rt5677_dsp_mode_i2c_read(rt5677, RT5677_PRIV_DATA, val);
+                       mutex_unlock(&rt5677->dsp_pri_lock);
+               } else {
+                       rt5677_dsp_mode_i2c_read(rt5677, reg, val);
+               }
+       } else {
+               regmap_read(rt5677->regmap_physical, reg, val);
+       }
+
+       return 0;
+}
+
+static int rt5677_write(void *context, unsigned int reg, unsigned int val)
+{
+       struct i2c_client *client = context;
+       struct rt5677_priv *rt5677 = i2c_get_clientdata(client);
+
+       if (rt5677->is_dsp_mode) {
+               if (reg > 0xff) {
+                       mutex_lock(&rt5677->dsp_pri_lock);
+                       rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_INDEX,
+                               reg & 0xff);
+                       rt5677_dsp_mode_i2c_write(rt5677, RT5677_PRIV_DATA,
+                               val);
+                       mutex_unlock(&rt5677->dsp_pri_lock);
+               } else {
+                       rt5677_dsp_mode_i2c_write(rt5677, reg, val);
+               }
+       } else {
+               regmap_write(rt5677->regmap_physical, reg, val);
+       }
+
+       return 0;
+}
+
 #define RT5677_STEREO_RATES SNDRV_PCM_RATE_8000_96000
 #define RT5677_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE | \
                        SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S8)
@@ -3541,6 +4510,20 @@ static struct snd_soc_codec_driver soc_codec_dev_rt5677 = {
        .num_dapm_routes = ARRAY_SIZE(rt5677_dapm_routes),
 };
 
+static const struct regmap_config rt5677_regmap_physical = {
+       .name = "physical",
+       .reg_bits = 8,
+       .val_bits = 16,
+
+       .max_register = RT5677_VENDOR_ID2 + 1 + (ARRAY_SIZE(rt5677_ranges) *
+                                               RT5677_PR_SPACING),
+       .readable_reg = rt5677_readable_register,
+
+       .cache_type = REGCACHE_NONE,
+       .ranges = rt5677_ranges,
+       .num_ranges = ARRAY_SIZE(rt5677_ranges),
+};
+
 static const struct regmap_config rt5677_regmap = {
        .reg_bits = 8,
        .val_bits = 16,
@@ -3550,6 +4533,8 @@ static const struct regmap_config rt5677_regmap = {
 
        .volatile_reg = rt5677_volatile_register,
        .readable_reg = rt5677_readable_register,
+       .reg_read = rt5677_read,
+       .reg_write = rt5677_write,
 
        .cache_type = REGCACHE_RBTREE,
        .reg_defaults = rt5677_reg,
@@ -3590,9 +4575,77 @@ static int rt5677_parse_dt(struct rt5677_priv *rt5677, struct device_node *np)
                        (rt5677->pow_ldo2 != -ENOENT))
                return rt5677->pow_ldo2;
 
+       of_property_read_u8_array(np, "realtek,gpio-config",
+               rt5677->pdata.gpio_config, RT5677_GPIO_NUM);
+
+       of_property_read_u32(np, "realtek,jd1-gpio", &rt5677->pdata.jd1_gpio);
+       of_property_read_u32(np, "realtek,jd2-gpio", &rt5677->pdata.jd2_gpio);
+       of_property_read_u32(np, "realtek,jd3-gpio", &rt5677->pdata.jd3_gpio);
+
+       return 0;
+}
+
+static struct regmap_irq rt5677_irqs[] = {
+       [RT5677_IRQ_JD1] = {
+               .reg_offset = 0,
+               .mask = RT5677_EN_IRQ_GPIO_JD1,
+       },
+       [RT5677_IRQ_JD2] = {
+               .reg_offset = 0,
+               .mask = RT5677_EN_IRQ_GPIO_JD2,
+       },
+       [RT5677_IRQ_JD3] = {
+               .reg_offset = 0,
+               .mask = RT5677_EN_IRQ_GPIO_JD3,
+       },
+};
+
+static struct regmap_irq_chip rt5677_irq_chip = {
+       .name = "rt5677",
+       .irqs = rt5677_irqs,
+       .num_irqs = ARRAY_SIZE(rt5677_irqs),
+
+       .num_regs = 1,
+       .status_base = RT5677_IRQ_CTRL1,
+       .mask_base = RT5677_IRQ_CTRL1,
+       .mask_invert = 1,
+};
+
+static int rt5677_init_irq(struct i2c_client *i2c)
+{
+       int ret;
+       struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+
+       if (!rt5677->pdata.jd1_gpio &&
+               !rt5677->pdata.jd2_gpio &&
+               !rt5677->pdata.jd3_gpio)
+               return 0;
+
+       if (!i2c->irq) {
+               dev_err(&i2c->dev, "No interrupt specified\n");
+               return -EINVAL;
+       }
+
+       ret = regmap_add_irq_chip(rt5677->regmap, i2c->irq,
+               IRQF_TRIGGER_RISING | IRQF_TRIGGER_FALLING | IRQF_ONESHOT, 0,
+               &rt5677_irq_chip, &rt5677->irq_data);
+
+       if (ret != 0) {
+               dev_err(&i2c->dev, "Failed to register IRQ chip: %d\n", ret);
+               return ret;
+       }
+
        return 0;
 }
 
+static void rt5677_free_irq(struct i2c_client *i2c)
+{
+       struct rt5677_priv *rt5677 = i2c_get_clientdata(i2c);
+
+       if (rt5677->irq_data)
+               regmap_del_irq_chip(i2c->irq, rt5677->irq_data);
+}
+
 static int rt5677_i2c_probe(struct i2c_client *i2c,
                    const struct i2c_device_id *id)
 {
@@ -3638,7 +4691,16 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
                msleep(10);
        }
 
-       rt5677->regmap = devm_regmap_init_i2c(i2c, &rt5677_regmap);
+       rt5677->regmap_physical = devm_regmap_init_i2c(i2c,
+                                       &rt5677_regmap_physical);
+       if (IS_ERR(rt5677->regmap_physical)) {
+               ret = PTR_ERR(rt5677->regmap_physical);
+               dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
+                       ret);
+               return ret;
+       }
+
+       rt5677->regmap = devm_regmap_init(&i2c->dev, NULL, i2c, &rt5677_regmap);
        if (IS_ERR(rt5677->regmap)) {
                ret = PTR_ERR(rt5677->regmap);
                dev_err(&i2c->dev, "Failed to allocate register map: %d\n",
@@ -3690,6 +4752,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
        }
 
        rt5677_init_gpio(i2c);
+       rt5677_init_irq(i2c);
 
        return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5677,
                                      rt5677_dai, ARRAY_SIZE(rt5677_dai));
@@ -3698,6 +4761,7 @@ static int rt5677_i2c_probe(struct i2c_client *i2c,
 static int rt5677_i2c_remove(struct i2c_client *i2c)
 {
        snd_soc_unregister_codec(&i2c->dev);
+       rt5677_free_irq(i2c);
        rt5677_free_gpio(i2c);
 
        return 0;
index d4eb6d5..c0a625f 100644 (file)
@@ -13,6 +13,7 @@
 #define __RT5677_H__
 
 #include <sound/rt5677.h>
+#include <linux/gpio/driver.h>
 
 /* Info */
 #define RT5677_RESET                           0x00
 #define RT5677_R_MUTE_SFT                      7
 #define RT5677_VOL_R_MUTE                      (0x1 << 6)
 #define RT5677_VOL_R_SFT                       6
-#define RT5677_L_VOL_MASK                      (0x3f << 8)
-#define RT5677_L_VOL_SFT                       8
-#define RT5677_R_VOL_MASK                      (0x3f)
-#define RT5677_R_VOL_SFT                       0
+#define RT5677_L_VOL_MASK                      (0x7f << 9)
+#define RT5677_L_VOL_SFT                       9
+#define RT5677_R_VOL_MASK                      (0x7f << 1)
+#define RT5677_R_VOL_SFT                       1
 
 /* LOUT1 Control (0x01) */
 #define RT5677_LOUT1_L_MUTE                    (0x1 << 15)
 #define RT5677_SEL_DAC2_R_SRC_SFT              0
 
 /* Stereo1 ADC Digital Volume Control (0x1c) */
-#define RT5677_STO1_ADC_L_VOL_MASK             (0x7f << 8)
-#define RT5677_STO1_ADC_L_VOL_SFT              8
-#define RT5677_STO1_ADC_R_VOL_MASK             (0x7f)
-#define RT5677_STO1_ADC_R_VOL_SFT              0
+#define RT5677_STO1_ADC_L_VOL_MASK             (0x3f << 9)
+#define RT5677_STO1_ADC_L_VOL_SFT              9
+#define RT5677_STO1_ADC_R_VOL_MASK             (0x3f << 1)
+#define RT5677_STO1_ADC_R_VOL_SFT              1
 
 /* Mono ADC Digital Volume Control (0x1d) */
-#define RT5677_MONO_ADC_L_VOL_MASK             (0x7f << 8)
-#define RT5677_MONO_ADC_L_VOL_SFT              8
-#define RT5677_MONO_ADC_R_VOL_MASK             (0x7f)
-#define RT5677_MONO_ADC_R_VOL_SFT              0
+#define RT5677_MONO_ADC_L_VOL_MASK             (0x3f << 9)
+#define RT5677_MONO_ADC_L_VOL_SFT              9
+#define RT5677_MONO_ADC_R_VOL_MASK             (0x3f << 1)
+#define RT5677_MONO_ADC_R_VOL_SFT              1
 
 /* Stereo 1/2 ADC Boost Gain Control (0x1e) */
 #define RT5677_STO1_ADC_L_BST_MASK             (0x3 << 14)
 #define RT5677_PDM2_I2C_EXE                    (0x1 << 1)
 #define RT5677_PDM2_I2C_BUSY                   (0x1 << 0)
 
-/* MX3C TDM1 control 1 (0x3c) */
+/* TDM1 control 1 (0x3b) */
+#define RT5677_IF1_ADC_MODE_MASK               (0x1 << 12)
+#define RT5677_IF1_ADC_MODE_SFT                        12
+#define RT5677_IF1_ADC_MODE_I2S                        (0x0 << 12)
+#define RT5677_IF1_ADC_MODE_TDM                        (0x1 << 12)
+#define RT5677_IF1_ADC1_SWAP_MASK              (0x3 << 6)
+#define RT5677_IF1_ADC1_SWAP_SFT               6
+#define RT5677_IF1_ADC2_SWAP_MASK              (0x3 << 4)
+#define RT5677_IF1_ADC2_SWAP_SFT               4
+#define RT5677_IF1_ADC3_SWAP_MASK              (0x3 << 2)
+#define RT5677_IF1_ADC3_SWAP_SFT               2
+#define RT5677_IF1_ADC4_SWAP_MASK              (0x3 << 0)
+#define RT5677_IF1_ADC4_SWAP_SFT               0
+
+/* TDM1 control 2 (0x3c) */
 #define RT5677_IF1_ADC4_MASK                   (0x3 << 10)
 #define RT5677_IF1_ADC4_SFT                    10
 #define RT5677_IF1_ADC3_MASK                   (0x3 << 8)
 #define RT5677_IF1_ADC2_SFT                    6
 #define RT5677_IF1_ADC1_MASK                   (0x3 << 4)
 #define RT5677_IF1_ADC1_SFT                    4
-
-/* MX41 TDM2 control 1 (0x41) */
+#define RT5677_IF1_ADC_CTRL_MASK               (0x7 << 0)
+#define RT5677_IF1_ADC_CTRL_SFT                        0
+
+/* TDM1 control 4 (0x3e) */
+#define RT5677_IF1_DAC0_MASK                   (0x7 << 12)
+#define RT5677_IF1_DAC0_SFT                    12
+#define RT5677_IF1_DAC1_MASK                   (0x7 << 8)
+#define RT5677_IF1_DAC1_SFT                    8
+#define RT5677_IF1_DAC2_MASK                   (0x7 << 4)
+#define RT5677_IF1_DAC2_SFT                    4
+#define RT5677_IF1_DAC3_MASK                   (0x7 << 0)
+#define RT5677_IF1_DAC3_SFT                    0
+
+/* TDM1 control 5 (0x3f) */
+#define RT5677_IF1_DAC4_MASK                   (0x7 << 12)
+#define RT5677_IF1_DAC4_SFT                    12
+#define RT5677_IF1_DAC5_MASK                   (0x7 << 8)
+#define RT5677_IF1_DAC5_SFT                    8
+#define RT5677_IF1_DAC6_MASK                   (0x7 << 4)
+#define RT5677_IF1_DAC6_SFT                    4
+#define RT5677_IF1_DAC7_MASK                   (0x7 << 0)
+#define RT5677_IF1_DAC7_SFT                    0
+
+/* TDM2 control 1 (0x40) */
+#define RT5677_IF2_ADC_MODE_MASK               (0x1 << 12)
+#define RT5677_IF2_ADC_MODE_SFT                        12
+#define RT5677_IF2_ADC_MODE_I2S                        (0x0 << 12)
+#define RT5677_IF2_ADC_MODE_TDM                        (0x1 << 12)
+#define RT5677_IF2_ADC1_SWAP_MASK              (0x3 << 6)
+#define RT5677_IF2_ADC1_SWAP_SFT               6
+#define RT5677_IF2_ADC2_SWAP_MASK              (0x3 << 4)
+#define RT5677_IF2_ADC2_SWAP_SFT               4
+#define RT5677_IF2_ADC3_SWAP_MASK              (0x3 << 2)
+#define RT5677_IF2_ADC3_SWAP_SFT               2
+#define RT5677_IF2_ADC4_SWAP_MASK              (0x3 << 0)
+#define RT5677_IF2_ADC4_SWAP_SFT               0
+
+/* TDM2 control 2 (0x41) */
 #define RT5677_IF2_ADC4_MASK                   (0x3 << 10)
 #define RT5677_IF2_ADC4_SFT                    10
 #define RT5677_IF2_ADC3_MASK                   (0x3 << 8)
 #define RT5677_IF2_ADC2_SFT                    6
 #define RT5677_IF2_ADC1_MASK                   (0x3 << 4)
 #define RT5677_IF2_ADC1_SFT                    4
+#define RT5677_IF2_ADC_CTRL_MASK               (0x7 << 0)
+#define RT5677_IF2_ADC_CTRL_SFT                        0
+
+/* TDM2 control 4 (0x43) */
+#define RT5677_IF2_DAC0_MASK                   (0x7 << 12)
+#define RT5677_IF2_DAC0_SFT                    12
+#define RT5677_IF2_DAC1_MASK                   (0x7 << 8)
+#define RT5677_IF2_DAC1_SFT                    8
+#define RT5677_IF2_DAC2_MASK                   (0x7 << 4)
+#define RT5677_IF2_DAC2_SFT                    4
+#define RT5677_IF2_DAC3_MASK                   (0x7 << 0)
+#define RT5677_IF2_DAC3_SFT                    0
+
+/* TDM2 control 5 (0x44) */
+#define RT5677_IF2_DAC4_MASK                   (0x7 << 12)
+#define RT5677_IF2_DAC4_SFT                    12
+#define RT5677_IF2_DAC5_MASK                   (0x7 << 8)
+#define RT5677_IF2_DAC5_SFT                    8
+#define RT5677_IF2_DAC6_MASK                   (0x7 << 4)
+#define RT5677_IF2_DAC6_SFT                    4
+#define RT5677_IF2_DAC7_MASK                   (0x7 << 0)
+#define RT5677_IF2_DAC7_SFT                    0
 
 /* Digital Microphone Control 1 (0x50) */
 #define RT5677_DMIC_1_EN_MASK                  (0x1 << 15)
 #define RT5677_SEL_SRC_IB01                    (0x1 << 0)
 #define RT5677_SEL_SRC_IB01_SFT                        0
 
+/* Jack Detect Control 1 (0xb5) */
+#define RT5677_SEL_GPIO_JD1_MASK               (0x3 << 14)
+#define RT5677_SEL_GPIO_JD1_SFT                        14
+#define RT5677_SEL_GPIO_JD2_MASK               (0x3 << 12)
+#define RT5677_SEL_GPIO_JD2_SFT                        12
+#define RT5677_SEL_GPIO_JD3_MASK               (0x3 << 10)
+#define RT5677_SEL_GPIO_JD3_SFT                        10
+
+/* IRQ Control 1 (0xbd) */
+#define RT5677_STA_GPIO_JD1                    (0x1 << 15)
+#define RT5677_STA_GPIO_JD1_SFT                        15
+#define RT5677_EN_IRQ_GPIO_JD1                 (0x1 << 14)
+#define RT5677_EN_IRQ_GPIO_JD1_SFT             14
+#define RT5677_EN_GPIO_JD1_STICKY              (0x1 << 13)
+#define RT5677_EN_GPIO_JD1_STICKY_SFT          13
+#define RT5677_INV_GPIO_JD1                    (0x1 << 12)
+#define RT5677_INV_GPIO_JD1_SFT                        12
+#define RT5677_STA_GPIO_JD2                    (0x1 << 11)
+#define RT5677_STA_GPIO_JD2_SFT                        11
+#define RT5677_EN_IRQ_GPIO_JD2                 (0x1 << 10)
+#define RT5677_EN_IRQ_GPIO_JD2_SFT             10
+#define RT5677_EN_GPIO_JD2_STICKY              (0x1 << 9)
+#define RT5677_EN_GPIO_JD2_STICKY_SFT          9
+#define RT5677_INV_GPIO_JD2                    (0x1 << 8)
+#define RT5677_INV_GPIO_JD2_SFT                        8
+#define RT5677_STA_MICBIAS1_OVCD               (0x1 << 7)
+#define RT5677_STA_MICBIAS1_OVCD_SFT           7
+#define RT5677_EN_IRQ_MICBIAS1_OVCD            (0x1 << 6)
+#define RT5677_EN_IRQ_MICBIAS1_OVCD_SFT                6
+#define RT5677_EN_MICBIAS1_OVCD_STICKY         (0x1 << 5)
+#define RT5677_EN_MICBIAS1_OVCD_STICKY_SFT     5
+#define RT5677_INV_MICBIAS1_OVCD               (0x1 << 4)
+#define RT5677_INV_MICBIAS1_OVCD_SFT           4
+#define RT5677_STA_GPIO_JD3                    (0x1 << 3)
+#define RT5677_STA_GPIO_JD3_SFT                        3
+#define RT5677_EN_IRQ_GPIO_JD3                 (0x1 << 2)
+#define RT5677_EN_IRQ_GPIO_JD3_SFT             2
+#define RT5677_EN_GPIO_JD3_STICKY              (0x1 << 1)
+#define RT5677_EN_GPIO_JD3_STICKY_SFT          1
+#define RT5677_INV_GPIO_JD3                    (0x1 << 0)
+#define RT5677_INV_GPIO_JD3_SFT                        0
+
 /* GPIO status (0xbf) */
 #define RT5677_GPIO6_STATUS_MASK               (0x1 << 5)
 #define RT5677_GPIO6_STATUS_SFT                        5
 #define RT5677_GPIO5_FUNC_GPIO                 (0x0 << 9)
 #define RT5677_GPIO5_FUNC_DMIC                 (0x1 << 9)
 
+#define RT5677_FIRMWARE1       "rt5677_dsp_fw1.bin"
+#define RT5677_FIRMWARE2       "rt5677_dsp_fw2.bin"
+
 /* System Clock Source */
 enum {
        RT5677_SCLK_S_MCLK,
@@ -1541,10 +1659,18 @@ enum {
        RT5677_GPIO_NUM,
 };
 
+enum {
+       RT5677_IRQ_JD1,
+       RT5677_IRQ_JD2,
+       RT5677_IRQ_JD3,
+};
+
 struct rt5677_priv {
        struct snd_soc_codec *codec;
        struct rt5677_platform_data pdata;
-       struct regmap *regmap;
+       struct regmap *regmap, *regmap_physical;
+       const struct firmware *fw1, *fw2;
+       struct mutex dsp_cmd_lock, dsp_pri_lock;
 
        int sysclk;
        int sysclk_src;
@@ -1558,6 +1684,10 @@ struct rt5677_priv {
 #ifdef CONFIG_GPIOLIB
        struct gpio_chip gpio_chip;
 #endif
+       bool dsp_vad_en;
+       struct regmap_irq_chip_data *irq_data;
+       bool is_dsp_mode;
+       bool is_vref_slow;
 };
 
 #endif /* __RT5677_H__ */
index dab9b15..29cf7ce 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/pm.h>
 #include <linux/i2c.h>
 #include <linux/clk.h>
+#include <linux/log2.h>
 #include <linux/regmap.h>
 #include <linux/regulator/driver.h>
 #include <linux/regulator/machine.h>
@@ -121,6 +122,13 @@ struct ldo_regulator {
        bool enabled;
 };
 
+enum sgtl5000_micbias_resistor {
+       SGTL5000_MICBIAS_OFF = 0,
+       SGTL5000_MICBIAS_2K = 2,
+       SGTL5000_MICBIAS_4K = 4,
+       SGTL5000_MICBIAS_8K = 8,
+};
+
 /* sgtl5000 private structure in codec */
 struct sgtl5000_priv {
        int sysclk;     /* sysclk rate */
@@ -131,6 +139,8 @@ struct sgtl5000_priv {
        struct regmap *regmap;
        struct clk *mclk;
        int revision;
+       u8 micbias_resistor;
+       u8 micbias_voltage;
 };
 
 /*
@@ -145,12 +155,14 @@ struct sgtl5000_priv {
 static int mic_bias_event(struct snd_soc_dapm_widget *w,
        struct snd_kcontrol *kcontrol, int event)
 {
+       struct sgtl5000_priv *sgtl5000 = snd_soc_codec_get_drvdata(w->codec);
+
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
-               /* change mic bias resistor to 4Kohm */
+               /* change mic bias resistor */
                snd_soc_update_bits(w->codec, SGTL5000_CHIP_MIC_CTRL,
-                               SGTL5000_BIAS_R_MASK,
-                               SGTL5000_BIAS_R_4k << SGTL5000_BIAS_R_SHIFT);
+                       SGTL5000_BIAS_R_MASK,
+                       sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
@@ -530,16 +542,16 @@ static int sgtl5000_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 
 /*
  * set clock according to i2s frame clock,
- * sgtl5000 provide 2 clock sources.
- * 1. sys_mclk. sample freq can only configure to
+ * sgtl5000 provides 2 clock sources:
+ * 1. sys_mclk: sample freq can only be configured to
  *     1/256, 1/384, 1/512 of sys_mclk.
- * 2. pll. can derive any audio clocks.
+ * 2. pll: can derive any audio clocks.
  *
  * clock setting rules:
- * 1. in slave mode, only sys_mclk can use.
- * 2. as constraint by sys_mclk, sample freq should
- *     set to 32k, 44.1k and above.
- * 3. using sys_mclk prefer to pll to save power.
+ * 1. in slave mode, only sys_mclk can be used
+ * 2. as constraint by sys_mclk, sample freq should be set to 32 kHz, 44.1 kHz
+ * and above.
+ * 3. usage of sys_mclk is preferred over pll to save power.
  */
 static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
 {
@@ -549,8 +561,8 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
 
        /*
         * sample freq should be divided by frame clock,
-        * if frame clock lower than 44.1khz, sample feq should set to
-        * 32khz or 44.1khz.
+        * if frame clock is lower than 44.1 kHz, sample freq should be set to
+        * 32 kHz or 44.1 kHz.
         */
        switch (frame_rate) {
        case 8000:
@@ -603,9 +615,10 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
 
        /*
         * calculate the divider of mclk/sample_freq,
-        * factor of freq =96k can only be 256, since mclk in range (12m,27m)
+        * factor of freq = 96 kHz can only be 256, since mclk is in the range
+        * of 8 MHz - 27 MHz
         */
-       switch (sgtl5000->sysclk / sys_fs) {
+       switch (sgtl5000->sysclk / frame_rate) {
        case 256:
                clk_ctl |= SGTL5000_MCLK_FREQ_256FS <<
                        SGTL5000_MCLK_FREQ_SHIFT;
@@ -619,7 +632,7 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
                        SGTL5000_MCLK_FREQ_SHIFT;
                break;
        default:
-               /* if mclk not satisify the divider, use pll */
+               /* if mclk does not satisfy the divider, use pll */
                if (sgtl5000->master) {
                        clk_ctl |= SGTL5000_MCLK_FREQ_PLL <<
                                SGTL5000_MCLK_FREQ_SHIFT;
@@ -628,7 +641,7 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
                                "PLL not supported in slave mode\n");
                        dev_err(codec->dev, "%d ratio is not supported. "
                                "SYS_MCLK needs to be 256, 384 or 512 * fs\n",
-                               sgtl5000->sysclk / sys_fs);
+                               sgtl5000->sysclk / frame_rate);
                        return -EINVAL;
                }
        }
@@ -795,7 +808,7 @@ static int ldo_regulator_enable(struct regulator_dev *dev)
                                SGTL5000_LINEREG_D_POWERUP,
                                SGTL5000_LINEREG_D_POWERUP);
 
-       /* when internal ldo enabled, simple digital power can be disabled */
+       /* when internal ldo is enabled, simple digital power can be disabled */
        snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
                                SGTL5000_LINREG_SIMPLE_POWERUP,
                                0);
@@ -1079,7 +1092,7 @@ static bool sgtl5000_readable(struct device *dev, unsigned int reg)
 /*
  * sgtl5000 has 3 internal power supplies:
  * 1. VAG, normally set to vdda/2
- * 2. chargepump, set to different value
+ * 2. charge pump, set to different value
  *     according to voltage of vdda and vddio
  * 3. line out VAG, normally set to vddio/2
  *
@@ -1325,8 +1338,13 @@ static int sgtl5000_probe(struct snd_soc_codec *codec)
                        SGTL5000_HP_ZCD_EN |
                        SGTL5000_ADC_ZCD_EN);
 
-       snd_soc_write(codec, SGTL5000_CHIP_MIC_CTRL, 2);
+       snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+                       SGTL5000_BIAS_R_MASK,
+                       sgtl5000->micbias_resistor << SGTL5000_BIAS_R_SHIFT);
 
+       snd_soc_update_bits(codec, SGTL5000_CHIP_MIC_CTRL,
+                       SGTL5000_BIAS_R_MASK,
+                       sgtl5000->micbias_voltage << SGTL5000_BIAS_R_SHIFT);
        /*
         * disable DAP
         * TODO:
@@ -1416,10 +1434,10 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
 {
        struct sgtl5000_priv *sgtl5000;
        int ret, reg, rev;
-       unsigned int mclk;
+       struct device_node *np = client->dev.of_node;
+       u32 value;
 
-       sgtl5000 = devm_kzalloc(&client->dev, sizeof(struct sgtl5000_priv),
-                                                               GFP_KERNEL);
+       sgtl5000 = devm_kzalloc(&client->dev, sizeof(*sgtl5000), GFP_KERNEL);
        if (!sgtl5000)
                return -ENOMEM;
 
@@ -1440,14 +1458,6 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
                return ret;
        }
 
-       /* SGTL5000 SYS_MCLK should be between 8 and 27 MHz */
-       mclk = clk_get_rate(sgtl5000->mclk);
-       if (mclk < 8000000 || mclk > 27000000) {
-               dev_err(&client->dev, "Invalid SYS_CLK frequency: %u.%03uMHz\n",
-                       mclk / 1000000, mclk / 1000 % 1000);
-               return -EINVAL;
-       }
-
        ret = clk_prepare_enable(sgtl5000->mclk);
        if (ret)
                return ret;
@@ -1469,6 +1479,47 @@ static int sgtl5000_i2c_probe(struct i2c_client *client,
        dev_info(&client->dev, "sgtl5000 revision 0x%x\n", rev);
        sgtl5000->revision = rev;
 
+       if (np) {
+               if (!of_property_read_u32(np,
+                       "micbias-resistor-k-ohms", &value)) {
+                       switch (value) {
+                       case SGTL5000_MICBIAS_OFF:
+                               sgtl5000->micbias_resistor = 0;
+                               break;
+                       case SGTL5000_MICBIAS_2K:
+                               sgtl5000->micbias_resistor = 1;
+                               break;
+                       case SGTL5000_MICBIAS_4K:
+                               sgtl5000->micbias_resistor = 2;
+                               break;
+                       case SGTL5000_MICBIAS_8K:
+                               sgtl5000->micbias_resistor = 3;
+                               break;
+                       default:
+                               sgtl5000->micbias_resistor = 2;
+                               dev_err(&client->dev,
+                                       "Unsuitable MicBias resistor\n");
+                       }
+               } else {
+                       /* default is 4Kohms */
+                       sgtl5000->micbias_resistor = 2;
+               }
+               if (!of_property_read_u32(np,
+                       "micbias-voltage-m-volts", &value)) {
+                       /* 1250mV => 0 */
+                       /* steps of 250mV */
+                       if ((value >= 1250) && (value <= 3000))
+                               sgtl5000->micbias_voltage = (value / 250) - 5;
+                       else {
+                               sgtl5000->micbias_voltage = 0;
+                               dev_err(&client->dev,
+                                       "Unsuitable MicBias resistor\n");
+                       }
+               } else {
+                       sgtl5000->micbias_voltage = 0;
+               }
+       }
+
        i2c_set_clientdata(client, sgtl5000);
 
        /* Ensure sgtl5000 will start with sane register values */
index 246081a..21ca3a5 100644 (file)
@@ -6,29 +6,88 @@
  * Licensed under the GPL-2 or later.
  */
 
-#include <linux/i2c.h>
 #include <linux/export.h>
+#include <linux/i2c.h>
 #include <linux/module.h>
+#include <linux/slab.h>
+#include <asm/unaligned.h>
 
 #include "sigmadsp.h"
 
-static int sigma_action_write_i2c(void *control_data,
-       const struct sigma_action *sa, size_t len)
+static int sigmadsp_write_i2c(void *control_data,
+       unsigned int addr, const uint8_t data[], size_t len)
+{
+       uint8_t *buf;
+       int ret;
+
+       buf = kzalloc(2 + len, GFP_KERNEL | GFP_DMA);
+       if (!buf)
+               return -ENOMEM;
+
+       put_unaligned_be16(addr, buf);
+       memcpy(buf + 2, data, len);
+
+       ret = i2c_master_send(control_data, buf, len + 2);
+
+       kfree(buf);
+
+       return ret;
+}
+
+static int sigmadsp_read_i2c(void *control_data,
+       unsigned int addr, uint8_t data[], size_t len)
 {
-       return i2c_master_send(control_data, (const unsigned char *)&sa->addr,
-               len);
+       struct i2c_client *client = control_data;
+       struct i2c_msg msgs[2];
+       uint8_t buf[2];
+       int ret;
+
+       put_unaligned_be16(addr, buf);
+
+       msgs[0].addr = client->addr;
+       msgs[0].len = sizeof(buf);
+       msgs[0].buf = buf;
+       msgs[0].flags = 0;
+
+       msgs[1].addr = client->addr;
+       msgs[1].len = len;
+       msgs[1].buf = data;
+       msgs[1].flags = I2C_M_RD;
+
+       ret = i2c_transfer(client->adapter, msgs, ARRAY_SIZE(msgs));
+       if (ret < 0)
+               return ret;
+       else if (ret != ARRAY_SIZE(msgs))
+               return -EIO;
+       return 0;
 }
 
-int process_sigma_firmware(struct i2c_client *client, const char *name)
+/**
+ * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance
+ * @client: The parent I2C device
+ * @ops: The sigmadsp_ops to use for this instance
+ * @firmware_name: Name of the firmware file to load
+ *
+ * Allocates a SigmaDSP instance and loads the specified firmware file.
+ *
+ * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error.
+ */
+struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client,
+       const struct sigmadsp_ops *ops, const char *firmware_name)
 {
-       struct sigma_firmware ssfw;
+       struct sigmadsp *sigmadsp;
+
+       sigmadsp = devm_sigmadsp_init(&client->dev, ops, firmware_name);
+       if (IS_ERR(sigmadsp))
+               return sigmadsp;
 
-       ssfw.control_data = client;
-       ssfw.write = sigma_action_write_i2c;
+       sigmadsp->control_data = client;
+       sigmadsp->write = sigmadsp_write_i2c;
+       sigmadsp->read = sigmadsp_read_i2c;
 
-       return _process_sigma_firmware(&client->dev, &ssfw, name);
+       return sigmadsp;
 }
-EXPORT_SYMBOL(process_sigma_firmware);
+EXPORT_SYMBOL_GPL(devm_sigmadsp_init_i2c);
 
 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 MODULE_DESCRIPTION("SigmaDSP I2C firmware loader");
index f78ed8d..912861b 100644 (file)
 
 #include "sigmadsp.h"
 
-static int sigma_action_write_regmap(void *control_data,
-       const struct sigma_action *sa, size_t len)
+static int sigmadsp_write_regmap(void *control_data,
+       unsigned int addr, const uint8_t data[], size_t len)
 {
-       return regmap_raw_write(control_data, be16_to_cpu(sa->addr),
-               sa->payload, len - 2);
+       return regmap_raw_write(control_data, addr,
+               data, len);
 }
 
-int process_sigma_firmware_regmap(struct device *dev, struct regmap *regmap,
-       const char *name)
+static int sigmadsp_read_regmap(void *control_data,
+       unsigned int addr, uint8_t data[], size_t len)
 {
-       struct sigma_firmware ssfw;
+       return regmap_raw_read(control_data, addr,
+               data, len);
+}
+
+/**
+ * devm_sigmadsp_init_i2c() - Initialize SigmaDSP instance
+ * @dev: The parent device
+ * @regmap: Regmap instance to use
+ * @ops: The sigmadsp_ops to use for this instance
+ * @firmware_name: Name of the firmware file to load
+ *
+ * Allocates a SigmaDSP instance and loads the specified firmware file.
+ *
+ * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error.
+ */
+struct sigmadsp *devm_sigmadsp_init_regmap(struct device *dev,
+       struct regmap *regmap, const struct sigmadsp_ops *ops,
+       const char *firmware_name)
+{
+       struct sigmadsp *sigmadsp;
+
+       sigmadsp = devm_sigmadsp_init(dev, ops, firmware_name);
+       if (IS_ERR(sigmadsp))
+               return sigmadsp;
 
-       ssfw.control_data = regmap;
-       ssfw.write = sigma_action_write_regmap;
+       sigmadsp->control_data = regmap;
+       sigmadsp->write = sigmadsp_write_regmap;
+       sigmadsp->read = sigmadsp_read_regmap;
 
-       return _process_sigma_firmware(dev, &ssfw, name);
+       return sigmadsp;
 }
-EXPORT_SYMBOL(process_sigma_firmware_regmap);
+EXPORT_SYMBOL_GPL(devm_sigmadsp_init_regmap);
 
 MODULE_AUTHOR("Lars-Peter Clausen <lars@metafoo.de>");
 MODULE_DESCRIPTION("SigmaDSP regmap firmware loader");
index f2de7e0..d53680a 100644 (file)
@@ -1,23 +1,74 @@
 /*
  * Load Analog Devices SigmaStudio firmware files
  *
- * Copyright 2009-2011 Analog Devices Inc.
+ * Copyright 2009-2014 Analog Devices Inc.
  *
  * Licensed under the GPL-2 or later.
  */
 
 #include <linux/crc32.h>
-#include <linux/delay.h>
 #include <linux/firmware.h>
 #include <linux/kernel.h>
 #include <linux/i2c.h>
 #include <linux/regmap.h>
 #include <linux/module.h>
+#include <linux/slab.h>
+
+#include <sound/control.h>
+#include <sound/soc.h>
 
 #include "sigmadsp.h"
 
 #define SIGMA_MAGIC "ADISIGM"
 
+#define SIGMA_FW_CHUNK_TYPE_DATA 0
+#define SIGMA_FW_CHUNK_TYPE_CONTROL 1
+#define SIGMA_FW_CHUNK_TYPE_SAMPLERATES 2
+
+struct sigmadsp_control {
+       struct list_head head;
+       uint32_t samplerates;
+       unsigned int addr;
+       unsigned int num_bytes;
+       const char *name;
+       struct snd_kcontrol *kcontrol;
+       bool cached;
+       uint8_t cache[];
+};
+
+struct sigmadsp_data {
+       struct list_head head;
+       uint32_t samplerates;
+       unsigned int addr;
+       unsigned int length;
+       uint8_t data[];
+};
+
+struct sigma_fw_chunk {
+       __le32 length;
+       __le32 tag;
+       __le32 samplerates;
+} __packed;
+
+struct sigma_fw_chunk_data {
+       struct sigma_fw_chunk chunk;
+       __le16 addr;
+       uint8_t data[];
+} __packed;
+
+struct sigma_fw_chunk_control {
+       struct sigma_fw_chunk chunk;
+       __le16 type;
+       __le16 addr;
+       __le16 num_bytes;
+       const char name[];
+} __packed;
+
+struct sigma_fw_chunk_samplerate {
+       struct sigma_fw_chunk chunk;
+       __le32 samplerates[];
+} __packed;
+
 struct sigma_firmware_header {
        unsigned char magic[7];
        u8 version;
@@ -28,12 +79,286 @@ enum {
        SIGMA_ACTION_WRITEXBYTES = 0,
        SIGMA_ACTION_WRITESINGLE,
        SIGMA_ACTION_WRITESAFELOAD,
-       SIGMA_ACTION_DELAY,
-       SIGMA_ACTION_PLLWAIT,
-       SIGMA_ACTION_NOOP,
        SIGMA_ACTION_END,
 };
 
+struct sigma_action {
+       u8 instr;
+       u8 len_hi;
+       __le16 len;
+       __be16 addr;
+       unsigned char payload[];
+} __packed;
+
+static int sigmadsp_write(struct sigmadsp *sigmadsp, unsigned int addr,
+       const uint8_t data[], size_t len)
+{
+       return sigmadsp->write(sigmadsp->control_data, addr, data, len);
+}
+
+static int sigmadsp_read(struct sigmadsp *sigmadsp, unsigned int addr,
+       uint8_t data[], size_t len)
+{
+       return sigmadsp->read(sigmadsp->control_data, addr, data, len);
+}
+
+static int sigmadsp_ctrl_info(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *info)
+{
+       struct sigmadsp_control *ctrl = (void *)kcontrol->private_value;
+
+       info->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       info->count = ctrl->num_bytes;
+
+       return 0;
+}
+
+static int sigmadsp_ctrl_write(struct sigmadsp *sigmadsp,
+       struct sigmadsp_control *ctrl, void *data)
+{
+       /* safeload loads up to 20 bytes in a atomic operation */
+       if (ctrl->num_bytes > 4 && ctrl->num_bytes <= 20 && sigmadsp->ops &&
+           sigmadsp->ops->safeload)
+               return sigmadsp->ops->safeload(sigmadsp, ctrl->addr, data,
+                       ctrl->num_bytes);
+       else
+               return sigmadsp_write(sigmadsp, ctrl->addr, data,
+                       ctrl->num_bytes);
+}
+
+static int sigmadsp_ctrl_put(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct sigmadsp_control *ctrl = (void *)kcontrol->private_value;
+       struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol);
+       uint8_t *data;
+       int ret = 0;
+
+       mutex_lock(&sigmadsp->lock);
+
+       data = ucontrol->value.bytes.data;
+
+       if (!(kcontrol->vd[0].access & SNDRV_CTL_ELEM_ACCESS_INACTIVE))
+               ret = sigmadsp_ctrl_write(sigmadsp, ctrl, data);
+
+       if (ret == 0) {
+               memcpy(ctrl->cache, data, ctrl->num_bytes);
+               ctrl->cached = true;
+       }
+
+       mutex_unlock(&sigmadsp->lock);
+
+       return ret;
+}
+
+static int sigmadsp_ctrl_get(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct sigmadsp_control *ctrl = (void *)kcontrol->private_value;
+       struct sigmadsp *sigmadsp = snd_kcontrol_chip(kcontrol);
+       int ret = 0;
+
+       mutex_lock(&sigmadsp->lock);
+
+       if (!ctrl->cached) {
+               ret = sigmadsp_read(sigmadsp, ctrl->addr, ctrl->cache,
+                       ctrl->num_bytes);
+       }
+
+       if (ret == 0) {
+               ctrl->cached = true;
+               memcpy(ucontrol->value.bytes.data, ctrl->cache,
+                       ctrl->num_bytes);
+       }
+
+       mutex_unlock(&sigmadsp->lock);
+
+       return ret;
+}
+
+static void sigmadsp_control_free(struct snd_kcontrol *kcontrol)
+{
+       struct sigmadsp_control *ctrl = (void *)kcontrol->private_value;
+
+       ctrl->kcontrol = NULL;
+}
+
+static bool sigma_fw_validate_control_name(const char *name, unsigned int len)
+{
+       unsigned int i;
+
+       for (i = 0; i < len; i++) {
+               /* Normal ASCII characters are valid */
+               if (name[i] < ' ' || name[i] > '~')
+                       return false;
+       }
+
+       return true;
+}
+
+static int sigma_fw_load_control(struct sigmadsp *sigmadsp,
+       const struct sigma_fw_chunk *chunk, unsigned int length)
+{
+       const struct sigma_fw_chunk_control *ctrl_chunk;
+       struct sigmadsp_control *ctrl;
+       unsigned int num_bytes;
+       size_t name_len;
+       char *name;
+       int ret;
+
+       if (length <= sizeof(*ctrl_chunk))
+               return -EINVAL;
+
+       ctrl_chunk = (const struct sigma_fw_chunk_control *)chunk;
+
+       name_len = length - sizeof(*ctrl_chunk);
+       if (name_len >= SNDRV_CTL_ELEM_ID_NAME_MAXLEN)
+               name_len = SNDRV_CTL_ELEM_ID_NAME_MAXLEN - 1;
+
+       /* Make sure there are no non-displayable characaters in the string */
+       if (!sigma_fw_validate_control_name(ctrl_chunk->name, name_len))
+               return -EINVAL;
+
+       num_bytes = le16_to_cpu(ctrl_chunk->num_bytes);
+       ctrl = kzalloc(sizeof(*ctrl) + num_bytes, GFP_KERNEL);
+       if (!ctrl)
+               return -ENOMEM;
+
+       name = kzalloc(name_len + 1, GFP_KERNEL);
+       if (!name) {
+               ret = -ENOMEM;
+               goto err_free_ctrl;
+       }
+       memcpy(name, ctrl_chunk->name, name_len);
+       name[name_len] = '\0';
+       ctrl->name = name;
+
+       ctrl->addr = le16_to_cpu(ctrl_chunk->addr);
+       ctrl->num_bytes = num_bytes;
+       ctrl->samplerates = le32_to_cpu(chunk->samplerates);
+
+       list_add_tail(&ctrl->head, &sigmadsp->ctrl_list);
+
+       return 0;
+
+err_free_ctrl:
+       kfree(ctrl);
+
+       return ret;
+}
+
+static int sigma_fw_load_data(struct sigmadsp *sigmadsp,
+       const struct sigma_fw_chunk *chunk, unsigned int length)
+{
+       const struct sigma_fw_chunk_data *data_chunk;
+       struct sigmadsp_data *data;
+
+       if (length <= sizeof(*data_chunk))
+               return -EINVAL;
+
+       data_chunk = (struct sigma_fw_chunk_data *)chunk;
+
+       length -= sizeof(*data_chunk);
+
+       data = kzalloc(sizeof(*data) + length, GFP_KERNEL);
+       if (!data)
+               return -ENOMEM;
+
+       data->addr = le16_to_cpu(data_chunk->addr);
+       data->length = length;
+       data->samplerates = le32_to_cpu(chunk->samplerates);
+       memcpy(data->data, data_chunk->data, length);
+       list_add_tail(&data->head, &sigmadsp->data_list);
+
+       return 0;
+}
+
+static int sigma_fw_load_samplerates(struct sigmadsp *sigmadsp,
+       const struct sigma_fw_chunk *chunk, unsigned int length)
+{
+       const struct sigma_fw_chunk_samplerate *rate_chunk;
+       unsigned int num_rates;
+       unsigned int *rates;
+       unsigned int i;
+
+       rate_chunk = (const struct sigma_fw_chunk_samplerate *)chunk;
+
+       num_rates = (length - sizeof(*rate_chunk)) / sizeof(__le32);
+
+       if (num_rates > 32 || num_rates == 0)
+               return -EINVAL;
+
+       /* We only allow one samplerates block per file */
+       if (sigmadsp->rate_constraints.count)
+               return -EINVAL;
+
+       rates = kcalloc(num_rates, sizeof(*rates), GFP_KERNEL);
+       if (!rates)
+               return -ENOMEM;
+
+       for (i = 0; i < num_rates; i++)
+               rates[i] = le32_to_cpu(rate_chunk->samplerates[i]);
+
+       sigmadsp->rate_constraints.count = num_rates;
+       sigmadsp->rate_constraints.list = rates;
+
+       return 0;
+}
+
+static int sigmadsp_fw_load_v2(struct sigmadsp *sigmadsp,
+       const struct firmware *fw)
+{
+       struct sigma_fw_chunk *chunk;
+       unsigned int length, pos;
+       int ret;
+
+       /*
+        * Make sure that there is at least one chunk to avoid integer
+        * underflows later on. Empty firmware is still valid though.
+        */
+       if (fw->size < sizeof(*chunk) + sizeof(struct sigma_firmware_header))
+               return 0;
+
+       pos = sizeof(struct sigma_firmware_header);
+
+       while (pos < fw->size - sizeof(*chunk)) {
+               chunk = (struct sigma_fw_chunk *)(fw->data + pos);
+
+               length = le32_to_cpu(chunk->length);
+
+               if (length > fw->size - pos || length < sizeof(*chunk))
+                       return -EINVAL;
+
+               switch (le32_to_cpu(chunk->tag)) {
+               case SIGMA_FW_CHUNK_TYPE_DATA:
+                       ret = sigma_fw_load_data(sigmadsp, chunk, length);
+                       break;
+               case SIGMA_FW_CHUNK_TYPE_CONTROL:
+                       ret = sigma_fw_load_control(sigmadsp, chunk, length);
+                       break;
+               case SIGMA_FW_CHUNK_TYPE_SAMPLERATES:
+                       ret = sigma_fw_load_samplerates(sigmadsp, chunk, length);
+                       break;
+               default:
+                       dev_warn(sigmadsp->dev, "Unknown chunk type: %d\n",
+                               chunk->tag);
+                       ret = 0;
+                       break;
+               }
+
+               if (ret)
+                       return ret;
+
+               /*
+                * This can not overflow since if length is larger than the
+                * maximum firmware size (0x4000000) we'll error out earilier.
+                */
+               pos += ALIGN(length, sizeof(__le32));
+       }
+
+       return 0;
+}
+
 static inline u32 sigma_action_len(struct sigma_action *sa)
 {
        return (sa->len_hi << 16) | le16_to_cpu(sa->len);
@@ -62,11 +387,11 @@ static size_t sigma_action_size(struct sigma_action *sa)
  * Returns a negative error value in case of an error, 0 if processing of
  * the firmware should be stopped after this action, 1 otherwise.
  */
-static int
-process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa)
+static int process_sigma_action(struct sigmadsp *sigmadsp,
+       struct sigma_action *sa)
 {
        size_t len = sigma_action_len(sa);
-       int ret;
+       struct sigmadsp_data *data;
 
        pr_debug("%s: instr:%i addr:%#x len:%zu\n", __func__,
                sa->instr, sa->addr, len);
@@ -75,13 +400,17 @@ process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa)
        case SIGMA_ACTION_WRITEXBYTES:
        case SIGMA_ACTION_WRITESINGLE:
        case SIGMA_ACTION_WRITESAFELOAD:
-               ret = ssfw->write(ssfw->control_data, sa, len);
-               if (ret < 0)
+               if (len < 3)
                        return -EINVAL;
-               break;
-       case SIGMA_ACTION_DELAY:
-               udelay(len);
-               len = 0;
+
+               data = kzalloc(sizeof(*data) + len - 2, GFP_KERNEL);
+               if (!data)
+                       return -ENOMEM;
+
+               data->addr = be16_to_cpu(sa->addr);
+               data->length = len - 2;
+               memcpy(data->data, sa->payload, data->length);
+               list_add_tail(&data->head, &sigmadsp->data_list);
                break;
        case SIGMA_ACTION_END:
                return 0;
@@ -92,22 +421,24 @@ process_sigma_action(struct sigma_firmware *ssfw, struct sigma_action *sa)
        return 1;
 }
 
-static int
-process_sigma_actions(struct sigma_firmware *ssfw)
+static int sigmadsp_fw_load_v1(struct sigmadsp *sigmadsp,
+       const struct firmware *fw)
 {
        struct sigma_action *sa;
-       size_t size;
+       size_t size, pos;
        int ret;
 
-       while (ssfw->pos + sizeof(*sa) <= ssfw->fw->size) {
-               sa = (struct sigma_action *)(ssfw->fw->data + ssfw->pos);
+       pos = sizeof(struct sigma_firmware_header);
+
+       while (pos + sizeof(*sa) <= fw->size) {
+               sa = (struct sigma_action *)(fw->data + pos);
 
                size = sigma_action_size(sa);
-               ssfw->pos += size;
-               if (ssfw->pos > ssfw->fw->size || size == 0)
+               pos += size;
+               if (pos > fw->size || size == 0)
                        break;
 
-               ret = process_sigma_action(ssfw, sa);
+               ret = process_sigma_action(sigmadsp, sa);
 
                pr_debug("%s: action returned %i\n", __func__, ret);
 
@@ -115,29 +446,47 @@ process_sigma_actions(struct sigma_firmware *ssfw)
                        return ret;
        }
 
-       if (ssfw->pos != ssfw->fw->size)
+       if (pos != fw->size)
                return -EINVAL;
 
        return 0;
 }
 
-int _process_sigma_firmware(struct device *dev,
-       struct sigma_firmware *ssfw, const char *name)
+static void sigmadsp_firmware_release(struct sigmadsp *sigmadsp)
 {
-       int ret;
-       struct sigma_firmware_header *ssfw_head;
+       struct sigmadsp_control *ctrl, *_ctrl;
+       struct sigmadsp_data *data, *_data;
+
+       list_for_each_entry_safe(ctrl, _ctrl, &sigmadsp->ctrl_list, head) {
+               kfree(ctrl->name);
+               kfree(ctrl);
+       }
+
+       list_for_each_entry_safe(data, _data, &sigmadsp->data_list, head)
+               kfree(data);
+
+       INIT_LIST_HEAD(&sigmadsp->ctrl_list);
+       INIT_LIST_HEAD(&sigmadsp->data_list);
+}
+
+static void devm_sigmadsp_release(struct device *dev, void *res)
+{
+       sigmadsp_firmware_release((struct sigmadsp *)res);
+}
+
+static int sigmadsp_firmware_load(struct sigmadsp *sigmadsp, const char *name)
+{
+       const struct sigma_firmware_header *ssfw_head;
        const struct firmware *fw;
+       int ret;
        u32 crc;
 
-       pr_debug("%s: loading firmware %s\n", __func__, name);
-
        /* first load the blob */
-       ret = request_firmware(&fw, name, dev);
+       ret = request_firmware(&fw, name, sigmadsp->dev);
        if (ret) {
                pr_debug("%s: request_firmware() failed with %i\n", __func__, ret);
-               return ret;
+               goto done;
        }
-       ssfw->fw = fw;
 
        /* then verify the header */
        ret = -EINVAL;
@@ -149,13 +498,13 @@ int _process_sigma_firmware(struct device *dev,
         * overflows later in the loading process.
         */
        if (fw->size < sizeof(*ssfw_head) || fw->size >= 0x4000000) {
-               dev_err(dev, "Failed to load firmware: Invalid size\n");
+               dev_err(sigmadsp->dev, "Failed to load firmware: Invalid size\n");
                goto done;
        }
 
        ssfw_head = (void *)fw->data;
        if (memcmp(ssfw_head->magic, SIGMA_MAGIC, ARRAY_SIZE(ssfw_head->magic))) {
-               dev_err(dev, "Failed to load firmware: Invalid magic\n");
+               dev_err(sigmadsp->dev, "Failed to load firmware: Invalid magic\n");
                goto done;
        }
 
@@ -163,23 +512,303 @@ int _process_sigma_firmware(struct device *dev,
                        fw->size - sizeof(*ssfw_head));
        pr_debug("%s: crc=%x\n", __func__, crc);
        if (crc != le32_to_cpu(ssfw_head->crc)) {
-               dev_err(dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n",
+               dev_err(sigmadsp->dev, "Failed to load firmware: Wrong crc checksum: expected %x got %x\n",
                        le32_to_cpu(ssfw_head->crc), crc);
                goto done;
        }
 
-       ssfw->pos = sizeof(*ssfw_head);
+       switch (ssfw_head->version) {
+       case 1:
+               ret = sigmadsp_fw_load_v1(sigmadsp, fw);
+               break;
+       case 2:
+               ret = sigmadsp_fw_load_v2(sigmadsp, fw);
+               break;
+       default:
+               dev_err(sigmadsp->dev,
+                       "Failed to load firmware: Invalid version %d. Supported firmware versions: 1, 2\n",
+                       ssfw_head->version);
+               ret = -EINVAL;
+               break;
+       }
 
-       /* finally process all of the actions */
-       ret = process_sigma_actions(ssfw);
+       if (ret)
+               sigmadsp_firmware_release(sigmadsp);
 
- done:
+done:
        release_firmware(fw);
 
-       pr_debug("%s: loaded %s\n", __func__, name);
+       return ret;
+}
+
+static int sigmadsp_init(struct sigmadsp *sigmadsp, struct device *dev,
+       const struct sigmadsp_ops *ops, const char *firmware_name)
+{
+       sigmadsp->ops = ops;
+       sigmadsp->dev = dev;
+
+       INIT_LIST_HEAD(&sigmadsp->ctrl_list);
+       INIT_LIST_HEAD(&sigmadsp->data_list);
+       mutex_init(&sigmadsp->lock);
+
+       return sigmadsp_firmware_load(sigmadsp, firmware_name);
+}
+
+/**
+ * devm_sigmadsp_init() - Initialize SigmaDSP instance
+ * @dev: The parent device
+ * @ops: The sigmadsp_ops to use for this instance
+ * @firmware_name: Name of the firmware file to load
+ *
+ * Allocates a SigmaDSP instance and loads the specified firmware file.
+ *
+ * Returns a pointer to a struct sigmadsp on success, or a PTR_ERR() on error.
+ */
+struct sigmadsp *devm_sigmadsp_init(struct device *dev,
+       const struct sigmadsp_ops *ops, const char *firmware_name)
+{
+       struct sigmadsp *sigmadsp;
+       int ret;
+
+       sigmadsp = devres_alloc(devm_sigmadsp_release, sizeof(*sigmadsp),
+               GFP_KERNEL);
+       if (!sigmadsp)
+               return ERR_PTR(-ENOMEM);
+
+       ret = sigmadsp_init(sigmadsp, dev, ops, firmware_name);
+       if (ret) {
+               devres_free(sigmadsp);
+               return ERR_PTR(ret);
+       }
+
+       devres_add(dev, sigmadsp);
+
+       return sigmadsp;
+}
+EXPORT_SYMBOL_GPL(devm_sigmadsp_init);
+
+static int sigmadsp_rate_to_index(struct sigmadsp *sigmadsp, unsigned int rate)
+{
+       unsigned int i;
+
+       for (i = 0; i < sigmadsp->rate_constraints.count; i++) {
+               if (sigmadsp->rate_constraints.list[i] == rate)
+                       return i;
+       }
+
+       return -EINVAL;
+}
+
+static unsigned int sigmadsp_get_samplerate_mask(struct sigmadsp *sigmadsp,
+       unsigned int samplerate)
+{
+       int samplerate_index;
+
+       if (samplerate == 0)
+               return 0;
+
+       if (sigmadsp->rate_constraints.count) {
+               samplerate_index = sigmadsp_rate_to_index(sigmadsp, samplerate);
+               if (samplerate_index < 0)
+                       return 0;
+
+               return BIT(samplerate_index);
+       } else {
+               return ~0;
+       }
+}
+
+static bool sigmadsp_samplerate_valid(unsigned int supported,
+       unsigned int requested)
+{
+       /* All samplerates are supported */
+       if (!supported)
+               return true;
+
+       return supported & requested;
+}
+
+static int sigmadsp_alloc_control(struct sigmadsp *sigmadsp,
+       struct sigmadsp_control *ctrl, unsigned int samplerate_mask)
+{
+       struct snd_kcontrol_new template;
+       struct snd_kcontrol *kcontrol;
+
+       memset(&template, 0, sizeof(template));
+       template.iface = SNDRV_CTL_ELEM_IFACE_MIXER;
+       template.name = ctrl->name;
+       template.info = sigmadsp_ctrl_info;
+       template.get = sigmadsp_ctrl_get;
+       template.put = sigmadsp_ctrl_put;
+       template.private_value = (unsigned long)ctrl;
+       template.access = SNDRV_CTL_ELEM_ACCESS_READWRITE;
+       if (!sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask))
+               template.access |= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+
+       kcontrol = snd_ctl_new1(&template, sigmadsp);
+       if (!kcontrol)
+               return -ENOMEM;
+
+       kcontrol->private_free = sigmadsp_control_free;
+       ctrl->kcontrol = kcontrol;
+
+       return snd_ctl_add(sigmadsp->component->card->snd_card, kcontrol);
+}
+
+static void sigmadsp_activate_ctrl(struct sigmadsp *sigmadsp,
+       struct sigmadsp_control *ctrl, unsigned int samplerate_mask)
+{
+       struct snd_card *card = sigmadsp->component->card->snd_card;
+       struct snd_kcontrol_volatile *vd;
+       struct snd_ctl_elem_id id;
+       bool active;
+       bool changed = false;
+
+       active = sigmadsp_samplerate_valid(ctrl->samplerates, samplerate_mask);
+
+       down_write(&card->controls_rwsem);
+       if (!ctrl->kcontrol) {
+               up_write(&card->controls_rwsem);
+               return;
+       }
+
+       id = ctrl->kcontrol->id;
+       vd = &ctrl->kcontrol->vd[0];
+       if (active == (bool)(vd->access & SNDRV_CTL_ELEM_ACCESS_INACTIVE)) {
+               vd->access ^= SNDRV_CTL_ELEM_ACCESS_INACTIVE;
+               changed = true;
+       }
+       up_write(&card->controls_rwsem);
+
+       if (active && changed) {
+               mutex_lock(&sigmadsp->lock);
+               if (ctrl->cached)
+                       sigmadsp_ctrl_write(sigmadsp, ctrl, ctrl->cache);
+               mutex_unlock(&sigmadsp->lock);
+       }
+
+       if (changed)
+               snd_ctl_notify(card, SNDRV_CTL_EVENT_MASK_INFO, &id);
+}
+
+/**
+ * sigmadsp_attach() - Attach a sigmadsp instance to a ASoC component
+ * @sigmadsp: The sigmadsp instance to attach
+ * @component: The component to attach to
+ *
+ * Typically called in the components probe callback.
+ *
+ * Note, once this function has been called the firmware must not be released
+ * until after the ALSA snd_card that the component belongs to has been
+ * disconnected, even if sigmadsp_attach() returns an error.
+ */
+int sigmadsp_attach(struct sigmadsp *sigmadsp,
+       struct snd_soc_component *component)
+{
+       struct sigmadsp_control *ctrl;
+       unsigned int samplerate_mask;
+       int ret;
+
+       sigmadsp->component = component;
+
+       samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp,
+               sigmadsp->current_samplerate);
+
+       list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head) {
+               ret = sigmadsp_alloc_control(sigmadsp, ctrl, samplerate_mask);
+               if (ret)
+                       return ret;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sigmadsp_attach);
+
+/**
+ * sigmadsp_setup() - Setup the DSP for the specified samplerate
+ * @sigmadsp: The sigmadsp instance to configure
+ * @samplerate: The samplerate the DSP should be configured for
+ *
+ * Loads the appropriate firmware program and parameter memory (if not already
+ * loaded) and enables the controls for the specified samplerate. Any control
+ * parameter changes that have been made previously will be restored.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int samplerate)
+{
+       struct sigmadsp_control *ctrl;
+       unsigned int samplerate_mask;
+       struct sigmadsp_data *data;
+       int ret;
+
+       if (sigmadsp->current_samplerate == samplerate)
+               return 0;
+
+       samplerate_mask = sigmadsp_get_samplerate_mask(sigmadsp, samplerate);
+       if (samplerate_mask == 0)
+               return -EINVAL;
+
+       list_for_each_entry(data, &sigmadsp->data_list, head) {
+               if (!sigmadsp_samplerate_valid(data->samplerates,
+                   samplerate_mask))
+                       continue;
+               ret = sigmadsp_write(sigmadsp, data->addr, data->data,
+                       data->length);
+               if (ret)
+                       goto err;
+       }
+
+       list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head)
+               sigmadsp_activate_ctrl(sigmadsp, ctrl, samplerate_mask);
+
+       sigmadsp->current_samplerate = samplerate;
+
+       return 0;
+err:
+       sigmadsp_reset(sigmadsp);
 
        return ret;
 }
-EXPORT_SYMBOL_GPL(_process_sigma_firmware);
+EXPORT_SYMBOL_GPL(sigmadsp_setup);
+
+/**
+ * sigmadsp_reset() - Notify the sigmadsp instance that the DSP has been reset
+ * @sigmadsp: The sigmadsp instance to reset
+ *
+ * Should be called whenever the DSP has been reset and parameter and program
+ * memory need to be re-loaded.
+ */
+void sigmadsp_reset(struct sigmadsp *sigmadsp)
+{
+       struct sigmadsp_control *ctrl;
+
+       list_for_each_entry(ctrl, &sigmadsp->ctrl_list, head)
+               sigmadsp_activate_ctrl(sigmadsp, ctrl, false);
+
+       sigmadsp->current_samplerate = 0;
+}
+EXPORT_SYMBOL_GPL(sigmadsp_reset);
+
+/**
+ * sigmadsp_restrict_params() - Applies DSP firmware specific constraints
+ * @sigmadsp: The sigmadsp instance
+ * @substream: The substream to restrict
+ *
+ * Applies samplerate constraints that may be required by the firmware Should
+ * typically be called from the CODEC/component drivers startup callback.
+ *
+ * Returns 0 on success, a negative error code otherwise.
+ */
+int sigmadsp_restrict_params(struct sigmadsp *sigmadsp,
+       struct snd_pcm_substream *substream)
+{
+       if (sigmadsp->rate_constraints.count == 0)
+               return 0;
+
+       return snd_pcm_hw_constraint_list(substream->runtime, 0,
+               SNDRV_PCM_HW_PARAM_RATE, &sigmadsp->rate_constraints);
+}
+EXPORT_SYMBOL_GPL(sigmadsp_restrict_params);
 
 MODULE_LICENSE("GPL");
index c47cd23..614475c 100644 (file)
 
 #include <linux/device.h>
 #include <linux/regmap.h>
+#include <linux/list.h>
 
-struct sigma_action {
-       u8 instr;
-       u8 len_hi;
-       __le16 len;
-       __be16 addr;
-       unsigned char payload[];
-} __packed;
+#include <sound/pcm.h>
 
-struct sigma_firmware {
-       const struct firmware *fw;
-       size_t pos;
+struct sigmadsp;
+struct snd_soc_component;
+struct snd_pcm_substream;
+
+struct sigmadsp_ops {
+       int (*safeload)(struct sigmadsp *sigmadsp, unsigned int addr,
+                       const uint8_t *data, size_t len);
+};
+
+struct sigmadsp {
+       const struct sigmadsp_ops *ops;
+
+       struct list_head ctrl_list;
+       struct list_head data_list;
+
+       struct snd_pcm_hw_constraint_list rate_constraints;
+
+       unsigned int current_samplerate;
+       struct snd_soc_component *component;
+       struct device *dev;
+
+       struct mutex lock;
 
        void *control_data;
-       int (*write)(void *control_data, const struct sigma_action *sa,
-                       size_t len);
+       int (*write)(void *, unsigned int, const uint8_t *, size_t);
+       int (*read)(void *, unsigned int, uint8_t *, size_t);
 };
 
-int _process_sigma_firmware(struct device *dev,
-       struct sigma_firmware *ssfw, const char *name);
+struct sigmadsp *devm_sigmadsp_init(struct device *dev,
+       const struct sigmadsp_ops *ops, const char *firmware_name);
+void sigmadsp_reset(struct sigmadsp *sigmadsp);
+
+int sigmadsp_restrict_params(struct sigmadsp *sigmadsp,
+       struct snd_pcm_substream *substream);
 
 struct i2c_client;
 
-extern int process_sigma_firmware(struct i2c_client *client, const char *name);
-extern int process_sigma_firmware_regmap(struct device *dev,
-               struct regmap *regmap, const char *name);
+struct sigmadsp *devm_sigmadsp_init_regmap(struct device *dev,
+       struct regmap *regmap, const struct sigmadsp_ops *ops,
+       const char *firmware_name);
+struct sigmadsp *devm_sigmadsp_init_i2c(struct i2c_client *client,
+       const struct sigmadsp_ops *ops, const char *firmware_name);
+
+int sigmadsp_attach(struct sigmadsp *sigmadsp,
+       struct snd_soc_component *component);
+int sigmadsp_setup(struct sigmadsp *sigmadsp, unsigned int rate);
+void sigmadsp_reset(struct sigmadsp *sigmadsp);
 
 #endif
index 06ba492..07eea20 100644 (file)
@@ -120,7 +120,8 @@ static int atlas6_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
 {
 #define ATLAS6_CODEC_ENABLE_BITS (1 << 29)
 #define ATLAS6_CODEC_RESET_BITS (1 << 28)
-       struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
                enable_and_reset_codec(sirf_audio_codec->regmap,
@@ -142,7 +143,8 @@ static int prima2_codec_enable_and_reset_event(struct snd_soc_dapm_widget *w,
 {
 #define PRIMA2_CODEC_ENABLE_BITS (1 << 27)
 #define PRIMA2_CODEC_RESET_BITS (1 << 26)
-       struct sirf_audio_codec *sirf_audio_codec = dev_get_drvdata(w->codec->dev);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct sirf_audio_codec *sirf_audio_codec = snd_soc_codec_get_drvdata(codec);
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
                enable_and_reset_codec(sirf_audio_codec->regmap,
index cf8fa40..31d97cd 100644 (file)
@@ -867,25 +867,16 @@ static int sn95031_codec_probe(struct snd_soc_codec *codec)
        snd_soc_write(codec, SN95031_SSR2, 0x10);
        snd_soc_write(codec, SN95031_SSR3, 0x40);
 
-       snd_soc_add_codec_controls(codec, sn95031_snd_controls,
-                            ARRAY_SIZE(sn95031_snd_controls));
-
-       return 0;
-}
-
-static int sn95031_codec_remove(struct snd_soc_codec *codec)
-{
-       pr_debug("codec_remove called\n");
-       sn95031_set_vaud_bias(codec, SND_SOC_BIAS_OFF);
-
        return 0;
 }
 
 static struct snd_soc_codec_driver sn95031_codec = {
        .probe          = sn95031_codec_probe,
-       .remove         = sn95031_codec_remove,
        .set_bias_level = sn95031_set_vaud_bias,
        .idle_bias_off  = true,
+
+       .controls       = sn95031_snd_controls,
+       .num_controls   = ARRAY_SIZE(sn95031_snd_controls),
        .dapm_widgets   = sn95031_dapm_widgets,
        .num_dapm_widgets       = ARRAY_SIZE(sn95031_dapm_widgets),
        .dapm_routes    = sn95031_audio_map,
index 4b5c17f..a984485 100644 (file)
 #define SSM4567_DAC_FS_64000_96000     0x3
 #define SSM4567_DAC_FS_128000_192000   0x4
 
+/* SAI_CTRL_1 */
+#define SSM4567_SAI_CTRL_1_BCLK                        BIT(6)
+#define SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK      (0x3 << 4)
+#define SSM4567_SAI_CTRL_1_TDM_BLCKS_32                (0x0 << 4)
+#define SSM4567_SAI_CTRL_1_TDM_BLCKS_48                (0x1 << 4)
+#define SSM4567_SAI_CTRL_1_TDM_BLCKS_64                (0x2 << 4)
+#define SSM4567_SAI_CTRL_1_FSYNC               BIT(3)
+#define SSM4567_SAI_CTRL_1_LJ                  BIT(2)
+#define SSM4567_SAI_CTRL_1_TDM                 BIT(1)
+#define SSM4567_SAI_CTRL_1_PDM                 BIT(0)
+
+/* SAI_CTRL_2 */
+#define SSM4567_SAI_CTRL_2_AUTO_SLOT           BIT(3)
+#define SSM4567_SAI_CTRL_2_TDM_SLOT_MASK       0x7
+#define SSM4567_SAI_CTRL_2_TDM_SLOT(x)         (x)
+
 struct ssm4567 {
        struct regmap *regmap;
 };
@@ -145,15 +161,24 @@ static const struct snd_kcontrol_new ssm4567_snd_controls[] = {
        SOC_SINGLE_TLV("Master Playback Volume", SSM4567_REG_DAC_VOLUME, 0,
                0xff, 1, ssm4567_vol_tlv),
        SOC_SINGLE("DAC Low Power Mode Switch", SSM4567_REG_DAC_CTRL, 4, 1, 0),
+       SOC_SINGLE("DAC High Pass Filter Switch", SSM4567_REG_DAC_CTRL,
+               5, 1, 0),
 };
 
+static const struct snd_kcontrol_new ssm4567_amplifier_boost_control =
+       SOC_DAPM_SINGLE("Switch", SSM4567_REG_POWER_CTRL, 1, 1, 1);
+
 static const struct snd_soc_dapm_widget ssm4567_dapm_widgets[] = {
        SND_SOC_DAPM_DAC("DAC", "HiFi Playback", SSM4567_REG_POWER_CTRL, 2, 1),
+       SND_SOC_DAPM_SWITCH("Amplifier Boost", SSM4567_REG_POWER_CTRL, 3, 1,
+               &ssm4567_amplifier_boost_control),
 
        SND_SOC_DAPM_OUTPUT("OUT"),
 };
 
 static const struct snd_soc_dapm_route ssm4567_routes[] = {
+       { "OUT", NULL, "Amplifier Boost" },
+       { "Amplifier Boost", "Switch", "DAC" },
        { "OUT", NULL, "DAC" },
 };
 
@@ -192,6 +217,107 @@ static int ssm4567_mute(struct snd_soc_dai *dai, int mute)
                        SSM4567_DAC_MUTE, val);
 }
 
+static int ssm4567_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
+       unsigned int rx_mask, int slots, int width)
+{
+       struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai);
+       unsigned int blcks;
+       int slot;
+       int ret;
+
+       if (tx_mask == 0)
+               return -EINVAL;
+
+       if (rx_mask && rx_mask != tx_mask)
+               return -EINVAL;
+
+       slot = __ffs(tx_mask);
+       if (tx_mask != BIT(slot))
+               return -EINVAL;
+
+       switch (width) {
+       case 32:
+               blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_32;
+               break;
+       case 48:
+               blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_48;
+               break;
+       case 64:
+               blcks = SSM4567_SAI_CTRL_1_TDM_BLCKS_64;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       ret = regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_2,
+               SSM4567_SAI_CTRL_2_AUTO_SLOT | SSM4567_SAI_CTRL_2_TDM_SLOT_MASK,
+               SSM4567_SAI_CTRL_2_TDM_SLOT(slot));
+       if (ret)
+               return ret;
+
+       return regmap_update_bits(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1,
+               SSM4567_SAI_CTRL_1_TDM_BLCKS_MASK, blcks);
+}
+
+static int ssm4567_set_dai_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct ssm4567 *ssm4567 = snd_soc_dai_get_drvdata(dai);
+       unsigned int ctrl1 = 0;
+       bool invert_fclk;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               invert_fclk = false;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               ctrl1 |= SSM4567_SAI_CTRL_1_BCLK;
+               invert_fclk = false;
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC;
+               invert_fclk = true;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               ctrl1 |= SSM4567_SAI_CTRL_1_BCLK;
+               invert_fclk = true;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               ctrl1 |= SSM4567_SAI_CTRL_1_LJ;
+               invert_fclk = !invert_fclk;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               ctrl1 |= SSM4567_SAI_CTRL_1_TDM;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               ctrl1 |= SSM4567_SAI_CTRL_1_TDM | SSM4567_SAI_CTRL_1_LJ;
+               break;
+       case SND_SOC_DAIFMT_PDM:
+               ctrl1 |= SSM4567_SAI_CTRL_1_PDM;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (invert_fclk)
+               ctrl1 |= SSM4567_SAI_CTRL_1_FSYNC;
+
+       return regmap_write(ssm4567->regmap, SSM4567_REG_SAI_CTRL_1, ctrl1);
+}
+
 static int ssm4567_set_power(struct ssm4567 *ssm4567, bool enable)
 {
        int ret = 0;
@@ -246,6 +372,8 @@ static int ssm4567_set_bias_level(struct snd_soc_codec *codec,
 static const struct snd_soc_dai_ops ssm4567_dai_ops = {
        .hw_params      = ssm4567_hw_params,
        .digital_mute   = ssm4567_mute,
+       .set_fmt        = ssm4567_set_dai_fmt,
+       .set_tdm_slot   = ssm4567_set_tdm_slot,
 };
 
 static struct snd_soc_dai_driver ssm4567_dai = {
index 4874085..7e18200 100644 (file)
@@ -833,23 +833,6 @@ static struct snd_soc_dai_driver sta32x_dai = {
        .ops = &sta32x_dai_ops,
 };
 
-#ifdef CONFIG_PM
-static int sta32x_suspend(struct snd_soc_codec *codec)
-{
-       sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int sta32x_resume(struct snd_soc_codec *codec)
-{
-       sta32x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-#else
-#define sta32x_suspend NULL
-#define sta32x_resume NULL
-#endif
-
 static int sta32x_probe(struct snd_soc_codec *codec)
 {
        struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
@@ -936,7 +919,6 @@ static int sta32x_remove(struct snd_soc_codec *codec)
        struct sta32x_priv *sta32x = snd_soc_codec_get_drvdata(codec);
 
        sta32x_watchdog_stop(sta32x);
-       sta32x_set_bias_level(codec, SND_SOC_BIAS_OFF);
        regulator_bulk_disable(ARRAY_SIZE(sta32x->supplies), sta32x->supplies);
 
        return 0;
@@ -955,9 +937,8 @@ static bool sta32x_reg_is_volatile(struct device *dev, unsigned int reg)
 static const struct snd_soc_codec_driver sta32x_codec = {
        .probe =                sta32x_probe,
        .remove =               sta32x_remove,
-       .suspend =              sta32x_suspend,
-       .resume =               sta32x_resume,
        .set_bias_level =       sta32x_set_bias_level,
+       .suspend_bias_off =     true,
        .controls =             sta32x_snd_controls,
        .num_controls =         ARRAY_SIZE(sta32x_snd_controls),
        .dapm_widgets =         sta32x_dapm_widgets,
index cc97dd5..bda2ee1 100644 (file)
@@ -912,23 +912,6 @@ static struct snd_soc_dai_driver sta350_dai = {
        .ops = &sta350_dai_ops,
 };
 
-#ifdef CONFIG_PM
-static int sta350_suspend(struct snd_soc_codec *codec)
-{
-       sta350_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int sta350_resume(struct snd_soc_codec *codec)
-{
-       sta350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-#else
-#define sta350_suspend NULL
-#define sta350_resume NULL
-#endif
-
 static int sta350_probe(struct snd_soc_codec *codec)
 {
        struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
@@ -1065,7 +1048,6 @@ static int sta350_remove(struct snd_soc_codec *codec)
 {
        struct sta350_priv *sta350 = snd_soc_codec_get_drvdata(codec);
 
-       sta350_set_bias_level(codec, SND_SOC_BIAS_OFF);
        regulator_bulk_disable(ARRAY_SIZE(sta350->supplies), sta350->supplies);
 
        return 0;
@@ -1074,9 +1056,8 @@ static int sta350_remove(struct snd_soc_codec *codec)
 static const struct snd_soc_codec_driver sta350_codec = {
        .probe =                sta350_probe,
        .remove =               sta350_remove,
-       .suspend =              sta350_suspend,
-       .resume =               sta350_resume,
        .set_bias_level =       sta350_set_bias_level,
+       .suspend_bias_off =     true,
        .controls =             sta350_snd_controls,
        .num_controls =         ARRAY_SIZE(sta350_snd_controls),
        .dapm_widgets =         sta350_dapm_widgets,
index 89c748d..b0f436d 100644 (file)
@@ -319,41 +319,10 @@ static struct snd_soc_dai_driver sta529_dai = {
        .ops    = &sta529_dai_ops,
 };
 
-static int sta529_probe(struct snd_soc_codec *codec)
-{
-       sta529_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-/* power down chip */
-static int sta529_remove(struct snd_soc_codec *codec)
-{
-       sta529_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int sta529_suspend(struct snd_soc_codec *codec)
-{
-       sta529_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int sta529_resume(struct snd_soc_codec *codec)
-{
-       sta529_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
 static const struct snd_soc_codec_driver sta529_codec_driver = {
-       .probe = sta529_probe,
-       .remove = sta529_remove,
        .set_bias_level = sta529_set_bias_level,
-       .suspend = sta529_suspend,
-       .resume = sta529_resume,
+       .suspend_bias_off = true,
+
        .controls = sta529_snd_controls,
        .num_controls = ARRAY_SIZE(sta529_snd_controls),
 };
index 53b810d..dbff0c8 100644 (file)
@@ -139,18 +139,19 @@ static const struct snd_kcontrol_new stac9766_snd_ac97_controls[] = {
 static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
                               unsigned int val)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        u16 *cache = codec->reg_cache;
 
        if (reg > AC97_STAC_PAGE0) {
                stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
-               soc_ac97_ops->write(codec->ac97, reg, val);
+               soc_ac97_ops->write(ac97, reg, val);
                stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
                return 0;
        }
        if (reg / 2 >= ARRAY_SIZE(stac9766_reg))
                return -EIO;
 
-       soc_ac97_ops->write(codec->ac97, reg, val);
+       soc_ac97_ops->write(ac97, reg, val);
        cache[reg / 2] = val;
        return 0;
 }
@@ -158,11 +159,12 @@ static int stac9766_ac97_write(struct snd_soc_codec *codec, unsigned int reg,
 static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
                                       unsigned int reg)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        u16 val = 0, *cache = codec->reg_cache;
 
        if (reg > AC97_STAC_PAGE0) {
                stac9766_ac97_write(codec, AC97_INT_PAGING, 0);
-               val = soc_ac97_ops->read(codec->ac97, reg - AC97_STAC_PAGE0);
+               val = soc_ac97_ops->read(ac97, reg - AC97_STAC_PAGE0);
                stac9766_ac97_write(codec, AC97_INT_PAGING, 1);
                return val;
        }
@@ -173,7 +175,7 @@ static unsigned int stac9766_ac97_read(struct snd_soc_codec *codec,
                reg == AC97_INT_PAGING || reg == AC97_VENDOR_ID1 ||
                reg == AC97_VENDOR_ID2) {
 
-               val = soc_ac97_ops->read(codec->ac97, reg);
+               val = soc_ac97_ops->read(ac97, reg);
                return val;
        }
        return cache[reg / 2];
@@ -240,45 +242,41 @@ static int stac9766_set_bias_level(struct snd_soc_codec *codec,
 
 static int stac9766_reset(struct snd_soc_codec *codec, int try_warm)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
        if (try_warm && soc_ac97_ops->warm_reset) {
-               soc_ac97_ops->warm_reset(codec->ac97);
+               soc_ac97_ops->warm_reset(ac97);
                if (stac9766_ac97_read(codec, 0) == stac9766_reg[0])
                        return 1;
        }
 
-       soc_ac97_ops->reset(codec->ac97);
+       soc_ac97_ops->reset(ac97);
        if (soc_ac97_ops->warm_reset)
-               soc_ac97_ops->warm_reset(codec->ac97);
+               soc_ac97_ops->warm_reset(ac97);
        if (stac9766_ac97_read(codec, 0) != stac9766_reg[0])
                return -EIO;
        return 0;
 }
 
-static int stac9766_codec_suspend(struct snd_soc_codec *codec)
-{
-       stac9766_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static int stac9766_codec_resume(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        u16 id, reset;
 
        reset = 0;
        /* give the codec an AC97 warm reset to start the link */
 reset:
        if (reset > 5) {
-               printk(KERN_ERR "stac9766 failed to resume");
+               dev_err(codec->dev, "Failed to resume\n");
                return -EIO;
        }
-       codec->ac97->bus->ops->warm_reset(codec->ac97);
-       id = soc_ac97_ops->read(codec->ac97, AC97_VENDOR_ID2);
+       ac97->bus->ops->warm_reset(ac97);
+       id = soc_ac97_ops->read(ac97, AC97_VENDOR_ID2);
        if (id != 0x4c13) {
                stac9766_reset(codec, 0);
                reset++;
                goto reset;
        }
-       stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
        return 0;
 }
@@ -294,7 +292,6 @@ static const struct snd_soc_dai_ops stac9766_dai_ops_digital = {
 static struct snd_soc_dai_driver stac9766_dai[] = {
 {
        .name = "stac9766-hifi-analog",
-       .ac97_control = 1,
 
        /* stream cababilities */
        .playback = {
@@ -316,7 +313,6 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
 },
 {
        .name = "stac9766-hifi-IEC958",
-       .ac97_control = 1,
 
        /* stream cababilities */
        .playback = {
@@ -334,46 +330,48 @@ static struct snd_soc_dai_driver stac9766_dai[] = {
 
 static int stac9766_codec_probe(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97;
        int ret = 0;
 
-       ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-       if (ret < 0)
-               goto codec_err;
+       ac97 = snd_soc_new_ac97_codec(codec);
+       if (IS_ERR(ac97))
+               return PTR_ERR(ac97);
+
+       snd_soc_codec_set_drvdata(codec, ac97);
 
        /* do a cold reset for the controller and then try
         * a warm reset followed by an optional cold reset for codec */
        stac9766_reset(codec, 0);
        ret = stac9766_reset(codec, 1);
        if (ret < 0) {
-               printk(KERN_ERR "Failed to reset STAC9766: AC97 link error\n");
+               dev_err(codec->dev, "Failed to reset: AC97 link error\n");
                goto codec_err;
        }
 
-       stac9766_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       snd_soc_add_codec_controls(codec, stac9766_snd_ac97_controls,
-                            ARRAY_SIZE(stac9766_snd_ac97_controls));
-
        return 0;
 
 codec_err:
-       snd_soc_free_ac97_codec(codec);
+       snd_soc_free_ac97_codec(ac97);
        return ret;
 }
 
 static int stac9766_codec_remove(struct snd_soc_codec *codec)
 {
-       snd_soc_free_ac97_codec(codec);
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+       snd_soc_free_ac97_codec(ac97);
        return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_stac9766 = {
+       .controls = stac9766_snd_ac97_controls,
+       .num_controls = ARRAY_SIZE(stac9766_snd_ac97_controls),
        .write = stac9766_ac97_write,
        .read = stac9766_ac97_read,
        .set_bias_level = stac9766_set_bias_level,
+       .suspend_bias_off = true,
        .probe = stac9766_codec_probe,
        .remove = stac9766_codec_remove,
-       .suspend = stac9766_codec_suspend,
        .resume = stac9766_codec_resume,
        .reg_cache_size = ARRAY_SIZE(stac9766_reg),
        .reg_word_size = sizeof(u16),
index f039dc8..b505212 100644 (file)
@@ -345,7 +345,6 @@ static const struct reg_default tas2552_init_regs[] = {
 static int tas2552_codec_probe(struct snd_soc_codec *codec)
 {
        struct tas2552_data *tas2552 = snd_soc_codec_get_drvdata(codec);
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
        int ret;
 
        tas2552->codec = codec;
@@ -390,11 +389,6 @@ static int tas2552_codec_probe(struct snd_soc_codec *codec)
        snd_soc_write(codec, TAS2552_CFG_2, TAS2552_BOOST_EN |
                                  TAS2552_APT_EN | TAS2552_LIM_EN);
 
-       snd_soc_dapm_new_controls(dapm, tas2552_dapm_widgets,
-                               ARRAY_SIZE(tas2552_dapm_widgets));
-       snd_soc_dapm_add_routes(dapm, tas2552_audio_map,
-                               ARRAY_SIZE(tas2552_audio_map));
-
        return 0;
 
 patch_fail:
@@ -462,6 +456,10 @@ static struct snd_soc_codec_driver soc_codec_dev_tas2552 = {
        .resume = tas2552_resume,
        .controls = tas2552_snd_controls,
        .num_controls = ARRAY_SIZE(tas2552_snd_controls),
+       .dapm_widgets = tas2552_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(tas2552_dapm_widgets),
+       .dapm_routes = tas2552_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(tas2552_audio_map),
 };
 
 static const struct regmap_config tas2552_regmap_config = {
diff --git a/sound/soc/codecs/tfa9879.c b/sound/soc/codecs/tfa9879.c
new file mode 100644 (file)
index 0000000..16f1b71
--- /dev/null
@@ -0,0 +1,328 @@
+/*
+ * tfa9879.c  --  driver for NXP Semiconductors TFA9879
+ *
+ * Copyright (C) 2014 Axentia Technologies AB
+ * Author: Peter Rosin <peda@axentia.se>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#include <linux/module.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/pcm_params.h>
+
+#include "tfa9879.h"
+
+struct tfa9879_priv {
+       struct regmap *regmap;
+       int lsb_justified;
+};
+
+static int tfa9879_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 tfa9879_priv *tfa9879 = snd_soc_codec_get_drvdata(codec);
+       int fs;
+       int i2s_set = 0;
+
+       switch (params_rate(params)) {
+       case 8000:
+               fs = TFA9879_I2S_FS_8000;
+               break;
+       case 11025:
+               fs = TFA9879_I2S_FS_11025;
+               break;
+       case 12000:
+               fs = TFA9879_I2S_FS_12000;
+               break;
+       case 16000:
+               fs = TFA9879_I2S_FS_16000;
+               break;
+       case 22050:
+               fs = TFA9879_I2S_FS_22050;
+               break;
+       case 24000:
+               fs = TFA9879_I2S_FS_24000;
+               break;
+       case 32000:
+               fs = TFA9879_I2S_FS_32000;
+               break;
+       case 44100:
+               fs = TFA9879_I2S_FS_44100;
+               break;
+       case 48000:
+               fs = TFA9879_I2S_FS_48000;
+               break;
+       case 64000:
+               fs = TFA9879_I2S_FS_64000;
+               break;
+       case 88200:
+               fs = TFA9879_I2S_FS_88200;
+               break;
+       case 96000:
+               fs = TFA9879_I2S_FS_96000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (params_width(params)) {
+       case 16:
+               i2s_set = TFA9879_I2S_SET_LSB_J_16;
+               break;
+       case 24:
+               i2s_set = TFA9879_I2S_SET_LSB_J_24;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       if (tfa9879->lsb_justified)
+               snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1,
+                                   TFA9879_I2S_SET_MASK,
+                                   i2s_set << TFA9879_I2S_SET_SHIFT);
+
+       snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1,
+                           TFA9879_I2S_FS_MASK,
+                           fs << TFA9879_I2S_FS_SHIFT);
+       return 0;
+}
+
+static int tfa9879_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+
+       snd_soc_update_bits(codec, TFA9879_MISC_CONTROL,
+                           TFA9879_S_MUTE_MASK,
+                           !!mute << TFA9879_S_MUTE_SHIFT);
+
+       return 0;
+}
+
+static int tfa9879_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct tfa9879_priv *tfa9879 = snd_soc_codec_get_drvdata(codec);
+       int i2s_set;
+       int sck_pol;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               sck_pol = TFA9879_SCK_POL_NORMAL;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               sck_pol = TFA9879_SCK_POL_INVERSE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               tfa9879->lsb_justified = 0;
+               i2s_set = TFA9879_I2S_SET_I2S_24;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               tfa9879->lsb_justified = 0;
+               i2s_set = TFA9879_I2S_SET_MSB_J_24;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               tfa9879->lsb_justified = 1;
+               i2s_set = TFA9879_I2S_SET_LSB_J_24;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1,
+                           TFA9879_SCK_POL_MASK,
+                           sck_pol << TFA9879_SCK_POL_SHIFT);
+       snd_soc_update_bits(codec, TFA9879_SERIAL_INTERFACE_1,
+                           TFA9879_I2S_SET_MASK,
+                           i2s_set << TFA9879_I2S_SET_SHIFT);
+       return 0;
+}
+
+static struct reg_default tfa9879_regs[] = {
+       { TFA9879_DEVICE_CONTROL,       0x0000 }, /* 0x00 */
+       { TFA9879_SERIAL_INTERFACE_1,   0x0a18 }, /* 0x01 */
+       { TFA9879_PCM_IOM2_FORMAT_1,    0x0007 }, /* 0x02 */
+       { TFA9879_SERIAL_INTERFACE_2,   0x0a18 }, /* 0x03 */
+       { TFA9879_PCM_IOM2_FORMAT_2,    0x0007 }, /* 0x04 */
+       { TFA9879_EQUALIZER_A1,         0x59dd }, /* 0x05 */
+       { TFA9879_EQUALIZER_A2,         0xc63e }, /* 0x06 */
+       { TFA9879_EQUALIZER_B1,         0x651a }, /* 0x07 */
+       { TFA9879_EQUALIZER_B2,         0xe53e }, /* 0x08 */
+       { TFA9879_EQUALIZER_C1,         0x4616 }, /* 0x09 */
+       { TFA9879_EQUALIZER_C2,         0xd33e }, /* 0x0a */
+       { TFA9879_EQUALIZER_D1,         0x4df3 }, /* 0x0b */
+       { TFA9879_EQUALIZER_D2,         0xea3e }, /* 0x0c */
+       { TFA9879_EQUALIZER_E1,         0x5ee0 }, /* 0x0d */
+       { TFA9879_EQUALIZER_E2,         0xf93e }, /* 0x0e */
+       { TFA9879_BYPASS_CONTROL,       0x0093 }, /* 0x0f */
+       { TFA9879_DYNAMIC_RANGE_COMPR,  0x92ba }, /* 0x10 */
+       { TFA9879_BASS_TREBLE,          0x12a5 }, /* 0x11 */
+       { TFA9879_HIGH_PASS_FILTER,     0x0004 }, /* 0x12 */
+       { TFA9879_VOLUME_CONTROL,       0x10bd }, /* 0x13 */
+       { TFA9879_MISC_CONTROL,         0x0000 }, /* 0x14 */
+};
+
+static bool tfa9879_volatile_reg(struct device *dev, unsigned int reg)
+{
+       return reg == TFA9879_MISC_STATUS;
+}
+
+static const DECLARE_TLV_DB_SCALE(volume_tlv, -7050, 50, 1);
+static const DECLARE_TLV_DB_SCALE(tb_gain_tlv, -1800, 200, 0);
+static const char * const tb_freq_text[] = {
+       "Low", "Mid", "High"
+};
+static const struct soc_enum treble_freq_enum =
+       SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_TRBLE_SHIFT,
+                       ARRAY_SIZE(tb_freq_text), tb_freq_text);
+static const struct soc_enum bass_freq_enum =
+       SOC_ENUM_SINGLE(TFA9879_BASS_TREBLE, TFA9879_F_BASS_SHIFT,
+                       ARRAY_SIZE(tb_freq_text), tb_freq_text);
+
+static const struct snd_kcontrol_new tfa9879_controls[] = {
+       SOC_SINGLE_TLV("PCM Playback Volume", TFA9879_VOLUME_CONTROL,
+                      TFA9879_VOL_SHIFT, 0xbd, 1, volume_tlv),
+       SOC_SINGLE_TLV("Treble Volume", TFA9879_BASS_TREBLE,
+                      TFA9879_G_TRBLE_SHIFT, 18, 0, tb_gain_tlv),
+       SOC_SINGLE_TLV("Bass Volume", TFA9879_BASS_TREBLE,
+                      TFA9879_G_BASS_SHIFT, 18, 0, tb_gain_tlv),
+       SOC_ENUM("Treble Corner Freq", treble_freq_enum),
+       SOC_ENUM("Bass Corner Freq", bass_freq_enum),
+};
+
+static const struct snd_soc_dapm_widget tfa9879_dapm_widgets[] = {
+SND_SOC_DAPM_AIF_IN("AIFINL", "Playback", 0, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_AIF_IN("AIFINR", "Playback", 1, SND_SOC_NOPM, 0, 0),
+SND_SOC_DAPM_DAC("DAC", NULL, TFA9879_DEVICE_CONTROL, TFA9879_OPMODE_SHIFT, 0),
+SND_SOC_DAPM_OUTPUT("LINEOUT"),
+SND_SOC_DAPM_SUPPLY("POWER", TFA9879_DEVICE_CONTROL, TFA9879_POWERUP_SHIFT, 0,
+                   NULL, 0),
+};
+
+static const struct snd_soc_dapm_route tfa9879_dapm_routes[] = {
+       { "DAC", NULL, "AIFINL" },
+       { "DAC", NULL, "AIFINR" },
+
+       { "LINEOUT", NULL, "DAC" },
+
+       { "DAC", NULL, "POWER" },
+};
+
+static const struct snd_soc_codec_driver tfa9879_codec = {
+       .controls = tfa9879_controls,
+       .num_controls = ARRAY_SIZE(tfa9879_controls),
+
+       .dapm_widgets = tfa9879_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(tfa9879_dapm_widgets),
+       .dapm_routes = tfa9879_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(tfa9879_dapm_routes),
+};
+
+static const struct regmap_config tfa9879_regmap = {
+       .reg_bits = 8,
+       .val_bits = 16,
+
+       .volatile_reg = tfa9879_volatile_reg,
+       .max_register = TFA9879_MISC_STATUS,
+       .reg_defaults = tfa9879_regs,
+       .num_reg_defaults = ARRAY_SIZE(tfa9879_regs),
+       .cache_type = REGCACHE_RBTREE,
+};
+
+static const struct snd_soc_dai_ops tfa9879_dai_ops = {
+       .hw_params = tfa9879_hw_params,
+       .digital_mute = tfa9879_digital_mute,
+       .set_fmt = tfa9879_set_fmt,
+};
+
+#define TFA9879_RATES SNDRV_PCM_RATE_8000_96000
+
+#define TFA9879_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | \
+                        SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver tfa9879_dai = {
+       .name = "tfa9879-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = TFA9879_RATES,
+               .formats = TFA9879_FORMATS, },
+       .ops = &tfa9879_dai_ops,
+};
+
+static int tfa9879_i2c_probe(struct i2c_client *i2c,
+                            const struct i2c_device_id *id)
+{
+       struct tfa9879_priv *tfa9879;
+       int i;
+
+       tfa9879 = devm_kzalloc(&i2c->dev, sizeof(*tfa9879), GFP_KERNEL);
+       if (IS_ERR(tfa9879))
+               return PTR_ERR(tfa9879);
+
+       i2c_set_clientdata(i2c, tfa9879);
+
+       tfa9879->regmap = devm_regmap_init_i2c(i2c, &tfa9879_regmap);
+       if (IS_ERR(tfa9879->regmap))
+               return PTR_ERR(tfa9879->regmap);
+
+       /* Ensure the device is in reset state */
+       for (i = 0; i < ARRAY_SIZE(tfa9879_regs); i++)
+               regmap_write(tfa9879->regmap,
+                            tfa9879_regs[i].reg, tfa9879_regs[i].def);
+
+       return snd_soc_register_codec(&i2c->dev, &tfa9879_codec,
+                                     &tfa9879_dai, 1);
+}
+
+static int tfa9879_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+
+       return 0;
+}
+
+static const struct i2c_device_id tfa9879_i2c_id[] = {
+       { "tfa9879", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, tfa9879_i2c_id);
+
+static struct i2c_driver tfa9879_i2c_driver = {
+       .driver = {
+               .name = "tfa9879",
+               .owner = THIS_MODULE,
+       },
+       .probe = tfa9879_i2c_probe,
+       .remove = tfa9879_i2c_remove,
+       .id_table = tfa9879_i2c_id,
+};
+
+module_i2c_driver(tfa9879_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC NXP Semiconductors TFA9879 driver");
+MODULE_AUTHOR("Peter Rosin <peda@axentia.se>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/tfa9879.h b/sound/soc/codecs/tfa9879.h
new file mode 100644 (file)
index 0000000..3408c90
--- /dev/null
@@ -0,0 +1,202 @@
+/*
+ * tfa9879.h  --  driver for NXP Semiconductors TFA9879
+ *
+ * Copyright (C) 2014 Axentia Technologies AB
+ * Author: Peter Rosin <peda@axentia.se>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ *
+ */
+
+#ifndef _TFA9879_H
+#define _TFA9879_H
+
+#define TFA9879_DEVICE_CONTROL         0x00
+#define TFA9879_SERIAL_INTERFACE_1     0x01
+#define TFA9879_PCM_IOM2_FORMAT_1      0x02
+#define TFA9879_SERIAL_INTERFACE_2     0x03
+#define TFA9879_PCM_IOM2_FORMAT_2      0x04
+#define TFA9879_EQUALIZER_A1           0x05
+#define TFA9879_EQUALIZER_A2           0x06
+#define TFA9879_EQUALIZER_B1           0x07
+#define TFA9879_EQUALIZER_B2           0x08
+#define TFA9879_EQUALIZER_C1           0x09
+#define TFA9879_EQUALIZER_C2           0x0a
+#define TFA9879_EQUALIZER_D1           0x0b
+#define TFA9879_EQUALIZER_D2           0x0c
+#define TFA9879_EQUALIZER_E1           0x0d
+#define TFA9879_EQUALIZER_E2           0x0e
+#define TFA9879_BYPASS_CONTROL         0x0f
+#define TFA9879_DYNAMIC_RANGE_COMPR    0x10
+#define TFA9879_BASS_TREBLE            0x11
+#define TFA9879_HIGH_PASS_FILTER       0x12
+#define TFA9879_VOLUME_CONTROL         0x13
+#define TFA9879_MISC_CONTROL           0x14
+#define TFA9879_MISC_STATUS            0x15
+
+/* TFA9879_DEVICE_CONTROL */
+#define TFA9879_INPUT_SEL_MASK         0x0010
+#define TFA9879_INPUT_SEL_SHIFT                4
+#define TFA9879_OPMODE_MASK            0x0008
+#define TFA9879_OPMODE_SHIFT           3
+#define TFA9879_RESET_MASK             0x0002
+#define TFA9879_RESET_SHIFT            1
+#define TFA9879_POWERUP_MASK           0x0001
+#define TFA9879_POWERUP_SHIFT          0
+
+/* TFA9879_SERIAL_INTERFACE */
+#define TFA9879_MONO_SEL_MASK          0x0c00
+#define TFA9879_MONO_SEL_SHIFT         10
+#define TFA9879_MONO_SEL_LEFT          0
+#define TFA9879_MONO_SEL_RIGHT         1
+#define TFA9879_MONO_SEL_BOTH          2
+#define TFA9879_I2S_FS_MASK            0x03c0
+#define TFA9879_I2S_FS_SHIFT           6
+#define TFA9879_I2S_FS_8000            0
+#define TFA9879_I2S_FS_11025           1
+#define TFA9879_I2S_FS_12000           2
+#define TFA9879_I2S_FS_16000           3
+#define TFA9879_I2S_FS_22050           4
+#define TFA9879_I2S_FS_24000           5
+#define TFA9879_I2S_FS_32000           6
+#define TFA9879_I2S_FS_44100           7
+#define TFA9879_I2S_FS_48000           8
+#define TFA9879_I2S_FS_64000           9
+#define TFA9879_I2S_FS_88200           10
+#define TFA9879_I2S_FS_96000           11
+#define TFA9879_I2S_SET_MASK           0x0038
+#define TFA9879_I2S_SET_SHIFT          3
+#define TFA9879_I2S_SET_MSB_J_24       2
+#define TFA9879_I2S_SET_I2S_24         3
+#define TFA9879_I2S_SET_LSB_J_16       4
+#define TFA9879_I2S_SET_LSB_J_18       5
+#define TFA9879_I2S_SET_LSB_J_20       6
+#define TFA9879_I2S_SET_LSB_J_24       7
+#define TFA9879_SCK_POL_MASK           0x0004
+#define TFA9879_SCK_POL_SHIFT          2
+#define TFA9879_SCK_POL_NORMAL         0
+#define TFA9879_SCK_POL_INVERSE                1
+#define TFA9879_I_MODE_MASK            0x0003
+#define TFA9879_I_MODE_SHIFT           0
+#define TFA9879_I_MODE_I2S             0
+#define TFA9879_I_MODE_PCM_IOM2_SHORT  1
+#define TFA9879_I_MODE_PCM_IOM2_LONG   2
+
+/* TFA9879_PCM_IOM2_FORMAT */
+#define TFA9879_PCM_FS_MASK            0x0800
+#define TFA9879_PCM_FS_SHIFT           11
+#define TFA9879_A_LAW_MASK             0x0400
+#define TFA9879_A_LAW_SHIFT            10
+#define TFA9879_PCM_COMP_MASK          0x0200
+#define TFA9879_PCM_COMP_SHIFT         9
+#define TFA9879_PCM_DL_MASK            0x0100
+#define TFA9879_PCM_DL_SHIFT           8
+#define TFA9879_D1_SLOT_MASK           0x00f0
+#define TFA9879_D1_SLOT_SHIFT          4
+#define TFA9879_D2_SLOT_MASK           0x000f
+#define TFA9879_D2_SLOT_SHIFT          0
+
+/* TFA9879_EQUALIZER_X1 */
+#define TFA9879_T1_MASK                        0x8000
+#define TFA9879_T1_SHIFT               15
+#define TFA9879_K1M_MASK               0x7ff0
+#define TFA9879_K1M_SHIFT              4
+#define TFA9879_K1E_MASK               0x000f
+#define TFA9879_K1E_SHIFT              0
+
+/* TFA9879_EQUALIZER_X2 */
+#define TFA9879_T2_MASK                        0x8000
+#define TFA9879_T2_SHIFT               15
+#define TFA9879_K2M_MASK               0x7800
+#define TFA9879_K2M_SHIFT              11
+#define TFA9879_K2E_MASK               0x0700
+#define TFA9879_K2E_SHIFT              8
+#define TFA9879_K0_MASK                        0x00fe
+#define TFA9879_K0_SHIFT               1
+#define TFA9879_S_MASK                 0x0001
+#define TFA9879_S_SHIFT                        0
+
+/* TFA9879_BYPASS_CONTROL */
+#define TFA9879_L_OCP_MASK             0x00c0
+#define TFA9879_L_OCP_SHIFT            6
+#define TFA9879_L_OTP_MASK             0x0030
+#define TFA9879_L_OTP_SHIFT            4
+#define TFA9879_CLIPCTRL_MASK          0x0008
+#define TFA9879_CLIPCTRL_SHIFT         3
+#define TFA9879_HPF_BP_MASK            0x0004
+#define TFA9879_HPF_BP_SHIFT           2
+#define TFA9879_DRC_BP_MASK            0x0002
+#define TFA9879_DRC_BP_SHIFT           1
+#define TFA9879_EQ_BP_MASK             0x0001
+#define TFA9879_EQ_BP_SHIFT            0
+
+/* TFA9879_DYNAMIC_RANGE_COMPR */
+#define TFA9879_AT_LVL_MASK            0xf000
+#define TFA9879_AT_LVL_SHIFT           12
+#define TFA9879_AT_RATE_MASK           0x0f00
+#define TFA9879_AT_RATE_SHIFT          8
+#define TFA9879_RL_LVL_MASK            0x00f0
+#define TFA9879_RL_LVL_SHIFT           4
+#define TFA9879_RL_RATE_MASK           0x000f
+#define TFA9879_RL_RATE_SHIFT          0
+
+/* TFA9879_BASS_TREBLE */
+#define TFA9879_G_TRBLE_MASK           0x3e00
+#define TFA9879_G_TRBLE_SHIFT          9
+#define TFA9879_F_TRBLE_MASK           0x0180
+#define TFA9879_F_TRBLE_SHIFT          7
+#define TFA9879_G_BASS_MASK            0x007c
+#define TFA9879_G_BASS_SHIFT           2
+#define TFA9879_F_BASS_MASK            0x0003
+#define TFA9879_F_BASS_SHIFT           0
+
+/* TFA9879_HIGH_PASS_FILTER */
+#define TFA9879_HP_CTRL_MASK           0x00ff
+#define TFA9879_HP_CTRL_SHIFT          0
+
+/* TFA9879_VOLUME_CONTROL */
+#define TFA9879_ZR_CRSS_MASK           0x1000
+#define TFA9879_ZR_CRSS_SHIFT          12
+#define TFA9879_VOL_MASK               0x00ff
+#define TFA9879_VOL_SHIFT              0
+
+/* TFA9879_MISC_CONTROL */
+#define TFA9879_DE_PHAS_MASK           0x0c00
+#define TFA9879_DE_PHAS_SHIFT          10
+#define TFA9879_H_MUTE_MASK            0x0200
+#define TFA9879_H_MUTE_SHIFT           9
+#define TFA9879_S_MUTE_MASK            0x0100
+#define TFA9879_S_MUTE_SHIFT           8
+#define TFA9879_P_LIM_MASK             0x00ff
+#define TFA9879_P_LIM_SHIFT            0
+
+/* TFA9879_MISC_STATUS */
+#define TFA9879_PS_MASK                        0x4000
+#define TFA9879_PS_SHIFT               14
+#define TFA9879_PORA_MASK              0x2000
+#define TFA9879_PORA_SHIFT             13
+#define TFA9879_AMP_MASK               0x0600
+#define TFA9879_AMP_SHIFT              9
+#define TFA9879_IBP_2_MASK             0x0100
+#define TFA9879_IBP_2_SHIFT            8
+#define TFA9879_OFP_2_MASK             0x0080
+#define TFA9879_OFP_2_SHIFT            7
+#define TFA9879_UFP_2_MASK             0x0040
+#define TFA9879_UFP_2_SHIFT            6
+#define TFA9879_IBP_1_MASK             0x0020
+#define TFA9879_IBP_1_SHIFT            5
+#define TFA9879_OFP_1_MASK             0x0010
+#define TFA9879_OFP_1_SHIFT            4
+#define TFA9879_UFP_1_MASK             0x0008
+#define TFA9879_UFP_1_SHIFT            3
+#define TFA9879_OCPOKA_MASK            0x0004
+#define TFA9879_OCPOKA_SHIFT           2
+#define TFA9879_OCPOKB_MASK            0x0002
+#define TFA9879_OCPOKB_SHIFT           1
+#define TFA9879_OTPOK_MASK             0x0001
+#define TFA9879_OTPOK_SHIFT            0
+
+#endif
index d671679..cc17e7e 100644 (file)
@@ -540,19 +540,11 @@ static struct snd_soc_dai_driver tlv320aic23_dai = {
        .ops = &tlv320aic23_dai_ops,
 };
 
-static int tlv320aic23_suspend(struct snd_soc_codec *codec)
-{
-       tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
 static int tlv320aic23_resume(struct snd_soc_codec *codec)
 {
        struct aic23 *aic23 = snd_soc_codec_get_drvdata(codec);
        regcache_mark_dirty(aic23->regmap);
        regcache_sync(aic23->regmap);
-       tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
        return 0;
 }
@@ -562,9 +554,6 @@ static int tlv320aic23_codec_probe(struct snd_soc_codec *codec)
        /* Reset codec */
        snd_soc_write(codec, TLV320AIC23_RESET, 0);
 
-       /* power on device */
-       tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        snd_soc_write(codec, TLV320AIC23_DIGT, TLV320AIC23_DEEMP_44K);
 
        /* Unmute input */
@@ -589,18 +578,12 @@ static int tlv320aic23_codec_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int tlv320aic23_remove(struct snd_soc_codec *codec)
-{
-       tlv320aic23_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_tlv320aic23 = {
        .probe = tlv320aic23_codec_probe,
-       .remove = tlv320aic23_remove,
-       .suspend = tlv320aic23_suspend,
        .resume = tlv320aic23_resume,
        .set_bias_level = tlv320aic23_set_bias_level,
+       .suspend_bias_off = true,
+
        .controls = tlv320aic23_snd_controls,
        .num_controls = ARRAY_SIZE(tlv320aic23_snd_controls),
        .dapm_widgets = tlv320aic23_dapm_widgets,
index 145fe5b..dc3223d 100644 (file)
@@ -911,12 +911,13 @@ static int aic31xx_set_dai_sysclk(struct snd_soc_dai *codec_dai,
        }
        aic31xx->p_div = i;
 
-       for (i = 0; aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++) {
-               if (i == ARRAY_SIZE(aic31xx_divs)) {
-                       dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
-                               __func__, freq);
-                       return -EINVAL;
-               }
+       for (i = 0; i < ARRAY_SIZE(aic31xx_divs) &&
+                    aic31xx_divs[i].mclk_p != freq/aic31xx->p_div; i++)
+               ;
+       if (i == ARRAY_SIZE(aic31xx_divs)) {
+               dev_err(aic31xx->dev, "%s: Unsupported frequency %d\n",
+                       __func__, freq);
+               return -EINVAL;
        }
 
        /* set clock on MCLK, BCLK, or GPIO1 as PLL input */
@@ -1056,18 +1057,6 @@ static int aic31xx_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-static int aic31xx_suspend(struct snd_soc_codec *codec)
-{
-       aic31xx_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int aic31xx_resume(struct snd_soc_codec *codec)
-{
-       aic31xx_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 static int aic31xx_codec_probe(struct snd_soc_codec *codec)
 {
        int ret = 0;
@@ -1110,8 +1099,6 @@ static int aic31xx_codec_remove(struct snd_soc_codec *codec)
 {
        struct aic31xx_priv *aic31xx = snd_soc_codec_get_drvdata(codec);
        int i;
-       /* power down chip */
-       aic31xx_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
        for (i = 0; i < ARRAY_SIZE(aic31xx->supplies); i++)
                regulator_unregister_notifier(aic31xx->supplies[i].consumer,
@@ -1123,9 +1110,9 @@ static int aic31xx_codec_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_driver_aic31xx = {
        .probe                  = aic31xx_codec_probe,
        .remove                 = aic31xx_codec_remove,
-       .suspend                = aic31xx_suspend,
-       .resume                 = aic31xx_resume,
        .set_bias_level         = aic31xx_set_bias_level,
+       .suspend_bias_off       = true,
+
        .controls               = aic31xx_snd_controls,
        .num_controls           = ARRAY_SIZE(aic31xx_snd_controls),
        .dapm_widgets           = aic31xx_dapm_widgets,
index 6ea662d..015467e 100644 (file)
@@ -597,18 +597,6 @@ static struct snd_soc_dai_driver aic32x4_dai = {
        .symmetric_rates = 1,
 };
 
-static int aic32x4_suspend(struct snd_soc_codec *codec)
-{
-       aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int aic32x4_resume(struct snd_soc_codec *codec)
-{
-       aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 static int aic32x4_probe(struct snd_soc_codec *codec)
 {
        struct aic32x4_priv *aic32x4 = snd_soc_codec_get_drvdata(codec);
@@ -654,8 +642,6 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
                snd_soc_write(codec, AIC32X4_RMICPGANIN,
                                AIC32X4_RMICPGANIN_CM1R_10K);
 
-       aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        /*
         * Workaround: for an unknown reason, the ADC needs to be powered up
         * and down for the first capture to work properly. It seems related to
@@ -669,18 +655,10 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int aic32x4_remove(struct snd_soc_codec *codec)
-{
-       aic32x4_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
        .probe = aic32x4_probe,
-       .remove = aic32x4_remove,
-       .suspend = aic32x4_suspend,
-       .resume = aic32x4_resume,
        .set_bias_level = aic32x4_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = aic32x4_snd_controls,
        .num_controls = ARRAY_SIZE(aic32x4_snd_controls),
index f7c2a57..b7ebce0 100644 (file)
@@ -78,6 +78,8 @@ struct aic3x_priv {
        struct aic3x_disable_nb disable_nb[AIC3X_NUM_SUPPLIES];
        struct aic3x_setup_data *setup;
        unsigned int sysclk;
+       unsigned int dai_fmt;
+       unsigned int tdm_delay;
        struct list_head list;
        int master;
        int gpio_reset;
@@ -214,61 +216,78 @@ static int mic_bias_event(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
-static const char *aic3x_left_dac_mux[] = { "DAC_L1", "DAC_L3", "DAC_L2" };
-static const char *aic3x_right_dac_mux[] = { "DAC_R1", "DAC_R3", "DAC_R2" };
-static const char *aic3x_left_hpcom_mux[] =
-    { "differential of HPLOUT", "constant VCM", "single-ended" };
-static const char *aic3x_right_hpcom_mux[] =
-    { "differential of HPROUT", "constant VCM", "single-ended",
-      "differential of HPLCOM", "external feedback" };
-static const char *aic3x_linein_mode_mux[] = { "single-ended", "differential" };
-static const char *aic3x_adc_hpf[] =
-    { "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" };
-
-#define LDAC_ENUM      0
-#define RDAC_ENUM      1
-#define LHPCOM_ENUM    2
-#define RHPCOM_ENUM    3
-#define LINE1L_2_L_ENUM        4
-#define LINE1L_2_R_ENUM        5
-#define LINE1R_2_L_ENUM        6
-#define LINE1R_2_R_ENUM        7
-#define LINE2L_ENUM    8
-#define LINE2R_ENUM    9
-#define ADC_HPF_ENUM   10
-
-static const struct soc_enum aic3x_enum[] = {
-       SOC_ENUM_SINGLE(DAC_LINE_MUX, 6, 3, aic3x_left_dac_mux),
-       SOC_ENUM_SINGLE(DAC_LINE_MUX, 4, 3, aic3x_right_dac_mux),
-       SOC_ENUM_SINGLE(HPLCOM_CFG, 4, 3, aic3x_left_hpcom_mux),
-       SOC_ENUM_SINGLE(HPRCOM_CFG, 3, 5, aic3x_right_hpcom_mux),
-       SOC_ENUM_SINGLE(LINE1L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
-       SOC_ENUM_SINGLE(LINE1L_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
-       SOC_ENUM_SINGLE(LINE1R_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
-       SOC_ENUM_SINGLE(LINE1R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
-       SOC_ENUM_SINGLE(LINE2L_2_LADC_CTRL, 7, 2, aic3x_linein_mode_mux),
-       SOC_ENUM_SINGLE(LINE2R_2_RADC_CTRL, 7, 2, aic3x_linein_mode_mux),
-       SOC_ENUM_DOUBLE(AIC3X_CODEC_DFILT_CTRL, 6, 4, 4, aic3x_adc_hpf),
-};
-
-static const char *aic3x_agc_level[] =
-       { "-5.5dB", "-8dB", "-10dB", "-12dB", "-14dB", "-17dB", "-20dB", "-24dB" };
-static const struct soc_enum aic3x_agc_level_enum[] = {
-       SOC_ENUM_SINGLE(LAGC_CTRL_A, 4, 8, aic3x_agc_level),
-       SOC_ENUM_SINGLE(RAGC_CTRL_A, 4, 8, aic3x_agc_level),
-};
-
-static const char *aic3x_agc_attack[] = { "8ms", "11ms", "16ms", "20ms" };
-static const struct soc_enum aic3x_agc_attack_enum[] = {
-       SOC_ENUM_SINGLE(LAGC_CTRL_A, 2, 4, aic3x_agc_attack),
-       SOC_ENUM_SINGLE(RAGC_CTRL_A, 2, 4, aic3x_agc_attack),
-};
-
-static const char *aic3x_agc_decay[] = { "100ms", "200ms", "400ms", "500ms" };
-static const struct soc_enum aic3x_agc_decay_enum[] = {
-       SOC_ENUM_SINGLE(LAGC_CTRL_A, 0, 4, aic3x_agc_decay),
-       SOC_ENUM_SINGLE(RAGC_CTRL_A, 0, 4, aic3x_agc_decay),
-};
+static const char * const aic3x_left_dac_mux[] = {
+       "DAC_L1", "DAC_L3", "DAC_L2" };
+static SOC_ENUM_SINGLE_DECL(aic3x_left_dac_enum, DAC_LINE_MUX, 6,
+                           aic3x_left_dac_mux);
+
+static const char * const aic3x_right_dac_mux[] = {
+       "DAC_R1", "DAC_R3", "DAC_R2" };
+static SOC_ENUM_SINGLE_DECL(aic3x_right_dac_enum, DAC_LINE_MUX, 4,
+                           aic3x_right_dac_mux);
+
+static const char * const aic3x_left_hpcom_mux[] = {
+       "differential of HPLOUT", "constant VCM", "single-ended" };
+static SOC_ENUM_SINGLE_DECL(aic3x_left_hpcom_enum, HPLCOM_CFG, 4,
+                           aic3x_left_hpcom_mux);
+
+static const char * const aic3x_right_hpcom_mux[] = {
+       "differential of HPROUT", "constant VCM", "single-ended",
+       "differential of HPLCOM", "external feedback" };
+static SOC_ENUM_SINGLE_DECL(aic3x_right_hpcom_enum, HPRCOM_CFG, 3,
+                           aic3x_right_hpcom_mux);
+
+static const char * const aic3x_linein_mode_mux[] = {
+       "single-ended", "differential" };
+static SOC_ENUM_SINGLE_DECL(aic3x_line1l_2_l_enum, LINE1L_2_LADC_CTRL, 7,
+                           aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line1l_2_r_enum, LINE1L_2_RADC_CTRL, 7,
+                           aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line1r_2_l_enum, LINE1R_2_LADC_CTRL, 7,
+                           aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line1r_2_r_enum, LINE1R_2_RADC_CTRL, 7,
+                           aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line2l_2_ldac_enum, LINE2L_2_LADC_CTRL, 7,
+                           aic3x_linein_mode_mux);
+static SOC_ENUM_SINGLE_DECL(aic3x_line2r_2_rdac_enum, LINE2R_2_RADC_CTRL, 7,
+                           aic3x_linein_mode_mux);
+
+static const char * const aic3x_adc_hpf[] = {
+       "Disabled", "0.0045xFs", "0.0125xFs", "0.025xFs" };
+static SOC_ENUM_DOUBLE_DECL(aic3x_adc_hpf_enum, AIC3X_CODEC_DFILT_CTRL, 6, 4,
+                           aic3x_adc_hpf);
+
+static const char * const aic3x_agc_level[] = {
+       "-5.5dB", "-8dB", "-10dB", "-12dB",
+       "-14dB", "-17dB", "-20dB", "-24dB" };
+static SOC_ENUM_SINGLE_DECL(aic3x_lagc_level_enum, LAGC_CTRL_A, 4,
+                           aic3x_agc_level);
+static SOC_ENUM_SINGLE_DECL(aic3x_ragc_level_enum, RAGC_CTRL_A, 4,
+                           aic3x_agc_level);
+
+static const char * const aic3x_agc_attack[] = {
+       "8ms", "11ms", "16ms", "20ms" };
+static SOC_ENUM_SINGLE_DECL(aic3x_lagc_attack_enum, LAGC_CTRL_A, 2,
+                           aic3x_agc_attack);
+static SOC_ENUM_SINGLE_DECL(aic3x_ragc_attack_enum, RAGC_CTRL_A, 2,
+                           aic3x_agc_attack);
+
+static const char * const aic3x_agc_decay[] = {
+       "100ms", "200ms", "400ms", "500ms" };
+static SOC_ENUM_SINGLE_DECL(aic3x_lagc_decay_enum, LAGC_CTRL_A, 0,
+                           aic3x_agc_decay);
+static SOC_ENUM_SINGLE_DECL(aic3x_ragc_decay_enum, RAGC_CTRL_A, 0,
+                           aic3x_agc_decay);
+
+static const char * const aic3x_poweron_time[] = {
+       "0us", "10us", "100us", "1ms", "10ms", "50ms",
+       "100ms", "200ms", "400ms", "800ms", "2s", "4s" };
+static SOC_ENUM_SINGLE_DECL(aic3x_poweron_time_enum, HPOUT_POP_REDUCTION, 4,
+                           aic3x_poweron_time);
+
+static const char * const aic3x_rampup_step[] = { "0ms", "1ms", "2ms", "4ms" };
+static SOC_ENUM_SINGLE_DECL(aic3x_rampup_step_enum, HPOUT_POP_REDUCTION, 2,
+                           aic3x_rampup_step);
 
 /*
  * DAC digital volumes. From -63.5 to 0 dB in 0.5 dB steps
@@ -383,12 +402,12 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
         * adjust PGA to max value when ADC is on and will never go back.
        */
        SOC_DOUBLE_R("AGC Switch", LAGC_CTRL_A, RAGC_CTRL_A, 7, 0x01, 0),
-       SOC_ENUM("Left AGC Target level", aic3x_agc_level_enum[0]),
-       SOC_ENUM("Right AGC Target level", aic3x_agc_level_enum[1]),
-       SOC_ENUM("Left AGC Attack time", aic3x_agc_attack_enum[0]),
-       SOC_ENUM("Right AGC Attack time", aic3x_agc_attack_enum[1]),
-       SOC_ENUM("Left AGC Decay time", aic3x_agc_decay_enum[0]),
-       SOC_ENUM("Right AGC Decay time", aic3x_agc_decay_enum[1]),
+       SOC_ENUM("Left AGC Target level", aic3x_lagc_level_enum),
+       SOC_ENUM("Right AGC Target level", aic3x_ragc_level_enum),
+       SOC_ENUM("Left AGC Attack time", aic3x_lagc_attack_enum),
+       SOC_ENUM("Right AGC Attack time", aic3x_ragc_attack_enum),
+       SOC_ENUM("Left AGC Decay time", aic3x_lagc_decay_enum),
+       SOC_ENUM("Right AGC Decay time", aic3x_ragc_decay_enum),
 
        /* De-emphasis */
        SOC_DOUBLE("De-emphasis Switch", AIC3X_CODEC_DFILT_CTRL, 2, 0, 0x01, 0),
@@ -398,7 +417,11 @@ static const struct snd_kcontrol_new aic3x_snd_controls[] = {
                         0, 119, 0, adc_tlv),
        SOC_DOUBLE_R("PGA Capture Switch", LADC_VOL, RADC_VOL, 7, 0x01, 1),
 
-       SOC_ENUM("ADC HPF Cut-off", aic3x_enum[ADC_HPF_ENUM]),
+       SOC_ENUM("ADC HPF Cut-off", aic3x_adc_hpf_enum),
+
+       /* Pop reduction */
+       SOC_ENUM("Output Driver Power-On time", aic3x_poweron_time_enum),
+       SOC_ENUM("Output Driver Ramp-up step", aic3x_rampup_step_enum),
 };
 
 static const struct snd_kcontrol_new aic3x_mono_controls[] = {
@@ -425,19 +448,19 @@ static const struct snd_kcontrol_new aic3x_classd_amp_gain_ctrl =
 
 /* Left DAC Mux */
 static const struct snd_kcontrol_new aic3x_left_dac_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LDAC_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_left_dac_enum);
 
 /* Right DAC Mux */
 static const struct snd_kcontrol_new aic3x_right_dac_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[RDAC_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_right_dac_enum);
 
 /* Left HPCOM Mux */
 static const struct snd_kcontrol_new aic3x_left_hpcom_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LHPCOM_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_left_hpcom_enum);
 
 /* Right HPCOM Mux */
 static const struct snd_kcontrol_new aic3x_right_hpcom_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[RHPCOM_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_right_hpcom_enum);
 
 /* Left Line Mixer */
 static const struct snd_kcontrol_new aic3x_left_line_mixer_controls[] = {
@@ -529,23 +552,23 @@ static const struct snd_kcontrol_new aic3x_right_pga_mixer_controls[] = {
 
 /* Left Line1 Mux */
 static const struct snd_kcontrol_new aic3x_left_line1l_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_L_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_line1l_2_l_enum);
 static const struct snd_kcontrol_new aic3x_right_line1l_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE1L_2_R_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_line1l_2_r_enum);
 
 /* Right Line1 Mux */
 static const struct snd_kcontrol_new aic3x_right_line1r_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_R_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_line1r_2_r_enum);
 static const struct snd_kcontrol_new aic3x_left_line1r_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE1R_2_L_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_line1r_2_l_enum);
 
 /* Left Line2 Mux */
 static const struct snd_kcontrol_new aic3x_left_line2_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE2L_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_line2l_2_ldac_enum);
 
 /* Right Line2 Mux */
 static const struct snd_kcontrol_new aic3x_right_line2_mux_controls =
-SOC_DAPM_ENUM("Route", aic3x_enum[LINE2R_ENUM]);
+SOC_DAPM_ENUM("Route", aic3x_line2r_2_rdac_enum);
 
 static const struct snd_soc_dapm_widget aic3x_dapm_widgets[] = {
        /* Left DAC to Left Outputs */
@@ -1009,6 +1032,25 @@ found:
        return 0;
 }
 
+static int aic3x_prepare(struct snd_pcm_substream *substream,
+                        struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+       int delay = 0;
+
+       /* TDM slot selection only valid in DSP_A/_B mode */
+       if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A)
+               delay += (aic3x->tdm_delay + 1);
+       else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B)
+               delay += aic3x->tdm_delay;
+
+       /* Configure data delay */
+       snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, aic3x->tdm_delay);
+
+       return 0;
+}
+
 static int aic3x_mute(struct snd_soc_dai *dai, int mute)
 {
        struct snd_soc_codec *codec = dai->codec;
@@ -1048,7 +1090,6 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
        struct snd_soc_codec *codec = codec_dai->codec;
        struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
        u8 iface_areg, iface_breg;
-       int delay = 0;
 
        iface_areg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLA) & 0x3f;
        iface_breg = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & 0x3f;
@@ -1076,7 +1117,6 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
        case (SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF):
                break;
        case (SND_SOC_DAIFMT_DSP_A | SND_SOC_DAIFMT_IB_NF):
-               delay = 1;
        case (SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF):
                iface_breg |= (0x01 << 6);
                break;
@@ -1090,10 +1130,45 @@ static int aic3x_set_dai_fmt(struct snd_soc_dai *codec_dai,
                return -EINVAL;
        }
 
+       aic3x->dai_fmt = fmt & SND_SOC_DAIFMT_FORMAT_MASK;
+
        /* set iface */
        snd_soc_write(codec, AIC3X_ASD_INTF_CTRLA, iface_areg);
        snd_soc_write(codec, AIC3X_ASD_INTF_CTRLB, iface_breg);
-       snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
+
+       return 0;
+}
+
+static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
+                                 unsigned int tx_mask, unsigned int rx_mask,
+                                 int slots, int slot_width)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
+       unsigned int lsb;
+
+       if (tx_mask != rx_mask) {
+               dev_err(codec->dev, "tx and rx masks must be symmetric\n");
+               return -EINVAL;
+       }
+
+       if (unlikely(!tx_mask)) {
+               dev_err(codec->dev, "tx and rx masks need to be non 0\n");
+               return -EINVAL;
+       }
+
+       /* TDM based on DSP mode requires slots to be adjacent */
+       lsb = __ffs(tx_mask);
+       if ((lsb + 1) != __fls(tx_mask)) {
+               dev_err(codec->dev, "Invalid mask, slots must be adjacent\n");
+               return -EINVAL;
+       }
+
+       aic3x->tdm_delay = lsb * slot_width;
+
+       /* DOUT in high-impedance on inactive bit clocks */
+       snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA,
+                           DOUT_TRISTATE, DOUT_TRISTATE);
 
        return 0;
 }
@@ -1212,9 +1287,11 @@ static int aic3x_set_bias_level(struct snd_soc_codec *codec,
 
 static const struct snd_soc_dai_ops aic3x_dai_ops = {
        .hw_params      = aic3x_hw_params,
+       .prepare        = aic3x_prepare,
        .digital_mute   = aic3x_mute,
        .set_sysclk     = aic3x_set_dai_sysclk,
        .set_fmt        = aic3x_set_dai_fmt,
+       .set_tdm_slot   = aic3x_set_dai_tdm_slot,
 };
 
 static struct snd_soc_dai_driver aic3x_dai = {
@@ -1414,7 +1491,6 @@ static int aic3x_remove(struct snd_soc_codec *codec)
        struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
        int i;
 
-       aic3x_set_bias_level(codec, SND_SOC_BIAS_OFF);
        list_del(&aic3x->list);
        for (i = 0; i < ARRAY_SIZE(aic3x->supplies); i++)
                regulator_unregister_notifier(aic3x->supplies[i].consumer,
index e521ac3..89fa692 100644 (file)
 /* Audio serial data interface control register A bits */
 #define BIT_CLK_MASTER          0x80
 #define WORD_CLK_MASTER         0x40
+#define DOUT_TRISTATE          0x20
 
 /* Codec Datapath setup register 7 */
 #define FSREF_44100            (1 << 7)
index e21ed93..0fe2ced 100644 (file)
@@ -1436,8 +1436,6 @@ static int dac33_soc_remove(struct snd_soc_codec *codec)
 {
        struct tlv320dac33_priv *dac33 = snd_soc_codec_get_drvdata(codec);
 
-       dac33_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
        if (dac33->irq >= 0) {
                free_irq(dac33->irq, dac33->codec);
                destroy_workqueue(dac33->dac33_wq);
diff --git a/sound/soc/codecs/ts3a227e.c b/sound/soc/codecs/ts3a227e.c
new file mode 100644 (file)
index 0000000..1d12057
--- /dev/null
@@ -0,0 +1,314 @@
+/*
+ * TS3A227E Autonomous Audio Accessory Detection and Configuration Switch
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * 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/gpio.h>
+#include <linux/i2c.h>
+#include <linux/init.h>
+#include <linux/input.h>
+#include <linux/module.h>
+#include <linux/of_gpio.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/soc.h>
+
+struct ts3a227e {
+       struct regmap *regmap;
+       struct snd_soc_jack *jack;
+       bool plugged;
+       bool mic_present;
+       unsigned int buttons_held;
+};
+
+/* Button values to be reported on the jack */
+static const int ts3a227e_buttons[] = {
+       SND_JACK_BTN_0,
+       SND_JACK_BTN_1,
+       SND_JACK_BTN_2,
+       SND_JACK_BTN_3,
+};
+
+#define TS3A227E_NUM_BUTTONS 4
+#define TS3A227E_JACK_MASK (SND_JACK_HEADPHONE | \
+                           SND_JACK_MICROPHONE | \
+                           SND_JACK_BTN_0 | \
+                           SND_JACK_BTN_1 | \
+                           SND_JACK_BTN_2 | \
+                           SND_JACK_BTN_3)
+
+/* TS3A227E registers */
+#define TS3A227E_REG_DEVICE_ID         0x00
+#define TS3A227E_REG_INTERRUPT         0x01
+#define TS3A227E_REG_KP_INTERRUPT      0x02
+#define TS3A227E_REG_INTERRUPT_DISABLE 0x03
+#define TS3A227E_REG_SETTING_1         0x04
+#define TS3A227E_REG_SETTING_2         0x05
+#define TS3A227E_REG_SETTING_3         0x06
+#define TS3A227E_REG_SWITCH_CONTROL_1  0x07
+#define TS3A227E_REG_SWITCH_CONTROL_2  0x08
+#define TS3A227E_REG_SWITCH_STATUS_1   0x09
+#define TS3A227E_REG_SWITCH_STATUS_2   0x0a
+#define TS3A227E_REG_ACCESSORY_STATUS  0x0b
+#define TS3A227E_REG_ADC_OUTPUT                0x0c
+#define TS3A227E_REG_KP_THRESHOLD_1    0x0d
+#define TS3A227E_REG_KP_THRESHOLD_2    0x0e
+#define TS3A227E_REG_KP_THRESHOLD_3    0x0f
+
+/* TS3A227E_REG_INTERRUPT 0x01 */
+#define INS_REM_EVENT 0x01
+#define DETECTION_COMPLETE_EVENT 0x02
+
+/* TS3A227E_REG_KP_INTERRUPT 0x02 */
+#define PRESS_MASK(idx) (0x01 << (2 * (idx)))
+#define RELEASE_MASK(idx) (0x02 << (2 * (idx)))
+
+/* TS3A227E_REG_INTERRUPT_DISABLE 0x03 */
+#define INS_REM_INT_DISABLE 0x01
+#define DETECTION_COMPLETE_INT_DISABLE 0x02
+#define ADC_COMPLETE_INT_DISABLE 0x04
+#define INTB_DISABLE 0x08
+
+/* TS3A227E_REG_SETTING_2 0x05 */
+#define KP_ENABLE 0x04
+
+/* TS3A227E_REG_ACCESSORY_STATUS  0x0b */
+#define TYPE_3_POLE 0x01
+#define TYPE_4_POLE_OMTP 0x02
+#define TYPE_4_POLE_STANDARD 0x04
+#define JACK_INSERTED 0x08
+#define EITHER_MIC_MASK (TYPE_4_POLE_OMTP | TYPE_4_POLE_STANDARD)
+
+static const struct reg_default ts3a227e_reg_defaults[] = {
+       { TS3A227E_REG_DEVICE_ID, 0x10 },
+       { TS3A227E_REG_INTERRUPT, 0x00 },
+       { TS3A227E_REG_KP_INTERRUPT, 0x00 },
+       { TS3A227E_REG_INTERRUPT_DISABLE, 0x08 },
+       { TS3A227E_REG_SETTING_1, 0x23 },
+       { TS3A227E_REG_SETTING_2, 0x00 },
+       { TS3A227E_REG_SETTING_3, 0x0e },
+       { TS3A227E_REG_SWITCH_CONTROL_1, 0x00 },
+       { TS3A227E_REG_SWITCH_CONTROL_2, 0x00 },
+       { TS3A227E_REG_SWITCH_STATUS_1, 0x0c },
+       { TS3A227E_REG_SWITCH_STATUS_2, 0x00 },
+       { TS3A227E_REG_ACCESSORY_STATUS, 0x00 },
+       { TS3A227E_REG_ADC_OUTPUT, 0x00 },
+       { TS3A227E_REG_KP_THRESHOLD_1, 0x20 },
+       { TS3A227E_REG_KP_THRESHOLD_2, 0x40 },
+       { TS3A227E_REG_KP_THRESHOLD_3, 0x68 },
+};
+
+static bool ts3a227e_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case TS3A227E_REG_DEVICE_ID ... TS3A227E_REG_KP_THRESHOLD_3:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool ts3a227e_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case TS3A227E_REG_INTERRUPT_DISABLE ... TS3A227E_REG_SWITCH_CONTROL_2:
+       case TS3A227E_REG_KP_THRESHOLD_1 ... TS3A227E_REG_KP_THRESHOLD_3:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool ts3a227e_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case TS3A227E_REG_INTERRUPT ... TS3A227E_REG_INTERRUPT_DISABLE:
+       case TS3A227E_REG_SETTING_2:
+       case TS3A227E_REG_SWITCH_STATUS_1 ... TS3A227E_REG_ADC_OUTPUT:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static void ts3a227e_jack_report(struct ts3a227e *ts3a227e)
+{
+       unsigned int i;
+       int report = 0;
+
+       if (!ts3a227e->jack)
+               return;
+
+       if (ts3a227e->plugged)
+               report = SND_JACK_HEADPHONE;
+       if (ts3a227e->mic_present)
+               report |= SND_JACK_MICROPHONE;
+       for (i = 0; i < TS3A227E_NUM_BUTTONS; i++) {
+               if (ts3a227e->buttons_held & (1 << i))
+                       report |= ts3a227e_buttons[i];
+       }
+       snd_soc_jack_report(ts3a227e->jack, report, TS3A227E_JACK_MASK);
+}
+
+static void ts3a227e_new_jack_state(struct ts3a227e *ts3a227e, unsigned acc_reg)
+{
+       bool plugged, mic_present;
+
+       plugged = !!(acc_reg & JACK_INSERTED);
+       mic_present = plugged && !!(acc_reg & EITHER_MIC_MASK);
+
+       ts3a227e->plugged = plugged;
+
+       if (mic_present != ts3a227e->mic_present) {
+               ts3a227e->mic_present = mic_present;
+               ts3a227e->buttons_held = 0;
+               if (mic_present) {
+                       /* Enable key press detection. */
+                       regmap_update_bits(ts3a227e->regmap,
+                                          TS3A227E_REG_SETTING_2,
+                                          KP_ENABLE, KP_ENABLE);
+               }
+       }
+}
+
+static irqreturn_t ts3a227e_interrupt(int irq, void *data)
+{
+       struct ts3a227e *ts3a227e = (struct ts3a227e *)data;
+       struct regmap *regmap = ts3a227e->regmap;
+       unsigned int int_reg, kp_int_reg, acc_reg, i;
+
+       /* Check for plug/unplug. */
+       regmap_read(regmap, TS3A227E_REG_INTERRUPT, &int_reg);
+       if (int_reg & (DETECTION_COMPLETE_EVENT | INS_REM_EVENT)) {
+               regmap_read(regmap, TS3A227E_REG_ACCESSORY_STATUS, &acc_reg);
+               ts3a227e_new_jack_state(ts3a227e, acc_reg);
+       }
+
+       /* Report any key events. */
+       regmap_read(regmap, TS3A227E_REG_KP_INTERRUPT, &kp_int_reg);
+       for (i = 0; i < TS3A227E_NUM_BUTTONS; i++) {
+               if (kp_int_reg & PRESS_MASK(i))
+                       ts3a227e->buttons_held |= (1 << i);
+               if (kp_int_reg & RELEASE_MASK(i))
+                       ts3a227e->buttons_held &= ~(1 << i);
+       }
+
+       ts3a227e_jack_report(ts3a227e);
+
+       return IRQ_HANDLED;
+}
+
+/**
+ * ts3a227e_enable_jack_detect - Specify a jack for event reporting
+ *
+ * @component:  component to register the jack with
+ * @jack: jack to use to report headset and button events on
+ *
+ * After this function has been called the headset insert/remove and button
+ * events 0-3 will be routed to the given jack.  Jack can be null to stop
+ * reporting.
+ */
+int ts3a227e_enable_jack_detect(struct snd_soc_component *component,
+                               struct snd_soc_jack *jack)
+{
+       struct ts3a227e *ts3a227e = snd_soc_component_get_drvdata(component);
+
+       snd_jack_set_key(jack->jack, SND_JACK_BTN_0, KEY_MEDIA);
+       snd_jack_set_key(jack->jack, SND_JACK_BTN_1, KEY_VOLUMEUP);
+       snd_jack_set_key(jack->jack, SND_JACK_BTN_2, KEY_VOLUMEDOWN);
+       snd_jack_set_key(jack->jack, SND_JACK_BTN_3, KEY_VOICECOMMAND);
+
+       ts3a227e->jack = jack;
+       ts3a227e_jack_report(ts3a227e);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(ts3a227e_enable_jack_detect);
+
+static struct snd_soc_component_driver ts3a227e_soc_driver;
+
+static const struct regmap_config ts3a227e_regmap_config = {
+       .val_bits = 8,
+       .reg_bits = 8,
+
+       .max_register = TS3A227E_REG_KP_THRESHOLD_3,
+       .readable_reg = ts3a227e_readable_reg,
+       .writeable_reg = ts3a227e_writeable_reg,
+       .volatile_reg = ts3a227e_volatile_reg,
+
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = ts3a227e_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(ts3a227e_reg_defaults),
+};
+
+static int ts3a227e_i2c_probe(struct i2c_client *i2c,
+                             const struct i2c_device_id *id)
+{
+       struct ts3a227e *ts3a227e;
+       struct device *dev = &i2c->dev;
+       int ret;
+
+       ts3a227e = devm_kzalloc(&i2c->dev, sizeof(*ts3a227e), GFP_KERNEL);
+       if (ts3a227e == NULL)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, ts3a227e);
+
+       ts3a227e->regmap = devm_regmap_init_i2c(i2c, &ts3a227e_regmap_config);
+       if (IS_ERR(ts3a227e->regmap))
+               return PTR_ERR(ts3a227e->regmap);
+
+       ret = devm_request_threaded_irq(dev, i2c->irq, NULL, ts3a227e_interrupt,
+                                       IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                       "TS3A227E", ts3a227e);
+       if (ret) {
+               dev_err(dev, "Cannot request irq %d (%d)\n", i2c->irq, ret);
+               return ret;
+       }
+
+       ret = devm_snd_soc_register_component(&i2c->dev, &ts3a227e_soc_driver,
+                                             NULL, 0);
+       if (ret)
+               return ret;
+
+       /* Enable interrupts except for ADC complete. */
+       regmap_update_bits(ts3a227e->regmap, TS3A227E_REG_INTERRUPT_DISABLE,
+                          INTB_DISABLE | ADC_COMPLETE_INT_DISABLE,
+                          ADC_COMPLETE_INT_DISABLE);
+
+       return 0;
+}
+
+static const struct i2c_device_id ts3a227e_i2c_ids[] = {
+       { "ts3a227e", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ts3a227e_i2c_ids);
+
+static const struct of_device_id ts3a227e_of_match[] = {
+       { .compatible = "ti,ts3a227e", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, ts3a227e_of_match);
+
+static struct i2c_driver ts3a227e_driver = {
+       .driver = {
+               .name = "ts3a227e",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(ts3a227e_of_match),
+       },
+       .probe = ts3a227e_i2c_probe,
+       .id_table = ts3a227e_i2c_ids,
+};
+module_i2c_driver(ts3a227e_driver);
+
+MODULE_DESCRIPTION("ASoC ts3a227e driver");
+MODULE_AUTHOR("Dylan Reid <dgreid@chromium.org>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/codecs/ts3a227e.h b/sound/soc/codecs/ts3a227e.h
new file mode 100644 (file)
index 0000000..e2acf9c
--- /dev/null
@@ -0,0 +1,17 @@
+/*
+ * TS3A227E Autonous Audio Accessory Detection and Configureation Switch
+ *
+ * Copyright (C) 2014 Google, Inc.
+ *
+ * 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 _TS3A227E_H
+#define _TS3A227E_H
+
+int ts3a227e_enable_jack_detect(struct snd_soc_component *component,
+                               struct snd_soc_jack *jack);
+
+#endif
index b6b0cb3..27f3b21 100644 (file)
@@ -2177,8 +2177,6 @@ static int twl4030_soc_remove(struct snd_soc_codec *codec)
        struct twl4030_priv *twl4030 = snd_soc_codec_get_drvdata(codec);
        struct twl4030_codec_data *pdata = twl4030->pdata;
 
-       twl4030_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
        if (pdata && pdata->hs_extmute && gpio_is_valid(pdata->hs_extmute_gpio))
                gpio_free(pdata->hs_extmute_gpio);
 
index 0f6067f..5ff2b1e 100644 (file)
@@ -1095,25 +1095,6 @@ static struct snd_soc_dai_driver twl6040_dai[] = {
 },
 };
 
-#ifdef CONFIG_PM
-static int twl6040_suspend(struct snd_soc_codec *codec)
-{
-       twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int twl6040_resume(struct snd_soc_codec *codec)
-{
-       twl6040_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-#else
-#define twl6040_suspend NULL
-#define twl6040_resume NULL
-#endif
-
 static int twl6040_probe(struct snd_soc_codec *codec)
 {
        struct twl6040_data *priv;
@@ -1160,7 +1141,6 @@ static int twl6040_remove(struct snd_soc_codec *codec)
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
 
        free_irq(priv->plug_irq, codec);
-       twl6040_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
        return 0;
 }
@@ -1168,11 +1148,10 @@ static int twl6040_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_twl6040 = {
        .probe = twl6040_probe,
        .remove = twl6040_remove,
-       .suspend = twl6040_suspend,
-       .resume = twl6040_resume,
        .read = twl6040_read,
        .write = twl6040_write,
        .set_bias_level = twl6040_set_bias_level,
+       .suspend_bias_off = true,
        .ignore_pmdown_time = true,
 
        .controls = twl6040_snd_controls,
index 32b2f78..4056260 100644 (file)
@@ -518,11 +518,6 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
 
        uda134x_reset(codec);
 
-       if (pd->is_powered_on_standby)
-               uda134x_set_bias_level(codec, SND_SOC_BIAS_ON);
-       else
-               uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        if (pd->model == UDA134X_UDA1341) {
                widgets = uda1341_dapm_widgets;
                num_widgets = ARRAY_SIZE(uda1341_dapm_widgets);
@@ -574,44 +569,21 @@ static int uda134x_soc_remove(struct snd_soc_codec *codec)
 {
        struct uda134x_priv *uda134x = snd_soc_codec_get_drvdata(codec);
 
-       uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
        kfree(uda134x);
        return 0;
 }
 
-#if defined(CONFIG_PM)
-static int uda134x_soc_suspend(struct snd_soc_codec *codec)
-{
-       uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       uda134x_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int uda134x_soc_resume(struct snd_soc_codec *codec)
-{
-       uda134x_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
-       uda134x_set_bias_level(codec, SND_SOC_BIAS_ON);
-       return 0;
-}
-#else
-#define uda134x_soc_suspend NULL
-#define uda134x_soc_resume NULL
-#endif /* CONFIG_PM */
-
 static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
        .probe =        uda134x_soc_probe,
        .remove =       uda134x_soc_remove,
-       .suspend =      uda134x_soc_suspend,
-       .resume =       uda134x_soc_resume,
        .reg_cache_size = sizeof(uda134x_reg),
        .reg_word_size = sizeof(u8),
        .reg_cache_default = uda134x_reg,
        .reg_cache_step = 1,
        .read = uda134x_read_reg_cache,
-       .write = uda134x_write,
        .set_bias_level = uda134x_set_bias_level,
+       .suspend_bias_off = true,
+
        .dapm_widgets = uda134x_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),
        .dapm_routes = uda134x_dapm_routes,
index e62e707..dc7778b 100644 (file)
@@ -693,18 +693,6 @@ static struct snd_soc_dai_driver uda1380_dai[] = {
 },
 };
 
-static int uda1380_suspend(struct snd_soc_codec *codec)
-{
-       uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int uda1380_resume(struct snd_soc_codec *codec)
-{
-       uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 static int uda1380_probe(struct snd_soc_codec *codec)
 {
        struct uda1380_platform_data *pdata =codec->dev->platform_data;
@@ -739,8 +727,6 @@ static int uda1380_probe(struct snd_soc_codec *codec)
 
        INIT_WORK(&uda1380->work, uda1380_flush_work);
 
-       /* power on device */
-       uda1380_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        /* set clock input */
        switch (pdata->dac_clk) {
        case UDA1380_DAC_CLK_SYSCLK:
@@ -766,8 +752,6 @@ static int uda1380_remove(struct snd_soc_codec *codec)
 {
        struct uda1380_platform_data *pdata =codec->dev->platform_data;
 
-       uda1380_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
        gpio_free(pdata->gpio_reset);
        gpio_free(pdata->gpio_power);
 
@@ -777,11 +761,11 @@ static int uda1380_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_uda1380 = {
        .probe =        uda1380_probe,
        .remove =       uda1380_remove,
-       .suspend =      uda1380_suspend,
-       .resume =       uda1380_resume,
        .read =         uda1380_read_reg_cache,
        .write =        uda1380_write,
        .set_bias_level = uda1380_set_bias_level,
+       .suspend_bias_off = true,
+
        .reg_cache_size = ARRAY_SIZE(uda1380_reg),
        .reg_word_size = sizeof(u16),
        .reg_cache_default = uda1380_reg,
index f3d4e88..00aea41 100644 (file)
@@ -452,7 +452,6 @@ static int wl1273_probe(struct snd_soc_codec *codec)
 {
        struct wl1273_core **core = codec->dev->platform_data;
        struct wl1273_priv *wl1273;
-       int r;
 
        dev_dbg(codec->dev, "%s.\n", __func__);
 
@@ -470,12 +469,7 @@ static int wl1273_probe(struct snd_soc_codec *codec)
 
        snd_soc_codec_set_drvdata(codec, wl1273);
 
-       r = snd_soc_add_codec_controls(codec, wl1273_controls,
-                                ARRAY_SIZE(wl1273_controls));
-       if (r)
-               kfree(wl1273);
-
-       return r;
+       return 0;
 }
 
 static int wl1273_remove(struct snd_soc_codec *codec)
@@ -492,6 +486,8 @@ static struct snd_soc_codec_driver soc_codec_dev_wl1273 = {
        .probe = wl1273_probe,
        .remove = wl1273_remove,
 
+       .controls = wl1273_controls,
+       .num_controls = ARRAY_SIZE(wl1273_controls),
        .dapm_widgets = wl1273_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets),
        .dapm_routes = wl1273_dapm_routes,
index f602349..d78fb8d 100644 (file)
@@ -619,10 +619,10 @@ static int wm5102_out_comp_coeff_get(struct snd_kcontrol *kcontrol,
        struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
        uint16_t data;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&arizona->dac_comp_lock);
        data = cpu_to_be16(arizona->dac_comp_coeff);
        memcpy(ucontrol->value.bytes.data, &data, sizeof(data));
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&arizona->dac_comp_lock);
 
        return 0;
 }
@@ -633,11 +633,11 @@ static int wm5102_out_comp_coeff_put(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&arizona->dac_comp_lock);
        memcpy(&arizona->dac_comp_coeff, ucontrol->value.bytes.data,
               sizeof(arizona->dac_comp_coeff));
        arizona->dac_comp_coeff = be16_to_cpu(arizona->dac_comp_coeff);
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&arizona->dac_comp_lock);
 
        return 0;
 }
@@ -648,9 +648,9 @@ static int wm5102_out_comp_switch_get(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&arizona->dac_comp_lock);
        ucontrol->value.integer.value[0] = arizona->dac_comp_enabled;
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&arizona->dac_comp_lock);
 
        return 0;
 }
@@ -661,9 +661,9 @@ static int wm5102_out_comp_switch_put(struct snd_kcontrol *kcontrol,
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&arizona->dac_comp_lock);
        arizona->dac_comp_enabled = ucontrol->value.integer.value[0];
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&arizona->dac_comp_lock);
 
        return 0;
 }
@@ -1900,6 +1900,8 @@ static int wm5102_probe(struct platform_device *pdev)
                return -ENOMEM;
        platform_set_drvdata(pdev, wm5102);
 
+       mutex_init(&arizona->dac_comp_lock);
+
        wm5102->core.arizona = arizona;
        wm5102->core.num_inputs = 6;
 
index 628ec77..87f664b 100644 (file)
@@ -1242,19 +1242,6 @@ static int wm8350_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-static int wm8350_suspend(struct snd_soc_codec *codec)
-{
-       wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8350_resume(struct snd_soc_codec *codec)
-{
-       wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
 static void wm8350_hp_work(struct wm8350_data *priv,
                           struct wm8350_jack_data *jack,
                           u16 mask)
@@ -1565,9 +1552,6 @@ static  int wm8350_codec_probe(struct snd_soc_codec *codec)
        wm8350_register_irq(wm8350, WM8350_IRQ_CODEC_MICD,
                            wm8350_mic_handler, 0, "Microphone detect", priv);
 
-
-       wm8350_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        return 0;
 }
 
@@ -1596,8 +1580,6 @@ static int  wm8350_codec_remove(struct snd_soc_codec *codec)
         * wait for its completion */
        flush_delayed_work(&codec->dapm.delayed_work);
 
-       wm8350_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
        wm8350_clear_bits(wm8350, WM8350_POWER_MGMT_5, WM8350_CODEC_ENA);
 
        return 0;
@@ -1613,10 +1595,9 @@ static struct regmap *wm8350_get_regmap(struct device *dev)
 static struct snd_soc_codec_driver soc_codec_dev_wm8350 = {
        .probe =        wm8350_codec_probe,
        .remove =       wm8350_codec_remove,
-       .suspend =      wm8350_suspend,
-       .resume =       wm8350_resume,
        .get_regmap =   wm8350_get_regmap,
        .set_bias_level = wm8350_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = wm8350_snd_controls,
        .num_controls = ARRAY_SIZE(wm8350_snd_controls),
index 72471be..385894f 100644 (file)
@@ -58,12 +58,10 @@ static struct regulator_bulk_data power[] = {
 
 /* codec private data */
 struct wm8400_priv {
-       struct snd_soc_codec *codec;
        struct wm8400 *wm8400;
        u16 fake_register;
        unsigned int sysclk;
        unsigned int pcmclk;
-       struct work_struct work;
        int fll_in, fll_out;
 };
 
@@ -1278,30 +1276,6 @@ static struct snd_soc_dai_driver wm8400_dai = {
        .ops = &wm8400_dai_ops,
 };
 
-static int wm8400_suspend(struct snd_soc_codec *codec)
-{
-       wm8400_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int wm8400_resume(struct snd_soc_codec *codec)
-{
-       wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static void wm8400_probe_deferred(struct work_struct *work)
-{
-       struct wm8400_priv *priv = container_of(work, struct wm8400_priv,
-                                               work);
-       struct snd_soc_codec *codec = priv->codec;
-
-       /* charge output caps */
-       wm8400_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-}
-
 static int wm8400_codec_probe(struct snd_soc_codec *codec)
 {
        struct wm8400 *wm8400 = dev_get_platdata(codec->dev);
@@ -1316,7 +1290,6 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec)
 
        snd_soc_codec_set_drvdata(codec, priv);
        priv->wm8400 = wm8400;
-       priv->codec = codec;
 
        ret = devm_regulator_bulk_get(wm8400->dev,
                                 ARRAY_SIZE(power), &power[0]);
@@ -1325,8 +1298,6 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec)
                return ret;
        }
 
-       INIT_WORK(&priv->work, wm8400_probe_deferred);
-
        wm8400_codec_reset(codec);
 
        reg = snd_soc_read(codec, WM8400_POWER_MANAGEMENT_1);
@@ -1343,8 +1314,6 @@ static int wm8400_codec_probe(struct snd_soc_codec *codec)
        snd_soc_write(codec, WM8400_LEFT_OUTPUT_VOLUME, 0x50 | (1<<8));
        snd_soc_write(codec, WM8400_RIGHT_OUTPUT_VOLUME, 0x50 | (1<<8));
 
-       if (!schedule_work(&priv->work))
-               return -EINVAL;
        return 0;
 }
 
@@ -1369,10 +1338,9 @@ static struct regmap *wm8400_get_regmap(struct device *dev)
 static struct snd_soc_codec_driver soc_codec_dev_wm8400 = {
        .probe =        wm8400_codec_probe,
        .remove =       wm8400_codec_remove,
-       .suspend =      wm8400_suspend,
-       .resume =       wm8400_resume,
        .get_regmap =   wm8400_get_regmap,
        .set_bias_level = wm8400_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = wm8400_snd_controls,
        .num_controls = ARRAY_SIZE(wm8400_snd_controls),
index e11127f..8736ad0 100644 (file)
@@ -575,41 +575,17 @@ static struct snd_soc_dai_driver wm8510_dai = {
        .symmetric_rates = 1,
 };
 
-static int wm8510_suspend(struct snd_soc_codec *codec)
-{
-       wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8510_resume(struct snd_soc_codec *codec)
-{
-       wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 static int wm8510_probe(struct snd_soc_codec *codec)
 {
        wm8510_reset(codec);
 
-       /* power on device */
-       wm8510_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-/* power down chip */
-static int wm8510_remove(struct snd_soc_codec *codec)
-{
-       wm8510_set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8510 = {
        .probe =        wm8510_probe,
-       .remove =       wm8510_remove,
-       .suspend =      wm8510_suspend,
-       .resume =       wm8510_resume,
        .set_bias_level = wm8510_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = wm8510_snd_controls,
        .num_controls = ARRAY_SIZE(wm8510_snd_controls),
index ec1f574..b1cc94f 100644 (file)
@@ -372,23 +372,6 @@ static struct snd_soc_dai_driver wm8523_dai = {
        .ops = &wm8523_dai_ops,
 };
 
-#ifdef CONFIG_PM
-static int wm8523_suspend(struct snd_soc_codec *codec)
-{
-       wm8523_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8523_resume(struct snd_soc_codec *codec)
-{
-       wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-#else
-#define wm8523_suspend NULL
-#define wm8523_resume NULL
-#endif
-
 static int wm8523_probe(struct snd_soc_codec *codec)
 {
        struct wm8523_priv *wm8523 = snd_soc_codec_get_drvdata(codec);
@@ -402,23 +385,13 @@ static int wm8523_probe(struct snd_soc_codec *codec)
                            WM8523_DACR_VU, WM8523_DACR_VU);
        snd_soc_update_bits(codec, WM8523_DAC_CTRL3, WM8523_ZC, WM8523_ZC);
 
-       wm8523_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static int wm8523_remove(struct snd_soc_codec *codec)
-{
-       wm8523_set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8523 = {
        .probe =        wm8523_probe,
-       .remove =       wm8523_remove,
-       .suspend =      wm8523_suspend,
-       .resume =       wm8523_resume,
        .set_bias_level = wm8523_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = wm8523_controls,
        .num_controls = ARRAY_SIZE(wm8523_controls),
index 911605e..0a887c5 100644 (file)
@@ -882,8 +882,6 @@ static int wm8580_probe(struct snd_soc_codec *codec)
                goto err_regulator_enable;
        }
 
-       wm8580_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        return 0;
 
 err_regulator_enable:
@@ -897,8 +895,6 @@ static int wm8580_remove(struct snd_soc_codec *codec)
 {
        struct wm8580_priv *wm8580 = snd_soc_codec_get_drvdata(codec);
 
-       wm8580_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
        regulator_bulk_disable(ARRAY_SIZE(wm8580->supplies), wm8580->supplies);
 
        return 0;
index 32187e7..121e46d 100644 (file)
@@ -350,19 +350,6 @@ static struct snd_soc_dai_driver wm8711_dai = {
        .ops = &wm8711_ops,
 };
 
-static int wm8711_suspend(struct snd_soc_codec *codec)
-{
-       snd_soc_write(codec, WM8711_ACTIVE, 0x0);
-       wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8711_resume(struct snd_soc_codec *codec)
-{
-       wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 static int wm8711_probe(struct snd_soc_codec *codec)
 {
        int ret;
@@ -373,8 +360,6 @@ static int wm8711_probe(struct snd_soc_codec *codec)
                return ret;
        }
 
-       wm8711_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        /* Latch the update bits */
        snd_soc_update_bits(codec, WM8711_LOUT1V, 0x0100, 0x0100);
        snd_soc_update_bits(codec, WM8711_ROUT1V, 0x0100, 0x0100);
@@ -383,19 +368,11 @@ static int wm8711_probe(struct snd_soc_codec *codec)
 
 }
 
-/* power down chip */
-static int wm8711_remove(struct snd_soc_codec *codec)
-{
-       wm8711_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8711 = {
        .probe =        wm8711_probe,
-       .remove =       wm8711_remove,
-       .suspend =      wm8711_suspend,
-       .resume =       wm8711_resume,
        .set_bias_level = wm8711_set_bias_level,
+       .suspend_bias_off = true,
+
        .controls = wm8711_snd_controls,
        .num_controls = ARRAY_SIZE(wm8711_snd_controls),
        .dapm_widgets = wm8711_dapm_widgets,
index 38ff826..55c7fb4 100644 (file)
@@ -212,40 +212,10 @@ static struct snd_soc_dai_driver wm8728_dai = {
        .ops = &wm8728_dai_ops,
 };
 
-static int wm8728_suspend(struct snd_soc_codec *codec)
-{
-       wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int wm8728_resume(struct snd_soc_codec *codec)
-{
-       wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static int wm8728_probe(struct snd_soc_codec *codec)
-{
-       /* power on device */
-       wm8728_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static int wm8728_remove(struct snd_soc_codec *codec)
-{
-       wm8728_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8728 = {
-       .probe =        wm8728_probe,
-       .remove =       wm8728_remove,
-       .suspend =      wm8728_suspend,
-       .resume =       wm8728_resume,
        .set_bias_level = wm8728_set_bias_level,
+       .suspend_bias_off = true,
+
        .controls = wm8728_snd_controls,
        .num_controls = ARRAY_SIZE(wm8728_snd_controls),
        .dapm_widgets = wm8728_dapm_widgets,
index eebb328..b9211b4 100644 (file)
@@ -24,6 +24,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/spi/spi.h>
 #include <linux/of_device.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -50,6 +51,8 @@ struct wm8731_priv {
        int sysclk_type;
        int playback_fs;
        bool deemph;
+
+       struct mutex lock;
 };
 
 
@@ -138,7 +141,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
        if (deemph > 1)
                return -EINVAL;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&wm8731->lock);
        if (wm8731->deemph != deemph) {
                wm8731->deemph = deemph;
 
@@ -146,7 +149,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
 
                ret = 1;
        }
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&wm8731->lock);
 
        return ret;
 }
@@ -559,25 +562,6 @@ static struct snd_soc_dai_driver wm8731_dai = {
        .symmetric_rates = 1,
 };
 
-#ifdef CONFIG_PM
-static int wm8731_suspend(struct snd_soc_codec *codec)
-{
-       wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int wm8731_resume(struct snd_soc_codec *codec)
-{
-       wm8731_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-#else
-#define wm8731_suspend NULL
-#define wm8731_resume NULL
-#endif
-
 static int wm8731_probe(struct snd_soc_codec *codec)
 {
        struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
@@ -633,8 +617,6 @@ static int wm8731_remove(struct snd_soc_codec *codec)
 {
        struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
 
-       wm8731_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
        regulator_bulk_disable(ARRAY_SIZE(wm8731->supplies), wm8731->supplies);
 
        return 0;
@@ -643,9 +625,9 @@ static int wm8731_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_wm8731 = {
        .probe =        wm8731_probe,
        .remove =       wm8731_remove,
-       .suspend =      wm8731_suspend,
-       .resume =       wm8731_resume,
        .set_bias_level = wm8731_set_bias_level,
+       .suspend_bias_off = true,
+
        .dapm_widgets = wm8731_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(wm8731_dapm_widgets),
        .dapm_routes = wm8731_intercon,
@@ -680,11 +662,12 @@ static int wm8731_spi_probe(struct spi_device *spi)
        struct wm8731_priv *wm8731;
        int ret;
 
-       wm8731 = devm_kzalloc(&spi->dev, sizeof(struct wm8731_priv),
-                             GFP_KERNEL);
+       wm8731 = devm_kzalloc(&spi->dev, sizeof(*wm8731), GFP_KERNEL);
        if (wm8731 == NULL)
                return -ENOMEM;
 
+       mutex_init(&wm8731->lock);
+
        wm8731->regmap = devm_regmap_init_spi(spi, &wm8731_regmap);
        if (IS_ERR(wm8731->regmap)) {
                ret = PTR_ERR(wm8731->regmap);
index 744a422..ada9ac1 100644 (file)
@@ -277,17 +277,6 @@ static const struct snd_soc_dapm_route intercon[] = {
        { "AIF", NULL, "ADCR" },
 };
 
-static int wm8737_add_widgets(struct snd_soc_codec *codec)
-{
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       snd_soc_dapm_new_controls(dapm, wm8737_dapm_widgets,
-                                 ARRAY_SIZE(wm8737_dapm_widgets));
-       snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-
-       return 0;
-}
-
 /* codec mclk clock divider coefficients */
 static const struct {
        u32 mclk;
@@ -548,23 +537,6 @@ static struct snd_soc_dai_driver wm8737_dai = {
        .ops = &wm8737_dai_ops,
 };
 
-#ifdef CONFIG_PM
-static int wm8737_suspend(struct snd_soc_codec *codec)
-{
-       wm8737_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8737_resume(struct snd_soc_codec *codec)
-{
-       wm8737_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-#else
-#define wm8737_suspend NULL
-#define wm8737_resume NULL
-#endif
-
 static int wm8737_probe(struct snd_soc_codec *codec)
 {
        struct wm8737_priv *wm8737 = snd_soc_codec_get_drvdata(codec);
@@ -593,10 +565,6 @@ static int wm8737_probe(struct snd_soc_codec *codec)
        /* Bias level configuration will have done an extra enable */
        regulator_bulk_disable(ARRAY_SIZE(wm8737->supplies), wm8737->supplies);
 
-       snd_soc_add_codec_controls(codec, wm8737_snd_controls,
-                            ARRAY_SIZE(wm8737_snd_controls));
-       wm8737_add_widgets(codec);
-
        return 0;
 
 err_enable:
@@ -605,18 +573,17 @@ err_get:
        return ret;
 }
 
-static int wm8737_remove(struct snd_soc_codec *codec)
-{
-       wm8737_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8737 = {
        .probe          = wm8737_probe,
-       .remove         = wm8737_remove,
-       .suspend        = wm8737_suspend,
-       .resume         = wm8737_resume,
        .set_bias_level = wm8737_set_bias_level,
+       .suspend_bias_off = true,
+
+       .controls = wm8737_snd_controls,
+       .num_controls = ARRAY_SIZE(wm8737_snd_controls),
+       .dapm_widgets = wm8737_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(wm8737_dapm_widgets),
+       .dapm_routes = intercon,
+       .num_dapm_routes = ARRAY_SIZE(intercon),
 };
 
 static const struct of_device_id wm8737_of_match[] = {
index 67653a2..f6847fd 100644 (file)
@@ -686,18 +686,6 @@ static struct snd_soc_dai_driver wm8750_dai = {
        .ops = &wm8750_dai_ops,
 };
 
-static int wm8750_suspend(struct snd_soc_codec *codec)
-{
-       wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8750_resume(struct snd_soc_codec *codec)
-{
-       wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 static int wm8750_probe(struct snd_soc_codec *codec)
 {
        int ret;
@@ -708,9 +696,6 @@ static int wm8750_probe(struct snd_soc_codec *codec)
                return ret;
        }
 
-       /* charge output caps */
-       wm8750_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        /* set the update bits */
        snd_soc_update_bits(codec, WM8750_LDAC, 0x0100, 0x0100);
        snd_soc_update_bits(codec, WM8750_RDAC, 0x0100, 0x0100);
@@ -724,18 +709,10 @@ static int wm8750_probe(struct snd_soc_codec *codec)
        return ret;
 }
 
-static int wm8750_remove(struct snd_soc_codec *codec)
-{
-       wm8750_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8750 = {
        .probe =        wm8750_probe,
-       .remove =       wm8750_remove,
-       .suspend =      wm8750_suspend,
-       .resume =       wm8750_resume,
        .set_bias_level = wm8750_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = wm8750_snd_controls,
        .num_controls = ARRAY_SIZE(wm8750_snd_controls),
index 70952ce..c13050b 100644 (file)
@@ -408,24 +408,6 @@ static struct snd_soc_dai_driver wm8776_dai[] = {
        },
 };
 
-#ifdef CONFIG_PM
-static int wm8776_suspend(struct snd_soc_codec *codec)
-{
-       wm8776_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int wm8776_resume(struct snd_soc_codec *codec)
-{
-       wm8776_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-#else
-#define wm8776_suspend NULL
-#define wm8776_resume NULL
-#endif
-
 static int wm8776_probe(struct snd_soc_codec *codec)
 {
        int ret = 0;
@@ -436,8 +418,6 @@ static int wm8776_probe(struct snd_soc_codec *codec)
                return ret;
        }
 
-       wm8776_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        /* Latch the update bits; right channel only since we always
         * update both. */
        snd_soc_update_bits(codec, WM8776_HPRVOL, 0x100, 0x100);
@@ -446,19 +426,10 @@ static int wm8776_probe(struct snd_soc_codec *codec)
        return ret;
 }
 
-/* power down chip */
-static int wm8776_remove(struct snd_soc_codec *codec)
-{
-       wm8776_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8776 = {
        .probe =        wm8776_probe,
-       .remove =       wm8776_remove,
-       .suspend =      wm8776_suspend,
-       .resume =       wm8776_resume,
        .set_bias_level = wm8776_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = wm8776_snd_controls,
        .num_controls = ARRAY_SIZE(wm8776_snd_controls),
index 3addc5f..1315f76 100644 (file)
@@ -524,7 +524,6 @@ static int wm8804_remove(struct snd_soc_codec *codec)
        int i;
 
        wm8804 = snd_soc_codec_get_drvdata(codec);
-       wm8804_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
        for (i = 0; i < ARRAY_SIZE(wm8804->supplies); ++i)
                regulator_unregister_notifier(wm8804->supplies[i].consumer,
@@ -606,8 +605,6 @@ static int wm8804_probe(struct snd_soc_codec *codec)
                goto err_reg_enable;
        }
 
-       wm8804_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        return 0;
 
 err_reg_enable:
index 44a5f15..3a0d4b7 100644 (file)
@@ -1209,16 +1209,8 @@ static int wm8900_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-/* power down chip */
-static int wm8900_remove(struct snd_soc_codec *codec)
-{
-       wm8900_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8900 = {
        .probe =        wm8900_probe,
-       .remove =       wm8900_remove,
        .suspend =      wm8900_suspend,
        .resume =       wm8900_resume,
        .set_bias_level = wm8900_set_bias_level,
index c038b3e..cc6b0ef 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/regmap.h>
 #include <linux/slab.h>
 #include <linux/irq.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
@@ -117,12 +118,12 @@ static const struct reg_default wm8903_reg_defaults[] = {
 struct wm8903_priv {
        struct wm8903_platform_data *pdata;
        struct device *dev;
-       struct snd_soc_codec *codec;
        struct regmap *regmap;
 
        int sysclk;
        int irq;
 
+       struct mutex lock;
        int fs;
        int deemph;
 
@@ -457,7 +458,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
        if (deemph > 1)
                return -EINVAL;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&wm8903->lock);
        if (wm8903->deemph != deemph) {
                wm8903->deemph = deemph;
 
@@ -465,7 +466,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
 
                ret = 1;
        }
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&wm8903->lock);
 
        return ret;
 }
@@ -1757,21 +1758,12 @@ static struct snd_soc_dai_driver wm8903_dai = {
        .symmetric_rates = 1,
 };
 
-static int wm8903_suspend(struct snd_soc_codec *codec)
-{
-       wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
 static int wm8903_resume(struct snd_soc_codec *codec)
 {
        struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
 
        regcache_sync(wm8903->regmap);
 
-       wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        return 0;
 }
 
@@ -1889,33 +1881,12 @@ static void wm8903_free_gpio(struct wm8903_priv *wm8903)
 }
 #endif
 
-static int wm8903_probe(struct snd_soc_codec *codec)
-{
-       struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
-
-       wm8903->codec = codec;
-
-       /* power on device */
-       wm8903_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-/* power down chip */
-static int wm8903_remove(struct snd_soc_codec *codec)
-{
-       wm8903_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8903 = {
-       .probe =        wm8903_probe,
-       .remove =       wm8903_remove,
-       .suspend =      wm8903_suspend,
        .resume =       wm8903_resume,
        .set_bias_level = wm8903_set_bias_level,
        .seq_notifier = wm8903_seq_notifier,
+       .suspend_bias_off = true,
+
        .controls = wm8903_snd_controls,
        .num_controls = ARRAY_SIZE(wm8903_snd_controls),
        .dapm_widgets = wm8903_dapm_widgets,
@@ -2023,6 +1994,8 @@ static int wm8903_i2c_probe(struct i2c_client *i2c,
                              GFP_KERNEL);
        if (wm8903 == NULL)
                return -ENOMEM;
+
+       mutex_init(&wm8903->lock);
        wm8903->dev = &i2c->dev;
 
        wm8903->regmap = devm_regmap_init_i2c(i2c, &wm8903_regmap);
index 5201104..e4142b4 100644 (file)
@@ -695,17 +695,6 @@ static struct snd_soc_dai_driver wm8940_dai = {
        .symmetric_rates = 1,
 };
 
-static int wm8940_suspend(struct snd_soc_codec *codec)
-{
-       return wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF);
-}
-
-static int wm8940_resume(struct snd_soc_codec *codec)
-{
-       wm8940_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 static int wm8940_probe(struct snd_soc_codec *codec)
 {
        struct wm8940_setup_data *pdata = codec->dev->platform_data;
@@ -736,18 +725,11 @@ static int wm8940_probe(struct snd_soc_codec *codec)
        return ret;
 }
 
-static int wm8940_remove(struct snd_soc_codec *codec)
-{
-       wm8940_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8940 = {
        .probe =        wm8940_probe,
-       .remove =       wm8940_remove,
-       .suspend =      wm8940_suspend,
-       .resume =       wm8940_resume,
        .set_bias_level = wm8940_set_bias_level,
+       .suspend_bias_off = true,
+
        .controls =     wm8940_snd_controls,
        .num_controls = ARRAY_SIZE(wm8940_snd_controls),
        .dapm_widgets = wm8940_dapm_widgets,
index 09d91d9..1173f7f 100644 (file)
@@ -866,29 +866,6 @@ static struct snd_soc_dai_driver wm8955_dai = {
        .ops = &wm8955_dai_ops,
 };
 
-#ifdef CONFIG_PM
-static int wm8955_suspend(struct snd_soc_codec *codec)
-{
-       struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
-
-       wm8955_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       regcache_mark_dirty(wm8955->regmap);
-
-       return 0;
-}
-
-static int wm8955_resume(struct snd_soc_codec *codec)
-{
-       wm8955_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-#else
-#define wm8955_suspend NULL
-#define wm8955_resume NULL
-#endif
-
 static int wm8955_probe(struct snd_soc_codec *codec)
 {
        struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
@@ -964,18 +941,10 @@ err_enable:
        return ret;
 }
 
-static int wm8955_remove(struct snd_soc_codec *codec)
-{
-       wm8955_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8955 = {
        .probe =        wm8955_probe,
-       .remove =       wm8955_remove,
-       .suspend =      wm8955_suspend,
-       .resume =       wm8955_resume,
        .set_bias_level = wm8955_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls =     wm8955_snd_controls,
        .num_controls = ARRAY_SIZE(wm8955_snd_controls),
index 0dada7f..3cbc82b 100644 (file)
@@ -867,9 +867,9 @@ static void wm8958_enh_eq_loaded(const struct firmware *fw, void *context)
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        if (fw && (wm8958_dsp2_fw(codec, "ENH_EQ", fw, true) == 0)) {
-               mutex_lock(&codec->mutex);
+               mutex_lock(&wm8994->fw_lock);
                wm8994->enh_eq = fw;
-               mutex_unlock(&codec->mutex);
+               mutex_unlock(&wm8994->fw_lock);
        }
 }
 
@@ -879,9 +879,9 @@ static void wm8958_mbc_vss_loaded(const struct firmware *fw, void *context)
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        if (fw && (wm8958_dsp2_fw(codec, "MBC+VSS", fw, true) == 0)) {
-               mutex_lock(&codec->mutex);
+               mutex_lock(&wm8994->fw_lock);
                wm8994->mbc_vss = fw;
-               mutex_unlock(&codec->mutex);
+               mutex_unlock(&wm8994->fw_lock);
        }
 }
 
@@ -891,9 +891,9 @@ static void wm8958_mbc_loaded(const struct firmware *fw, void *context)
        struct wm8994_priv *wm8994 = snd_soc_codec_get_drvdata(codec);
 
        if (fw && (wm8958_dsp2_fw(codec, "MBC", fw, true) == 0)) {
-               mutex_lock(&codec->mutex);
+               mutex_lock(&wm8994->fw_lock);
                wm8994->mbc = fw;
-               mutex_unlock(&codec->mutex);
+               mutex_unlock(&wm8994->fw_lock);
        }
 }
 
index 4dc4e85..031a1ae 100644 (file)
@@ -125,9 +125,10 @@ struct wm8960_priv {
        struct snd_soc_dapm_widget *out3;
        bool deemph;
        int playback_fs;
+       struct wm8960_data pdata;
 };
 
-#define wm8960_reset(c)        snd_soc_write(c, WM8960_RESET, 0)
+#define wm8960_reset(c)        regmap_write(c, WM8960_RESET, 0)
 
 /* enumerated controls */
 static const char *wm8960_polarity[] = {"No Inversion", "Left Inverted",
@@ -440,8 +441,8 @@ static const struct snd_soc_dapm_route audio_paths_capless[] = {
 
 static int wm8960_add_widgets(struct snd_soc_codec *codec)
 {
-       struct wm8960_data *pdata = codec->dev->platform_data;
        struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
+       struct wm8960_data *pdata = &wm8960->pdata;
        struct snd_soc_dapm_context *dapm = &codec->dapm;
        struct snd_soc_dapm_widget *w;
 
@@ -942,56 +943,15 @@ static struct snd_soc_dai_driver wm8960_dai = {
        .symmetric_rates = 1,
 };
 
-static int wm8960_suspend(struct snd_soc_codec *codec)
-{
-       struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
-
-       wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8960_resume(struct snd_soc_codec *codec)
-{
-       struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
-
-       wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 static int wm8960_probe(struct snd_soc_codec *codec)
 {
        struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
-       struct wm8960_data *pdata = dev_get_platdata(codec->dev);
-       int ret;
-
-       wm8960->set_bias_level = wm8960_set_bias_level_out3;
-
-       if (!pdata) {
-               dev_warn(codec->dev, "No platform data supplied\n");
-       } else {
-               if (pdata->capless)
-                       wm8960->set_bias_level = wm8960_set_bias_level_capless;
-       }
-
-       ret = wm8960_reset(codec);
-       if (ret < 0) {
-               dev_err(codec->dev, "Failed to issue reset\n");
-               return ret;
-       }
-
-       wm8960->set_bias_level(codec, SND_SOC_BIAS_STANDBY);
+       struct wm8960_data *pdata = &wm8960->pdata;
 
-       /* Latch the update bits */
-       snd_soc_update_bits(codec, WM8960_LINVOL, 0x100, 0x100);
-       snd_soc_update_bits(codec, WM8960_RINVOL, 0x100, 0x100);
-       snd_soc_update_bits(codec, WM8960_LADC, 0x100, 0x100);
-       snd_soc_update_bits(codec, WM8960_RADC, 0x100, 0x100);
-       snd_soc_update_bits(codec, WM8960_LDAC, 0x100, 0x100);
-       snd_soc_update_bits(codec, WM8960_RDAC, 0x100, 0x100);
-       snd_soc_update_bits(codec, WM8960_LOUT1, 0x100, 0x100);
-       snd_soc_update_bits(codec, WM8960_ROUT1, 0x100, 0x100);
-       snd_soc_update_bits(codec, WM8960_LOUT2, 0x100, 0x100);
-       snd_soc_update_bits(codec, WM8960_ROUT2, 0x100, 0x100);
+       if (pdata->capless)
+               wm8960->set_bias_level = wm8960_set_bias_level_capless;
+       else
+               wm8960->set_bias_level = wm8960_set_bias_level_out3;
 
        snd_soc_add_codec_controls(codec, wm8960_snd_controls,
                                     ARRAY_SIZE(wm8960_snd_controls));
@@ -1000,21 +960,10 @@ static int wm8960_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-/* power down chip */
-static int wm8960_remove(struct snd_soc_codec *codec)
-{
-       struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
-
-       wm8960->set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8960 = {
        .probe =        wm8960_probe,
-       .remove =       wm8960_remove,
-       .suspend =      wm8960_suspend,
-       .resume =       wm8960_resume,
        .set_bias_level = wm8960_set_bias_level,
+       .suspend_bias_off = true,
 };
 
 static const struct regmap_config wm8960_regmap = {
@@ -1029,6 +978,18 @@ static const struct regmap_config wm8960_regmap = {
        .volatile_reg = wm8960_volatile,
 };
 
+static void wm8960_set_pdata_from_of(struct i2c_client *i2c,
+                               struct wm8960_data *pdata)
+{
+       const struct device_node *np = i2c->dev.of_node;
+
+       if (of_property_read_bool(np, "wlf,capless"))
+               pdata->capless = true;
+
+       if (of_property_read_bool(np, "wlf,shared-lrclk"))
+               pdata->shared_lrclk = true;
+}
+
 static int wm8960_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
@@ -1045,7 +1006,18 @@ static int wm8960_i2c_probe(struct i2c_client *i2c,
        if (IS_ERR(wm8960->regmap))
                return PTR_ERR(wm8960->regmap);
 
-       if (pdata && pdata->shared_lrclk) {
+       if (pdata)
+               memcpy(&wm8960->pdata, pdata, sizeof(struct wm8960_data));
+       else if (i2c->dev.of_node)
+               wm8960_set_pdata_from_of(i2c, &wm8960->pdata);
+
+       ret = wm8960_reset(wm8960->regmap);
+       if (ret != 0) {
+               dev_err(&i2c->dev, "Failed to issue reset\n");
+               return ret;
+       }
+
+       if (wm8960->pdata.shared_lrclk) {
                ret = regmap_update_bits(wm8960->regmap, WM8960_ADDCTL2,
                                         0x4, 0x4);
                if (ret != 0) {
@@ -1055,6 +1027,18 @@ static int wm8960_i2c_probe(struct i2c_client *i2c,
                }
        }
 
+       /* Latch the update bits */
+       regmap_update_bits(wm8960->regmap, WM8960_LINVOL, 0x100, 0x100);
+       regmap_update_bits(wm8960->regmap, WM8960_RINVOL, 0x100, 0x100);
+       regmap_update_bits(wm8960->regmap, WM8960_LADC, 0x100, 0x100);
+       regmap_update_bits(wm8960->regmap, WM8960_RADC, 0x100, 0x100);
+       regmap_update_bits(wm8960->regmap, WM8960_LDAC, 0x100, 0x100);
+       regmap_update_bits(wm8960->regmap, WM8960_RDAC, 0x100, 0x100);
+       regmap_update_bits(wm8960->regmap, WM8960_LOUT1, 0x100, 0x100);
+       regmap_update_bits(wm8960->regmap, WM8960_ROUT1, 0x100, 0x100);
+       regmap_update_bits(wm8960->regmap, WM8960_LOUT2, 0x100, 0x100);
+       regmap_update_bits(wm8960->regmap, WM8960_ROUT2, 0x100, 0x100);
+
        i2c_set_clientdata(i2c, wm8960);
 
        ret = snd_soc_register_codec(&i2c->dev,
@@ -1075,10 +1059,17 @@ static const struct i2c_device_id wm8960_i2c_id[] = {
 };
 MODULE_DEVICE_TABLE(i2c, wm8960_i2c_id);
 
+static const struct of_device_id wm8960_of_match[] = {
+       { .compatible = "wlf,wm8960", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, wm8960_of_match);
+
 static struct i2c_driver wm8960_i2c_driver = {
        .driver = {
                .name = "wm8960",
                .owner = THIS_MODULE,
+               .of_match_table = wm8960_of_match,
        },
        .probe =    wm8960_i2c_probe,
        .remove =   wm8960_i2c_remove,
index 41d23e9..eeffd05 100644 (file)
@@ -835,7 +835,6 @@ static struct snd_soc_dai_driver wm8961_dai = {
 
 static int wm8961_probe(struct snd_soc_codec *codec)
 {
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
        u16 reg;
 
        /* Enable class W */
@@ -871,50 +870,33 @@ static int wm8961_probe(struct snd_soc_codec *codec)
        reg &= ~WM8961_MANUAL_MODE;
        snd_soc_write(codec, WM8961_CLOCKING_3, reg);
 
-       wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       snd_soc_add_codec_controls(codec, wm8961_snd_controls,
-                               ARRAY_SIZE(wm8961_snd_controls));
-       snd_soc_dapm_new_controls(dapm, wm8961_dapm_widgets,
-                                 ARRAY_SIZE(wm8961_dapm_widgets));
-       snd_soc_dapm_add_routes(dapm, audio_paths, ARRAY_SIZE(audio_paths));
-
-       return 0;
-}
-
-static int wm8961_remove(struct snd_soc_codec *codec)
-{
-       wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
 }
 
 #ifdef CONFIG_PM
-static int wm8961_suspend(struct snd_soc_codec *codec)
-{
-       wm8961_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
 
 static int wm8961_resume(struct snd_soc_codec *codec)
 {
        snd_soc_cache_sync(codec);
 
-       wm8961_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        return 0;
 }
 #else
-#define wm8961_suspend NULL
 #define wm8961_resume NULL
 #endif
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8961 = {
        .probe =        wm8961_probe,
-       .remove =       wm8961_remove,
-       .suspend =      wm8961_suspend,
        .resume =       wm8961_resume,
        .set_bias_level = wm8961_set_bias_level,
+       .suspend_bias_off = true,
+
+       .controls = wm8961_snd_controls,
+       .num_controls = ARRAY_SIZE(wm8961_snd_controls),
+       .dapm_widgets = wm8961_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(wm8961_dapm_widgets),
+       .dapm_routes = audio_paths,
+       .num_dapm_routes = ARRAY_SIZE(audio_paths),
 };
 
 static const struct regmap_config wm8961_regmap = {
index 9077411..1534d88 100644 (file)
@@ -26,6 +26,7 @@
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
 #include <linux/workqueue.h>
+#include <linux/mutex.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
@@ -67,6 +68,7 @@ struct wm8962_priv {
        int fll_fref;
        int fll_fout;
 
+       struct mutex dsp2_ena_lock;
        u16 dsp2_ena;
 
        struct delayed_work mic_work;
@@ -1570,7 +1572,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
        int dsp2_running = snd_soc_read(codec, WM8962_DSP2_POWER_MANAGEMENT) &
                WM8962_DSP2_ENA;
 
-       mutex_lock(&codec->mutex);
+       mutex_lock(&wm8962->dsp2_ena_lock);
 
        if (ucontrol->value.integer.value[0])
                wm8962->dsp2_ena |= 1 << shift;
@@ -1590,7 +1592,7 @@ static int wm8962_dsp2_ena_put(struct snd_kcontrol *kcontrol,
        }
 
 out:
-       mutex_unlock(&codec->mutex);
+       mutex_unlock(&wm8962->dsp2_ena_lock);
 
        return ret;
 }
@@ -3552,11 +3554,12 @@ static int wm8962_i2c_probe(struct i2c_client *i2c,
        unsigned int reg;
        int ret, i, irq_pol, trigger;
 
-       wm8962 = devm_kzalloc(&i2c->dev, sizeof(struct wm8962_priv),
-                             GFP_KERNEL);
+       wm8962 = devm_kzalloc(&i2c->dev, sizeof(*wm8962), GFP_KERNEL);
        if (wm8962 == NULL)
                return -ENOMEM;
 
+       mutex_init(&wm8962->dsp2_ena_lock);
+
        i2c_set_clientdata(i2c, wm8962);
 
        INIT_DELAYED_WORK(&wm8962->mic_work, wm8962_mic_work);
index 682e9ed..ff0e464 100644 (file)
@@ -568,18 +568,6 @@ static struct snd_soc_dai_driver wm8974_dai = {
        .symmetric_rates = 1,
 };
 
-static int wm8974_suspend(struct snd_soc_codec *codec)
-{
-       wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8974_resume(struct snd_soc_codec *codec)
-{
-       wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 static const struct regmap_config wm8974_regmap = {
        .reg_bits = 7,
        .val_bits = 9,
@@ -599,24 +587,13 @@ static int wm8974_probe(struct snd_soc_codec *codec)
                return ret;
        }
 
-       wm8974_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return ret;
-}
-
-/* power down chip */
-static int wm8974_remove(struct snd_soc_codec *codec)
-{
-       wm8974_set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8974 = {
        .probe =        wm8974_probe,
-       .remove =       wm8974_remove,
-       .suspend =      wm8974_suspend,
-       .resume =       wm8974_resume,
        .set_bias_level = wm8974_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = wm8974_snd_controls,
        .num_controls = ARRAY_SIZE(wm8974_snd_controls),
index ee2ba57..cf70329 100644 (file)
@@ -991,21 +991,11 @@ static int wm8978_probe(struct snd_soc_codec *codec)
        for (i = 0; i < ARRAY_SIZE(update_reg); i++)
                snd_soc_update_bits(codec, update_reg[i], 0x100, 0x100);
 
-       wm8978_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-/* power down chip */
-static int wm8978_remove(struct snd_soc_codec *codec)
-{
-       wm8978_set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8978 = {
        .probe =        wm8978_probe,
-       .remove =       wm8978_remove,
        .suspend =      wm8978_suspend,
        .resume =       wm8978_resume,
        .set_bias_level = wm8978_set_bias_level,
index ac5defd..5d1cf08 100644 (file)
@@ -967,29 +967,6 @@ static int wm8983_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8983_suspend(struct snd_soc_codec *codec)
-{
-       wm8983_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8983_resume(struct snd_soc_codec *codec)
-{
-       wm8983_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-#else
-#define wm8983_suspend NULL
-#define wm8983_resume NULL
-#endif
-
-static int wm8983_remove(struct snd_soc_codec *codec)
-{
-       wm8983_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static int wm8983_probe(struct snd_soc_codec *codec)
 {
        int ret;
@@ -1055,10 +1032,8 @@ static struct snd_soc_dai_driver wm8983_dai = {
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8983 = {
        .probe = wm8983_probe,
-       .remove = wm8983_remove,
-       .suspend = wm8983_suspend,
-       .resume = wm8983_resume,
        .set_bias_level = wm8983_set_bias_level,
+       .suspend_bias_off = true,
        .controls = wm8983_snd_controls,
        .num_controls = ARRAY_SIZE(wm8983_snd_controls),
        .dapm_widgets = wm8983_dapm_widgets,
index ee38019..0b3b54c 100644 (file)
@@ -961,29 +961,6 @@ static int wm8985_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm8985_suspend(struct snd_soc_codec *codec)
-{
-       wm8985_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8985_resume(struct snd_soc_codec *codec)
-{
-       wm8985_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-#else
-#define wm8985_suspend NULL
-#define wm8985_resume NULL
-#endif
-
-static int wm8985_remove(struct snd_soc_codec *codec)
-{
-       wm8985_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static int wm8985_probe(struct snd_soc_codec *codec)
 {
        size_t i;
@@ -1023,7 +1000,6 @@ static int wm8985_probe(struct snd_soc_codec *codec)
        snd_soc_update_bits(codec, WM8985_BIAS_CTRL, WM8985_BIASCUT,
                            WM8985_BIASCUT);
 
-       wm8985_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
        return 0;
 
 err_reg_enable:
@@ -1064,10 +1040,8 @@ static struct snd_soc_dai_driver wm8985_dai = {
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8985 = {
        .probe = wm8985_probe,
-       .remove = wm8985_remove,
-       .suspend = wm8985_suspend,
-       .resume = wm8985_resume,
        .set_bias_level = wm8985_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = wm8985_snd_controls,
        .num_controls = ARRAY_SIZE(wm8985_snd_controls),
index a5130d9..e418199 100644 (file)
@@ -793,21 +793,6 @@ static struct snd_soc_dai_driver wm8988_dai = {
        .symmetric_rates = 1,
 };
 
-static int wm8988_suspend(struct snd_soc_codec *codec)
-{
-       struct wm8988_priv *wm8988 = snd_soc_codec_get_drvdata(codec);
-
-       wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       regcache_mark_dirty(wm8988->regmap);
-       return 0;
-}
-
-static int wm8988_resume(struct snd_soc_codec *codec)
-{
-       wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 static int wm8988_probe(struct snd_soc_codec *codec)
 {
        int ret = 0;
@@ -825,23 +810,13 @@ static int wm8988_probe(struct snd_soc_codec *codec)
        snd_soc_update_bits(codec, WM8988_ROUT2V, 0x0100, 0x0100);
        snd_soc_update_bits(codec, WM8988_RINVOL, 0x0100, 0x0100);
 
-       wm8988_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
-static int wm8988_remove(struct snd_soc_codec *codec)
-{
-       wm8988_set_bias_level(codec, SND_SOC_BIAS_OFF);
        return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8988 = {
        .probe =        wm8988_probe,
-       .remove =       wm8988_remove,
-       .suspend =      wm8988_suspend,
-       .resume =       wm8988_resume,
        .set_bias_level = wm8988_set_bias_level,
+       .suspend_bias_off = true,
 
        .controls = wm8988_snd_controls,
        .num_controls = ARRAY_SIZE(wm8988_snd_controls),
index 03e43e3..8a58422 100644 (file)
@@ -1271,18 +1271,6 @@ static struct snd_soc_dai_driver wm8990_dai = {
        .ops = &wm8990_dai_ops,
 };
 
-static int wm8990_suspend(struct snd_soc_codec *codec)
-{
-       wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8990_resume(struct snd_soc_codec *codec)
-{
-       wm8990_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
 /*
  * initialise the WM8990 driver
  * register the mixer and dsp interfaces with the kernel
@@ -1309,19 +1297,11 @@ static int wm8990_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-/* power down chip */
-static int wm8990_remove(struct snd_soc_codec *codec)
-{
-       wm8990_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm8990 = {
        .probe =        wm8990_probe,
-       .remove =       wm8990_remove,
-       .suspend =      wm8990_suspend,
-       .resume =       wm8990_resume,
        .set_bias_level = wm8990_set_bias_level,
+       .suspend_bias_off = true,
+
        .controls =     wm8990_snd_controls,
        .num_controls = ARRAY_SIZE(wm8990_snd_controls),
        .dapm_widgets = wm8990_dapm_widgets,
index d0be897..b0ac2c3 100644 (file)
@@ -1227,32 +1227,6 @@ static int wm8991_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
-static int wm8991_suspend(struct snd_soc_codec *codec)
-{
-       wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8991_resume(struct snd_soc_codec *codec)
-{
-       wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       return 0;
-}
-
-/* power down chip */
-static int wm8991_remove(struct snd_soc_codec *codec)
-{
-       wm8991_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
-static int wm8991_probe(struct snd_soc_codec *codec)
-{
-       wm8991_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-
 #define WM8991_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
                        SNDRV_PCM_FMTBIT_S24_LE)
 
@@ -1293,11 +1267,9 @@ static struct snd_soc_dai_driver wm8991_dai = {
 };
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8991 = {
-       .probe = wm8991_probe,
-       .remove = wm8991_remove,
-       .suspend = wm8991_suspend,
-       .resume = wm8991_resume,
        .set_bias_level = wm8991_set_bias_level,
+       .suspend_bias_off = true,
+
        .controls = wm8991_snd_controls,
        .num_controls = ARRAY_SIZE(wm8991_snd_controls),
        .dapm_widgets = wm8991_dapm_widgets,
index 93b14ed..53c6fe3 100644 (file)
@@ -1486,7 +1486,6 @@ static int wm8993_probe(struct snd_soc_codec *codec)
 {
        struct wm8993_priv *wm8993 = snd_soc_codec_get_drvdata(codec);
        struct snd_soc_dapm_context *dapm = &codec->dapm;
-       int ret;
 
        wm8993->hubs_data.hp_startup_mode = 1;
        wm8993->hubs_data.dcs_codes_l = -2;
@@ -1518,10 +1517,6 @@ static int wm8993_probe(struct snd_soc_codec *codec)
                                      wm8993->pdata.micbias1_lvl,
                                      wm8993->pdata.micbias2_lvl);
 
-       ret = wm8993_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       if (ret != 0)
-               return ret;
-
        snd_soc_add_codec_controls(codec, wm8993_snd_controls,
                             ARRAY_SIZE(wm8993_snd_controls));
        if (wm8993->pdata.num_retune_configs != 0) {
@@ -1550,12 +1545,6 @@ static int wm8993_probe(struct snd_soc_codec *codec)
 
 }
 
-static int wm8993_remove(struct snd_soc_codec *codec)
-{
-       wm8993_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 #ifdef CONFIG_PM
 static int wm8993_suspend(struct snd_soc_codec *codec)
 {
@@ -1629,7 +1618,6 @@ static const struct regmap_config wm8993_regmap = {
 
 static struct snd_soc_codec_driver soc_codec_dev_wm8993 = {
        .probe =        wm8993_probe,
-       .remove =       wm8993_remove,
        .suspend =      wm8993_suspend,
        .resume =       wm8993_resume,
        .set_bias_level = wm8993_set_bias_level,
index 1fcb9f3..36b767f 100644 (file)
@@ -4391,8 +4391,6 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
        struct wm8994 *control = wm8994->wm8994;
        int i;
 
-       wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
        for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
                wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i,
                                &wm8994->fll_locked[i]);
@@ -4457,6 +4455,8 @@ static int wm8994_probe(struct platform_device *pdev)
                return -ENOMEM;
        platform_set_drvdata(pdev, wm8994);
 
+       mutex_init(&wm8994->fw_lock);
+
        wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent);
 
        pm_runtime_enable(&pdev->dev);
index 6536f8d..dd73387 100644 (file)
@@ -13,6 +13,7 @@
 #include <linux/firmware.h>
 #include <linux/completion.h>
 #include <linux/workqueue.h>
+#include <linux/mutex.h>
 
 #include "wm_hubs.h"
 
@@ -156,6 +157,7 @@ struct wm8994_priv {
        unsigned int aif1clk_disable:1;
        unsigned int aif2clk_disable:1;
 
+       struct mutex fw_lock;
        int dsp_active;
        const struct firmware *cur_fw;
        const struct firmware *mbc;
index 1288ede..c280f0a 100644 (file)
@@ -2004,7 +2004,6 @@ static int wm8995_remove(struct snd_soc_codec *codec)
        int i;
 
        wm8995 = snd_soc_codec_get_drvdata(codec);
-       wm8995_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
        for (i = 0; i < ARRAY_SIZE(wm8995->supplies); ++i)
                regulator_unregister_notifier(wm8995->supplies[i].consumer,
@@ -2078,8 +2077,6 @@ static int wm8995_probe(struct snd_soc_codec *codec)
                goto err_reg_enable;
        }
 
-       wm8995_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        /* Latch volume updates (right only; we always do left then right). */
        snd_soc_update_bits(codec, WM8995_AIF1_DAC1_RIGHT_VOLUME,
                            WM8995_AIF1DAC1_VU_MASK, WM8995_AIF1DAC1_VU);
@@ -2102,13 +2099,6 @@ static int wm8995_probe(struct snd_soc_codec *codec)
 
        wm8995_update_class_w(codec);
 
-       snd_soc_add_codec_controls(codec, wm8995_snd_controls,
-                            ARRAY_SIZE(wm8995_snd_controls));
-       snd_soc_dapm_new_controls(&codec->dapm, wm8995_dapm_widgets,
-                                 ARRAY_SIZE(wm8995_dapm_widgets));
-       snd_soc_dapm_add_routes(&codec->dapm, wm8995_intercon,
-                               ARRAY_SIZE(wm8995_intercon));
-
        return 0;
 
 err_reg_enable:
@@ -2205,6 +2195,13 @@ static struct snd_soc_codec_driver soc_codec_dev_wm8995 = {
        .remove = wm8995_remove,
        .set_bias_level = wm8995_set_bias_level,
        .idle_bias_off = true,
+
+       .controls = wm8995_snd_controls,
+       .num_controls = ARRAY_SIZE(wm8995_snd_controls),
+       .dapm_widgets = wm8995_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(wm8995_dapm_widgets),
+       .dapm_routes = wm8995_intercon,
+       .num_dapm_routes = ARRAY_SIZE(wm8995_intercon),
 };
 
 static struct regmap_config wm8995_regmap = {
index 0cdc9e2..b1d946f 100644 (file)
@@ -1277,15 +1277,8 @@ static int wm9081_probe(struct snd_soc_codec *codec)
        return 0;
 }
 
-static int wm9081_remove(struct snd_soc_codec *codec)
-{
-       wm9081_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm9081 = {
        .probe =        wm9081_probe,
-       .remove =       wm9081_remove,
 
        .set_sysclk = wm9081_set_sysclk,
        .set_bias_level = wm9081_set_bias_level,
index a13f072..6ffe8dc 100644 (file)
@@ -550,45 +550,15 @@ static int wm9090_probe(struct snd_soc_codec *codec)
        snd_soc_update_bits(codec, WM9090_CLOCKING_1,
                            WM9090_TOCLK_ENA, WM9090_TOCLK_ENA);
 
-       wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
        wm9090_add_controls(codec);
 
        return 0;
 }
 
-#ifdef CONFIG_PM
-static int wm9090_suspend(struct snd_soc_codec *codec)
-{
-       wm9090_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
-static int wm9090_resume(struct snd_soc_codec *codec)
-{
-       wm9090_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-
-       return 0;
-}
-#else
-#define wm9090_suspend NULL
-#define wm9090_resume NULL
-#endif
-
-static int wm9090_remove(struct snd_soc_codec *codec)
-{
-       wm9090_set_bias_level(codec, SND_SOC_BIAS_OFF);
-
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_codec_dev_wm9090 = {
        .probe =        wm9090_probe,
-       .remove =       wm9090_remove,
-       .suspend =      wm9090_suspend,
-       .resume =       wm9090_resume,
        .set_bias_level = wm9090_set_bias_level,
+       .suspend_bias_off = true,
 };
 
 static const struct regmap_config wm9090_regmap = {
index c0b7f45..d3a800f 100644 (file)
@@ -203,13 +203,14 @@ static const struct snd_soc_dapm_route wm9705_audio_map[] = {
 /* We use a register cache to enhance read performance. */
 static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        u16 *cache = codec->reg_cache;
 
        switch (reg) {
        case AC97_RESET:
        case AC97_VENDOR_ID1:
        case AC97_VENDOR_ID2:
-               return soc_ac97_ops->read(codec->ac97, reg);
+               return soc_ac97_ops->read(ac97, reg);
        default:
                reg = reg >> 1;
 
@@ -223,9 +224,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec, unsigned int reg)
 static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
        unsigned int val)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        u16 *cache = codec->reg_cache;
 
-       soc_ac97_ops->write(codec->ac97, reg, val);
+       soc_ac97_ops->write(ac97, reg, val);
        reg = reg >> 1;
        if (reg < (ARRAY_SIZE(wm9705_reg)))
                cache[reg] = val;
@@ -263,7 +265,6 @@ static const struct snd_soc_dai_ops wm9705_dai_ops = {
 static struct snd_soc_dai_driver wm9705_dai[] = {
        {
                .name = "wm9705-hifi",
-               .ac97_control = 1,
                .playback = {
                        .stream_name = "HiFi Playback",
                        .channels_min = 1,
@@ -294,36 +295,41 @@ static struct snd_soc_dai_driver wm9705_dai[] = {
 
 static int wm9705_reset(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
        if (soc_ac97_ops->reset) {
-               soc_ac97_ops->reset(codec->ac97);
+               soc_ac97_ops->reset(ac97);
                if (ac97_read(codec, 0) == wm9705_reg[0])
                        return 0; /* Success */
        }
 
+       dev_err(codec->dev, "Failed to reset: AC97 link error\n");
+
        return -EIO;
 }
 
 #ifdef CONFIG_PM
 static int wm9705_soc_suspend(struct snd_soc_codec *codec)
 {
-       soc_ac97_ops->write(codec->ac97, AC97_POWERDOWN, 0xffff);
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+       soc_ac97_ops->write(ac97, AC97_POWERDOWN, 0xffff);
 
        return 0;
 }
 
 static int wm9705_soc_resume(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
        int i, ret;
        u16 *cache = codec->reg_cache;
 
        ret = wm9705_reset(codec);
-       if (ret < 0) {
-               printk(KERN_ERR "could not reset AC97 codec\n");
+       if (ret < 0)
                return ret;
-       }
 
        for (i = 2; i < ARRAY_SIZE(wm9705_reg) << 1; i += 2) {
-               soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+               soc_ac97_ops->write(ac97, i, cache[i>>1]);
        }
 
        return 0;
@@ -335,31 +341,34 @@ static int wm9705_soc_resume(struct snd_soc_codec *codec)
 
 static int wm9705_soc_probe(struct snd_soc_codec *codec)
 {
+       struct snd_ac97 *ac97;
        int ret = 0;
 
-       ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-       if (ret < 0) {
-               printk(KERN_ERR "wm9705: failed to register AC97 codec\n");
+       ac97 = snd_soc_new_ac97_codec(codec);
+       if (IS_ERR(ac97)) {
+               ret = PTR_ERR(ac97);
+               dev_err(codec->dev, "Failed to register AC97 codec\n");
                return ret;
        }
 
+       snd_soc_codec_set_drvdata(codec, ac97);
+
        ret = wm9705_reset(codec);
        if (ret)
                goto reset_err;
 
-       snd_soc_add_codec_controls(codec, wm9705_snd_ac97_controls,
-                               ARRAY_SIZE(wm9705_snd_ac97_controls));
-
        return 0;
 
 reset_err:
-       snd_soc_free_ac97_codec(codec);
+       snd_soc_free_ac97_codec(ac97);
        return ret;
 }
 
 static int wm9705_soc_remove(struct snd_soc_codec *codec)
 {
-       snd_soc_free_ac97_codec(codec);
+       struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+       snd_soc_free_ac97_codec(ac97);
        return 0;
 }
 
@@ -374,6 +383,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9705 = {
        .reg_word_size = sizeof(u16),
        .reg_cache_step = 2,
        .reg_cache_default = wm9705_reg,
+
+       .controls = wm9705_snd_ac97_controls,
+       .num_controls = ARRAY_SIZE(wm9705_snd_ac97_controls),
        .dapm_widgets = wm9705_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(wm9705_dapm_widgets),
        .dapm_routes = wm9705_audio_map,
index c5eb746..7c45971 100644 (file)
 #include <sound/tlv.h>
 #include "wm9712.h"
 
+struct wm9712_priv {
+       struct snd_ac97 *ac97;
+       unsigned int hp_mixer[2];
+       struct mutex lock;
+};
+
 static unsigned int ac97_read(struct snd_soc_codec *codec,
        unsigned int reg);
 static int ac97_write(struct snd_soc_codec *codec,
@@ -48,12 +54,10 @@ static const u16 wm9712_reg[] = {
        0x0000, 0x0000, 0x0000, 0x0000, /* 6e */
        0x0000, 0x0000, 0x0000, 0x0006, /* 76 */
        0x0001, 0x0000, 0x574d, 0x4c12, /* 7e */
-       0x0000, 0x0000 /* virtual hp mixers */
 };
 
-/* virtual HP mixers regs */
-#define HPL_MIXER      0x80
-#define HPR_MIXER      0x82
+#define HPL_MIXER      0x0
+#define HPR_MIXER      0x1
 
 static const char *wm9712_alc_select[] = {"None", "Left", "Right", "Stereo"};
 static const char *wm9712_alc_mux[] = {"Stereo", "Left", "Right", "None"};
@@ -157,75 +161,108 @@ SOC_SINGLE_TLV("Mic 2 Volume", AC97_MIC, 0, 31, 1, main_tlv),
 SOC_SINGLE_TLV("Mic Boost Volume", AC97_MIC, 7, 1, 0, boost_tlv),
 };
 
+static const unsigned int wm9712_mixer_mute_regs[] = {
+       AC97_VIDEO,
+       AC97_PCM,
+       AC97_LINE,
+       AC97_PHONE,
+       AC97_CD,
+       AC97_PC_BEEP,
+};
+
 /* We have to create a fake left and right HP mixers because
  * the codec only has a single control that is shared by both channels.
  * This makes it impossible to determine the audio path.
  */
-static int mixer_event(struct snd_soc_dapm_widget *w,
-       struct snd_kcontrol *k, int event)
+static int wm9712_hp_mixer_put(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
 {
-       u16 l, r, beep, line, phone, mic, pcm, aux;
-
-       l = ac97_read(w->codec, HPL_MIXER);
-       r = ac97_read(w->codec, HPR_MIXER);
-       beep = ac97_read(w->codec, AC97_PC_BEEP);
-       mic = ac97_read(w->codec, AC97_VIDEO);
-       phone = ac97_read(w->codec, AC97_PHONE);
-       line = ac97_read(w->codec, AC97_LINE);
-       pcm = ac97_read(w->codec, AC97_PCM);
-       aux = ac97_read(w->codec, AC97_CD);
-
-       if (l & 0x1 || r & 0x1)
-               ac97_write(w->codec, AC97_VIDEO, mic & 0x7fff);
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val = ucontrol->value.enumerated.item[0];
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int mixer, mask, shift, old;
+       struct snd_soc_dapm_update update;
+       bool change;
+
+       mixer = mc->shift >> 8;
+       shift = mc->shift & 0xff;
+       mask = 1 << shift;
+
+       mutex_lock(&wm9712->lock);
+       old = wm9712->hp_mixer[mixer];
+       if (ucontrol->value.enumerated.item[0])
+               wm9712->hp_mixer[mixer] |= mask;
        else
-               ac97_write(w->codec, AC97_VIDEO, mic | 0x8000);
+               wm9712->hp_mixer[mixer] &= ~mask;
+
+       change = old != wm9712->hp_mixer[mixer];
+       if (change) {
+               update.kcontrol = kcontrol;
+               update.reg = wm9712_mixer_mute_regs[shift];
+               update.mask = 0x8000;
+               if ((wm9712->hp_mixer[0] & mask) ||
+                   (wm9712->hp_mixer[1] & mask))
+                       update.val = 0x0;
+               else
+                       update.val = 0x8000;
+
+               snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
+                       &update);
+       }
 
-       if (l & 0x2 || r & 0x2)
-               ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
+       mutex_unlock(&wm9712->lock);
 
-       if (l & 0x4 || r & 0x4)
-               ac97_write(w->codec, AC97_LINE, line & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_LINE, line | 0x8000);
+       return change;
+}
 
-       if (l & 0x8 || r & 0x8)
-               ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
+static int wm9712_hp_mixer_get(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int shift, mixer;
 
-       if (l & 0x10 || r & 0x10)
-               ac97_write(w->codec, AC97_CD, aux & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_CD, aux | 0x8000);
+       mixer = mc->shift >> 8;
+       shift = mc->shift & 0xff;
 
-       if (l & 0x20 || r & 0x20)
-               ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
+       ucontrol->value.enumerated.item[0] =
+               (wm9712->hp_mixer[mixer] >> shift) & 1;
 
        return 0;
 }
 
+#define WM9712_HP_MIXER_CTRL(xname, xmixer, xshift) { \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, \
+       .get = wm9712_hp_mixer_get, .put = wm9712_hp_mixer_put, \
+       .private_value = SOC_SINGLE_VALUE(SND_SOC_NOPM, \
+               (xmixer << 8) | xshift, 1, 0, 0) \
+}
+
 /* Left Headphone Mixers */
 static const struct snd_kcontrol_new wm9712_hpl_mixer_controls[] = {
-       SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPL_MIXER, 5, 1, 0),
-       SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 4, 1, 0),
-       SOC_DAPM_SINGLE("Phone Bypass Switch", HPL_MIXER, 3, 1, 0),
-       SOC_DAPM_SINGLE("Line Bypass Switch", HPL_MIXER, 2, 1, 0),
-       SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 1, 1, 0),
-       SOC_DAPM_SINGLE("Mic Sidetone Switch", HPL_MIXER, 0, 1, 0),
+       WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPL_MIXER, 5),
+       WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 4),
+       WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPL_MIXER, 3),
+       WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPL_MIXER, 2),
+       WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 1),
+       WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPL_MIXER, 0),
 };
 
 /* Right Headphone Mixers */
 static const struct snd_kcontrol_new wm9712_hpr_mixer_controls[] = {
-       SOC_DAPM_SINGLE("PCBeep Bypass Switch", HPR_MIXER, 5, 1, 0),
-       SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 4, 1, 0),
-       SOC_DAPM_SINGLE("Phone Bypass Switch", HPR_MIXER, 3, 1, 0),
-       SOC_DAPM_SINGLE("Line Bypass Switch", HPR_MIXER, 2, 1, 0),
-       SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 1, 1, 0),
-       SOC_DAPM_SINGLE("Mic Sidetone Switch", HPR_MIXER, 0, 1, 0),
+       WM9712_HP_MIXER_CTRL("PCBeep Bypass Switch", HPR_MIXER, 5),
+       WM9712_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 4),
+       WM9712_HP_MIXER_CTRL("Phone Bypass Switch", HPR_MIXER, 3),
+       WM9712_HP_MIXER_CTRL("Line Bypass Switch", HPR_MIXER, 2),
+       WM9712_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 1),
+       WM9712_HP_MIXER_CTRL("Mic Sidetone Switch", HPR_MIXER, 0),
 };
 
 /* Speaker Mixer */
@@ -299,12 +336,10 @@ SND_SOC_DAPM_MUX("Right Mic Select Source", SND_SOC_NOPM, 0, 0,
 SND_SOC_DAPM_MUX("Differential Source", SND_SOC_NOPM, 0, 0,
        &wm9712_diff_sel_controls),
 SND_SOC_DAPM_MIXER("AC97 Mixer", SND_SOC_NOPM, 0, 0, NULL, 0),
-SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_INT_PAGING, 9, 1,
-       &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls),
-       mixer_event, SND_SOC_DAPM_POST_REG),
-SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_INT_PAGING, 8, 1,
-       &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls),
-        mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_INT_PAGING, 9, 1,
+       &wm9712_hpl_mixer_controls[0], ARRAY_SIZE(wm9712_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_INT_PAGING, 8, 1,
+       &wm9712_hpr_mixer_controls[0], ARRAY_SIZE(wm9712_hpr_mixer_controls)),
 SND_SOC_DAPM_MIXER("Phone Mixer", AC97_INT_PAGING, 6, 1,
        &wm9712_phone_mixer_controls[0], ARRAY_SIZE(wm9712_phone_mixer_controls)),
 SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_INT_PAGING, 7, 1,
@@ -450,12 +485,13 @@ static const struct snd_soc_dapm_route wm9712_audio_map[] = {
 static unsigned int ac97_read(struct snd_soc_codec *codec,
        unsigned int reg)
 {
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
        u16 *cache = codec->reg_cache;
 
        if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
                reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
                reg == AC97_REC_GAIN)
-               return soc_ac97_ops->read(codec->ac97, reg);
+               return soc_ac97_ops->read(wm9712->ac97, reg);
        else {
                reg = reg >> 1;
 
@@ -469,10 +505,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
 static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
        unsigned int val)
 {
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
        u16 *cache = codec->reg_cache;
 
-       if (reg < 0x7c)
-               soc_ac97_ops->write(codec->ac97, reg, val);
+       soc_ac97_ops->write(wm9712->ac97, reg, val);
        reg = reg >> 1;
        if (reg < (ARRAY_SIZE(wm9712_reg)))
                cache[reg] = val;
@@ -532,7 +568,6 @@ static const struct snd_soc_dai_ops wm9712_dai_ops_aux = {
 static struct snd_soc_dai_driver wm9712_dai[] = {
 {
        .name = "wm9712-hifi",
-       .ac97_control = 1,
        .playback = {
                .stream_name = "HiFi Playback",
                .channels_min = 1,
@@ -581,40 +616,35 @@ static int wm9712_set_bias_level(struct snd_soc_codec *codec,
 
 static int wm9712_reset(struct snd_soc_codec *codec, int try_warm)
 {
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+
        if (try_warm && soc_ac97_ops->warm_reset) {
-               soc_ac97_ops->warm_reset(codec->ac97);
+               soc_ac97_ops->warm_reset(wm9712->ac97);
                if (ac97_read(codec, 0) == wm9712_reg[0])
                        return 1;
        }
 
-       soc_ac97_ops->reset(codec->ac97);
+       soc_ac97_ops->reset(wm9712->ac97);
        if (soc_ac97_ops->warm_reset)
-               soc_ac97_ops->warm_reset(codec->ac97);
+               soc_ac97_ops->warm_reset(wm9712->ac97);
        if (ac97_read(codec, 0) != wm9712_reg[0])
                goto err;
        return 0;
 
 err:
-       printk(KERN_ERR "WM9712 AC97 reset failed\n");
+       dev_err(codec->dev, "Failed to reset: AC97 link error\n");
        return -EIO;
 }
 
-static int wm9712_soc_suspend(struct snd_soc_codec *codec)
-{
-       wm9712_set_bias_level(codec, SND_SOC_BIAS_OFF);
-       return 0;
-}
-
 static int wm9712_soc_resume(struct snd_soc_codec *codec)
 {
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
        int i, ret;
        u16 *cache = codec->reg_cache;
 
        ret = wm9712_reset(codec, 1);
-       if (ret < 0) {
-               printk(KERN_ERR "could not reset AC97 codec\n");
+       if (ret < 0)
                return ret;
-       }
 
        wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
@@ -624,7 +654,7 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
                        if (i == AC97_INT_PAGING || i == AC97_POWERDOWN ||
                            (i > 0x58 && i != 0x5c))
                                continue;
-                       soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+                       soc_ac97_ops->write(wm9712->ac97, i, cache[i>>1]);
                }
        }
 
@@ -633,52 +663,53 @@ static int wm9712_soc_resume(struct snd_soc_codec *codec)
 
 static int wm9712_soc_probe(struct snd_soc_codec *codec)
 {
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
        int ret = 0;
 
-       ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-       if (ret < 0) {
-               printk(KERN_ERR "wm9712: failed to register AC97 codec\n");
+       wm9712->ac97 = snd_soc_new_ac97_codec(codec);
+       if (IS_ERR(wm9712->ac97)) {
+               ret = PTR_ERR(wm9712->ac97);
+               dev_err(codec->dev, "Failed to register AC97 codec: %d\n", ret);
                return ret;
        }
 
        ret = wm9712_reset(codec, 0);
-       if (ret < 0) {
-               printk(KERN_ERR "Failed to reset WM9712: AC97 link error\n");
+       if (ret < 0)
                goto reset_err;
-       }
 
        /* set alc mux to none */
        ac97_write(codec, AC97_VIDEO, ac97_read(codec, AC97_VIDEO) | 0x3000);
 
-       wm9712_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       snd_soc_add_codec_controls(codec, wm9712_snd_ac97_controls,
-                               ARRAY_SIZE(wm9712_snd_ac97_controls));
-
        return 0;
 
 reset_err:
-       snd_soc_free_ac97_codec(codec);
+       snd_soc_free_ac97_codec(wm9712->ac97);
        return ret;
 }
 
 static int wm9712_soc_remove(struct snd_soc_codec *codec)
 {
-       snd_soc_free_ac97_codec(codec);
+       struct wm9712_priv *wm9712 = snd_soc_codec_get_drvdata(codec);
+
+       snd_soc_free_ac97_codec(wm9712->ac97);
        return 0;
 }
 
 static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
        .probe =        wm9712_soc_probe,
        .remove =       wm9712_soc_remove,
-       .suspend =      wm9712_soc_suspend,
        .resume =       wm9712_soc_resume,
        .read = ac97_read,
        .write = ac97_write,
        .set_bias_level = wm9712_set_bias_level,
+       .suspend_bias_off = true,
        .reg_cache_size = ARRAY_SIZE(wm9712_reg),
        .reg_word_size = sizeof(u16),
        .reg_cache_step = 2,
        .reg_cache_default = wm9712_reg,
+
+       .controls = wm9712_snd_ac97_controls,
+       .num_controls = ARRAY_SIZE(wm9712_snd_ac97_controls),
        .dapm_widgets = wm9712_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(wm9712_dapm_widgets),
        .dapm_routes = wm9712_audio_map,
@@ -687,6 +718,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9712 = {
 
 static int wm9712_probe(struct platform_device *pdev)
 {
+       struct wm9712_priv *wm9712;
+
+       wm9712 = devm_kzalloc(&pdev->dev, sizeof(*wm9712), GFP_KERNEL);
+       if (wm9712 == NULL)
+               return -ENOMEM;
+
+       mutex_init(&wm9712->lock);
+
+       platform_set_drvdata(pdev, wm9712);
+
        return snd_soc_register_codec(&pdev->dev,
                        &soc_codec_dev_wm9712, wm9712_dai, ARRAY_SIZE(wm9712_dai));
 }
index bddee30..5df7f6d 100644 (file)
 #include "wm9713.h"
 
 struct wm9713_priv {
+       struct snd_ac97 *ac97;
        u32 pll_in; /* PLL input frequency */
+       unsigned int hp_mixer[2];
+       struct mutex lock;
 };
 
 static unsigned int ac97_read(struct snd_soc_codec *codec,
@@ -59,13 +62,10 @@ static const u16 wm9713_reg[] = {
        0x0000, 0x0000, 0x0000, 0x0000,
        0x0000, 0x0000, 0x0000, 0x0006,
        0x0001, 0x0000, 0x574d, 0x4c13,
-       0x0000, 0x0000, 0x0000
 };
 
-/* virtual HP mixers regs */
-#define HPL_MIXER      0x80
-#define HPR_MIXER      0x82
-#define MICB_MUX       0x82
+#define HPL_MIXER 0
+#define HPR_MIXER 1
 
 static const char *wm9713_mic_mixer[] = {"Stereo", "Mic 1", "Mic 2", "Mute"};
 static const char *wm9713_rec_mux[] = {"Stereo", "Left", "Right", "Mute"};
@@ -110,7 +110,7 @@ SOC_ENUM_SINGLE(AC97_REC_GAIN_MIC, 10, 8, wm9713_dac_inv), /* dac invert 2 15 */
 SOC_ENUM_SINGLE(AC97_GENERAL_PURPOSE, 15, 2, wm9713_bass), /* bass control 16 */
 SOC_ENUM_SINGLE(AC97_PCI_SVID, 5, 2, wm9713_ng_type), /* noise gate type 17 */
 SOC_ENUM_SINGLE(AC97_3D_CONTROL, 12, 3, wm9713_mic_select), /* mic selection 18 */
-SOC_ENUM_SINGLE(MICB_MUX, 0, 2, wm9713_micb_select), /* mic selection 19 */
+SOC_ENUM_SINGLE_VIRT(2, wm9713_micb_select), /* mic selection 19 */
 };
 
 static const DECLARE_TLV_DB_SCALE(out_tlv, -4650, 150, 0);
@@ -234,6 +234,14 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
        return 0;
 }
 
+static const unsigned int wm9713_mixer_mute_regs[] = {
+       AC97_PC_BEEP,
+       AC97_MASTER_TONE,
+       AC97_PHONE,
+       AC97_REC_SEL,
+       AC97_PCM,
+       AC97_AUX,
+};
 
 /* We have to create a fake left and right HP mixers because
  * the codec only has a single control that is shared by both channels.
@@ -241,73 +249,95 @@ static int wm9713_voice_shutdown(struct snd_soc_dapm_widget *w,
  * register map, thus we add a new (virtual) register to help determine the
  * audio route within the device.
  */
-static int mixer_event(struct snd_soc_dapm_widget *w,
-       struct snd_kcontrol *kcontrol, int event)
+static int wm9713_hp_mixer_put(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
 {
-       u16 l, r, beep, tone, phone, rec, pcm, aux;
-
-       l = ac97_read(w->codec, HPL_MIXER);
-       r = ac97_read(w->codec, HPR_MIXER);
-       beep = ac97_read(w->codec, AC97_PC_BEEP);
-       tone = ac97_read(w->codec, AC97_MASTER_TONE);
-       phone = ac97_read(w->codec, AC97_PHONE);
-       rec = ac97_read(w->codec, AC97_REC_SEL);
-       pcm = ac97_read(w->codec, AC97_PCM);
-       aux = ac97_read(w->codec, AC97_AUX);
-
-       if (event & SND_SOC_DAPM_PRE_REG)
-               return 0;
-       if ((l & 0x1) || (r & 0x1))
-               ac97_write(w->codec, AC97_PC_BEEP, beep & 0x7fff);
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+       struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val = ucontrol->value.enumerated.item[0];
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int mixer, mask, shift, old;
+       struct snd_soc_dapm_update update;
+       bool change;
+
+       mixer = mc->shift >> 8;
+       shift = mc->shift & 0xff;
+       mask = (1 << shift);
+
+       mutex_lock(&wm9713->lock);
+       old = wm9713->hp_mixer[mixer];
+       if (ucontrol->value.enumerated.item[0])
+               wm9713->hp_mixer[mixer] |= mask;
        else
-               ac97_write(w->codec, AC97_PC_BEEP, beep | 0x8000);
+               wm9713->hp_mixer[mixer] &= ~mask;
+
+       change = old != wm9713->hp_mixer[mixer];
+       if (change) {
+               update.kcontrol = kcontrol;
+               update.reg = wm9713_mixer_mute_regs[shift];
+               update.mask = 0x8000;
+               if ((wm9713->hp_mixer[0] & mask) ||
+                   (wm9713->hp_mixer[1] & mask))
+                       update.val = 0x0;
+               else
+                       update.val = 0x8000;
+
+               snd_soc_dapm_mixer_update_power(dapm, kcontrol, val,
+                       &update);
+       }
 
-       if ((l & 0x2) || (r & 0x2))
-               ac97_write(w->codec, AC97_MASTER_TONE, tone & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_MASTER_TONE, tone | 0x8000);
+       mutex_unlock(&wm9713->lock);
 
-       if ((l & 0x4) || (r & 0x4))
-               ac97_write(w->codec, AC97_PHONE, phone & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_PHONE, phone | 0x8000);
+       return change;
+}
 
-       if ((l & 0x8) || (r & 0x8))
-               ac97_write(w->codec, AC97_REC_SEL, rec & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_REC_SEL, rec | 0x8000);
+static int wm9713_hp_mixer_get(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dapm_context *dapm = snd_soc_dapm_kcontrol_dapm(kcontrol);
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(dapm);
+       struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int mixer, shift;
 
-       if ((l & 0x10) || (r & 0x10))
-               ac97_write(w->codec, AC97_PCM, pcm & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_PCM, pcm | 0x8000);
+       mixer = mc->shift >> 8;
+       shift = mc->shift & 0xff;
 
-       if ((l & 0x20) || (r & 0x20))
-               ac97_write(w->codec, AC97_AUX, aux & 0x7fff);
-       else
-               ac97_write(w->codec, AC97_AUX, aux | 0x8000);
+       ucontrol->value.enumerated.item[0] =
+               (wm9713->hp_mixer[mixer] >> shift) & 1;
 
        return 0;
 }
 
+#define WM9713_HP_MIXER_CTRL(xname, xmixer, xshift) { \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, \
+       .get = wm9713_hp_mixer_get, .put = wm9713_hp_mixer_put, \
+       .private_value = SOC_DOUBLE_VALUE(SND_SOC_NOPM, \
+               xshift, xmixer, 1, 0, 0) \
+}
+
 /* Left Headphone Mixers */
 static const struct snd_kcontrol_new wm9713_hpl_mixer_controls[] = {
-SOC_DAPM_SINGLE("Beep Playback Switch", HPL_MIXER, 5, 1, 0),
-SOC_DAPM_SINGLE("Voice Playback Switch", HPL_MIXER, 4, 1, 0),
-SOC_DAPM_SINGLE("Aux Playback Switch", HPL_MIXER, 3, 1, 0),
-SOC_DAPM_SINGLE("PCM Playback Switch", HPL_MIXER, 2, 1, 0),
-SOC_DAPM_SINGLE("MonoIn Playback Switch", HPL_MIXER, 1, 1, 0),
-SOC_DAPM_SINGLE("Bypass Playback Switch", HPL_MIXER, 0, 1, 0),
+WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPL_MIXER, 5),
+WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPL_MIXER, 4),
+WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPL_MIXER, 3),
+WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPL_MIXER, 2),
+WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPL_MIXER, 1),
+WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPL_MIXER, 0),
 };
 
 /* Right Headphone Mixers */
 static const struct snd_kcontrol_new wm9713_hpr_mixer_controls[] = {
-SOC_DAPM_SINGLE("Beep Playback Switch", HPR_MIXER, 5, 1, 0),
-SOC_DAPM_SINGLE("Voice Playback Switch", HPR_MIXER, 4, 1, 0),
-SOC_DAPM_SINGLE("Aux Playback Switch", HPR_MIXER, 3, 1, 0),
-SOC_DAPM_SINGLE("PCM Playback Switch", HPR_MIXER, 2, 1, 0),
-SOC_DAPM_SINGLE("MonoIn Playback Switch", HPR_MIXER, 1, 1, 0),
-SOC_DAPM_SINGLE("Bypass Playback Switch", HPR_MIXER, 0, 1, 0),
+WM9713_HP_MIXER_CTRL("Beep Playback Switch", HPR_MIXER, 5),
+WM9713_HP_MIXER_CTRL("Voice Playback Switch", HPR_MIXER, 4),
+WM9713_HP_MIXER_CTRL("Aux Playback Switch", HPR_MIXER, 3),
+WM9713_HP_MIXER_CTRL("PCM Playback Switch", HPR_MIXER, 2),
+WM9713_HP_MIXER_CTRL("MonoIn Playback Switch", HPR_MIXER, 1),
+WM9713_HP_MIXER_CTRL("Bypass Playback Switch", HPR_MIXER, 0),
 };
 
 /* headphone capture mux */
@@ -429,12 +459,10 @@ SND_SOC_DAPM_MUX("Mic A Source", SND_SOC_NOPM, 0, 0,
        &wm9713_mic_sel_mux_controls),
 SND_SOC_DAPM_MUX("Mic B Source", SND_SOC_NOPM, 0, 0,
        &wm9713_micb_sel_mux_controls),
-SND_SOC_DAPM_MIXER_E("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
-       &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls),
-       mixer_event, SND_SOC_DAPM_POST_REG),
-SND_SOC_DAPM_MIXER_E("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
-       &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls),
-       mixer_event, SND_SOC_DAPM_POST_REG),
+SND_SOC_DAPM_MIXER("Left HP Mixer", AC97_EXTENDED_MID, 3, 1,
+       &wm9713_hpl_mixer_controls[0], ARRAY_SIZE(wm9713_hpl_mixer_controls)),
+SND_SOC_DAPM_MIXER("Right HP Mixer", AC97_EXTENDED_MID, 2, 1,
+       &wm9713_hpr_mixer_controls[0], ARRAY_SIZE(wm9713_hpr_mixer_controls)),
 SND_SOC_DAPM_MIXER("Mono Mixer", AC97_EXTENDED_MID, 0, 1,
        &wm9713_mono_mixer_controls[0], ARRAY_SIZE(wm9713_mono_mixer_controls)),
 SND_SOC_DAPM_MIXER("Speaker Mixer", AC97_EXTENDED_MID, 1, 1,
@@ -647,12 +675,13 @@ static const struct snd_soc_dapm_route wm9713_audio_map[] = {
 static unsigned int ac97_read(struct snd_soc_codec *codec,
        unsigned int reg)
 {
+       struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
        u16 *cache = codec->reg_cache;
 
        if (reg == AC97_RESET || reg == AC97_GPIO_STATUS ||
                reg == AC97_VENDOR_ID1 || reg == AC97_VENDOR_ID2 ||
                reg == AC97_CD)
-               return soc_ac97_ops->read(codec->ac97, reg);
+               return soc_ac97_ops->read(wm9713->ac97, reg);
        else {
                reg = reg >> 1;
 
@@ -666,9 +695,10 @@ static unsigned int ac97_read(struct snd_soc_codec *codec,
 static int ac97_write(struct snd_soc_codec *codec, unsigned int reg,
        unsigned int val)
 {
+       struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+
        u16 *cache = codec->reg_cache;
-       if (reg < 0x7c)
-               soc_ac97_ops->write(codec->ac97, reg, val);
+       soc_ac97_ops->write(wm9713->ac97, reg, val);
        reg = reg >> 1;
        if (reg < (ARRAY_SIZE(wm9713_reg)))
                cache[reg] = val;
@@ -689,7 +719,8 @@ struct _pll_div {
  * to allow rounding later */
 #define FIXED_PLL_SIZE ((1 << 22) * 10)
 
-static void pll_factors(struct _pll_div *pll_div, unsigned int source)
+static void pll_factors(struct snd_soc_codec *codec,
+       struct _pll_div *pll_div, unsigned int source)
 {
        u64 Kpart;
        unsigned int K, Ndiv, Nmod, target;
@@ -724,7 +755,7 @@ static void pll_factors(struct _pll_div *pll_div, unsigned int source)
 
        Ndiv = target / source;
        if ((Ndiv < 5) || (Ndiv > 12))
-               printk(KERN_WARNING
+               dev_warn(codec->dev,
                        "WM9713 PLL N value %u out of recommended range!\n",
                        Ndiv);
 
@@ -768,7 +799,7 @@ static int wm9713_set_pll(struct snd_soc_codec *codec,
                return 0;
        }
 
-       pll_factors(&pll_div, freq_in);
+       pll_factors(codec, &pll_div, freq_in);
 
        if (pll_div.k == 0) {
                reg = (pll_div.n << 12) | (pll_div.lf << 11) |
@@ -1049,7 +1080,6 @@ static const struct snd_soc_dai_ops wm9713_dai_ops_voice = {
 static struct snd_soc_dai_driver wm9713_dai[] = {
 {
        .name = "wm9713-hifi",
-       .ac97_control = 1,
        .playback = {
                .stream_name = "HiFi Playback",
                .channels_min = 1,
@@ -1095,17 +1125,22 @@ static struct snd_soc_dai_driver wm9713_dai[] = {
 
 int wm9713_reset(struct snd_soc_codec *codec, int try_warm)
 {
+       struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
+
        if (try_warm && soc_ac97_ops->warm_reset) {
-               soc_ac97_ops->warm_reset(codec->ac97);
+               soc_ac97_ops->warm_reset(wm9713->ac97);
                if (ac97_read(codec, 0) == wm9713_reg[0])
                        return 1;
        }
 
-       soc_ac97_ops->reset(codec->ac97);
+       soc_ac97_ops->reset(wm9713->ac97);
        if (soc_ac97_ops->warm_reset)
-               soc_ac97_ops->warm_reset(codec->ac97);
-       if (ac97_read(codec, 0) != wm9713_reg[0])
+               soc_ac97_ops->warm_reset(wm9713->ac97);
+       if (ac97_read(codec, 0) != wm9713_reg[0]) {
+               dev_err(codec->dev, "Failed to reset: AC97 link error\n");
                return -EIO;
+       }
+
        return 0;
 }
 EXPORT_SYMBOL_GPL(wm9713_reset);
@@ -1163,10 +1198,8 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
        u16 *cache = codec->reg_cache;
 
        ret = wm9713_reset(codec, 1);
-       if (ret < 0) {
-               printk(KERN_ERR "could not reset AC97 codec\n");
+       if (ret < 0)
                return ret;
-       }
 
        wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
@@ -1180,7 +1213,7 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
                        if (i == AC97_POWERDOWN || i == AC97_EXTENDED_MID ||
                                i == AC97_EXTENDED_MSTATUS || i > 0x66)
                                continue;
-                       soc_ac97_ops->write(codec->ac97, i, cache[i>>1]);
+                       soc_ac97_ops->write(wm9713->ac97, i, cache[i>>1]);
                }
        }
 
@@ -1189,50 +1222,36 @@ static int wm9713_soc_resume(struct snd_soc_codec *codec)
 
 static int wm9713_soc_probe(struct snd_soc_codec *codec)
 {
-       struct wm9713_priv *wm9713;
+       struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
        int ret = 0, reg;
 
-       wm9713 = kzalloc(sizeof(struct wm9713_priv), GFP_KERNEL);
-       if (wm9713 == NULL)
-               return -ENOMEM;
-       snd_soc_codec_set_drvdata(codec, wm9713);
-
-       ret = snd_soc_new_ac97_codec(codec, soc_ac97_ops, 0);
-       if (ret < 0)
-               goto codec_err;
+       wm9713->ac97 = snd_soc_new_ac97_codec(codec);
+       if (IS_ERR(wm9713->ac97))
+               return PTR_ERR(wm9713->ac97);
 
        /* do a cold reset for the controller and then try
         * a warm reset followed by an optional cold reset for codec */
        wm9713_reset(codec, 0);
        ret = wm9713_reset(codec, 1);
-       if (ret < 0) {
-               printk(KERN_ERR "Failed to reset WM9713: AC97 link error\n");
+       if (ret < 0)
                goto reset_err;
-       }
-
-       wm9713_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
        /* unmute the adc - move to kcontrol */
        reg = ac97_read(codec, AC97_CD) & 0x7fff;
        ac97_write(codec, AC97_CD, reg);
 
-       snd_soc_add_codec_controls(codec, wm9713_snd_ac97_controls,
-                               ARRAY_SIZE(wm9713_snd_ac97_controls));
-
        return 0;
 
 reset_err:
-       snd_soc_free_ac97_codec(codec);
-codec_err:
-       kfree(wm9713);
+       snd_soc_free_ac97_codec(wm9713->ac97);
        return ret;
 }
 
 static int wm9713_soc_remove(struct snd_soc_codec *codec)
 {
        struct wm9713_priv *wm9713 = snd_soc_codec_get_drvdata(codec);
-       snd_soc_free_ac97_codec(codec);
-       kfree(wm9713);
+
+       snd_soc_free_ac97_codec(wm9713->ac97);
        return 0;
 }
 
@@ -1248,6 +1267,9 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
        .reg_word_size = sizeof(u16),
        .reg_cache_step = 2,
        .reg_cache_default = wm9713_reg,
+
+       .controls = wm9713_snd_ac97_controls,
+       .num_controls = ARRAY_SIZE(wm9713_snd_ac97_controls),
        .dapm_widgets = wm9713_dapm_widgets,
        .num_dapm_widgets = ARRAY_SIZE(wm9713_dapm_widgets),
        .dapm_routes = wm9713_audio_map,
@@ -1256,6 +1278,16 @@ static struct snd_soc_codec_driver soc_codec_dev_wm9713 = {
 
 static int wm9713_probe(struct platform_device *pdev)
 {
+       struct wm9713_priv *wm9713;
+
+       wm9713 = devm_kzalloc(&pdev->dev, sizeof(*wm9713), GFP_KERNEL);
+       if (wm9713 == NULL)
+               return -ENOMEM;
+
+       mutex_init(&wm9713->lock);
+
+       platform_set_drvdata(pdev, wm9713);
+
        return snd_soc_register_codec(&pdev->dev,
                        &soc_codec_dev_wm9713, wm9713_dai, ARRAY_SIZE(wm9713_dai));
 }
index 6712478..720d6e8 100644 (file)
@@ -21,6 +21,7 @@
 #include <linux/regmap.h>
 #include <linux/regulator/consumer.h>
 #include <linux/slab.h>
+#include <linux/vmalloc.h>
 #include <linux/workqueue.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -169,11 +170,12 @@ static struct wm_adsp_buf *wm_adsp_buf_alloc(const void *src, size_t len,
        if (buf == NULL)
                return NULL;
 
-       buf->buf = kmemdup(src, len, GFP_KERNEL | GFP_DMA);
+       buf->buf = vmalloc(len);
        if (!buf->buf) {
-               kfree(buf);
+               vfree(buf);
                return NULL;
        }
+       memcpy(buf->buf, src, len);
 
        if (list)
                list_add_tail(&buf->list, list);
@@ -188,7 +190,7 @@ static void wm_adsp_buf_free(struct list_head *list)
                                                           struct wm_adsp_buf,
                                                           list);
                list_del(&buf->list);
-               kfree(buf->buf);
+               vfree(buf->buf);
                kfree(buf);
        }
 }
@@ -684,38 +686,24 @@ static int wm_adsp_load(struct wm_adsp *dsp)
                }
 
                if (reg) {
-                       size_t to_write = PAGE_SIZE;
-                       size_t remain = le32_to_cpu(region->len);
-                       const u8 *data = region->data;
-
-                       while (remain > 0) {
-                               if (remain < PAGE_SIZE)
-                                       to_write = remain;
-
-                               buf = wm_adsp_buf_alloc(data,
-                                                       to_write,
-                                                       &buf_list);
-                               if (!buf) {
-                                       adsp_err(dsp, "Out of memory\n");
-                                       ret = -ENOMEM;
-                                       goto out_fw;
-                               }
-
-                               ret = regmap_raw_write_async(regmap, reg,
-                                                            buf->buf,
-                                                            to_write);
-                               if (ret != 0) {
-                                       adsp_err(dsp,
-                                               "%s.%d: Failed to write %zd bytes at %d in %s: %d\n",
-                                               file, regions,
-                                               to_write, offset,
-                                               region_name, ret);
-                                       goto out_fw;
-                               }
+                       buf = wm_adsp_buf_alloc(region->data,
+                                               le32_to_cpu(region->len),
+                                               &buf_list);
+                       if (!buf) {
+                               adsp_err(dsp, "Out of memory\n");
+                               ret = -ENOMEM;
+                               goto out_fw;
+                       }
 
-                               data += to_write;
-                               reg += to_write / 2;
-                               remain -= to_write;
+                       ret = regmap_raw_write_async(regmap, reg, buf->buf,
+                                                    le32_to_cpu(region->len));
+                       if (ret != 0) {
+                               adsp_err(dsp,
+                                       "%s.%d: Failed to write %d bytes at %d in %s: %d\n",
+                                       file, regions,
+                                       le32_to_cpu(region->len), offset,
+                                       region_name, ret);
+                               goto out_fw;
                        }
                }
 
@@ -1065,8 +1053,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                                  be32_to_cpu(adsp1_alg[i].zm));
 
                        region = kzalloc(sizeof(*region), GFP_KERNEL);
-                       if (!region)
-                               return -ENOMEM;
+                       if (!region) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        region->type = WMFW_ADSP1_DM;
                        region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
                        region->base = be32_to_cpu(adsp1_alg[i].dm);
@@ -1083,8 +1073,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                        }
 
                        region = kzalloc(sizeof(*region), GFP_KERNEL);
-                       if (!region)
-                               return -ENOMEM;
+                       if (!region) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        region->type = WMFW_ADSP1_ZM;
                        region->alg = be32_to_cpu(adsp1_alg[i].alg.id);
                        region->base = be32_to_cpu(adsp1_alg[i].zm);
@@ -1113,8 +1105,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                                  be32_to_cpu(adsp2_alg[i].zm));
 
                        region = kzalloc(sizeof(*region), GFP_KERNEL);
-                       if (!region)
-                               return -ENOMEM;
+                       if (!region) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        region->type = WMFW_ADSP2_XM;
                        region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
                        region->base = be32_to_cpu(adsp2_alg[i].xm);
@@ -1131,8 +1125,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                        }
 
                        region = kzalloc(sizeof(*region), GFP_KERNEL);
-                       if (!region)
-                               return -ENOMEM;
+                       if (!region) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        region->type = WMFW_ADSP2_YM;
                        region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
                        region->base = be32_to_cpu(adsp2_alg[i].ym);
@@ -1149,8 +1145,10 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp)
                        }
 
                        region = kzalloc(sizeof(*region), GFP_KERNEL);
-                       if (!region)
-                               return -ENOMEM;
+                       if (!region) {
+                               ret = -ENOMEM;
+                               goto out;
+                       }
                        region->type = WMFW_ADSP2_ZM;
                        region->alg = be32_to_cpu(adsp2_alg[i].alg.id);
                        region->base = be32_to_cpu(adsp2_alg[i].zm);
@@ -1595,13 +1593,6 @@ static void wm_adsp2_boot_work(struct work_struct *work)
        if (ret != 0)
                goto err;
 
-       ret = regmap_update_bits_async(dsp->regmap,
-                                      dsp->base + ADSP2_CONTROL,
-                                      ADSP2_CORE_ENA,
-                                      ADSP2_CORE_ENA);
-       if (ret != 0)
-               goto err;
-
        dsp->running = true;
 
        return;
@@ -1651,8 +1642,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
 
                ret = regmap_update_bits(dsp->regmap,
                                         dsp->base + ADSP2_CONTROL,
-                                        ADSP2_START,
-                                        ADSP2_START);
+                                        ADSP2_CORE_ENA | ADSP2_START,
+                                        ADSP2_CORE_ENA | ADSP2_START);
                if (ret != 0)
                        goto err;
                break;
index 0eed9b1..0dab382 100644 (file)
@@ -70,6 +70,7 @@ struct davinci_mcasp {
        void __iomem *base;
        u32 fifo_base;
        struct device *dev;
+       struct snd_pcm_substream *substreams[2];
 
        /* McASP specific data */
        int     tdm_slots;
@@ -80,6 +81,7 @@ struct davinci_mcasp {
        u8      bclk_div;
        u16     bclk_lrclk_ratio;
        int     streams;
+       u32     irq_request[2];
 
        int     sysclk_freq;
        bool    bclk_master;
@@ -90,6 +92,9 @@ struct davinci_mcasp {
 
        bool    dat_port;
 
+       /* Used for comstraint setting on the second stream */
+       u32     channels;
+
 #ifdef CONFIG_PM_SLEEP
        struct davinci_mcasp_context context;
 #endif
@@ -154,9 +159,16 @@ static bool mcasp_is_synchronous(struct davinci_mcasp *mcasp)
 
 static void mcasp_start_rx(struct davinci_mcasp *mcasp)
 {
+       if (mcasp->rxnumevt) {  /* enable FIFO */
+               u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+
+               mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+               mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+       }
+
+       /* Start clocks */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXHCLKRST);
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXCLKRST);
-
        /*
         * When ASYNC == 0 the transmit and receive sections operate
         * synchronously from the transmit clock and frame sync. We need to make
@@ -167,74 +179,69 @@ static void mcasp_start_rx(struct davinci_mcasp *mcasp)
                mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
        }
 
+       /* Activate serializer(s) */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSERCLR);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
-
-       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
-       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_RXBUF_REG, 0);
-
+       /* Release RX state machine */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXSMRST);
+       /* Release Frame Sync generator */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, RXFSRST);
-
        if (mcasp_is_synchronous(mcasp))
                mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+
+       /* enable receive IRQs */
+       mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG,
+                      mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]);
 }
 
 static void mcasp_start_tx(struct davinci_mcasp *mcasp)
 {
-       u8 offset = 0, i;
        u32 cnt;
 
+       if (mcasp->txnumevt) {  /* enable FIFO */
+               u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+               mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+               mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
+       }
+
+       /* Start clocks */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXHCLKRST);
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXCLKRST);
+       /* Activate serializer(s) */
        mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSERCLR);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
 
-       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
-       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
-       for (i = 0; i < mcasp->num_serializer; i++) {
-               if (mcasp->serial_dir[i] == TX_MODE) {
-                       offset = i;
-                       break;
-               }
-       }
-
-       /* wait for TX ready */
+       /* wait for XDATA to be cleared */
        cnt = 0;
-       while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_XRSRCTL_REG(offset)) &
-                TXSTATE) && (cnt < 100000))
+       while (!(mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG) &
+                ~XRDATA) && (cnt < 100000))
                cnt++;
 
-       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXBUF_REG, 0);
+       /* Release TX state machine */
+       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXSMRST);
+       /* Release Frame Sync generator */
+       mcasp_set_ctl_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, TXFSRST);
+
+       /* enable transmit IRQs */
+       mcasp_set_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG,
+                      mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]);
 }
 
 static void davinci_mcasp_start(struct davinci_mcasp *mcasp, int stream)
 {
-       u32 reg;
-
        mcasp->streams++;
 
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               if (mcasp->txnumevt) {  /* enable FIFO */
-                       reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
-                       mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-                       mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
-               }
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
                mcasp_start_tx(mcasp);
-       } else {
-               if (mcasp->rxnumevt) {  /* enable FIFO */
-                       reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
-                       mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-                       mcasp_set_bits(mcasp, reg, FIFO_ENABLE);
-               }
+       else
                mcasp_start_rx(mcasp);
-       }
 }
 
 static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
 {
+       /* disable IRQ sources */
+       mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLR_REG,
+                      mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE]);
+
        /*
         * In synchronous mode stop the TX clocks if no other stream is
         * running
@@ -244,12 +251,22 @@ static void mcasp_stop_rx(struct davinci_mcasp *mcasp)
 
        mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLR_REG, 0);
        mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, 0xFFFFFFFF);
+
+       if (mcasp->rxnumevt) {  /* disable FIFO */
+               u32 reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
+
+               mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+       }
 }
 
 static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
 {
        u32 val = 0;
 
+       /* disable IRQ sources */
+       mcasp_clr_bits(mcasp, DAVINCI_MCASP_EVTCTLX_REG,
+                      mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK]);
+
        /*
         * In synchronous mode keep TX clocks running if the capture stream is
         * still running.
@@ -259,27 +276,92 @@ static void mcasp_stop_tx(struct davinci_mcasp *mcasp)
 
        mcasp_set_reg(mcasp, DAVINCI_MCASP_GBLCTLX_REG, val);
        mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, 0xFFFFFFFF);
+
+       if (mcasp->txnumevt) {  /* disable FIFO */
+               u32 reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
+
+               mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
+       }
 }
 
 static void davinci_mcasp_stop(struct davinci_mcasp *mcasp, int stream)
 {
-       u32 reg;
-
        mcasp->streams--;
 
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               if (mcasp->txnumevt) {  /* disable FIFO */
-                       reg = mcasp->fifo_base + MCASP_WFIFOCTL_OFFSET;
-                       mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-               }
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
                mcasp_stop_tx(mcasp);
-       } else {
-               if (mcasp->rxnumevt) {  /* disable FIFO */
-                       reg = mcasp->fifo_base + MCASP_RFIFOCTL_OFFSET;
-                       mcasp_clr_bits(mcasp, reg, FIFO_ENABLE);
-               }
+       else
                mcasp_stop_rx(mcasp);
+}
+
+static irqreturn_t davinci_mcasp_tx_irq_handler(int irq, void *data)
+{
+       struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
+       struct snd_pcm_substream *substream;
+       u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK];
+       u32 handled_mask = 0;
+       u32 stat;
+
+       stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG);
+       if (stat & XUNDRN & irq_mask) {
+               dev_warn(mcasp->dev, "Transmit buffer underflow\n");
+               handled_mask |= XUNDRN;
+
+               substream = mcasp->substreams[SNDRV_PCM_STREAM_PLAYBACK];
+               if (substream) {
+                       snd_pcm_stream_lock_irq(substream);
+                       if (snd_pcm_running(substream))
+                               snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+                       snd_pcm_stream_unlock_irq(substream);
+               }
        }
+
+       if (!handled_mask)
+               dev_warn(mcasp->dev, "unhandled tx event. txstat: 0x%08x\n",
+                        stat);
+
+       if (stat & XRERR)
+               handled_mask |= XRERR;
+
+       /* Ack the handled event only */
+       mcasp_set_reg(mcasp, DAVINCI_MCASP_TXSTAT_REG, handled_mask);
+
+       return IRQ_RETVAL(handled_mask);
+}
+
+static irqreturn_t davinci_mcasp_rx_irq_handler(int irq, void *data)
+{
+       struct davinci_mcasp *mcasp = (struct davinci_mcasp *)data;
+       struct snd_pcm_substream *substream;
+       u32 irq_mask = mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE];
+       u32 handled_mask = 0;
+       u32 stat;
+
+       stat = mcasp_get_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG);
+       if (stat & ROVRN & irq_mask) {
+               dev_warn(mcasp->dev, "Receive buffer overflow\n");
+               handled_mask |= ROVRN;
+
+               substream = mcasp->substreams[SNDRV_PCM_STREAM_CAPTURE];
+               if (substream) {
+                       snd_pcm_stream_lock_irq(substream);
+                       if (snd_pcm_running(substream))
+                               snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
+                       snd_pcm_stream_unlock_irq(substream);
+               }
+       }
+
+       if (!handled_mask)
+               dev_warn(mcasp->dev, "unhandled rx event. rxstat: 0x%08x\n",
+                        stat);
+
+       if (stat & XRERR)
+               handled_mask |= XRERR;
+
+       /* Ack the handled event only */
+       mcasp_set_reg(mcasp, DAVINCI_MCASP_RXSTAT_REG, handled_mask);
+
+       return IRQ_RETVAL(handled_mask);
 }
 
 static int davinci_mcasp_set_dai_fmt(struct snd_soc_dai *cpu_dai,
@@ -500,8 +582,17 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
         * both left and right channels), so it has to be divided by number of
         * tdm-slots (for I2S - divided by 2).
         */
-       if (mcasp->bclk_lrclk_ratio)
-               word_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
+       if (mcasp->bclk_lrclk_ratio) {
+               u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
+
+               /*
+                * When we have more bclk then it is needed for the data, we
+                * need to use the rotation to move the received samples to have
+                * correct alignment.
+                */
+               rx_rotate = (slot_length - word_length) / 4;
+               word_length = slot_length;
+       }
 
        /* mapping of the XSSZ bit-field as described in the datasheet */
        fmt = (word_length >> 1) - 1;
@@ -635,19 +726,29 @@ static int mcasp_common_hw_param(struct davinci_mcasp *mcasp, int stream,
        return 0;
 }
 
-static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream)
+static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
+                             int channels)
 {
        int i, active_slots;
+       int total_slots;
+       int active_serializers;
        u32 mask = 0;
        u32 busel = 0;
 
-       if ((mcasp->tdm_slots < 2) || (mcasp->tdm_slots > 32)) {
-               dev_err(mcasp->dev, "tdm slot %d not supported\n",
-                       mcasp->tdm_slots);
-               return -EINVAL;
-       }
+       total_slots = mcasp->tdm_slots;
+
+       /*
+        * If more than one serializer is needed, then use them with
+        * their specified tdm_slots count. Otherwise, one serializer
+        * can cope with the transaction using as many slots as channels
+        * in the stream, requires channels symmetry
+        */
+       active_serializers = (channels + total_slots - 1) / total_slots;
+       if (active_serializers == 1)
+               active_slots = channels;
+       else
+               active_slots = total_slots;
 
-       active_slots = (mcasp->tdm_slots > 31) ? 32 : mcasp->tdm_slots;
        for (i = 0; i < active_slots; i++)
                mask |= (1 << i);
 
@@ -659,12 +760,12 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream)
        mcasp_set_reg(mcasp, DAVINCI_MCASP_TXTDM_REG, mask);
        mcasp_set_bits(mcasp, DAVINCI_MCASP_TXFMT_REG, busel | TXORD);
        mcasp_mod_bits(mcasp, DAVINCI_MCASP_TXFMCTL_REG,
-                      FSXMOD(mcasp->tdm_slots), FSXMOD(0x1FF));
+                      FSXMOD(total_slots), FSXMOD(0x1FF));
 
        mcasp_set_reg(mcasp, DAVINCI_MCASP_RXTDM_REG, mask);
        mcasp_set_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, busel | RXORD);
        mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMCTL_REG,
-                      FSRMOD(mcasp->tdm_slots), FSRMOD(0x1FF));
+                      FSRMOD(total_slots), FSRMOD(0x1FF));
 
        return 0;
 }
@@ -778,7 +879,8 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
        if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
                ret = mcasp_dit_hw_param(mcasp, params_rate(params));
        else
-               ret = mcasp_i2s_hw_param(mcasp, substream->stream);
+               ret = mcasp_i2s_hw_param(mcasp, substream->stream,
+                                        channels);
 
        if (ret)
                return ret;
@@ -826,6 +928,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
 
        davinci_config_channel_size(mcasp, word_length);
 
+       if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE)
+               mcasp->channels = channels;
+
        return 0;
 }
 
@@ -854,7 +959,65 @@ static int davinci_mcasp_trigger(struct snd_pcm_substream *substream,
        return ret;
 }
 
+static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *cpu_dai)
+{
+       struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+       u32 max_channels = 0;
+       int i, dir;
+
+       mcasp->substreams[substream->stream] = substream;
+
+       if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+               return 0;
+
+       /*
+        * Limit the maximum allowed channels for the first stream:
+        * number of serializers for the direction * tdm slots per serializer
+        */
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               dir = TX_MODE;
+       else
+               dir = RX_MODE;
+
+       for (i = 0; i < mcasp->num_serializer; i++) {
+               if (mcasp->serial_dir[i] == dir)
+                       max_channels++;
+       }
+       max_channels *= mcasp->tdm_slots;
+       /*
+        * If the already active stream has less channels than the calculated
+        * limnit based on the seirializers * tdm_slots, we need to use that as
+        * a constraint for the second stream.
+        * Otherwise (first stream or less allowed channels) we use the
+        * calculated constraint.
+        */
+       if (mcasp->channels && mcasp->channels < max_channels)
+               max_channels = mcasp->channels;
+
+       snd_pcm_hw_constraint_minmax(substream->runtime,
+                                    SNDRV_PCM_HW_PARAM_CHANNELS,
+                                    2, max_channels);
+       return 0;
+}
+
+static void davinci_mcasp_shutdown(struct snd_pcm_substream *substream,
+                                  struct snd_soc_dai *cpu_dai)
+{
+       struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(cpu_dai);
+
+       mcasp->substreams[substream->stream] = NULL;
+
+       if (mcasp->op_mode == DAVINCI_MCASP_DIT_MODE)
+               return;
+
+       if (!cpu_dai->active)
+               mcasp->channels = 0;
+}
+
 static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
+       .startup        = davinci_mcasp_startup,
+       .shutdown       = davinci_mcasp_shutdown,
        .trigger        = davinci_mcasp_trigger,
        .hw_params      = davinci_mcasp_hw_params,
        .set_fmt        = davinci_mcasp_set_dai_fmt,
@@ -971,6 +1134,7 @@ static struct snd_soc_dai_driver davinci_mcasp_dai[] = {
                },
                .ops            = &davinci_mcasp_dai_ops,
 
+               .symmetric_samplebits   = 1,
        },
        {
                .name           = "davinci-mcasp.1",
@@ -1194,6 +1358,8 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        struct resource *mem, *ioarea, *res, *dat;
        struct davinci_mcasp_pdata *pdata;
        struct davinci_mcasp *mcasp;
+       char *irq_name;
+       int irq;
        int ret;
 
        if (!pdev->dev.platform_data && !pdev->dev.of_node) {
@@ -1235,6 +1401,7 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        ret = pm_runtime_get_sync(&pdev->dev);
        if (IS_ERR_VALUE(ret)) {
                dev_err(&pdev->dev, "pm_runtime_get_sync() failed\n");
+               pm_runtime_disable(&pdev->dev);
                return ret;
        }
 
@@ -1246,7 +1413,21 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
        }
 
        mcasp->op_mode = pdata->op_mode;
-       mcasp->tdm_slots = pdata->tdm_slots;
+       /* sanity check for tdm slots parameter */
+       if (mcasp->op_mode == DAVINCI_MCASP_IIS_MODE) {
+               if (pdata->tdm_slots < 2) {
+                       dev_err(&pdev->dev, "invalid tdm slots: %d\n",
+                               pdata->tdm_slots);
+                       mcasp->tdm_slots = 2;
+               } else if (pdata->tdm_slots > 32) {
+                       dev_err(&pdev->dev, "invalid tdm slots: %d\n",
+                               pdata->tdm_slots);
+                       mcasp->tdm_slots = 32;
+               } else {
+                       mcasp->tdm_slots = pdata->tdm_slots;
+               }
+       }
+
        mcasp->num_serializer = pdata->num_serializer;
 #ifdef CONFIG_PM_SLEEP
        mcasp->context.xrsr_regs = devm_kzalloc(&pdev->dev,
@@ -1260,6 +1441,36 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
 
        mcasp->dev = &pdev->dev;
 
+       irq = platform_get_irq_byname(pdev, "rx");
+       if (irq >= 0) {
+               irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_rx\n",
+                                         dev_name(&pdev->dev));
+               ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+                                               davinci_mcasp_rx_irq_handler,
+                                               IRQF_ONESHOT, irq_name, mcasp);
+               if (ret) {
+                       dev_err(&pdev->dev, "RX IRQ request failed\n");
+                       goto err;
+               }
+
+               mcasp->irq_request[SNDRV_PCM_STREAM_CAPTURE] = ROVRN;
+       }
+
+       irq = platform_get_irq_byname(pdev, "tx");
+       if (irq >= 0) {
+               irq_name = devm_kasprintf(&pdev->dev, GFP_KERNEL, "%s_tx\n",
+                                         dev_name(&pdev->dev));
+               ret = devm_request_threaded_irq(&pdev->dev, irq, NULL,
+                                               davinci_mcasp_tx_irq_handler,
+                                               IRQF_ONESHOT, irq_name, mcasp);
+               if (ret) {
+                       dev_err(&pdev->dev, "TX IRQ request failed\n");
+                       goto err;
+               }
+
+               mcasp->irq_request[SNDRV_PCM_STREAM_PLAYBACK] = XUNDRN;
+       }
+
        dat = platform_get_resource_byname(pdev, IORESOURCE_MEM, "dat");
        if (dat)
                mcasp->dat_port = true;
index 98fbc45..79dc511 100644 (file)
 #define TXSMRST                BIT(11) /* Transmitter State Machine Reset */
 #define TXFSRST                BIT(12) /* Frame Sync Generator Reset */
 
+/*
+ * DAVINCI_MCASP_TXSTAT_REG - Transmitter Status Register Bits
+ * DAVINCI_MCASP_RXSTAT_REG - Receiver Status Register Bits
+ */
+#define XRERR          BIT(8) /* Transmit/Receive error */
+#define XRDATA         BIT(5) /* Transmit/Receive data ready */
+
 /*
  * DAVINCI_MCASP_AMUTE_REG -  Mute Control Register Bits
  */
  */
 #define TXDATADMADIS   BIT(0)
 
+/*
+ * DAVINCI_MCASP_EVTCTLR_REG - Receiver Interrupt Control Register Bits
+ */
+#define ROVRN          BIT(0)
+
+/*
+ * DAVINCI_MCASP_EVTCTLX_REG - Transmitter Interrupt Control Register Bits
+ */
+#define XUNDRN         BIT(0)
+
 /*
  * DAVINCI_MCASP_W[R]FIFOCTL - Write/Read FIFO Control Register bits
  */
index e961388..08f0229 100644 (file)
@@ -338,31 +338,34 @@ static int dw_i2s_probe(struct platform_device *pdev)
                return -EINVAL;
        }
 
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               dev_err(&pdev->dev, "no i2s resource defined\n");
-               return -ENODEV;
-       }
-
-       if (!devm_request_mem_region(&pdev->dev, res->start,
-                               resource_size(res), pdev->name)) {
-               dev_err(&pdev->dev, "i2s region already claimed\n");
-               return -EBUSY;
-       }
-
        dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
        if (!dev) {
                dev_warn(&pdev->dev, "kzalloc fail\n");
                return -ENOMEM;
        }
 
-       dev->i2s_base = devm_ioremap(&pdev->dev, res->start,
-                       resource_size(res));
-       if (!dev->i2s_base) {
-               dev_err(&pdev->dev, "ioremap fail for i2s_region\n");
+       dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
+       if (!dw_i2s_dai) {
+               dev_err(&pdev->dev, "mem allocation failed for dai driver\n");
                return -ENOMEM;
        }
 
+       dw_i2s_dai->ops = &dw_i2s_dai_ops;
+       dw_i2s_dai->suspend = dw_i2s_suspend;
+       dw_i2s_dai->resume = dw_i2s_resume;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(&pdev->dev, "no i2s resource defined\n");
+               return -ENODEV;
+       }
+
+       dev->i2s_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(dev->i2s_base)) {
+               dev_err(&pdev->dev, "ioremap fail for i2s_region\n");
+               return PTR_ERR(dev->i2s_base);
+       }
+
        cap = pdata->cap;
        dev->capability = cap;
        dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
@@ -388,13 +391,6 @@ static int dw_i2s_probe(struct platform_device *pdev)
        if (ret < 0)
                goto err_clk_put;
 
-       dw_i2s_dai = devm_kzalloc(&pdev->dev, sizeof(*dw_i2s_dai), GFP_KERNEL);
-       if (!dw_i2s_dai) {
-               dev_err(&pdev->dev, "mem allocation failed for dai driver\n");
-               ret = -ENOMEM;
-               goto err_clk_disable;
-       }
-
        if (cap & DWC_I2S_PLAY) {
                dev_dbg(&pdev->dev, " designware: play supported\n");
                dw_i2s_dai->playback.channels_min = MIN_CHANNEL_NUM;
@@ -411,10 +407,6 @@ static int dw_i2s_probe(struct platform_device *pdev)
                dw_i2s_dai->capture.rates = pdata->snd_rates;
        }
 
-       dw_i2s_dai->ops = &dw_i2s_dai_ops;
-       dw_i2s_dai->suspend = dw_i2s_suspend;
-       dw_i2s_dai->resume = dw_i2s_resume;
-
        dev->dev = &pdev->dev;
        dev_set_drvdata(&pdev->dev, dev);
        ret = snd_soc_register_component(&pdev->dev, &dw_i2s_component,
index eb093d5..b175b01 100644 (file)
@@ -105,7 +105,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
        int ret;
        int int_port = 0, ext_port;
        struct device_node *np = pdev->dev.of_node;
-       struct device_node *ssi_np, *codec_np;
+       struct device_node *ssi_np = NULL, *codec_np = NULL;
 
        eukrea_tlv320.dev = &pdev->dev;
        if (np) {
@@ -217,8 +217,7 @@ static int eukrea_tlv320_probe(struct platform_device *pdev)
 err:
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n", ret);
-       if (np)
-               of_node_put(ssi_np);
+       of_node_put(ssi_np);
 
        return ret;
 }
index 007c772..3f6959c 100644 (file)
@@ -51,6 +51,7 @@ struct codec_priv {
  * @sysclk_freq[2]: SYSCLK rates for set_sysclk()
  * @sysclk_dir[2]: SYSCLK directions for set_sysclk()
  * @sysclk_id[2]: SYSCLK ids for set_sysclk()
+ * @slot_width: Slot width of each frame
  *
  * Note: [1] for tx and [0] for rx
  */
@@ -58,6 +59,7 @@ struct cpu_priv {
        unsigned long sysclk_freq[2];
        u32 sysclk_dir[2];
        u32 sysclk_id[2];
+       u32 slot_width;
 };
 
 /**
@@ -125,7 +127,12 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
        priv->sample_rate = params_rate(params);
        priv->sample_format = params_format(params);
 
-       if (priv->card.set_bias_level)
+       /*
+        * If codec-dai is DAI Master and all configurations are already in the
+        * set_bias_level(), bypass the remaining settings in hw_params().
+        * Note: (dai_fmt & CBM_CFM) includes CBM_CFM and CBM_CFS.
+        */
+       if (priv->card.set_bias_level && priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM)
                return 0;
 
        /* Specific configurations of DAIs starts from here */
@@ -137,6 +144,15 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
                return ret;
        }
 
+       if (cpu_priv->slot_width) {
+               ret = snd_soc_dai_set_tdm_slot(rtd->cpu_dai, 0x3, 0x3, 2,
+                                              cpu_priv->slot_width);
+               if (ret) {
+                       dev_err(dev, "failed to set TDM slot for cpu dai\n");
+                       return ret;
+               }
+       }
+
        return 0;
 }
 
@@ -448,6 +464,7 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
                priv->cpu_priv.sysclk_freq[RX] = priv->codec_priv.mclk_freq;
                priv->cpu_priv.sysclk_dir[TX] = SND_SOC_CLOCK_OUT;
                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-sgtl5000")) {
                priv->codec_priv.mclk_id = SGTL5000_SYSCLK;
index a645e29..ca319d5 100644 (file)
@@ -513,10 +513,15 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
        u32 width = snd_pcm_format_width(params_format(params));
        u32 channels = params_channels(params);
        u32 pins = DIV_ROUND_UP(channels, esai_priv->slots);
+       u32 slot_width = width;
        u32 bclk, mask, val;
        int ret;
 
-       bclk = params_rate(params) * esai_priv->slot_width * esai_priv->slots;
+       /* Override slot_width if being specifially set */
+       if (esai_priv->slot_width)
+               slot_width = esai_priv->slot_width;
+
+       bclk = params_rate(params) * slot_width * esai_priv->slots;
 
        ret = fsl_esai_set_bclk(dai, tx, bclk);
        if (ret)
@@ -538,7 +543,7 @@ static int fsl_esai_hw_params(struct snd_pcm_substream *substream,
        regmap_update_bits(esai_priv->regmap, REG_ESAI_xFCR(tx), mask, val);
 
        mask = ESAI_xCR_xSWS_MASK | (tx ? ESAI_xCR_PADC : 0);
-       val = ESAI_xCR_xSWS(esai_priv->slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
+       val = ESAI_xCR_xSWS(slot_width, width) | (tx ? ESAI_xCR_PADC : 0);
 
        regmap_update_bits(esai_priv->regmap, REG_ESAI_xCR(tx), mask, val);
 
@@ -780,9 +785,6 @@ static int fsl_esai_probe(struct platform_device *pdev)
                return ret;
        }
 
-       /* Set a default slot size */
-       esai_priv->slot_width = 32;
-
        /* Set a default slot number */
        esai_priv->slots = 2;
 
index e695517..b6b0d25 100644 (file)
@@ -67,8 +67,6 @@
 /**
  * FSLSSI_I2S_FORMATS: audio formats supported by the SSI
  *
- * This driver currently only supports the SSI running in I2S slave mode.
- *
  * The SSI has a limitation in that the samples must be in the same byte
  * order as the host CPU.  This is because when multiple bytes are written
  * to the STX register, the bytes and bits must be written in the same
@@ -1099,7 +1097,7 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
 };
 
 static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
-       .ac97_control = 1,
+       .bus_control = true,
        .playback = {
                .stream_name = "AC97 Playback",
                .channels_min = 2,
@@ -1363,7 +1361,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
                return PTR_ERR(ssi_private->regs);
        }
 
-       ssi_private->irq = irq_of_parse_and_map(np, 0);
+       ssi_private->irq = platform_get_irq(pdev, 0);
        if (!ssi_private->irq) {
                dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
                return -ENXIO;
@@ -1389,7 +1387,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        if (ssi_private->soc->imx) {
                ret = fsl_ssi_imx_probe(pdev, ssi_private, iomem);
                if (ret)
-                       goto error_irqmap;
+                       return ret;
        }
 
        ret = snd_soc_register_component(&pdev->dev, &fsl_ssi_component,
@@ -1412,7 +1410,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
 
        ret = fsl_ssi_debugfs_create(&ssi_private->dbg_stats, &pdev->dev);
        if (ret)
-               goto error_asoc_register;
+               goto error_irq;
 
        /*
         * If codec-handle property is missing from SSI node, we assume
@@ -1460,10 +1458,6 @@ error_asoc_register:
        if (ssi_private->soc->imx)
                fsl_ssi_imx_clean(pdev, ssi_private);
 
-error_irqmap:
-       if (ssi_private->use_dma)
-               irq_dispose_mapping(ssi_private->irq);
-
        return ret;
 }
 
@@ -1480,9 +1474,6 @@ static int fsl_ssi_remove(struct platform_device *pdev)
        if (ssi_private->soc->imx)
                fsl_ssi_imx_clean(pdev, ssi_private);
 
-       if (ssi_private->use_dma)
-               irq_dispose_mapping(ssi_private->irq);
-
        return 0;
 }
 
index 1cb22dd..1dab963 100644 (file)
@@ -175,10 +175,8 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
 fail:
        if (data && !IS_ERR(data->codec_clk))
                clk_put(data->codec_clk);
-       if (ssi_np)
-               of_node_put(ssi_np);
-       if (codec_np)
-               of_node_put(codec_np);
+       of_node_put(ssi_np);
+       of_node_put(codec_np);
 
        return ret;
 }
index e1dc401..0c9068e 100644 (file)
@@ -74,8 +74,7 @@ static int imx_spdif_audio_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, data);
 
 end:
-       if (spdif_np)
-               of_node_put(spdif_np);
+       of_node_put(spdif_np);
 
        return ret;
 }
index ab2fdd7..60b0a5b 100644 (file)
@@ -382,7 +382,7 @@ static struct snd_soc_dai_driver imx_ssi_dai = {
 
 static struct snd_soc_dai_driver imx_ac97_dai = {
        .probe = imx_ssi_dai_probe,
-       .ac97_control = 1,
+       .bus_control = true,
        .playback = {
                .stream_name = "AC97 Playback",
                .channels_min = 2,
index 3a3d17c..48179ff 100644 (file)
@@ -281,10 +281,8 @@ static int imx_wm8962_probe(struct platform_device *pdev)
 clk_fail:
        clk_disable_unprepare(data->codec_clk);
 fail:
-       if (ssi_np)
-               of_node_put(ssi_np);
-       if (codec_np)
-               of_node_put(codec_np);
+       of_node_put(ssi_np);
+       of_node_put(codec_np);
 
        return ret;
 }
index f2b5d75..0b82e20 100644 (file)
@@ -327,9 +327,6 @@ static int psc_dma_new(struct snd_soc_pcm_runtime *rtd)
                        goto capture_alloc_err;
        }
 
-       if (rtd->codec->ac97)
-               rtd->codec->ac97->private_data = psc_dma;
-
        return 0;
 
  capture_alloc_err:
index 24eafa2..c6ed6ba 100644 (file)
@@ -237,7 +237,7 @@ static const struct snd_soc_dai_ops psc_ac97_digital_ops = {
 static struct snd_soc_dai_driver psc_ac97_dai[] = {
 {
        .name = "mpc5200-psc-ac97.0",
-       .ac97_control = 1,
+       .bus_control = true,
        .probe  = psc_ac97_probe,
        .playback = {
                .stream_name    = "AC97 Playback",
@@ -257,7 +257,7 @@ static struct snd_soc_dai_driver psc_ac97_dai[] = {
 },
 {
        .name = "mpc5200-psc-ac97.1",
-       .ac97_control = 1,
+       .bus_control = true,
        .playback = {
                .stream_name    = "AC97 SPDIF",
                .channels_min   = 1,
@@ -282,7 +282,6 @@ static const struct snd_soc_component_driver psc_ac97_component = {
 static int psc_ac97_of_probe(struct platform_device *op)
 {
        int rc;
-       struct snd_ac97 ac97;
        struct mpc52xx_psc __iomem *regs;
 
        rc = mpc5200_audio_dma_create(op);
@@ -304,7 +303,6 @@ static int psc_ac97_of_probe(struct platform_device *op)
 
        psc_dma = dev_get_drvdata(&op->dev);
        regs = psc_dma->psc_regs;
-       ac97.private_data = psc_dma;
 
        psc_dma->imr = 0;
        out_be16(&psc_dma->psc_regs->isr_imr.imr, psc_dma->imr);
index d1b7293..ece22d5 100644 (file)
@@ -29,7 +29,9 @@ struct simple_card_data {
        } *dai_props;
        unsigned int mclk_fs;
        int gpio_hp_det;
+       int gpio_hp_det_invert;
        int gpio_mic_det;
+       int gpio_mic_det_invert;
        struct snd_soc_dai_link dai_link[];     /* dynamically allocated */
 };
 
@@ -148,6 +150,7 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
                                      simple_card_hp_jack_pins);
 
                simple_card_hp_jack_gpio.gpio = priv->gpio_hp_det;
+               simple_card_hp_jack_gpio.invert = priv->gpio_hp_det_invert;
                snd_soc_jack_add_gpios(&simple_card_hp_jack, 1,
                                       &simple_card_hp_jack_gpio);
        }
@@ -159,6 +162,7 @@ static int asoc_simple_card_dai_init(struct snd_soc_pcm_runtime *rtd)
                                      ARRAY_SIZE(simple_card_mic_jack_pins),
                                      simple_card_mic_jack_pins);
                simple_card_mic_jack_gpio.gpio = priv->gpio_mic_det;
+               simple_card_mic_jack_gpio.invert = priv->gpio_mic_det_invert;
                snd_soc_jack_add_gpios(&simple_card_mic_jack, 1,
                                       &simple_card_mic_jack_gpio);
        }
@@ -226,6 +230,52 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
        return 0;
 }
 
+static int asoc_simple_card_parse_daifmt(struct device_node *node,
+                                        struct simple_card_data *priv,
+                                        struct device_node *codec,
+                                        char *prefix, int idx)
+{
+       struct device *dev = simple_priv_to_dev(priv);
+       struct device_node *bitclkmaster = NULL;
+       struct device_node *framemaster = NULL;
+       struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
+       struct asoc_simple_dai *cpu_dai = &dai_props->cpu_dai;
+       struct asoc_simple_dai *codec_dai = &dai_props->codec_dai;
+       unsigned int daifmt;
+
+       daifmt = snd_soc_of_parse_daifmt(node, prefix,
+                                        &bitclkmaster, &framemaster);
+       daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
+
+       if (strlen(prefix) && !bitclkmaster && !framemaster) {
+               /*
+                * No dai-link level and master setting was not found from
+                * sound node level, revert back to legacy DT parsing and
+                * take the settings from codec node.
+                */
+               dev_dbg(dev, "Revert to legacy daifmt parsing\n");
+
+               cpu_dai->fmt = codec_dai->fmt =
+                       snd_soc_of_parse_daifmt(codec, NULL, NULL, NULL) |
+                       (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
+       } else {
+               if (codec == bitclkmaster)
+                       daifmt |= (codec == framemaster) ?
+                               SND_SOC_DAIFMT_CBM_CFM : SND_SOC_DAIFMT_CBM_CFS;
+               else
+                       daifmt |= (codec == framemaster) ?
+                               SND_SOC_DAIFMT_CBS_CFM : SND_SOC_DAIFMT_CBS_CFS;
+
+               cpu_dai->fmt    = daifmt;
+               codec_dai->fmt  = daifmt;
+       }
+
+       of_node_put(bitclkmaster);
+       of_node_put(framemaster);
+
+       return 0;
+}
+
 static int asoc_simple_card_dai_link_of(struct device_node *node,
                                        struct simple_card_data *priv,
                                        int idx,
@@ -234,10 +284,8 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
        struct device *dev = simple_priv_to_dev(priv);
        struct snd_soc_dai_link *dai_link = simple_priv_to_link(priv, idx);
        struct simple_dai_props *dai_props = simple_priv_to_props(priv, idx);
-       struct device_node *np = NULL;
-       struct device_node *bitclkmaster = NULL;
-       struct device_node *framemaster = NULL;
-       unsigned int daifmt;
+       struct device_node *cpu = NULL;
+       struct device_node *codec = NULL;
        char *name;
        char prop[128];
        char *prefix = "";
@@ -247,85 +295,36 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
        if (is_top_level_node)
                prefix = "simple-audio-card,";
 
-       daifmt = snd_soc_of_parse_daifmt(node, prefix,
-                                        &bitclkmaster, &framemaster);
-       daifmt &= ~SND_SOC_DAIFMT_MASTER_MASK;
-
        snprintf(prop, sizeof(prop), "%scpu", prefix);
-       np = of_get_child_by_name(node, prop);
-       if (!np) {
+       cpu = of_get_child_by_name(node, prop);
+
+       snprintf(prop, sizeof(prop), "%scodec", prefix);
+       codec = of_get_child_by_name(node, prop);
+
+       if (!cpu || !codec) {
                ret = -EINVAL;
                dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
                goto dai_link_of_err;
        }
 
-       ret = asoc_simple_card_sub_parse_of(np, &dai_props->cpu_dai,
+       ret = asoc_simple_card_parse_daifmt(node, priv,
+                                           codec, prefix, idx);
+       if (ret < 0)
+               goto dai_link_of_err;
+
+       ret = asoc_simple_card_sub_parse_of(cpu, &dai_props->cpu_dai,
                                            &dai_link->cpu_of_node,
                                            &dai_link->cpu_dai_name,
                                            &cpu_args);
        if (ret < 0)
                goto dai_link_of_err;
 
-       dai_props->cpu_dai.fmt = daifmt;
-       switch (((np == bitclkmaster) << 4) | (np == framemaster)) {
-       case 0x11:
-               dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS;
-               break;
-       case 0x10:
-               dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM;
-               break;
-       case 0x01:
-               dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS;
-               break;
-       default:
-               dai_props->cpu_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM;
-               break;
-       }
-
-       of_node_put(np);
-       snprintf(prop, sizeof(prop), "%scodec", prefix);
-       np = of_get_child_by_name(node, prop);
-       if (!np) {
-               ret = -EINVAL;
-               dev_err(dev, "%s: Can't find %s DT node\n", __func__, prop);
-               goto dai_link_of_err;
-       }
-
-       ret = asoc_simple_card_sub_parse_of(np, &dai_props->codec_dai,
+       ret = asoc_simple_card_sub_parse_of(codec, &dai_props->codec_dai,
                                            &dai_link->codec_of_node,
                                            &dai_link->codec_dai_name, NULL);
        if (ret < 0)
                goto dai_link_of_err;
 
-       if (strlen(prefix) && !bitclkmaster && !framemaster) {
-               /*
-                * No DAI link level and master setting was found
-                * from sound node level, revert back to legacy DT
-                * parsing and take the settings from codec node.
-                */
-               dev_dbg(dev, "%s: Revert to legacy daifmt parsing\n",
-                       __func__);
-               dai_props->cpu_dai.fmt = dai_props->codec_dai.fmt =
-                       snd_soc_of_parse_daifmt(np, NULL, NULL, NULL) |
-                       (daifmt & ~SND_SOC_DAIFMT_CLOCK_MASK);
-       } else {
-               dai_props->codec_dai.fmt = daifmt;
-               switch (((np == bitclkmaster) << 4) | (np == framemaster)) {
-               case 0x11:
-                       dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFM;
-                       break;
-               case 0x10:
-                       dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBM_CFS;
-                       break;
-               case 0x01:
-                       dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFM;
-                       break;
-               default:
-                       dai_props->codec_dai.fmt |= SND_SOC_DAIFMT_CBS_CFS;
-                       break;
-               }
-       }
-
        if (!dai_link->cpu_dai_name || !dai_link->codec_dai_name) {
                ret = -EINVAL;
                goto dai_link_of_err;
@@ -368,12 +367,9 @@ static int asoc_simple_card_dai_link_of(struct device_node *node,
                dai_link->cpu_dai_name = NULL;
 
 dai_link_of_err:
-       if (np)
-               of_node_put(np);
-       if (bitclkmaster)
-               of_node_put(bitclkmaster);
-       if (framemaster)
-               of_node_put(framemaster);
+       of_node_put(cpu);
+       of_node_put(codec);
+
        return ret;
 }
 
@@ -381,6 +377,7 @@ static int asoc_simple_card_parse_of(struct device_node *node,
                                     struct simple_card_data *priv)
 {
        struct device *dev = simple_priv_to_dev(priv);
+       enum of_gpio_flags flags;
        u32 val;
        int ret;
 
@@ -436,13 +433,15 @@ static int asoc_simple_card_parse_of(struct device_node *node,
                        return ret;
        }
 
-       priv->gpio_hp_det = of_get_named_gpio(node,
-                               "simple-audio-card,hp-det-gpio", 0);
+       priv->gpio_hp_det = of_get_named_gpio_flags(node,
+                               "simple-audio-card,hp-det-gpio", 0, &flags);
+       priv->gpio_hp_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
        if (priv->gpio_hp_det == -EPROBE_DEFER)
                return -EPROBE_DEFER;
 
-       priv->gpio_mic_det = of_get_named_gpio(node,
-                               "simple-audio-card,mic-det-gpio", 0);
+       priv->gpio_mic_det = of_get_named_gpio_flags(node,
+                               "simple-audio-card,mic-det-gpio", 0, &flags);
+       priv->gpio_mic_det_invert = !!(flags & OF_GPIO_ACTIVE_LOW);
        if (priv->gpio_mic_det == -EPROBE_DEFER)
                return -EPROBE_DEFER;
 
@@ -457,18 +456,13 @@ static int asoc_simple_card_unref(struct platform_device *pdev)
 {
        struct snd_soc_card *card = platform_get_drvdata(pdev);
        struct snd_soc_dai_link *dai_link;
-       struct device_node *np;
        int num_links;
 
        for (num_links = 0, dai_link = card->dai_link;
             num_links < card->num_links;
             num_links++, dai_link++) {
-               np = (struct device_node *) dai_link->cpu_of_node;
-               if (np)
-                       of_node_put(np);
-               np = (struct device_node *) dai_link->codec_of_node;
-               if (np)
-                       of_node_put(np);
+               of_node_put(dai_link->cpu_of_node);
+               of_node_put(dai_link->codec_of_node);
        }
        return 0;
 }
index f5b4a9c..e989ecf 100644 (file)
@@ -3,6 +3,7 @@ config SND_MFLD_MACHINE
        depends on INTEL_SCU_IPC
        select SND_SOC_SN95031
        select SND_SST_MFLD_PLATFORM
+       select SND_SST_IPC_PCI
        help
           This adds support for ASoC machine driver for Intel(R) MID Medfield platform
           used as alsa device in audio substem in Intel(R) MID devices
@@ -12,10 +13,23 @@ config SND_MFLD_MACHINE
 config SND_SST_MFLD_PLATFORM
        tristate
 
+config SND_SST_IPC
+       tristate
+
+config SND_SST_IPC_PCI
+       tristate
+       select SND_SST_IPC
+
+config SND_SST_IPC_ACPI
+       tristate
+       select SND_SST_IPC
+       depends on ACPI
+
 config SND_SOC_INTEL_SST
        tristate "ASoC support for Intel(R) Smart Sound Technology"
        select SND_SOC_INTEL_SST_ACPI if ACPI
        depends on (X86 || COMPILE_TEST)
+       depends on DW_DMAC_CORE
        help
           This adds support for Intel(R) Smart Sound Technology (SST).
           Say Y if you have such a device
@@ -32,7 +46,8 @@ config SND_SOC_INTEL_BAYTRAIL
 
 config SND_SOC_INTEL_HASWELL_MACH
        tristate "ASoC Audio DSP support for Intel Haswell Lynxpoint"
-       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C
+       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && I2C && \\
+                  I2C_DESIGNWARE_PLATFORM
        select SND_SOC_INTEL_HASWELL
        select SND_SOC_RT5640
        help
@@ -61,7 +76,8 @@ config SND_SOC_INTEL_BYT_MAX98090_MACH
 
 config SND_SOC_INTEL_BROADWELL_MACH
        tristate "ASoC Audio DSP support for Intel Broadwell Wildcatpoint"
-       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC
+       depends on SND_SOC_INTEL_SST && X86_INTEL_LPSS && DW_DMAC && \\
+                  I2C_DESIGNWARE_PLATFORM
        select SND_SOC_INTEL_HASWELL
        select SND_COMPRESS_OFFLOAD
        select SND_SOC_RT286
@@ -70,3 +86,27 @@ config SND_SOC_INTEL_BROADWELL_MACH
          Ultrabook platforms.
          Say Y if you have such a device
          If unsure select "N".
+
+config SND_SOC_INTEL_BYTCR_RT5640_MACH
+       tristate "ASoC Audio DSP Support for MID BYT Platform"
+       depends on X86
+       select SND_SOC_RT5640
+       select SND_SST_MFLD_PLATFORM
+       select SND_SST_IPC_ACPI
+       help
+         This adds support for ASoC machine driver for Intel(R) MID Baytrail platform
+          used as alsa device in audio substem in Intel(R) MID devices
+          Say Y if you have such a device
+          If unsure select "N".
+
+config SND_SOC_INTEL_CHT_BSW_RT5672_MACH
+        tristate "ASoC Audio driver for Intel Cherrytrail & Braswell with RT5672 codec"
+        depends on X86_INTEL_LPSS
+        select SND_SOC_RT5670
+        select SND_SST_MFLD_PLATFORM
+        select SND_SST_IPC_ACPI
+        help
+          This adds support for ASoC machine driver for Intel(R) Cherrytrail & Braswell
+          platforms with RT5672 audio codec.
+          Say Y if you have such a device
+          If unsure select "N".
index f841786..e928ec3 100644 (file)
@@ -26,8 +26,15 @@ snd-soc-sst-haswell-objs := haswell.o
 snd-soc-sst-byt-rt5640-mach-objs := byt-rt5640.o
 snd-soc-sst-byt-max98090-mach-objs := byt-max98090.o
 snd-soc-sst-broadwell-objs := broadwell.o
+snd-soc-sst-bytcr-dpcm-rt5640-objs := bytcr_dpcm_rt5640.o
+snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
 
 obj-$(CONFIG_SND_SOC_INTEL_HASWELL_MACH) += snd-soc-sst-haswell.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_RT5640_MACH) += snd-soc-sst-byt-rt5640-mach.o
 obj-$(CONFIG_SND_SOC_INTEL_BYT_MAX98090_MACH) += snd-soc-sst-byt-max98090-mach.o
 obj-$(CONFIG_SND_SOC_INTEL_BROADWELL_MACH) += snd-soc-sst-broadwell.o
+obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-dpcm-rt5640.o
+obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
+
+# DSP driver
+obj-$(CONFIG_SND_SST_IPC) += sst/
index 0e550f1..c256764 100644 (file)
@@ -19,6 +19,7 @@
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/soc.h>
+#include <sound/jack.h>
 #include <sound/pcm_params.h>
 
 #include "sst-dsp.h"
 
 #include "../codecs/rt286.h"
 
+static struct snd_soc_jack broadwell_headset;
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin broadwell_headset_pins[] = {
+       {
+               .pin = "Mic Jack",
+               .mask = SND_JACK_MICROPHONE,
+       },
+       {
+               .pin = "Headphone Jack",
+               .mask = SND_JACK_HEADPHONE,
+       },
+};
+
+static const struct snd_kcontrol_new broadwell_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Speaker"),
+       SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+};
+
 static const struct snd_soc_dapm_widget broadwell_widgets[] = {
-       SND_SOC_DAPM_HP("Headphones", NULL),
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
        SND_SOC_DAPM_SPK("Speaker", NULL),
        SND_SOC_DAPM_MIC("Mic Jack", NULL),
        SND_SOC_DAPM_MIC("DMIC1", NULL),
@@ -42,7 +61,7 @@ static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
        {"Speaker", NULL, "SPOL"},
 
        /* HP jack connectors - unknown if we have jack deteck */
-       {"Headphones", NULL, "HPO Pin"},
+       {"Headphone Jack", NULL, "HPO Pin"},
 
        /* other jacks */
        {"MIC1", NULL, "Mic Jack"},
@@ -57,6 +76,27 @@ static const struct snd_soc_dapm_route broadwell_rt286_map[] = {
        {"AIF1 Playback", NULL, "SSP0 CODEC OUT"},
 };
 
+static int broadwell_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_codec *codec = rtd->codec;
+       int ret = 0;
+       ret = snd_soc_jack_new(codec, "Headset",
+               SND_JACK_HEADSET | SND_JACK_BTN_0, &broadwell_headset);
+
+       if (ret)
+               return ret;
+
+       ret = snd_soc_jack_add_pins(&broadwell_headset,
+               ARRAY_SIZE(broadwell_headset_pins),
+               broadwell_headset_pins);
+       if (ret)
+               return ret;
+
+       rt286_mic_detect(codec, &broadwell_headset);
+       return 0;
+}
+
+
 static int broadwell_ssp0_fixup(struct snd_soc_pcm_runtime *rtd,
                        struct snd_pcm_hw_params *params)
 {
@@ -116,7 +156,7 @@ static int broadwell_rtd_init(struct snd_soc_pcm_runtime *rtd)
        }
 
        /* always connected - check HP for jack detect */
-       snd_soc_dapm_enable_pin(dapm, "Headphones");
+       snd_soc_dapm_enable_pin(dapm, "Headphone Jack");
        snd_soc_dapm_enable_pin(dapm, "Speaker");
        snd_soc_dapm_enable_pin(dapm, "Mic Jack");
        snd_soc_dapm_enable_pin(dapm, "Line Jack");
@@ -131,7 +171,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
        /* Front End DAI links */
        {
                .name = "System PCM",
-               .stream_name = "System Playback",
+               .stream_name = "System Playback/Capture",
                .cpu_dai_name = "System Pin",
                .platform_name = "haswell-pcm-audio",
                .dynamic = 1,
@@ -140,6 +180,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
                .init = broadwell_rtd_init,
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_playback = 1,
+               .dpcm_capture = 1,
        },
        {
                .name = "Offload0",
@@ -174,18 +215,6 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_capture = 1,
        },
-       {
-               .name = "Capture PCM",
-               .stream_name = "Capture",
-               .cpu_dai_name = "Capture Pin",
-               .platform_name = "haswell-pcm-audio",
-               .dynamic = 1,
-               .codec_name = "snd-soc-dummy",
-               .codec_dai_name = "snd-soc-dummy-dai",
-               .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
-               .dpcm_capture = 1,
-       },
-
        /* Back End DAI links */
        {
                /* SSP0 - Codec */
@@ -196,6 +225,7 @@ static struct snd_soc_dai_link broadwell_rt286_dais[] = {
                .no_pcm = 1,
                .codec_name = "i2c-INT343A:00",
                .codec_dai_name = "rt286-aif1",
+               .init = broadwell_rt286_codec_init,
                .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
                        SND_SOC_DAIFMT_CBS_CFS,
                .ignore_suspend = 1,
@@ -213,6 +243,8 @@ static struct snd_soc_card broadwell_rt286 = {
        .owner = THIS_MODULE,
        .dai_link = broadwell_rt286_dais,
        .num_links = ARRAY_SIZE(broadwell_rt286_dais),
+       .controls = broadwell_controls,
+       .num_controls = ARRAY_SIZE(broadwell_controls),
        .dapm_widgets = broadwell_widgets,
        .num_dapm_widgets = ARRAY_SIZE(broadwell_widgets),
        .dapm_routes = broadwell_rt286_map,
diff --git a/sound/soc/intel/bytcr_dpcm_rt5640.c b/sound/soc/intel/bytcr_dpcm_rt5640.c
new file mode 100644 (file)
index 0000000..f5d0fc1
--- /dev/null
@@ -0,0 +1,230 @@
+/*
+ *  byt_cr_dpcm_rt5640.c - ASoc Machine driver for Intel Byt CR platform
+ *
+ *  Copyright (C) 2014 Intel Corp
+ *  Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+
+#include <linux/init.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+#include <linux/slab.h>
+#include <linux/input.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../codecs/rt5640.h"
+#include "sst-atom-controls.h"
+
+static const struct snd_soc_dapm_widget byt_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Int Mic", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+};
+
+static const struct snd_soc_dapm_route byt_audio_map[] = {
+       {"IN2P", NULL, "Headset Mic"},
+       {"IN2N", NULL, "Headset Mic"},
+       {"Headset Mic", NULL, "MICBIAS1"},
+       {"IN1P", NULL, "MICBIAS1"},
+       {"LDO2", NULL, "Int Mic"},
+       {"Headphone", NULL, "HPOL"},
+       {"Headphone", NULL, "HPOR"},
+       {"Ext Spk", NULL, "SPOLP"},
+       {"Ext Spk", NULL, "SPOLN"},
+       {"Ext Spk", NULL, "SPORP"},
+       {"Ext Spk", NULL, "SPORN"},
+
+       {"AIF1 Playback", NULL, "ssp2 Tx"},
+       {"ssp2 Tx", NULL, "codec_out0"},
+       {"ssp2 Tx", NULL, "codec_out1"},
+       {"codec_in0", NULL, "ssp2 Rx"},
+       {"codec_in1", NULL, "ssp2 Rx"},
+       {"ssp2 Rx", NULL, "AIF1 Capture"},
+};
+
+static const struct snd_kcontrol_new byt_mc_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Headphone"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Int Mic"),
+       SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int byt_aif1_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret;
+
+       snd_soc_dai_set_bclk_ratio(codec_dai, 50);
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, RT5640_SCLK_S_PLL1,
+                                    params_rate(params) * 512,
+                                    SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec clock %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_dai_set_pll(codec_dai, 0, RT5640_PLL1_S_BCLK1,
+                                 params_rate(params) * 50,
+                                 params_rate(params) * 512);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_pcm_stream byt_dai_params = {
+       .formats = SNDRV_PCM_FMTBIT_S24_LE,
+       .rate_min = 48000,
+       .rate_max = 48000,
+       .channels_min = 2,
+       .channels_max = 2,
+};
+
+static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+                           struct snd_pcm_hw_params *params)
+{
+       struct snd_interval *rate = hw_param_interval(params,
+                       SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels = hw_param_interval(params,
+                                               SNDRV_PCM_HW_PARAM_CHANNELS);
+
+       /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+       rate->min = rate->max = 48000;
+       channels->min = channels->max = 2;
+
+       /* set SSP2 to 24-bit */
+       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
+                                   SNDRV_PCM_FORMAT_S24_LE);
+       return 0;
+}
+
+static unsigned int rates_48000[] = {
+       48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+       .count = ARRAY_SIZE(rates_48000),
+       .list  = rates_48000,
+};
+
+static int byt_aif1_startup(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_hw_constraint_list(substream->runtime, 0,
+                       SNDRV_PCM_HW_PARAM_RATE,
+                       &constraints_48000);
+}
+
+static struct snd_soc_ops byt_aif1_ops = {
+       .startup = byt_aif1_startup,
+};
+
+static struct snd_soc_ops byt_be_ssp2_ops = {
+       .hw_params = byt_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link byt_dailink[] = {
+       [MERR_DPCM_AUDIO] = {
+               .name = "Baytrail Audio Port",
+               .stream_name = "Baytrail Audio",
+               .cpu_dai_name = "media-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+               .ignore_suspend = 1,
+               .dynamic = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .ops = &byt_aif1_ops,
+       },
+       [MERR_DPCM_COMPR] = {
+               .name = "Baytrail Compressed Port",
+               .stream_name = "Baytrail Compress",
+               .cpu_dai_name = "compress-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+       },
+               /* back ends */
+       {
+               .name = "SSP2-Codec",
+               .be_id = 1,
+               .cpu_dai_name = "ssp2-port",
+               .platform_name = "sst-mfld-platform",
+               .no_pcm = 1,
+               .codec_dai_name = "rt5640-aif1",
+               .codec_name = "i2c-10EC5640:00",
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+                                               | SND_SOC_DAIFMT_CBS_CFS,
+               .be_hw_params_fixup = byt_codec_fixup,
+               .ignore_suspend = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .ops = &byt_be_ssp2_ops,
+       },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_byt = {
+       .name = "baytrailcraudio",
+       .dai_link = byt_dailink,
+       .num_links = ARRAY_SIZE(byt_dailink),
+       .dapm_widgets = byt_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(byt_dapm_widgets),
+       .dapm_routes = byt_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(byt_audio_map),
+       .controls = byt_mc_controls,
+       .num_controls = ARRAY_SIZE(byt_mc_controls),
+};
+
+static int snd_byt_mc_probe(struct platform_device *pdev)
+{
+       int ret_val = 0;
+
+       /* register the soc card */
+       snd_soc_card_byt.dev = &pdev->dev;
+
+       ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_byt);
+       if (ret_val) {
+               dev_err(&pdev->dev, "devm_snd_soc_register_card failed %d\n", ret_val);
+               return ret_val;
+       }
+       platform_set_drvdata(pdev, &snd_soc_card_byt);
+       return ret_val;
+}
+
+static struct platform_driver snd_byt_mc_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "bytt100_rt5640",
+               .pm = &snd_soc_pm_ops,
+       },
+       .probe = snd_byt_mc_probe,
+};
+
+module_platform_driver(snd_byt_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
+MODULE_AUTHOR("Subhransu S. Prusty <subhransu.s.prusty@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:bytrt5640-audio");
diff --git a/sound/soc/intel/cht_bsw_rt5672.c b/sound/soc/intel/cht_bsw_rt5672.c
new file mode 100644 (file)
index 0000000..9b8b561
--- /dev/null
@@ -0,0 +1,285 @@
+/*
+ *  cht_bsw_rt5672.c - ASoc Machine driver for Intel Cherryview-based platforms
+ *                     Cherrytrail and Braswell, with RT5672 codec.
+ *
+ *  Copyright (C) 2014 Intel Corp
+ *  Author: Subhransu S. Prusty <subhransu.s.prusty@intel.com>
+ *          Mengdong Lin <mengdong.lin@intel.com>
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include "../codecs/rt5670.h"
+#include "sst-atom-controls.h"
+
+/* The platform clock #3 outputs 19.2Mhz clock to codec as I2S MCLK */
+#define CHT_PLAT_CLK_3_HZ      19200000
+#define CHT_CODEC_DAI  "rt5670-aif1"
+
+static inline struct snd_soc_dai *cht_get_codec_dai(struct snd_soc_card *card)
+{
+       int i;
+
+       for (i = 0; i < card->num_rtd; i++) {
+               struct snd_soc_pcm_runtime *rtd;
+
+               rtd = card->rtd + i;
+               if (!strncmp(rtd->codec_dai->name, CHT_CODEC_DAI,
+                            strlen(CHT_CODEC_DAI)))
+                       return rtd->codec_dai;
+       }
+       return NULL;
+}
+
+static int platform_clock_control(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *k, int  event)
+{
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct snd_soc_card *card = dapm->card;
+       struct snd_soc_dai *codec_dai;
+
+       codec_dai = cht_get_codec_dai(card);
+       if (!codec_dai) {
+               dev_err(card->dev, "Codec dai not found; Unable to set platform clock\n");
+               return -EIO;
+       }
+
+       if (!SND_SOC_DAPM_EVENT_OFF(event))
+               return 0;
+
+       /* Set codec sysclk source to its internal clock because codec PLL will
+        * be off when idle and MCLK will also be off by ACPI when codec is
+        * runtime suspended. Codec needs clock for jack detection and button
+        * press.
+        */
+       snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_RCCLK,
+                              0, SND_SOC_CLOCK_IN);
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget cht_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone", NULL),
+       SND_SOC_DAPM_MIC("Headset Mic", NULL),
+       SND_SOC_DAPM_MIC("Int Mic", NULL),
+       SND_SOC_DAPM_SPK("Ext Spk", NULL),
+       SND_SOC_DAPM_SUPPLY("Platform Clock", SND_SOC_NOPM, 0, 0,
+                       platform_clock_control, SND_SOC_DAPM_POST_PMD),
+};
+
+static const struct snd_soc_dapm_route cht_audio_map[] = {
+       {"IN1P", NULL, "Headset Mic"},
+       {"IN1N", NULL, "Headset Mic"},
+       {"DMIC L1", NULL, "Int Mic"},
+       {"DMIC R1", NULL, "Int Mic"},
+       {"Headphone", NULL, "HPOL"},
+       {"Headphone", NULL, "HPOR"},
+       {"Ext Spk", NULL, "SPOLP"},
+       {"Ext Spk", NULL, "SPOLN"},
+       {"Ext Spk", NULL, "SPORP"},
+       {"Ext Spk", NULL, "SPORN"},
+       {"AIF1 Playback", NULL, "ssp2 Tx"},
+       {"ssp2 Tx", NULL, "codec_out0"},
+       {"ssp2 Tx", NULL, "codec_out1"},
+       {"codec_in0", NULL, "ssp2 Rx"},
+       {"codec_in1", NULL, "ssp2 Rx"},
+       {"ssp2 Rx", NULL, "AIF1 Capture"},
+       {"Headphone", NULL, "Platform Clock"},
+       {"Headset Mic", NULL, "Platform Clock"},
+       {"Int Mic", NULL, "Platform Clock"},
+       {"Ext Spk", NULL, "Platform Clock"},
+};
+
+static const struct snd_kcontrol_new cht_mc_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Headphone"),
+       SOC_DAPM_PIN_SWITCH("Headset Mic"),
+       SOC_DAPM_PIN_SWITCH("Int Mic"),
+       SOC_DAPM_PIN_SWITCH("Ext Spk"),
+};
+
+static int cht_aif1_hw_params(struct snd_pcm_substream *substream,
+                                       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int ret;
+
+       /* set codec PLL source to the 19.2MHz platform clock (MCLK) */
+       ret = snd_soc_dai_set_pll(codec_dai, 0, RT5670_PLL1_S_MCLK,
+                                 CHT_PLAT_CLK_3_HZ, params_rate(params) * 512);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec pll: %d\n", ret);
+               return ret;
+       }
+
+       /* set codec sysclk source to PLL */
+       ret = snd_soc_dai_set_sysclk(codec_dai, RT5670_SCLK_S_PLL1,
+                                    params_rate(params) * 512,
+                                    SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               dev_err(rtd->dev, "can't set codec sysclk: %d\n", ret);
+               return ret;
+       }
+       return 0;
+}
+
+static int cht_codec_init(struct snd_soc_pcm_runtime *runtime)
+{
+       int ret;
+       struct snd_soc_dai *codec_dai = runtime->codec_dai;
+
+       /* TDM 4 slots 24 bit, set Rx & Tx bitmask to 4 active slots */
+       ret = snd_soc_dai_set_tdm_slot(codec_dai, 0xF, 0xF, 4, 24);
+       if (ret < 0) {
+               dev_err(runtime->dev, "can't set codec TDM slot %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
+                           struct snd_pcm_hw_params *params)
+{
+       struct snd_interval *rate = hw_param_interval(params,
+                       SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval *channels = hw_param_interval(params,
+                                               SNDRV_PCM_HW_PARAM_CHANNELS);
+
+       /* The DSP will covert the FE rate to 48k, stereo, 24bits */
+       rate->min = rate->max = 48000;
+       channels->min = channels->max = 2;
+
+       /* set SSP2 to 24-bit */
+       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
+                                   SNDRV_PCM_HW_PARAM_FIRST_MASK],
+                                   SNDRV_PCM_FORMAT_S24_LE);
+       return 0;
+}
+
+static unsigned int rates_48000[] = {
+       48000,
+};
+
+static struct snd_pcm_hw_constraint_list constraints_48000 = {
+       .count = ARRAY_SIZE(rates_48000),
+       .list  = rates_48000,
+};
+
+static int cht_aif1_startup(struct snd_pcm_substream *substream)
+{
+       return snd_pcm_hw_constraint_list(substream->runtime, 0,
+                       SNDRV_PCM_HW_PARAM_RATE,
+                       &constraints_48000);
+}
+
+static struct snd_soc_ops cht_aif1_ops = {
+       .startup = cht_aif1_startup,
+};
+
+static struct snd_soc_ops cht_be_ssp2_ops = {
+       .hw_params = cht_aif1_hw_params,
+};
+
+static struct snd_soc_dai_link cht_dailink[] = {
+       /* Front End DAI links */
+       [MERR_DPCM_AUDIO] = {
+               .name = "Audio Port",
+               .stream_name = "Audio",
+               .cpu_dai_name = "media-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+               .ignore_suspend = 1,
+               .dynamic = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .ops = &cht_aif1_ops,
+       },
+       [MERR_DPCM_COMPR] = {
+               .name = "Compressed Port",
+               .stream_name = "Compress",
+               .cpu_dai_name = "compress-cpu-dai",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .codec_name = "snd-soc-dummy",
+               .platform_name = "sst-mfld-platform",
+       },
+
+       /* Back End DAI links */
+       {
+               /* SSP2 - Codec */
+               .name = "SSP2-Codec",
+               .be_id = 1,
+               .cpu_dai_name = "ssp2-port",
+               .platform_name = "sst-mfld-platform",
+               .no_pcm = 1,
+               .codec_dai_name = "rt5670-aif1",
+               .codec_name = "i2c-10EC5670:00",
+               .dai_fmt = SND_SOC_DAIFMT_DSP_B | SND_SOC_DAIFMT_IB_NF
+                                       | SND_SOC_DAIFMT_CBS_CFS,
+               .init = cht_codec_init,
+               .be_hw_params_fixup = cht_codec_fixup,
+               .ignore_suspend = 1,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+               .ops = &cht_be_ssp2_ops,
+       },
+};
+
+/* SoC card */
+static struct snd_soc_card snd_soc_card_cht = {
+       .name = "cherrytrailcraudio",
+       .dai_link = cht_dailink,
+       .num_links = ARRAY_SIZE(cht_dailink),
+       .dapm_widgets = cht_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(cht_dapm_widgets),
+       .dapm_routes = cht_audio_map,
+       .num_dapm_routes = ARRAY_SIZE(cht_audio_map),
+       .controls = cht_mc_controls,
+       .num_controls = ARRAY_SIZE(cht_mc_controls),
+};
+
+static int snd_cht_mc_probe(struct platform_device *pdev)
+{
+       int ret_val = 0;
+
+       /* register the soc card */
+       snd_soc_card_cht.dev = &pdev->dev;
+       ret_val = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_cht);
+       if (ret_val) {
+               dev_err(&pdev->dev,
+                       "snd_soc_register_card failed %d\n", ret_val);
+               return ret_val;
+       }
+       platform_set_drvdata(pdev, &snd_soc_card_cht);
+       return ret_val;
+}
+
+static struct platform_driver snd_cht_mc_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "cht-bsw-rt5672",
+               .pm = &snd_soc_pm_ops,
+       },
+       .probe = snd_cht_mc_probe,
+};
+
+module_platform_driver(snd_cht_mc_driver);
+
+MODULE_DESCRIPTION("ASoC Intel(R) Baytrail CR Machine driver");
+MODULE_AUTHOR("Subhransu S. Prusty, Mengdong Lin");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:cht-bsw-rt5672");
index 3981982..cb8a482 100644 (file)
@@ -109,7 +109,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
        /* Front End DAI links */
        {
                .name = "System",
-               .stream_name = "System Playback",
+               .stream_name = "System Playback/Capture",
                .cpu_dai_name = "System Pin",
                .platform_name = "haswell-pcm-audio",
                .dynamic = 1,
@@ -118,6 +118,7 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
                .init = haswell_rtd_init,
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_playback = 1,
+               .dpcm_capture = 1,
        },
        {
                .name = "Offload0",
@@ -152,17 +153,6 @@ static struct snd_soc_dai_link haswell_rt5640_dais[] = {
                .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
                .dpcm_capture = 1,
        },
-       {
-               .name = "Capture",
-               .stream_name = "Capture",
-               .cpu_dai_name = "Capture Pin",
-               .platform_name = "haswell-pcm-audio",
-               .dynamic = 1,
-               .codec_name = "snd-soc-dummy",
-               .codec_dai_name = "snd-soc-dummy-dai",
-               .trigger = {SND_SOC_DPCM_TRIGGER_POST, SND_SOC_DPCM_TRIGGER_POST},
-               .dpcm_capture = 1,
-       },
 
        /* Back End DAI links */
        {
index 7104a34..90aa5c0 100644 (file)
@@ -15,6 +15,9 @@
  *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
  *  General Public License for more details.
  *
+ *  In the dpcm driver modelling when a particular FE/BE/Mixer/Pipe is active
+ *  we forward the settings and parameters, rest we keep the values  in
+ *  driver and forward when DAPM enables them
  * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
  */
 #define pr_fmt(fmt) KBUILD_MODNAME ": " fmt
@@ -81,6 +84,183 @@ static int sst_fill_and_send_cmd(struct sst_data *drv,
        return ret;
 }
 
+/**
+ * tx map value is a bitfield where each bit represents a FW channel
+ *
+ *                     3 2 1 0         # 0 = codec0, 1 = codec1
+ *                     RLRLRLRL        # 3, 4 = reserved
+ *
+ * e.g. slot 0 rx map =        00001100b -> data from slot 0 goes into codec_in1 L,R
+ */
+static u8 sst_ssp_tx_map[SST_MAX_TDM_SLOTS] = {
+       0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default rx map */
+};
+
+/**
+ * rx map value is a bitfield where each bit represents a slot
+ *
+ *                       76543210      # 0 = slot 0, 1 = slot 1
+ *
+ * e.g. codec1_0 tx map = 00000101b -> data from codec_out1_0 goes into slot 0, 2
+ */
+static u8 sst_ssp_rx_map[SST_MAX_TDM_SLOTS] = {
+       0x1, 0x2, 0x4, 0x8, 0x10, 0x20, 0x40, 0x80, /* default tx map */
+};
+
+/**
+ * NOTE: this is invoked with lock held
+ */
+static int sst_send_slot_map(struct sst_data *drv)
+{
+       struct sst_param_sba_ssp_slot_map cmd;
+
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       cmd.header.command_id = SBA_SET_SSP_SLOT_MAP;
+       cmd.header.length = sizeof(struct sst_param_sba_ssp_slot_map)
+                               - sizeof(struct sst_dsp_header);
+
+       cmd.param_id = SBA_SET_SSP_SLOT_MAP;
+       cmd.param_len = sizeof(cmd.rx_slot_map) + sizeof(cmd.tx_slot_map)
+                                       + sizeof(cmd.ssp_index);
+       cmd.ssp_index = SSP_CODEC;
+
+       memcpy(cmd.rx_slot_map, &sst_ssp_tx_map[0], sizeof(cmd.rx_slot_map));
+       memcpy(cmd.tx_slot_map, &sst_ssp_rx_map[0], sizeof(cmd.tx_slot_map));
+
+       return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+                       SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+}
+
+int sst_slot_enum_info(struct snd_kcontrol *kcontrol,
+                      struct snd_ctl_elem_info *uinfo)
+{
+       struct sst_enum *e = (struct sst_enum *)kcontrol->private_value;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+       uinfo->count = 1;
+       uinfo->value.enumerated.items = e->max;
+
+       if (uinfo->value.enumerated.item > e->max - 1)
+               uinfo->value.enumerated.item = e->max - 1;
+       strcpy(uinfo->value.enumerated.name,
+               e->texts[uinfo->value.enumerated.item]);
+
+       return 0;
+}
+
+/**
+ * sst_slot_get - get the status of the interleaver/deinterleaver control
+ *
+ * Searches the map where the control status is stored, and gets the
+ * channel/slot which is currently set for this enumerated control. Since it is
+ * an enumerated control, there is only one possible value.
+ */
+static int sst_slot_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct sst_enum *e = (void *)kcontrol->private_value;
+       struct snd_soc_component *c = snd_kcontrol_chip(kcontrol);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       unsigned int ctl_no = e->reg;
+       unsigned int is_tx = e->tx;
+       unsigned int val, mux;
+       u8 *map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+       mutex_lock(&drv->lock);
+       val = 1 << ctl_no;
+       /* search which slot/channel has this bit set - there should be only one */
+       for (mux = e->max; mux > 0;  mux--)
+               if (map[mux - 1] & val)
+                       break;
+
+       ucontrol->value.enumerated.item[0] = mux;
+       mutex_unlock(&drv->lock);
+
+       dev_dbg(c->dev, "%s - %s map = %#x\n",
+                       is_tx ? "tx channel" : "rx slot",
+                        e->texts[mux], mux ? map[mux - 1] : -1);
+       return 0;
+}
+
+/* sst_check_and_send_slot_map - helper for checking power state and sending
+ * slot map cmd
+ *
+ * called with lock held
+ */
+static int sst_check_and_send_slot_map(struct sst_data *drv, struct snd_kcontrol *kcontrol)
+{
+       struct sst_enum *e = (void *)kcontrol->private_value;
+       int ret = 0;
+
+       if (e->w && e->w->power)
+               ret = sst_send_slot_map(drv);
+       else
+               dev_err(&drv->pdev->dev, "Slot control: %s doesn't have DAPM widget!!!\n",
+                               kcontrol->id.name);
+       return ret;
+}
+
+/**
+ * sst_slot_put - set the status of interleaver/deinterleaver control
+ *
+ * (de)interleaver controls are defined in opposite sense to be user-friendly
+ *
+ * Instead of the enum value being the value written to the register, it is the
+ * register address; and the kcontrol number (register num) is the value written
+ * to the register. This is so that there can be only one value for each
+ * slot/channel since there is only one control for each slot/channel.
+ *
+ * This means that whenever an enum is set, we need to clear the bit
+ * for that kcontrol_no for all the interleaver OR deinterleaver registers
+ */
+static int sst_slot_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *c = snd_soc_kcontrol_component(kcontrol);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       struct sst_enum *e = (void *)kcontrol->private_value;
+       int i, ret = 0;
+       unsigned int ctl_no = e->reg;
+       unsigned int is_tx = e->tx;
+       unsigned int slot_channel_no;
+       unsigned int val, mux;
+       u8 *map;
+
+       map = is_tx ? sst_ssp_rx_map : sst_ssp_tx_map;
+
+       val = 1 << ctl_no;
+       mux = ucontrol->value.enumerated.item[0];
+       if (mux > e->max - 1)
+               return -EINVAL;
+
+       mutex_lock(&drv->lock);
+       /* first clear all registers of this bit */
+       for (i = 0; i < e->max; i++)
+               map[i] &= ~val;
+
+       if (mux == 0) {
+               /* kctl set to 'none' and we reset the bits so send IPC */
+               ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+               mutex_unlock(&drv->lock);
+               return ret;
+       }
+
+       /* offset by one to take "None" into account */
+       slot_channel_no = mux - 1;
+       map[slot_channel_no] |= val;
+
+       dev_dbg(c->dev, "%s %s map = %#x\n",
+                       is_tx ? "tx channel" : "rx slot",
+                       e->texts[mux], map[slot_channel_no]);
+
+       ret = sst_check_and_send_slot_map(drv, kcontrol);
+
+       mutex_unlock(&drv->lock);
+       return ret;
+}
+
 static int sst_send_algo_cmd(struct sst_data *drv,
                              struct sst_algo_control *bc)
 {
@@ -104,6 +284,34 @@ static int sst_send_algo_cmd(struct sst_data *drv,
        return ret;
 }
 
+/**
+ * sst_find_and_send_pipe_algo - send all the algo parameters for a pipe
+ *
+ * The algos which are in each pipeline are sent to the firmware one by one
+ *
+ * Called with lock held
+ */
+static int sst_find_and_send_pipe_algo(struct sst_data *drv,
+                                       const char *pipe, struct sst_ids *ids)
+{
+       int ret = 0;
+       struct sst_algo_control *bc;
+       struct sst_module *algo = NULL;
+
+       dev_dbg(&drv->pdev->dev, "Enter: widget=%s\n", pipe);
+
+       list_for_each_entry(algo, &ids->algo_list, node) {
+               bc = (void *)algo->kctl->private_value;
+
+               dev_dbg(&drv->pdev->dev, "Found algo control name=%s pipe=%s\n",
+                               algo->kctl->id.name, pipe);
+               ret = sst_send_algo_cmd(drv, bc);
+               if (ret)
+                       return ret;
+       }
+       return ret;
+}
+
 static int sst_algo_bytes_ctl_info(struct snd_kcontrol *kcontrol,
                            struct snd_ctl_elem_info *uinfo)
 {
@@ -162,6 +370,743 @@ static int sst_algo_control_set(struct snd_kcontrol *kcontrol,
        return ret;
 }
 
+static int sst_gain_ctl_info(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = mc->stereo ? 2 : 1;
+       uinfo->value.integer.min = mc->min;
+       uinfo->value.integer.max = mc->max;
+
+       return 0;
+}
+
+/**
+ * sst_send_gain_cmd - send the gain algorithm IPC to the FW
+ * @gv:                the stored value of gain (also contains rampduration)
+ * @mute:      flag that indicates whether this was called from the
+ *             digital_mute callback or directly. If called from the
+ *             digital_mute callback, module will be muted/unmuted based on this
+ *             flag. The flag is always 0 if called directly.
+ *
+ * Called with sst_data.lock held
+ *
+ * The user-set gain value is sent only if the user-controllable 'mute' control
+ * is OFF (indicated by gv->mute). Otherwise, the mute value (MIN value) is
+ * sent.
+ */
+static int sst_send_gain_cmd(struct sst_data *drv, struct sst_gain_value *gv,
+                             u16 task_id, u16 loc_id, u16 module_id, int mute)
+{
+       struct sst_cmd_set_gain_dual cmd;
+
+       dev_dbg(&drv->pdev->dev, "Enter\n");
+
+       cmd.header.command_id = MMX_SET_GAIN;
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       cmd.gain_cell_num = 1;
+
+       if (mute || gv->mute) {
+               cmd.cell_gains[0].cell_gain_left = SST_GAIN_MIN_VALUE;
+               cmd.cell_gains[0].cell_gain_right = SST_GAIN_MIN_VALUE;
+       } else {
+               cmd.cell_gains[0].cell_gain_left = gv->l_gain;
+               cmd.cell_gains[0].cell_gain_right = gv->r_gain;
+       }
+
+       SST_FILL_DESTINATION(2, cmd.cell_gains[0].dest,
+                            loc_id, module_id);
+       cmd.cell_gains[0].gain_time_constant = gv->ramp_duration;
+
+       cmd.header.length = sizeof(struct sst_cmd_set_gain_dual)
+                               - sizeof(struct sst_dsp_header);
+
+       /* we are with lock held, so call the unlocked api  to send */
+       return sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_SET_PARAMS,
+                               SST_FLAG_BLOCKED, task_id, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+}
+
+static int sst_gain_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+       struct sst_gain_value *gv = mc->gain_val;
+
+       switch (mc->type) {
+       case SST_GAIN_TLV:
+               ucontrol->value.integer.value[0] = gv->l_gain;
+               ucontrol->value.integer.value[1] = gv->r_gain;
+               break;
+
+       case SST_GAIN_MUTE:
+               ucontrol->value.integer.value[0] = gv->mute ? 1 : 0;
+               break;
+
+       case SST_GAIN_RAMP_DURATION:
+               ucontrol->value.integer.value[0] = gv->ramp_duration;
+               break;
+
+       default:
+               dev_err(component->dev, "Invalid Input- gain type:%d\n",
+                               mc->type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int sst_gain_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       int ret = 0;
+       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
+       struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+       struct sst_gain_mixer_control *mc = (void *)kcontrol->private_value;
+       struct sst_gain_value *gv = mc->gain_val;
+
+       mutex_lock(&drv->lock);
+
+       switch (mc->type) {
+       case SST_GAIN_TLV:
+               gv->l_gain = ucontrol->value.integer.value[0];
+               gv->r_gain = ucontrol->value.integer.value[1];
+               dev_dbg(cmpnt->dev, "%s: Volume %d, %d\n",
+                               mc->pname, gv->l_gain, gv->r_gain);
+               break;
+
+       case SST_GAIN_MUTE:
+               gv->mute = !!ucontrol->value.integer.value[0];
+               dev_dbg(cmpnt->dev, "%s: Mute %d\n", mc->pname, gv->mute);
+               break;
+
+       case SST_GAIN_RAMP_DURATION:
+               gv->ramp_duration = ucontrol->value.integer.value[0];
+               dev_dbg(cmpnt->dev, "%s: Ramp Delay%d\n",
+                                       mc->pname, gv->ramp_duration);
+               break;
+
+       default:
+               mutex_unlock(&drv->lock);
+               dev_err(cmpnt->dev, "Invalid Input- gain type:%d\n",
+                               mc->type);
+               return -EINVAL;
+       }
+
+       if (mc->w && mc->w->power)
+               ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+                       mc->pipe_id | mc->instance_id, mc->module_id, 0);
+       mutex_unlock(&drv->lock);
+
+       return ret;
+}
+
+static int sst_set_pipe_gain(struct sst_ids *ids,
+                               struct sst_data *drv, int mute);
+
+static int sst_send_pipe_module_params(struct snd_soc_dapm_widget *w,
+               struct snd_kcontrol *kcontrol)
+{
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       struct sst_ids *ids = w->priv;
+
+       mutex_lock(&drv->lock);
+       sst_find_and_send_pipe_algo(drv, w->name, ids);
+       sst_set_pipe_gain(ids, drv, 0);
+       mutex_unlock(&drv->lock);
+
+       return 0;
+}
+
+static int sst_generic_modules_event(struct snd_soc_dapm_widget *w,
+                                    struct snd_kcontrol *k, int event)
+{
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               return sst_send_pipe_module_params(w, k);
+       return 0;
+}
+
+static const DECLARE_TLV_DB_SCALE(sst_gain_tlv_common, SST_GAIN_MIN_VALUE * 10, 10, 0);
+
+/* Look up table to convert MIXER SW bit regs to SWM inputs */
+static const uint swm_mixer_input_ids[SST_SWM_INPUT_COUNT] = {
+       [SST_IP_CODEC0]         = SST_SWM_IN_CODEC0,
+       [SST_IP_CODEC1]         = SST_SWM_IN_CODEC1,
+       [SST_IP_LOOP0]          = SST_SWM_IN_SPROT_LOOP,
+       [SST_IP_LOOP1]          = SST_SWM_IN_MEDIA_LOOP1,
+       [SST_IP_LOOP2]          = SST_SWM_IN_MEDIA_LOOP2,
+       [SST_IP_PCM0]           = SST_SWM_IN_PCM0,
+       [SST_IP_PCM1]           = SST_SWM_IN_PCM1,
+       [SST_IP_MEDIA0]         = SST_SWM_IN_MEDIA0,
+       [SST_IP_MEDIA1]         = SST_SWM_IN_MEDIA1,
+       [SST_IP_MEDIA2]         = SST_SWM_IN_MEDIA2,
+       [SST_IP_MEDIA3]         = SST_SWM_IN_MEDIA3,
+};
+
+/**
+ * fill_swm_input - fill in the SWM input ids given the register
+ *
+ * The register value is a bit-field inicated which mixer inputs are ON. Use the
+ * lookup table to get the input-id and fill it in the structure.
+ */
+static int fill_swm_input(struct snd_soc_component *cmpnt,
+               struct swm_input_ids *swm_input, unsigned int reg)
+{
+       uint i, is_set, nb_inputs = 0;
+       u16 input_loc_id;
+
+       dev_dbg(cmpnt->dev, "reg: %#x\n", reg);
+       for (i = 0; i < SST_SWM_INPUT_COUNT; i++) {
+               is_set = reg & BIT(i);
+               if (!is_set)
+                       continue;
+
+               input_loc_id = swm_mixer_input_ids[i];
+               SST_FILL_DESTINATION(2, swm_input->input_id,
+                                    input_loc_id, SST_DEFAULT_MODULE_ID);
+               nb_inputs++;
+               swm_input++;
+               dev_dbg(cmpnt->dev, "input id: %#x, nb_inputs: %d\n",
+                               input_loc_id, nb_inputs);
+
+               if (nb_inputs == SST_CMD_SWM_MAX_INPUTS) {
+                       dev_warn(cmpnt->dev, "SET_SWM cmd max inputs reached");
+                       break;
+               }
+       }
+       return nb_inputs;
+}
+
+
+/**
+ * called with lock held
+ */
+static int sst_set_pipe_gain(struct sst_ids *ids,
+                       struct sst_data *drv, int mute)
+{
+       int ret = 0;
+       struct sst_gain_mixer_control *mc;
+       struct sst_gain_value *gv;
+       struct sst_module *gain = NULL;
+
+       list_for_each_entry(gain, &ids->gain_list, node) {
+               struct snd_kcontrol *kctl = gain->kctl;
+
+               dev_dbg(&drv->pdev->dev, "control name=%s\n", kctl->id.name);
+               mc = (void *)kctl->private_value;
+               gv = mc->gain_val;
+
+               ret = sst_send_gain_cmd(drv, gv, mc->task_id,
+                       mc->pipe_id | mc->instance_id, mc->module_id, mute);
+               if (ret)
+                       return ret;
+       }
+       return ret;
+}
+
+static int sst_swm_mixer_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+{
+       struct sst_cmd_set_swm cmd;
+       struct snd_soc_component *cmpnt = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(cmpnt);
+       struct sst_ids *ids = w->priv;
+       bool set_mixer = false;
+       struct soc_mixer_control *mc;
+       int val = 0;
+       int i = 0;
+
+       dev_dbg(cmpnt->dev, "widget = %s\n", w->name);
+       /*
+        * Identify which mixer input is on and send the bitmap of the
+        * inputs as an IPC to the DSP.
+        */
+       for (i = 0; i < w->num_kcontrols; i++) {
+               if (dapm_kcontrol_get_value(w->kcontrols[i])) {
+                       mc = (struct soc_mixer_control *)(w->kcontrols[i])->private_value;
+                       val |= 1 << mc->shift;
+               }
+       }
+       dev_dbg(cmpnt->dev, "val = %#x\n", val);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+       case SND_SOC_DAPM_POST_PMD:
+               set_mixer = true;
+               break;
+       case SND_SOC_DAPM_POST_REG:
+               if (w->power)
+                       set_mixer = true;
+               break;
+       default:
+               set_mixer = false;
+       }
+
+       if (set_mixer == false)
+               return 0;
+
+       if (SND_SOC_DAPM_EVENT_ON(event) ||
+           event == SND_SOC_DAPM_POST_REG)
+               cmd.switch_state = SST_SWM_ON;
+       else
+               cmd.switch_state = SST_SWM_OFF;
+
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       /* MMX_SET_SWM == SBA_SET_SWM */
+       cmd.header.command_id = SBA_SET_SWM;
+
+       SST_FILL_DESTINATION(2, cmd.output_id,
+                            ids->location_id, SST_DEFAULT_MODULE_ID);
+       cmd.nb_inputs = fill_swm_input(cmpnt, &cmd.input[0], val);
+       cmd.header.length = offsetof(struct sst_cmd_set_swm, input)
+                               - sizeof(struct sst_dsp_header)
+                               + (cmd.nb_inputs * sizeof(cmd.input[0]));
+
+       return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+                             ids->task_id, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+}
+
+/* SBA mixers - 16 inputs */
+#define SST_SBA_DECLARE_MIX_CONTROLS(kctl_name)                                                        \
+       static const struct snd_kcontrol_new kctl_name[] = {                                    \
+               SOC_DAPM_SINGLE("codec_in0 Switch", SND_SOC_NOPM, SST_IP_CODEC0, 1, 0),         \
+               SOC_DAPM_SINGLE("codec_in1 Switch", SND_SOC_NOPM, SST_IP_CODEC1, 1, 0),         \
+               SOC_DAPM_SINGLE("sprot_loop_in Switch", SND_SOC_NOPM, SST_IP_LOOP0, 1, 0),      \
+               SOC_DAPM_SINGLE("media_loop1_in Switch", SND_SOC_NOPM, SST_IP_LOOP1, 1, 0),     \
+               SOC_DAPM_SINGLE("media_loop2_in Switch", SND_SOC_NOPM, SST_IP_LOOP2, 1, 0),     \
+               SOC_DAPM_SINGLE("pcm0_in Switch", SND_SOC_NOPM, SST_IP_PCM0, 1, 0),             \
+               SOC_DAPM_SINGLE("pcm1_in Switch", SND_SOC_NOPM, SST_IP_PCM1, 1, 0),             \
+       }
+
+#define SST_SBA_MIXER_GRAPH_MAP(mix_name)                      \
+       { mix_name, "codec_in0 Switch", "codec_in0" },          \
+       { mix_name, "codec_in1 Switch", "codec_in1" },          \
+       { mix_name, "sprot_loop_in Switch",     "sprot_loop_in" },      \
+       { mix_name, "media_loop1_in Switch",    "media_loop1_in" },     \
+       { mix_name, "media_loop2_in Switch",    "media_loop2_in" },     \
+       { mix_name, "pcm0_in Switch",           "pcm0_in" },            \
+       { mix_name, "pcm1_in Switch",           "pcm1_in" }
+
+#define SST_MMX_DECLARE_MIX_CONTROLS(kctl_name)                                                \
+       static const struct snd_kcontrol_new kctl_name[] = {                            \
+               SOC_DAPM_SINGLE("media0_in Switch", SND_SOC_NOPM, SST_IP_MEDIA0, 1, 0), \
+               SOC_DAPM_SINGLE("media1_in Switch", SND_SOC_NOPM, SST_IP_MEDIA1, 1, 0), \
+               SOC_DAPM_SINGLE("media2_in Switch", SND_SOC_NOPM, SST_IP_MEDIA2, 1, 0), \
+               SOC_DAPM_SINGLE("media3_in Switch", SND_SOC_NOPM, SST_IP_MEDIA3, 1, 0), \
+       }
+
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media0_controls);
+SST_MMX_DECLARE_MIX_CONTROLS(sst_mix_media1_controls);
+
+/* 18 SBA mixers */
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_pcm2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_sprot_l0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l1_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_media_l2_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_voip_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec0_controls);
+SST_SBA_DECLARE_MIX_CONTROLS(sst_mix_codec1_controls);
+
+/*
+ * sst_handle_vb_timer - Start/Stop the DSP scheduler
+ *
+ * The DSP expects first cmd to be SBA_VB_START, so at first startup send
+ * that.
+ * DSP expects last cmd to be SBA_VB_IDLE, so at last shutdown send that.
+ *
+ * Do refcount internally so that we send command only at first start
+ * and last end. Since SST driver does its own ref count, invoke sst's
+ * power ops always!
+ */
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable)
+{
+       int ret = 0;
+       struct sst_cmd_generic cmd;
+       struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+       static int timer_usage;
+
+       if (enable)
+               cmd.header.command_id = SBA_VB_START;
+       else
+               cmd.header.command_id = SBA_IDLE;
+       dev_dbg(dai->dev, "enable=%u, usage=%d\n", enable, timer_usage);
+
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       cmd.header.length = 0;
+
+       if (enable) {
+               ret = sst->ops->power(sst->dev, true);
+               if (ret < 0)
+                       return ret;
+       }
+
+       mutex_lock(&drv->lock);
+       if (enable)
+               timer_usage++;
+       else
+               timer_usage--;
+
+       /*
+        * Send the command only if this call is the first enable or last
+        * disable
+        */
+       if ((enable && (timer_usage == 1)) ||
+           (!enable && (timer_usage == 0))) {
+               ret = sst_fill_and_send_cmd_unlocked(drv, SST_IPC_IA_CMD,
+                               SST_FLAG_BLOCKED, SST_TASK_SBA, 0, &cmd,
+                               sizeof(cmd.header) + cmd.header.length);
+               if (ret && enable) {
+                       timer_usage--;
+                       enable  = false;
+               }
+       }
+       mutex_unlock(&drv->lock);
+
+       if (!enable)
+               sst->ops->power(sst->dev, false);
+       return ret;
+}
+
+/**
+ * sst_ssp_config - contains SSP configuration for media UC
+ */
+static const struct sst_ssp_config sst_ssp_configs = {
+       .ssp_id = SSP_CODEC,
+       .bits_per_slot = 24,
+       .slots = 4,
+       .ssp_mode = SSP_MODE_MASTER,
+       .pcm_mode = SSP_PCM_MODE_NETWORK,
+       .duplex = SSP_DUPLEX,
+       .ssp_protocol = SSP_MODE_PCM,
+       .fs_width = 1,
+       .fs_frequency = SSP_FS_48_KHZ,
+       .active_slot_map = 0xF,
+       .start_delay = 0,
+};
+
+int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable)
+{
+       struct sst_cmd_sba_hw_set_ssp cmd;
+       struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+       const struct sst_ssp_config *config;
+
+       dev_info(dai->dev, "Enter: enable=%d port_name=%s\n", enable, id);
+
+       SST_FILL_DEFAULT_DESTINATION(cmd.header.dst);
+       cmd.header.command_id = SBA_HW_SET_SSP;
+       cmd.header.length = sizeof(struct sst_cmd_sba_hw_set_ssp)
+                               - sizeof(struct sst_dsp_header);
+
+       config = &sst_ssp_configs;
+       dev_dbg(dai->dev, "ssp_id: %u\n", config->ssp_id);
+
+       if (enable)
+               cmd.switch_state = SST_SWITCH_ON;
+       else
+               cmd.switch_state = SST_SWITCH_OFF;
+
+       cmd.selection = config->ssp_id;
+       cmd.nb_bits_per_slots = config->bits_per_slot;
+       cmd.nb_slots = config->slots;
+       cmd.mode = config->ssp_mode | (config->pcm_mode << 1);
+       cmd.duplex = config->duplex;
+       cmd.active_tx_slot_map = config->active_slot_map;
+       cmd.active_rx_slot_map = config->active_slot_map;
+       cmd.frame_sync_frequency = config->fs_frequency;
+       cmd.frame_sync_polarity = SSP_FS_ACTIVE_HIGH;
+       cmd.data_polarity = 1;
+       cmd.frame_sync_width = config->fs_width;
+       cmd.ssp_protocol = config->ssp_protocol;
+       cmd.start_delay = config->start_delay;
+       cmd.reserved1 = cmd.reserved2 = 0xFF;
+
+       return sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+                               SST_TASK_SBA, 0, &cmd,
+                               sizeof(cmd.header) + cmd.header.length);
+}
+
+static int sst_set_be_modules(struct snd_soc_dapm_widget *w,
+                        struct snd_kcontrol *k, int event)
+{
+       int ret = 0;
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+
+       dev_dbg(c->dev, "Enter: widget=%s\n", w->name);
+
+       if (SND_SOC_DAPM_EVENT_ON(event)) {
+               ret = sst_send_slot_map(drv);
+               if (ret)
+                       return ret;
+               ret = sst_send_pipe_module_params(w, k);
+       }
+       return ret;
+}
+
+static int sst_set_media_path(struct snd_soc_dapm_widget *w,
+                             struct snd_kcontrol *k, int event)
+{
+       int ret = 0;
+       struct sst_cmd_set_media_path cmd;
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       struct sst_ids *ids = w->priv;
+
+       dev_dbg(c->dev, "widget=%s\n", w->name);
+       dev_dbg(c->dev, "task=%u, location=%#x\n",
+                               ids->task_id, ids->location_id);
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               cmd.switch_state = SST_PATH_ON;
+       else
+               cmd.switch_state = SST_PATH_OFF;
+
+       SST_FILL_DESTINATION(2, cmd.header.dst,
+                            ids->location_id, SST_DEFAULT_MODULE_ID);
+
+       /* MMX_SET_MEDIA_PATH == SBA_SET_MEDIA_PATH */
+       cmd.header.command_id = MMX_SET_MEDIA_PATH;
+       cmd.header.length = sizeof(struct sst_cmd_set_media_path)
+                               - sizeof(struct sst_dsp_header);
+
+       ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+                             ids->task_id, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+       if (ret)
+               return ret;
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               ret = sst_send_pipe_module_params(w, k);
+       return ret;
+}
+
+static int sst_set_media_loop(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+{
+       int ret = 0;
+       struct sst_cmd_sba_set_media_loop_map cmd;
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_data *drv = snd_soc_component_get_drvdata(c);
+       struct sst_ids *ids = w->priv;
+
+       dev_dbg(c->dev, "Enter:widget=%s\n", w->name);
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               cmd.switch_state = SST_SWITCH_ON;
+       else
+               cmd.switch_state = SST_SWITCH_OFF;
+
+       SST_FILL_DESTINATION(2, cmd.header.dst,
+                            ids->location_id, SST_DEFAULT_MODULE_ID);
+
+       cmd.header.command_id = SBA_SET_MEDIA_LOOP_MAP;
+       cmd.header.length = sizeof(struct sst_cmd_sba_set_media_loop_map)
+                                - sizeof(struct sst_dsp_header);
+       cmd.param.part.cfg.rate = 2; /* 48khz */
+
+       cmd.param.part.cfg.format = ids->format; /* stereo/Mono */
+       cmd.param.part.cfg.s_length = 1; /* 24bit left justified */
+       cmd.map = 0; /* Algo sequence: Gain - DRP - FIR - IIR */
+
+       ret = sst_fill_and_send_cmd(drv, SST_IPC_IA_CMD, SST_FLAG_BLOCKED,
+                             SST_TASK_SBA, 0, &cmd,
+                             sizeof(cmd.header) + cmd.header.length);
+       if (ret)
+               return ret;
+
+       if (SND_SOC_DAPM_EVENT_ON(event))
+               ret = sst_send_pipe_module_params(w, k);
+       return ret;
+}
+
+static const struct snd_soc_dapm_widget sst_dapm_widgets[] = {
+       SST_AIF_IN("codec_in0", sst_set_be_modules),
+       SST_AIF_IN("codec_in1", sst_set_be_modules),
+       SST_AIF_OUT("codec_out0", sst_set_be_modules),
+       SST_AIF_OUT("codec_out1", sst_set_be_modules),
+
+       /* Media Paths */
+       /* MediaX IN paths are set via ALLOC, so no SET_MEDIA_PATH command */
+       SST_PATH_INPUT("media0_in", SST_TASK_MMX, SST_SWM_IN_MEDIA0, sst_generic_modules_event),
+       SST_PATH_INPUT("media1_in", SST_TASK_MMX, SST_SWM_IN_MEDIA1, NULL),
+       SST_PATH_INPUT("media2_in", SST_TASK_MMX, SST_SWM_IN_MEDIA2, sst_set_media_path),
+       SST_PATH_INPUT("media3_in", SST_TASK_MMX, SST_SWM_IN_MEDIA3, NULL),
+       SST_PATH_OUTPUT("media0_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA0, sst_set_media_path),
+       SST_PATH_OUTPUT("media1_out", SST_TASK_MMX, SST_SWM_OUT_MEDIA1, sst_set_media_path),
+
+       /* SBA PCM Paths */
+       SST_PATH_INPUT("pcm0_in", SST_TASK_SBA, SST_SWM_IN_PCM0, sst_set_media_path),
+       SST_PATH_INPUT("pcm1_in", SST_TASK_SBA, SST_SWM_IN_PCM1, sst_set_media_path),
+       SST_PATH_OUTPUT("pcm0_out", SST_TASK_SBA, SST_SWM_OUT_PCM0, sst_set_media_path),
+       SST_PATH_OUTPUT("pcm1_out", SST_TASK_SBA, SST_SWM_OUT_PCM1, sst_set_media_path),
+       SST_PATH_OUTPUT("pcm2_out", SST_TASK_SBA, SST_SWM_OUT_PCM2, sst_set_media_path),
+
+       /* SBA Loops */
+       SST_PATH_INPUT("sprot_loop_in", SST_TASK_SBA, SST_SWM_IN_SPROT_LOOP, NULL),
+       SST_PATH_INPUT("media_loop1_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP1, NULL),
+       SST_PATH_INPUT("media_loop2_in", SST_TASK_SBA, SST_SWM_IN_MEDIA_LOOP2, NULL),
+       SST_PATH_MEDIA_LOOP_OUTPUT("sprot_loop_out", SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP, SST_FMT_MONO, sst_set_media_loop),
+       SST_PATH_MEDIA_LOOP_OUTPUT("media_loop1_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1, SST_FMT_MONO, sst_set_media_loop),
+       SST_PATH_MEDIA_LOOP_OUTPUT("media_loop2_out", SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2, SST_FMT_STEREO, sst_set_media_loop),
+
+       /* Media Mixers */
+       SST_SWM_MIXER("media0_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA0,
+                     sst_mix_media0_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("media1_out mix 0", SND_SOC_NOPM, SST_TASK_MMX, SST_SWM_OUT_MEDIA1,
+                     sst_mix_media1_controls, sst_swm_mixer_event),
+
+       /* SBA PCM mixers */
+       SST_SWM_MIXER("pcm0_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM0,
+                     sst_mix_pcm0_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("pcm1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM1,
+                     sst_mix_pcm1_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("pcm2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_PCM2,
+                     sst_mix_pcm2_controls, sst_swm_mixer_event),
+
+       /* SBA Loop mixers */
+       SST_SWM_MIXER("sprot_loop_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_SPROT_LOOP,
+                     sst_mix_sprot_l0_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("media_loop1_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP1,
+                     sst_mix_media_l1_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("media_loop2_out mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_MEDIA_LOOP2,
+                     sst_mix_media_l2_controls, sst_swm_mixer_event),
+
+       /* SBA Backend mixers */
+       SST_SWM_MIXER("codec_out0 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC0,
+                     sst_mix_codec0_controls, sst_swm_mixer_event),
+       SST_SWM_MIXER("codec_out1 mix 0", SND_SOC_NOPM, SST_TASK_SBA, SST_SWM_OUT_CODEC1,
+                     sst_mix_codec1_controls, sst_swm_mixer_event),
+};
+
+static const struct snd_soc_dapm_route intercon[] = {
+       {"media0_in", NULL, "Compress Playback"},
+       {"media1_in", NULL, "Headset Playback"},
+       {"media2_in", NULL, "pcm0_out"},
+
+       {"media0_out mix 0", "media0_in Switch", "media0_in"},
+       {"media0_out mix 0", "media1_in Switch", "media1_in"},
+       {"media0_out mix 0", "media2_in Switch", "media2_in"},
+       {"media0_out mix 0", "media3_in Switch", "media3_in"},
+       {"media1_out mix 0", "media0_in Switch", "media0_in"},
+       {"media1_out mix 0", "media1_in Switch", "media1_in"},
+       {"media1_out mix 0", "media2_in Switch", "media2_in"},
+       {"media1_out mix 0", "media3_in Switch", "media3_in"},
+
+       {"media0_out", NULL, "media0_out mix 0"},
+       {"media1_out", NULL, "media1_out mix 0"},
+       {"pcm0_in", NULL, "media0_out"},
+       {"pcm1_in", NULL, "media1_out"},
+
+       {"Headset Capture", NULL, "pcm1_out"},
+       {"Headset Capture", NULL, "pcm2_out"},
+       {"pcm0_out", NULL, "pcm0_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("pcm0_out mix 0"),
+       {"pcm1_out", NULL, "pcm1_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("pcm1_out mix 0"),
+       {"pcm2_out", NULL, "pcm2_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("pcm2_out mix 0"),
+
+       {"media_loop1_in", NULL, "media_loop1_out"},
+       {"media_loop1_out", NULL, "media_loop1_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("media_loop1_out mix 0"),
+       {"media_loop2_in", NULL, "media_loop2_out"},
+       {"media_loop2_out", NULL, "media_loop2_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("media_loop2_out mix 0"),
+       {"sprot_loop_in", NULL, "sprot_loop_out"},
+       {"sprot_loop_out", NULL, "sprot_loop_out mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("sprot_loop_out mix 0"),
+
+       {"codec_out0", NULL, "codec_out0 mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("codec_out0 mix 0"),
+       {"codec_out1", NULL, "codec_out1 mix 0"},
+       SST_SBA_MIXER_GRAPH_MAP("codec_out1 mix 0"),
+
+};
+static const char * const slot_names[] = {
+       "none",
+       "slot 0", "slot 1", "slot 2", "slot 3",
+       "slot 4", "slot 5", "slot 6", "slot 7", /* not supported by FW */
+};
+
+static const char * const channel_names[] = {
+       "none",
+       "codec_out0_0", "codec_out0_1", "codec_out1_0", "codec_out1_1",
+       "codec_out2_0", "codec_out2_1", "codec_out3_0", "codec_out3_1", /* not supported by FW */
+};
+
+#define SST_INTERLEAVER(xpname, slot_name, slotno) \
+       SST_SSP_SLOT_CTL(xpname, "tx interleaver", slot_name, slotno, true, \
+                        channel_names, sst_slot_get, sst_slot_put)
+
+#define SST_DEINTERLEAVER(xpname, channel_name, channel_no) \
+       SST_SSP_SLOT_CTL(xpname, "rx deinterleaver", channel_name, channel_no, false, \
+                        slot_names, sst_slot_get, sst_slot_put)
+
+static const struct snd_kcontrol_new sst_slot_controls[] = {
+       SST_INTERLEAVER("codec_out", "slot 0", 0),
+       SST_INTERLEAVER("codec_out", "slot 1", 1),
+       SST_INTERLEAVER("codec_out", "slot 2", 2),
+       SST_INTERLEAVER("codec_out", "slot 3", 3),
+       SST_DEINTERLEAVER("codec_in", "codec_in0_0", 0),
+       SST_DEINTERLEAVER("codec_in", "codec_in0_1", 1),
+       SST_DEINTERLEAVER("codec_in", "codec_in1_0", 2),
+       SST_DEINTERLEAVER("codec_in", "codec_in1_1", 3),
+};
+
+/* Gain helper with min/max set */
+#define SST_GAIN(name, path_id, task_id, instance, gain_var)                           \
+       SST_GAIN_KCONTROLS(name, "Gain", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE,        \
+               SST_GAIN_TC_MIN, SST_GAIN_TC_MAX,                                       \
+               sst_gain_get, sst_gain_put,                                             \
+               SST_MODULE_ID_GAIN_CELL, path_id, instance, task_id,                    \
+               sst_gain_tlv_common, gain_var)
+
+#define SST_VOLUME(name, path_id, task_id, instance, gain_var)                         \
+       SST_GAIN_KCONTROLS(name, "Volume", SST_GAIN_MIN_VALUE, SST_GAIN_MAX_VALUE,      \
+               SST_GAIN_TC_MIN, SST_GAIN_TC_MAX,                                       \
+               sst_gain_get, sst_gain_put,                                             \
+               SST_MODULE_ID_VOLUME, path_id, instance, task_id,                       \
+               sst_gain_tlv_common, gain_var)
+
+static struct sst_gain_value sst_gains[];
+
+static const struct snd_kcontrol_new sst_gain_controls[] = {
+       SST_GAIN("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[0]),
+       SST_GAIN("media1_in", SST_PATH_INDEX_MEDIA1_IN, SST_TASK_MMX, 0, &sst_gains[1]),
+       SST_GAIN("media2_in", SST_PATH_INDEX_MEDIA2_IN, SST_TASK_MMX, 0, &sst_gains[2]),
+       SST_GAIN("media3_in", SST_PATH_INDEX_MEDIA3_IN, SST_TASK_MMX, 0, &sst_gains[3]),
+
+       SST_GAIN("pcm0_in", SST_PATH_INDEX_PCM0_IN, SST_TASK_SBA, 0, &sst_gains[4]),
+       SST_GAIN("pcm1_in", SST_PATH_INDEX_PCM1_IN, SST_TASK_SBA, 0, &sst_gains[5]),
+       SST_GAIN("pcm1_out", SST_PATH_INDEX_PCM1_OUT, SST_TASK_SBA, 0, &sst_gains[6]),
+       SST_GAIN("pcm2_out", SST_PATH_INDEX_PCM2_OUT, SST_TASK_SBA, 0, &sst_gains[7]),
+
+       SST_GAIN("codec_in0", SST_PATH_INDEX_CODEC_IN0, SST_TASK_SBA, 0, &sst_gains[8]),
+       SST_GAIN("codec_in1", SST_PATH_INDEX_CODEC_IN1, SST_TASK_SBA, 0, &sst_gains[9]),
+       SST_GAIN("codec_out0", SST_PATH_INDEX_CODEC_OUT0, SST_TASK_SBA, 0, &sst_gains[10]),
+       SST_GAIN("codec_out1", SST_PATH_INDEX_CODEC_OUT1, SST_TASK_SBA, 0, &sst_gains[11]),
+       SST_GAIN("media_loop1_out", SST_PATH_INDEX_MEDIA_LOOP1_OUT, SST_TASK_SBA, 0, &sst_gains[12]),
+       SST_GAIN("media_loop2_out", SST_PATH_INDEX_MEDIA_LOOP2_OUT, SST_TASK_SBA, 0, &sst_gains[13]),
+       SST_GAIN("sprot_loop_out", SST_PATH_INDEX_SPROT_LOOP_OUT, SST_TASK_SBA, 0, &sst_gains[14]),
+       SST_VOLUME("media0_in", SST_PATH_INDEX_MEDIA0_IN, SST_TASK_MMX, 0, &sst_gains[15]),
+};
+
+#define SST_GAIN_NUM_CONTROLS 3
+/* the SST_GAIN macro above will create three alsa controls for each
+ * instance invoked, gain, mute and ramp duration, which use the same gain
+ * cell sst_gain to keep track of data
+ * To calculate number of gain cell instances we need to device by 3 in
+ * below caulcation for gain cell memory.
+ * This gets rid of static number and issues while adding new controls
+ */
+static struct sst_gain_value sst_gains[ARRAY_SIZE(sst_gain_controls)/SST_GAIN_NUM_CONTROLS];
+
 static const struct snd_kcontrol_new sst_algo_controls[] = {
        SST_ALGO_KCONTROL_BYTES("media_loop1_out", "fir", 272, SST_MODULE_ID_FIR_24,
                 SST_PATH_INDEX_MEDIA_LOOP1_OUT, 0, SST_TASK_SBA, SBA_VB_SET_FIR),
@@ -198,21 +1143,280 @@ static int sst_algo_control_init(struct device *dev)
        return 0;
 }
 
-int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
+static bool is_sst_dapm_widget(struct snd_soc_dapm_widget *w)
+{
+       switch (w->id) {
+       case snd_soc_dapm_pga:
+       case snd_soc_dapm_aif_in:
+       case snd_soc_dapm_aif_out:
+       case snd_soc_dapm_input:
+       case snd_soc_dapm_output:
+       case snd_soc_dapm_mixer:
+               return true;
+       default:
+               return false;
+       }
+}
+
+/**
+ * sst_send_pipe_gains - send gains for the front-end DAIs
+ *
+ * The gains in the pipes connected to the front-ends are muted/unmuted
+ * automatically via the digital_mute() DAPM callback. This function sends the
+ * gains for the front-end pipes.
+ */
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute)
+{
+       struct sst_data *drv = snd_soc_dai_get_drvdata(dai);
+       struct snd_soc_dapm_widget *w;
+       struct snd_soc_dapm_path *p = NULL;
+
+       dev_dbg(dai->dev, "enter, dai-name=%s dir=%d\n", dai->name, stream);
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               dev_dbg(dai->dev, "Stream name=%s\n",
+                               dai->playback_widget->name);
+               w = dai->playback_widget;
+               list_for_each_entry(p, &w->sinks, list_source) {
+                       if (p->connected && !p->connected(w, p->sink))
+                               continue;
+
+                       if (p->connect && p->sink->power &&
+                                       is_sst_dapm_widget(p->sink)) {
+                               struct sst_ids *ids = p->sink->priv;
+
+                               dev_dbg(dai->dev, "send gains for widget=%s\n",
+                                               p->sink->name);
+                               mutex_lock(&drv->lock);
+                               sst_set_pipe_gain(ids, drv, mute);
+                               mutex_unlock(&drv->lock);
+                       }
+               }
+       } else {
+               dev_dbg(dai->dev, "Stream name=%s\n",
+                               dai->capture_widget->name);
+               w = dai->capture_widget;
+               list_for_each_entry(p, &w->sources, list_sink) {
+                       if (p->connected && !p->connected(w, p->sink))
+                               continue;
+
+                       if (p->connect &&  p->source->power &&
+                                       is_sst_dapm_widget(p->source)) {
+                               struct sst_ids *ids = p->source->priv;
+
+                               dev_dbg(dai->dev, "send gain for widget=%s\n",
+                                               p->source->name);
+                               mutex_lock(&drv->lock);
+                               sst_set_pipe_gain(ids, drv, mute);
+                               mutex_unlock(&drv->lock);
+                       }
+               }
+       }
+       return 0;
+}
+
+/**
+ * sst_fill_module_list - populate the list of modules/gains for a pipe
+ *
+ *
+ * Fills the widget pointer in the kcontrol private data, and also fills the
+ * kcontrol pointer in the widget private data.
+ *
+ * Widget pointer is used to send the algo/gain in the .put() handler if the
+ * widget is powerd on.
+ *
+ * Kcontrol pointer is used to send the algo/gain in the widget power ON/OFF
+ * event handler. Each widget (pipe) has multiple algos stored in the algo_list.
+ */
+static int sst_fill_module_list(struct snd_kcontrol *kctl,
+        struct snd_soc_dapm_widget *w, int type)
 {
+       struct sst_module *module = NULL;
+       struct snd_soc_component *c = snd_soc_dapm_to_component(w->dapm);
+       struct sst_ids *ids = w->priv;
        int ret = 0;
+
+       module = devm_kzalloc(c->dev, sizeof(*module), GFP_KERNEL);
+       if (!module)
+               return -ENOMEM;
+
+       if (type == SST_MODULE_GAIN) {
+               struct sst_gain_mixer_control *mc = (void *)kctl->private_value;
+
+               mc->w = w;
+               module->kctl = kctl;
+               list_add_tail(&module->node, &ids->gain_list);
+       } else if (type == SST_MODULE_ALGO) {
+               struct sst_algo_control *bc = (void *)kctl->private_value;
+
+               bc->w = w;
+               module->kctl = kctl;
+               list_add_tail(&module->node, &ids->algo_list);
+       } else {
+               dev_err(c->dev, "invoked for unknown type %d module %s",
+                               type, kctl->id.name);
+               ret = -EINVAL;
+       }
+
+       return ret;
+}
+
+/**
+ * sst_fill_widget_module_info - fill list of gains/algos for the pipe
+ * @widget:    pipe modelled as a DAPM widget
+ *
+ * Fill the list of gains/algos for the widget by looking at all the card
+ * controls and comparing the name of the widget with the first part of control
+ * name. First part of control name contains the pipe name (widget name).
+ */
+static int sst_fill_widget_module_info(struct snd_soc_dapm_widget *w,
+       struct snd_soc_platform *platform)
+{
+       struct snd_kcontrol *kctl;
+       int index, ret = 0;
+       struct snd_card *card = platform->component.card->snd_card;
+       char *idx;
+
+       down_read(&card->controls_rwsem);
+
+       list_for_each_entry(kctl, &card->controls, list) {
+               idx = strstr(kctl->id.name, " ");
+               if (idx == NULL)
+                       continue;
+               index  = strlen(kctl->id.name) - strlen(idx);
+
+               if (strstr(kctl->id.name, "Volume") &&
+                   !strncmp(kctl->id.name, w->name, index))
+                       ret = sst_fill_module_list(kctl, w, SST_MODULE_GAIN);
+
+               else if (strstr(kctl->id.name, "params") &&
+                        !strncmp(kctl->id.name, w->name, index))
+                       ret = sst_fill_module_list(kctl, w, SST_MODULE_ALGO);
+
+               else if (strstr(kctl->id.name, "Switch") &&
+                        !strncmp(kctl->id.name, w->name, index) &&
+                        strstr(kctl->id.name, "Gain")) {
+                       struct sst_gain_mixer_control *mc =
+                                               (void *)kctl->private_value;
+
+                       mc->w = w;
+
+               } else if (strstr(kctl->id.name, "interleaver") &&
+                        !strncmp(kctl->id.name, w->name, index)) {
+                       struct sst_enum *e = (void *)kctl->private_value;
+
+                       e->w = w;
+
+               } else if (strstr(kctl->id.name, "deinterleaver") &&
+                        !strncmp(kctl->id.name, w->name, index)) {
+
+                       struct sst_enum *e = (void *)kctl->private_value;
+
+                       e->w = w;
+               }
+
+               if (ret < 0) {
+                       up_read(&card->controls_rwsem);
+                       return ret;
+               }
+       }
+
+       up_read(&card->controls_rwsem);
+       return 0;
+}
+
+/**
+ * sst_fill_linked_widgets - fill the parent pointer for the linked widget
+ */
+static void sst_fill_linked_widgets(struct snd_soc_platform *platform,
+                                               struct sst_ids *ids)
+{
+       struct snd_soc_dapm_widget *w;
+       unsigned int len = strlen(ids->parent_wname);
+
+       list_for_each_entry(w, &platform->component.card->widgets, list) {
+               if (!strncmp(ids->parent_wname, w->name, len)) {
+                       ids->parent_w = w;
+                       break;
+               }
+       }
+}
+
+/**
+ * sst_map_modules_to_pipe - fill algo/gains list for all pipes
+ */
+static int sst_map_modules_to_pipe(struct snd_soc_platform *platform)
+{
+       struct snd_soc_dapm_widget *w;
+       int ret = 0;
+
+       list_for_each_entry(w, &platform->component.card->widgets, list) {
+               if (is_sst_dapm_widget(w) && (w->priv)) {
+                       struct sst_ids *ids = w->priv;
+
+                       dev_dbg(platform->dev, "widget type=%d name=%s\n",
+                                       w->id, w->name);
+                       INIT_LIST_HEAD(&ids->algo_list);
+                       INIT_LIST_HEAD(&ids->gain_list);
+                       ret = sst_fill_widget_module_info(w, platform);
+
+                       if (ret < 0)
+                               return ret;
+
+                       /* fill linked widgets */
+                       if (ids->parent_wname !=  NULL)
+                               sst_fill_linked_widgets(platform, ids);
+               }
+       }
+       return 0;
+}
+
+int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform)
+{
+       int i, ret = 0;
+       struct snd_soc_dapm_context *dapm =
+                       snd_soc_component_get_dapm(&platform->component);
        struct sst_data *drv = snd_soc_platform_get_drvdata(platform);
+       unsigned int gains = ARRAY_SIZE(sst_gain_controls)/3;
 
        drv->byte_stream = devm_kzalloc(platform->dev,
                                        SST_MAX_BIN_BYTES, GFP_KERNEL);
        if (!drv->byte_stream)
                return -ENOMEM;
 
-       /*Initialize algo control params*/
+       snd_soc_dapm_new_controls(dapm, sst_dapm_widgets,
+                       ARRAY_SIZE(sst_dapm_widgets));
+       snd_soc_dapm_add_routes(dapm, intercon,
+                       ARRAY_SIZE(intercon));
+       snd_soc_dapm_new_widgets(dapm->card);
+
+       for (i = 0; i < gains; i++) {
+               sst_gains[i].mute = SST_GAIN_MUTE_DEFAULT;
+               sst_gains[i].l_gain = SST_GAIN_VOLUME_DEFAULT;
+               sst_gains[i].r_gain = SST_GAIN_VOLUME_DEFAULT;
+               sst_gains[i].ramp_duration = SST_GAIN_RAMP_DURATION_DEFAULT;
+       }
+
+       ret = snd_soc_add_platform_controls(platform, sst_gain_controls,
+                       ARRAY_SIZE(sst_gain_controls));
+       if (ret)
+               return ret;
+
+       /* Initialize algo control params */
        ret = sst_algo_control_init(platform->dev);
        if (ret)
                return ret;
        ret = snd_soc_add_platform_controls(platform, sst_algo_controls,
                        ARRAY_SIZE(sst_algo_controls));
+       if (ret)
+               return ret;
+
+       ret = snd_soc_add_platform_controls(platform, sst_slot_controls,
+                       ARRAY_SIZE(sst_slot_controls));
+       if (ret)
+               return ret;
+
+       ret = sst_map_modules_to_pipe(platform);
+
        return ret;
 }
index a73e894..dfebfdd 100644 (file)
@@ -23,6 +23,9 @@
 #ifndef __SST_ATOM_CONTROLS_H__
 #define __SST_ATOM_CONTROLS_H__
 
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
 enum {
        MERR_DPCM_AUDIO = 0,
        MERR_DPCM_COMPR,
@@ -360,16 +363,416 @@ struct sst_dsp_header {
 struct sst_cmd_generic {
        struct sst_dsp_header header;
 } __packed;
+
+struct swm_input_ids {
+       struct sst_destination_id input_id;
+} __packed;
+
+struct sst_cmd_set_swm {
+       struct sst_dsp_header header;
+       struct sst_destination_id output_id;
+       u16    switch_state;
+       u16    nb_inputs;
+       struct swm_input_ids input[SST_CMD_SWM_MAX_INPUTS];
+} __packed;
+
+struct sst_cmd_set_media_path {
+       struct sst_dsp_header header;
+       u16    switch_state;
+} __packed;
+
+struct pcm_cfg {
+               u8 s_length:2;
+               u8 rate:3;
+               u8 format:3;
+} __packed;
+
+struct sst_cmd_set_speech_path {
+       struct sst_dsp_header header;
+       u16    switch_state;
+       struct {
+               u16 rsvd:8;
+               struct pcm_cfg cfg;
+       } config;
+} __packed;
+
+struct gain_cell {
+       struct sst_destination_id dest;
+       s16 cell_gain_left;
+       s16 cell_gain_right;
+       u16 gain_time_constant;
+} __packed;
+
+#define NUM_GAIN_CELLS 1
+struct sst_cmd_set_gain_dual {
+       struct sst_dsp_header header;
+       u16    gain_cell_num;
+       struct gain_cell cell_gains[NUM_GAIN_CELLS];
+} __packed;
 struct sst_cmd_set_params {
        struct sst_destination_id dst;
        u16 command_id;
        char params[0];
 } __packed;
+
+
+struct sst_cmd_sba_vb_start {
+       struct sst_dsp_header header;
+} __packed;
+
+union sba_media_loop_params {
+       struct {
+               u16 rsvd:8;
+               struct pcm_cfg cfg;
+       } part;
+       u16 full;
+} __packed;
+
+struct sst_cmd_sba_set_media_loop_map {
+       struct  sst_dsp_header header;
+       u16     switch_state;
+       union   sba_media_loop_params param;
+       u16     map;
+} __packed;
+
+struct sst_cmd_tone_stop {
+       struct  sst_dsp_header header;
+       u16     switch_state;
+} __packed;
+
+enum sst_ssp_mode {
+       SSP_MODE_MASTER = 0,
+       SSP_MODE_SLAVE = 1,
+};
+
+enum sst_ssp_pcm_mode {
+       SSP_PCM_MODE_NORMAL = 0,
+       SSP_PCM_MODE_NETWORK = 1,
+};
+
+enum sst_ssp_duplex {
+       SSP_DUPLEX = 0,
+       SSP_RX = 1,
+       SSP_TX = 2,
+};
+
+enum sst_ssp_fs_frequency {
+       SSP_FS_8_KHZ = 0,
+       SSP_FS_16_KHZ = 1,
+       SSP_FS_44_1_KHZ = 2,
+       SSP_FS_48_KHZ = 3,
+};
+
+enum sst_ssp_fs_polarity {
+       SSP_FS_ACTIVE_LOW = 0,
+       SSP_FS_ACTIVE_HIGH = 1,
+};
+
+enum sst_ssp_protocol {
+       SSP_MODE_PCM = 0,
+       SSP_MODE_I2S = 1,
+};
+
+enum sst_ssp_port_id {
+       SSP_MODEM = 0,
+       SSP_BT = 1,
+       SSP_FM = 2,
+       SSP_CODEC = 3,
+};
+
+struct sst_cmd_sba_hw_set_ssp {
+       struct sst_dsp_header header;
+       u16 selection;                  /* 0:SSP0(def), 1:SSP1, 2:SSP2 */
+
+       u16 switch_state;
+
+       u16 nb_bits_per_slots:6;        /* 0-32 bits, 24 (def) */
+       u16 nb_slots:4;                 /* 0-8: slots per frame  */
+       u16 mode:3;                     /* 0:Master, 1: Slave  */
+       u16 duplex:3;
+
+       u16 active_tx_slot_map:8;       /* Bit map, 0:off, 1:on */
+       u16 reserved1:8;
+
+       u16 active_rx_slot_map:8;       /* Bit map 0: Off, 1:On */
+       u16 reserved2:8;
+
+       u16 frame_sync_frequency;
+
+       u16 frame_sync_polarity:8;
+       u16 data_polarity:8;
+
+       u16 frame_sync_width;           /* 1 to N clocks */
+       u16 ssp_protocol:8;
+       u16 start_delay:8;              /* Start delay in terms of clock ticks */
+} __packed;
+
+#define SST_MAX_TDM_SLOTS 8
+
+struct sst_param_sba_ssp_slot_map {
+       struct sst_dsp_header header;
+
+       u16 param_id;
+       u16 param_len;
+       u16 ssp_index;
+
+       u8 rx_slot_map[SST_MAX_TDM_SLOTS];
+       u8 tx_slot_map[SST_MAX_TDM_SLOTS];
+} __packed;
+
+enum {
+       SST_PROBE_EXTRACTOR = 0,
+       SST_PROBE_INJECTOR = 1,
+};
+
+/**** widget defines *****/
+
+#define SST_MODULE_GAIN 1
+#define SST_MODULE_ALGO 2
+
+#define SST_FMT_MONO 0
+#define SST_FMT_STEREO 3
+
+/* physical SSP numbers */
+enum {
+       SST_SSP0 = 0,
+       SST_SSP1,
+       SST_SSP2,
+       SST_SSP_LAST = SST_SSP2,
+};
+
+#define SST_NUM_SSPS           (SST_SSP_LAST + 1)      /* physical SSPs */
+#define SST_MAX_SSP_MUX                2                       /* single SSP muxed between pipes */
+#define SST_MAX_SSP_DOMAINS    2                       /* domains present in each pipe */
+
+struct sst_module {
+       struct snd_kcontrol *kctl;
+       struct list_head node;
+};
+
+struct sst_ssp_config {
+       u8 ssp_id;
+       u8 bits_per_slot;
+       u8 slots;
+       u8 ssp_mode;
+       u8 pcm_mode;
+       u8 duplex;
+       u8 ssp_protocol;
+       u8 fs_frequency;
+       u8 active_slot_map;
+       u8 start_delay;
+       u16 fs_width;
+};
+
+struct sst_ssp_cfg {
+       const u8 ssp_number;
+       const int *mux_shift;
+       const int (*domain_shift)[SST_MAX_SSP_MUX];
+       const struct sst_ssp_config (*ssp_config)[SST_MAX_SSP_MUX][SST_MAX_SSP_DOMAINS];
+};
+
+struct sst_ids {
+       u16 location_id;
+       u16 module_id;
+       u8  task_id;
+       u8  format;
+       u8  reg;
+       const char *parent_wname;
+       struct snd_soc_dapm_widget *parent_w;
+       struct list_head algo_list;
+       struct list_head gain_list;
+       const struct sst_pcm_format *pcm_fmt;
+};
+
+
+#define SST_AIF_IN(wname, wevent)                                                      \
+{      .id = snd_soc_dapm_aif_in, .name = wname, .sname = NULL,                        \
+       .reg = SND_SOC_NOPM, .shift = 0,                                        \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }            \
+}
+
+#define SST_AIF_OUT(wname, wevent)                                                     \
+{      .id = snd_soc_dapm_aif_out, .name = wname, .sname = NULL,                       \
+       .reg = SND_SOC_NOPM, .shift = 0,                                                \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }            \
+}
+
+#define SST_INPUT(wname, wevent)                                                       \
+{      .id = snd_soc_dapm_input, .name = wname, .sname = NULL,                         \
+       .reg = SND_SOC_NOPM, .shift = 0,                                                \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }            \
+}
+
+#define SST_OUTPUT(wname, wevent)                                                      \
+{      .id = snd_soc_dapm_output, .name = wname, .sname = NULL,                        \
+       .reg = SND_SOC_NOPM, .shift = 0,                                                \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .task_id = 0, .location_id = 0 }            \
+}
+
+#define SST_DAPM_OUTPUT(wname, wloc_id, wtask_id, wformat, wevent)                      \
+{      .id = snd_soc_dapm_output, .name = wname, .sname = NULL,                        \
+       .reg = SND_SOC_NOPM, .shift = 0,                                                \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD,   \
+       .priv = (void *)&(struct sst_ids) { .location_id = wloc_id, .task_id = wtask_id,\
+                                               .pcm_fmt = wformat, }                   \
+}
+
+#define SST_PATH(wname, wtask, wloc_id, wevent, wflags)                                        \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,         \
+       .kcontrol_news = NULL, .num_kcontrols = 0,                              \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = wflags,                                         \
+       .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id, } \
+}
+
+#define SST_LINKED_PATH(wname, wtask, wloc_id, linked_wname, wevent, wflags)           \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,         \
+       .kcontrol_news = NULL, .num_kcontrols = 0,                              \
+       .on_val = 1, .off_val = 0,                                                      \
+       .event = wevent, .event_flags = wflags,                                         \
+       .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id,   \
+                                       .parent_wname = linked_wname}                   \
+}
+
+#define SST_PATH_MEDIA_LOOP(wname, wtask, wloc_id, wformat, wevent, wflags)             \
+{      .id = snd_soc_dapm_pga, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,         \
+       .kcontrol_news = NULL, .num_kcontrols = 0,                         \
+       .event = wevent, .event_flags = wflags,                                         \
+       .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id,   \
+                                           .format = wformat,}                         \
+}
+
+/* output is triggered before input */
+#define SST_PATH_INPUT(name, task_id, loc_id, event)                                   \
+       SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_LINKED_INPUT(name, task_id, loc_id, linked_wname, event)              \
+       SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event,                     \
+                                       SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD)
+
+#define SST_PATH_OUTPUT(name, task_id, loc_id, event)                                  \
+       SST_PATH(name, task_id, loc_id, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_LINKED_OUTPUT(name, task_id, loc_id, linked_wname, event)             \
+       SST_LINKED_PATH(name, task_id, loc_id, linked_wname, event,                     \
+                                       SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+#define SST_PATH_MEDIA_LOOP_OUTPUT(name, task_id, loc_id, format, event)               \
+       SST_PATH_MEDIA_LOOP(name, task_id, loc_id, format, event, SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD)
+
+
+#define SST_SWM_MIXER(wname, wreg, wtask, wloc_id, wcontrols, wevent)                  \
+{      .id = snd_soc_dapm_mixer, .name = wname, .reg = SND_SOC_NOPM, .shift = 0,       \
+       .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols),\
+       .event = wevent, .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD |  \
+                                       SND_SOC_DAPM_POST_REG,                          \
+       .priv = (void *)&(struct sst_ids) { .task_id = wtask, .location_id = wloc_id,   \
+                                           .reg = wreg }                               \
+}
+
+enum sst_gain_kcontrol_type {
+       SST_GAIN_TLV,
+       SST_GAIN_MUTE,
+       SST_GAIN_RAMP_DURATION,
+};
+
+struct sst_gain_mixer_control {
+       bool stereo;
+       enum sst_gain_kcontrol_type type;
+       struct sst_gain_value *gain_val;
+       int max;
+       int min;
+       u16 instance_id;
+       u16 module_id;
+       u16 pipe_id;
+       u16 task_id;
+       char pname[44];
+       struct snd_soc_dapm_widget *w;
+};
+
+struct sst_gain_value {
+       u16 ramp_duration;
+       s16 l_gain;
+       s16 r_gain;
+       bool mute;
+};
+#define SST_GAIN_VOLUME_DEFAULT                (-1440)
+#define SST_GAIN_RAMP_DURATION_DEFAULT 5 /* timeconstant */
+#define SST_GAIN_MUTE_DEFAULT          true
+
+#define SST_GAIN_KCONTROL_TLV(xname, xhandler_get, xhandler_put, \
+                             xmod, xpipe, xinstance, xtask, tlv_array, xgain_val, \
+                             xmin, xmax, xpname) \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
+                 SNDRV_CTL_ELEM_ACCESS_READWRITE, \
+       .tlv.p = (tlv_array), \
+       .info = sst_gain_ctl_info,\
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+       { .stereo = true, .max = xmax, .min = xmin, .type = SST_GAIN_TLV, \
+         .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+         .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
+
+#define SST_GAIN_KCONTROL_INT(xname, xhandler_get, xhandler_put, \
+                             xmod, xpipe, xinstance, xtask, xtype, xgain_val, \
+                             xmin, xmax, xpname) \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = sst_gain_ctl_info, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+       { .stereo = false, .max = xmax, .min = xmin, .type = xtype, \
+         .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+         .instance_id = xinstance, .gain_val = xgain_val, .pname =  xpname}
+
+#define SST_GAIN_KCONTROL_BOOL(xname, xhandler_get, xhandler_put,\
+                              xmod, xpipe, xinstance, xtask, xgain_val, xpname) \
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_bool_ext, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct sst_gain_mixer_control) \
+       { .stereo = false, .type = SST_GAIN_MUTE, \
+         .module_id = xmod, .pipe_id = xpipe, .task_id = xtask,\
+         .instance_id = xinstance, .gain_val = xgain_val, .pname = xpname}
 #define SST_CONTROL_NAME(xpname, xmname, xinstance, xtype) \
        xpname " " xmname " " #xinstance " " xtype
 
 #define SST_COMBO_CONTROL_NAME(xpname, xmname, xinstance, xtype, xsubmodule) \
        xpname " " xmname " " #xinstance " " xtype " " xsubmodule
+
+/*
+ * 3 Controls for each Gain module
+ * e.g.        - pcm0_in Gain 0 Volume
+ *     - pcm0_in Gain 0 Ramp Delay
+ *     - pcm0_in Gain 0 Switch
+ */
+#define SST_GAIN_KCONTROLS(xpname, xmname, xmin_gain, xmax_gain, xmin_tc, xmax_tc, \
+                          xhandler_get, xhandler_put, \
+                          xmod, xpipe, xinstance, xtask, tlv_array, xgain_val) \
+       { SST_GAIN_KCONTROL_INT(SST_CONTROL_NAME(xpname, xmname, xinstance, "Ramp Delay"), \
+               xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, SST_GAIN_RAMP_DURATION, \
+               xgain_val, xmin_tc, xmax_tc, xpname) }, \
+       { SST_GAIN_KCONTROL_BOOL(SST_CONTROL_NAME(xpname, xmname, xinstance, "Switch"), \
+               xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, \
+               xgain_val, xpname) } ,\
+       { SST_GAIN_KCONTROL_TLV(SST_CONTROL_NAME(xpname, xmname, xinstance, "Volume"), \
+               xhandler_get, xhandler_put, xmod, xpipe, xinstance, xtask, tlv_array, \
+               xgain_val, xmin_gain, xmax_gain, xpname) }
+
+#define SST_GAIN_TC_MIN                5
+#define SST_GAIN_TC_MAX                5000
+#define SST_GAIN_MIN_VALUE     -1440 /* in 0.1 DB units */
+#define SST_GAIN_MAX_VALUE     360
+
 enum sst_algo_kcontrol_type {
        SST_ALGO_PARAMS,
        SST_ALGO_BYPASS,
@@ -439,4 +842,29 @@ struct sst_enum {
        struct snd_soc_dapm_widget *w;
 };
 
+/* only 4 slots/channels supported atm */
+#define SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts) \
+       (struct sst_enum){ .reg = s_ch_no, .tx = is_tx, .max = 4+1, .texts = xtexts, }
+
+#define SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name) \
+       xpname " " xmname " " s_ch_name
+
+#define SST_SSP_SLOT_CTL(xpname, xmname, s_ch_name, s_ch_no, is_tx, xtexts, xget, xput) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = SST_SLOT_CTL_NAME(xpname, xmname, s_ch_name), \
+       .info = sst_slot_enum_info, \
+       .get = xget, .put = xput, \
+       .private_value = (unsigned long)&SST_SSP_SLOT_ENUM(s_ch_no, is_tx, xtexts), \
+}
+
+#define SST_MUX_CTL_NAME(xpname, xinstance) \
+       xpname " " #xinstance
+
+#define SST_SSP_MUX_ENUM(xreg, xshift, xtexts) \
+       (struct soc_enum) SOC_ENUM_DOUBLE(xreg, xshift, xshift, ARRAY_SIZE(xtexts), xtexts)
+
+#define SST_SSP_MUX_CTL(xpname, xinstance, xreg, xshift, xtexts) \
+       SOC_DAPM_ENUM(SST_MUX_CTL_NAME(xpname, xinstance), \
+                         SST_SSP_MUX_ENUM(xreg, xshift, xtexts))
+
 #endif
index fc58876..5a9e567 100644 (file)
@@ -67,17 +67,12 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 {
        struct dma_block_info *block;
        struct sst_module *mod;
-       struct sst_module_data block_data;
        struct sst_module_template template;
        int count;
 
        memset(&template, 0, sizeof(template));
        template.id = module->type;
        template.entry = module->entry_point;
-       template.p.type = SST_MEM_DRAM;
-       template.p.data_type = SST_DATA_P;
-       template.s.type = SST_MEM_DRAM;
-       template.s.data_type = SST_DATA_S;
 
        mod = sst_module_new(fw, &template, NULL);
        if (mod == NULL)
@@ -94,19 +89,19 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 
                switch (block->type) {
                case SST_BYT_IRAM:
-                       block_data.offset = block->ram_offset +
+                       mod->offset = block->ram_offset +
                                            dsp->addr.iram_offset;
-                       block_data.type = SST_MEM_IRAM;
+                       mod->type = SST_MEM_IRAM;
                        break;
                case SST_BYT_DRAM:
-                       block_data.offset = block->ram_offset +
+                       mod->offset = block->ram_offset +
                                            dsp->addr.dram_offset;
-                       block_data.type = SST_MEM_DRAM;
+                       mod->type = SST_MEM_DRAM;
                        break;
                case SST_BYT_CACHE:
-                       block_data.offset = block->ram_offset +
+                       mod->offset = block->ram_offset +
                                            (dsp->addr.fw_ext - dsp->addr.lpe);
-                       block_data.type = SST_MEM_CACHE;
+                       mod->type = SST_MEM_CACHE;
                        break;
                default:
                        dev_err(dsp->dev, "wrong ram type 0x%x in block0x%x\n",
@@ -114,11 +109,10 @@ static int sst_byt_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
                        return -EINVAL;
                }
 
-               block_data.size = block->size;
-               block_data.data_type = SST_DATA_M;
-               block_data.data = (void *)block + sizeof(*block);
+               mod->size = block->size;
+               mod->data = (void *)block + sizeof(*block);
 
-               sst_module_insert_fixed_block(mod, &block_data);
+               sst_module_alloc_blocks(mod);
 
                block = (void *)block + sizeof(*block) + block->size;
        }
index ffb308b..b9da030 100644 (file)
@@ -26,6 +26,9 @@ struct sst_mem_block;
 struct sst_module;
 struct sst_fw;
 
+/* do we need to remove or keep */
+#define DSP_DRAM_ADDR_OFFSET           0x400000
+
 /*
  * DSP Operations exported by platform Audio DSP driver.
  */
@@ -33,6 +36,9 @@ struct sst_ops {
        /* DSP core boot / reset */
        void (*boot)(struct sst_dsp *);
        void (*reset)(struct sst_dsp *);
+       int (*wake)(struct sst_dsp *);
+       void (*sleep)(struct sst_dsp *);
+       void (*stall)(struct sst_dsp *);
 
        /* Shim IO */
        void (*write)(void __iomem *addr, u32 offset, u32 value);
@@ -67,6 +73,8 @@ struct sst_addr {
        u32 shim_offset;
        u32 iram_offset;
        u32 dram_offset;
+       u32 dsp_iram_offset;
+       u32 dsp_dram_offset;
        void __iomem *lpe;
        void __iomem *shim;
        void __iomem *pci_cfg;
@@ -83,15 +91,6 @@ struct sst_mailbox {
        size_t out_size;
 };
 
-/*
- * Audio DSP Firmware data types.
- */
-enum sst_data_type {
-       SST_DATA_M      = 0, /* module block data */
-       SST_DATA_P      = 1, /* peristant data (text, data) */
-       SST_DATA_S      = 2, /* scratch data (usually buffers) */
-};
-
 /*
  * Audio DSP memory block types.
  */
@@ -124,23 +123,6 @@ struct sst_fw {
        void *private;                  /* core doesn't touch this */
 };
 
-/*
- * Audio DSP Generic Module data.
- *
- * This is used to dsecribe any sections of persistent (text and data) and
- * scratch (buffers) of module data in ADSP memory space.
- */
-struct sst_module_data {
-
-       enum sst_mem_type type;         /* destination memory type */
-       enum sst_data_type data_type;   /* type of module data */
-
-       u32 size;               /* size in bytes */
-       int32_t offset;         /* offset in FW file */
-       u32 data_offset;        /* offset in ADSP memory space */
-       void *data;             /* module data */
-};
-
 /*
  * Audio DSP Generic Module Template.
  *
@@ -150,15 +132,52 @@ struct sst_module_data {
 struct sst_module_template {
        u32 id;
        u32 entry;                      /* entry point */
-       struct sst_module_data s;       /* scratch data */
-       struct sst_module_data p;       /* peristant data */
+       u32 scratch_size;
+       u32 persistent_size;
+};
+
+/*
+ * Block Allocator - Used to allocate blocks of DSP memory.
+ */
+struct sst_block_allocator {
+       u32 id;
+       u32 offset;
+       int size;
+       enum sst_mem_type type;
+};
+
+/*
+ * Runtime Module Instance - A module object can be instanciated multiple
+ * times within the DSP FW.
+ */
+struct sst_module_runtime {
+       struct sst_dsp *dsp;
+       int id;
+       struct sst_module *module;      /* parent module we belong too */
+
+       u32 persistent_offset;          /* private memory offset */
+       void *private;
+
+       struct list_head list;
+       struct list_head block_list;    /* list of blocks used */
+};
+
+/*
+ * Runtime Module Context - The runtime context must be manually stored by the
+ * driver prior to enter S3 and restored after leaving S3. This should really be
+ * part of the memory context saved by the enter D3 message IPC ???
+ */
+struct sst_module_runtime_context {
+       dma_addr_t dma_buffer;
+       u32 *buffer;
 };
 
 /*
  * Audio DSP Generic Module.
  *
  * Each Firmware file can consist of 1..N modules. A module can span multiple
- * ADSP memory blocks. The simplest FW will be a file with 1 module.
+ * ADSP memory blocks. The simplest FW will be a file with 1 module. A module
+ * can be instanciated multiple times in the DSP.
  */
 struct sst_module {
        struct sst_dsp *dsp;
@@ -167,10 +186,13 @@ struct sst_module {
        /* module configuration */
        u32 id;
        u32 entry;                      /* module entry point */
-       u32 offset;                     /* module offset in firmware file */
+       s32 offset;                     /* module offset in firmware file */
        u32 size;                       /* module size */
-       struct sst_module_data s;       /* scratch data */
-       struct sst_module_data p;       /* peristant data */
+       u32 scratch_size;               /* global scratch memory required */
+       u32 persistent_size;            /* private memory required */
+       enum sst_mem_type type;         /* destination memory type */
+       u32 data_offset;                /* offset in ADSP memory space */
+       void *data;                     /* module data */
 
        /* runtime */
        u32 usage_count;                /* can be unloaded if count == 0 */
@@ -180,6 +202,7 @@ struct sst_module {
        struct list_head block_list;    /* Module list of blocks in use */
        struct list_head list;          /* DSP list of modules */
        struct list_head list_fw;       /* FW list of modules */
+       struct list_head runtime_list;  /* list of runtime module objects*/
 };
 
 /*
@@ -208,7 +231,6 @@ struct sst_mem_block {
        struct sst_block_ops *ops;      /* block operations, if any */
 
        /* block status */
-       enum sst_data_type data_type;   /* data type held in this block */
        u32 bytes_used;                 /* bytes in use by modules */
        void *private;                  /* generic core does not touch this */
        int users;                      /* number of modules using this block */
@@ -253,6 +275,11 @@ struct sst_dsp {
        struct list_head module_list;
        struct list_head fw_list;
 
+       /* scratch buffer */
+       struct list_head scratch_block_list;
+       u32 scratch_offset;
+       u32 scratch_size;
+
        /* platform data */
        struct sst_pdata *pdata;
 
@@ -290,18 +317,33 @@ void sst_fw_unload(struct sst_fw *sst_fw);
 /* Create/Free firmware modules */
 struct sst_module *sst_module_new(struct sst_fw *sst_fw,
        struct sst_module_template *template, void *private);
-void sst_module_free(struct sst_module *sst_module);
-int sst_module_insert(struct sst_module *sst_module);
-int sst_module_remove(struct sst_module *sst_module);
-int sst_module_insert_fixed_block(struct sst_module *module,
-       struct sst_module_data *data);
+void sst_module_free(struct sst_module *module);
 struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id);
-
-/* allocate/free pesistent/scratch memory regions managed by drv */
-struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp);
-void sst_mem_block_free_scratch(struct sst_dsp *dsp,
-       struct sst_module *scratch);
-int sst_block_module_remove(struct sst_module *module);
+int sst_module_alloc_blocks(struct sst_module *module);
+int sst_module_free_blocks(struct sst_module *module);
+
+/* Create/Free firmware module runtime instances */
+struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
+       int id, void *private);
+void sst_module_runtime_free(struct sst_module_runtime *runtime);
+struct sst_module_runtime *sst_module_runtime_get_from_id(
+       struct sst_module *module, u32 id);
+int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
+       int offset);
+int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime);
+int sst_module_runtime_save(struct sst_module_runtime *runtime,
+       struct sst_module_runtime_context *context);
+int sst_module_runtime_restore(struct sst_module_runtime *runtime,
+       struct sst_module_runtime_context *context);
+
+/* generic block allocation */
+int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+       struct list_head *block_list);
+int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list);
+
+/* scratch allocation */
+int sst_block_alloc_scratch(struct sst_dsp *dsp);
+void sst_block_free_scratch(struct sst_dsp *dsp);
 
 /* Register the DSPs memory blocks - would be nice to read from ACPI */
 struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
@@ -309,4 +351,10 @@ struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
        void *private);
 void sst_mem_block_unregister_all(struct sst_dsp *dsp);
 
+/* Create/Free DMA resources */
+int sst_dma_new(struct sst_dsp *sst);
+void sst_dma_free(struct sst_dma *dma);
+
+u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
+       enum sst_mem_type type);
 #endif
index cd23060..86e4108 100644 (file)
@@ -245,6 +245,29 @@ int sst_dsp_boot(struct sst_dsp *sst)
 }
 EXPORT_SYMBOL_GPL(sst_dsp_boot);
 
+int sst_dsp_wake(struct sst_dsp *sst)
+{
+       if (sst->ops->wake)
+               return sst->ops->wake(sst);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_wake);
+
+void sst_dsp_sleep(struct sst_dsp *sst)
+{
+       if (sst->ops->sleep)
+               sst->ops->sleep(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_sleep);
+
+void sst_dsp_stall(struct sst_dsp *sst)
+{
+       if (sst->ops->stall)
+               sst->ops->stall(sst);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_stall);
+
 void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg)
 {
        sst_dsp_shim_write_unlocked(dsp, SST_IPCX, msg | SST_IPCX_BUSY);
@@ -352,6 +375,7 @@ struct sst_dsp *sst_dsp_new(struct device *dev,
        INIT_LIST_HEAD(&sst->free_block_list);
        INIT_LIST_HEAD(&sst->module_list);
        INIT_LIST_HEAD(&sst->fw_list);
+       INIT_LIST_HEAD(&sst->scratch_block_list);
 
        /* Initialise SST Audio DSP */
        if (sst->ops->init) {
@@ -366,6 +390,10 @@ struct sst_dsp *sst_dsp_new(struct device *dev,
        if (err)
                goto irq_err;
 
+       err = sst_dma_new(sst);
+       if (err)
+               dev_warn(dev, "sst_dma_new failed %d\n", err);
+
        return sst;
 
 irq_err:
@@ -381,6 +409,9 @@ void sst_dsp_free(struct sst_dsp *sst)
        free_irq(sst->irq, sst);
        if (sst->ops->free)
                sst->ops->free(sst);
+
+       if (sst->dma)
+               sst_dma_free(sst->dma);
 }
 EXPORT_SYMBOL_GPL(sst_dsp_free);
 
index 3165dfa..f291e32 100644 (file)
@@ -30,6 +30,9 @@
 #define SST_DMA_TYPE_DW                1
 #define SST_DMA_TYPE_MID       2
 
+/* autosuspend delay 5s*/
+#define SST_RUNTIME_SUSPEND_DELAY      (5 * 1000)
+
 /* SST Shim register map
  * The register naming can differ between products. Some products also
  * contain extra functionality.
 #define SST_VDRTCTL3           0xaC
 
 /* VDRTCTL0 */
-#define SST_VDRTCL0_APLLSE_MASK                1
-#define SST_VDRTCL0_DSRAMPGE_SHIFT     16
-#define SST_VDRTCL0_DSRAMPGE_MASK      (0xffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
-#define SST_VDRTCL0_ISRAMPGE_SHIFT     6
+#define SST_VDRTCL0_D3PGD              (1 << 0)
+#define SST_VDRTCL0_D3SRAMPGD          (1 << 1)
+#define SST_VDRTCL0_DSRAMPGE_SHIFT     12
+#define SST_VDRTCL0_DSRAMPGE_MASK      (0xfffff << SST_VDRTCL0_DSRAMPGE_SHIFT)
+#define SST_VDRTCL0_ISRAMPGE_SHIFT     2
 #define SST_VDRTCL0_ISRAMPGE_MASK      (0x3ff << SST_VDRTCL0_ISRAMPGE_SHIFT)
 
+/* VDRTCTL2 */
+#define SST_VDRTCL2_DCLCGE             (1 << 1)
+#define SST_VDRTCL2_DTCGE              (1 << 10)
+#define SST_VDRTCL2_APLLSE_MASK                (1 << 31)
+
 /* PMCS */
 #define SST_PMCS               0x84
 #define SST_PMCS_PS_MASK       0x3
@@ -245,6 +254,17 @@ void sst_memcpy_fromio_32(struct sst_dsp *sst,
 /* DSP reset & boot */
 void sst_dsp_reset(struct sst_dsp *sst);
 int sst_dsp_boot(struct sst_dsp *sst);
+int sst_dsp_wake(struct sst_dsp *sst);
+void sst_dsp_sleep(struct sst_dsp *sst);
+void sst_dsp_stall(struct sst_dsp *sst);
+
+/* DMA */
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id);
+void sst_dsp_dma_put_channel(struct sst_dsp *dsp);
+int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size);
+int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size);
 
 /* Msg IO */
 void sst_dsp_ipc_msg_tx(struct sst_dsp *dsp, u32 msg);
index 3bb43da..4a5bde9 100644 (file)
 #include <linux/dma-mapping.h>
 #include <linux/dmaengine.h>
 #include <linux/pci.h>
+#include <linux/acpi.h>
+
+/* supported DMA engine drivers */
+#include <linux/platform_data/dma-dw.h>
+#include <linux/dma/dw.h>
 
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include "sst-dsp.h"
 #include "sst-dsp-priv.h"
 
-static void block_module_remove(struct sst_module *module);
+#define SST_DMA_RESOURCES      2
+#define SST_DSP_DMA_MAX_BURST  0x3
+#define SST_HSW_BLOCK_ANY      0xffffffff
+
+#define SST_HSW_MASK_DMA_ADDR_DSP 0xfff00000
+
+struct sst_dma {
+       struct sst_dsp *sst;
+
+       struct dw_dma_chip *chip;
+
+       struct dma_async_tx_descriptor *desc;
+       struct dma_chan *ch;
+};
+
+static inline void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
+{
+       /* __iowrite32_copy use 32bit size values so divide by 4 */
+       __iowrite32_copy((void *)dest, src, bytes/4);
+}
+
+static void sst_dma_transfer_complete(void *arg)
+{
+       struct sst_dsp *sst = (struct sst_dsp *)arg;
+
+       dev_dbg(sst->dev, "DMA: callback\n");
+}
+
+static int sst_dsp_dma_copy(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size)
+{
+       struct dma_async_tx_descriptor *desc;
+       struct sst_dma *dma = sst->dma;
+
+       if (dma->ch == NULL) {
+               dev_err(sst->dev, "error: no DMA channel\n");
+               return -ENODEV;
+       }
+
+       dev_dbg(sst->dev, "DMA: src: 0x%lx dest 0x%lx size %zu\n",
+               (unsigned long)src_addr, (unsigned long)dest_addr, size);
+
+       desc = dma->ch->device->device_prep_dma_memcpy(dma->ch, dest_addr,
+               src_addr, size, DMA_CTRL_ACK);
+       if (!desc){
+               dev_err(sst->dev, "error: dma prep memcpy failed\n");
+               return -EINVAL;
+       }
+
+       desc->callback = sst_dma_transfer_complete;
+       desc->callback_param = sst;
+
+       desc->tx_submit(desc);
+       dma_wait_for_async_tx(desc);
+
+       return 0;
+}
+
+/* copy to DSP */
+int sst_dsp_dma_copyto(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size)
+{
+       return sst_dsp_dma_copy(sst, dest_addr | SST_HSW_MASK_DMA_ADDR_DSP,
+                       src_addr, size);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_copyto);
+
+/* copy from DSP */
+int sst_dsp_dma_copyfrom(struct sst_dsp *sst, dma_addr_t dest_addr,
+       dma_addr_t src_addr, size_t size)
+{
+       return sst_dsp_dma_copy(sst, dest_addr,
+               src_addr | SST_HSW_MASK_DMA_ADDR_DSP, size);
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_copyfrom);
+
+/* remove module from memory - callers hold locks */
+static void block_list_remove(struct sst_dsp *dsp,
+       struct list_head *block_list)
+{
+       struct sst_mem_block *block, *tmp;
+       int err;
+
+       /* disable each block  */
+       list_for_each_entry(block, block_list, module_list) {
+
+               if (block->ops && block->ops->disable) {
+                       err = block->ops->disable(block);
+                       if (err < 0)
+                               dev_err(dsp->dev,
+                                       "error: cant disable block %d:%d\n",
+                                       block->type, block->index);
+               }
+       }
+
+       /* mark each block as free */
+       list_for_each_entry_safe(block, tmp, block_list, module_list) {
+               list_del(&block->module_list);
+               list_move(&block->list, &dsp->free_block_list);
+               dev_dbg(dsp->dev, "block freed %d:%d at offset 0x%x\n",
+                       block->type, block->index, block->offset);
+       }
+}
+
+/* prepare the memory block to receive data from host - callers hold locks */
+static int block_list_prepare(struct sst_dsp *dsp,
+       struct list_head *block_list)
+{
+       struct sst_mem_block *block;
+       int ret = 0;
+
+       /* enable each block so that's it'e ready for data */
+       list_for_each_entry(block, block_list, module_list) {
+
+               if (block->ops && block->ops->enable && !block->users) {
+                       ret = block->ops->enable(block);
+                       if (ret < 0) {
+                               dev_err(dsp->dev,
+                                       "error: cant disable block %d:%d\n",
+                                       block->type, block->index);
+                               goto err;
+                       }
+               }
+       }
+       return ret;
+
+err:
+       list_for_each_entry(block, block_list, module_list) {
+               if (block->ops && block->ops->disable)
+                       block->ops->disable(block);
+       }
+       return ret;
+}
+
+static struct dw_dma_platform_data dw_pdata = {
+       .is_private = 1,
+       .chan_allocation_order = CHAN_ALLOCATION_ASCENDING,
+       .chan_priority = CHAN_PRIORITY_ASCENDING,
+};
+
+static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
+       int irq)
+{
+       struct dw_dma_chip *chip;
+       int err;
+
+       chip = devm_kzalloc(dev, sizeof(*chip), GFP_KERNEL);
+       if (!chip)
+               return ERR_PTR(-ENOMEM);
+
+       chip->irq = irq;
+       chip->regs = devm_ioremap_resource(dev, mem);
+       if (IS_ERR(chip->regs))
+               return ERR_CAST(chip->regs);
+
+       err = dma_coerce_mask_and_coherent(dev, DMA_BIT_MASK(31));
+       if (err)
+               return ERR_PTR(err);
+
+       chip->dev = dev;
+       err = dw_dma_probe(chip, &dw_pdata);
+       if (err)
+               return ERR_PTR(err);
+
+       return chip;
+}
+
+static void dw_remove(struct dw_dma_chip *chip)
+{
+       dw_dma_remove(chip);
+}
+
+static bool dma_chan_filter(struct dma_chan *chan, void *param)
+{
+       struct sst_dsp *dsp = (struct sst_dsp *)param;
+
+       return chan->device->dev == dsp->dma_dev;
+}
 
-static void sst_memcpy32(volatile void __iomem *dest, void *src, u32 bytes)
+int sst_dsp_dma_get_channel(struct sst_dsp *dsp, int chan_id)
 {
-       u32 i;
+       struct sst_dma *dma = dsp->dma;
+       struct dma_slave_config slave;
+       dma_cap_mask_t mask;
+       int ret;
+
+       /* The Intel MID DMA engine driver needs the slave config set but
+        * Synopsis DMA engine driver safely ignores the slave config */
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+       dma_cap_set(DMA_MEMCPY, mask);
+
+       dma->ch = dma_request_channel(mask, dma_chan_filter, dsp);
+       if (dma->ch == NULL) {
+               dev_err(dsp->dev, "error: DMA request channel failed\n");
+               return -EIO;
+       }
+
+       memset(&slave, 0, sizeof(slave));
+       slave.direction = DMA_MEM_TO_DEV;
+       slave.src_addr_width =
+               slave.dst_addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       slave.src_maxburst = slave.dst_maxburst = SST_DSP_DMA_MAX_BURST;
+
+       ret = dmaengine_slave_config(dma->ch, &slave);
+       if (ret) {
+               dev_err(dsp->dev, "error: unable to set DMA slave config %d\n",
+                       ret);
+               dma_release_channel(dma->ch);
+               dma->ch = NULL;
+       }
 
-       /* copy one 32 bit word at a time as 64 bit access is not supported */
-       for (i = 0; i < bytes; i += 4)
-               memcpy_toio(dest + i, src + i, 4);
+       return ret;
 }
+EXPORT_SYMBOL_GPL(sst_dsp_dma_get_channel);
+
+void sst_dsp_dma_put_channel(struct sst_dsp *dsp)
+{
+       struct sst_dma *dma = dsp->dma;
+
+       if (!dma->ch)
+               return;
+
+       dma_release_channel(dma->ch);
+       dma->ch = NULL;
+}
+EXPORT_SYMBOL_GPL(sst_dsp_dma_put_channel);
+
+int sst_dma_new(struct sst_dsp *sst)
+{
+       struct sst_pdata *sst_pdata = sst->pdata;
+       struct sst_dma *dma;
+       struct resource mem;
+       const char *dma_dev_name;
+       int ret = 0;
+
+       /* configure the correct platform data for whatever DMA engine
+       * is attached to the ADSP IP. */
+       switch (sst->pdata->dma_engine) {
+       case SST_DMA_TYPE_DW:
+               dma_dev_name = "dw_dmac";
+               break;
+       case SST_DMA_TYPE_MID:
+               dma_dev_name = "Intel MID DMA";
+               break;
+       default:
+               dev_err(sst->dev, "error: invalid DMA engine %d\n",
+                       sst->pdata->dma_engine);
+               return -EINVAL;
+       }
+
+       dma = devm_kzalloc(sst->dev, sizeof(struct sst_dma), GFP_KERNEL);
+       if (!dma)
+               return -ENOMEM;
+
+       dma->sst = sst;
+
+       memset(&mem, 0, sizeof(mem));
+
+       mem.start = sst->addr.lpe_base + sst_pdata->dma_base;
+       mem.end   = sst->addr.lpe_base + sst_pdata->dma_base + sst_pdata->dma_size - 1;
+       mem.flags = IORESOURCE_MEM;
+
+       /* now register DMA engine device */
+       dma->chip = dw_probe(sst->dma_dev, &mem, sst_pdata->irq);
+       if (IS_ERR(dma->chip)) {
+               dev_err(sst->dev, "error: DMA device register failed\n");
+               ret = PTR_ERR(dma->chip);
+               goto err_dma_dev;
+       }
+
+       sst->dma = dma;
+       sst->fw_use_dma = true;
+       return 0;
+
+err_dma_dev:
+       devm_kfree(sst->dev, dma);
+       return ret;
+}
+EXPORT_SYMBOL(sst_dma_new);
+
+void sst_dma_free(struct sst_dma *dma)
+{
+
+       if (dma == NULL)
+               return;
+
+       if (dma->ch)
+               dma_release_channel(dma->ch);
+
+       if (dma->chip)
+               dw_remove(dma->chip);
+
+}
+EXPORT_SYMBOL(sst_dma_free);
 
 /* create new generic firmware object */
 struct sst_fw *sst_fw_new(struct sst_dsp *dsp, 
@@ -71,6 +361,12 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
        /* copy FW data to DMA-able memory */
        memcpy((void *)sst_fw->dma_buf, (void *)fw->data, fw->size);
 
+       if (dsp->fw_use_dma) {
+               err = sst_dsp_dma_get_channel(dsp, 0);
+               if (err < 0)
+                       goto chan_err;
+       }
+
        /* call core specific FW paser to load FW data into DSP */
        err = dsp->ops->parse_fw(sst_fw);
        if (err < 0) {
@@ -78,6 +374,9 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
                goto parse_err;
        }
 
+       if (dsp->fw_use_dma)
+               sst_dsp_dma_put_channel(dsp);
+
        mutex_lock(&dsp->mutex);
        list_add(&sst_fw->list, &dsp->fw_list);
        mutex_unlock(&dsp->mutex);
@@ -85,9 +384,13 @@ struct sst_fw *sst_fw_new(struct sst_dsp *dsp,
        return sst_fw;
 
 parse_err:
-       dma_free_coherent(dsp->dev, sst_fw->size,
+       if (dsp->fw_use_dma)
+               sst_dsp_dma_put_channel(dsp);
+chan_err:
+       dma_free_coherent(dsp->dma_dev, sst_fw->size,
                                sst_fw->dma_buf,
                                sst_fw->dmable_fw_paddr);
+       sst_fw->dma_buf = NULL;
        kfree(sst_fw);
        return NULL;
 }
@@ -111,21 +414,37 @@ EXPORT_SYMBOL_GPL(sst_fw_reload);
 
 void sst_fw_unload(struct sst_fw *sst_fw)
 {
-        struct sst_dsp *dsp = sst_fw->dsp;
-        struct sst_module *module, *tmp;
+       struct sst_dsp *dsp = sst_fw->dsp;
+       struct sst_module *module, *mtmp;
+       struct sst_module_runtime *runtime, *rtmp;
+
+       dev_dbg(dsp->dev, "unloading firmware\n");
+
+       mutex_lock(&dsp->mutex);
+
+       /* check module by module */
+       list_for_each_entry_safe(module, mtmp, &dsp->module_list, list) {
+               if (module->sst_fw == sst_fw) {
+
+                       /* remove runtime modules */
+                       list_for_each_entry_safe(runtime, rtmp, &module->runtime_list, list) {
 
-        dev_dbg(dsp->dev, "unloading firmware\n");
+                               block_list_remove(dsp, &runtime->block_list);
+                               list_del(&runtime->list);
+                               kfree(runtime);
+                       }
+
+                       /* now remove the module */
+                       block_list_remove(dsp, &module->block_list);
+                       list_del(&module->list);
+                       kfree(module);
+               }
+       }
 
-        mutex_lock(&dsp->mutex);
-        list_for_each_entry_safe(module, tmp, &dsp->module_list, list) {
-                if (module->sst_fw == sst_fw) {
-                        block_module_remove(module);
-                        list_del(&module->list);
-                        kfree(module);
-                }
-        }
+       /* remove all scratch blocks */
+       block_list_remove(dsp, &dsp->scratch_block_list);
 
-        mutex_unlock(&dsp->mutex);
+       mutex_unlock(&dsp->mutex);
 }
 EXPORT_SYMBOL_GPL(sst_fw_unload);
 
@@ -138,7 +457,8 @@ void sst_fw_free(struct sst_fw *sst_fw)
        list_del(&sst_fw->list);
        mutex_unlock(&dsp->mutex);
 
-       dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
+       if (sst_fw->dma_buf)
+               dma_free_coherent(dsp->dma_dev, sst_fw->size, sst_fw->dma_buf,
                        sst_fw->dmable_fw_paddr);
        kfree(sst_fw);
 }
@@ -175,11 +495,11 @@ struct sst_module *sst_module_new(struct sst_fw *sst_fw,
        sst_module->id = template->id;
        sst_module->dsp = dsp;
        sst_module->sst_fw = sst_fw;
-
-       memcpy(&sst_module->s, &template->s, sizeof(struct sst_module_data));
-       memcpy(&sst_module->p, &template->p, sizeof(struct sst_module_data));
+       sst_module->scratch_size = template->scratch_size;
+       sst_module->persistent_size = template->persistent_size;
 
        INIT_LIST_HEAD(&sst_module->block_list);
+       INIT_LIST_HEAD(&sst_module->runtime_list);
 
        mutex_lock(&dsp->mutex);
        list_add(&sst_module->list, &dsp->module_list);
@@ -202,73 +522,122 @@ void sst_module_free(struct sst_module *sst_module)
 }
 EXPORT_SYMBOL_GPL(sst_module_free);
 
-static struct sst_mem_block *find_block(struct sst_dsp *dsp, int type,
-       u32 offset)
+struct sst_module_runtime *sst_module_runtime_new(struct sst_module *module,
+       int id, void *private)
+{
+       struct sst_dsp *dsp = module->dsp;
+       struct sst_module_runtime *runtime;
+
+       runtime = kzalloc(sizeof(*runtime), GFP_KERNEL);
+       if (runtime == NULL)
+               return NULL;
+
+       runtime->id = id;
+       runtime->dsp = dsp;
+       runtime->module = module;
+       INIT_LIST_HEAD(&runtime->block_list);
+
+       mutex_lock(&dsp->mutex);
+       list_add(&runtime->list, &module->runtime_list);
+       mutex_unlock(&dsp->mutex);
+
+       return runtime;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_new);
+
+void sst_module_runtime_free(struct sst_module_runtime *runtime)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+
+       mutex_lock(&dsp->mutex);
+       list_del(&runtime->list);
+       mutex_unlock(&dsp->mutex);
+
+       kfree(runtime);
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_free);
+
+static struct sst_mem_block *find_block(struct sst_dsp *dsp,
+       struct sst_block_allocator *ba)
 {
        struct sst_mem_block *block;
 
        list_for_each_entry(block, &dsp->free_block_list, list) {
-               if (block->type == type && block->offset == offset)
+               if (block->type == ba->type && block->offset == ba->offset)
                        return block;
        }
 
        return NULL;
 }
 
-static int block_alloc_contiguous(struct sst_module *module,
-       struct sst_module_data *data, u32 offset, int size)
+/* Block allocator must be on block boundary */
+static int block_alloc_contiguous(struct sst_dsp *dsp,
+       struct sst_block_allocator *ba, struct list_head *block_list)
 {
        struct list_head tmp = LIST_HEAD_INIT(tmp);
-       struct sst_dsp *dsp = module->dsp;
        struct sst_mem_block *block;
+       u32 block_start = SST_HSW_BLOCK_ANY;
+       int size = ba->size, offset = ba->offset;
+
+       while (ba->size > 0) {
 
-       while (size > 0) {
-               block = find_block(dsp, data->type, offset);
+               block = find_block(dsp, ba);
                if (!block) {
                        list_splice(&tmp, &dsp->free_block_list);
+
+                       ba->size = size;
+                       ba->offset = offset;
                        return -ENOMEM;
                }
 
                list_move_tail(&block->list, &tmp);
-               offset += block->size;
-               size -= block->size;
+               ba->offset += block->size;
+               ba->size -= block->size;
        }
+       ba->size = size;
+       ba->offset = offset;
+
+       list_for_each_entry(block, &tmp, list) {
+
+               if (block->offset < block_start)
+                       block_start = block->offset;
 
-       list_for_each_entry(block, &tmp, list)
-               list_add(&block->module_list, &module->block_list);
+               list_add(&block->module_list, block_list);
+
+               dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+                       block->type, block->index, block->offset);
+       }
 
        list_splice(&tmp, &dsp->used_block_list);
        return 0;
 }
 
-/* allocate free DSP blocks for module data - callers hold locks */
-static int block_alloc(struct sst_module *module,
-       struct sst_module_data *data)
+/* allocate first free DSP blocks for data - callers hold locks */
+static int block_alloc(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+       struct list_head *block_list)
 {
-       struct sst_dsp *dsp = module->dsp;
        struct sst_mem_block *block, *tmp;
        int ret = 0;
 
-       if (data->size == 0)
+       if (ba->size == 0)
                return 0;
 
        /* find first free whole blocks that can hold module */
        list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
 
                /* ignore blocks with wrong type */
-               if (block->type != data->type)
+               if (block->type != ba->type)
                        continue;
 
-               if (data->size > block->size)
+               if (ba->size > block->size)
                        continue;
 
-               data->offset = block->offset;
-               block->data_type = data->data_type;
-               block->bytes_used = data->size % block->size;
-               list_add(&block->module_list, &module->block_list);
+               ba->offset = block->offset;
+               block->bytes_used = ba->size % block->size;
+               list_add(&block->module_list, block_list);
                list_move(&block->list, &dsp->used_block_list);
-               dev_dbg(dsp->dev, " *module %d added block %d:%d\n",
-                       module->id, block->type, block->index);
+               dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+                       block->type, block->index, block->offset);
                return 0;
        }
 
@@ -276,15 +645,19 @@ static int block_alloc(struct sst_module *module,
        list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
 
                /* ignore blocks with wrong type */
-               if (block->type != data->type)
+               if (block->type != ba->type)
                        continue;
 
                /* do we span > 1 blocks */
-               if (data->size > block->size) {
-                       ret = block_alloc_contiguous(module, data,
-                               block->offset, data->size);
+               if (ba->size > block->size) {
+
+                       /* align ba to block boundary */
+                       ba->offset = block->offset;
+
+                       ret = block_alloc_contiguous(dsp, ba, block_list);
                        if (ret == 0)
                                return ret;
+
                }
        }
 
@@ -292,93 +665,74 @@ static int block_alloc(struct sst_module *module,
        return -ENOMEM;
 }
 
-/* remove module from memory - callers hold locks */
-static void block_module_remove(struct sst_module *module)
+int sst_alloc_blocks(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+       struct list_head *block_list)
 {
-       struct sst_mem_block *block, *tmp;
-       struct sst_dsp *dsp = module->dsp;
-       int err;
+       int ret;
 
-       /* disable each block  */
-       list_for_each_entry(block, &module->block_list, module_list) {
+       dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
+               ba->size, ba->offset, ba->type);
 
-               if (block->ops && block->ops->disable) {
-                       err = block->ops->disable(block);
-                       if (err < 0)
-                               dev_err(dsp->dev,
-                                       "error: cant disable block %d:%d\n",
-                                       block->type, block->index);
-               }
-       }
+       mutex_lock(&dsp->mutex);
 
-       /* mark each block as free */
-       list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
-               list_del(&block->module_list);
-               list_move(&block->list, &dsp->free_block_list);
+       ret = block_alloc(dsp, ba, block_list);
+       if (ret < 0) {
+               dev_err(dsp->dev, "error: can't alloc blocks %d\n", ret);
+               goto out;
        }
-}
-
-/* prepare the memory block to receive data from host - callers hold locks */
-static int block_module_prepare(struct sst_module *module)
-{
-       struct sst_mem_block *block;
-       int ret = 0;
 
-       /* enable each block so that's it'e ready for module P/S data */
-       list_for_each_entry(block, &module->block_list, module_list) {
+       /* prepare DSP blocks for module usage */
+       ret = block_list_prepare(dsp, block_list);
+       if (ret < 0)
+               dev_err(dsp->dev, "error: prepare failed\n");
 
-               if (block->ops && block->ops->enable) {
-                       ret = block->ops->enable(block);
-                       if (ret < 0) {
-                               dev_err(module->dsp->dev,
-                                       "error: cant disable block %d:%d\n",
-                                       block->type, block->index);
-                               goto err;
-                       }
-               }
-       }
+out:
+       mutex_unlock(&dsp->mutex);
        return ret;
+}
+EXPORT_SYMBOL_GPL(sst_alloc_blocks);
 
-err:
-       list_for_each_entry(block, &module->block_list, module_list) {
-               if (block->ops && block->ops->disable)
-                       block->ops->disable(block);
-       }
-       return ret;
+int sst_free_blocks(struct sst_dsp *dsp, struct list_head *block_list)
+{
+       mutex_lock(&dsp->mutex);
+       block_list_remove(dsp, block_list);
+       mutex_unlock(&dsp->mutex);
+       return 0;
 }
+EXPORT_SYMBOL_GPL(sst_free_blocks);
 
 /* allocate memory blocks for static module addresses - callers hold locks */
-static int block_alloc_fixed(struct sst_module *module,
-       struct sst_module_data *data)
+static int block_alloc_fixed(struct sst_dsp *dsp, struct sst_block_allocator *ba,
+       struct list_head *block_list)
 {
-       struct sst_dsp *dsp = module->dsp;
        struct sst_mem_block *block, *tmp;
-       u32 end = data->offset + data->size, block_end;
+       u32 end = ba->offset + ba->size, block_end;
        int err;
 
        /* only IRAM/DRAM blocks are managed */
-       if (data->type != SST_MEM_IRAM && data->type != SST_MEM_DRAM)
+       if (ba->type != SST_MEM_IRAM && ba->type != SST_MEM_DRAM)
                return 0;
 
        /* are blocks already attached to this module */
-       list_for_each_entry_safe(block, tmp, &module->block_list, module_list) {
+       list_for_each_entry_safe(block, tmp, block_list, module_list) {
 
-               /* force compacting mem blocks of the same data_type */
-               if (block->data_type != data->data_type)
+               /* ignore blocks with wrong type */
+               if (block->type != ba->type)
                        continue;
 
                block_end = block->offset + block->size;
 
                /* find block that holds section */
-               if (data->offset >= block->offset && end < block_end)
+               if (ba->offset >= block->offset && end <= block_end)
                        return 0;
 
                /* does block span more than 1 section */
-               if (data->offset >= block->offset && data->offset < block_end) {
+               if (ba->offset >= block->offset && ba->offset < block_end) {
 
-                       err = block_alloc_contiguous(module, data,
-                               block->offset + block->size,
-                               data->size - block->size);
+                       /* align ba to block boundary */
+                       ba->size -= block_end - ba->offset;
+                       ba->offset = block_end;
+                       err = block_alloc_contiguous(dsp, ba, block_list);
                        if (err < 0)
                                return -ENOMEM;
 
@@ -391,82 +745,270 @@ static int block_alloc_fixed(struct sst_module *module,
        list_for_each_entry_safe(block, tmp, &dsp->free_block_list, list) {
                block_end = block->offset + block->size;
 
+               /* ignore blocks with wrong type */
+               if (block->type != ba->type)
+                       continue;
+
                /* find block that holds section */
-               if (data->offset >= block->offset && end < block_end) {
+               if (ba->offset >= block->offset && end <= block_end) {
 
                        /* add block */
-                       block->data_type = data->data_type;
                        list_move(&block->list, &dsp->used_block_list);
-                       list_add(&block->module_list, &module->block_list);
+                       list_add(&block->module_list, block_list);
+                       dev_dbg(dsp->dev, "block allocated %d:%d at offset 0x%x\n",
+                               block->type, block->index, block->offset);
                        return 0;
                }
 
                /* does block span more than 1 section */
-               if (data->offset >= block->offset && data->offset < block_end) {
+               if (ba->offset >= block->offset && ba->offset < block_end) {
 
-                       err = block_alloc_contiguous(module, data,
-                               block->offset, data->size);
+                       /* align ba to block boundary */
+                       ba->offset = block->offset;
+
+                       err = block_alloc_contiguous(dsp, ba, block_list);
                        if (err < 0)
                                return -ENOMEM;
 
                        return 0;
                }
-
        }
 
        return -ENOMEM;
 }
 
 /* Load fixed module data into DSP memory blocks */
-int sst_module_insert_fixed_block(struct sst_module *module,
-       struct sst_module_data *data)
+int sst_module_alloc_blocks(struct sst_module *module)
 {
        struct sst_dsp *dsp = module->dsp;
+       struct sst_fw *sst_fw = module->sst_fw;
+       struct sst_block_allocator ba;
        int ret;
 
+       ba.size = module->size;
+       ba.type = module->type;
+       ba.offset = module->offset;
+
+       dev_dbg(dsp->dev, "block request 0x%x bytes at offset 0x%x type %d\n",
+               ba.size, ba.offset, ba.type);
+
        mutex_lock(&dsp->mutex);
 
        /* alloc blocks that includes this section */
-       ret = block_alloc_fixed(module, data);
+       ret = block_alloc_fixed(dsp, &ba, &module->block_list);
        if (ret < 0) {
                dev_err(dsp->dev,
                        "error: no free blocks for section at offset 0x%x size 0x%x\n",
-                       data->offset, data->size);
+                       module->offset, module->size);
                mutex_unlock(&dsp->mutex);
                return -ENOMEM;
        }
 
        /* prepare DSP blocks for module copy */
-       ret = block_module_prepare(module);
+       ret = block_list_prepare(dsp, &module->block_list);
        if (ret < 0) {
                dev_err(dsp->dev, "error: fw module prepare failed\n");
                goto err;
        }
 
        /* copy partial module data to blocks */
-       sst_memcpy32(dsp->addr.lpe + data->offset, data->data, data->size);
+       if (dsp->fw_use_dma) {
+               ret = sst_dsp_dma_copyto(dsp,
+                       dsp->addr.lpe_base + module->offset,
+                       sst_fw->dmable_fw_paddr + module->data_offset,
+                       module->size);
+               if (ret < 0) {
+                       dev_err(dsp->dev, "error: module copy failed\n");
+                       goto err;
+               }
+       } else
+               sst_memcpy32(dsp->addr.lpe + module->offset, module->data,
+                       module->size);
 
        mutex_unlock(&dsp->mutex);
        return ret;
 
 err:
-       block_module_remove(module);
+       block_list_remove(dsp, &module->block_list);
        mutex_unlock(&dsp->mutex);
        return ret;
 }
-EXPORT_SYMBOL_GPL(sst_module_insert_fixed_block);
+EXPORT_SYMBOL_GPL(sst_module_alloc_blocks);
 
 /* Unload entire module from DSP memory */
-int sst_block_module_remove(struct sst_module *module)
+int sst_module_free_blocks(struct sst_module *module)
 {
        struct sst_dsp *dsp = module->dsp;
 
        mutex_lock(&dsp->mutex);
-       block_module_remove(module);
+       block_list_remove(dsp, &module->block_list);
        mutex_unlock(&dsp->mutex);
        return 0;
 }
-EXPORT_SYMBOL_GPL(sst_block_module_remove);
+EXPORT_SYMBOL_GPL(sst_module_free_blocks);
+
+int sst_module_runtime_alloc_blocks(struct sst_module_runtime *runtime,
+       int offset)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+       struct sst_module *module = runtime->module;
+       struct sst_block_allocator ba;
+       int ret;
+
+       if (module->persistent_size == 0)
+               return 0;
+
+       ba.size = module->persistent_size;
+       ba.type = SST_MEM_DRAM;
+
+       mutex_lock(&dsp->mutex);
+
+       /* do we need to allocate at a fixed address ? */
+       if (offset != 0) {
+
+               ba.offset = offset;
+
+               dev_dbg(dsp->dev, "persistent fixed block request 0x%x bytes type %d offset 0x%x\n",
+                       ba.size, ba.type, ba.offset);
+
+               /* alloc blocks that includes this section */
+               ret = block_alloc_fixed(dsp, &ba, &runtime->block_list);
+
+       } else {
+               dev_dbg(dsp->dev, "persistent block request 0x%x bytes type %d\n",
+                       ba.size, ba.type);
+
+               /* alloc blocks that includes this section */
+               ret = block_alloc(dsp, &ba, &runtime->block_list);
+       }
+       if (ret < 0) {
+               dev_err(dsp->dev,
+               "error: no free blocks for runtime module size 0x%x\n",
+                       module->persistent_size);
+               mutex_unlock(&dsp->mutex);
+               return -ENOMEM;
+       }
+       runtime->persistent_offset = ba.offset;
+
+       /* prepare DSP blocks for module copy */
+       ret = block_list_prepare(dsp, &runtime->block_list);
+       if (ret < 0) {
+               dev_err(dsp->dev, "error: runtime block prepare failed\n");
+               goto err;
+       }
+
+       mutex_unlock(&dsp->mutex);
+       return ret;
+
+err:
+       block_list_remove(dsp, &module->block_list);
+       mutex_unlock(&dsp->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_alloc_blocks);
+
+int sst_module_runtime_free_blocks(struct sst_module_runtime *runtime)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+
+       mutex_lock(&dsp->mutex);
+       block_list_remove(dsp, &runtime->block_list);
+       mutex_unlock(&dsp->mutex);
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_free_blocks);
+
+int sst_module_runtime_save(struct sst_module_runtime *runtime,
+       struct sst_module_runtime_context *context)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+       struct sst_module *module = runtime->module;
+       int ret = 0;
+
+       dev_dbg(dsp->dev, "saving runtime %d memory at 0x%x size 0x%x\n",
+               runtime->id, runtime->persistent_offset,
+               module->persistent_size);
+
+       context->buffer = dma_alloc_coherent(dsp->dma_dev,
+               module->persistent_size,
+               &context->dma_buffer, GFP_DMA | GFP_KERNEL);
+       if (!context->buffer) {
+               dev_err(dsp->dev, "error: DMA context alloc failed\n");
+               return -ENOMEM;
+       }
+
+       mutex_lock(&dsp->mutex);
+
+       if (dsp->fw_use_dma) {
+
+               ret = sst_dsp_dma_get_channel(dsp, 0);
+               if (ret < 0)
+                       goto err;
+
+               ret = sst_dsp_dma_copyfrom(dsp, context->dma_buffer,
+                       dsp->addr.lpe_base + runtime->persistent_offset,
+                       module->persistent_size);
+               sst_dsp_dma_put_channel(dsp);
+               if (ret < 0) {
+                       dev_err(dsp->dev, "error: context copy failed\n");
+                       goto err;
+               }
+       } else
+               sst_memcpy32(context->buffer, dsp->addr.lpe +
+                       runtime->persistent_offset,
+                       module->persistent_size);
+
+err:
+       mutex_unlock(&dsp->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_save);
+
+int sst_module_runtime_restore(struct sst_module_runtime *runtime,
+       struct sst_module_runtime_context *context)
+{
+       struct sst_dsp *dsp = runtime->dsp;
+       struct sst_module *module = runtime->module;
+       int ret = 0;
+
+       dev_dbg(dsp->dev, "restoring runtime %d memory at 0x%x size 0x%x\n",
+               runtime->id, runtime->persistent_offset,
+               module->persistent_size);
+
+       mutex_lock(&dsp->mutex);
+
+       if (!context->buffer) {
+               dev_info(dsp->dev, "no context buffer need to restore!\n");
+               goto err;
+       }
+
+       if (dsp->fw_use_dma) {
+
+               ret = sst_dsp_dma_get_channel(dsp, 0);
+               if (ret < 0)
+                       goto err;
+
+               ret = sst_dsp_dma_copyto(dsp,
+                       dsp->addr.lpe_base + runtime->persistent_offset,
+                       context->dma_buffer, module->persistent_size);
+               sst_dsp_dma_put_channel(dsp);
+               if (ret < 0) {
+                       dev_err(dsp->dev, "error: module copy failed\n");
+                       goto err;
+               }
+       } else
+               sst_memcpy32(dsp->addr.lpe + runtime->persistent_offset,
+                       context->buffer, module->persistent_size);
+
+       dma_free_coherent(dsp->dma_dev, module->persistent_size,
+                               context->buffer, context->dma_buffer);
+       context->buffer = NULL;
+
+err:
+       mutex_unlock(&dsp->mutex);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_restore);
 
 /* register a DSP memory block for use with FW based modules */
 struct sst_mem_block *sst_mem_block_register(struct sst_dsp *dsp, u32 offset,
@@ -519,80 +1061,84 @@ void sst_mem_block_unregister_all(struct sst_dsp *dsp)
 EXPORT_SYMBOL_GPL(sst_mem_block_unregister_all);
 
 /* allocate scratch buffer blocks */
-struct sst_module *sst_mem_block_alloc_scratch(struct sst_dsp *dsp)
+int sst_block_alloc_scratch(struct sst_dsp *dsp)
 {
-       struct sst_module *sst_module, *scratch;
-       struct sst_mem_block *block, *tmp;
-       u32 block_size;
-       int ret = 0;
-
-       scratch = kzalloc(sizeof(struct sst_module), GFP_KERNEL);
-       if (scratch == NULL)
-               return NULL;
+       struct sst_module *module;
+       struct sst_block_allocator ba;
+       int ret;
 
        mutex_lock(&dsp->mutex);
 
        /* calculate required scratch size */
-       list_for_each_entry(sst_module, &dsp->module_list, list) {
-               if (scratch->s.size < sst_module->s.size)
-                       scratch->s.size = sst_module->s.size;
+       dsp->scratch_size = 0;
+       list_for_each_entry(module, &dsp->module_list, list) {
+               dev_dbg(dsp->dev, "module %d scratch req 0x%x bytes\n",
+                       module->id, module->scratch_size);
+               if (dsp->scratch_size < module->scratch_size)
+                       dsp->scratch_size = module->scratch_size;
        }
 
-       dev_dbg(dsp->dev, "scratch buffer required is %d bytes\n",
-               scratch->s.size);
-
-       /* init scratch module */
-       scratch->dsp = dsp;
-       scratch->s.type = SST_MEM_DRAM;
-       scratch->s.data_type = SST_DATA_S;
-       INIT_LIST_HEAD(&scratch->block_list);
+       dev_dbg(dsp->dev, "scratch buffer required is 0x%x bytes\n",
+               dsp->scratch_size);
 
-       /* check free blocks before looking at used blocks for space */
-       if (!list_empty(&dsp->free_block_list))
-               block = list_first_entry(&dsp->free_block_list,
-                       struct sst_mem_block, list);
-       else
-               block = list_first_entry(&dsp->used_block_list,
-                       struct sst_mem_block, list);
-       block_size = block->size;
+       if (dsp->scratch_size == 0) {
+               dev_info(dsp->dev, "no modules need scratch buffer\n");
+               mutex_unlock(&dsp->mutex);
+               return 0;
+       }
 
        /* allocate blocks for module scratch buffers */
        dev_dbg(dsp->dev, "allocating scratch blocks\n");
-       ret = block_alloc(scratch, &scratch->s);
+
+       ba.size = dsp->scratch_size;
+       ba.type = SST_MEM_DRAM;
+
+       /* do we need to allocate at fixed offset */
+       if (dsp->scratch_offset != 0) {
+
+               dev_dbg(dsp->dev, "block request 0x%x bytes type %d at 0x%x\n",
+                       ba.size, ba.type, ba.offset);
+
+               ba.offset = dsp->scratch_offset;
+
+               /* alloc blocks that includes this section */
+               ret = block_alloc_fixed(dsp, &ba, &dsp->scratch_block_list);
+
+       } else {
+               dev_dbg(dsp->dev, "block request 0x%x bytes type %d\n",
+                       ba.size, ba.type);
+
+               ba.offset = 0;
+               ret = block_alloc(dsp, &ba, &dsp->scratch_block_list);
+       }
        if (ret < 0) {
                dev_err(dsp->dev, "error: can't alloc scratch blocks\n");
-               goto err;
+               mutex_unlock(&dsp->mutex);
+               return ret;
        }
 
-       /* assign the same offset of scratch to each module */
-       list_for_each_entry(sst_module, &dsp->module_list, list)
-               sst_module->s.offset = scratch->s.offset;
-
-       mutex_unlock(&dsp->mutex);
-       return scratch;
+       ret = block_list_prepare(dsp, &dsp->scratch_block_list);
+       if (ret < 0) {
+               dev_err(dsp->dev, "error: scratch block prepare failed\n");
+               mutex_unlock(&dsp->mutex);
+               return ret;
+       }
 
-err:
-       list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
-               list_del(&block->module_list);
+       /* assign the same offset of scratch to each module */
+       dsp->scratch_offset = ba.offset;
        mutex_unlock(&dsp->mutex);
-       return NULL;
+       return dsp->scratch_size;
 }
-EXPORT_SYMBOL_GPL(sst_mem_block_alloc_scratch);
+EXPORT_SYMBOL_GPL(sst_block_alloc_scratch);
 
 /* free all scratch blocks */
-void sst_mem_block_free_scratch(struct sst_dsp *dsp,
-       struct sst_module *scratch)
+void sst_block_free_scratch(struct sst_dsp *dsp)
 {
-       struct sst_mem_block *block, *tmp;
-
        mutex_lock(&dsp->mutex);
-
-       list_for_each_entry_safe(block, tmp, &scratch->block_list, module_list)
-               list_del(&block->module_list);
-
+       block_list_remove(dsp, &dsp->scratch_block_list);
        mutex_unlock(&dsp->mutex);
 }
-EXPORT_SYMBOL_GPL(sst_mem_block_free_scratch);
+EXPORT_SYMBOL_GPL(sst_block_free_scratch);
 
 /* get a module from it's unique ID */
 struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
@@ -612,3 +1158,40 @@ struct sst_module *sst_module_get_from_id(struct sst_dsp *dsp, u32 id)
        return NULL;
 }
 EXPORT_SYMBOL_GPL(sst_module_get_from_id);
+
+struct sst_module_runtime *sst_module_runtime_get_from_id(
+       struct sst_module *module, u32 id)
+{
+       struct sst_module_runtime *runtime;
+       struct sst_dsp *dsp = module->dsp;
+
+       mutex_lock(&dsp->mutex);
+
+       list_for_each_entry(runtime, &module->runtime_list, list) {
+               if (runtime->id == id) {
+                       mutex_unlock(&dsp->mutex);
+                       return runtime;
+               }
+       }
+
+       mutex_unlock(&dsp->mutex);
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(sst_module_runtime_get_from_id);
+
+/* returns block address in DSP address space */
+u32 sst_dsp_get_offset(struct sst_dsp *dsp, u32 offset,
+       enum sst_mem_type type)
+{
+       switch (type) {
+       case SST_MEM_IRAM:
+               return offset - dsp->addr.iram_offset +
+                       dsp->addr.dsp_iram_offset;
+       case SST_MEM_DRAM:
+               return offset - dsp->addr.dram_offset +
+                       dsp->addr.dsp_dram_offset;
+       default:
+               return 0;
+       }
+}
+EXPORT_SYMBOL_GPL(sst_dsp_get_offset);
index 4b6c163..57039b0 100644 (file)
 #define SST_LP_SHIM_OFFSET     0xE7000
 #define SST_WPT_IRAM_OFFSET    0xA0000
 #define SST_LP_IRAM_OFFSET     0x80000
+#define SST_WPT_DSP_DRAM_OFFSET        0x400000
+#define SST_WPT_DSP_IRAM_OFFSET        0x00000
+#define SST_LPT_DSP_DRAM_OFFSET        0x400000
+#define SST_LPT_DSP_IRAM_OFFSET        0x00000
 
 #define SST_SHIM_PM_REG                0x84
 
@@ -86,9 +90,8 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 {
        struct dma_block_info *block;
        struct sst_module *mod;
-       struct sst_module_data block_data;
        struct sst_module_template template;
-       int count;
+       int count, ret;
        void __iomem *ram;
 
        /* TODO: allowed module types need to be configurable */
@@ -109,13 +112,9 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
 
        memset(&template, 0, sizeof(template));
        template.id = module->type;
-       template.entry = module->entry_point;
-       template.p.size = module->info.persistent_size;
-       template.p.type = SST_MEM_DRAM;
-       template.p.data_type = SST_DATA_P;
-       template.s.size = module->info.scratch_size;
-       template.s.type = SST_MEM_DRAM;
-       template.s.data_type = SST_DATA_S;
+       template.entry = module->entry_point - 4;
+       template.persistent_size = module->info.persistent_size;
+       template.scratch_size = module->info.scratch_size;
 
        mod = sst_module_new(fw, &template, NULL);
        if (mod == NULL)
@@ -135,14 +134,14 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
                switch (block->type) {
                case SST_HSW_IRAM:
                        ram = dsp->addr.lpe;
-                       block_data.offset =
+                       mod->offset =
                                block->ram_offset + dsp->addr.iram_offset;
-                       block_data.type = SST_MEM_IRAM;
+                       mod->type = SST_MEM_IRAM;
                        break;
                case SST_HSW_DRAM:
                        ram = dsp->addr.lpe;
-                       block_data.offset = block->ram_offset;
-                       block_data.type = SST_MEM_DRAM;
+                       mod->offset = block->ram_offset;
+                       mod->type = SST_MEM_DRAM;
                        break;
                default:
                        dev_err(dsp->dev, "error: bad type 0x%x for block 0x%x\n",
@@ -151,30 +150,34 @@ static int hsw_parse_module(struct sst_dsp *dsp, struct sst_fw *fw,
                        return -EINVAL;
                }
 
-               block_data.size = block->size;
-               block_data.data_type = SST_DATA_M;
-               block_data.data = (void *)block + sizeof(*block);
-               block_data.data_offset = block_data.data - fw->dma_buf;
+               mod->size = block->size;
+               mod->data = (void *)block + sizeof(*block);
+               mod->data_offset = mod->data - fw->dma_buf;
 
-               dev_dbg(dsp->dev, "copy firmware block %d type 0x%x "
+               dev_dbg(dsp->dev, "module block %d type 0x%x "
                        "size 0x%x ==> ram %p offset 0x%x\n",
-                       count, block->type, block->size, ram,
+                       count, mod->type, block->size, ram,
                        block->ram_offset);
 
-               sst_module_insert_fixed_block(mod, &block_data);
+               ret = sst_module_alloc_blocks(mod);
+               if (ret < 0) {
+                       dev_err(dsp->dev, "error: could not allocate blocks for module %d\n",
+                               count);
+                       sst_module_free(mod);
+                       return ret;
+               }
 
                block = (void *)block + sizeof(*block) + block->size;
        }
+
        return 0;
 }
 
 static int hsw_parse_fw_image(struct sst_fw *sst_fw)
 {
        struct fw_header *header;
-       struct sst_module *scratch;
        struct fw_module_header *module;
        struct sst_dsp *dsp = sst_fw->dsp;
-       struct sst_hsw *hsw = sst_fw->private;
        int ret, count;
 
        /* Read the header information from the data pointer */
@@ -204,12 +207,8 @@ static int hsw_parse_fw_image(struct sst_fw *sst_fw)
                module = (void *)module + sizeof(*module) + module->mod_size;
        }
 
-       /* allocate persistent/scratch mem regions */
-       scratch = sst_mem_block_alloc_scratch(dsp);
-       if (scratch == NULL)
-               return -ENOMEM;
-
-       sst_hsw_set_scratch_module(hsw, scratch);
+       /* allocate scratch mem regions */
+       sst_block_alloc_scratch(dsp);
 
        return 0;
 }
@@ -248,8 +247,94 @@ static irqreturn_t hsw_irq(int irq, void *context)
        return ret;
 }
 
-static void hsw_boot(struct sst_dsp *sst)
+static void hsw_set_dsp_D3(struct sst_dsp *sst)
+{
+       u32 val;
+       u32 reg;
+
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* enable power gating and switch off DRAM & IRAM blocks */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+       val |= SST_VDRTCL0_DSRAMPGE_MASK |
+               SST_VDRTCL0_ISRAMPGE_MASK;
+       val &= ~(SST_VDRTCL0_D3PGD | SST_VDRTCL0_D3SRAMPGD);
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+       /* switch off audio PLL */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val |= SST_VDRTCL2_APLLSE_MASK;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* disable MCLK(clkctl.smos = 0) */
+       sst_dsp_shim_update_bits_unlocked(sst, SST_CLKCTL,
+               SST_CLKCTL_MASK, 0);
+
+       /* Set D3 state, delay 50 us */
+       val = readl(sst->addr.pci_cfg + SST_PMCS);
+       val |= SST_PMCS_PS_MASK;
+       writel(val, sst->addr.pci_cfg + SST_PMCS);
+       udelay(50);
+
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
+}
+
+static void hsw_reset(struct sst_dsp *sst)
 {
+       /* put DSP into reset and stall */
+       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+               SST_CSR_RST | SST_CSR_STALL,
+               SST_CSR_RST | SST_CSR_STALL);
+
+       /* keep in reset for 10ms */
+       mdelay(10);
+
+       /* take DSP out of reset and keep stalled for FW loading */
+       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
+               SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+}
+
+static int hsw_set_dsp_D0(struct sst_dsp *sst)
+{
+       int tries = 10;
+       u32 reg;
+
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg &= ~(SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE);
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* Disable D3PG (VDRTCTL0.D3PGD = 1) */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+       reg |= SST_VDRTCL0_D3PGD;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+       /* Set D0 state */
+       reg = readl(sst->addr.pci_cfg + SST_PMCS);
+       reg &= ~SST_PMCS_PS_MASK;
+       writel(reg, sst->addr.pci_cfg + SST_PMCS);
+
+       /* check that ADSP shim is enabled */
+       while (tries--) {
+               reg = readl(sst->addr.pci_cfg + SST_PMCS) & SST_PMCS_PS_MASK;
+               if (reg == 0)
+                       goto finish;
+
+               msleep(1);
+       }
+
+       return -ENODEV;
+
+finish:
        /* select SSP1 19.2MHz base clock, SSP clock 0, turn off Low Power Clock */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
                SST_CSR_S1IOCS | SST_CSR_SBCS1 | SST_CSR_LPCS, 0x0);
@@ -264,34 +349,96 @@ static void hsw_boot(struct sst_dsp *sst)
                SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0,
                SST_CLKCTL_MASK | SST_CLKCTL_DCPLCG | SST_CLKCTL_SCOE0);
 
+       /* Stall and reset core, set CSR */
+       hsw_reset(sst);
+
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg |= SST_VDRTCL2_DCLCGE | SST_VDRTCL2_DTCGE;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
+       /* switch on audio PLL */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       reg &= ~SST_VDRTCL2_APLLSE_MASK;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       /* set default power gating control, enable power gating control for all blocks. that is,
+       can't be accessed, please enable each block before accessing. */
+       reg = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
+       reg |= SST_VDRTCL0_DSRAMPGE_MASK | SST_VDRTCL0_ISRAMPGE_MASK;
+       writel(reg, sst->addr.pci_cfg + SST_VDRTCTL0);
+
+
        /* disable DMA finish function for SSP0 & SSP1 */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CSR2, SST_CSR2_SDFD_SSP1,
                SST_CSR2_SDFD_SSP1);
 
-       /* enable DMA engine 0,1 all channels to access host memory */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_HMDC,
-               SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff),
-               SST_HMDC_HDDA1(0xff) | SST_HMDC_HDDA0(0xff));
+       /* set on-demond mode on engine 0,1 for all channels */
+       sst_dsp_shim_update_bits(sst, SST_HMDC,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+       /* Enable Interrupt from both sides */
+       sst_dsp_shim_update_bits(sst, SST_IMRX, (SST_IMRX_BUSY | SST_IMRX_DONE),
+                                0x0);
+       sst_dsp_shim_update_bits(sst, SST_IMRD, (SST_IMRD_DONE | SST_IMRD_BUSY |
+                               SST_IMRD_SSP0 | SST_IMRD_DMAC), 0x0);
+
+       /* clear IPC registers */
+       sst_dsp_shim_write(sst, SST_IPCX, 0x0);
+       sst_dsp_shim_write(sst, SST_IPCD, 0x0);
+       sst_dsp_shim_write(sst, 0x80, 0x6);
+       sst_dsp_shim_write(sst, 0xe0, 0x300a);
+
+       return 0;
+}
 
-       /* disable all clock gating */
-       writel(0x0, sst->addr.pci_cfg + SST_VDRTCTL2);
+static void hsw_boot(struct sst_dsp *sst)
+{
+       /* set oportunistic mode on engine 0,1 for all channels */
+       sst_dsp_shim_update_bits(sst, SST_HMDC,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH, 0);
 
        /* set DSP to RUN */
        sst_dsp_shim_update_bits_unlocked(sst, SST_CSR, SST_CSR_STALL, 0x0);
 }
 
-static void hsw_reset(struct sst_dsp *sst)
+static void hsw_stall(struct sst_dsp *sst)
+{
+       /* stall DSP */
+       sst_dsp_shim_update_bits(sst, SST_CSR,
+               SST_CSR_24MHZ_LPCS | SST_CSR_STALL,
+               SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
+}
+
+static void hsw_sleep(struct sst_dsp *sst)
 {
+       dev_dbg(sst->dev, "HSW_PM dsp runtime suspend\n");
+
        /* put DSP into reset and stall */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
-               SST_CSR_RST | SST_CSR_STALL, SST_CSR_RST | SST_CSR_STALL);
+       sst_dsp_shim_update_bits(sst, SST_CSR,
+               SST_CSR_24MHZ_LPCS | SST_CSR_RST | SST_CSR_STALL,
+               SST_CSR_RST | SST_CSR_STALL | SST_CSR_24MHZ_LPCS);
 
-       /* keep in reset for 10ms */
-       mdelay(10);
+       hsw_set_dsp_D3(sst);
+       dev_dbg(sst->dev, "HSW_PM dsp runtime suspend exit\n");
+}
 
-       /* take DSP out of reset and keep stalled for FW loading */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_CSR,
-               SST_CSR_RST | SST_CSR_STALL, SST_CSR_STALL);
+static int hsw_wake(struct sst_dsp *sst)
+{
+       int ret;
+
+       dev_dbg(sst->dev, "HSW_PM dsp runtime resume\n");
+
+       ret = hsw_set_dsp_D0(sst);
+       if (ret < 0)
+               return ret;
+
+       dev_dbg(sst->dev, "HSW_PM dsp runtime resume exit\n");
+
+       return 0;
 }
 
 struct sst_adsp_memregion {
@@ -396,6 +543,11 @@ static int hsw_block_enable(struct sst_mem_block *block)
        dev_dbg(block->dsp->dev, " enabled block %d:%d at offset 0x%x\n",
                block->type, block->index, block->offset);
 
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val &= ~SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
        val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
        bit = hsw_block_get_bit(block);
        writel(val & ~bit, sst->addr.pci_cfg + SST_VDRTCTL0);
@@ -403,6 +555,13 @@ static int hsw_block_enable(struct sst_mem_block *block)
        /* wait 18 DSP clock ticks */
        udelay(10);
 
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val |= SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
        /*add a dummy read before the SRAM block is written, otherwise the writing may miss bytes sometimes.*/
        sst_mem_block_dummy_read(block);
        return 0;
@@ -420,10 +579,26 @@ static int hsw_block_disable(struct sst_mem_block *block)
        dev_dbg(block->dsp->dev, " disabled block %d:%d at offset 0x%x\n",
                block->type, block->index, block->offset);
 
+       /* Disable core clock gating (VDRTCTL2.DCLCGE = 0) */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val &= ~SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+
        val = readl(sst->addr.pci_cfg + SST_VDRTCTL0);
        bit = hsw_block_get_bit(block);
        writel(val | bit, sst->addr.pci_cfg + SST_VDRTCTL0);
 
+       /* wait 18 DSP clock ticks */
+       udelay(10);
+
+       /* Enable core clock gating (VDRTCTL2.DCLCGE = 1), delay 50 us */
+       val = readl(sst->addr.pci_cfg + SST_VDRTCTL2);
+       val |= SST_VDRTCL2_DCLCGE;
+       writel(val, sst->addr.pci_cfg + SST_VDRTCTL2);
+
+       udelay(50);
+
        return 0;
 }
 
@@ -432,27 +607,6 @@ static struct sst_block_ops sst_hsw_ops = {
        .disable = hsw_block_disable,
 };
 
-static int hsw_enable_shim(struct sst_dsp *sst)
-{
-       int tries = 10;
-       u32 reg;
-
-       /* enable shim */
-       reg = readl(sst->addr.pci_cfg + SST_SHIM_PM_REG);
-       writel(reg & ~0x3, sst->addr.pci_cfg + SST_SHIM_PM_REG);
-
-       /* check that ADSP shim is enabled */
-       while (tries--) {
-               reg = sst_dsp_shim_read_unlocked(sst, SST_CSR);
-               if (reg != 0xffffffff)
-                       return 0;
-
-               msleep(1);
-       }
-
-       return -ENODEV;
-}
-
 static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
 {
        const struct sst_adsp_memregion *region;
@@ -467,12 +621,16 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
                region = lp_region;
                region_count = ARRAY_SIZE(lp_region);
                sst->addr.iram_offset = SST_LP_IRAM_OFFSET;
+               sst->addr.dsp_iram_offset = SST_LPT_DSP_IRAM_OFFSET;
+               sst->addr.dsp_dram_offset = SST_LPT_DSP_DRAM_OFFSET;
                sst->addr.shim_offset = SST_LP_SHIM_OFFSET;
                break;
        case SST_DEV_ID_WILDCAT_POINT:
                region = wpt_region;
                region_count = ARRAY_SIZE(wpt_region);
                sst->addr.iram_offset = SST_WPT_IRAM_OFFSET;
+               sst->addr.dsp_iram_offset = SST_WPT_DSP_IRAM_OFFSET;
+               sst->addr.dsp_dram_offset = SST_WPT_DSP_DRAM_OFFSET;
                sst->addr.shim_offset = SST_WPT_SHIM_OFFSET;
                break;
        default:
@@ -487,7 +645,7 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
        }
 
        /* enable the DSP SHIM */
-       ret = hsw_enable_shim(sst);
+       ret = hsw_set_dsp_D0(sst);
        if (ret < 0) {
                dev_err(dev, "error: failed to set DSP D0 and reset SHIM\n");
                return ret;
@@ -497,10 +655,6 @@ static int hsw_init(struct sst_dsp *sst, struct sst_pdata *pdata)
        if (ret)
                return ret;
 
-       /* Enable Interrupt from both sides */
-       sst_dsp_shim_update_bits_unlocked(sst, SST_IMRX, 0x3, 0x0);
-       sst_dsp_shim_update_bits_unlocked(sst, SST_IMRD,
-               (0x3 | 0x1 << 16 | 0x3 << 21), 0x0);
 
        /* register DSP memory blocks - ideally we should get this from ACPI */
        for (i = 0; i < region_count; i++) {
@@ -532,6 +686,9 @@ static void hsw_free(struct sst_dsp *sst)
 struct sst_ops haswell_ops = {
        .reset = hsw_reset,
        .boot = hsw_boot,
+       .stall = hsw_stall,
+       .wake = hsw_wake,
+       .sleep = hsw_sleep,
        .write = sst_shim32_write,
        .read = sst_shim32_read,
        .write64 = sst_shim32_write64,
index b629151..3f8c482 100644 (file)
@@ -30,6 +30,7 @@
 #include <linux/firmware.h>
 #include <linux/dma-mapping.h>
 #include <linux/debugfs.h>
+#include <linux/pm_runtime.h>
 
 #include "sst-haswell-ipc.h"
 #include "sst-dsp.h"
@@ -276,6 +277,7 @@ struct sst_hsw {
        struct sst_hsw_ipc_fw_version version;
        struct sst_module *scratch;
        bool fw_done;
+       struct sst_fw *sst_fw;
 
        /* stream */
        struct list_head stream_list;
@@ -289,6 +291,8 @@ struct sst_hsw {
 
        /* DX */
        struct sst_hsw_ipc_dx_reply dx;
+       void *dx_context;
+       dma_addr_t dx_context_paddr;
 
        /* boot */
        wait_queue_head_t boot_wait;
@@ -1038,14 +1042,9 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
 
        trace_ipc_request("set stream volume", stream->reply.stream_hw_id);
 
-       if (channel > 1)
+       if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
                return -EINVAL;
 
-       if (stream->mute[channel]) {
-               stream->mute_volume[channel] = volume;
-               return 0;
-       }
-
        header = IPC_GLB_TYPE(IPC_GLB_STREAM_MESSAGE) |
                IPC_STR_TYPE(IPC_STR_STAGE_MESSAGE);
        header |= (stream->reply.stream_hw_id << IPC_STR_ID_SHIFT);
@@ -1053,9 +1052,28 @@ int sst_hsw_stream_set_volume(struct sst_hsw *hsw,
        header |= (stage_id << IPC_STG_ID_SHIFT);
 
        req = &stream->vol_req;
-       req->channel = channel;
        req->target_volume = volume;
 
+       /* set both at same time ? */
+       if (channel == SST_HSW_CHANNELS_ALL) {
+               if (hsw->mute[0] && hsw->mute[1]) {
+                       hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
+                       return 0;
+               } else if (hsw->mute[0])
+                       req->channel = 1;
+               else if (hsw->mute[1])
+                       req->channel = 0;
+               else
+                       req->channel = SST_HSW_CHANNELS_ALL;
+       } else {
+               /* set only 1 channel */
+               if (hsw->mute[channel]) {
+                       hsw->mute_volume[channel] = volume;
+                       return 0;
+               }
+               req->channel = channel;
+       }
+
        ret = ipc_tx_message_wait(hsw, header, req, sizeof(*req), NULL, 0);
        if (ret < 0) {
                dev_err(hsw->dev, "error: set stream volume failed\n");
@@ -1134,8 +1152,11 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
 
        trace_ipc_request("set mixer volume", volume);
 
+       if (channel >= 2 && channel != SST_HSW_CHANNELS_ALL)
+               return -EINVAL;
+
        /* set both at same time ? */
-       if (channel == 2) {
+       if (channel == SST_HSW_CHANNELS_ALL) {
                if (hsw->mute[0] && hsw->mute[1]) {
                        hsw->mute_volume[0] = hsw->mute_volume[1] = volume;
                        return 0;
@@ -1144,7 +1165,7 @@ int sst_hsw_mixer_set_volume(struct sst_hsw *hsw, u32 stage_id, u32 channel,
                else if (hsw->mute[1])
                        req.channel = 0;
                else
-                       req.channel = 0xffffffff;
+                       req.channel = SST_HSW_CHANNELS_ALL;
        } else {
                /* set only 1 channel */
                if (hsw->mute[channel]) {
@@ -1256,10 +1277,6 @@ int sst_hsw_stream_set_channels(struct sst_hsw *hsw,
                return -EINVAL;
        }
 
-       /* stereo is only supported atm */
-       if (channels != 2)
-               return -EINVAL;
-
        stream->request.format.ch_num = channels;
        return 0;
 }
@@ -1355,10 +1372,11 @@ int sst_hsw_stream_buffer(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
 }
 
 int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
-       struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id,
-       u32 entry_point)
+       struct sst_hsw_stream *stream, struct sst_module_runtime *runtime)
 {
        struct sst_hsw_module_map *map = &stream->request.map;
+       struct sst_dsp *dsp = sst_hsw_get_dsp(hsw);
+       struct sst_module *module = runtime->module;
 
        if (stream->commited) {
                dev_err(hsw->dev, "error: stream committed for set module\n");
@@ -1367,36 +1385,25 @@ int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
 
        /* only support initial module atm */
        map->module_entries_count = 1;
-       map->module_entries[0].module_id = module_id;
-       map->module_entries[0].entry_point = entry_point;
-
-       return 0;
-}
-
-int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
-       struct sst_hsw_stream *stream, u32 offset, u32 size)
-{
-       if (stream->commited) {
-               dev_err(hsw->dev, "error: stream committed for set pmem\n");
-               return -EINVAL;
-       }
-
-       stream->request.persistent_mem.offset = offset;
-       stream->request.persistent_mem.size = size;
-
-       return 0;
-}
-
-int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
-       struct sst_hsw_stream *stream, u32 offset, u32 size)
-{
-       if (stream->commited) {
-               dev_err(hsw->dev, "error: stream committed for set smem\n");
-               return -EINVAL;
-       }
-
-       stream->request.scratch_mem.offset = offset;
-       stream->request.scratch_mem.size = size;
+       map->module_entries[0].module_id = module->id;
+       map->module_entries[0].entry_point = module->entry;
+
+       stream->request.persistent_mem.offset =
+               sst_dsp_get_offset(dsp, runtime->persistent_offset, SST_MEM_DRAM);
+       stream->request.persistent_mem.size = module->persistent_size;
+
+       stream->request.scratch_mem.offset =
+               sst_dsp_get_offset(dsp, dsp->scratch_offset, SST_MEM_DRAM);
+       stream->request.scratch_mem.size = dsp->scratch_size;
+
+       dev_dbg(hsw->dev, "module %d runtime %d using:\n", module->id,
+               runtime->id);
+       dev_dbg(hsw->dev, " persistent offset 0x%x bytes 0x%x\n",
+               stream->request.persistent_mem.offset,
+               stream->request.persistent_mem.size);
+       dev_dbg(hsw->dev, " scratch offset 0x%x bytes 0x%x\n",
+               stream->request.scratch_mem.offset,
+               stream->request.scratch_mem.size);
 
        return 0;
 }
@@ -1630,6 +1637,10 @@ int sst_hsw_device_set_config(struct sst_hsw *hsw,
        config.clock_frequency = mclk;
        config.mode = mode;
        config.clock_divider = clock_divider;
+       if (mode == SST_HSW_DEVICE_TDM_CLOCK_MASTER)
+               config.channels = 4;
+       else
+               config.channels = 2;
 
        trace_hsw_device_config_req(&config);
 
@@ -1673,34 +1684,283 @@ int sst_hsw_dx_set_state(struct sst_hsw *hsw,
        dev_dbg(hsw->dev, "ipc: got %d entry numbers for state %d\n",
                dx->entries_no, state);
 
-       memcpy(&hsw->dx, dx, sizeof(*dx));
-       return 0;
+       return ret;
 }
 
-/* Used to save state into hsw->dx_reply */
-int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item,
-       u32 *offset, u32 *size, u32 *source)
+struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
+       int mod_id, int offset)
 {
-       struct sst_hsw_ipc_dx_memory_item *dx_mem;
-       struct sst_hsw_ipc_dx_reply *dx_reply;
-       int entry_no;
+       struct sst_dsp *dsp = hsw->dsp;
+       struct sst_module *module;
+       struct sst_module_runtime *runtime;
+       int err;
 
-       dx_reply = &hsw->dx;
-       entry_no = dx_reply->entries_no;
+       module = sst_module_get_from_id(dsp, mod_id);
+       if (module == NULL) {
+               dev_err(dsp->dev, "error: failed to get module %d for pcm\n",
+                       mod_id);
+               return NULL;
+       }
+
+       runtime = sst_module_runtime_new(module, mod_id, NULL);
+       if (runtime == NULL) {
+               dev_err(dsp->dev, "error: failed to create module %d runtime\n",
+                       mod_id);
+               return NULL;
+       }
+
+       err = sst_module_runtime_alloc_blocks(runtime, offset);
+       if (err < 0) {
+               dev_err(dsp->dev, "error: failed to alloc blocks for module %d runtime\n",
+                       mod_id);
+               sst_module_runtime_free(runtime);
+               return NULL;
+       }
+
+       dev_dbg(dsp->dev, "runtime id %d created for module %d\n", runtime->id,
+               mod_id);
+       return runtime;
+}
+
+void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime)
+{
+       sst_module_runtime_free_blocks(runtime);
+       sst_module_runtime_free(runtime);
+}
+
+#ifdef CONFIG_PM
+static int sst_hsw_dx_state_dump(struct sst_hsw *hsw)
+{
+       struct sst_dsp *sst = hsw->dsp;
+       u32 item, offset, size;
+       int ret = 0;
 
-       trace_ipc_request("PM get Dx state", entry_no);
+       trace_ipc_request("PM state dump. Items #", SST_HSW_MAX_DX_REGIONS);
 
-       if (item >= entry_no)
+       if (hsw->dx.entries_no > SST_HSW_MAX_DX_REGIONS) {
+               dev_err(hsw->dev,
+                       "error: number of FW context regions greater than %d\n",
+                       SST_HSW_MAX_DX_REGIONS);
+               memset(&hsw->dx, 0, sizeof(hsw->dx));
                return -EINVAL;
+       }
+
+       ret = sst_dsp_dma_get_channel(sst, 0);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+               return ret;
+       }
+
+       /* set on-demond mode on engine 0 channel 3 */
+       sst_dsp_shim_update_bits(sst, SST_HMDC,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH,
+                       SST_HMDC_HDDA_E0_ALLCH | SST_HMDC_HDDA_E1_ALLCH);
+
+       for (item = 0; item < hsw->dx.entries_no; item++) {
+               if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+                       && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+                       && hsw->dx.mem_info[item].offset <
+                       DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+                       offset = hsw->dx.mem_info[item].offset
+                                       - DSP_DRAM_ADDR_OFFSET;
+                       size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+                       ret = sst_dsp_dma_copyfrom(sst, hsw->dx_context_paddr + offset,
+                               sst->addr.lpe_base + offset, size);
+                       if (ret < 0) {
+                               dev_err(hsw->dev,
+                                       "error: FW context dump failed\n");
+                               memset(&hsw->dx, 0, sizeof(hsw->dx));
+                               goto out;
+                       }
+               }
+       }
+
+out:
+       sst_dsp_dma_put_channel(sst);
+       return ret;
+}
+
+static int sst_hsw_dx_state_restore(struct sst_hsw *hsw)
+{
+       struct sst_dsp *sst = hsw->dsp;
+       u32 item, offset, size;
+       int ret;
+
+       for (item = 0; item < hsw->dx.entries_no; item++) {
+               if (hsw->dx.mem_info[item].source == SST_HSW_DX_TYPE_MEMORY_DUMP
+                       && hsw->dx.mem_info[item].offset > DSP_DRAM_ADDR_OFFSET
+                       && hsw->dx.mem_info[item].offset <
+                       DSP_DRAM_ADDR_OFFSET + SST_HSW_DX_CONTEXT_SIZE) {
+
+                       offset = hsw->dx.mem_info[item].offset
+                                       - DSP_DRAM_ADDR_OFFSET;
+                       size = (hsw->dx.mem_info[item].size + 3) & (~3);
+
+                       ret = sst_dsp_dma_copyto(sst, sst->addr.lpe_base + offset,
+                               hsw->dx_context_paddr + offset, size);
+                       if (ret < 0) {
+                               dev_err(hsw->dev,
+                                       "error: FW context restore failed\n");
+                               return ret;
+                       }
+               }
+       }
+
+       return 0;
+}
+
+static void sst_hsw_drop_all(struct sst_hsw *hsw)
+{
+       struct ipc_message *msg, *tmp;
+       unsigned long flags;
+       int tx_drop_cnt = 0, rx_drop_cnt = 0;
 
-       dx_mem = &dx_reply->mem_info[item];
-       *offset = dx_mem->offset;
-       *size = dx_mem->size;
-       *source = dx_mem->source;
+       /* drop all TX and Rx messages before we stall + reset DSP */
+       spin_lock_irqsave(&hsw->dsp->spinlock, flags);
+
+       list_for_each_entry_safe(msg, tmp, &hsw->tx_list, list) {
+               list_move(&msg->list, &hsw->empty_list);
+               tx_drop_cnt++;
+       }
+
+       list_for_each_entry_safe(msg, tmp, &hsw->rx_list, list) {
+               list_move(&msg->list, &hsw->empty_list);
+               rx_drop_cnt++;
+       }
+
+       spin_unlock_irqrestore(&hsw->dsp->spinlock, flags);
+
+       if (tx_drop_cnt || rx_drop_cnt)
+               dev_err(hsw->dev, "dropped IPC msg RX=%d, TX=%d\n",
+                       tx_drop_cnt, rx_drop_cnt);
+}
+
+int sst_hsw_dsp_load(struct sst_hsw *hsw)
+{
+       struct sst_dsp *dsp = hsw->dsp;
+       int ret;
+
+       dev_dbg(hsw->dev, "loading audio DSP....");
+
+       ret = sst_dsp_wake(dsp);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: failed to wake audio DSP\n");
+               return -ENODEV;
+       }
+
+       ret = sst_dsp_dma_get_channel(dsp, 0);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+               return ret;
+       }
+
+       ret = sst_fw_reload(hsw->sst_fw);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: SST FW reload failed\n");
+               sst_dsp_dma_put_channel(dsp);
+               return -ENOMEM;
+       }
 
+       sst_dsp_dma_put_channel(dsp);
        return 0;
 }
 
+static int sst_hsw_dsp_restore(struct sst_hsw *hsw)
+{
+       struct sst_dsp *dsp = hsw->dsp;
+       int ret;
+
+       dev_dbg(hsw->dev, "restoring audio DSP....");
+
+       ret = sst_dsp_dma_get_channel(dsp, 0);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: cant allocate dma channel %d\n", ret);
+               return ret;
+       }
+
+       ret = sst_hsw_dx_state_restore(hsw);
+       if (ret < 0) {
+               dev_err(hsw->dev, "error: SST FW context restore failed\n");
+               sst_dsp_dma_put_channel(dsp);
+               return -ENOMEM;
+       }
+       sst_dsp_dma_put_channel(dsp);
+
+       /* wait for DSP boot completion */
+       sst_dsp_boot(dsp);
+
+       return ret;
+}
+
+int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw)
+{
+       int ret;
+
+       dev_dbg(hsw->dev, "audio dsp runtime suspend\n");
+
+       ret = sst_hsw_dx_set_state(hsw, SST_HSW_DX_STATE_D3, &hsw->dx);
+       if (ret < 0)
+               return ret;
+
+       sst_dsp_stall(hsw->dsp);
+
+       ret = sst_hsw_dx_state_dump(hsw);
+       if (ret < 0)
+               return ret;
+
+       sst_hsw_drop_all(hsw);
+
+       return 0;
+}
+
+int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw)
+{
+       sst_fw_unload(hsw->sst_fw);
+       sst_block_free_scratch(hsw->dsp);
+
+       hsw->boot_complete = false;
+
+       sst_dsp_sleep(hsw->dsp);
+
+       return 0;
+}
+
+int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw)
+{
+       struct device *dev = hsw->dev;
+       int ret;
+
+       dev_dbg(dev, "audio dsp runtime resume\n");
+
+       if (hsw->boot_complete)
+               return 1; /* tell caller no action is required */
+
+       ret = sst_hsw_dsp_restore(hsw);
+       if (ret < 0)
+               dev_err(dev, "error: audio DSP boot failure\n");
+
+       ret = wait_event_timeout(hsw->boot_wait, hsw->boot_complete,
+               msecs_to_jiffies(IPC_BOOT_MSECS));
+       if (ret == 0) {
+               dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
+                       sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
+                       sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
+               return -EIO;
+       }
+
+       /* Set ADSP SSP port settings */
+       ret = sst_hsw_device_set_config(hsw, SST_HSW_DEVICE_SSP_0,
+                                       SST_HSW_DEVICE_MCLK_FREQ_24_MHZ,
+                                       SST_HSW_DEVICE_CLOCK_MASTER, 9);
+       if (ret < 0)
+               dev_err(dev, "error: SSP re-initialization failed\n");
+
+       return ret;
+}
+#endif
+
 static int msg_empty_list_init(struct sst_hsw *hsw)
 {
        int i;
@@ -1718,12 +1978,6 @@ static int msg_empty_list_init(struct sst_hsw *hsw)
        return 0;
 }
 
-void sst_hsw_set_scratch_module(struct sst_hsw *hsw,
-       struct sst_module *scratch)
-{
-       hsw->scratch = scratch;
-}
-
 struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw)
 {
        return hsw->dsp;
@@ -1738,7 +1992,6 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 {
        struct sst_hsw_ipc_fw_version version;
        struct sst_hsw *hsw;
-       struct sst_fw *hsw_sst_fw;
        int ret;
 
        dev_dbg(dev, "initialising Audio DSP IPC\n");
@@ -1780,12 +2033,19 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
                goto dsp_err;
        }
 
+       /* allocate DMA buffer for context storage */
+       hsw->dx_context = dma_alloc_coherent(hsw->dsp->dma_dev,
+               SST_HSW_DX_CONTEXT_SIZE, &hsw->dx_context_paddr, GFP_KERNEL);
+       if (hsw->dx_context == NULL) {
+               ret = -ENOMEM;
+               goto dma_err;
+       }
+
        /* keep the DSP in reset state for base FW loading */
        sst_dsp_reset(hsw->dsp);
 
-       hsw_sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw);
-
-       if (hsw_sst_fw == NULL) {
+       hsw->sst_fw = sst_fw_new(hsw->dsp, pdata->fw, hsw);
+       if (hsw->sst_fw == NULL) {
                ret = -ENODEV;
                dev_err(dev, "error: failed to load firmware\n");
                goto fw_err;
@@ -1797,7 +2057,9 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
                msecs_to_jiffies(IPC_BOOT_MSECS));
        if (ret == 0) {
                ret = -EIO;
-               dev_err(hsw->dev, "error: ADSP boot timeout\n");
+               dev_err(hsw->dev, "error: audio DSP boot timeout IPCD 0x%x IPCX 0x%x\n",
+                       sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCD),
+                       sst_dsp_shim_read_unlocked(hsw->dsp, SST_IPCX));
                goto boot_err;
        }
 
@@ -1816,8 +2078,11 @@ int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata)
 
 boot_err:
        sst_dsp_reset(hsw->dsp);
-       sst_fw_free(hsw_sst_fw);
+       sst_fw_free(hsw->sst_fw);
 fw_err:
+       dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+                       hsw->dx_context, hsw->dx_context_paddr);
+dma_err:
        sst_dsp_free(hsw->dsp);
 dsp_err:
        kthread_stop(hsw->tx_thread);
@@ -1834,6 +2099,8 @@ void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata)
 
        sst_dsp_reset(hsw->dsp);
        sst_fw_free_all(hsw->dsp);
+       dma_free_coherent(hsw->dsp->dma_dev, SST_HSW_DX_CONTEXT_SIZE,
+                       hsw->dx_context, hsw->dx_context_paddr);
        sst_dsp_free(hsw->dsp);
        kfree(hsw->scratch);
        kthread_stop(hsw->tx_thread);
index 2ac194a..138e894 100644 (file)
 #include <linux/kernel.h>
 #include <linux/platform_device.h>
 
-#define SST_HSW_NO_CHANNELS            2
+#define SST_HSW_NO_CHANNELS            4
 #define SST_HSW_MAX_DX_REGIONS         14
+#define SST_HSW_DX_CONTEXT_SIZE        (640 * 1024)
+#define SST_HSW_CHANNELS_ALL           0xffffffff
 
 #define SST_HSW_FW_LOG_CONFIG_DWORDS   12
 #define SST_HSW_GLOBAL_LOG             15
@@ -40,6 +42,7 @@ struct sst_hsw_stream;
 struct sst_hsw_log_stream;
 struct sst_pdata;
 struct sst_module;
+struct sst_module_runtime;
 extern struct sst_ops haswell_ops;
 
 /* Stream Allocate Path ID */
@@ -84,6 +87,7 @@ enum sst_hsw_device_mclk {
 enum sst_hsw_device_mode {
        SST_HSW_DEVICE_CLOCK_SLAVE   = 0,
        SST_HSW_DEVICE_CLOCK_MASTER  = 1,
+       SST_HSW_DEVICE_TDM_CLOCK_MASTER = 2,
 };
 
 /* DX Power State */
@@ -295,7 +299,8 @@ struct sst_hsw_ipc_device_config_req {
        u32 clock_frequency;
        u32 mode;
        u16 clock_divider;
-       u16 reserved;
+       u8 channels;
+       u8 reserved;
 } __attribute__((packed));
 
 /* Audio Data formats */
@@ -430,8 +435,7 @@ int sst_hsw_stream_set_map_config(struct sst_hsw *hsw,
 int sst_hsw_stream_set_style(struct sst_hsw *hsw, struct sst_hsw_stream *stream,
        enum sst_hsw_interleaving style);
 int sst_hsw_stream_set_module_info(struct sst_hsw *hsw,
-       struct sst_hsw_stream *stream, enum sst_hsw_module_id module_id,
-       u32 entry_point);
+       struct sst_hsw_stream *stream, struct sst_module_runtime *runtime);
 int sst_hsw_stream_set_pmemory_info(struct sst_hsw *hsw,
        struct sst_hsw_stream *stream, u32 offset, u32 size);
 int sst_hsw_stream_set_smemory_info(struct sst_hsw *hsw,
@@ -484,7 +488,16 @@ int sst_hsw_dx_get_state(struct sst_hsw *hsw, u32 item,
 int sst_hsw_dsp_init(struct device *dev, struct sst_pdata *pdata);
 void sst_hsw_dsp_free(struct device *dev, struct sst_pdata *pdata);
 struct sst_dsp *sst_hsw_get_dsp(struct sst_hsw *hsw);
-void sst_hsw_set_scratch_module(struct sst_hsw *hsw,
-       struct sst_module *scratch);
+
+/* runtime module management */
+struct sst_module_runtime *sst_hsw_runtime_module_create(struct sst_hsw *hsw,
+       int mod_id, int offset);
+void sst_hsw_runtime_module_free(struct sst_module_runtime *runtime);
+
+/* PM */
+int sst_hsw_dsp_runtime_resume(struct sst_hsw *hsw);
+int sst_hsw_dsp_runtime_suspend(struct sst_hsw *hsw);
+int sst_hsw_dsp_load(struct sst_hsw *hsw);
+int sst_hsw_dsp_runtime_sleep(struct sst_hsw *hsw);
 
 #endif
index 4df867c..0180b38 100644 (file)
@@ -18,6 +18,7 @@
 #include <linux/dma-mapping.h>
 #include <linux/slab.h>
 #include <linux/delay.h>
+#include <linux/pm_runtime.h>
 #include <asm/page.h>
 #include <asm/pgtable.h>
 #include <sound/core.h>
@@ -73,6 +74,13 @@ static const u32 volume_map[] = {
 #define HSW_PCM_PERIODS_MAX    64
 #define HSW_PCM_PERIODS_MIN    2
 
+#define HSW_PCM_DAI_ID_SYSTEM  0
+#define HSW_PCM_DAI_ID_OFFLOAD0        1
+#define HSW_PCM_DAI_ID_OFFLOAD1        2
+#define HSW_PCM_DAI_ID_LOOPBACK        3
+#define HSW_PCM_DAI_ID_CAPTURE 4
+
+
 static const struct snd_pcm_hardware hsw_pcm_hardware = {
        .info                   = SNDRV_PCM_INFO_MMAP |
                                  SNDRV_PCM_INFO_MMAP_VALID |
@@ -89,22 +97,39 @@ static const struct snd_pcm_hardware hsw_pcm_hardware = {
        .buffer_bytes_max       = HSW_PCM_PERIODS_MAX * PAGE_SIZE,
 };
 
+struct hsw_pcm_module_map {
+       int dai_id;
+       enum sst_hsw_module_id mod_id;
+};
+
 /* private data for each PCM DSP stream */
 struct hsw_pcm_data {
        int dai_id;
        struct sst_hsw_stream *stream;
+       struct sst_module_runtime *runtime;
+       struct sst_module_runtime_context context;
+       struct snd_pcm *hsw_pcm;
        u32 volume[2];
        struct snd_pcm_substream *substream;
        struct snd_compr_stream *cstream;
        unsigned int wpos;
        struct mutex mutex;
        bool allocated;
+       int persistent_offset;
+};
+
+enum hsw_pm_state {
+       HSW_PM_STATE_D3 = 0,
+       HSW_PM_STATE_D0 = 1,
 };
 
 /* private data for the driver */
 struct hsw_priv_data {
        /* runtime DSP */
        struct sst_hsw *hsw;
+       struct device *dev;
+       enum hsw_pm_state pm_state;
+       struct snd_soc_card *soc_card;
 
        /* page tables */
        struct snd_dma_buffer dmab[HSW_PCM_COUNT][2];
@@ -138,21 +163,25 @@ static inline unsigned int hsw_ipc_to_mixer(u32 value)
 static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
+       struct hsw_priv_data *pdata =
+               snd_soc_platform_get_drvdata(platform);
        struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
 
        mutex_lock(&pcm_data->mutex);
+       pm_runtime_get_sync(pdata->dev);
 
        if (!pcm_data->stream) {
                pcm_data->volume[0] =
                        hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
                pcm_data->volume[1] =
                        hsw_mixer_to_ipc(ucontrol->value.integer.value[1]);
+               pm_runtime_mark_last_busy(pdata->dev);
+               pm_runtime_put_autosuspend(pdata->dev);
                mutex_unlock(&pcm_data->mutex);
                return 0;
        }
@@ -160,7 +189,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
        if (ucontrol->value.integer.value[0] ==
                ucontrol->value.integer.value[1]) {
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
-               sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 2, volume);
+               /* apply volume value to all channels */
+               sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, SST_HSW_CHANNELS_ALL, volume);
        } else {
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
                sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 0, volume);
@@ -168,6 +198,8 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
                sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0, 1, volume);
        }
 
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        mutex_unlock(&pcm_data->mutex);
        return 0;
 }
@@ -175,21 +207,25 @@ static int hsw_stream_volume_put(struct snd_kcontrol *kcontrol,
 static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
+       struct hsw_priv_data *pdata =
+               snd_soc_platform_get_drvdata(platform);
        struct hsw_pcm_data *pcm_data = &pdata->pcm[mc->reg];
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
 
        mutex_lock(&pcm_data->mutex);
+       pm_runtime_get_sync(pdata->dev);
 
        if (!pcm_data->stream) {
                ucontrol->value.integer.value[0] =
                        hsw_ipc_to_mixer(pcm_data->volume[0]);
                ucontrol->value.integer.value[1] =
                        hsw_ipc_to_mixer(pcm_data->volume[1]);
+               pm_runtime_mark_last_busy(pdata->dev);
+               pm_runtime_put_autosuspend(pdata->dev);
                mutex_unlock(&pcm_data->mutex);
                return 0;
        }
@@ -198,6 +234,9 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
        ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
        sst_hsw_stream_get_volume(hsw, pcm_data->stream, 0, 1, &volume);
        ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
+
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        mutex_unlock(&pcm_data->mutex);
 
        return 0;
@@ -206,16 +245,18 @@ static int hsw_stream_volume_get(struct snd_kcontrol *kcontrol,
 static int hsw_volume_put(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+       struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
        struct sst_hsw *hsw = pdata->hsw;
        u32 volume;
 
+       pm_runtime_get_sync(pdata->dev);
+
        if (ucontrol->value.integer.value[0] ==
                ucontrol->value.integer.value[1]) {
 
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
-               sst_hsw_mixer_set_volume(hsw, 0, 2, volume);
+               sst_hsw_mixer_set_volume(hsw, 0, SST_HSW_CHANNELS_ALL, volume);
 
        } else {
                volume = hsw_mixer_to_ipc(ucontrol->value.integer.value[0]);
@@ -225,23 +266,28 @@ static int hsw_volume_put(struct snd_kcontrol *kcontrol,
                sst_hsw_mixer_set_volume(hsw, 0, 1, volume);
        }
 
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        return 0;
 }
 
 static int hsw_volume_get(struct snd_kcontrol *kcontrol,
                                struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_component *cmpnt = snd_soc_kcontrol_component(kcontrol);
-       struct hsw_priv_data *pdata = snd_soc_component_get_drvdata(cmpnt);
+       struct snd_soc_platform *platform = snd_soc_kcontrol_platform(kcontrol);
+       struct hsw_priv_data *pdata = snd_soc_platform_get_drvdata(platform);
        struct sst_hsw *hsw = pdata->hsw;
        unsigned int volume = 0;
 
+       pm_runtime_get_sync(pdata->dev);
        sst_hsw_mixer_get_volume(hsw, 0, 0, &volume);
        ucontrol->value.integer.value[0] = hsw_ipc_to_mixer(volume);
 
        sst_hsw_mixer_get_volume(hsw, 0, 1, &volume);
        ucontrol->value.integer.value[1] = hsw_ipc_to_mixer(volume);
 
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        return 0;
 }
 
@@ -252,23 +298,19 @@ static const DECLARE_TLV_DB_SCALE(hsw_vol_tlv, -9000, 300, 1);
 static const struct snd_kcontrol_new hsw_volume_controls[] = {
        /* Global DSP volume */
        SOC_DOUBLE_EXT_TLV("Master Playback Volume", 0, 0, 8,
-               ARRAY_SIZE(volume_map) -1, 0,
+               ARRAY_SIZE(volume_map) - 1, 0,
                hsw_volume_get, hsw_volume_put, hsw_vol_tlv),
        /* Offload 0 volume */
        SOC_DOUBLE_EXT_TLV("Media0 Playback Volume", 1, 0, 8,
-               ARRAY_SIZE(volume_map), 0,
+               ARRAY_SIZE(volume_map) - 1, 0,
                hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
        /* Offload 1 volume */
        SOC_DOUBLE_EXT_TLV("Media1 Playback Volume", 2, 0, 8,
-               ARRAY_SIZE(volume_map), 0,
-               hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
-       /* Loopback volume */
-       SOC_DOUBLE_EXT_TLV("Loopback Capture Volume", 3, 0, 8,
-               ARRAY_SIZE(volume_map), 0,
+               ARRAY_SIZE(volume_map) - 1, 0,
                hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
        /* Mic Capture volume */
-       SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 4, 0, 8,
-               ARRAY_SIZE(volume_map), 0,
+       SOC_DOUBLE_EXT_TLV("Mic Capture Volume", 0, 0, 8,
+               ARRAY_SIZE(volume_map) - 1, 0,
                hsw_stream_volume_get, hsw_stream_volume_put, hsw_vol_tlv),
 };
 
@@ -354,8 +396,14 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
        /* DSP stream type depends on DAI ID */
        switch (rtd->cpu_dai->id) {
        case 0:
-               stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
-               module_id = SST_HSW_MODULE_PCM_SYSTEM;
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+                       stream_type = SST_HSW_STREAM_TYPE_SYSTEM;
+                       module_id = SST_HSW_MODULE_PCM_SYSTEM;
+               }
+               else {
+                       stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
+                       module_id = SST_HSW_MODULE_PCM_CAPTURE;
+               }
                break;
        case 1:
        case 2:
@@ -368,10 +416,6 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
                path_id = SST_HSW_STREAM_PATH_SSP0_OUT;
                module_id = SST_HSW_MODULE_PCM_REFERENCE;
                break;
-       case 4:
-               stream_type = SST_HSW_STREAM_TYPE_CAPTURE;
-               module_id = SST_HSW_MODULE_PCM_CAPTURE;
-               break;
        default:
                dev_err(rtd->dev, "error: invalid DAI ID %d\n",
                        rtd->cpu_dai->id);
@@ -421,13 +465,7 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
                return ret;
        }
 
-       /* we only support stereo atm */
        channels = params_channels(params);
-       if (channels != 2) {
-               dev_err(rtd->dev, "error: invalid channels %d\n", channels);
-               return -EINVAL;
-       }
-
        map = create_channel_map(SST_HSW_CHANNEL_CONFIG_STEREO);
        sst_hsw_stream_set_map_config(hsw, pcm_data->stream,
                        map, SST_HSW_CHANNEL_CONFIG_STEREO);
@@ -478,35 +516,23 @@ static int hsw_pcm_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       /* we use hardcoded memory offsets atm, will be updated for new FW */
-       if (stream_type == SST_HSW_STREAM_TYPE_CAPTURE) {
-               sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
-                       SST_HSW_MODULE_PCM_CAPTURE, module_data->entry);
-               sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-                       0x449400, 0x4000);
-               sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-                       0x400000, 0);
-       } else { /* stream_type == SST_HSW_STREAM_TYPE_SYSTEM */
-               sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
-                       SST_HSW_MODULE_PCM_SYSTEM, module_data->entry);
-
-               sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-                       module_data->offset, module_data->size);
-               sst_hsw_stream_set_pmemory_info(hsw, pcm_data->stream,
-                       0x44d400, 0x3800);
-
-               sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-                       module_data->offset, module_data->size);
-               sst_hsw_stream_set_smemory_info(hsw, pcm_data->stream,
-                       0x400000, 0);
-       }
+       sst_hsw_stream_set_module_info(hsw, pcm_data->stream,
+               pcm_data->runtime);
 
        ret = sst_hsw_stream_commit(hsw, pcm_data->stream);
        if (ret < 0) {
                dev_err(rtd->dev, "error: failed to commit stream %d\n", ret);
                return ret;
        }
-       pcm_data->allocated = true;
+
+       if (!pcm_data->allocated) {
+               /* Set previous saved volume */
+               sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+                               0, pcm_data->volume[0]);
+               sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
+                               1, pcm_data->volume[1]);
+               pcm_data->allocated = true;
+       }
 
        ret = sst_hsw_stream_pause(hsw, pcm_data->stream, 1);
        if (ret < 0)
@@ -558,7 +584,7 @@ static u32 hsw_notify_pointer(struct sst_hsw_stream *stream, void *data)
        pos = frames_to_bytes(runtime,
                (runtime->control->appl_ptr % runtime->buffer_size));
 
-       dev_dbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
+       dev_vdbg(rtd->dev, "PCM: App pointer %d bytes\n", pos);
 
        /* let alsa know we have play a period */
        snd_pcm_period_elapsed(substream);
@@ -580,7 +606,7 @@ static snd_pcm_uframes_t hsw_pcm_pointer(struct snd_pcm_substream *substream)
        offset = bytes_to_frames(runtime, position);
        ppos = sst_hsw_get_dsp_presentation_position(hsw, pcm_data->stream);
 
-       dev_dbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
+       dev_vdbg(rtd->dev, "PCM: DMA pointer %du bytes, pos %llu\n",
                position, ppos);
        return offset;
 }
@@ -596,6 +622,7 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
        pcm_data = &pdata->pcm[rtd->cpu_dai->id];
 
        mutex_lock(&pcm_data->mutex);
+       pm_runtime_get_sync(pdata->dev);
 
        snd_soc_pcm_set_drvdata(rtd, pcm_data);
        pcm_data->substream = substream;
@@ -606,16 +633,12 @@ static int hsw_pcm_open(struct snd_pcm_substream *substream)
                hsw_notify_pointer, pcm_data);
        if (pcm_data->stream == NULL) {
                dev_err(rtd->dev, "error: failed to create stream\n");
+               pm_runtime_mark_last_busy(pdata->dev);
+               pm_runtime_put_autosuspend(pdata->dev);
                mutex_unlock(&pcm_data->mutex);
                return -EINVAL;
        }
 
-       /* Set previous saved volume */
-       sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
-                       0, pcm_data->volume[0]);
-       sst_hsw_stream_set_volume(hsw, pcm_data->stream, 0,
-                       1, pcm_data->volume[1]);
-
        mutex_unlock(&pcm_data->mutex);
        return 0;
 }
@@ -645,6 +668,8 @@ static int hsw_pcm_close(struct snd_pcm_substream *substream)
        pcm_data->stream = NULL;
 
 out:
+       pm_runtime_mark_last_busy(pdata->dev);
+       pm_runtime_put_autosuspend(pdata->dev);
        mutex_unlock(&pcm_data->mutex);
        return ret;
 }
@@ -660,6 +685,56 @@ static struct snd_pcm_ops hsw_pcm_ops = {
        .page           = snd_pcm_sgbuf_ops_page,
 };
 
+/* static mappings between PCMs and modules - may be dynamic in future */
+static struct hsw_pcm_module_map mod_map[] = {
+       {HSW_PCM_DAI_ID_SYSTEM, SST_HSW_MODULE_PCM_SYSTEM},
+       {HSW_PCM_DAI_ID_OFFLOAD0, SST_HSW_MODULE_PCM},
+       {HSW_PCM_DAI_ID_OFFLOAD1, SST_HSW_MODULE_PCM},
+       {HSW_PCM_DAI_ID_LOOPBACK, SST_HSW_MODULE_PCM_REFERENCE},
+       {HSW_PCM_DAI_ID_CAPTURE, SST_HSW_MODULE_PCM_CAPTURE},
+};
+
+static int hsw_pcm_create_modules(struct hsw_priv_data *pdata)
+{
+       struct sst_hsw *hsw = pdata->hsw;
+       struct hsw_pcm_data *pcm_data;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+               pcm_data = &pdata->pcm[i];
+
+               /* create new runtime module, use same offset if recreated */
+               pcm_data->runtime = sst_hsw_runtime_module_create(hsw,
+                       mod_map[i].mod_id, pcm_data->persistent_offset);
+               if (pcm_data->runtime == NULL)
+                       goto err;
+               pcm_data->persistent_offset =
+                       pcm_data->runtime->persistent_offset;
+       }
+
+       return 0;
+
+err:
+       for (--i; i >= 0; i--) {
+               pcm_data = &pdata->pcm[i];
+               sst_hsw_runtime_module_free(pcm_data->runtime);
+       }
+
+       return -ENODEV;
+}
+
+static void hsw_pcm_free_modules(struct hsw_priv_data *pdata)
+{
+       struct hsw_pcm_data *pcm_data;
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(mod_map); i++) {
+               pcm_data = &pdata->pcm[i];
+
+               sst_hsw_runtime_module_free(pcm_data->runtime);
+       }
+}
+
 static void hsw_pcm_free(struct snd_pcm *pcm)
 {
        snd_pcm_lib_preallocate_free_for_all(pcm);
@@ -670,6 +745,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
        struct snd_pcm *pcm = rtd->pcm;
        struct snd_soc_platform *platform = rtd->platform;
        struct sst_pdata *pdata = dev_get_platdata(platform->dev);
+       struct hsw_priv_data *priv_data = dev_get_drvdata(platform->dev);
        struct device *dev = pdata->dma_dev;
        int ret = 0;
 
@@ -686,6 +762,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
                        return ret;
                }
        }
+       priv_data->pcm[rtd->cpu_dai->id].hsw_pcm = pcm;
 
        return ret;
 }
@@ -696,6 +773,7 @@ static int hsw_pcm_new(struct snd_soc_pcm_runtime *rtd)
 static struct snd_soc_dai_driver hsw_dais[] = {
        {
                .name  = "System Pin",
+               .id = HSW_PCM_DAI_ID_SYSTEM,
                .playback = {
                        .stream_name = "System Playback",
                        .channels_min = 2,
@@ -703,10 +781,18 @@ static struct snd_soc_dai_driver hsw_dais[] = {
                        .rates = SNDRV_PCM_RATE_48000,
                        .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
                },
+               .capture = {
+                       .stream_name = "Analog Capture",
+                       .channels_min = 2,
+                       .channels_max = 4,
+                       .rates = SNDRV_PCM_RATE_48000,
+                       .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
+               },
        },
        {
                /* PCM */
                .name  = "Offload0 Pin",
+               .id = HSW_PCM_DAI_ID_OFFLOAD0,
                .playback = {
                        .stream_name = "Offload0 Playback",
                        .channels_min = 2,
@@ -718,6 +804,7 @@ static struct snd_soc_dai_driver hsw_dais[] = {
        {
                /* PCM */
                .name  = "Offload1 Pin",
+               .id = HSW_PCM_DAI_ID_OFFLOAD1,
                .playback = {
                        .stream_name = "Offload1 Playback",
                        .channels_min = 2,
@@ -728,6 +815,7 @@ static struct snd_soc_dai_driver hsw_dais[] = {
        },
        {
                .name  = "Loopback Pin",
+               .id = HSW_PCM_DAI_ID_LOOPBACK,
                .capture = {
                        .stream_name = "Loopback Capture",
                        .channels_min = 2,
@@ -736,16 +824,6 @@ static struct snd_soc_dai_driver hsw_dais[] = {
                        .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
                },
        },
-       {
-               .name  = "Capture Pin",
-               .capture = {
-                       .stream_name = "Analog Capture",
-                       .channels_min = 2,
-                       .channels_max = 2,
-                       .rates = SNDRV_PCM_RATE_48000,
-                       .formats = SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE,
-               },
-       },
 };
 
 static const struct snd_soc_dapm_widget widgets[] = {
@@ -776,9 +854,20 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
 {
        struct hsw_priv_data *priv_data = snd_soc_platform_get_drvdata(platform);
        struct sst_pdata *pdata = dev_get_platdata(platform->dev);
-       struct device *dma_dev = pdata->dma_dev;
+       struct device *dma_dev, *dev;
        int i, ret = 0;
 
+       if (!pdata)
+               return -ENODEV;
+
+       dev = platform->dev;
+       dma_dev = pdata->dma_dev;
+
+       priv_data->hsw = pdata->dsp;
+       priv_data->dev = platform->dev;
+       priv_data->pm_state = HSW_PM_STATE_D0;
+       priv_data->soc_card = platform->component.card;
+
        /* allocate DSP buffer page tables */
        for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
 
@@ -801,6 +890,16 @@ static int hsw_pcm_probe(struct snd_soc_platform *platform)
                }
        }
 
+       /* allocate runtime modules */
+       hsw_pcm_create_modules(priv_data);
+
+       /* enable runtime PM with auto suspend */
+       pm_runtime_set_autosuspend_delay(platform->dev,
+               SST_RUNTIME_SUSPEND_DELAY);
+       pm_runtime_use_autosuspend(platform->dev);
+       pm_runtime_enable(platform->dev);
+       pm_runtime_idle(platform->dev);
+
        return 0;
 
 err:
@@ -819,6 +918,9 @@ static int hsw_pcm_remove(struct snd_soc_platform *platform)
                snd_soc_platform_get_drvdata(platform);
        int i;
 
+       pm_runtime_disable(platform->dev);
+       hsw_pcm_free_modules(priv_data);
+
        for (i = 0; i < ARRAY_SIZE(hsw_dais); i++) {
                if (hsw_dais[i].playback.channels_min)
                        snd_dma_free_pages(&priv_data->dmab[i][0]);
@@ -896,10 +998,181 @@ static int hsw_pcm_dev_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_RUNTIME
+
+static int hsw_pcm_runtime_idle(struct device *dev)
+{
+       return 0;
+}
+
+static int hsw_pcm_runtime_suspend(struct device *dev)
+{
+       struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+       struct sst_hsw *hsw = pdata->hsw;
+
+       if (pdata->pm_state == HSW_PM_STATE_D3)
+               return 0;
+
+       sst_hsw_dsp_runtime_suspend(hsw);
+       sst_hsw_dsp_runtime_sleep(hsw);
+       pdata->pm_state = HSW_PM_STATE_D3;
+
+       return 0;
+}
+
+static int hsw_pcm_runtime_resume(struct device *dev)
+{
+       struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+       struct sst_hsw *hsw = pdata->hsw;
+       int ret;
+
+       if (pdata->pm_state == HSW_PM_STATE_D0)
+               return 0;
+
+       ret = sst_hsw_dsp_load(hsw);
+       if (ret < 0) {
+               dev_err(dev, "failed to reload %d\n", ret);
+               return ret;
+       }
+
+       ret = hsw_pcm_create_modules(pdata);
+       if (ret < 0) {
+               dev_err(dev, "failed to create modules %d\n", ret);
+               return ret;
+       }
+
+       ret = sst_hsw_dsp_runtime_resume(hsw);
+       if (ret < 0)
+               return ret;
+       else if (ret == 1) /* no action required */
+               return 0;
+
+       pdata->pm_state = HSW_PM_STATE_D0;
+       return ret;
+}
+
+#else
+#define hsw_pcm_runtime_idle           NULL
+#define hsw_pcm_runtime_suspend                NULL
+#define hsw_pcm_runtime_resume         NULL
+#endif
+
+#if defined(CONFIG_PM_SLEEP) && defined(CONFIG_PM_RUNTIME)
+
+static void hsw_pcm_complete(struct device *dev)
+{
+       struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+       struct sst_hsw *hsw = pdata->hsw;
+       struct hsw_pcm_data *pcm_data;
+       int i, err;
+
+       if (pdata->pm_state == HSW_PM_STATE_D0)
+               return;
+
+       err = sst_hsw_dsp_load(hsw);
+       if (err < 0) {
+               dev_err(dev, "failed to reload %d\n", err);
+               return;
+       }
+
+       err = hsw_pcm_create_modules(pdata);
+       if (err < 0) {
+               dev_err(dev, "failed to create modules %d\n", err);
+               return;
+       }
+
+       for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+               pcm_data = &pdata->pcm[i];
+
+               if (!pcm_data->substream)
+                       continue;
+
+               err = sst_module_runtime_restore(pcm_data->runtime,
+                       &pcm_data->context);
+               if (err < 0)
+                       dev_err(dev, "failed to restore context for PCM %d\n", i);
+       }
+
+       snd_soc_resume(pdata->soc_card->dev);
+
+       err = sst_hsw_dsp_runtime_resume(hsw);
+       if (err < 0)
+               return;
+       else if (err == 1) /* no action required */
+               return;
+
+       pdata->pm_state = HSW_PM_STATE_D0;
+       return;
+}
+
+static int hsw_pcm_prepare(struct device *dev)
+{
+       struct hsw_priv_data *pdata = dev_get_drvdata(dev);
+       struct sst_hsw *hsw = pdata->hsw;
+       struct hsw_pcm_data *pcm_data;
+       int i, err;
+
+       if (pdata->pm_state == HSW_PM_STATE_D3)
+               return 0;
+       /* suspend all active streams */
+       for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+               pcm_data = &pdata->pcm[i];
+
+               if (!pcm_data->substream)
+                       continue;
+               dev_dbg(dev, "suspending pcm %d\n", i);
+               snd_pcm_suspend_all(pcm_data->hsw_pcm);
+
+               /* We need to wait until the DSP FW stops the streams */
+               msleep(2);
+       }
+
+       snd_soc_suspend(pdata->soc_card->dev);
+       snd_soc_poweroff(pdata->soc_card->dev);
+
+       /* enter D3 state and stall */
+       sst_hsw_dsp_runtime_suspend(hsw);
+
+       /* preserve persistent memory */
+       for (i = 0; i < HSW_PCM_DAI_ID_CAPTURE + 1; i++) {
+               pcm_data = &pdata->pcm[i];
+
+               if (!pcm_data->substream)
+                       continue;
+
+               dev_dbg(dev, "saving context pcm %d\n", i);
+               err = sst_module_runtime_save(pcm_data->runtime,
+                       &pcm_data->context);
+               if (err < 0)
+                       dev_err(dev, "failed to save context for PCM %d\n", i);
+       }
+
+       /* put the DSP to sleep */
+       sst_hsw_dsp_runtime_sleep(hsw);
+       pdata->pm_state = HSW_PM_STATE_D3;
+
+       return 0;
+}
+
+#else
+#define hsw_pcm_prepare                NULL
+#define hsw_pcm_complete       NULL
+#endif
+
+static const struct dev_pm_ops hsw_pcm_pm = {
+       .runtime_idle = hsw_pcm_runtime_idle,
+       .runtime_suspend = hsw_pcm_runtime_suspend,
+       .runtime_resume = hsw_pcm_runtime_resume,
+       .prepare = hsw_pcm_prepare,
+       .complete = hsw_pcm_complete,
+};
+
 static struct platform_driver hsw_pcm_driver = {
        .driver = {
                .name = "haswell-pcm-audio",
                .owner = THIS_MODULE,
+               .pm = &hsw_pcm_pm,
+
        },
 
        .probe = hsw_pcm_dev_probe,
index 5946777..3951689 100644 (file)
@@ -67,8 +67,11 @@ static int sst_platform_compr_open(struct snd_compr_stream *cstream)
                goto out_ops;
        }
        stream->compr_ops = sst->compr_ops;
-
        stream->id = 0;
+
+       /* Turn on LPE */
+       sst->compr_ops->power(sst->dev, true);
+
        sst_set_stream_status(stream, SST_PLATFORM_INIT);
        runtime->private_data = stream;
        return 0;
@@ -83,6 +86,9 @@ static int sst_platform_compr_free(struct snd_compr_stream *cstream)
        int ret_val = 0, str_id;
 
        stream = cstream->runtime->private_data;
+       /* Turn off LPE */
+       sst->compr_ops->power(sst->dev, false);
+
        /*need to check*/
        str_id = stream->id;
        if (str_id)
index aa9b600..6032f18 100644 (file)
@@ -101,35 +101,11 @@ static struct sst_dev_stream_map dpcm_strm_map[] = {
        {MERR_DPCM_AUDIO, 0, SNDRV_PCM_STREAM_CAPTURE, PIPE_PCM1_OUT, SST_TASK_ID_MEDIA, 0},
 };
 
-/* MFLD - MSIC */
-static struct snd_soc_dai_driver sst_platform_dai[] = {
+static int sst_media_digital_mute(struct snd_soc_dai *dai, int mute, int stream)
 {
-       .name = "Headset-cpu-dai",
-       .id = 0,
-       .playback = {
-               .channels_min = SST_STEREO,
-               .channels_max = SST_STEREO,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S24_LE,
-       },
-       .capture = {
-               .channels_min = 1,
-               .channels_max = 5,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S24_LE,
-       },
-},
-{
-       .name = "Compress-cpu-dai",
-       .compress_dai = 1,
-       .playback = {
-               .channels_min = SST_STEREO,
-               .channels_max = SST_STEREO,
-               .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,
-       },
-},
-};
+
+       return sst_send_pipe_gains(dai, stream, mute);
+}
 
 /* helper functions */
 void sst_set_stream_status(struct sst_runtime_stream *stream,
@@ -451,12 +427,133 @@ static int sst_media_hw_free(struct snd_pcm_substream *substream,
        return snd_pcm_lib_free_pages(substream);
 }
 
+static int sst_enable_ssp(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai)
+{
+       int ret = 0;
+
+       if (!dai->active) {
+               ret = sst_handle_vb_timer(dai, true);
+               if (ret)
+                       return ret;
+               ret = send_ssp_cmd(dai, dai->name, 1);
+       }
+       return ret;
+}
+
+static void sst_disable_ssp(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *dai)
+{
+       if (!dai->active) {
+               send_ssp_cmd(dai, dai->name, 0);
+               sst_handle_vb_timer(dai, false);
+       }
+}
+
 static struct snd_soc_dai_ops sst_media_dai_ops = {
        .startup = sst_media_open,
        .shutdown = sst_media_close,
        .prepare = sst_media_prepare,
        .hw_params = sst_media_hw_params,
        .hw_free = sst_media_hw_free,
+       .mute_stream = sst_media_digital_mute,
+};
+
+static struct snd_soc_dai_ops sst_compr_dai_ops = {
+       .mute_stream = sst_media_digital_mute,
+};
+
+static struct snd_soc_dai_ops sst_be_dai_ops = {
+       .startup = sst_enable_ssp,
+       .shutdown = sst_disable_ssp,
+};
+
+static struct snd_soc_dai_driver sst_platform_dai[] = {
+{
+       .name = "media-cpu-dai",
+       .ops = &sst_media_dai_ops,
+       .playback = {
+               .stream_name = "Headset Playback",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "Headset Capture",
+               .channels_min = 1,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_44100|SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+{
+       .name = "compress-cpu-dai",
+       .compress_dai = 1,
+       .ops = &sst_compr_dai_ops,
+       .playback = {
+               .stream_name = "Compress Playback",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+/* BE CPU  Dais */
+{
+       .name = "ssp0-port",
+       .ops = &sst_be_dai_ops,
+       .playback = {
+               .stream_name = "ssp0 Tx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp0 Rx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+{
+       .name = "ssp1-port",
+       .ops = &sst_be_dai_ops,
+       .playback = {
+               .stream_name = "ssp1 Tx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp1 Rx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_8000|SNDRV_PCM_RATE_16000|SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
+{
+       .name = "ssp2-port",
+       .ops = &sst_be_dai_ops,
+       .playback = {
+               .stream_name = "ssp2 Tx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp2 Rx",
+               .channels_min = SST_STEREO,
+               .channels_max = SST_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
 };
 
 static int sst_platform_open(struct snd_pcm_substream *substream)
@@ -609,6 +706,7 @@ static int sst_platform_probe(struct platform_device *pdev)
        pdata->pdev_strm_map = dpcm_strm_map;
        pdata->strm_map_size = ARRAY_SIZE(dpcm_strm_map);
        drv->pdata = pdata;
+       drv->pdev = pdev;
        mutex_init(&drv->lock);
        dev_set_drvdata(&pdev->dev, drv);
 
index 19f83ec..79c8d12 100644 (file)
@@ -117,6 +117,7 @@ struct compress_sst_ops {
        int (*get_codec_caps)(struct snd_compr_codec_caps *codec);
        int (*set_metadata)(struct device *dev, unsigned int str_id,
                        struct snd_compr_metadata *mdata);
+       int (*power)(struct device *dev, bool state);
 };
 
 struct sst_ops {
@@ -153,6 +154,10 @@ struct sst_device {
 struct sst_data;
 
 int sst_dsp_init_v2_dpcm(struct snd_soc_platform *platform);
+int sst_send_pipe_gains(struct snd_soc_dai *dai, int stream, int mute);
+int send_ssp_cmd(struct snd_soc_dai *dai, const char *id, bool enable);
+int sst_handle_vb_timer(struct snd_soc_dai *dai, bool enable);
+
 void sst_set_stream_status(struct sst_runtime_stream *stream, int state);
 int sst_fill_stream_params(void *substream, const struct sst_data *ctx,
                           struct snd_sst_params *str_params, bool is_compress);
diff --git a/sound/soc/intel/sst/Makefile b/sound/soc/intel/sst/Makefile
new file mode 100644 (file)
index 0000000..fd21726
--- /dev/null
@@ -0,0 +1,7 @@
+snd-intel-sst-core-objs := sst.o sst_ipc.o sst_stream.o sst_drv_interface.o sst_loader.o sst_pvt.o
+snd-intel-sst-pci-objs += sst_pci.o
+snd-intel-sst-acpi-objs += sst_acpi.o
+
+obj-$(CONFIG_SND_SST_IPC) += snd-intel-sst-core.o
+obj-$(CONFIG_SND_SST_IPC_PCI) += snd-intel-sst-pci.o
+obj-$(CONFIG_SND_SST_IPC_ACPI) += snd-intel-sst-acpi.o
diff --git a/sound/soc/intel/sst/sst.c b/sound/soc/intel/sst/sst.c
new file mode 100644 (file)
index 0000000..8a8d56a
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ *  sst.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14      Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/module.h>
+#include <linux/fs.h>
+#include <linux/interrupt.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/async.h>
+#include <linux/acpi.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine Driver");
+MODULE_LICENSE("GPL v2");
+
+static inline bool sst_is_process_reply(u32 msg_id)
+{
+       return ((msg_id & PROCESS_MSG) ? true : false);
+}
+
+static inline bool sst_validate_mailbox_size(unsigned int size)
+{
+       return ((size <= SST_MAILBOX_SIZE) ? true : false);
+}
+
+static irqreturn_t intel_sst_interrupt_mrfld(int irq, void *context)
+{
+       union interrupt_reg_mrfld isr;
+       union ipc_header_mrfld header;
+       union sst_imr_reg_mrfld imr;
+       struct ipc_post *msg = NULL;
+       unsigned int size = 0;
+       struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+       irqreturn_t retval = IRQ_HANDLED;
+
+       /* Interrupt arrived, check src */
+       isr.full = sst_shim_read64(drv->shim, SST_ISRX);
+
+       if (isr.part.done_interrupt) {
+               /* Clear done bit */
+               spin_lock(&drv->ipc_spin_lock);
+               header.full = sst_shim_read64(drv->shim,
+                                       drv->ipc_reg.ipcx);
+               header.p.header_high.part.done = 0;
+               sst_shim_write64(drv->shim, drv->ipc_reg.ipcx, header.full);
+
+               /* write 1 to clear status register */;
+               isr.part.done_interrupt = 1;
+               sst_shim_write64(drv->shim, SST_ISRX, isr.full);
+               spin_unlock(&drv->ipc_spin_lock);
+
+               /* we can send more messages to DSP so trigger work */
+               queue_work(drv->post_msg_wq, &drv->ipc_post_msg_wq);
+               retval = IRQ_HANDLED;
+       }
+
+       if (isr.part.busy_interrupt) {
+               /* message from dsp so copy that */
+               spin_lock(&drv->ipc_spin_lock);
+               imr.full = sst_shim_read64(drv->shim, SST_IMRX);
+               imr.part.busy_interrupt = 1;
+               sst_shim_write64(drv->shim, SST_IMRX, imr.full);
+               spin_unlock(&drv->ipc_spin_lock);
+               header.full =  sst_shim_read64(drv->shim, drv->ipc_reg.ipcd);
+
+               if (sst_create_ipc_msg(&msg, header.p.header_high.part.large)) {
+                       drv->ops->clear_interrupt(drv);
+                       return IRQ_HANDLED;
+               }
+
+               if (header.p.header_high.part.large) {
+                       size = header.p.header_low_payload;
+                       if (sst_validate_mailbox_size(size)) {
+                               memcpy_fromio(msg->mailbox_data,
+                                       drv->mailbox + drv->mailbox_recv_offset, size);
+                       } else {
+                               dev_err(drv->dev,
+                                       "Mailbox not copied, payload size is: %u\n", size);
+                               header.p.header_low_payload = 0;
+                       }
+               }
+
+               msg->mrfld_header = header;
+               msg->is_process_reply =
+                       sst_is_process_reply(header.p.header_high.part.msg_id);
+               spin_lock(&drv->rx_msg_lock);
+               list_add_tail(&msg->node, &drv->rx_list);
+               spin_unlock(&drv->rx_msg_lock);
+               drv->ops->clear_interrupt(drv);
+               retval = IRQ_WAKE_THREAD;
+       }
+       return retval;
+}
+
+static irqreturn_t intel_sst_irq_thread_mrfld(int irq, void *context)
+{
+       struct intel_sst_drv *drv = (struct intel_sst_drv *) context;
+       struct ipc_post *__msg, *msg = NULL;
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+       if (list_empty(&drv->rx_list)) {
+               spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+               return IRQ_HANDLED;
+       }
+
+       list_for_each_entry_safe(msg, __msg, &drv->rx_list, node) {
+               list_del(&msg->node);
+               spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+               if (msg->is_process_reply)
+                       drv->ops->process_message(msg);
+               else
+                       drv->ops->process_reply(drv, msg);
+
+               if (msg->is_large)
+                       kfree(msg->mailbox_data);
+               kfree(msg);
+               spin_lock_irqsave(&drv->rx_msg_lock, irq_flags);
+       }
+       spin_unlock_irqrestore(&drv->rx_msg_lock, irq_flags);
+       return IRQ_HANDLED;
+}
+
+static int sst_save_dsp_context_v2(struct intel_sst_drv *sst)
+{
+       int ret = 0;
+
+       ret = sst_prepare_and_post_msg(sst, SST_TASK_ID_MEDIA, IPC_CMD,
+                       IPC_PREP_D3, PIPE_RSVD, 0, NULL, NULL,
+                       true, true, false, true);
+
+       if (ret < 0) {
+               dev_err(sst->dev, "not suspending FW!!, Err: %d\n", ret);
+               return -EIO;
+       }
+
+       return 0;
+}
+
+
+static struct intel_sst_ops mrfld_ops = {
+       .interrupt = intel_sst_interrupt_mrfld,
+       .irq_thread = intel_sst_irq_thread_mrfld,
+       .clear_interrupt = intel_sst_clear_intr_mrfld,
+       .start = sst_start_mrfld,
+       .reset = intel_sst_reset_dsp_mrfld,
+       .post_message = sst_post_message_mrfld,
+       .process_reply = sst_process_reply_mrfld,
+       .save_dsp_context =  sst_save_dsp_context_v2,
+       .alloc_stream = sst_alloc_stream_mrfld,
+       .post_download = sst_post_download_mrfld,
+};
+
+int sst_driver_ops(struct intel_sst_drv *sst)
+{
+
+       switch (sst->dev_id) {
+       case SST_MRFLD_PCI_ID:
+       case SST_BYT_ACPI_ID:
+       case SST_CHV_ACPI_ID:
+               sst->tstamp = SST_TIME_STAMP_MRFLD;
+               sst->ops = &mrfld_ops;
+               return 0;
+
+       default:
+               dev_err(sst->dev,
+                       "SST Driver capablities missing for dev_id: %x", sst->dev_id);
+               return -EINVAL;
+       };
+}
+
+void sst_process_pending_msg(struct work_struct *work)
+{
+       struct intel_sst_drv *ctx = container_of(work,
+                       struct intel_sst_drv, ipc_post_msg_wq);
+
+       ctx->ops->post_message(ctx, NULL, false);
+}
+
+static int sst_workqueue_init(struct intel_sst_drv *ctx)
+{
+       INIT_LIST_HEAD(&ctx->memcpy_list);
+       INIT_LIST_HEAD(&ctx->rx_list);
+       INIT_LIST_HEAD(&ctx->ipc_dispatch_list);
+       INIT_LIST_HEAD(&ctx->block_list);
+       INIT_WORK(&ctx->ipc_post_msg_wq, sst_process_pending_msg);
+       init_waitqueue_head(&ctx->wait_queue);
+
+       ctx->post_msg_wq =
+               create_singlethread_workqueue("sst_post_msg_wq");
+       if (!ctx->post_msg_wq)
+               return -EBUSY;
+       return 0;
+}
+
+static void sst_init_locks(struct intel_sst_drv *ctx)
+{
+       mutex_init(&ctx->sst_lock);
+       spin_lock_init(&ctx->rx_msg_lock);
+       spin_lock_init(&ctx->ipc_spin_lock);
+       spin_lock_init(&ctx->block_lock);
+}
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+               struct device *dev, unsigned int dev_id)
+{
+       *ctx = devm_kzalloc(dev, sizeof(struct intel_sst_drv), GFP_KERNEL);
+       if (!(*ctx))
+               return -ENOMEM;
+
+       (*ctx)->dev = dev;
+       (*ctx)->dev_id = dev_id;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(sst_alloc_drv_context);
+
+int sst_context_init(struct intel_sst_drv *ctx)
+{
+       int ret = 0, i;
+
+       if (!ctx->pdata)
+               return -EINVAL;
+
+       if (!ctx->pdata->probe_data)
+               return -EINVAL;
+
+       memcpy(&ctx->info, ctx->pdata->probe_data, sizeof(ctx->info));
+
+       ret = sst_driver_ops(ctx);
+       if (ret != 0)
+               return -EINVAL;
+
+       sst_init_locks(ctx);
+       sst_set_fw_state_locked(ctx, SST_RESET);
+
+       /* pvt_id 0 reserved for async messages */
+       ctx->pvt_id = 1;
+       ctx->stream_cnt = 0;
+       ctx->fw_in_mem = NULL;
+       /* we use memcpy, so set to 0 */
+       ctx->use_dma = 0;
+       ctx->use_lli = 0;
+
+       if (sst_workqueue_init(ctx))
+               return -EINVAL;
+
+       ctx->mailbox_recv_offset = ctx->pdata->ipc_info->mbox_recv_off;
+       ctx->ipc_reg.ipcx = SST_IPCX + ctx->pdata->ipc_info->ipc_offset;
+       ctx->ipc_reg.ipcd = SST_IPCD + ctx->pdata->ipc_info->ipc_offset;
+
+       dev_info(ctx->dev, "Got drv data max stream %d\n",
+                               ctx->info.max_streams);
+
+       for (i = 1; i <= ctx->info.max_streams; i++) {
+               struct stream_info *stream = &ctx->streams[i];
+
+               memset(stream, 0, sizeof(*stream));
+               stream->pipe_id = PIPE_RSVD;
+               mutex_init(&stream->lock);
+       }
+
+       /* Register the ISR */
+       ret = devm_request_threaded_irq(ctx->dev, ctx->irq_num, ctx->ops->interrupt,
+                                       ctx->ops->irq_thread, 0, SST_DRV_NAME,
+                                       ctx);
+       if (ret)
+               goto do_free_mem;
+
+       dev_dbg(ctx->dev, "Registered IRQ %#x\n", ctx->irq_num);
+
+       /* default intr are unmasked so set this as masked */
+       sst_shim_write64(ctx->shim, SST_IMRX, 0xFFFF0038);
+
+       ctx->qos = devm_kzalloc(ctx->dev,
+               sizeof(struct pm_qos_request), GFP_KERNEL);
+       if (!ctx->qos) {
+               ret = -ENOMEM;
+               goto do_free_mem;
+       }
+       pm_qos_add_request(ctx->qos, PM_QOS_CPU_DMA_LATENCY,
+                               PM_QOS_DEFAULT_VALUE);
+
+       dev_dbg(ctx->dev, "Requesting FW %s now...\n", ctx->firmware_name);
+       ret = request_firmware_nowait(THIS_MODULE, true, ctx->firmware_name,
+                                     ctx->dev, GFP_KERNEL, ctx, sst_firmware_load_cb);
+       if (ret) {
+               dev_err(ctx->dev, "Firmware download failed:%d\n", ret);
+               goto do_free_mem;
+       }
+       sst_register(ctx->dev);
+       return 0;
+
+do_free_mem:
+       destroy_workqueue(ctx->post_msg_wq);
+       return ret;
+}
+EXPORT_SYMBOL_GPL(sst_context_init);
+
+void sst_context_cleanup(struct intel_sst_drv *ctx)
+{
+       pm_runtime_get_noresume(ctx->dev);
+       pm_runtime_disable(ctx->dev);
+       sst_unregister(ctx->dev);
+       sst_set_fw_state_locked(ctx, SST_SHUTDOWN);
+       flush_scheduled_work();
+       destroy_workqueue(ctx->post_msg_wq);
+       pm_qos_remove_request(ctx->qos);
+       kfree(ctx->fw_sg_list.src);
+       kfree(ctx->fw_sg_list.dst);
+       ctx->fw_sg_list.list_len = 0;
+       kfree(ctx->fw_in_mem);
+       ctx->fw_in_mem = NULL;
+       sst_memcpy_free_resources(ctx);
+       ctx = NULL;
+}
+EXPORT_SYMBOL_GPL(sst_context_cleanup);
+
+static inline void sst_save_shim64(struct intel_sst_drv *ctx,
+                           void __iomem *shim,
+                           struct sst_shim_regs64 *shim_regs)
+{
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
+
+       shim_regs->imrx = sst_shim_read64(shim, SST_IMRX),
+
+       spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
+}
+
+static inline void sst_restore_shim64(struct intel_sst_drv *ctx,
+                                     void __iomem *shim,
+                                     struct sst_shim_regs64 *shim_regs)
+{
+       unsigned long irq_flags;
+
+       /*
+        * we only need to restore IMRX for this case, rest will be
+        * initialize by FW or driver when firmware is loaded
+        */
+       spin_lock_irqsave(&ctx->ipc_spin_lock, irq_flags);
+       sst_shim_write64(shim, SST_IMRX, shim_regs->imrx),
+       spin_unlock_irqrestore(&ctx->ipc_spin_lock, irq_flags);
+}
+
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx)
+{
+       pm_runtime_set_autosuspend_delay(ctx->dev, SST_SUSPEND_DELAY);
+       pm_runtime_use_autosuspend(ctx->dev);
+       /*
+        * For acpi devices, the actual physical device state is
+        * initially active. So change the state to active before
+        * enabling the pm
+        */
+       pm_runtime_enable(ctx->dev);
+
+       if (acpi_disabled)
+               pm_runtime_set_active(ctx->dev);
+       else
+               pm_runtime_put_noidle(ctx->dev);
+
+       sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+}
+EXPORT_SYMBOL_GPL(sst_configure_runtime_pm);
+
+static int intel_sst_runtime_suspend(struct device *dev)
+{
+       int ret = 0;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state == SST_RESET) {
+               dev_dbg(dev, "LPE is already in RESET state, No action\n");
+               return 0;
+       }
+       /* save fw context */
+       if (ctx->ops->save_dsp_context(ctx))
+               return -EBUSY;
+
+       /* Move the SST state to Reset */
+       sst_set_fw_state_locked(ctx, SST_RESET);
+
+       synchronize_irq(ctx->irq_num);
+       flush_workqueue(ctx->post_msg_wq);
+
+       /* save the shim registers because PMC doesn't save state */
+       sst_save_shim64(ctx, ctx->shim, ctx->shim_regs64);
+
+       return ret;
+}
+
+static int intel_sst_runtime_resume(struct device *dev)
+{
+       int ret = 0;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state == SST_RESET) {
+               ret = sst_load_fw(ctx);
+               if (ret) {
+                       dev_err(dev, "FW download fail %d\n", ret);
+                       sst_set_fw_state_locked(ctx, SST_RESET);
+               }
+       }
+       return ret;
+}
+
+const struct dev_pm_ops intel_sst_pm = {
+       .runtime_suspend = intel_sst_runtime_suspend,
+       .runtime_resume = intel_sst_runtime_resume,
+};
+EXPORT_SYMBOL_GPL(intel_sst_pm);
diff --git a/sound/soc/intel/sst/sst.h b/sound/soc/intel/sst/sst.h
new file mode 100644 (file)
index 0000000..7f4bbfc
--- /dev/null
@@ -0,0 +1,546 @@
+/*
+ *  sst.h - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corporation
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  Common private declarations for SST
+ */
+#ifndef __SST_H__
+#define __SST_H__
+
+#include <linux/firmware.h>
+
+/* driver names */
+#define SST_DRV_NAME "intel_sst_driver"
+#define SST_MRFLD_PCI_ID 0x119A
+#define SST_BYT_ACPI_ID        0x80860F28
+#define SST_CHV_ACPI_ID        0x808622A8
+
+#define SST_SUSPEND_DELAY 2000
+#define FW_CONTEXT_MEM (64*1024)
+#define SST_ICCM_BOUNDARY 4
+#define SST_CONFIG_SSP_SIGN 0x7ffe8001
+
+#define MRFLD_FW_VIRTUAL_BASE 0xC0000000
+#define MRFLD_FW_DDR_BASE_OFFSET 0x0
+#define MRFLD_FW_FEATURE_BASE_OFFSET 0x4
+#define MRFLD_FW_BSS_RESET_BIT 0
+
+extern const struct dev_pm_ops intel_sst_pm;
+enum sst_states {
+       SST_FW_LOADING = 1,
+       SST_FW_RUNNING,
+       SST_RESET,
+       SST_SHUTDOWN,
+};
+
+enum sst_algo_ops {
+       SST_SET_ALGO = 0,
+       SST_GET_ALGO = 1,
+};
+
+#define SST_BLOCK_TIMEOUT      1000
+
+#define FW_SIGNATURE_SIZE      4
+
+/* stream states */
+enum sst_stream_states {
+       STREAM_UN_INIT  = 0,    /* Freed/Not used stream */
+       STREAM_RUNNING  = 1,    /* Running */
+       STREAM_PAUSED   = 2,    /* Paused stream */
+       STREAM_DECODE   = 3,    /* stream is in decoding only state */
+       STREAM_INIT     = 4,    /* stream init, waiting for data */
+       STREAM_RESET    = 5,    /* force reset on recovery */
+};
+
+enum sst_ram_type {
+       SST_IRAM        = 1,
+       SST_DRAM        = 2,
+       SST_DDR = 5,
+       SST_CUSTOM_INFO = 7,    /* consists of FW binary information */
+};
+
+/* SST shim registers to structure mapping */
+union interrupt_reg {
+       struct {
+               u64 done_interrupt:1;
+               u64 busy_interrupt:1;
+               u64 rsvd:62;
+       } part;
+       u64 full;
+};
+
+union sst_pisr_reg {
+       struct {
+               u32 pssp0:1;
+               u32 pssp1:1;
+               u32 rsvd0:3;
+               u32 dmac:1;
+               u32 rsvd1:26;
+       } part;
+       u32 full;
+};
+
+union sst_pimr_reg {
+       struct {
+               u32 ssp0:1;
+               u32 ssp1:1;
+               u32 rsvd0:3;
+               u32 dmac:1;
+               u32 rsvd1:10;
+               u32 ssp0_sc:1;
+               u32 ssp1_sc:1;
+               u32 rsvd2:3;
+               u32 dmac_sc:1;
+               u32 rsvd3:10;
+       } part;
+       u32 full;
+};
+
+union config_status_reg_mrfld {
+       struct {
+               u64 lpe_reset:1;
+               u64 lpe_reset_vector:1;
+               u64 runstall:1;
+               u64 pwaitmode:1;
+               u64 clk_sel:3;
+               u64 rsvd2:1;
+               u64 sst_clk:3;
+               u64 xt_snoop:1;
+               u64 rsvd3:4;
+               u64 clk_sel1:6;
+               u64 clk_enable:3;
+               u64 rsvd4:6;
+               u64 slim0baseclk:1;
+               u64 rsvd:32;
+       } part;
+       u64 full;
+};
+
+union interrupt_reg_mrfld {
+       struct {
+               u64 done_interrupt:1;
+               u64 busy_interrupt:1;
+               u64 rsvd:62;
+       } part;
+       u64 full;
+};
+
+union sst_imr_reg_mrfld {
+       struct {
+               u64 done_interrupt:1;
+               u64 busy_interrupt:1;
+               u64 rsvd:62;
+       } part;
+       u64 full;
+};
+
+/**
+ * struct sst_block - This structure is used to block a user/fw data call to another
+ * fw/user call
+ *
+ * @condition: condition for blocking check
+ * @ret_code: ret code when block is released
+ * @data: data ptr
+ * @size: size of data
+ * @on: block condition
+ * @msg_id: msg_id = msgid in mfld/ctp, mrfld = NULL
+ * @drv_id: str_id in mfld/ctp, = drv_id in mrfld
+ * @node: list head node
+ */
+struct sst_block {
+       bool    condition;
+       int     ret_code;
+       void    *data;
+       u32     size;
+       bool    on;
+       u32     msg_id;
+       u32     drv_id;
+       struct list_head node;
+};
+
+/**
+ * struct stream_info - structure that holds the stream information
+ *
+ * @status : stream current state
+ * @prev : stream prev state
+ * @ops : stream operation pb/cp/drm...
+ * @bufs: stream buffer list
+ * @lock : stream mutex for protecting state
+ * @pcm_substream : PCM substream
+ * @period_elapsed : PCM period elapsed callback
+ * @sfreq : stream sampling freq
+ * @str_type : stream type
+ * @cumm_bytes : cummulative bytes decoded
+ * @str_type : stream type
+ * @src : stream source
+ */
+struct stream_info {
+       unsigned int            status;
+       unsigned int            prev;
+       unsigned int            ops;
+       struct mutex            lock;
+
+       void                    *pcm_substream;
+       void (*period_elapsed)(void *pcm_substream);
+
+       unsigned int            sfreq;
+       u32                     cumm_bytes;
+
+       void                    *compr_cb_param;
+       void (*compr_cb)(void *compr_cb_param);
+
+       void                    *drain_cb_param;
+       void (*drain_notify)(void *drain_cb_param);
+
+       unsigned int            num_ch;
+       unsigned int            pipe_id;
+       unsigned int            str_id;
+       unsigned int            task_id;
+};
+
+#define SST_FW_SIGN "$SST"
+#define SST_FW_LIB_SIGN "$LIB"
+
+/**
+ * struct sst_fw_header - FW file headers
+ *
+ * @signature : FW signature
+ * @file_size: size of fw image
+ * @modules : # of modules
+ * @file_format : version of header format
+ * @reserved : reserved fields
+ */
+struct sst_fw_header {
+       unsigned char signature[FW_SIGNATURE_SIZE];
+       u32 file_size;
+       u32 modules;
+       u32 file_format;
+       u32 reserved[4];
+};
+
+/**
+ * struct fw_module_header - module header in FW
+ *
+ * @signature: module signature
+ * @mod_size: size of module
+ * @blocks: block count
+ * @type: block type
+ * @entry_point: module netry point
+ */
+struct fw_module_header {
+       unsigned char signature[FW_SIGNATURE_SIZE];
+       u32 mod_size;
+       u32 blocks;
+       u32 type;
+       u32 entry_point;
+};
+
+/**
+ * struct fw_block_info - block header for FW
+ *
+ * @type: block ram type I/D
+ * @size: size of block
+ * @ram_offset: offset in ram
+ */
+struct fw_block_info {
+       enum sst_ram_type       type;
+       u32                     size;
+       u32                     ram_offset;
+       u32                     rsvd;
+};
+
+struct sst_runtime_param {
+       struct snd_sst_runtime_params param;
+};
+
+struct sst_sg_list {
+       struct scatterlist *src;
+       struct scatterlist *dst;
+       int list_len;
+       unsigned int sg_idx;
+};
+
+struct sst_memcpy_list {
+       struct list_head memcpylist;
+       void *dstn;
+       const void *src;
+       u32 size;
+       bool is_io;
+};
+
+/*Firmware Module Information*/
+enum sst_lib_dwnld_status {
+       SST_LIB_NOT_FOUND = 0,
+       SST_LIB_FOUND,
+       SST_LIB_DOWNLOADED,
+};
+
+struct sst_module_info {
+       const char *name; /*Library name*/
+       u32     id; /*Module ID*/
+       u32     entry_pt; /*Module entry point*/
+       u8      status; /*module status*/
+       u8      rsvd1;
+       u16     rsvd2;
+};
+
+/*
+ * Structure for managing the Library Region(1.5MB)
+ * in DDR in Merrifield
+ */
+struct sst_mem_mgr {
+       phys_addr_t current_base;
+       int avail;
+       unsigned int count;
+};
+
+struct sst_ipc_reg {
+       int ipcx;
+       int ipcd;
+};
+
+struct sst_shim_regs64 {
+       u64 csr;
+       u64 pisr;
+       u64 pimr;
+       u64 isrx;
+       u64 isrd;
+       u64 imrx;
+       u64 imrd;
+       u64 ipcx;
+       u64 ipcd;
+       u64 isrsc;
+       u64 isrlpesc;
+       u64 imrsc;
+       u64 imrlpesc;
+       u64 ipcsc;
+       u64 ipclpesc;
+       u64 clkctl;
+       u64 csr2;
+};
+
+/**
+ * struct intel_sst_drv - driver ops
+ *
+ * @sst_state : current sst device state
+ * @dev_id : device identifier, pci_id for pci devices and acpi_id for acpi
+ *          devices
+ * @shim : SST shim pointer
+ * @mailbox : SST mailbox pointer
+ * @iram : SST IRAM pointer
+ * @dram : SST DRAM pointer
+ * @pdata : SST info passed as a part of pci platform data
+ * @shim_phy_add : SST shim phy addr
+ * @shim_regs64: Struct to save shim registers
+ * @ipc_dispatch_list : ipc messages dispatched
+ * @rx_list : to copy the process_reply/process_msg from DSP
+ * @ipc_post_msg_wq : wq to post IPC messages context
+ * @mad_ops : MAD driver operations registered
+ * @mad_wq : MAD driver wq
+ * @post_msg_wq : wq to post IPC messages
+ * @streams : sst stream contexts
+ * @list_lock : sst driver list lock (deprecated)
+ * @ipc_spin_lock : spin lock to handle audio shim access and ipc queue
+ * @block_lock : spin lock to add block to block_list and assign pvt_id
+ * @rx_msg_lock : spin lock to handle the rx messages from the DSP
+ * @scard_ops : sst card ops
+ * @pci : sst pci device struture
+ * @dev : pointer to current device struct
+ * @sst_lock : sst device lock
+ * @pvt_id : sst private id
+ * @stream_cnt : total sst active stream count
+ * @pb_streams : total active pb streams
+ * @cp_streams : total active cp streams
+ * @audio_start : audio status
+ * @qos                : PM Qos struct
+ * firmware_name : Firmware / Library name
+ */
+struct intel_sst_drv {
+       int                     sst_state;
+       int                     irq_num;
+       unsigned int            dev_id;
+       void __iomem            *ddr;
+       void __iomem            *shim;
+       void __iomem            *mailbox;
+       void __iomem            *iram;
+       void __iomem            *dram;
+       unsigned int            mailbox_add;
+       unsigned int            iram_base;
+       unsigned int            dram_base;
+       unsigned int            shim_phy_add;
+       unsigned int            iram_end;
+       unsigned int            dram_end;
+       unsigned int            ddr_end;
+       unsigned int            ddr_base;
+       unsigned int            mailbox_recv_offset;
+       struct sst_shim_regs64  *shim_regs64;
+       struct list_head        block_list;
+       struct list_head        ipc_dispatch_list;
+       struct sst_platform_info *pdata;
+       struct list_head        rx_list;
+       struct work_struct      ipc_post_msg_wq;
+       wait_queue_head_t       wait_queue;
+       struct workqueue_struct *post_msg_wq;
+       unsigned int            tstamp;
+       /* str_id 0 is not used */
+       struct stream_info      streams[MAX_NUM_STREAMS+1];
+       spinlock_t              ipc_spin_lock;
+       spinlock_t              block_lock;
+       spinlock_t              rx_msg_lock;
+       struct pci_dev          *pci;
+       struct device           *dev;
+       volatile long unsigned          pvt_id;
+       struct mutex            sst_lock;
+       unsigned int            stream_cnt;
+       unsigned int            csr_value;
+       void                    *fw_in_mem;
+       struct sst_sg_list      fw_sg_list, library_list;
+       struct intel_sst_ops    *ops;
+       struct sst_info         info;
+       struct pm_qos_request   *qos;
+       unsigned int            use_dma;
+       unsigned int            use_lli;
+       atomic_t                fw_clear_context;
+       bool                    lib_dwnld_reqd;
+       struct list_head        memcpy_list;
+       struct sst_ipc_reg      ipc_reg;
+       struct sst_mem_mgr      lib_mem_mgr;
+       /*
+        * Holder for firmware name. Due to async call it needs to be
+        * persistent till worker thread gets called
+        */
+       char firmware_name[20];
+};
+
+/* misc definitions */
+#define FW_DWNL_ID 0x01
+
+struct intel_sst_ops {
+       irqreturn_t (*interrupt)(int, void *);
+       irqreturn_t (*irq_thread)(int, void *);
+       void (*clear_interrupt)(struct intel_sst_drv *ctx);
+       int (*start)(struct intel_sst_drv *ctx);
+       int (*reset)(struct intel_sst_drv *ctx);
+       void (*process_reply)(struct intel_sst_drv *ctx, struct ipc_post *msg);
+       int (*post_message)(struct intel_sst_drv *ctx,
+                       struct ipc_post *msg, bool sync);
+       void (*process_message)(struct ipc_post *msg);
+       void (*set_bypass)(bool set);
+       int (*save_dsp_context)(struct intel_sst_drv *sst);
+       void (*restore_dsp_context)(void);
+       int (*alloc_stream)(struct intel_sst_drv *ctx, void *params);
+       void (*post_download)(struct intel_sst_drv *sst);
+};
+
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int id);
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id);
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *ctx,
+                       struct snd_sst_bytes_v2 *sbytes);
+int sst_set_stream_param(int str_id, struct snd_sst_params *str_param);
+int sst_set_metadata(int str_id, char *params);
+int sst_get_stream(struct intel_sst_drv *sst_drv_ctx,
+               struct snd_sst_params *str_param);
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+               struct snd_sst_params *str_param,
+               struct snd_sst_lib_download **lib_dnld);
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+               int str_id, bool partial_drain);
+int sst_post_message_mrfld(struct intel_sst_drv *ctx,
+               struct ipc_post *msg, bool sync);
+void sst_process_reply_mrfld(struct intel_sst_drv *ctx, struct ipc_post *msg);
+int sst_start_mrfld(struct intel_sst_drv *ctx);
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *ctx);
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *ctx);
+
+int sst_load_fw(struct intel_sst_drv *ctx);
+int sst_load_library(struct snd_sst_lib_download *lib, u8 ops);
+void sst_post_download_mrfld(struct intel_sst_drv *ctx);
+int sst_get_block_stream(struct intel_sst_drv *sst_drv_ctx);
+void sst_memcpy_free_resources(struct intel_sst_drv *ctx);
+
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+                               struct sst_block *block);
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx,
+                       struct sst_block *block);
+int sst_create_ipc_msg(struct ipc_post **arg, bool large);
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id);
+void sst_clean_stream(struct stream_info *stream);
+int intel_sst_register_compress(struct intel_sst_drv *sst);
+int intel_sst_remove_compress(struct intel_sst_drv *sst);
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id);
+int sst_send_sync_msg(int ipc, int str_id);
+int sst_get_num_channel(struct snd_sst_params *str_param);
+int sst_get_sfreq(struct snd_sst_params *str_param);
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params);
+void sst_restore_fw_context(void);
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+                               u32 msg_id, u32 drv_id);
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+               struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+               u32 msg_id, u32 drv_id);
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed);
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+               u32 drv_id, u32 ipc, void *data, u32 size);
+int sst_request_firmware_async(struct intel_sst_drv *ctx);
+int sst_driver_ops(struct intel_sst_drv *sst);
+struct sst_platform_info *sst_get_acpi_driver_data(const char *hid);
+void sst_firmware_load_cb(const struct firmware *fw, void *context);
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+               int task_id, int ipc_msg, int cmd_id, int pipe_id,
+               size_t mbox_data_len, const void *mbox_data, void **data,
+               bool large, bool fill_dsp, bool sync, bool response);
+
+void sst_process_pending_msg(struct work_struct *work);
+int sst_assign_pvt_id(struct intel_sst_drv *sst_drv_ctx);
+void sst_init_stream(struct stream_info *stream,
+               int codec, int sst_id, int ops, u8 slot);
+int sst_validate_strid(struct intel_sst_drv *sst_drv_ctx, int str_id);
+struct stream_info *get_stream_info(struct intel_sst_drv *sst_drv_ctx,
+               int str_id);
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               u32 pipe_id);
+u32 relocate_imr_addr_mrfld(u32 base_addr);
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+                                       struct ipc_post *msg);
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv);
+int sst_shim_write(void __iomem *addr, int offset, int value);
+u32 sst_shim_read(void __iomem *addr, int offset);
+u64 sst_reg_read64(void __iomem *addr, int offset);
+int sst_shim_write64(void __iomem *addr, int offset, u64 value);
+u64 sst_shim_read64(void __iomem *addr, int offset);
+void sst_set_fw_state_locked(
+               struct intel_sst_drv *sst_drv_ctx, int sst_state);
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+                               int msg, int task_id, int large, int drv_id);
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+                                       int pipe_id, int len);
+
+int sst_register(struct device *);
+int sst_unregister(struct device *);
+
+int sst_alloc_drv_context(struct intel_sst_drv **ctx,
+               struct device *dev, unsigned int dev_id);
+int sst_context_init(struct intel_sst_drv *ctx);
+void sst_context_cleanup(struct intel_sst_drv *ctx);
+void sst_configure_runtime_pm(struct intel_sst_drv *ctx);
+#endif
diff --git a/sound/soc/intel/sst/sst_acpi.c b/sound/soc/intel/sst/sst_acpi.c
new file mode 100644 (file)
index 0000000..3abc29e
--- /dev/null
@@ -0,0 +1,383 @@
+/*
+ * sst_acpi.c - SST (LPE) driver init file for ACPI enumeration.
+ *
+ * Copyright (c) 2013, Intel Corporation.
+ *
+ *  Authors:   Ramesh Babu K V <Ramesh.Babu@intel.com>
+ *  Authors:   Omair Mohammed Abdullah <omair.m.abdullah@intel.com>
+ *
+ * This program is free software; you can redistribute it and/or modify it
+ * under the terms and conditions of the GNU General Public License,
+ * version 2, as published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope 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/fs.h>
+#include <linux/interrupt.h>
+#include <linux/slab.h>
+#include <linux/io.h>
+#include <linux/miscdevice.h>
+#include <linux/platform_device.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/acpi.h>
+#include <asm/platform_sst_audio.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <acpi/acbuffer.h>
+#include <acpi/platform/acenv.h>
+#include <acpi/platform/aclinux.h>
+#include <acpi/actypes.h>
+#include <acpi/acpi_bus.h>
+#include "../sst-mfld-platform.h"
+#include "../sst-dsp.h"
+#include "sst.h"
+
+struct sst_machines {
+       char *codec_id;
+       char board[32];
+       char machine[32];
+       void (*machine_quirk)(void);
+       char firmware[32];
+       struct sst_platform_info *pdata;
+
+};
+
+/* LPE viewpoint addresses */
+#define SST_BYT_IRAM_PHY_START 0xff2c0000
+#define SST_BYT_IRAM_PHY_END   0xff2d4000
+#define SST_BYT_DRAM_PHY_START 0xff300000
+#define SST_BYT_DRAM_PHY_END   0xff320000
+#define SST_BYT_IMR_VIRT_START 0xc0000000 /* virtual addr in LPE */
+#define SST_BYT_IMR_VIRT_END   0xc01fffff
+#define SST_BYT_SHIM_PHY_ADDR  0xff340000
+#define SST_BYT_MBOX_PHY_ADDR  0xff344000
+#define SST_BYT_DMA0_PHY_ADDR  0xff298000
+#define SST_BYT_DMA1_PHY_ADDR  0xff29c000
+#define SST_BYT_SSP0_PHY_ADDR  0xff2a0000
+#define SST_BYT_SSP2_PHY_ADDR  0xff2a2000
+
+#define BYT_FW_MOD_TABLE_OFFSET        0x80000
+#define BYT_FW_MOD_TABLE_SIZE  0x100
+#define BYT_FW_MOD_OFFSET      (BYT_FW_MOD_TABLE_OFFSET + BYT_FW_MOD_TABLE_SIZE)
+
+static const struct sst_info byt_fwparse_info = {
+       .use_elf        = false,
+       .max_streams    = 25,
+       .iram_start     = SST_BYT_IRAM_PHY_START,
+       .iram_end       = SST_BYT_IRAM_PHY_END,
+       .iram_use       = true,
+       .dram_start     = SST_BYT_DRAM_PHY_START,
+       .dram_end       = SST_BYT_DRAM_PHY_END,
+       .dram_use       = true,
+       .imr_start      = SST_BYT_IMR_VIRT_START,
+       .imr_end        = SST_BYT_IMR_VIRT_END,
+       .imr_use        = true,
+       .mailbox_start  = SST_BYT_MBOX_PHY_ADDR,
+       .num_probes     = 0,
+       .lpe_viewpt_rqd  = true,
+};
+
+static const struct sst_ipc_info byt_ipc_info = {
+       .ipc_offset = 0,
+       .mbox_recv_off = 0x400,
+};
+
+static const struct sst_lib_dnld_info  byt_lib_dnld_info = {
+       .mod_base           = SST_BYT_IMR_VIRT_START,
+       .mod_end            = SST_BYT_IMR_VIRT_END,
+       .mod_table_offset   = BYT_FW_MOD_TABLE_OFFSET,
+       .mod_table_size     = BYT_FW_MOD_TABLE_SIZE,
+       .mod_ddr_dnld       = false,
+};
+
+static const struct sst_res_info byt_rvp_res_info = {
+       .shim_offset = 0x140000,
+       .shim_size = 0x000100,
+       .shim_phy_addr = SST_BYT_SHIM_PHY_ADDR,
+       .ssp0_offset = 0xa0000,
+       .ssp0_size = 0x1000,
+       .dma0_offset = 0x98000,
+       .dma0_size = 0x4000,
+       .dma1_offset = 0x9c000,
+       .dma1_size = 0x4000,
+       .iram_offset = 0x0c0000,
+       .iram_size = 0x14000,
+       .dram_offset = 0x100000,
+       .dram_size = 0x28000,
+       .mbox_offset = 0x144000,
+       .mbox_size = 0x1000,
+       .acpi_lpe_res_index = 0,
+       .acpi_ddr_index = 2,
+       .acpi_ipc_irq_index = 5,
+};
+
+static struct sst_platform_info byt_rvp_platform_data = {
+       .probe_data = &byt_fwparse_info,
+       .ipc_info = &byt_ipc_info,
+       .lib_info = &byt_lib_dnld_info,
+       .res_info = &byt_rvp_res_info,
+       .platform = "sst-mfld-platform",
+};
+
+/* Cherryview (Cherrytrail and Braswell) uses same mrfld dpcm fw as Baytrail,
+ * so pdata is same as Baytrail.
+ */
+static struct sst_platform_info chv_platform_data = {
+       .probe_data = &byt_fwparse_info,
+       .ipc_info = &byt_ipc_info,
+       .lib_info = &byt_lib_dnld_info,
+       .res_info = &byt_rvp_res_info,
+       .platform = "sst-mfld-platform",
+};
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+       struct resource *rsrc;
+       struct platform_device *pdev = to_platform_device(ctx->dev);
+
+       /* All ACPI resource request here */
+       /* Get Shim addr */
+       rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+                                       ctx->pdata->res_info->acpi_lpe_res_index);
+       if (!rsrc) {
+               dev_err(ctx->dev, "Invalid SHIM base from IFWI");
+               return -EIO;
+       }
+       dev_info(ctx->dev, "LPE base: %#x size:%#x", (unsigned int) rsrc->start,
+                                       (unsigned int)resource_size(rsrc));
+
+       ctx->iram_base = rsrc->start + ctx->pdata->res_info->iram_offset;
+       ctx->iram_end =  ctx->iram_base + ctx->pdata->res_info->iram_size - 1;
+       dev_info(ctx->dev, "IRAM base: %#x", ctx->iram_base);
+       ctx->iram = devm_ioremap_nocache(ctx->dev, ctx->iram_base,
+                                        ctx->pdata->res_info->iram_size);
+       if (!ctx->iram) {
+               dev_err(ctx->dev, "unable to map IRAM");
+               return -EIO;
+       }
+
+       ctx->dram_base = rsrc->start + ctx->pdata->res_info->dram_offset;
+       ctx->dram_end = ctx->dram_base + ctx->pdata->res_info->dram_size - 1;
+       dev_info(ctx->dev, "DRAM base: %#x", ctx->dram_base);
+       ctx->dram = devm_ioremap_nocache(ctx->dev, ctx->dram_base,
+                                        ctx->pdata->res_info->dram_size);
+       if (!ctx->dram) {
+               dev_err(ctx->dev, "unable to map DRAM");
+               return -EIO;
+       }
+
+       ctx->shim_phy_add = rsrc->start + ctx->pdata->res_info->shim_offset;
+       dev_info(ctx->dev, "SHIM base: %#x", ctx->shim_phy_add);
+       ctx->shim = devm_ioremap_nocache(ctx->dev, ctx->shim_phy_add,
+                                       ctx->pdata->res_info->shim_size);
+       if (!ctx->shim) {
+               dev_err(ctx->dev, "unable to map SHIM");
+               return -EIO;
+       }
+
+       /* reassign physical address to LPE viewpoint address */
+       ctx->shim_phy_add = ctx->pdata->res_info->shim_phy_addr;
+
+       /* Get mailbox addr */
+       ctx->mailbox_add = rsrc->start + ctx->pdata->res_info->mbox_offset;
+       dev_info(ctx->dev, "Mailbox base: %#x", ctx->mailbox_add);
+       ctx->mailbox = devm_ioremap_nocache(ctx->dev, ctx->mailbox_add,
+                                           ctx->pdata->res_info->mbox_size);
+       if (!ctx->mailbox) {
+               dev_err(ctx->dev, "unable to map mailbox");
+               return -EIO;
+       }
+
+       /* reassign physical address to LPE viewpoint address */
+       ctx->mailbox_add = ctx->info.mailbox_start;
+
+       rsrc = platform_get_resource(pdev, IORESOURCE_MEM,
+                                       ctx->pdata->res_info->acpi_ddr_index);
+       if (!rsrc) {
+               dev_err(ctx->dev, "Invalid DDR base from IFWI");
+               return -EIO;
+       }
+       ctx->ddr_base = rsrc->start;
+       ctx->ddr_end = rsrc->end;
+       dev_info(ctx->dev, "DDR base: %#x", ctx->ddr_base);
+       ctx->ddr = devm_ioremap_nocache(ctx->dev, ctx->ddr_base,
+                                       resource_size(rsrc));
+       if (!ctx->ddr) {
+               dev_err(ctx->dev, "unable to map DDR");
+               return -EIO;
+       }
+
+       /* Find the IRQ */
+       ctx->irq_num = platform_get_irq(pdev,
+                               ctx->pdata->res_info->acpi_ipc_irq_index);
+       return 0;
+}
+
+static acpi_status sst_acpi_mach_match(acpi_handle handle, u32 level,
+                                      void *context, void **ret)
+{
+       *(bool *)context = true;
+       return AE_OK;
+}
+
+static struct sst_machines *sst_acpi_find_machine(
+       struct sst_machines *machines)
+{
+       struct sst_machines *mach;
+       bool found = false;
+
+       for (mach = machines; mach->codec_id; mach++)
+               if (ACPI_SUCCESS(acpi_get_devices(mach->codec_id,
+                                                 sst_acpi_mach_match,
+                                                 &found, NULL)) && found)
+                       return mach;
+
+       return NULL;
+}
+
+int sst_acpi_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       int ret = 0;
+       struct intel_sst_drv *ctx;
+       const struct acpi_device_id *id;
+       struct sst_machines *mach;
+       struct platform_device *mdev;
+       struct platform_device *plat_dev;
+       unsigned int dev_id;
+
+       id = acpi_match_device(dev->driver->acpi_match_table, dev);
+       if (!id)
+               return -ENODEV;
+       dev_dbg(dev, "for %s", id->id);
+
+       mach = (struct sst_machines *)id->driver_data;
+       mach = sst_acpi_find_machine(mach);
+       if (mach == NULL) {
+               dev_err(dev, "No matching machine driver found\n");
+               return -ENODEV;
+       }
+
+       ret = kstrtouint(id->id, 16, &dev_id);
+       if (ret < 0) {
+               dev_err(dev, "Unique device id conversion error: %d\n", ret);
+               return ret;
+       }
+
+       dev_dbg(dev, "ACPI device id: %x\n", dev_id);
+
+       plat_dev = platform_device_register_data(dev, mach->pdata->platform, -1, NULL, 0);
+       if (IS_ERR(plat_dev)) {
+               dev_err(dev, "Failed to create machine device: %s\n", mach->pdata->platform);
+               return PTR_ERR(plat_dev);
+       }
+
+       /* Create platform device for sst machine driver */
+       mdev = platform_device_register_data(dev, mach->machine, -1, NULL, 0);
+       if (IS_ERR(mdev)) {
+               dev_err(dev, "Failed to create machine device: %s\n", mach->machine);
+               return PTR_ERR(mdev);
+       }
+
+       ret = sst_alloc_drv_context(&ctx, dev, dev_id);
+       if (ret < 0)
+               return ret;
+
+       /* Fill sst platform data */
+       ctx->pdata = mach->pdata;
+       strcpy(ctx->firmware_name, mach->firmware);
+
+       ret = sst_platform_get_resources(ctx);
+       if (ret)
+               return ret;
+
+       ret = sst_context_init(ctx);
+       if (ret < 0)
+               return ret;
+
+       /* need to save shim registers in BYT */
+       ctx->shim_regs64 = devm_kzalloc(ctx->dev, sizeof(*ctx->shim_regs64),
+                                       GFP_KERNEL);
+       if (!ctx->shim_regs64) {
+               return -ENOMEM;
+               goto do_sst_cleanup;
+       }
+
+       sst_configure_runtime_pm(ctx);
+       platform_set_drvdata(pdev, ctx);
+       return ret;
+
+do_sst_cleanup:
+       sst_context_cleanup(ctx);
+       platform_set_drvdata(pdev, NULL);
+       dev_err(ctx->dev, "failed with %d\n", ret);
+       return ret;
+}
+
+/**
+* intel_sst_remove - remove function
+*
+* @pdev:       platform device structure
+*
+* This function is called by OS when a device is unloaded
+* This frees the interrupt etc
+*/
+int sst_acpi_remove(struct platform_device *pdev)
+{
+       struct intel_sst_drv *ctx;
+
+       ctx = platform_get_drvdata(pdev);
+       sst_context_cleanup(ctx);
+       platform_set_drvdata(pdev, NULL);
+       return 0;
+}
+
+static struct sst_machines sst_acpi_bytcr[] = {
+       {"10EC5640", "T100", "bytt100_rt5640", NULL, "fw_sst_0f28.bin",
+                                               &byt_rvp_platform_data },
+       {},
+};
+
+/* Cherryview-based platforms: CherryTrail and Braswell */
+static struct sst_machines sst_acpi_chv[] = {
+       {"10EC5670", "cht-bsw", "cht-bsw-rt5672", NULL, "fw_sst_22a8.bin",
+                                               &chv_platform_data },
+       {},
+};
+
+static const struct acpi_device_id sst_acpi_ids[] = {
+       { "80860F28", (unsigned long)&sst_acpi_bytcr},
+       { "808622A8", (unsigned long) &sst_acpi_chv},
+       { },
+};
+
+MODULE_DEVICE_TABLE(acpi, sst_acpi_ids);
+
+static struct platform_driver sst_acpi_driver = {
+       .driver = {
+               .name                   = "intel_sst_acpi",
+               .owner                  = THIS_MODULE,
+               .acpi_match_table       = ACPI_PTR(sst_acpi_ids),
+               .pm                     = &intel_sst_pm,
+       },
+       .probe  = sst_acpi_probe,
+       .remove = sst_acpi_remove,
+};
+
+module_platform_driver(sst_acpi_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine ACPI Driver");
+MODULE_AUTHOR("Ramesh Babu K V");
+MODULE_AUTHOR("Omair Mohammed Abdullah");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
diff --git a/sound/soc/intel/sst/sst_drv_interface.c b/sound/soc/intel/sst/sst_drv_interface.c
new file mode 100644 (file)
index 0000000..5f75ef3
--- /dev/null
@@ -0,0 +1,686 @@
+/*
+ *  sst_drv_interface.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com)
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/delay.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <linux/math64.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+
+
+#define NUM_CODEC 2
+#define MIN_FRAGMENT 2
+#define MAX_FRAGMENT 4
+#define MIN_FRAGMENT_SIZE (50 * 1024)
+#define MAX_FRAGMENT_SIZE (1024 * 1024)
+#define SST_GET_BYTES_PER_SAMPLE(pcm_wd_sz)  (((pcm_wd_sz + 15) >> 4) << 1)
+
+int free_stream_context(struct intel_sst_drv *ctx, unsigned int str_id)
+{
+       struct stream_info *stream;
+       int ret = 0;
+
+       stream = get_stream_info(ctx, str_id);
+       if (stream) {
+               /* str_id is valid, so stream is alloacted */
+               ret = sst_free_stream(ctx, str_id);
+               if (ret)
+                       sst_clean_stream(&ctx->streams[str_id]);
+               return ret;
+       } else {
+               dev_err(ctx->dev, "we tried to free stream context %d which was freed!!!\n", str_id);
+       }
+       return ret;
+}
+
+int sst_get_stream_allocated(struct intel_sst_drv *ctx,
+       struct snd_sst_params *str_param,
+       struct snd_sst_lib_download **lib_dnld)
+{
+       int retval;
+
+       retval = ctx->ops->alloc_stream(ctx, str_param);
+       if (retval > 0)
+               dev_dbg(ctx->dev, "Stream allocated %d\n", retval);
+       return retval;
+
+}
+
+/*
+ * sst_get_sfreq - this function returns the frequency of the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_sfreq(struct snd_sst_params *str_param)
+{
+       switch (str_param->codec) {
+       case SST_CODEC_TYPE_PCM:
+               return str_param->sparams.uc.pcm_params.sfreq;
+       case SST_CODEC_TYPE_AAC:
+               return str_param->sparams.uc.aac_params.externalsr;
+       case SST_CODEC_TYPE_MP3:
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+/*
+ * sst_get_num_channel - get number of channels for the stream
+ *
+ * @str_param : stream params
+ */
+int sst_get_num_channel(struct snd_sst_params *str_param)
+{
+       switch (str_param->codec) {
+       case SST_CODEC_TYPE_PCM:
+               return str_param->sparams.uc.pcm_params.num_chan;
+       case SST_CODEC_TYPE_MP3:
+               return str_param->sparams.uc.mp3_params.num_chan;
+       case SST_CODEC_TYPE_AAC:
+               return str_param->sparams.uc.aac_params.num_chan;
+       default:
+               return -EINVAL;
+       }
+}
+
+/*
+ * sst_get_stream - this function prepares for stream allocation
+ *
+ * @str_param : stream param
+ */
+int sst_get_stream(struct intel_sst_drv *ctx,
+                       struct snd_sst_params *str_param)
+{
+       int retval;
+       struct stream_info *str_info;
+
+       /* stream is not allocated, we are allocating */
+       retval = ctx->ops->alloc_stream(ctx, str_param);
+       if (retval <= 0) {
+               return -EIO;
+       }
+       /* store sampling freq */
+       str_info = &ctx->streams[retval];
+       str_info->sfreq = sst_get_sfreq(str_param);
+
+       return retval;
+}
+
+static int sst_power_control(struct device *dev, bool state)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       dev_dbg(ctx->dev, "state:%d", state);
+       if (state == true)
+               return pm_runtime_get_sync(dev);
+       else
+               return sst_pm_runtime_put(ctx);
+}
+
+/*
+ * sst_open_pcm_stream - Open PCM interface
+ *
+ * @str_param: parameters of pcm stream
+ *
+ * This function is called by MID sound card driver to open
+ * a new pcm interface
+ */
+static int sst_open_pcm_stream(struct device *dev,
+               struct snd_sst_params *str_param)
+{
+       int retval;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (!str_param)
+               return -EINVAL;
+
+       retval = sst_get_stream(ctx, str_param);
+       if (retval > 0)
+               ctx->stream_cnt++;
+       else
+               dev_err(ctx->dev, "sst_get_stream returned err %d\n", retval);
+
+       return retval;
+}
+
+static int sst_cdev_open(struct device *dev,
+               struct snd_sst_params *str_params, struct sst_compress_cb *cb)
+{
+       int str_id, retval;
+       struct stream_info *stream;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       retval = pm_runtime_get_sync(ctx->dev);
+       if (retval < 0)
+               return retval;
+
+       str_id = sst_get_stream(ctx, str_params);
+       if (str_id > 0) {
+               dev_dbg(dev, "stream allocated in sst_cdev_open %d\n", str_id);
+               stream = &ctx->streams[str_id];
+               stream->compr_cb = cb->compr_cb;
+               stream->compr_cb_param = cb->param;
+               stream->drain_notify = cb->drain_notify;
+               stream->drain_cb_param = cb->drain_cb_param;
+       } else {
+               dev_err(dev, "stream encountered error during alloc %d\n", str_id);
+               str_id = -EINVAL;
+               sst_pm_runtime_put(ctx);
+       }
+       return str_id;
+}
+
+static int sst_cdev_close(struct device *dev, unsigned int str_id)
+{
+       int retval;
+       struct stream_info *stream;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream) {
+               dev_err(dev, "stream info is NULL for str %d!!!\n", str_id);
+               return -EINVAL;
+       }
+
+       if (stream->status == STREAM_RESET) {
+               dev_dbg(dev, "stream in reset state...\n");
+               stream->status = STREAM_UN_INIT;
+
+               retval = 0;
+               goto put;
+       }
+
+       retval = sst_free_stream(ctx, str_id);
+put:
+       stream->compr_cb_param = NULL;
+       stream->compr_cb = NULL;
+
+       if (retval)
+               dev_err(dev, "free stream returned err %d\n", retval);
+
+       dev_dbg(dev, "End\n");
+       return retval;
+
+}
+
+static int sst_cdev_ack(struct device *dev, unsigned int str_id,
+               unsigned long bytes)
+{
+       struct stream_info *stream;
+       struct snd_sst_tstamp fw_tstamp = {0,};
+       int offset;
+       void __iomem *addr;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream)
+               return -EINVAL;
+
+       /* update bytes sent */
+       stream->cumm_bytes += bytes;
+       dev_dbg(dev, "bytes copied %d inc by %ld\n", stream->cumm_bytes, bytes);
+
+       memcpy_fromio(&fw_tstamp,
+               ((void *)(ctx->mailbox + ctx->tstamp)
+               +(str_id * sizeof(fw_tstamp))),
+               sizeof(fw_tstamp));
+
+       fw_tstamp.bytes_copied = stream->cumm_bytes;
+       dev_dbg(dev, "bytes sent to fw %llu inc by %ld\n",
+                       fw_tstamp.bytes_copied, bytes);
+
+       addr =  ((void *)(ctx->mailbox + ctx->tstamp)) +
+                       (str_id * sizeof(fw_tstamp));
+       offset =  offsetof(struct snd_sst_tstamp, bytes_copied);
+       sst_shim_write(addr, offset, fw_tstamp.bytes_copied);
+       return 0;
+}
+
+static int sst_cdev_set_metadata(struct device *dev,
+               unsigned int str_id, struct snd_compr_metadata *metadata)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       dev_dbg(dev, "set metadata for stream %d\n", str_id);
+
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+
+       dev_dbg(dev, "pipe id = %d\n", str_info->pipe_id);
+       retval = sst_prepare_and_post_msg(ctx, str_info->task_id, IPC_CMD,
+                       IPC_IA_SET_STREAM_PARAMS_MRFLD, str_info->pipe_id,
+                       sizeof(*metadata), metadata, NULL,
+                       true, true, true, false);
+
+       return retval;
+}
+
+static int sst_cdev_stream_pause(struct device *dev, unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_pause_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_pause_release(struct device *dev,
+               unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_resume_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_start(struct device *dev, unsigned int str_id)
+{
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       str_info->prev = str_info->status;
+       str_info->status = STREAM_RUNNING;
+       return sst_start_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drop(struct device *dev, unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_cdev_stream_drain(struct device *dev, unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_drain_stream(ctx, str_id, false);
+}
+
+static int sst_cdev_stream_partial_drain(struct device *dev,
+               unsigned int str_id)
+{
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       return sst_drain_stream(ctx, str_id, true);
+}
+
+static int sst_cdev_tstamp(struct device *dev, unsigned int str_id,
+               struct snd_compr_tstamp *tstamp)
+{
+       struct snd_sst_tstamp fw_tstamp = {0,};
+       struct stream_info *stream;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       memcpy_fromio(&fw_tstamp,
+               ((void *)(ctx->mailbox + ctx->tstamp)
+               +(str_id * sizeof(fw_tstamp))),
+               sizeof(fw_tstamp));
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream)
+               return -EINVAL;
+       dev_dbg(dev, "rb_counter %llu in bytes\n", fw_tstamp.ring_buffer_counter);
+
+       tstamp->copied_total = fw_tstamp.ring_buffer_counter;
+       tstamp->pcm_frames = fw_tstamp.frames_decoded;
+       tstamp->pcm_io_frames = div_u64(fw_tstamp.hardware_counter,
+                       (u64)((stream->num_ch) * SST_GET_BYTES_PER_SAMPLE(24)));
+       tstamp->sampling_rate = fw_tstamp.sampling_frequency;
+
+       dev_dbg(dev, "PCM  = %u\n", tstamp->pcm_io_frames);
+       dev_dbg(dev, "Ptr Query on strid = %d  copied_total %d, decodec %d\n",
+               str_id, tstamp->copied_total, tstamp->pcm_frames);
+       dev_dbg(dev, "rendered %d\n", tstamp->pcm_io_frames);
+
+       return 0;
+}
+
+static int sst_cdev_caps(struct snd_compr_caps *caps)
+{
+       caps->num_codecs = NUM_CODEC;
+       caps->min_fragment_size = MIN_FRAGMENT_SIZE;  /* 50KB */
+       caps->max_fragment_size = MAX_FRAGMENT_SIZE;  /* 1024KB */
+       caps->min_fragments = MIN_FRAGMENT;
+       caps->max_fragments = MAX_FRAGMENT;
+       caps->codecs[0] = SND_AUDIOCODEC_MP3;
+       caps->codecs[1] = SND_AUDIOCODEC_AAC;
+       return 0;
+}
+
+static struct snd_compr_codec_caps caps_mp3 = {
+       .num_descriptors = 1,
+       .descriptor[0].max_ch = 2,
+       .descriptor[0].sample_rates[0] = 48000,
+       .descriptor[0].sample_rates[1] = 44100,
+       .descriptor[0].sample_rates[2] = 32000,
+       .descriptor[0].sample_rates[3] = 16000,
+       .descriptor[0].sample_rates[4] = 8000,
+       .descriptor[0].num_sample_rates = 5,
+       .descriptor[0].bit_rate[0] = 320,
+       .descriptor[0].bit_rate[1] = 192,
+       .descriptor[0].num_bitrates = 2,
+       .descriptor[0].profiles = 0,
+       .descriptor[0].modes = SND_AUDIOCHANMODE_MP3_STEREO,
+       .descriptor[0].formats = 0,
+};
+
+static struct snd_compr_codec_caps caps_aac = {
+       .num_descriptors = 2,
+       .descriptor[1].max_ch = 2,
+       .descriptor[0].sample_rates[0] = 48000,
+       .descriptor[0].sample_rates[1] = 44100,
+       .descriptor[0].sample_rates[2] = 32000,
+       .descriptor[0].sample_rates[3] = 16000,
+       .descriptor[0].sample_rates[4] = 8000,
+       .descriptor[0].num_sample_rates = 5,
+       .descriptor[1].bit_rate[0] = 320,
+       .descriptor[1].bit_rate[1] = 192,
+       .descriptor[1].num_bitrates = 2,
+       .descriptor[1].profiles = 0,
+       .descriptor[1].modes = 0,
+       .descriptor[1].formats =
+                       (SND_AUDIOSTREAMFORMAT_MP4ADTS |
+                               SND_AUDIOSTREAMFORMAT_RAW),
+};
+
+static int sst_cdev_codec_caps(struct snd_compr_codec_caps *codec)
+{
+       if (codec->codec == SND_AUDIOCODEC_MP3)
+               *codec = caps_mp3;
+       else if (codec->codec == SND_AUDIOCODEC_AAC)
+               *codec = caps_aac;
+       else
+               return -EINVAL;
+
+       return 0;
+}
+
+void sst_cdev_fragment_elapsed(struct intel_sst_drv *ctx, int str_id)
+{
+       struct stream_info *stream;
+
+       dev_dbg(ctx->dev, "fragment elapsed from firmware for str_id %d\n",
+                       str_id);
+       stream = &ctx->streams[str_id];
+       if (stream->compr_cb)
+               stream->compr_cb(stream->compr_cb_param);
+}
+
+/*
+ * sst_close_pcm_stream - Close PCM interface
+ *
+ * @str_id: stream id to be closed
+ *
+ * This function is called by MID sound card driver to close
+ * an existing pcm interface
+ */
+static int sst_close_pcm_stream(struct device *dev, unsigned int str_id)
+{
+       struct stream_info *stream;
+       int retval = 0;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream) {
+               dev_err(ctx->dev, "stream info is NULL for str %d!!!\n", str_id);
+               return -EINVAL;
+       }
+
+       if (stream->status == STREAM_RESET) {
+               /* silently fail here as we have cleaned the stream earlier */
+               dev_dbg(ctx->dev, "stream in reset state...\n");
+
+               retval = 0;
+               goto put;
+       }
+
+       retval = free_stream_context(ctx, str_id);
+put:
+       stream->pcm_substream = NULL;
+       stream->status = STREAM_UN_INIT;
+       stream->period_elapsed = NULL;
+       ctx->stream_cnt--;
+
+       if (retval)
+               dev_err(ctx->dev, "free stream returned err %d\n", retval);
+
+       dev_dbg(ctx->dev, "Exit\n");
+       return 0;
+}
+
+static inline int sst_calc_tstamp(struct intel_sst_drv *ctx,
+               struct pcm_stream_info *info,
+               struct snd_pcm_substream *substream,
+               struct snd_sst_tstamp *fw_tstamp)
+{
+       size_t delay_bytes, delay_frames;
+       size_t buffer_sz;
+       u32 pointer_bytes, pointer_samples;
+
+       dev_dbg(ctx->dev, "mrfld ring_buffer_counter %llu in bytes\n",
+                       fw_tstamp->ring_buffer_counter);
+       dev_dbg(ctx->dev, "mrfld hardware_counter %llu in bytes\n",
+                        fw_tstamp->hardware_counter);
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               delay_bytes = (size_t) (fw_tstamp->ring_buffer_counter -
+                                       fw_tstamp->hardware_counter);
+       else
+               delay_bytes = (size_t) (fw_tstamp->hardware_counter -
+                                       fw_tstamp->ring_buffer_counter);
+       delay_frames = bytes_to_frames(substream->runtime, delay_bytes);
+       buffer_sz = snd_pcm_lib_buffer_bytes(substream);
+       div_u64_rem(fw_tstamp->ring_buffer_counter, buffer_sz, &pointer_bytes);
+       pointer_samples = bytes_to_samples(substream->runtime, pointer_bytes);
+
+       dev_dbg(ctx->dev, "pcm delay %zu in bytes\n", delay_bytes);
+
+       info->buffer_ptr = pointer_samples / substream->runtime->channels;
+
+       info->pcm_delay = delay_frames / substream->runtime->channels;
+       dev_dbg(ctx->dev, "buffer ptr %llu pcm_delay rep: %llu\n",
+                       info->buffer_ptr, info->pcm_delay);
+       return 0;
+}
+
+static int sst_read_timestamp(struct device *dev, struct pcm_stream_info *info)
+{
+       struct stream_info *stream;
+       struct snd_pcm_substream *substream;
+       struct snd_sst_tstamp fw_tstamp;
+       unsigned int str_id;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       str_id = info->str_id;
+       stream = get_stream_info(ctx, str_id);
+       if (!stream)
+               return -EINVAL;
+
+       if (!stream->pcm_substream)
+               return -EINVAL;
+       substream = stream->pcm_substream;
+
+       memcpy_fromio(&fw_tstamp,
+               ((void *)(ctx->mailbox + ctx->tstamp)
+                       + (str_id * sizeof(fw_tstamp))),
+               sizeof(fw_tstamp));
+       return sst_calc_tstamp(ctx, info, substream, &fw_tstamp);
+}
+
+static int sst_stream_start(struct device *dev, int str_id)
+{
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state != SST_FW_RUNNING)
+               return 0;
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       str_info->prev = str_info->status;
+       str_info->status = STREAM_RUNNING;
+       sst_start_stream(ctx, str_id);
+
+       return 0;
+}
+
+static int sst_stream_drop(struct device *dev, int str_id)
+{
+       struct stream_info *str_info;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (ctx->sst_state != SST_FW_RUNNING)
+               return 0;
+
+       str_info = get_stream_info(ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       str_info->prev = STREAM_UN_INIT;
+       str_info->status = STREAM_INIT;
+       return sst_drop_stream(ctx, str_id);
+}
+
+static int sst_stream_init(struct device *dev, struct pcm_stream_info *str_info)
+{
+       int str_id = 0;
+       struct stream_info *stream;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       str_id = str_info->str_id;
+
+       if (ctx->sst_state != SST_FW_RUNNING)
+               return 0;
+
+       stream = get_stream_info(ctx, str_id);
+       if (!stream)
+               return -EINVAL;
+
+       dev_dbg(ctx->dev, "setting the period ptrs\n");
+       stream->pcm_substream = str_info->arg;
+       stream->period_elapsed = str_info->period_elapsed;
+       stream->sfreq = str_info->sfreq;
+       stream->prev = stream->status;
+       stream->status = STREAM_INIT;
+       dev_dbg(ctx->dev,
+               "pcm_substream %p, period_elapsed %p, sfreq %d, status %d\n",
+               stream->pcm_substream, stream->period_elapsed,
+               stream->sfreq, stream->status);
+
+       return 0;
+}
+
+/*
+ * sst_set_byte_stream - Set generic params
+ *
+ * @cmd: control cmd to be set
+ * @arg: command argument
+ *
+ * This function is called by MID sound card driver to configure
+ * SST runtime params.
+ */
+static int sst_send_byte_stream(struct device *dev,
+               struct snd_sst_bytes_v2 *bytes)
+{
+       int ret_val = 0;
+       struct intel_sst_drv *ctx = dev_get_drvdata(dev);
+
+       if (NULL == bytes)
+               return -EINVAL;
+       ret_val = pm_runtime_get_sync(ctx->dev);
+       if (ret_val < 0)
+               return ret_val;
+
+       ret_val = sst_send_byte_stream_mrfld(ctx, bytes);
+       sst_pm_runtime_put(ctx);
+
+       return ret_val;
+}
+
+static struct sst_ops pcm_ops = {
+       .open = sst_open_pcm_stream,
+       .stream_init = sst_stream_init,
+       .stream_start = sst_stream_start,
+       .stream_drop = sst_stream_drop,
+       .stream_read_tstamp = sst_read_timestamp,
+       .send_byte_stream = sst_send_byte_stream,
+       .close = sst_close_pcm_stream,
+       .power = sst_power_control,
+};
+
+static struct compress_sst_ops compr_ops = {
+       .open = sst_cdev_open,
+       .close = sst_cdev_close,
+       .stream_pause = sst_cdev_stream_pause,
+       .stream_pause_release = sst_cdev_stream_pause_release,
+       .stream_start = sst_cdev_stream_start,
+       .stream_drop = sst_cdev_stream_drop,
+       .stream_drain = sst_cdev_stream_drain,
+       .stream_partial_drain = sst_cdev_stream_partial_drain,
+       .tstamp = sst_cdev_tstamp,
+       .ack = sst_cdev_ack,
+       .get_caps = sst_cdev_caps,
+       .get_codec_caps = sst_cdev_codec_caps,
+       .set_metadata = sst_cdev_set_metadata,
+       .power = sst_power_control,
+};
+
+static struct sst_device sst_dsp_device = {
+       .name = "Intel(R) SST LPE",
+       .dev = NULL,
+       .ops = &pcm_ops,
+       .compr_ops = &compr_ops,
+};
+
+/*
+ * sst_register - function to register DSP
+ *
+ * This functions registers DSP with the platform driver
+ */
+int sst_register(struct device *dev)
+{
+       int ret_val;
+
+       sst_dsp_device.dev = dev;
+       ret_val = sst_register_dsp(&sst_dsp_device);
+       if (ret_val)
+               dev_err(dev, "Unable to register DSP with platform driver\n");
+
+       return ret_val;
+}
+
+int sst_unregister(struct device *dev)
+{
+       return sst_unregister_dsp(&sst_dsp_device);
+}
diff --git a/sound/soc/intel/sst/sst_ipc.c b/sound/soc/intel/sst/sst_ipc.c
new file mode 100644 (file)
index 0000000..484e609
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ *  sst_ipc.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corporation
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/intel-mid.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+struct sst_block *sst_create_block(struct intel_sst_drv *ctx,
+                                       u32 msg_id, u32 drv_id)
+{
+       struct sst_block *msg = NULL;
+
+       dev_dbg(ctx->dev, "Enter\n");
+       msg = kzalloc(sizeof(*msg), GFP_KERNEL);
+       if (!msg)
+               return NULL;
+       msg->condition = false;
+       msg->on = true;
+       msg->msg_id = msg_id;
+       msg->drv_id = drv_id;
+       spin_lock_bh(&ctx->block_lock);
+       list_add_tail(&msg->node, &ctx->block_list);
+       spin_unlock_bh(&ctx->block_lock);
+
+       return msg;
+}
+
+/*
+ * while handling the interrupts, we need to check for message status and
+ * then if we are blocking for a message
+ *
+ * here we are unblocking the blocked ones, this is based on id we have
+ * passed and search that for block threads.
+ * We will not find block in two cases
+ *  a) when its small message and block in not there, so silently ignore
+ *  them
+ *  b) when we are actually not able to find the block (bug perhaps)
+ *
+ *  Since we have bit of small messages we can spam kernel log with err
+ *  print on above so need to keep as debug prints which should be enabled
+ *  via dynamic debug while debugging IPC issues
+ */
+int sst_wake_up_block(struct intel_sst_drv *ctx, int result,
+               u32 drv_id, u32 ipc, void *data, u32 size)
+{
+       struct sst_block *block = NULL;
+
+       dev_dbg(ctx->dev, "Enter\n");
+
+       spin_lock_bh(&ctx->block_lock);
+       list_for_each_entry(block, &ctx->block_list, node) {
+               dev_dbg(ctx->dev, "Block ipc %d, drv_id %d\n", block->msg_id,
+                                                       block->drv_id);
+               if (block->msg_id == ipc && block->drv_id == drv_id) {
+                       dev_dbg(ctx->dev, "free up the block\n");
+                       block->ret_code = result;
+                       block->data = data;
+                       block->size = size;
+                       block->condition = true;
+                       spin_unlock_bh(&ctx->block_lock);
+                       wake_up(&ctx->wait_queue);
+                       return 0;
+               }
+       }
+       spin_unlock_bh(&ctx->block_lock);
+       dev_dbg(ctx->dev,
+               "Block not found or a response received for a short msg for ipc %d, drv_id %d\n",
+               ipc, drv_id);
+       return -EINVAL;
+}
+
+int sst_free_block(struct intel_sst_drv *ctx, struct sst_block *freed)
+{
+       struct sst_block *block = NULL, *__block;
+
+       dev_dbg(ctx->dev, "Enter\n");
+       spin_lock_bh(&ctx->block_lock);
+       list_for_each_entry_safe(block, __block, &ctx->block_list, node) {
+               if (block == freed) {
+                       pr_debug("pvt_id freed --> %d\n", freed->drv_id);
+                       /* toggle the index position of pvt_id */
+                       list_del(&freed->node);
+                       spin_unlock_bh(&ctx->block_lock);
+                       kfree(freed->data);
+                       freed->data = NULL;
+                       kfree(freed);
+                       return 0;
+               }
+       }
+       spin_unlock_bh(&ctx->block_lock);
+       dev_err(ctx->dev, "block is already freed!!!\n");
+       return -EINVAL;
+}
+
+int sst_post_message_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               struct ipc_post *ipc_msg, bool sync)
+{
+       struct ipc_post *msg = ipc_msg;
+       union ipc_header_mrfld header;
+       unsigned int loop_count = 0;
+       int retval = 0;
+       unsigned long irq_flags;
+
+       dev_dbg(sst_drv_ctx->dev, "Enter: sync: %d\n", sync);
+       spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+       header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+       if (sync) {
+               while (header.p.header_high.part.busy) {
+                       if (loop_count > 25) {
+                               dev_err(sst_drv_ctx->dev,
+                                       "sst: Busy wait failed, cant send this msg\n");
+                               retval = -EBUSY;
+                               goto out;
+                       }
+                       cpu_relax();
+                       loop_count++;
+                       header.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCX);
+               }
+       } else {
+               if (list_empty(&sst_drv_ctx->ipc_dispatch_list)) {
+                       /* queue is empty, nothing to send */
+                       spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+                       dev_dbg(sst_drv_ctx->dev,
+                                       "Empty msg queue... NO Action\n");
+                       return 0;
+               }
+
+               if (header.p.header_high.part.busy) {
+                       spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+                       dev_dbg(sst_drv_ctx->dev, "Busy not free... post later\n");
+                       return 0;
+               }
+
+               /* copy msg from list */
+               msg = list_entry(sst_drv_ctx->ipc_dispatch_list.next,
+                               struct ipc_post, node);
+               list_del(&msg->node);
+       }
+       dev_dbg(sst_drv_ctx->dev, "sst: Post message: header = %x\n",
+                               msg->mrfld_header.p.header_high.full);
+       dev_dbg(sst_drv_ctx->dev, "sst: size = 0x%x\n",
+                       msg->mrfld_header.p.header_low_payload);
+
+       if (msg->mrfld_header.p.header_high.part.large)
+               memcpy_toio(sst_drv_ctx->mailbox + SST_MAILBOX_SEND,
+                       msg->mailbox_data,
+                       msg->mrfld_header.p.header_low_payload);
+
+       sst_shim_write64(sst_drv_ctx->shim, SST_IPCX, msg->mrfld_header.full);
+
+out:
+       spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+       kfree(msg->mailbox_data);
+       kfree(msg);
+       return retval;
+}
+
+void intel_sst_clear_intr_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+       union interrupt_reg_mrfld isr;
+       union interrupt_reg_mrfld imr;
+       union ipc_header_mrfld clear_ipc;
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+       imr.full = sst_shim_read64(sst_drv_ctx->shim, SST_IMRX);
+       isr.full = sst_shim_read64(sst_drv_ctx->shim, SST_ISRX);
+
+       /* write 1 to clear*/
+       isr.part.busy_interrupt = 1;
+       sst_shim_write64(sst_drv_ctx->shim, SST_ISRX, isr.full);
+
+       /* Set IA done bit */
+       clear_ipc.full = sst_shim_read64(sst_drv_ctx->shim, SST_IPCD);
+
+       clear_ipc.p.header_high.part.busy = 0;
+       clear_ipc.p.header_high.part.done = 1;
+       clear_ipc.p.header_low_payload = IPC_ACK_SUCCESS;
+       sst_shim_write64(sst_drv_ctx->shim, SST_IPCD, clear_ipc.full);
+       /* un mask busy interrupt */
+       imr.part.busy_interrupt = 0;
+       sst_shim_write64(sst_drv_ctx->shim, SST_IMRX, imr.full);
+       spin_unlock_irqrestore(&sst_drv_ctx->ipc_spin_lock, irq_flags);
+}
+
+
+/*
+ * process_fw_init - process the FW init msg
+ *
+ * @msg: IPC message mailbox data from FW
+ *
+ * This function processes the FW init msg from FW
+ * marks FW state and prints debug info of loaded FW
+ */
+static void process_fw_init(struct intel_sst_drv *sst_drv_ctx,
+                       void *msg)
+{
+       struct ipc_header_fw_init *init =
+               (struct ipc_header_fw_init *)msg;
+       int retval = 0;
+
+       dev_dbg(sst_drv_ctx->dev, "*** FW Init msg came***\n");
+       if (init->result) {
+               sst_set_fw_state_locked(sst_drv_ctx, SST_RESET);
+               dev_err(sst_drv_ctx->dev, "FW Init failed, Error %x\n",
+                               init->result);
+               retval = init->result;
+               goto ret;
+       }
+
+ret:
+       sst_wake_up_block(sst_drv_ctx, retval, FW_DWNL_ID, 0 , NULL, 0);
+}
+
+static void process_fw_async_msg(struct intel_sst_drv *sst_drv_ctx,
+                       struct ipc_post *msg)
+{
+       u32 msg_id;
+       int str_id;
+       u32 data_size, i;
+       void *data_offset;
+       struct stream_info *stream;
+       union ipc_header_high msg_high;
+       u32 msg_low, pipe_id;
+
+       msg_high = msg->mrfld_header.p.header_high;
+       msg_low = msg->mrfld_header.p.header_low_payload;
+       msg_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->cmd_id;
+       data_offset = (msg->mailbox_data + sizeof(struct ipc_dsp_hdr));
+       data_size =  msg_low - (sizeof(struct ipc_dsp_hdr));
+
+       switch (msg_id) {
+       case IPC_SST_PERIOD_ELAPSED_MRFLD:
+               pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+               str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+               if (str_id > 0) {
+                       dev_dbg(sst_drv_ctx->dev,
+                               "Period elapsed rcvd for pipe id 0x%x\n",
+                               pipe_id);
+                       stream = &sst_drv_ctx->streams[str_id];
+                       if (stream->period_elapsed)
+                               stream->period_elapsed(stream->pcm_substream);
+                       if (stream->compr_cb)
+                               stream->compr_cb(stream->compr_cb_param);
+               }
+               break;
+
+       case IPC_IA_DRAIN_STREAM_MRFLD:
+               pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+               str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+               if (str_id > 0) {
+                       stream = &sst_drv_ctx->streams[str_id];
+                       if (stream->drain_notify)
+                               stream->drain_notify(stream->drain_cb_param);
+               }
+               break;
+
+       case IPC_IA_FW_ASYNC_ERR_MRFLD:
+               dev_err(sst_drv_ctx->dev, "FW sent async error msg:\n");
+               for (i = 0; i < (data_size/4); i++)
+                       print_hex_dump(KERN_DEBUG, NULL, DUMP_PREFIX_NONE,
+                                       16, 4, data_offset, data_size, false);
+               break;
+
+       case IPC_IA_FW_INIT_CMPLT_MRFLD:
+               process_fw_init(sst_drv_ctx, data_offset);
+               break;
+
+       case IPC_IA_BUF_UNDER_RUN_MRFLD:
+               pipe_id = ((struct ipc_dsp_hdr *)msg->mailbox_data)->pipe_id;
+               str_id = get_stream_id_mrfld(sst_drv_ctx, pipe_id);
+               if (str_id > 0)
+                       dev_err(sst_drv_ctx->dev,
+                               "Buffer under-run for pipe:%#x str_id:%d\n",
+                               pipe_id, str_id);
+               break;
+
+       default:
+               dev_err(sst_drv_ctx->dev,
+                       "Unrecognized async msg from FW msg_id %#x\n", msg_id);
+       }
+}
+
+void sst_process_reply_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               struct ipc_post *msg)
+{
+       unsigned int drv_id;
+       void *data;
+       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;
+
+       dev_dbg(sst_drv_ctx->dev, "IPC process message header %x payload %x\n",
+                       msg->mrfld_header.p.header_high.full,
+                       msg->mrfld_header.p.header_low_payload);
+
+       drv_id = msg_high.part.drv_id;
+
+       /* Check for async messages first */
+       if (drv_id == SST_ASYNC_DRV_ID) {
+               /*FW sent async large message*/
+               process_fw_async_msg(sst_drv_ctx, msg);
+               return;
+       }
+
+       /* FW sent short error response for an IPC */
+       if (msg_high.part.result && drv_id && !msg_high.part.large) {
+               /* 32-bit FW error code in msg_low */
+               dev_err(sst_drv_ctx->dev, "FW sent error response 0x%x", msg_low);
+               sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+                       msg_high.part.drv_id,
+                       msg_high.part.msg_id, NULL, 0);
+               return;
+       }
+
+       /*
+        * Process all valid responses
+        * if it is a large message, the payload contains the size to
+        * copy from mailbox
+        **/
+       if (msg_high.part.large) {
+               data = kzalloc(msg_low, GFP_KERNEL);
+               if (!data)
+                       return;
+               memcpy(data, (void *) msg->mailbox_data, msg_low);
+               /* 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,
+                               msg_high.part.msg_id, data, msg_low))
+                       kfree(data);
+       } else {
+               sst_wake_up_block(sst_drv_ctx, msg_high.part.result,
+                               msg_high.part.drv_id,
+                               msg_high.part.msg_id, NULL, 0);
+       }
+
+}
diff --git a/sound/soc/intel/sst/sst_loader.c b/sound/soc/intel/sst/sst_loader.c
new file mode 100644 (file)
index 0000000..b580f96
--- /dev/null
@@ -0,0 +1,456 @@
+/*
+ *  sst_dsp.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14      Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This file contains all dsp controlling functions like firmware download,
+ * setting/resetting dsp cores, etc
+ */
+#include <linux/pci.h>
+#include <linux/delay.h>
+#include <linux/fs.h>
+#include <linux/sched.h>
+#include <linux/firmware.h>
+#include <linux/dmaengine.h>
+#include <linux/pm_runtime.h>
+#include <linux/pm_qos.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+static inline void memcpy32_toio(void __iomem *dst, const void *src, int count)
+{
+       /* __iowrite32_copy uses 32-bit count values so divide by 4 for
+        * right count in words
+        */
+       __iowrite32_copy(dst, src, count/4);
+}
+
+/**
+ * intel_sst_reset_dsp_mrfld - Resetting SST DSP
+ *
+ * This resets DSP in case of MRFLD platfroms
+ */
+int intel_sst_reset_dsp_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+       union config_status_reg_mrfld csr;
+
+       dev_dbg(sst_drv_ctx->dev, "sst: Resetting the DSP in mrfld\n");
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+       csr.full |= 0x7;
+       sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+       csr.full &= ~(0x1);
+       sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+       return 0;
+}
+
+/**
+ * sst_start_merrifield - Start the SST DSP processor
+ *
+ * This starts the DSP in MERRIFIELD platfroms
+ */
+int sst_start_mrfld(struct intel_sst_drv *sst_drv_ctx)
+{
+       union config_status_reg_mrfld csr;
+
+       dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP in mrfld LALALALA\n");
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+       csr.full |= 0x7;
+       sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+       dev_dbg(sst_drv_ctx->dev, "value:0x%llx\n", csr.full);
+
+       csr.part.xt_snoop = 1;
+       csr.full &= ~(0x5);
+       sst_shim_write64(sst_drv_ctx->shim, SST_CSR, csr.full);
+
+       csr.full = sst_shim_read64(sst_drv_ctx->shim, SST_CSR);
+       dev_dbg(sst_drv_ctx->dev, "sst: Starting the DSP_merrifield:%llx\n",
+                       csr.full);
+       return 0;
+}
+
+static int sst_validate_fw_image(struct intel_sst_drv *ctx, unsigned long size,
+               struct fw_module_header **module, u32 *num_modules)
+{
+       struct sst_fw_header *header;
+       const void *sst_fw_in_mem = ctx->fw_in_mem;
+
+       dev_dbg(ctx->dev, "Enter\n");
+
+       /* Read the header information from the data pointer */
+       header = (struct sst_fw_header *)sst_fw_in_mem;
+       dev_dbg(ctx->dev,
+               "header sign=%s size=%x modules=%x fmt=%x size=%zx\n",
+               header->signature, header->file_size, header->modules,
+               header->file_format, sizeof(*header));
+
+       /* verify FW */
+       if ((strncmp(header->signature, SST_FW_SIGN, 4) != 0) ||
+               (size != header->file_size + sizeof(*header))) {
+               /* Invalid FW signature */
+               dev_err(ctx->dev, "InvalidFW sign/filesize mismatch\n");
+               return -EINVAL;
+       }
+       *num_modules = header->modules;
+       *module = (void *)sst_fw_in_mem + sizeof(*header);
+
+       return 0;
+}
+
+/*
+ * sst_fill_memcpy_list - Fill the memcpy list
+ *
+ * @memcpy_list: List to be filled
+ * @destn: Destination addr to be filled in the list
+ * @src: Source addr to be filled in the list
+ * @size: Size to be filled in the list
+ *
+ * Adds the node to the list after required fields
+ * are populated in the node
+ */
+static int sst_fill_memcpy_list(struct list_head *memcpy_list,
+                       void *destn, const void *src, u32 size, bool is_io)
+{
+       struct sst_memcpy_list *listnode;
+
+       listnode = kzalloc(sizeof(*listnode), GFP_KERNEL);
+       if (listnode == NULL)
+               return -ENOMEM;
+       listnode->dstn = destn;
+       listnode->src = src;
+       listnode->size = size;
+       listnode->is_io = is_io;
+       list_add_tail(&listnode->memcpylist, memcpy_list);
+
+       return 0;
+}
+
+/**
+ * sst_parse_module_memcpy - Parse audio FW modules and populate the memcpy list
+ *
+ * @sst_drv_ctx                : driver context
+ * @module             : FW module header
+ * @memcpy_list        : Pointer to the list to be populated
+ * Create the memcpy list as the number of block to be copied
+ * returns error or 0 if module sizes are proper
+ */
+static int sst_parse_module_memcpy(struct intel_sst_drv *sst_drv_ctx,
+               struct fw_module_header *module, struct list_head *memcpy_list)
+{
+       struct fw_block_info *block;
+       u32 count;
+       int ret_val = 0;
+       void __iomem *ram_iomem;
+
+       dev_dbg(sst_drv_ctx->dev, "module sign %s size %x blocks %x type %x\n",
+                       module->signature, module->mod_size,
+                       module->blocks, module->type);
+       dev_dbg(sst_drv_ctx->dev, "module entrypoint 0x%x\n", module->entry_point);
+
+       block = (void *)module + sizeof(*module);
+
+       for (count = 0; count < module->blocks; count++) {
+               if (block->size <= 0) {
+                       dev_err(sst_drv_ctx->dev, "block size invalid\n");
+                       return -EINVAL;
+               }
+               switch (block->type) {
+               case SST_IRAM:
+                       ram_iomem = sst_drv_ctx->iram;
+                       break;
+               case SST_DRAM:
+                       ram_iomem = sst_drv_ctx->dram;
+                       break;
+               case SST_DDR:
+                       ram_iomem = sst_drv_ctx->ddr;
+                       break;
+               case SST_CUSTOM_INFO:
+                       block = (void *)block + sizeof(*block) + block->size;
+                       continue;
+               default:
+                       dev_err(sst_drv_ctx->dev, "wrong ram type0x%x in block0x%x\n",
+                                       block->type, count);
+                       return -EINVAL;
+               }
+
+               ret_val = sst_fill_memcpy_list(memcpy_list,
+                               ram_iomem + block->ram_offset,
+                               (void *)block + sizeof(*block), block->size, 1);
+               if (ret_val)
+                       return ret_val;
+
+               block = (void *)block + sizeof(*block) + block->size;
+       }
+       return 0;
+}
+
+/**
+ * sst_parse_fw_memcpy - parse the firmware image & populate the list for memcpy
+ *
+ * @ctx                        : pointer to drv context
+ * @size               : size of the firmware
+ * @fw_list            : pointer to list_head to be populated
+ * This function parses the FW image and saves the parsed image in the list
+ * for memcpy
+ */
+static int sst_parse_fw_memcpy(struct intel_sst_drv *ctx, unsigned long size,
+                               struct list_head *fw_list)
+{
+       struct fw_module_header *module;
+       u32 count, num_modules;
+       int ret_val;
+
+       ret_val = sst_validate_fw_image(ctx, size, &module, &num_modules);
+       if (ret_val)
+               return ret_val;
+
+       for (count = 0; count < num_modules; count++) {
+               ret_val = sst_parse_module_memcpy(ctx, module, fw_list);
+               if (ret_val)
+                       return ret_val;
+               module = (void *)module + sizeof(*module) + module->mod_size;
+       }
+
+       return 0;
+}
+
+/**
+ * sst_do_memcpy - function initiates the memcpy
+ *
+ * @memcpy_list: Pter to memcpy list on which the memcpy needs to be initiated
+ *
+ * Triggers the memcpy
+ */
+static void sst_do_memcpy(struct list_head *memcpy_list)
+{
+       struct sst_memcpy_list *listnode;
+
+       list_for_each_entry(listnode, memcpy_list, memcpylist) {
+               if (listnode->is_io == true)
+                       memcpy32_toio((void __iomem *)listnode->dstn,
+                                       listnode->src, listnode->size);
+               else
+                       memcpy(listnode->dstn, listnode->src, listnode->size);
+       }
+}
+
+void sst_memcpy_free_resources(struct intel_sst_drv *sst_drv_ctx)
+{
+       struct sst_memcpy_list *listnode, *tmplistnode;
+
+       /* Free the list */
+       if (!list_empty(&sst_drv_ctx->memcpy_list)) {
+               list_for_each_entry_safe(listnode, tmplistnode,
+                               &sst_drv_ctx->memcpy_list, memcpylist) {
+                       list_del(&listnode->memcpylist);
+                       kfree(listnode);
+               }
+       }
+}
+
+static int sst_cache_and_parse_fw(struct intel_sst_drv *sst,
+               const struct firmware *fw)
+{
+       int retval = 0;
+
+       sst->fw_in_mem = kzalloc(fw->size, GFP_KERNEL);
+       if (!sst->fw_in_mem) {
+               retval = -ENOMEM;
+               goto end_release;
+       }
+       dev_dbg(sst->dev, "copied fw to %p", sst->fw_in_mem);
+       dev_dbg(sst->dev, "phys: %lx", (unsigned long)virt_to_phys(sst->fw_in_mem));
+       memcpy(sst->fw_in_mem, fw->data, fw->size);
+       retval = sst_parse_fw_memcpy(sst, fw->size, &sst->memcpy_list);
+       if (retval) {
+               dev_err(sst->dev, "Failed to parse fw\n");
+               kfree(sst->fw_in_mem);
+               sst->fw_in_mem = NULL;
+       }
+
+end_release:
+       release_firmware(fw);
+       return retval;
+
+}
+
+void sst_firmware_load_cb(const struct firmware *fw, void *context)
+{
+       struct intel_sst_drv *ctx = context;
+
+       dev_dbg(ctx->dev, "Enter\n");
+
+       if (fw == NULL) {
+               dev_err(ctx->dev, "request fw failed\n");
+               return;
+       }
+
+       mutex_lock(&ctx->sst_lock);
+
+       if (ctx->sst_state != SST_RESET ||
+                       ctx->fw_in_mem != NULL) {
+               if (fw != NULL)
+                       release_firmware(fw);
+               mutex_unlock(&ctx->sst_lock);
+               return;
+       }
+
+       dev_dbg(ctx->dev, "Request Fw completed\n");
+       sst_cache_and_parse_fw(ctx, fw);
+       mutex_unlock(&ctx->sst_lock);
+}
+
+/*
+ * sst_request_fw - requests audio fw from kernel and saves a copy
+ *
+ * This function requests the SST FW from the kernel, parses it and
+ * saves a copy in the driver context
+ */
+static int sst_request_fw(struct intel_sst_drv *sst)
+{
+       int retval = 0;
+       const struct firmware *fw;
+
+       retval = request_firmware(&fw, sst->firmware_name, sst->dev);
+       if (fw == NULL) {
+               dev_err(sst->dev, "fw is returning as null\n");
+               return -EINVAL;
+       }
+       if (retval) {
+               dev_err(sst->dev, "request fw failed %d\n", retval);
+               return retval;
+       }
+       mutex_lock(&sst->sst_lock);
+       retval = sst_cache_and_parse_fw(sst, fw);
+       mutex_unlock(&sst->sst_lock);
+
+       return retval;
+}
+
+/*
+ * Writing the DDR physical base to DCCM offset
+ * so that FW can use it to setup TLB
+ */
+static void sst_dccm_config_write(void __iomem *dram_base,
+               unsigned int ddr_base)
+{
+       void __iomem *addr;
+       u32 bss_reset = 0;
+
+       addr = (void __iomem *)(dram_base + MRFLD_FW_DDR_BASE_OFFSET);
+       memcpy32_toio(addr, (void *)&ddr_base, sizeof(u32));
+       bss_reset |= (1 << MRFLD_FW_BSS_RESET_BIT);
+       addr = (void __iomem *)(dram_base + MRFLD_FW_FEATURE_BASE_OFFSET);
+       memcpy32_toio(addr, &bss_reset, sizeof(u32));
+
+}
+
+void sst_post_download_mrfld(struct intel_sst_drv *ctx)
+{
+       sst_dccm_config_write(ctx->dram, ctx->ddr_base);
+       dev_dbg(ctx->dev, "config written to DCCM\n");
+}
+
+/**
+ * sst_load_fw - function to load FW into DSP
+ * Transfers the FW to DSP using dma/memcpy
+ */
+int sst_load_fw(struct intel_sst_drv *sst_drv_ctx)
+{
+       int ret_val = 0;
+       struct sst_block *block;
+
+       dev_dbg(sst_drv_ctx->dev, "sst_load_fw\n");
+
+       if (sst_drv_ctx->sst_state !=  SST_RESET ||
+                       sst_drv_ctx->sst_state == SST_SHUTDOWN)
+               return -EAGAIN;
+
+       if (!sst_drv_ctx->fw_in_mem) {
+               dev_dbg(sst_drv_ctx->dev, "sst: FW not in memory retry to download\n");
+               ret_val = sst_request_fw(sst_drv_ctx);
+               if (ret_val)
+                       return ret_val;
+       }
+
+       BUG_ON(!sst_drv_ctx->fw_in_mem);
+       block = sst_create_block(sst_drv_ctx, 0, FW_DWNL_ID);
+       if (block == NULL)
+               return -ENOMEM;
+
+       /* Prevent C-states beyond C6 */
+       pm_qos_update_request(sst_drv_ctx->qos, 0);
+
+       sst_drv_ctx->sst_state = SST_FW_LOADING;
+
+       ret_val = sst_drv_ctx->ops->reset(sst_drv_ctx);
+       if (ret_val)
+               goto restore;
+
+       sst_do_memcpy(&sst_drv_ctx->memcpy_list);
+
+       /* Write the DRAM/DCCM config before enabling FW */
+       if (sst_drv_ctx->ops->post_download)
+               sst_drv_ctx->ops->post_download(sst_drv_ctx);
+
+       /* bring sst out of reset */
+       ret_val = sst_drv_ctx->ops->start(sst_drv_ctx);
+       if (ret_val)
+               goto restore;
+
+       ret_val = sst_wait_timeout(sst_drv_ctx, block);
+       if (ret_val) {
+               dev_err(sst_drv_ctx->dev, "fw download failed %d\n" , ret_val);
+               /* FW download failed due to timeout */
+               ret_val = -EBUSY;
+
+       }
+
+
+restore:
+       /* Re-enable Deeper C-states beyond C6 */
+       pm_qos_update_request(sst_drv_ctx->qos, PM_QOS_DEFAULT_VALUE);
+       sst_free_block(sst_drv_ctx, block);
+       dev_dbg(sst_drv_ctx->dev, "fw load successful!!!\n");
+
+       if (sst_drv_ctx->ops->restore_dsp_context)
+               sst_drv_ctx->ops->restore_dsp_context();
+       sst_drv_ctx->sst_state = SST_FW_RUNNING;
+       return ret_val;
+}
+
diff --git a/sound/soc/intel/sst/sst_pci.c b/sound/soc/intel/sst/sst_pci.c
new file mode 100644 (file)
index 0000000..3a0b3bf
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ *  sst_pci.c - SST (LPE) driver init file for pci enumeration.
+ *
+ *  Copyright (C) 2008-14      Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/module.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/soc.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+
+static int sst_platform_get_resources(struct intel_sst_drv *ctx)
+{
+       int ddr_base, ret = 0;
+       struct pci_dev *pci = ctx->pci;
+
+       ret = pci_request_regions(pci, SST_DRV_NAME);
+       if (ret)
+               return ret;
+
+       /* map registers */
+       /* DDR base */
+       if (ctx->dev_id == SST_MRFLD_PCI_ID) {
+               ctx->ddr_base = pci_resource_start(pci, 0);
+               /* check that the relocated IMR base matches with FW Binary */
+               ddr_base = relocate_imr_addr_mrfld(ctx->ddr_base);
+               if (!ctx->pdata->lib_info) {
+                       dev_err(ctx->dev, "lib_info pointer NULL\n");
+                       ret = -EINVAL;
+                       goto do_release_regions;
+               }
+               if (ddr_base != ctx->pdata->lib_info->mod_base) {
+                       dev_err(ctx->dev,
+                                       "FW LSP DDR BASE does not match with IFWI\n");
+                       ret = -EINVAL;
+                       goto do_release_regions;
+               }
+               ctx->ddr_end = pci_resource_end(pci, 0);
+
+               ctx->ddr = pcim_iomap(pci, 0,
+                                       pci_resource_len(pci, 0));
+               if (!ctx->ddr) {
+                       ret = -EINVAL;
+                       goto do_release_regions;
+               }
+               dev_dbg(ctx->dev, "sst: DDR Ptr %p\n", ctx->ddr);
+       } else {
+               ctx->ddr = NULL;
+       }
+       /* SHIM */
+       ctx->shim_phy_add = pci_resource_start(pci, 1);
+       ctx->shim = pcim_iomap(pci, 1, pci_resource_len(pci, 1));
+       if (!ctx->shim) {
+               ret = -EINVAL;
+               goto do_release_regions;
+       }
+       dev_dbg(ctx->dev, "SST Shim Ptr %p\n", ctx->shim);
+
+       /* Shared SRAM */
+       ctx->mailbox_add = pci_resource_start(pci, 2);
+       ctx->mailbox = pcim_iomap(pci, 2, pci_resource_len(pci, 2));
+       if (!ctx->mailbox) {
+               ret = -EINVAL;
+               goto do_release_regions;
+       }
+       dev_dbg(ctx->dev, "SRAM Ptr %p\n", ctx->mailbox);
+
+       /* IRAM */
+       ctx->iram_end = pci_resource_end(pci, 3);
+       ctx->iram_base = pci_resource_start(pci, 3);
+       ctx->iram = pcim_iomap(pci, 3, pci_resource_len(pci, 3));
+       if (!ctx->iram) {
+               ret = -EINVAL;
+               goto do_release_regions;
+       }
+       dev_dbg(ctx->dev, "IRAM Ptr %p\n", ctx->iram);
+
+       /* DRAM */
+       ctx->dram_end = pci_resource_end(pci, 4);
+       ctx->dram_base = pci_resource_start(pci, 4);
+       ctx->dram = pcim_iomap(pci, 4, pci_resource_len(pci, 4));
+       if (!ctx->dram) {
+               ret = -EINVAL;
+               goto do_release_regions;
+       }
+       dev_dbg(ctx->dev, "DRAM Ptr %p\n", ctx->dram);
+do_release_regions:
+       pci_release_regions(pci);
+       return 0;
+}
+
+/*
+ * intel_sst_probe - PCI probe function
+ *
+ * @pci:       PCI device structure
+ * @pci_id: PCI device ID structure
+ *
+ */
+static int intel_sst_probe(struct pci_dev *pci,
+                       const struct pci_device_id *pci_id)
+{
+       int ret = 0;
+       struct intel_sst_drv *sst_drv_ctx;
+       struct sst_platform_info *sst_pdata = pci->dev.platform_data;
+
+       dev_dbg(&pci->dev, "Probe for DID %x\n", pci->device);
+       ret = sst_alloc_drv_context(&sst_drv_ctx, &pci->dev, pci->device);
+       if (ret < 0)
+               return ret;
+
+       sst_drv_ctx->pdata = sst_pdata;
+       sst_drv_ctx->irq_num = pci->irq;
+       snprintf(sst_drv_ctx->firmware_name, sizeof(sst_drv_ctx->firmware_name),
+                       "%s%04x%s", "fw_sst_",
+                       sst_drv_ctx->dev_id, ".bin");
+
+       ret = sst_context_init(sst_drv_ctx);
+       if (ret < 0)
+               return ret;
+
+       /* Init the device */
+       ret = pcim_enable_device(pci);
+       if (ret) {
+               dev_err(sst_drv_ctx->dev,
+                       "device can't be enabled. Returned err: %d\n", ret);
+               goto do_free_drv_ctx;
+       }
+       sst_drv_ctx->pci = pci_dev_get(pci);
+       ret = sst_platform_get_resources(sst_drv_ctx);
+       if (ret < 0)
+               goto do_free_drv_ctx;
+
+       pci_set_drvdata(pci, sst_drv_ctx);
+       sst_configure_runtime_pm(sst_drv_ctx);
+
+       return ret;
+
+do_free_drv_ctx:
+       sst_context_cleanup(sst_drv_ctx);
+       dev_err(sst_drv_ctx->dev, "Probe failed with %d\n", ret);
+       return ret;
+}
+
+/**
+ * intel_sst_remove - PCI remove function
+ *
+ * @pci:       PCI device structure
+ *
+ * This function is called by OS when a device is unloaded
+ * This frees the interrupt etc
+ */
+static void intel_sst_remove(struct pci_dev *pci)
+{
+       struct intel_sst_drv *sst_drv_ctx = pci_get_drvdata(pci);
+
+       sst_context_cleanup(sst_drv_ctx);
+       pci_dev_put(sst_drv_ctx->pci);
+       pci_release_regions(pci);
+       pci_set_drvdata(pci, NULL);
+}
+
+/* PCI Routines */
+static struct pci_device_id intel_sst_ids[] = {
+       { PCI_VDEVICE(INTEL, SST_MRFLD_PCI_ID), 0},
+       { 0, }
+};
+
+static struct pci_driver sst_driver = {
+       .name = SST_DRV_NAME,
+       .id_table = intel_sst_ids,
+       .probe = intel_sst_probe,
+       .remove = intel_sst_remove,
+#ifdef CONFIG_PM
+       .driver = {
+               .pm = &intel_sst_pm,
+       },
+#endif
+};
+
+module_pci_driver(sst_driver);
+
+MODULE_DESCRIPTION("Intel (R) SST(R) Audio Engine PCI Driver");
+MODULE_AUTHOR("Vinod Koul <vinod.koul@intel.com>");
+MODULE_AUTHOR("Harsha Priya <priya.harsha@intel.com>");
+MODULE_AUTHOR("Dharageswari R <dharageswari.r@intel.com>");
+MODULE_AUTHOR("KP Jeeja <jeeja.kp@intel.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("sst");
diff --git a/sound/soc/intel/sst/sst_pvt.c b/sound/soc/intel/sst/sst_pvt.c
new file mode 100644 (file)
index 0000000..4b77208
--- /dev/null
@@ -0,0 +1,449 @@
+/*
+ *  sst_pvt.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14      Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/kobject.h>
+#include <linux/pci.h>
+#include <linux/fs.h>
+#include <linux/firmware.h>
+#include <linux/pm_runtime.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <sound/asound.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+int sst_shim_write(void __iomem *addr, int offset, int value)
+{
+       writel(value, addr + offset);
+       return 0;
+}
+
+u32 sst_shim_read(void __iomem *addr, int offset)
+{
+       return readl(addr + offset);
+}
+
+u64 sst_reg_read64(void __iomem *addr, int offset)
+{
+       u64 val = 0;
+
+       memcpy_fromio(&val, addr + offset, sizeof(val));
+
+       return val;
+}
+
+int sst_shim_write64(void __iomem *addr, int offset, u64 value)
+{
+       memcpy_toio(addr + offset, &value, sizeof(value));
+       return 0;
+}
+
+u64 sst_shim_read64(void __iomem *addr, int offset)
+{
+       u64 val = 0;
+
+       memcpy_fromio(&val, addr + offset, sizeof(val));
+       return val;
+}
+
+void sst_set_fw_state_locked(
+               struct intel_sst_drv *sst_drv_ctx, int sst_state)
+{
+       mutex_lock(&sst_drv_ctx->sst_lock);
+       sst_drv_ctx->sst_state = sst_state;
+       mutex_unlock(&sst_drv_ctx->sst_lock);
+}
+
+/*
+ * sst_wait_interruptible - wait on event
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits without a timeout (and is interruptable) for a
+ * given block event
+ */
+int sst_wait_interruptible(struct intel_sst_drv *sst_drv_ctx,
+                               struct sst_block *block)
+{
+       int retval = 0;
+
+       if (!wait_event_interruptible(sst_drv_ctx->wait_queue,
+                               block->condition)) {
+               /* event wake */
+               if (block->ret_code < 0) {
+                       dev_err(sst_drv_ctx->dev,
+                               "stream failed %d\n", block->ret_code);
+                       retval = -EBUSY;
+               } else {
+                       dev_dbg(sst_drv_ctx->dev, "event up\n");
+                       retval = 0;
+               }
+       } else {
+               dev_err(sst_drv_ctx->dev, "signal interrupted\n");
+               retval = -EINTR;
+       }
+       return retval;
+
+}
+
+unsigned long long read_shim_data(struct intel_sst_drv *sst, int addr)
+{
+       unsigned long long val = 0;
+
+       switch (sst->dev_id) {
+       case SST_MRFLD_PCI_ID:
+       case SST_BYT_ACPI_ID:
+               val = sst_shim_read64(sst->shim, addr);
+               break;
+       }
+       return val;
+}
+
+void write_shim_data(struct intel_sst_drv *sst, int addr,
+                               unsigned long long data)
+{
+       switch (sst->dev_id) {
+       case SST_MRFLD_PCI_ID:
+       case SST_BYT_ACPI_ID:
+               sst_shim_write64(sst->shim, addr, (u64) data);
+               break;
+       }
+}
+
+/*
+ * sst_wait_timeout - wait on event for timeout
+ *
+ * @sst_drv_ctx: Driver context
+ * @block: Driver block to wait on
+ *
+ * This function waits with a timeout value (and is not interruptible) on a
+ * given block event
+ */
+int sst_wait_timeout(struct intel_sst_drv *sst_drv_ctx, struct sst_block *block)
+{
+       int retval = 0;
+
+       /*
+        * NOTE:
+        * Observed that FW processes the alloc msg and replies even
+        * before the alloc thread has finished execution
+        */
+       dev_dbg(sst_drv_ctx->dev,
+               "waiting for condition %x ipc %d drv_id %d\n",
+               block->condition, block->msg_id, block->drv_id);
+       if (wait_event_timeout(sst_drv_ctx->wait_queue,
+                               block->condition,
+                               msecs_to_jiffies(SST_BLOCK_TIMEOUT))) {
+               /* event wake */
+               dev_dbg(sst_drv_ctx->dev, "Event wake %x\n",
+                               block->condition);
+               dev_dbg(sst_drv_ctx->dev, "message ret: %d\n",
+                               block->ret_code);
+               retval = -block->ret_code;
+       } else {
+               block->on = false;
+               dev_err(sst_drv_ctx->dev,
+                       "Wait timed-out condition:%#x, msg_id:%#x fw_state %#x\n",
+                       block->condition, block->msg_id, sst_drv_ctx->sst_state);
+               sst_drv_ctx->sst_state = SST_RESET;
+
+               retval = -EBUSY;
+       }
+       return retval;
+}
+
+/*
+ * sst_create_ipc_msg - create a IPC message
+ *
+ * @arg: ipc message
+ * @large: large or short message
+ *
+ * this function allocates structures to send a large or short
+ * message to the firmware
+ */
+int sst_create_ipc_msg(struct ipc_post **arg, bool large)
+{
+       struct ipc_post *msg;
+
+       msg = kzalloc(sizeof(struct ipc_post), GFP_ATOMIC);
+       if (!msg)
+               return -ENOMEM;
+       if (large) {
+               msg->mailbox_data = kzalloc(SST_MAILBOX_SIZE, GFP_ATOMIC);
+               if (!msg->mailbox_data) {
+                       kfree(msg);
+                       return -ENOMEM;
+               }
+       } else {
+               msg->mailbox_data = NULL;
+       }
+       msg->is_large = large;
+       *arg = msg;
+       return 0;
+}
+
+/*
+ * sst_create_block_and_ipc_msg - Creates IPC message and sst block
+ * @arg: passed to sst_create_ipc_message API
+ * @large: large or short message
+ * @sst_drv_ctx: sst driver context
+ * @block: return block allocated
+ * @msg_id: IPC
+ * @drv_id: stream id or private id
+ */
+int sst_create_block_and_ipc_msg(struct ipc_post **arg, bool large,
+               struct intel_sst_drv *sst_drv_ctx, struct sst_block **block,
+               u32 msg_id, u32 drv_id)
+{
+       int retval = 0;
+
+       retval = sst_create_ipc_msg(arg, large);
+       if (retval)
+               return retval;
+       *block = sst_create_block(sst_drv_ctx, msg_id, drv_id);
+       if (*block == NULL) {
+               kfree(*arg);
+               return -ENOMEM;
+       }
+       return retval;
+}
+
+/*
+ * sst_clean_stream - clean the stream context
+ *
+ * @stream: stream structure
+ *
+ * this function resets the stream contexts
+ * should be called in free
+ */
+void sst_clean_stream(struct stream_info *stream)
+{
+       stream->status = STREAM_UN_INIT;
+       stream->prev = STREAM_UN_INIT;
+       mutex_lock(&stream->lock);
+       stream->cumm_bytes = 0;
+       mutex_unlock(&stream->lock);
+}
+
+int sst_prepare_and_post_msg(struct intel_sst_drv *sst,
+               int task_id, int ipc_msg, int cmd_id, int pipe_id,
+               size_t mbox_data_len, const void *mbox_data, void **data,
+               bool large, bool fill_dsp, bool sync, bool response)
+{
+       struct ipc_post *msg = NULL;
+       struct ipc_dsp_hdr dsp_hdr;
+       struct sst_block *block;
+       int ret = 0, pvt_id;
+
+       pvt_id = sst_assign_pvt_id(sst);
+       if (pvt_id < 0)
+               return pvt_id;
+
+       if (response)
+               ret = sst_create_block_and_ipc_msg(
+                               &msg, large, sst, &block, ipc_msg, pvt_id);
+       else
+               ret = sst_create_ipc_msg(&msg, large);
+
+       if (ret < 0) {
+               test_and_clear_bit(pvt_id, &sst->pvt_id);
+               return -ENOMEM;
+       }
+
+       dev_dbg(sst->dev, "pvt_id = %d, pipe id = %d, task = %d ipc_msg: %d\n",
+                pvt_id, pipe_id, task_id, ipc_msg);
+       sst_fill_header_mrfld(&msg->mrfld_header, ipc_msg,
+                                       task_id, large, pvt_id);
+       msg->mrfld_header.p.header_low_payload = sizeof(dsp_hdr) + mbox_data_len;
+       msg->mrfld_header.p.header_high.part.res_rqd = !sync;
+       dev_dbg(sst->dev, "header:%x\n",
+                       msg->mrfld_header.p.header_high.full);
+       dev_dbg(sst->dev, "response rqd: %x",
+                       msg->mrfld_header.p.header_high.part.res_rqd);
+       dev_dbg(sst->dev, "msg->mrfld_header.p.header_low_payload:%d",
+                       msg->mrfld_header.p.header_low_payload);
+       if (fill_dsp) {
+               sst_fill_header_dsp(&dsp_hdr, cmd_id, pipe_id, mbox_data_len);
+               memcpy(msg->mailbox_data, &dsp_hdr, sizeof(dsp_hdr));
+               if (mbox_data_len) {
+                       memcpy(msg->mailbox_data + sizeof(dsp_hdr),
+                                       mbox_data, mbox_data_len);
+               }
+       }
+
+       if (sync)
+               sst->ops->post_message(sst, msg, true);
+       else
+               sst_add_to_dispatch_list_and_post(sst, msg);
+
+       if (response) {
+               ret = sst_wait_timeout(sst, block);
+               if (ret < 0) {
+                       goto out;
+               } else if(block->data) {
+                       if (!data)
+                               goto out;
+                       *data = kzalloc(block->size, GFP_KERNEL);
+                       if (!(*data)) {
+                               ret = -ENOMEM;
+                               goto out;
+                       } else
+                               memcpy(data, (void *) block->data, block->size);
+               }
+       }
+out:
+       if (response)
+               sst_free_block(sst, block);
+       test_and_clear_bit(pvt_id, &sst->pvt_id);
+       return ret;
+}
+
+int sst_pm_runtime_put(struct intel_sst_drv *sst_drv)
+{
+       int ret;
+
+       pm_runtime_mark_last_busy(sst_drv->dev);
+       ret = pm_runtime_put_autosuspend(sst_drv->dev);
+       if (ret < 0)
+               return ret;
+       return 0;
+}
+
+void sst_fill_header_mrfld(union ipc_header_mrfld *header,
+                               int msg, int task_id, int large, int drv_id)
+{
+       header->full = 0;
+       header->p.header_high.part.msg_id = msg;
+       header->p.header_high.part.task_id = task_id;
+       header->p.header_high.part.large = large;
+       header->p.header_high.part.drv_id = drv_id;
+       header->p.header_high.part.done = 0;
+       header->p.header_high.part.busy = 1;
+       header->p.header_high.part.res_rqd = 1;
+}
+
+void sst_fill_header_dsp(struct ipc_dsp_hdr *dsp, int msg,
+                                       int pipe_id, int len)
+{
+       dsp->cmd_id = msg;
+       dsp->mod_index_id = 0xff;
+       dsp->pipe_id = pipe_id;
+       dsp->length = len;
+       dsp->mod_id = 0;
+}
+
+#define SST_MAX_BLOCKS 15
+/*
+ * sst_assign_pvt_id - assign a pvt id for stream
+ *
+ * @sst_drv_ctx : driver context
+ *
+ * this function assigns a private id for calls that dont have stream
+ * context yet, should be called with lock held
+ * uses bits for the id, and finds first free bits and assigns that
+ */
+int sst_assign_pvt_id(struct intel_sst_drv *drv)
+{
+       int local;
+
+       spin_lock(&drv->block_lock);
+       /* find first zero index from lsb */
+       local = ffz(drv->pvt_id);
+       dev_dbg(drv->dev, "pvt_id assigned --> %d\n", local);
+       if (local >= SST_MAX_BLOCKS){
+               spin_unlock(&drv->block_lock);
+               dev_err(drv->dev, "PVT _ID error: no free id blocks ");
+               return -EINVAL;
+       }
+       /* toggle the index */
+       change_bit(local, &drv->pvt_id);
+       spin_unlock(&drv->block_lock);
+       return local;
+}
+
+void sst_init_stream(struct stream_info *stream,
+               int codec, int sst_id, int ops, u8 slot)
+{
+       stream->status = STREAM_INIT;
+       stream->prev = STREAM_UN_INIT;
+       stream->ops = ops;
+}
+
+int sst_validate_strid(
+               struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       if (str_id <= 0 || str_id > sst_drv_ctx->info.max_streams) {
+               dev_err(sst_drv_ctx->dev,
+                       "SST ERR: invalid stream id : %d, max %d\n",
+                       str_id, sst_drv_ctx->info.max_streams);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+struct stream_info *get_stream_info(
+               struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       if (sst_validate_strid(sst_drv_ctx, str_id))
+               return NULL;
+       return &sst_drv_ctx->streams[str_id];
+}
+
+int get_stream_id_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               u32 pipe_id)
+{
+       int i;
+
+       for (i = 1; i <= sst_drv_ctx->info.max_streams; i++)
+               if (pipe_id == sst_drv_ctx->streams[i].pipe_id)
+                       return i;
+
+       dev_dbg(sst_drv_ctx->dev, "no such pipe_id(%u)", pipe_id);
+       return -1;
+}
+
+u32 relocate_imr_addr_mrfld(u32 base_addr)
+{
+       /* Get the difference from 512MB aligned base addr */
+       /* relocate the base */
+       base_addr = MRFLD_FW_VIRTUAL_BASE + (base_addr % (512 * 1024 * 1024));
+       return base_addr;
+}
+EXPORT_SYMBOL_GPL(relocate_imr_addr_mrfld);
+
+void sst_add_to_dispatch_list_and_post(struct intel_sst_drv *sst,
+                                               struct ipc_post *msg)
+{
+       unsigned long irq_flags;
+
+       spin_lock_irqsave(&sst->ipc_spin_lock, irq_flags);
+       list_add_tail(&msg->node, &sst->ipc_dispatch_list);
+       spin_unlock_irqrestore(&sst->ipc_spin_lock, irq_flags);
+       sst->ops->post_message(sst, NULL, false);
+}
diff --git a/sound/soc/intel/sst/sst_stream.c b/sound/soc/intel/sst/sst_stream.c
new file mode 100644 (file)
index 0000000..dae2a41
--- /dev/null
@@ -0,0 +1,437 @@
+/*
+ *  sst_stream.c - Intel SST Driver for audio engine
+ *
+ *  Copyright (C) 2008-14 Intel Corp
+ *  Authors:   Vinod Koul <vinod.koul@intel.com>
+ *             Harsha Priya <priya.harsha@intel.com>
+ *             Dharageswari R <dharageswari.r@intel.com>
+ *             KP Jeeja <jeeja.kp@intel.com>
+ *  ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ *
+ *  This program is free software; you can redistribute it and/or modify
+ *  it under the terms of the GNU General Public License as published by
+ *  the Free Software Foundation; version 2 of the License.
+ *
+ *  This program is distributed in the hope that it will be useful, but
+ *  WITHOUT ANY WARRANTY; without even the implied warranty of
+ *  MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ *  General Public License for more details.
+ *
+ * ~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~~
+ */
+#include <linux/pci.h>
+#include <linux/firmware.h>
+#include <linux/sched.h>
+#include <linux/delay.h>
+#include <linux/pm_runtime.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/compress_driver.h>
+#include <asm/platform_sst_audio.h>
+#include "../sst-mfld-platform.h"
+#include "sst.h"
+#include "../sst-dsp.h"
+
+int sst_alloc_stream_mrfld(struct intel_sst_drv *sst_drv_ctx, void *params)
+{
+       struct snd_sst_alloc_mrfld alloc_param;
+       struct snd_sst_params *str_params;
+       struct snd_sst_tstamp fw_tstamp;
+       struct stream_info *str_info;
+       struct snd_sst_alloc_response *response;
+       unsigned int str_id, pipe_id, task_id;
+       int i, num_ch, ret = 0;
+       void *data = NULL;
+
+       dev_dbg(sst_drv_ctx->dev, "Enter\n");
+       BUG_ON(!params);
+
+       str_params = (struct snd_sst_params *)params;
+       memset(&alloc_param, 0, sizeof(alloc_param));
+       alloc_param.operation = str_params->ops;
+       alloc_param.codec_type = str_params->codec;
+       alloc_param.sg_count = str_params->aparams.sg_count;
+       alloc_param.ring_buf_info[0].addr =
+               str_params->aparams.ring_buf_info[0].addr;
+       alloc_param.ring_buf_info[0].size =
+               str_params->aparams.ring_buf_info[0].size;
+       alloc_param.frag_size = str_params->aparams.frag_size;
+
+       memcpy(&alloc_param.codec_params, &str_params->sparams,
+                       sizeof(struct snd_sst_stream_params));
+
+       /*
+        * fill channel map params for multichannel support.
+        * Ideally channel map should be received from upper layers
+        * for multichannel support.
+        * Currently hardcoding as per FW reqm.
+        */
+       num_ch = sst_get_num_channel(str_params);
+       for (i = 0; i < 8; i++) {
+               if (i < num_ch)
+                       alloc_param.codec_params.uc.pcm_params.channel_map[i] = i;
+               else
+                       alloc_param.codec_params.uc.pcm_params.channel_map[i] = 0xFF;
+       }
+
+       str_id = str_params->stream_id;
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (str_info == NULL) {
+               dev_err(sst_drv_ctx->dev, "get stream info returned null\n");
+               return -EINVAL;
+       }
+
+       pipe_id = str_params->device_type;
+       task_id = str_params->task;
+       sst_drv_ctx->streams[str_id].pipe_id = pipe_id;
+       sst_drv_ctx->streams[str_id].task_id = task_id;
+       sst_drv_ctx->streams[str_id].num_ch = num_ch;
+
+       if (sst_drv_ctx->info.lpe_viewpt_rqd)
+               alloc_param.ts = sst_drv_ctx->info.mailbox_start +
+                       sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+       else
+               alloc_param.ts = sst_drv_ctx->mailbox_add +
+                       sst_drv_ctx->tstamp + (str_id * sizeof(fw_tstamp));
+
+       dev_dbg(sst_drv_ctx->dev, "alloc tstamp location = 0x%x\n",
+                       alloc_param.ts);
+       dev_dbg(sst_drv_ctx->dev, "assigned pipe id 0x%x to task %d\n",
+                       pipe_id, task_id);
+
+       /* allocate device type context */
+       sst_init_stream(&sst_drv_ctx->streams[str_id], alloc_param.codec_type,
+                       str_id, alloc_param.operation, 0);
+
+       dev_info(sst_drv_ctx->dev, "Alloc for str %d pipe %#x\n",
+                       str_id, pipe_id);
+       ret = sst_prepare_and_post_msg(sst_drv_ctx, task_id, IPC_CMD,
+                       IPC_IA_ALLOC_STREAM_MRFLD, pipe_id, sizeof(alloc_param),
+                       &alloc_param, data, true, true, false, true);
+
+       if (ret < 0) {
+               dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+               /* alloc failed, so reset the state to uninit */
+               str_info->status = STREAM_UN_INIT;
+               str_id = ret;
+       } else if (data) {
+               response = (struct snd_sst_alloc_response *)data;
+               ret = response->str_type.result;
+               if (!ret)
+                       goto out;
+               dev_err(sst_drv_ctx->dev, "FW alloc failed ret %d\n", ret);
+               if (ret == SST_ERR_STREAM_IN_USE) {
+                       dev_err(sst_drv_ctx->dev,
+                               "FW not in clean state, send free for:%d\n", str_id);
+                       sst_free_stream(sst_drv_ctx, str_id);
+               }
+               str_id = -ret;
+       }
+out:
+       kfree(data);
+       return str_id;
+}
+
+/**
+* sst_start_stream - Send msg for a starting stream
+* @str_id:      stream ID
+*
+* This function is called by any function which wants to start
+* a stream.
+*/
+int sst_start_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+       u16 data = 0;
+
+       dev_dbg(sst_drv_ctx->dev, "sst_start_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       if (str_info->status != STREAM_RUNNING)
+               return -EBADRQC;
+
+       retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+                       IPC_CMD, IPC_IA_START_STREAM_MRFLD, str_info->pipe_id,
+                       sizeof(u16), &data, NULL, true, true, true, false);
+
+       return retval;
+}
+
+int sst_send_byte_stream_mrfld(struct intel_sst_drv *sst_drv_ctx,
+               struct snd_sst_bytes_v2 *bytes)
+{      struct ipc_post *msg = NULL;
+       u32 length;
+       int pvt_id, ret = 0;
+       struct sst_block *block = NULL;
+
+       dev_dbg(sst_drv_ctx->dev,
+               "type:%u ipc_msg:%u block:%u task_id:%u pipe: %#x length:%#x\n",
+               bytes->type, bytes->ipc_msg, bytes->block, bytes->task_id,
+               bytes->pipe_id, bytes->len);
+
+       if (sst_create_ipc_msg(&msg, true))
+               return -ENOMEM;
+
+       pvt_id = sst_assign_pvt_id(sst_drv_ctx);
+       sst_fill_header_mrfld(&msg->mrfld_header, bytes->ipc_msg,
+                       bytes->task_id, 1, pvt_id);
+       msg->mrfld_header.p.header_high.part.res_rqd = bytes->block;
+       length = bytes->len;
+       msg->mrfld_header.p.header_low_payload = length;
+       dev_dbg(sst_drv_ctx->dev, "length is %d\n", length);
+       memcpy(msg->mailbox_data, &bytes->bytes, bytes->len);
+       if (bytes->block) {
+               block = sst_create_block(sst_drv_ctx, bytes->ipc_msg, pvt_id);
+               if (block == NULL) {
+                       kfree(msg);
+                       ret = -ENOMEM;
+                       goto out;
+               }
+       }
+
+       sst_add_to_dispatch_list_and_post(sst_drv_ctx, msg);
+       dev_dbg(sst_drv_ctx->dev, "msg->mrfld_header.p.header_low_payload:%d",
+                       msg->mrfld_header.p.header_low_payload);
+
+       if (bytes->block) {
+               ret = sst_wait_timeout(sst_drv_ctx, block);
+               if (ret) {
+                       dev_err(sst_drv_ctx->dev, "fw returned err %d\n", ret);
+                       sst_free_block(sst_drv_ctx, block);
+                       goto out;
+               }
+       }
+       if (bytes->type == SND_SST_BYTES_GET) {
+               /*
+                * copy the reply and send back
+                * we need to update only sz and payload
+                */
+               if (bytes->block) {
+                       unsigned char *r = block->data;
+
+                       dev_dbg(sst_drv_ctx->dev, "read back %d bytes",
+                                       bytes->len);
+                       memcpy(bytes->bytes, r, bytes->len);
+               }
+       }
+       if (bytes->block)
+               sst_free_block(sst_drv_ctx, block);
+out:
+       test_and_clear_bit(pvt_id, &sst_drv_ctx->pvt_id);
+       return 0;
+}
+
+/*
+ * sst_pause_stream - Send msg for a pausing stream
+ * @str_id:     stream ID
+ *
+ * This function is called by any function which wants to pause
+ * an already running stream.
+ */
+int sst_pause_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_pause_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       if (str_info->status == STREAM_PAUSED)
+               return 0;
+       if (str_info->status == STREAM_RUNNING ||
+               str_info->status == STREAM_INIT) {
+               if (str_info->prev == STREAM_UN_INIT)
+                       return -EBADRQC;
+
+               retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+                               IPC_IA_PAUSE_STREAM_MRFLD, str_info->pipe_id,
+                               0, NULL, NULL, true, true, false, true);
+
+               if (retval == 0) {
+                       str_info->prev = str_info->status;
+                       str_info->status = STREAM_PAUSED;
+               } else if (retval == SST_ERR_INVALID_STREAM_ID) {
+                       retval = -EINVAL;
+                       mutex_lock(&sst_drv_ctx->sst_lock);
+                       sst_clean_stream(str_info);
+                       mutex_unlock(&sst_drv_ctx->sst_lock);
+               }
+       } else {
+               retval = -EBADRQC;
+               dev_dbg(sst_drv_ctx->dev, "SST DBG:BADRQC for stream\n ");
+       }
+
+       return retval;
+}
+
+/**
+ * sst_resume_stream - Send msg for resuming stream
+ * @str_id:            stream ID
+ *
+ * This function is called by any function which wants to resume
+ * an already paused stream.
+ */
+int sst_resume_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_resume_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       if (str_info->status == STREAM_RUNNING)
+                       return 0;
+       if (str_info->status == STREAM_PAUSED) {
+               retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+                               IPC_CMD, IPC_IA_RESUME_STREAM_MRFLD,
+                               str_info->pipe_id, 0, NULL, NULL,
+                               true, true, false, true);
+
+               if (!retval) {
+                       if (str_info->prev == STREAM_RUNNING)
+                               str_info->status = STREAM_RUNNING;
+                       else
+                               str_info->status = STREAM_INIT;
+                       str_info->prev = STREAM_PAUSED;
+               } else if (retval == -SST_ERR_INVALID_STREAM_ID) {
+                       retval = -EINVAL;
+                       mutex_lock(&sst_drv_ctx->sst_lock);
+                       sst_clean_stream(str_info);
+                       mutex_unlock(&sst_drv_ctx->sst_lock);
+               }
+       } else {
+               retval = -EBADRQC;
+               dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream\n");
+       }
+
+       return retval;
+}
+
+
+/**
+ * sst_drop_stream - Send msg for stopping stream
+ * @str_id:            stream ID
+ *
+ * This function is called by any function which wants to stop
+ * a stream.
+ */
+int sst_drop_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drop_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+
+       if (str_info->status != STREAM_UN_INIT) {
+               str_info->prev = STREAM_UN_INIT;
+               str_info->status = STREAM_INIT;
+               str_info->cumm_bytes = 0;
+               retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id,
+                               IPC_CMD, IPC_IA_DROP_STREAM_MRFLD,
+                               str_info->pipe_id, 0, NULL, NULL,
+                               true, true, true, false);
+       } else {
+               retval = -EBADRQC;
+               dev_dbg(sst_drv_ctx->dev, "BADQRC for stream, state %x\n",
+                               str_info->status);
+       }
+       return retval;
+}
+
+/**
+* sst_drain_stream - Send msg for draining stream
+* @str_id:             stream ID
+*
+* This function is called by any function which wants to drain
+* a stream.
+*/
+int sst_drain_stream(struct intel_sst_drv *sst_drv_ctx,
+                       int str_id, bool partial_drain)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_drain_stream for %d\n", str_id);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       if (str_info->status != STREAM_RUNNING &&
+               str_info->status != STREAM_INIT &&
+               str_info->status != STREAM_PAUSED) {
+                       dev_err(sst_drv_ctx->dev, "SST ERR: BADQRC for stream = %d\n",
+                                      str_info->status);
+                       return -EBADRQC;
+       }
+
+       retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+                       IPC_IA_DRAIN_STREAM_MRFLD, str_info->pipe_id,
+                       sizeof(u8), &partial_drain, NULL, true, true, false, false);
+       /*
+        * with new non blocked drain implementation in core we dont need to
+        * wait for respsonse, and need to only invoke callback for drain
+        * complete
+        */
+
+       return retval;
+}
+
+/**
+ * sst_free_stream - Frees a stream
+ * @str_id:            stream ID
+ *
+ * This function is called by any function which wants to free
+ * a stream.
+ */
+int sst_free_stream(struct intel_sst_drv *sst_drv_ctx, int str_id)
+{
+       int retval = 0;
+       struct stream_info *str_info;
+       struct intel_sst_ops *ops;
+
+       dev_dbg(sst_drv_ctx->dev, "SST DBG:sst_free_stream for %d\n", str_id);
+
+       mutex_lock(&sst_drv_ctx->sst_lock);
+       if (sst_drv_ctx->sst_state == SST_RESET) {
+               mutex_unlock(&sst_drv_ctx->sst_lock);
+               return -ENODEV;
+       }
+       mutex_unlock(&sst_drv_ctx->sst_lock);
+       str_info = get_stream_info(sst_drv_ctx, str_id);
+       if (!str_info)
+               return -EINVAL;
+       ops = sst_drv_ctx->ops;
+
+       mutex_lock(&str_info->lock);
+       if (str_info->status != STREAM_UN_INIT) {
+               str_info->prev =  str_info->status;
+               str_info->status = STREAM_UN_INIT;
+               mutex_unlock(&str_info->lock);
+
+               dev_info(sst_drv_ctx->dev, "Free for str %d pipe %#x\n",
+                               str_id, str_info->pipe_id);
+               retval = sst_prepare_and_post_msg(sst_drv_ctx, str_info->task_id, IPC_CMD,
+                               IPC_IA_FREE_STREAM_MRFLD, str_info->pipe_id, 0,
+                               NULL, NULL, true, true, false, true);
+
+               dev_dbg(sst_drv_ctx->dev, "sst: wait for free returned %d\n",
+                               retval);
+               mutex_lock(&sst_drv_ctx->sst_lock);
+               sst_clean_stream(str_info);
+               mutex_unlock(&sst_drv_ctx->sst_lock);
+               dev_dbg(sst_drv_ctx->dev, "SST DBG:Stream freed\n");
+       } else {
+               mutex_unlock(&str_info->lock);
+               retval = -EBADRQC;
+               dev_dbg(sst_drv_ctx->dev, "SST DBG:BADQRC for stream\n");
+       }
+
+       return retval;
+}
index 5cb91f9..0fb7d2a 100644 (file)
@@ -77,25 +77,18 @@ static int qi_lb60_probe(struct platform_device *pdev)
 {
        struct qi_lb60 *qi_lb60;
        struct snd_soc_card *card = &qi_lb60_card;
-       int ret;
 
        qi_lb60 = devm_kzalloc(&pdev->dev, sizeof(*qi_lb60), GFP_KERNEL);
        if (!qi_lb60)
                return -ENOMEM;
 
-       qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd");
+       qi_lb60->snd_gpio = devm_gpiod_get(&pdev->dev, "snd", GPIOD_OUT_LOW);
        if (IS_ERR(qi_lb60->snd_gpio))
                return PTR_ERR(qi_lb60->snd_gpio);
-       ret = gpiod_direction_output(qi_lb60->snd_gpio, 0);
-       if (ret)
-               return ret;
 
-       qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp");
+       qi_lb60->amp_gpio = devm_gpiod_get(&pdev->dev, "amp", GPIOD_OUT_LOW);
        if (IS_ERR(qi_lb60->amp_gpio))
                return PTR_ERR(qi_lb60->amp_gpio);
-       ret = gpiod_direction_output(qi_lb60->amp_gpio, 0);
-       if (ret)
-               return ret;
 
        card->dev = &pdev->dev;
 
index 231d7e7..83b2fea 100644 (file)
@@ -773,7 +773,7 @@ static int mxs_saif_probe(struct platform_device *pdev)
 
        saif->dev = &pdev->dev;
        ret = devm_request_irq(&pdev->dev, saif->irq, mxs_saif_irq, 0,
-                              "mxs-saif", saif);
+                              dev_name(&pdev->dev), saif);
        if (ret) {
                dev_err(&pdev->dev, "failed to request irq\n");
                return ret;
index 61822cc..3bba6cf 100644 (file)
@@ -49,13 +49,6 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
                break;
        }
 
-       /* Sgtl5000 sysclk should be >= 8MHz and <= 27M */
-       if (mclk < 8000000 || mclk > 27000000) {
-               dev_err(codec_dai->dev, "Invalid mclk frequency: %u.%03uMHz\n",
-                       mclk / 1000000, mclk / 1000 % 1000);
-               return -EINVAL;
-       }
-
        /* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */
        ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0);
        if (ret) {
index f2f6794..dff443e 100644 (file)
@@ -298,7 +298,7 @@ static const struct snd_soc_dai_ops nuc900_ac97_dai_ops = {
 static struct snd_soc_dai_driver nuc900_ac97_dai = {
        .probe                  = nuc900_ac97_probe,
        .remove                 = nuc900_ac97_remove,
-       .ac97_control           = 1,
+       .bus_control            = true,
        .playback = {
                .rates          = SNDRV_PCM_RATE_8000_48000,
                .formats        = SNDRV_PCM_FMTBIT_S16_LE,
index d44463a..2738b19 100644 (file)
@@ -25,15 +25,15 @@ config SND_OMAP_SOC_N810
          Say Y if you want to add support for SoC audio on Nokia N810.
 
 config SND_OMAP_SOC_RX51
-       tristate "SoC Audio support for Nokia RX-51"
-       depends on SND_OMAP_SOC && ARM && (MACH_NOKIA_RX51 || COMPILE_TEST) && I2C
+       tristate "SoC Audio support for Nokia N900 (RX-51)"
+       depends on SND_OMAP_SOC && ARM && I2C
        select SND_OMAP_SOC_MCBSP
        select SND_SOC_TLV320AIC3X
        select SND_SOC_TPA6130A2
        depends on GPIOLIB
        help
-         Say Y if you want to add support for SoC audio on Nokia RX-51
-         hardware. This is also known as Nokia N900 product.
+         Say Y if you want to add support for SoC audio on Nokia N900
+         cellphone.
 
 config SND_OMAP_SOC_AMS_DELTA
        tristate "SoC Audio support for Amstrad E3 (Delta) videophone"
index 86c7538..68a1252 100644 (file)
@@ -621,8 +621,7 @@ void omap_mcbsp_free(struct omap_mcbsp *mcbsp)
        mcbsp->reg_cache = NULL;
        spin_unlock(&mcbsp->lock);
 
-       if (reg_cache)
-               kfree(reg_cache);
+       kfree(reg_cache);
 }
 
 /*
index 595eee3..a6b2be2 100644 (file)
@@ -127,15 +127,12 @@ static const struct snd_soc_dapm_route audio_map[] = {
 static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_codec *codec = rtd->codec;
-       unsigned short reg;
 
        /* Prepare GPIO8 for rear speaker amplifier */
-       reg = codec->driver->read(codec, AC97_GPIO_CFG);
-       codec->driver->write(codec, AC97_GPIO_CFG, reg | 0x0100);
+       snd_soc_update_bits(codec, AC97_GPIO_CFG, 0x100, 0x100);
 
        /* Prepare MIC input */
-       reg = codec->driver->read(codec, AC97_3D_CONTROL);
-       codec->driver->write(codec, AC97_3D_CONTROL, reg | 0xc000);
+       snd_soc_update_bits(codec, AC97_3D_CONTROL, 0xc000, 0xc000);
 
        return 0;
 }
index a8e0974..cbba063 100644 (file)
@@ -97,7 +97,7 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream,
        int ret = 0;
 
        if (!cpu_dai->active) {
-               clk_enable(ssp->clk);
+               clk_prepare_enable(ssp->clk);
                pxa_ssp_disable(ssp);
        }
 
@@ -121,7 +121,7 @@ static void pxa_ssp_shutdown(struct snd_pcm_substream *substream,
 
        if (!cpu_dai->active) {
                pxa_ssp_disable(ssp);
-               clk_disable(ssp->clk);
+               clk_disable_unprepare(ssp->clk);
        }
 
        kfree(snd_soc_dai_get_dma_data(cpu_dai, substream));
@@ -136,7 +136,7 @@ static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
        struct ssp_device *ssp = priv->ssp;
 
        if (!cpu_dai->active)
-               clk_enable(ssp->clk);
+               clk_prepare_enable(ssp->clk);
 
        priv->cr0 = __raw_readl(ssp->mmio_base + SSCR0);
        priv->cr1 = __raw_readl(ssp->mmio_base + SSCR1);
@@ -144,7 +144,7 @@ static int pxa_ssp_suspend(struct snd_soc_dai *cpu_dai)
        priv->psp = __raw_readl(ssp->mmio_base + SSPSP);
 
        pxa_ssp_disable(ssp);
-       clk_disable(ssp->clk);
+       clk_disable_unprepare(ssp->clk);
        return 0;
 }
 
@@ -154,7 +154,7 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
        struct ssp_device *ssp = priv->ssp;
        uint32_t sssr = SSSR_ROR | SSSR_TUR | SSSR_BCE;
 
-       clk_enable(ssp->clk);
+       clk_prepare_enable(ssp->clk);
 
        __raw_writel(sssr, ssp->mmio_base + SSSR);
        __raw_writel(priv->cr0 & ~SSCR0_SSE, ssp->mmio_base + SSCR0);
@@ -165,7 +165,7 @@ static int pxa_ssp_resume(struct snd_soc_dai *cpu_dai)
        if (cpu_dai->active)
                pxa_ssp_enable(ssp);
        else
-               clk_disable(ssp->clk);
+               clk_disable_unprepare(ssp->clk);
 
        return 0;
 }
@@ -256,11 +256,11 @@ static int pxa_ssp_set_dai_sysclk(struct snd_soc_dai *cpu_dai,
        /* The SSP clock must be disabled when changing SSP clock mode
         * on PXA2xx.  On PXA3xx it must be enabled when doing so. */
        if (ssp->type != PXA3xx_SSP)
-               clk_disable(ssp->clk);
+               clk_disable_unprepare(ssp->clk);
        val = pxa_ssp_read_reg(ssp, SSCR0) | sscr0;
        pxa_ssp_write_reg(ssp, SSCR0, val);
        if (ssp->type != PXA3xx_SSP)
-               clk_enable(ssp->clk);
+               clk_prepare_enable(ssp->clk);
 
        return 0;
 }
index ae956e3..73ca282 100644 (file)
@@ -157,7 +157,7 @@ static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
 static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
 {
        .name = "pxa2xx-ac97",
-       .ac97_control = 1,
+       .bus_control = true,
        .playback = {
                .stream_name = "AC97 Playback",
                .channels_min = 2,
@@ -174,7 +174,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
 },
 {
        .name = "pxa2xx-ac97-aux",
-       .ac97_control = 1,
+       .bus_control = true,
        .playback = {
                .stream_name = "AC97 Aux Playback",
                .channels_min = 1,
@@ -191,7 +191,7 @@ static struct snd_soc_dai_driver pxa_ac97_dai_driver[] = {
 },
 {
        .name = "pxa2xx-ac97-mic",
-       .ac97_control = 1,
+       .bus_control = true,
        .capture = {
                .stream_name = "AC97 Mic Capture",
                .channels_min = 1,
index 1373b01..d7d5fb2 100644 (file)
@@ -305,19 +305,15 @@ static struct snd_soc_card snd_soc_spitz = {
        .num_dapm_routes = ARRAY_SIZE(spitz_audio_map),
 };
 
-static struct platform_device *spitz_snd_device;
-
-static int __init spitz_init(void)
+static int spitz_probe(struct platform_device *pdev)
 {
+       struct snd_soc_card *card = &snd_soc_spitz;
        int ret;
 
-       if (!(machine_is_spitz() || machine_is_borzoi() || machine_is_akita()))
-               return -ENODEV;
-
-       if (machine_is_borzoi() || machine_is_spitz())
-               spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS;
-       else
+       if (machine_is_akita())
                spitz_mic_gpio = AKITA_GPIO_MIC_BIAS;
+       else
+               spitz_mic_gpio = SPITZ_GPIO_MIC_BIAS;
 
        ret = gpio_request(spitz_mic_gpio, "MIC GPIO");
        if (ret)
@@ -327,37 +323,45 @@ static int __init spitz_init(void)
        if (ret)
                goto err2;
 
-       spitz_snd_device = platform_device_alloc("soc-audio", -1);
-       if (!spitz_snd_device) {
-               ret = -ENOMEM;
+       card->dev = &pdev->dev;
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
+                       ret);
                goto err2;
        }
 
-       platform_set_drvdata(spitz_snd_device, &snd_soc_spitz);
-
-       ret = platform_device_add(spitz_snd_device);
-       if (ret)
-               goto err3;
-
        return 0;
 
-err3:
-       platform_device_put(spitz_snd_device);
 err2:
        gpio_free(spitz_mic_gpio);
 err1:
        return ret;
 }
 
-static void __exit spitz_exit(void)
+static int spitz_remove(struct platform_device *pdev)
 {
-       platform_device_unregister(spitz_snd_device);
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       snd_soc_unregister_card(card);
        gpio_free(spitz_mic_gpio);
+       return 0;
 }
 
-module_init(spitz_init);
-module_exit(spitz_exit);
+static struct platform_driver spitz_driver = {
+       .driver         = {
+               .name   = "spitz-audio",
+               .owner  = THIS_MODULE,
+               .pm     = &snd_soc_pm_ops,
+       },
+       .probe          = spitz_probe,
+       .remove         = spitz_remove,
+};
+
+module_platform_driver(spitz_driver);
 
 MODULE_AUTHOR("Richard Purdie");
 MODULE_DESCRIPTION("ALSA SoC Spitz");
 MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:spitz-audio");
index 78fc159..e181826 100644 (file)
@@ -1,11 +1,16 @@
 config SND_SOC_ROCKCHIP
        tristate "ASoC support for Rockchip"
        depends on COMPILE_TEST || ARCH_ROCKCHIP
-       select SND_SOC_GENERIC_DMAENGINE_PCM
        help
          Say Y or M if you want to add support for codecs attached to
          the Rockchip SoCs' Audio interfaces. You will also need to
          select the audio interfaces to support below.
 
 config SND_SOC_ROCKCHIP_I2S
-       tristate
+       tristate "Rockchip I2S Device Driver"
+       depends on CLKDEV_LOOKUP && SND_SOC_ROCKCHIP
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+       help
+         Say Y or M if you want to add support for I2S driver for
+         Rockchip I2S device. The device supports upto maximum of
+         8 channels each for play and record.
index 55a3869..fc67f97 100644 (file)
@@ -1,6 +1,6 @@
 config SND_SOC_SAMSUNG
        tristate "ASoC support for Samsung"
-       depends on PLAT_SAMSUNG
+       depends on (PLAT_SAMSUNG || ARCH_EXYNOS)
        depends on S3C64XX_PL080 || !ARCH_S3C64XX
        depends on S3C24XX_DMAC || !ARCH_S3C24XX
        select SND_SOC_GENERIC_DMAENGINE_PCM
@@ -239,3 +239,9 @@ config SND_SOC_ODROIDX2
        select SND_SAMSUNG_I2S
        help
          Say Y here to enable audio support for the Odroid-X2/U3.
+
+config SND_SOC_ARNDALE_RT5631_ALC5631
+        tristate "Audio support for RT5631(ALC5631) on Arndale Board"
+        depends on SND_SOC_SAMSUNG
+        select SND_SAMSUNG_I2S
+        select SND_SOC_RT5631
index 91505dd..31e3dba 100644 (file)
@@ -45,6 +45,7 @@ snd-soc-lowland-objs := lowland.o
 snd-soc-littlemill-objs := littlemill.o
 snd-soc-bells-objs := bells.o
 snd-soc-odroidx2-max98090-objs := odroidx2_max98090.o
+snd-soc-arndale-rt5631-objs := arndale_rt5631.o
 
 obj-$(CONFIG_SND_SOC_SAMSUNG_JIVE_WM8750) += snd-soc-jive-wm8750.o
 obj-$(CONFIG_SND_SOC_SAMSUNG_NEO1973_WM8753) += snd-soc-neo1973-wm8753.o
@@ -71,3 +72,4 @@ obj-$(CONFIG_SND_SOC_LOWLAND) += snd-soc-lowland.o
 obj-$(CONFIG_SND_SOC_LITTLEMILL) += snd-soc-littlemill.o
 obj-$(CONFIG_SND_SOC_BELLS) += snd-soc-bells.o
 obj-$(CONFIG_SND_SOC_ODROIDX2) += snd-soc-odroidx2-max98090.o
+obj-$(CONFIG_SND_SOC_ARNDALE_RT5631_ALC5631) += snd-soc-arndale-rt5631.o
index e161511..7952a62 100644 (file)
@@ -288,7 +288,7 @@ static int s3c_ac97_mic_dai_probe(struct snd_soc_dai *dai)
 static struct snd_soc_dai_driver s3c_ac97_dai[] = {
        [S3C_AC97_DAI_PCM] = {
                .name = "samsung-ac97",
-               .ac97_control = 1,
+               .bus_control = true,
                .playback = {
                        .stream_name = "AC97 Playback",
                        .channels_min = 2,
@@ -306,7 +306,7 @@ static struct snd_soc_dai_driver s3c_ac97_dai[] = {
        },
        [S3C_AC97_DAI_MIC] = {
                .name = "samsung-ac97-mic",
-               .ac97_control = 1,
+               .bus_control = true,
                .capture = {
                        .stream_name = "AC97 Mic Capture",
                        .channels_min = 1,
diff --git a/sound/soc/samsung/arndale_rt5631.c b/sound/soc/samsung/arndale_rt5631.c
new file mode 100644 (file)
index 0000000..1e2b61c
--- /dev/null
@@ -0,0 +1,150 @@
+/*
+ *  arndale_rt5631.c
+ *
+ *  Copyright (c) 2014, Insignal Co., Ltd.
+ *
+ *  Author: Claude <claude@insginal.co.kr>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/clk.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+
+#include "i2s.h"
+
+static int arndale_hw_params(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       int rfs, ret;
+       unsigned long rclk;
+
+       rfs = 256;
+
+       rclk = params_rate(params) * rfs;
+
+       ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_CDCLK,
+                                       0, SND_SOC_CLOCK_OUT);
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(cpu_dai, SAMSUNG_I2S_RCLKSRC_0,
+                                       0, SND_SOC_CLOCK_OUT);
+
+       if (ret < 0)
+               return ret;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, 0, rclk, SND_SOC_CLOCK_OUT);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static struct snd_soc_ops arndale_ops = {
+       .hw_params = arndale_hw_params,
+};
+
+static struct snd_soc_dai_link arndale_rt5631_dai[] = {
+       {
+               .name = "RT5631 HiFi",
+               .stream_name = "Primary",
+               .codec_dai_name = "rt5631-hifi",
+               .dai_fmt = SND_SOC_DAIFMT_I2S
+                       | SND_SOC_DAIFMT_NB_NF
+                       | SND_SOC_DAIFMT_CBS_CFS,
+               .ops = &arndale_ops,
+       },
+};
+
+static struct snd_soc_card arndale_rt5631 = {
+       .name = "Arndale RT5631",
+       .dai_link = arndale_rt5631_dai,
+       .num_links = ARRAY_SIZE(arndale_rt5631_dai),
+};
+
+static int arndale_audio_probe(struct platform_device *pdev)
+{
+       int n, ret;
+       struct device_node *np = pdev->dev.of_node;
+       struct snd_soc_card *card = &arndale_rt5631;
+
+       card->dev = &pdev->dev;
+
+       for (n = 0; np && n < ARRAY_SIZE(arndale_rt5631_dai); n++) {
+               if (!arndale_rt5631_dai[n].cpu_dai_name) {
+                       arndale_rt5631_dai[n].cpu_of_node = of_parse_phandle(np,
+                                       "samsung,audio-cpu", n);
+
+                       if (!arndale_rt5631_dai[n].cpu_of_node) {
+                               dev_err(&pdev->dev,
+                               "Property 'samsung,audio-cpu' missing or invalid\n");
+                               return -EINVAL;
+                       }
+               }
+               if (!arndale_rt5631_dai[n].platform_name)
+                       arndale_rt5631_dai[n].platform_of_node =
+                                       arndale_rt5631_dai[n].cpu_of_node;
+
+               arndale_rt5631_dai[n].codec_name = NULL;
+               arndale_rt5631_dai[n].codec_of_node = of_parse_phandle(np,
+                                       "samsung,audio-codec", n);
+               if (!arndale_rt5631_dai[0].codec_of_node) {
+                       dev_err(&pdev->dev,
+                       "Property 'samsung,audio-codec' missing or invalid\n");
+                       return -EINVAL;
+               }
+       }
+
+       ret = devm_snd_soc_register_card(card->dev, card);
+
+       if (ret)
+               dev_err(&pdev->dev, "snd_soc_register_card() failed:%d\n", ret);
+
+       return ret;
+}
+
+static int arndale_audio_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+
+       snd_soc_unregister_card(card);
+
+       return 0;
+}
+
+static const struct of_device_id samsung_arndale_rt5631_of_match[] __maybe_unused = {
+       { .compatible = "samsung,arndale-rt5631", },
+       { .compatible = "samsung,arndale-alc5631", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_arndale_rt5631_of_match);
+
+static struct platform_driver arndale_audio_driver = {
+       .driver = {
+               .name   = "arndale-audio",
+               .owner  = THIS_MODULE,
+               .pm = &snd_soc_pm_ops,
+               .of_match_table = of_match_ptr(samsung_arndale_rt5631_of_match),
+       },
+       .probe = arndale_audio_probe,
+       .remove = arndale_audio_remove,
+};
+
+module_platform_driver(arndale_audio_driver);
+
+MODULE_AUTHOR("Claude <claude@insignal.co.kr>");
+MODULE_DESCRIPTION("ALSA SoC Driver for Arndale Board");
+MODULE_LICENSE("GPL");
index 821a502..9170c31 100644 (file)
@@ -33,8 +33,9 @@
 #define I2SLVL3ADDR    0x3c
 #define I2SSTR1                0x40
 #define I2SVER         0x44
-#define I2SFIC2                0x48
+#define I2SFIC1                0x48
 #define I2STDM         0x4c
+#define I2SFSTA                0x50
 
 #define CON_RSTCLR             (1 << 31)
 #define CON_FRXOFSTATUS                (1 << 26)
@@ -93,8 +94,6 @@
 #define MOD_BLC_24BIT          (2 << 13)
 #define MOD_BLC_MASK           (3 << 13)
 
-#define MOD_IMS_SYSMUX         (1 << 10)
-#define MOD_SLAVE              (1 << 11)
 #define MOD_TXONLY             (0 << 8)
 #define MOD_RXONLY             (1 << 8)
 #define MOD_TXRX               (2 << 8)
 #define EXYNOS5420_MOD_BCLK_256FS      8
 #define EXYNOS5420_MOD_BCLK_MASK       0xf
 
-#define MOD_CDCLKCON           (1 << 12)
+#define EXYNOS7_MOD_RCLK_64FS  4
+#define EXYNOS7_MOD_RCLK_128FS 5
+#define EXYNOS7_MOD_RCLK_96FS  6
+#define EXYNOS7_MOD_RCLK_192FS 7
 
 #define PSR_PSREN              (1 << 15)
 
index 7f98ee6..eca17dc 100644 (file)
@@ -36,9 +36,24 @@ enum samsung_dai_type {
        TYPE_SEC,
 };
 
+struct samsung_i2s_variant_regs {
+       unsigned int    bfs_off;
+       unsigned int    rfs_off;
+       unsigned int    sdf_off;
+       unsigned int    txr_off;
+       unsigned int    rclksrc_off;
+       unsigned int    mss_off;
+       unsigned int    cdclkcon_off;
+       unsigned int    lrp_off;
+       unsigned int    bfs_mask;
+       unsigned int    rfs_mask;
+       unsigned int    ftx0cnt_off;
+};
+
 struct samsung_i2s_dai_data {
        int dai_type;
        u32 quirks;
+       const struct samsung_i2s_variant_regs *i2s_variant_regs;
 };
 
 struct i2s_dai {
@@ -81,6 +96,7 @@ struct i2s_dai {
        u32     suspend_i2scon;
        u32     suspend_i2spsr;
        unsigned long gpios[7]; /* i2s gpio line numbers */
+       const struct samsung_i2s_variant_regs *variant_regs;
 };
 
 /* Lock for cross i/f checks */
@@ -95,7 +111,8 @@ static inline bool is_secondary(struct i2s_dai *i2s)
 /* If operating in SoC-Slave mode */
 static inline bool is_slave(struct i2s_dai *i2s)
 {
-       return (readl(i2s->addr + I2SMOD) & MOD_SLAVE) ? true : false;
+       u32 mod = readl(i2s->addr + I2SMOD);
+       return (mod & (1 << i2s->variant_regs->mss_off)) ? true : false;
 }
 
 /* If this interface of the controller is transmitting data */
@@ -200,14 +217,14 @@ static inline bool is_manager(struct i2s_dai *i2s)
 static inline unsigned get_rfs(struct i2s_dai *i2s)
 {
        u32 rfs;
-
-       if (i2s->quirks & QUIRK_SUPPORTS_TDM)
-               rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT;
-       else
-               rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT);
-       rfs &= MOD_RCLK_MASK;
+       rfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->rfs_off;
+       rfs &= i2s->variant_regs->rfs_mask;
 
        switch (rfs) {
+       case 7: return 192;
+       case 6: return 96;
+       case 5: return 128;
+       case 4: return 64;
        case 3: return 768;
        case 2: return 384;
        case 1: return 512;
@@ -219,15 +236,23 @@ static inline unsigned get_rfs(struct i2s_dai *i2s)
 static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
 {
        u32 mod = readl(i2s->addr + I2SMOD);
-       int rfs_shift;
+       int rfs_shift = i2s->variant_regs->rfs_off;
 
-       if (i2s->quirks & QUIRK_SUPPORTS_TDM)
-               rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT;
-       else
-               rfs_shift = MOD_RCLK_SHIFT;
-       mod &= ~(MOD_RCLK_MASK << rfs_shift);
+       mod &= ~(i2s->variant_regs->rfs_mask << rfs_shift);
 
        switch (rfs) {
+       case 192:
+               mod |= (EXYNOS7_MOD_RCLK_192FS << rfs_shift);
+               break;
+       case 96:
+               mod |= (EXYNOS7_MOD_RCLK_96FS << rfs_shift);
+               break;
+       case 128:
+               mod |= (EXYNOS7_MOD_RCLK_128FS << rfs_shift);
+               break;
+       case 64:
+               mod |= (EXYNOS7_MOD_RCLK_64FS << rfs_shift);
+               break;
        case 768:
                mod |= (MOD_RCLK_768FS << rfs_shift);
                break;
@@ -249,14 +274,8 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
 static inline unsigned get_bfs(struct i2s_dai *i2s)
 {
        u32 bfs;
-
-       if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
-               bfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_BCLK_SHIFT;
-               bfs &= EXYNOS5420_MOD_BCLK_MASK;
-       } else {
-               bfs =  readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT;
-               bfs &= MOD_BCLK_MASK;
-       }
+       bfs = readl(i2s->addr + I2SMOD) >> i2s->variant_regs->bfs_off;
+       bfs &= i2s->variant_regs->bfs_mask;
 
        switch (bfs) {
        case 8: return 256;
@@ -275,16 +294,8 @@ static inline unsigned get_bfs(struct i2s_dai *i2s)
 static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
 {
        u32 mod = readl(i2s->addr + I2SMOD);
-       int bfs_shift;
        int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM;
-
-       if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
-               bfs_shift = EXYNOS5420_MOD_BCLK_SHIFT;
-               mod &= ~(EXYNOS5420_MOD_BCLK_MASK << bfs_shift);
-       } else {
-               bfs_shift = MOD_BCLK_SHIFT;
-               mod &= ~(MOD_BCLK_MASK << bfs_shift);
-       }
+       int bfs_shift = i2s->variant_regs->bfs_off;
 
        /* Non-TDM I2S controllers do not support BCLK > 48 * FS */
        if (!tdm && bfs > 48) {
@@ -292,6 +303,8 @@ static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
                return;
        }
 
+       mod &= ~(i2s->variant_regs->bfs_mask << bfs_shift);
+
        switch (bfs) {
        case 48:
                mod |= (MOD_BCLK_48FS << bfs_shift);
@@ -346,8 +359,9 @@ static inline int get_blc(struct i2s_dai *i2s)
 static void i2s_txctrl(struct i2s_dai *i2s, int on)
 {
        void __iomem *addr = i2s->addr;
+       int txr_off = i2s->variant_regs->txr_off;
        u32 con = readl(addr + I2SCON);
-       u32 mod = readl(addr + I2SMOD) & ~MOD_MASK;
+       u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
 
        if (on) {
                con |= CON_ACTIVE;
@@ -362,9 +376,9 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
                }
 
                if (any_rx_active(i2s))
-                       mod |= MOD_TXRX;
+                       mod |= 2 << txr_off;
                else
-                       mod |= MOD_TXONLY;
+                       mod |= 0 << txr_off;
        } else {
                if (is_secondary(i2s)) {
                        con |=  CON_TXSDMA_PAUSE;
@@ -382,7 +396,7 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
                con |=  CON_TXCH_PAUSE;
 
                if (any_rx_active(i2s))
-                       mod |= MOD_RXONLY;
+                       mod |= 1 << txr_off;
                else
                        con &= ~CON_ACTIVE;
        }
@@ -395,23 +409,24 @@ static void i2s_txctrl(struct i2s_dai *i2s, int on)
 static void i2s_rxctrl(struct i2s_dai *i2s, int on)
 {
        void __iomem *addr = i2s->addr;
+       int txr_off = i2s->variant_regs->txr_off;
        u32 con = readl(addr + I2SCON);
-       u32 mod = readl(addr + I2SMOD) & ~MOD_MASK;
+       u32 mod = readl(addr + I2SMOD) & ~(3 << txr_off);
 
        if (on) {
                con |= CON_RXDMA_ACTIVE | CON_ACTIVE;
                con &= ~(CON_RXDMA_PAUSE | CON_RXCH_PAUSE);
 
                if (any_tx_active(i2s))
-                       mod |= MOD_TXRX;
+                       mod |= 2 << txr_off;
                else
-                       mod |= MOD_RXONLY;
+                       mod |= 1 << txr_off;
        } else {
                con |=  CON_RXDMA_PAUSE | CON_RXCH_PAUSE;
                con &= ~CON_RXDMA_ACTIVE;
 
                if (any_tx_active(i2s))
-                       mod |= MOD_TXONLY;
+                       mod |= 0 << txr_off;
                else
                        con &= ~CON_ACTIVE;
        }
@@ -451,6 +466,9 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
        struct i2s_dai *i2s = to_info(dai);
        struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
        u32 mod = readl(i2s->addr + I2SMOD);
+       const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
+       unsigned int cdcon_mask = 1 << i2s_regs->cdclkcon_off;
+       unsigned int rsrc_mask = 1 << i2s_regs->rclksrc_off;
 
        switch (clk_id) {
        case SAMSUNG_I2S_OPCLK:
@@ -465,18 +483,18 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
                if ((rfs && other && other->rfs && (other->rfs != rfs)) ||
                                (any_active(i2s) &&
                                (((dir == SND_SOC_CLOCK_IN)
-                                       && !(mod & MOD_CDCLKCON)) ||
+                                       && !(mod & cdcon_mask)) ||
                                ((dir == SND_SOC_CLOCK_OUT)
-                                       && (mod & MOD_CDCLKCON))))) {
+                                       && (mod & cdcon_mask))))) {
                        dev_err(&i2s->pdev->dev,
                                "%s:%d Other DAI busy\n", __func__, __LINE__);
                        return -EAGAIN;
                }
 
                if (dir == SND_SOC_CLOCK_IN)
-                       mod |= MOD_CDCLKCON;
+                       mod |= 1 << i2s_regs->cdclkcon_off;
                else
-                       mod &= ~MOD_CDCLKCON;
+                       mod &= ~(1 << i2s_regs->cdclkcon_off);
 
                i2s->rfs = rfs;
                break;
@@ -491,8 +509,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
 
                if (!any_active(i2s)) {
                        if (i2s->op_clk && !IS_ERR(i2s->op_clk)) {
-                               if ((clk_id && !(mod & MOD_IMS_SYSMUX)) ||
-                                       (!clk_id && (mod & MOD_IMS_SYSMUX))) {
+                               if ((clk_id && !(mod & rsrc_mask)) ||
+                                       (!clk_id && (mod & rsrc_mask))) {
                                        clk_disable_unprepare(i2s->op_clk);
                                        clk_put(i2s->op_clk);
                                } else {
@@ -520,8 +538,8 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
                                other->op_clk = i2s->op_clk;
                                other->rclk_srcrate = i2s->rclk_srcrate;
                        }
-               } else if ((!clk_id && (mod & MOD_IMS_SYSMUX))
-                               || (clk_id && !(mod & MOD_IMS_SYSMUX))) {
+               } else if ((!clk_id && (mod & rsrc_mask))
+                               || (clk_id && !(mod & rsrc_mask))) {
                        dev_err(&i2s->pdev->dev,
                                "%s:%d Other DAI busy\n", __func__, __LINE__);
                        return -EAGAIN;
@@ -533,11 +551,11 @@ static int i2s_set_sysclk(struct snd_soc_dai *dai,
                }
 
                if (clk_id == 0)
-                       mod &= ~MOD_IMS_SYSMUX;
+                       mod &= ~(1 << i2s_regs->rclksrc_off);
                else
-                       mod |= MOD_IMS_SYSMUX;
-               break;
+                       mod |= 1 << i2s_regs->rclksrc_off;
 
+               break;
        default:
                dev_err(&i2s->pdev->dev, "We don't serve that!\n");
                return -EINVAL;
@@ -553,16 +571,12 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
 {
        struct i2s_dai *i2s = to_info(dai);
        u32 mod = readl(i2s->addr + I2SMOD);
-       int lrp_shift, sdf_shift, sdf_mask, lrp_rlow;
+       int lrp_shift, sdf_shift, sdf_mask, lrp_rlow, mod_slave;
        u32 tmp = 0;
 
-       if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
-               lrp_shift = EXYNOS5420_MOD_LRP_SHIFT;
-               sdf_shift = EXYNOS5420_MOD_SDF_SHIFT;
-       } else {
-               lrp_shift = MOD_LRP_SHIFT;
-               sdf_shift = MOD_SDF_SHIFT;
-       }
+       lrp_shift = i2s->variant_regs->lrp_off;
+       sdf_shift = i2s->variant_regs->sdf_off;
+       mod_slave = 1 << i2s->variant_regs->mss_off;
 
        sdf_mask = MOD_SDF_MASK << sdf_shift;
        lrp_rlow = MOD_LR_RLOW << lrp_shift;
@@ -605,7 +619,7 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
 
        switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
        case SND_SOC_DAIFMT_CBM_CFM:
-               tmp |= MOD_SLAVE;
+               tmp |= mod_slave;
                break;
        case SND_SOC_DAIFMT_CBS_CFS:
                /* Set default source clock in Master mode */
@@ -623,13 +637,13 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
         * channel.
         */
        if (any_active(i2s) &&
-               ((mod & (sdf_mask | lrp_rlow | MOD_SLAVE)) != tmp)) {
+               ((mod & (sdf_mask | lrp_rlow | mod_slave)) != tmp)) {
                dev_err(&i2s->pdev->dev,
                                "%s:%d Other DAI busy\n", __func__, __LINE__);
                return -EAGAIN;
        }
 
-       mod &= ~(sdf_mask | lrp_rlow | MOD_SLAVE);
+       mod &= ~(sdf_mask | lrp_rlow | mod_slave);
        mod |= tmp;
        writel(mod, i2s->addr + I2SMOD);
 
@@ -751,6 +765,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
        struct i2s_dai *i2s = to_info(dai);
        struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
        unsigned long flags;
+       const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
 
        spin_lock_irqsave(&lock, flags);
 
@@ -761,7 +776,7 @@ static void i2s_shutdown(struct snd_pcm_substream *substream,
                other->mode |= DAI_MANAGER;
        } else {
                u32 mod = readl(i2s->addr + I2SMOD);
-               i2s->cdclk_out = !(mod & MOD_CDCLKCON);
+               i2s->cdclk_out = !(mod & (1 << i2s_regs->cdclkcon_off));
                if (other)
                        other->cdclk_out = i2s->cdclk_out;
        }
@@ -914,13 +929,14 @@ i2s_delay(struct snd_pcm_substream *substream, struct snd_soc_dai *dai)
        struct i2s_dai *i2s = to_info(dai);
        u32 reg = readl(i2s->addr + I2SFIC);
        snd_pcm_sframes_t delay;
+       const struct samsung_i2s_variant_regs *i2s_regs = i2s->variant_regs;
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
                delay = FIC_RXCOUNT(reg);
        else if (is_secondary(i2s))
                delay = FICS_TXCOUNT(readl(i2s->addr + I2SFICS));
        else
-               delay = FIC_TXCOUNT(reg);
+               delay = (reg >> i2s_regs->ftx0cnt_off) & 0x7f;
 
        return delay;
 }
@@ -956,6 +972,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
 {
        struct i2s_dai *i2s = to_info(dai);
        struct i2s_dai *other = i2s->pri_dai ? : i2s->sec_dai;
+       int ret;
 
        if (other && other->clk) { /* If this is probe on secondary */
                samsung_asoc_init_dma_data(dai, &other->sec_dai->dma_playback,
@@ -973,9 +990,14 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
        if (IS_ERR(i2s->clk)) {
                dev_err(&i2s->pdev->dev, "failed to get i2s_clock\n");
                iounmap(i2s->addr);
-               return -ENOENT;
+               return PTR_ERR(i2s->clk);
+       }
+
+       ret = clk_prepare_enable(i2s->clk);
+       if (ret != 0) {
+               dev_err(&i2s->pdev->dev, "failed to enable clock: %d\n", ret);
+               return ret;
        }
-       clk_prepare_enable(i2s->clk);
 
        samsung_asoc_init_dma_data(dai, &i2s->dma_playback, &i2s->dma_capture);
 
@@ -987,7 +1009,7 @@ static int samsung_i2s_dai_probe(struct snd_soc_dai *dai)
        if (i2s->quirks & QUIRK_NEED_RSTCLR)
                writel(CON_RSTCLR, i2s->addr + I2SCON);
 
-       if (i2s->quirks & QUIRK_SEC_DAI)
+       if (i2s->quirks & QUIRK_SUPPORTS_IDMA)
                idma_reg_addr_init(i2s->addr,
                                        i2s->sec_dai->idma_playback.dma_addr);
 
@@ -1199,10 +1221,9 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                quirks = i2s_dai_data->quirks;
                if (of_property_read_u32(np, "samsung,idma-addr",
                                         &idma_addr)) {
-                       if (quirks & QUIRK_SEC_DAI) {
-                               dev_err(&pdev->dev, "idma address is not"\
+                       if (quirks & QUIRK_SUPPORTS_IDMA) {
+                               dev_info(&pdev->dev, "idma address is not"\
                                                "specified");
-                               return -EINVAL;
                        }
                }
        }
@@ -1228,6 +1249,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
        pri_dai->dma_capture.dma_size = 4;
        pri_dai->base = regs_base;
        pri_dai->quirks = quirks;
+       pri_dai->variant_regs = i2s_dai_data->i2s_variant_regs;
 
        if (quirks & QUIRK_PRI_6CHAN)
                pri_dai->i2s_dai_drv.playback.channels_max = 6;
@@ -1304,20 +1326,93 @@ static int samsung_i2s_remove(struct platform_device *pdev)
        return 0;
 }
 
+static const struct samsung_i2s_variant_regs i2sv3_regs = {
+       .bfs_off = 1,
+       .rfs_off = 3,
+       .sdf_off = 5,
+       .txr_off = 8,
+       .rclksrc_off = 10,
+       .mss_off = 11,
+       .cdclkcon_off = 12,
+       .lrp_off = 7,
+       .bfs_mask = 0x3,
+       .rfs_mask = 0x3,
+       .ftx0cnt_off = 8,
+};
+
+static const struct samsung_i2s_variant_regs i2sv6_regs = {
+       .bfs_off = 0,
+       .rfs_off = 4,
+       .sdf_off = 6,
+       .txr_off = 8,
+       .rclksrc_off = 10,
+       .mss_off = 11,
+       .cdclkcon_off = 12,
+       .lrp_off = 15,
+       .bfs_mask = 0xf,
+       .rfs_mask = 0x3,
+       .ftx0cnt_off = 8,
+};
+
+static const struct samsung_i2s_variant_regs i2sv7_regs = {
+       .bfs_off = 0,
+       .rfs_off = 4,
+       .sdf_off = 7,
+       .txr_off = 9,
+       .rclksrc_off = 11,
+       .mss_off = 12,
+       .cdclkcon_off = 22,
+       .lrp_off = 15,
+       .bfs_mask = 0xf,
+       .rfs_mask = 0x7,
+       .ftx0cnt_off = 0,
+};
+
+static const struct samsung_i2s_variant_regs i2sv5_i2s1_regs = {
+       .bfs_off = 0,
+       .rfs_off = 3,
+       .sdf_off = 6,
+       .txr_off = 8,
+       .rclksrc_off = 10,
+       .mss_off = 11,
+       .cdclkcon_off = 12,
+       .lrp_off = 15,
+       .bfs_mask = 0x7,
+       .rfs_mask = 0x7,
+       .ftx0cnt_off = 8,
+};
+
 static const struct samsung_i2s_dai_data i2sv3_dai_type = {
        .dai_type = TYPE_PRI,
        .quirks = QUIRK_NO_MUXPSR,
+       .i2s_variant_regs = &i2sv3_regs,
 };
 
 static const struct samsung_i2s_dai_data i2sv5_dai_type = {
        .dai_type = TYPE_PRI,
-       .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR,
+       .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
+                       QUIRK_SUPPORTS_IDMA,
+       .i2s_variant_regs = &i2sv3_regs,
 };
 
 static const struct samsung_i2s_dai_data i2sv6_dai_type = {
+       .dai_type = TYPE_PRI,
+       .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
+                       QUIRK_SUPPORTS_TDM | QUIRK_SUPPORTS_IDMA,
+       .i2s_variant_regs = &i2sv6_regs,
+};
+
+static const struct samsung_i2s_dai_data i2sv7_dai_type = {
        .dai_type = TYPE_PRI,
        .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
                        QUIRK_SUPPORTS_TDM,
+       .i2s_variant_regs = &i2sv7_regs,
+};
+
+static const struct samsung_i2s_dai_data i2sv5_dai_type_i2s1 = {
+       .dai_type = TYPE_PRI,
+       .quirks = QUIRK_PRI_6CHAN | QUIRK_NEED_RSTCLR,
+       .i2s_variant_regs = &i2sv5_i2s1_regs,
 };
 
 static const struct samsung_i2s_dai_data samsung_dai_type_pri = {
@@ -1331,10 +1426,13 @@ static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
 static struct platform_device_id samsung_i2s_driver_ids[] = {
        {
                .name           = "samsung-i2s",
-               .driver_data    = (kernel_ulong_t)&samsung_dai_type_pri,
+               .driver_data    = (kernel_ulong_t)&i2sv3_dai_type,
        }, {
                .name           = "samsung-i2s-sec",
                .driver_data    = (kernel_ulong_t)&samsung_dai_type_sec,
+       }, {
+               .name           = "samsung-i2sv4",
+               .driver_data    = (kernel_ulong_t)&i2sv5_dai_type,
        },
        {},
 };
@@ -1351,6 +1449,12 @@ static const struct of_device_id exynos_i2s_match[] = {
        }, {
                .compatible = "samsung,exynos5420-i2s",
                .data = &i2sv6_dai_type,
+       }, {
+               .compatible = "samsung,exynos7-i2s",
+               .data = &i2sv7_dai_type,
+       }, {
+               .compatible = "samsung,exynos7-i2s1",
+               .data = &i2sv5_dai_type_i2s1,
        },
        {},
 };
index 3c8f604..d7640e7 100644 (file)
@@ -153,8 +153,8 @@ static int odroidx2_audio_remove(struct platform_device *pdev)
 
        snd_soc_unregister_card(card);
 
-       of_node_put((struct device_node *)odroidx2_dai[0].cpu_of_node);
-       of_node_put((struct device_node *)odroidx2_dai[0].codec_of_node);
+       of_node_put(odroidx2_dai[0].cpu_of_node);
+       of_node_put(odroidx2_dai[0].codec_of_node);
 
        return 0;
 }
index 88e5df4..8869971 100644 (file)
@@ -842,12 +842,9 @@ static int fsi_clk_disable(struct device *dev,
                return -EINVAL;
 
        if (1 == clock->count--) {
-               if (clock->xck)
-                       clk_disable(clock->xck);
-               if (clock->ick)
-                       clk_disable(clock->ick);
-               if (clock->div)
-                       clk_disable(clock->div);
+               clk_disable(clock->xck);
+               clk_disable(clock->ick);
+               clk_disable(clock->div);
        }
 
        return 0;
index 0af2e4d..d5f567e 100644 (file)
@@ -272,7 +272,7 @@ static const struct snd_soc_dai_ops hac_dai_ops = {
 static struct snd_soc_dai_driver sh4_hac_dai[] = {
 {
        .name                   = "hac-dai.0",
-       .ac97_control           = 1,
+       .bus_control            = true,
        .playback = {
                .rates          = AC97_RATES,
                .formats        = AC97_FMTS,
index fc41a0e..14d1a71 100644 (file)
@@ -430,7 +430,7 @@ int rsnd_adg_probe(struct platform_device *pdev,
        adg->clk[CLKI]  = devm_clk_get(dev, "clk_i");
 
        for_each_rsnd_clk(clk, adg, i)
-               dev_dbg(dev, "clk %d : %p\n", i, clk);
+               dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
 
        rsnd_adg_ssi_clk_init(priv, adg);
 
index 7004219..75308bb 100644 (file)
@@ -349,7 +349,7 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
                                                     dma_name);
        if (!dma->chan) {
                dev_err(dev, "can't get dma channel\n");
-               return -EIO;
+               goto rsnd_dma_channel_err;
        }
 
        ret = dmaengine_slave_config(dma->chan, &cfg);
@@ -363,8 +363,15 @@ int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
 
 rsnd_dma_init_err:
        rsnd_dma_quit(priv, dma);
+rsnd_dma_channel_err:
 
-       return ret;
+       /*
+        * DMA failed. try to PIO mode
+        * see
+        *      rsnd_ssi_fallback()
+        *      rsnd_rdai_continuance_probe()
+        */
+       return -EAGAIN;
 }
 
 void  rsnd_dma_quit(struct rsnd_priv *priv,
@@ -409,9 +416,16 @@ u32 rsnd_get_adinr(struct rsnd_mod *mod)
 ({                                                             \
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);         \
        struct device *dev = rsnd_priv_to_dev(priv);            \
-       dev_dbg(dev, "%s [%d] %s\n",                            \
-               rsnd_mod_name(mod), rsnd_mod_id(mod), #func);   \
-       (mod)->ops->func(mod, rdai);                            \
+       u32 mask = 1 << __rsnd_mod_shift_##func;                        \
+       u32 call = __rsnd_mod_call_##func << __rsnd_mod_shift_##func;   \
+       int ret = 0;                                                    \
+       if ((mod->status & mask) == call) {                             \
+               dev_dbg(dev, "%s[%d] %s\n",                             \
+                       rsnd_mod_name(mod), rsnd_mod_id(mod), #func);   \
+               ret = (mod)->ops->func(mod, rdai);                      \
+               mod->status = (mod->status & ~mask) | (~call & mask);   \
+       }                                                               \
+       ret;                                                            \
 })
 
 #define rsnd_mod_call(mod, func, rdai...)      \
@@ -456,6 +470,13 @@ static int rsnd_dai_connect(struct rsnd_mod *mod,
        return 0;
 }
 
+static void rsnd_dai_disconnect(struct rsnd_mod *mod,
+                               struct rsnd_dai_stream *io)
+{
+       mod->io = NULL;
+       io->mod[mod->type] = NULL;
+}
+
 int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai)
 {
        int id = rdai - priv->rdai;
@@ -686,6 +707,20 @@ static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
        ret;                                                    \
 })
 
+#define rsnd_path_break(priv, io, type)                                \
+{                                                              \
+       struct rsnd_mod *mod;                                   \
+       int id = -1;                                            \
+                                                               \
+       if (rsnd_is_enable_path(io, type)) {                    \
+               id = rsnd_info_id(priv, io, type);              \
+               if (id >= 0) {                                  \
+                       mod = rsnd_##type##_mod_get(priv, id);  \
+                       rsnd_dai_disconnect(mod, io);           \
+               }                                               \
+       }                                                       \
+}
+
 static int rsnd_path_init(struct rsnd_priv *priv,
                          struct rsnd_dai *rdai,
                          struct rsnd_dai_stream *io)
@@ -933,6 +968,150 @@ static struct snd_pcm_ops rsnd_pcm_ops = {
        .pointer        = rsnd_pointer,
 };
 
+/*
+ *             snd_kcontrol
+ */
+#define kcontrol_to_cfg(kctrl) ((struct rsnd_kctrl_cfg *)kctrl->private_value)
+static int rsnd_kctrl_info(struct snd_kcontrol *kctrl,
+                          struct snd_ctl_elem_info *uinfo)
+{
+       struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+
+       if (cfg->texts) {
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
+               uinfo->count = cfg->size;
+               uinfo->value.enumerated.items = cfg->max;
+               if (uinfo->value.enumerated.item >= cfg->max)
+                       uinfo->value.enumerated.item = cfg->max - 1;
+               strlcpy(uinfo->value.enumerated.name,
+                       cfg->texts[uinfo->value.enumerated.item],
+                       sizeof(uinfo->value.enumerated.name));
+       } else {
+               uinfo->count = cfg->size;
+               uinfo->value.integer.min = 0;
+               uinfo->value.integer.max = cfg->max;
+               uinfo->type = (cfg->max == 1) ?
+                       SNDRV_CTL_ELEM_TYPE_BOOLEAN :
+                       SNDRV_CTL_ELEM_TYPE_INTEGER;
+       }
+
+       return 0;
+}
+
+static int rsnd_kctrl_get(struct snd_kcontrol *kctrl,
+                         struct snd_ctl_elem_value *uc)
+{
+       struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+       int i;
+
+       for (i = 0; i < cfg->size; i++)
+               if (cfg->texts)
+                       uc->value.enumerated.item[i] = cfg->val[i];
+               else
+                       uc->value.integer.value[i] = cfg->val[i];
+
+       return 0;
+}
+
+static int rsnd_kctrl_put(struct snd_kcontrol *kctrl,
+                         struct snd_ctl_elem_value *uc)
+{
+       struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
+       struct rsnd_kctrl_cfg *cfg = kcontrol_to_cfg(kctrl);
+       int i, change = 0;
+
+       for (i = 0; i < cfg->size; i++) {
+               if (cfg->texts) {
+                       change |= (uc->value.enumerated.item[i] != cfg->val[i]);
+                       cfg->val[i] = uc->value.enumerated.item[i];
+               } else {
+                       change |= (uc->value.integer.value[i] != cfg->val[i]);
+                       cfg->val[i] = uc->value.integer.value[i];
+               }
+       }
+
+       if (change)
+               cfg->update(mod);
+
+       return change;
+}
+
+static int __rsnd_kctrl_new(struct rsnd_mod *mod,
+                           struct rsnd_dai *rdai,
+                           struct snd_soc_pcm_runtime *rtd,
+                           const unsigned char *name,
+                           struct rsnd_kctrl_cfg *cfg,
+                           void (*update)(struct rsnd_mod *mod))
+{
+       struct snd_card *card = rtd->card->snd_card;
+       struct snd_kcontrol *kctrl;
+       struct snd_kcontrol_new knew = {
+               .iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name           = name,
+               .info           = rsnd_kctrl_info,
+               .get            = rsnd_kctrl_get,
+               .put            = rsnd_kctrl_put,
+               .private_value  = (unsigned long)cfg,
+       };
+       int ret;
+
+       kctrl = snd_ctl_new1(&knew, mod);
+       if (!kctrl)
+               return -ENOMEM;
+
+       ret = snd_ctl_add(card, kctrl);
+       if (ret < 0)
+               return ret;
+
+       cfg->update = update;
+
+       return 0;
+}
+
+int rsnd_kctrl_new_m(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    void (*update)(struct rsnd_mod *mod),
+                    struct rsnd_kctrl_cfg_m *_cfg,
+                    u32 max)
+{
+       _cfg->cfg.max   = max;
+       _cfg->cfg.size  = RSND_DVC_CHANNELS;
+       _cfg->cfg.val   = _cfg->val;
+       return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
+int rsnd_kctrl_new_s(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    void (*update)(struct rsnd_mod *mod),
+                    struct rsnd_kctrl_cfg_s *_cfg,
+                    u32 max)
+{
+       _cfg->cfg.max   = max;
+       _cfg->cfg.size  = 1;
+       _cfg->cfg.val   = &_cfg->val;
+       return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
+int rsnd_kctrl_new_e(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    struct rsnd_kctrl_cfg_s *_cfg,
+                    void (*update)(struct rsnd_mod *mod),
+                    const char * const *texts,
+                    u32 max)
+{
+       _cfg->cfg.max   = max;
+       _cfg->cfg.size  = 1;
+       _cfg->cfg.val   = &_cfg->val;
+       _cfg->cfg.texts = texts;
+       return __rsnd_kctrl_new(mod, rdai, rtd, name, &_cfg->cfg, update);
+}
+
 /*
  *             snd_soc_platform
  */
@@ -976,6 +1155,49 @@ static const struct snd_soc_component_driver rsnd_soc_component = {
        .name           = "rsnd",
 };
 
+static int rsnd_rdai_continuance_probe(struct rsnd_priv *priv,
+                                      struct rsnd_dai *rdai,
+                                      int is_play)
+{
+       struct rsnd_dai_stream *io = is_play ? &rdai->playback : &rdai->capture;
+       int ret;
+
+       ret = rsnd_dai_call(probe, io, rdai);
+       if (ret == -EAGAIN) {
+               /*
+                * Fallback to PIO mode
+                */
+
+               /*
+                * call "remove" for SSI/SRC/DVC
+                * SSI will be switch to PIO mode if it was DMA mode
+                * see
+                *      rsnd_dma_init()
+                *      rsnd_ssi_fallback()
+                */
+               rsnd_dai_call(remove, io, rdai);
+
+               /*
+                * remove SRC/DVC from DAI,
+                */
+               rsnd_path_break(priv, io, src);
+               rsnd_path_break(priv, io, dvc);
+
+               /*
+                * fallback
+                */
+               rsnd_dai_call(fallback, io, rdai);
+
+               /*
+                * retry to "probe".
+                * DAI has SSI which is PIO mode only now.
+                */
+               ret = rsnd_dai_call(probe, io, rdai);
+       }
+
+       return ret;
+}
+
 /*
  *     rsnd probe
  */
@@ -1037,11 +1259,11 @@ static int rsnd_probe(struct platform_device *pdev)
        }
 
        for_each_rsnd_dai(rdai, priv, i) {
-               ret = rsnd_dai_call(probe, &rdai->playback, rdai);
+               ret = rsnd_rdai_continuance_probe(priv, rdai, 1);
                if (ret)
                        goto exit_snd_probe;
 
-               ret = rsnd_dai_call(probe, &rdai->capture, rdai);
+               ret = rsnd_rdai_continuance_probe(priv, rdai, 0);
                if (ret)
                        goto exit_snd_probe;
        }
index 3f44393..5380a48 100644 (file)
@@ -11,8 +11,6 @@
 #include "rsnd.h"
 
 #define RSND_DVC_NAME_SIZE     16
-#define RSND_DVC_VOLUME_MAX    100
-#define RSND_DVC_VOLUME_NUM    2
 
 #define DVC_NAME "dvc"
 
@@ -20,8 +18,11 @@ struct rsnd_dvc {
        struct rsnd_dvc_platform_info *info; /* rcar_snd.h */
        struct rsnd_mod mod;
        struct clk *clk;
-       u8 volume[RSND_DVC_VOLUME_NUM];
-       u8 mute[RSND_DVC_VOLUME_NUM];
+       struct rsnd_kctrl_cfg_m volume;
+       struct rsnd_kctrl_cfg_m mute;
+       struct rsnd_kctrl_cfg_s ren;    /* Ramp Enable */
+       struct rsnd_kctrl_cfg_s rup;    /* Ramp Rate Up */
+       struct rsnd_kctrl_cfg_s rdown;  /* Ramp Rate Down */
 };
 
 #define rsnd_mod_to_dvc(_mod)  \
@@ -33,23 +34,87 @@ struct rsnd_dvc {
             ((pos) = (struct rsnd_dvc *)(priv)->dvc + i);      \
             i++)
 
+static const char const *dvc_ramp_rate[] = {
+       "128 dB/1 step",         /* 00000 */
+       "64 dB/1 step",          /* 00001 */
+       "32 dB/1 step",          /* 00010 */
+       "16 dB/1 step",          /* 00011 */
+       "8 dB/1 step",           /* 00100 */
+       "4 dB/1 step",           /* 00101 */
+       "2 dB/1 step",           /* 00110 */
+       "1 dB/1 step",           /* 00111 */
+       "0.5 dB/1 step",         /* 01000 */
+       "0.25 dB/1 step",        /* 01001 */
+       "0.125 dB/1 step",       /* 01010 */
+       "0.125 dB/2 steps",      /* 01011 */
+       "0.125 dB/4 steps",      /* 01100 */
+       "0.125 dB/8 steps",      /* 01101 */
+       "0.125 dB/16 steps",     /* 01110 */
+       "0.125 dB/32 steps",     /* 01111 */
+       "0.125 dB/64 steps",     /* 10000 */
+       "0.125 dB/128 steps",    /* 10001 */
+       "0.125 dB/256 steps",    /* 10010 */
+       "0.125 dB/512 steps",    /* 10011 */
+       "0.125 dB/1024 steps",   /* 10100 */
+       "0.125 dB/2048 steps",   /* 10101 */
+       "0.125 dB/4096 steps",   /* 10110 */
+       "0.125 dB/8192 steps",   /* 10111 */
+};
+
 static void rsnd_dvc_volume_update(struct rsnd_mod *mod)
 {
        struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
-       u32 max = (0x00800000 - 1);
-       u32 vol[RSND_DVC_VOLUME_NUM];
+       u32 val[RSND_DVC_CHANNELS];
+       u32 dvucr = 0;
        u32 mute = 0;
        int i;
 
-       for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
-               vol[i] = max / RSND_DVC_VOLUME_MAX * dvc->volume[i];
-               mute |= (!!dvc->mute[i]) << i;
+       for (i = 0; i < dvc->mute.cfg.size; i++)
+               mute |= (!!dvc->mute.cfg.val[i]) << i;
+
+       /* Disable DVC Register access */
+       rsnd_mod_write(mod, DVC_DVUER, 0);
+
+       /* Enable Ramp */
+       if (dvc->ren.val) {
+               dvucr |= 0x10;
+
+               /* Digital Volume Max */
+               for (i = 0; i < RSND_DVC_CHANNELS; i++)
+                       val[i] = dvc->volume.cfg.max;
+
+               rsnd_mod_write(mod, DVC_VRCTR, 0xff);
+               rsnd_mod_write(mod, DVC_VRPDR, dvc->rup.val << 8 |
+                                              dvc->rdown.val);
+               /*
+                * FIXME !!
+                * use scale-downed Digital Volume
+                * as Volume Ramp
+                * 7F FFFF -> 3FF
+                */
+               rsnd_mod_write(mod, DVC_VRDBR,
+                              0x3ff - (dvc->volume.val[0] >> 13));
+
+       } else {
+               for (i = 0; i < RSND_DVC_CHANNELS; i++)
+                       val[i] = dvc->volume.val[i];
+       }
+
+       /* Enable Digital Volume */
+       dvucr |= 0x100;
+       rsnd_mod_write(mod, DVC_VOL0R, val[0]);
+       rsnd_mod_write(mod, DVC_VOL1R, val[1]);
+
+       /*  Enable Mute */
+       if (mute) {
+               dvucr |= 0x1;
+               rsnd_mod_write(mod, DVC_ZCMCR, mute);
        }
 
-       rsnd_mod_write(mod, DVC_VOL0R, vol[0]);
-       rsnd_mod_write(mod, DVC_VOL1R, vol[1]);
+       rsnd_mod_write(mod, DVC_DVUCR, dvucr);
 
-       rsnd_mod_write(mod, DVC_ZCMCR, mute);
+       /* Enable DVC Register access */
+       rsnd_mod_write(mod, DVC_DVUER, 1);
 }
 
 static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
@@ -58,7 +123,8 @@ static int rsnd_dvc_probe_gen2(struct rsnd_mod *mod,
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
 
-       dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
+       dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return 0;
 }
@@ -102,16 +168,11 @@ static int rsnd_dvc_init(struct rsnd_mod *dvc_mod,
 
        rsnd_mod_write(dvc_mod, DVC_ADINR, rsnd_get_adinr(dvc_mod));
 
-       /*  enable Volume / Mute */
-       rsnd_mod_write(dvc_mod, DVC_DVUCR, 0x101);
-
        /* ch0/ch1 Volume */
        rsnd_dvc_volume_update(dvc_mod);
 
        rsnd_mod_write(dvc_mod, DVC_DVUIR, 0);
 
-       rsnd_mod_write(dvc_mod, DVC_DVUER, 1);
-
        rsnd_adg_set_cmd_timsel_gen2(rdai, dvc_mod, io);
 
        return 0;
@@ -143,86 +204,6 @@ static int rsnd_dvc_stop(struct rsnd_mod *mod,
        return 0;
 }
 
-static int rsnd_dvc_volume_info(struct snd_kcontrol *kctrl,
-                              struct snd_ctl_elem_info *uinfo)
-{
-       struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
-       struct rsnd_dvc *dvc = rsnd_mod_to_dvc(mod);
-       u8 *val = (u8 *)kctrl->private_value;
-
-       uinfo->count = RSND_DVC_VOLUME_NUM;
-       uinfo->value.integer.min = 0;
-
-       if (val == dvc->volume) {
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-               uinfo->value.integer.max = RSND_DVC_VOLUME_MAX;
-       } else {
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-               uinfo->value.integer.max = 1;
-       }
-
-       return 0;
-}
-
-static int rsnd_dvc_volume_get(struct snd_kcontrol *kctrl,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       u8 *val = (u8 *)kctrl->private_value;
-       int i;
-
-       for (i = 0; i < RSND_DVC_VOLUME_NUM; i++)
-               ucontrol->value.integer.value[i] = val[i];
-
-       return 0;
-}
-
-static int rsnd_dvc_volume_put(struct snd_kcontrol *kctrl,
-                             struct snd_ctl_elem_value *ucontrol)
-{
-       struct rsnd_mod *mod = snd_kcontrol_chip(kctrl);
-       u8 *val = (u8 *)kctrl->private_value;
-       int i, change = 0;
-
-       for (i = 0; i < RSND_DVC_VOLUME_NUM; i++) {
-               change |= (ucontrol->value.integer.value[i] != val[i]);
-               val[i] = ucontrol->value.integer.value[i];
-       }
-
-       if (change)
-               rsnd_dvc_volume_update(mod);
-
-       return change;
-}
-
-static int __rsnd_dvc_pcm_new(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai,
-                             struct snd_soc_pcm_runtime *rtd,
-                             const unsigned char *name,
-                             u8 *private)
-{
-       struct snd_card *card = rtd->card->snd_card;
-       struct snd_kcontrol *kctrl;
-       struct snd_kcontrol_new knew = {
-               .iface          = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name           = name,
-               .info           = rsnd_dvc_volume_info,
-               .get            = rsnd_dvc_volume_get,
-               .put            = rsnd_dvc_volume_put,
-               .private_value  = (unsigned long)private,
-       };
-       int ret;
-
-       kctrl = snd_ctl_new1(&knew, mod);
-       if (!kctrl)
-               return -ENOMEM;
-
-       ret = snd_ctl_add(card, kctrl);
-       if (ret < 0)
-               return ret;
-
-       return 0;
-}
-
 static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
                            struct rsnd_dai *rdai,
                            struct snd_soc_pcm_runtime *rtd)
@@ -232,18 +213,48 @@ static int rsnd_dvc_pcm_new(struct rsnd_mod *mod,
        int ret;
 
        /* Volume */
-       ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
+       ret = rsnd_kctrl_new_m(mod, rdai, rtd,
                        rsnd_dai_is_play(rdai, io) ?
                        "DVC Out Playback Volume" : "DVC In Capture Volume",
-                       dvc->volume);
+                       rsnd_dvc_volume_update,
+                       &dvc->volume, 0x00800000 - 1);
        if (ret < 0)
                return ret;
 
        /* Mute */
-       ret = __rsnd_dvc_pcm_new(mod, rdai, rtd,
+       ret = rsnd_kctrl_new_m(mod, rdai, rtd,
                        rsnd_dai_is_play(rdai, io) ?
                        "DVC Out Mute Switch" : "DVC In Mute Switch",
-                       dvc->mute);
+                       rsnd_dvc_volume_update,
+                       &dvc->mute, 1);
+       if (ret < 0)
+               return ret;
+
+       /* Ramp */
+       ret = rsnd_kctrl_new_s(mod, rdai, rtd,
+                       rsnd_dai_is_play(rdai, io) ?
+                       "DVC Out Ramp Switch" : "DVC In Ramp Switch",
+                       rsnd_dvc_volume_update,
+                       &dvc->ren, 1);
+       if (ret < 0)
+               return ret;
+
+       ret = rsnd_kctrl_new_e(mod, rdai, rtd,
+                       rsnd_dai_is_play(rdai, io) ?
+                       "DVC Out Ramp Up Rate" : "DVC In Ramp Up Rate",
+                       &dvc->rup,
+                       rsnd_dvc_volume_update,
+                       dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+       if (ret < 0)
+               return ret;
+
+       ret = rsnd_kctrl_new_e(mod, rdai, rtd,
+                       rsnd_dai_is_play(rdai, io) ?
+                       "DVC Out Ramp Down Rate" : "DVC In Ramp Down Rate",
+                       &dvc->rdown,
+                       rsnd_dvc_volume_update,
+                       dvc_ramp_rate, ARRAY_SIZE(dvc_ramp_rate));
+
        if (ret < 0)
                return ret;
 
index f95e7ab..87a6f2d 100644 (file)
@@ -8,6 +8,17 @@
  * it under the terms of the GNU General Public License version 2 as
  * published by the Free Software Foundation.
  */
+
+/*
+ * #define DEBUG
+ *
+ * you can also add below in
+ * ${LINUX}/drivers/base/regmap/regmap.c
+ * for regmap debug
+ *
+ * #define LOG_DEVICE "xxxx.rcar_sound"
+ */
+
 #include "rsnd.h"
 
 struct rsnd_gen {
@@ -67,9 +78,10 @@ u32 rsnd_read(struct rsnd_priv *priv,
        if (!rsnd_is_accessible_reg(priv, gen, reg))
                return 0;
 
-       regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
+       dev_dbg(dev, "r %s[%d] - %4d : %08x\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod), reg, val);
 
-       dev_dbg(dev, "r %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, val);
+       regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
 
        return val;
 }
@@ -84,9 +96,10 @@ void rsnd_write(struct rsnd_priv *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] - %4d : %08x\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data);
 
-       dev_dbg(dev, "w %s - 0x%04d : %08x\n", rsnd_mod_name(mod), reg, data);
+       regmap_fields_write(gen->regs[reg], rsnd_mod_id(mod), data);
 }
 
 void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
@@ -98,11 +111,11 @@ void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
        if (!rsnd_is_accessible_reg(priv, gen, reg))
                return;
 
+       dev_dbg(dev, "b %s[%d] - %4d : %08x/%08x\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod), reg, data, mask);
+
        regmap_fields_update_bits(gen->regs[reg], rsnd_mod_id(mod),
                                  mask, data);
-
-       dev_dbg(dev, "b %s - 0x%04d : %08x/%08x\n",
-               rsnd_mod_name(mod), reg, data, mask);
 }
 
 #define rsnd_gen_regmap_init(priv, id_size, reg_id, conf)              \
@@ -311,6 +324,9 @@ static int rsnd_gen2_probe(struct platform_device *pdev,
                RSND_GEN_M_REG(DVC_ADINR,       0xe08,  0x100),
                RSND_GEN_M_REG(DVC_DVUCR,       0xe10,  0x100),
                RSND_GEN_M_REG(DVC_ZCMCR,       0xe14,  0x100),
+               RSND_GEN_M_REG(DVC_VRCTR,       0xe18,  0x100),
+               RSND_GEN_M_REG(DVC_VRPDR,       0xe1c,  0x100),
+               RSND_GEN_M_REG(DVC_VRDBR,       0xe20,  0x100),
                RSND_GEN_M_REG(DVC_VOL0R,       0xe28,  0x100),
                RSND_GEN_M_REG(DVC_VOL1R,       0xe2c,  0x100),
                RSND_GEN_M_REG(DVC_DVUER,       0xe48,  0x100),
index d119adf..5826c8a 100644 (file)
@@ -91,6 +91,9 @@ enum rsnd_reg {
        RSND_REG_SHARE20,
        RSND_REG_SHARE21,
        RSND_REG_SHARE22,
+       RSND_REG_SHARE23,
+       RSND_REG_SHARE24,
+       RSND_REG_SHARE25,
 
        RSND_REG_MAX,
 };
@@ -129,6 +132,9 @@ enum rsnd_reg {
 #define RSND_REG_CMD_CTRL              RSND_REG_SHARE20
 #define RSND_REG_CMDOUT_TIMSEL         RSND_REG_SHARE21
 #define RSND_REG_BUSIF_DALIGN          RSND_REG_SHARE22
+#define RSND_REG_DVC_VRCTR             RSND_REG_SHARE23
+#define RSND_REG_DVC_VRPDR             RSND_REG_SHARE24
+#define RSND_REG_DVC_VRDBR             RSND_REG_SHARE25
 
 struct rsnd_of_data;
 struct rsnd_priv;
@@ -200,6 +206,8 @@ struct rsnd_mod_ops {
        int (*pcm_new)(struct rsnd_mod *mod,
                       struct rsnd_dai *rdai,
                       struct snd_soc_pcm_runtime *rtd);
+       int (*fallback)(struct rsnd_mod *mod,
+                       struct rsnd_dai *rdai);
 };
 
 struct rsnd_dai_stream;
@@ -210,7 +218,35 @@ struct rsnd_mod {
        struct rsnd_mod_ops *ops;
        struct rsnd_dma dma;
        struct rsnd_dai_stream *io;
+       u32 status;
 };
+/*
+ * status
+ *
+ * bit
+ * 0   0: probe        1: remove
+ * 1   0: init         1: quit
+ * 2   0: start        1: stop
+ * 3   0: pcm_new
+ * 4   0: fallback
+ */
+#define __rsnd_mod_shift_probe         0
+#define __rsnd_mod_shift_remove                0
+#define __rsnd_mod_shift_init          1
+#define __rsnd_mod_shift_quit          1
+#define __rsnd_mod_shift_start         2
+#define __rsnd_mod_shift_stop          2
+#define __rsnd_mod_shift_pcm_new       3
+#define __rsnd_mod_shift_fallback      4
+
+#define __rsnd_mod_call_probe          0
+#define __rsnd_mod_call_remove         1
+#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_pcm_new                0
+#define __rsnd_mod_call_fallback       0
 
 #define rsnd_mod_to_priv(mod) ((mod)->priv)
 #define rsnd_mod_to_dma(mod) (&(mod)->dma)
@@ -267,7 +303,8 @@ struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id);
 int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io);
 int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai);
 #define rsnd_dai_get_platform_info(rdai) ((rdai)->info)
-#define rsnd_io_to_runtime(io) ((io)->substream->runtime)
+#define rsnd_io_to_runtime(io) ((io)->substream ? \
+                               (io)->substream->runtime : NULL)
 
 void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
 int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
@@ -381,6 +418,51 @@ struct rsnd_priv {
        is_play;                                                        \
 })
 
+/*
+ *     rsnd_kctrl
+ */
+struct rsnd_kctrl_cfg {
+       unsigned int max;
+       unsigned int size;
+       u32 *val;
+       const char * const *texts;
+       void (*update)(struct rsnd_mod *mod);
+};
+
+#define RSND_DVC_CHANNELS      2
+struct rsnd_kctrl_cfg_m {
+       struct rsnd_kctrl_cfg cfg;
+       u32 val[RSND_DVC_CHANNELS];
+};
+
+struct rsnd_kctrl_cfg_s {
+       struct rsnd_kctrl_cfg cfg;
+       u32 val;
+};
+
+int rsnd_kctrl_new_m(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    void (*update)(struct rsnd_mod *mod),
+                    struct rsnd_kctrl_cfg_m *_cfg,
+                    u32 max);
+int rsnd_kctrl_new_s(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    void (*update)(struct rsnd_mod *mod),
+                    struct rsnd_kctrl_cfg_s *_cfg,
+                    u32 max);
+int rsnd_kctrl_new_e(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct snd_soc_pcm_runtime *rtd,
+                    const unsigned char *name,
+                    struct rsnd_kctrl_cfg_s *_cfg,
+                    void (*update)(struct rsnd_mod *mod),
+                    const char * const *texts,
+                    u32 max);
+
 /*
  *     R-Car SRC
  */
@@ -395,10 +477,11 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
                        struct rsnd_dai *rdai,
                        int use_busif);
 int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
-                      struct rsnd_dai *rdai,
-                      int use_busif);
-int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
+                      struct rsnd_dai *rdai);
+int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod,
                            struct rsnd_dai *rdai);
+int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod,
+                            struct rsnd_dai *rdai);
 
 #define rsnd_src_nr(priv) ((priv)->src_nr)
 
@@ -410,6 +493,7 @@ int rsnd_ssi_probe(struct platform_device *pdev,
                   struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id);
 int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod);
 
 /*
  *     R-Car DVC
index 9183e01..eede3ac 100644 (file)
@@ -175,30 +175,47 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
 }
 
 int rsnd_src_ssiu_stop(struct rsnd_mod *ssi_mod,
-                       struct rsnd_dai *rdai,
-                       int use_busif)
+                      struct rsnd_dai *rdai)
 {
        /*
         * DMA settings for SSIU
         */
-       if (use_busif)
-               rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
+       rsnd_mod_write(ssi_mod, SSI_CTRL, 0);
 
        return 0;
 }
 
-int rsnd_src_enable_ssi_irq(struct rsnd_mod *ssi_mod,
+int rsnd_src_ssi_irq_enable(struct rsnd_mod *ssi_mod,
                            struct rsnd_dai *rdai)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
 
-       /* enable PIO interrupt if Gen2 */
-       if (rsnd_is_gen2(priv))
+       if (rsnd_is_gen1(priv))
+               return 0;
+
+       /* enable SSI interrupt if Gen2 */
+       if (rsnd_ssi_is_dma_mode(ssi_mod))
+               rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0e000000);
+       else
                rsnd_mod_write(ssi_mod, INT_ENABLE, 0x0f000000);
 
        return 0;
 }
 
+int rsnd_src_ssi_irq_disable(struct rsnd_mod *ssi_mod,
+                           struct rsnd_dai *rdai)
+{
+       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, INT_ENABLE, 0x00000000);
+
+       return 0;
+}
+
 unsigned int rsnd_src_get_ssi_rate(struct rsnd_priv *priv,
                                   struct rsnd_dai_stream *io,
                                   struct snd_pcm_runtime *runtime)
@@ -239,12 +256,6 @@ static int rsnd_src_set_convert_rate(struct rsnd_mod *mod,
        rsnd_mod_write(mod, SRC_SWRSR, 0);
        rsnd_mod_write(mod, SRC_SWRSR, 1);
 
-       /*
-        * Initialize the operation of the SRC internal circuits
-        * see rsnd_src_start()
-        */
-       rsnd_mod_write(mod, SRC_SRCIR, 1);
-
        /* Set channel number and output bit length */
        rsnd_mod_write(mod, SRC_ADINR, rsnd_get_adinr(mod));
 
@@ -269,6 +280,12 @@ static int rsnd_src_init(struct rsnd_mod *mod,
 
        clk_prepare_enable(src->clk);
 
+       /*
+        * Initialize the operation of the SRC internal circuits
+        * see rsnd_src_start()
+        */
+       rsnd_mod_write(mod, SRC_SRCIR, 1);
+
        return 0;
 }
 
@@ -282,32 +299,20 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
        return 0;
 }
 
-static int rsnd_src_start(struct rsnd_mod *mod,
-                         struct rsnd_dai *rdai)
+static int rsnd_src_start(struct rsnd_mod *mod)
 {
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-
        /*
         * Cancel the initialization and operate the SRC function
-        * see rsnd_src_set_convert_rate()
+        * see rsnd_src_init()
         */
        rsnd_mod_write(mod, SRC_SRCIR, 0);
 
-       if (rsnd_src_convert_rate(src))
-               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
-
        return 0;
 }
 
-
-static int rsnd_src_stop(struct rsnd_mod *mod,
-                        struct rsnd_dai *rdai)
+static int rsnd_src_stop(struct rsnd_mod *mod)
 {
-       struct rsnd_src *src = rsnd_mod_to_src(mod);
-
-       if (rsnd_src_convert_rate(src))
-               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 0);
-
+       /* nothing to do */
        return 0;
 }
 
@@ -414,6 +419,7 @@ static int rsnd_src_set_convert_timing_gen1(struct rsnd_mod *mod,
 static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
                                          struct rsnd_dai *rdai)
 {
+       struct rsnd_src *src = rsnd_mod_to_src(mod);
        int ret;
 
        ret = rsnd_src_set_convert_rate(mod, rdai);
@@ -427,6 +433,10 @@ static int rsnd_src_set_convert_rate_gen1(struct rsnd_mod *mod,
        rsnd_mod_write(mod, SRC_MNFSR,
                       rsnd_mod_read(mod, SRC_IFSVR) / 100 * 98);
 
+       /* Gen1/Gen2 are not compatible */
+       if (rsnd_src_convert_rate(src))
+               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+
        /* no SRC_BFSSR settings, since SRC_SRCCR::BUFMD is 0 */
 
        return 0;
@@ -438,7 +448,8 @@ static int rsnd_src_probe_gen1(struct rsnd_mod *mod,
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
 
-       dev_dbg(dev, "%s (Gen1) is probed\n", rsnd_mod_name(mod));
+       dev_dbg(dev, "%s[%d] (Gen1) is probed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return 0;
 }
@@ -474,7 +485,7 @@ static int rsnd_src_start_gen1(struct rsnd_mod *mod,
 
        rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), (1 << id));
 
-       return rsnd_src_start(mod, rdai);
+       return rsnd_src_start(mod);
 }
 
 static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
@@ -484,7 +495,7 @@ static int rsnd_src_stop_gen1(struct rsnd_mod *mod,
 
        rsnd_mod_bset(mod, SRC_ROUTE_CTRL, (1 << id), 0);
 
-       return rsnd_src_stop(mod, rdai);
+       return rsnd_src_stop(mod);
 }
 
 static struct rsnd_mod_ops rsnd_src_gen1_ops = {
@@ -507,16 +518,17 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        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(src);
        uint ratio;
        int ret;
 
        /* 6 - 1/6 are very enough ratio for SRC_BSDSR */
-       if (!rsnd_src_convert_rate(src))
+       if (!convert_rate)
                ratio = 0;
-       else if (rsnd_src_convert_rate(src) > runtime->rate)
-               ratio = 100 * rsnd_src_convert_rate(src) / runtime->rate;
+       else if (convert_rate > runtime->rate)
+               ratio = 100 * convert_rate / runtime->rate;
        else
-               ratio = 100 * runtime->rate / rsnd_src_convert_rate(src);
+               ratio = 100 * runtime->rate / convert_rate;
 
        if (ratio > 600) {
                dev_err(dev, "FSO/FSI ratio error\n");
@@ -529,6 +541,11 @@ static int rsnd_src_set_convert_rate_gen2(struct rsnd_mod *mod,
 
        rsnd_mod_write(mod, SRC_SRCCR, 0x00011110);
 
+       if (convert_rate) {
+               /* Gen1/Gen2 are not compatible */
+               rsnd_mod_write(mod, SRC_ROUTE_MODE0, 1);
+       }
+
        switch (rsnd_mod_id(mod)) {
        case 5:
        case 6:
@@ -578,9 +595,11 @@ static int rsnd_src_probe_gen2(struct rsnd_mod *mod,
                            rsnd_info_is_playback(priv, src),
                            src->info->dma_id);
        if (ret < 0)
-               dev_err(dev, "SRC DMA failed\n");
-
-       dev_dbg(dev, "%s (Gen2) is probed\n", rsnd_mod_name(mod));
+               dev_err(dev, "%s[%d] (Gen2) failed\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
+       else
+               dev_dbg(dev, "%s[%d] (Gen2) is probed\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return ret;
 }
@@ -624,7 +643,7 @@ static int rsnd_src_start_gen2(struct rsnd_mod *mod,
 
        rsnd_mod_write(mod, SRC_CTRL, val);
 
-       return rsnd_src_start(mod, rdai);
+       return rsnd_src_start(mod);
 }
 
 static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
@@ -636,7 +655,7 @@ static int rsnd_src_stop_gen2(struct rsnd_mod *mod,
 
        rsnd_dma_stop(rsnd_mod_to_dma(&src->mod));
 
-       return rsnd_src_stop(mod, rdai);
+       return rsnd_src_stop(mod);
 }
 
 static struct rsnd_mod_ops rsnd_src_gen2_ops = {
index 34e8400..3844fbe 100644 (file)
@@ -68,7 +68,6 @@ struct rsnd_ssi {
        struct rsnd_dai *rdai;
        u32 cr_own;
        u32 cr_clk;
-       u32 cr_etc;
        int err;
        unsigned int usrcnt;
        unsigned int rate;
@@ -83,7 +82,7 @@ struct rsnd_ssi {
 #define rsnd_ssi_nr(priv) ((priv)->ssi_nr)
 #define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
 #define rsnd_dma_to_ssi(dma)  rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
-#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0)
+#define rsnd_ssi_pio_available(ssi) ((ssi)->info->irq > 0)
 #define rsnd_ssi_dma_available(ssi) \
        rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))
 #define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
@@ -96,6 +95,9 @@ static int rsnd_ssi_use_busif(struct rsnd_mod *mod)
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        int use_busif = 0;
 
+       if (!rsnd_ssi_is_dma_mode(mod))
+               return 0;
+
        if (!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_NO_BUSIF))
                use_busif = 1;
        if (rsnd_io_to_mod_src(io))
@@ -159,7 +161,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
                                ssi->cr_clk     = FORCE | SWL_32 |
                                                  SCKD | SWSD | CKDV(j);
 
-                               dev_dbg(dev, "ssi%d outputs %u Hz\n",
+                               dev_dbg(dev, "%s[%d] outputs %u Hz\n",
+                                       rsnd_mod_name(&ssi->mod),
                                        rsnd_mod_id(&ssi->mod), rate);
 
                                return 0;
@@ -184,6 +187,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
        struct device *dev = rsnd_priv_to_dev(priv);
+       u32 cr_mode;
        u32 cr;
 
        if (0 == ssi->usrcnt) {
@@ -197,16 +201,29 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
                }
        }
 
+       cr_mode = rsnd_ssi_is_dma_mode(&ssi->mod) ?
+               DMEN :  /* DMA : enable DMA */
+               DIEN;   /* PIO : enable Data interrupt */
+
+
        cr  =   ssi->cr_own     |
                ssi->cr_clk     |
-               ssi->cr_etc     |
-               EN;
+               cr_mode         |
+               UIEN | OIEN | EN;
 
        rsnd_mod_write(&ssi->mod, SSICR, cr);
 
+       /* enable WS continue */
+       if (rsnd_dai_is_clk_master(rdai))
+               rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+
+       /* clear error status */
+       rsnd_mod_write(&ssi->mod, SSISR, 0);
+
        ssi->usrcnt++;
 
-       dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod));
+       dev_dbg(dev, "%s[%d] hw started\n",
+               rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
 }
 
 static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
@@ -249,7 +266,8 @@ static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
                clk_disable_unprepare(ssi->clk);
        }
 
-       dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod));
+       dev_dbg(dev, "%s[%d] hw stopped\n",
+               rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
 }
 
 /*
@@ -334,25 +352,54 @@ static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
        }
 }
 
-/*
- *             SSI PIO
- */
-static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
+static int rsnd_ssi_start(struct rsnd_mod *mod,
+                         struct rsnd_dai *rdai)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+
+       rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
+
+       rsnd_ssi_hw_start(ssi, rdai, io);
+
+       rsnd_src_ssi_irq_enable(mod, rdai);
+
+       return 0;
+}
+
+static int rsnd_ssi_stop(struct rsnd_mod *mod,
+                        struct rsnd_dai *rdai)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+       rsnd_src_ssi_irq_disable(mod, rdai);
+
+       rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
+
+       rsnd_ssi_hw_stop(ssi, rdai);
+
+       rsnd_src_ssiu_stop(mod, rdai);
+
+       return 0;
+}
+
+static irqreturn_t rsnd_ssi_interrupt(int irq, void *data)
 {
        struct rsnd_ssi *ssi = data;
+       struct rsnd_dai *rdai = ssi->rdai;
        struct rsnd_mod *mod = &ssi->mod;
        struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
        u32 status = rsnd_mod_read(mod, SSISR);
-       irqreturn_t ret = IRQ_NONE;
 
-       if (io && (status & DIRQ)) {
-               struct rsnd_dai *rdai = ssi->rdai;
+       if (!io)
+               return IRQ_NONE;
+
+       /* PIO only */
+       if (status & DIRQ) {
                struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
                u32 *buf = (u32 *)(runtime->dma_area +
                                   rsnd_dai_pointer_offset(io, 0));
 
-               rsnd_ssi_record_error(ssi, status);
-
                /*
                 * 8/16/32 data can be assesse to TDR/RDR register
                 * directly as 32bit data
@@ -364,73 +411,60 @@ static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
                        *buf = rsnd_mod_read(mod, SSIRDR);
 
                rsnd_dai_pointer_update(io, sizeof(*buf));
+       }
+
+       /* PIO / DMA */
+       if (status & (UIRQ | OIRQ)) {
+               struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+               struct device *dev = rsnd_priv_to_dev(priv);
+
+               /*
+                * restart SSI
+                */
+               rsnd_ssi_stop(mod, rdai);
+               rsnd_ssi_start(mod, rdai);
 
-               ret = IRQ_HANDLED;
+               dev_dbg(dev, "%s[%d] restart\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
        }
 
-       return ret;
+       rsnd_ssi_record_error(ssi, status);
+
+       return IRQ_HANDLED;
 }
 
+/*
+ *             SSI PIO
+ */
 static int rsnd_ssi_pio_probe(struct rsnd_mod *mod,
                              struct rsnd_dai *rdai)
 {
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       int irq = ssi->info->pio_irq;
        int ret;
 
-       ret = devm_request_irq(dev, irq,
-                              rsnd_ssi_pio_interrupt,
+       ret = devm_request_irq(dev, ssi->info->irq,
+                              rsnd_ssi_interrupt,
                               IRQF_SHARED,
                               dev_name(dev), ssi);
        if (ret)
-               dev_err(dev, "SSI request interrupt failed\n");
-
-       dev_dbg(dev, "%s (PIO) is probed\n", rsnd_mod_name(mod));
+               dev_err(dev, "%s[%d] (PIO) request interrupt failed\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
+       else
+               dev_dbg(dev, "%s[%d] (PIO) is probed\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return ret;
 }
 
-static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai)
-{
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
-
-       /* enable PIO IRQ */
-       ssi->cr_etc = UIEN | OIEN | DIEN;
-
-       rsnd_src_ssiu_start(mod, rdai, 0);
-
-       rsnd_src_enable_ssi_irq(mod, rdai);
-
-       rsnd_ssi_hw_start(ssi, rdai, io);
-
-       return 0;
-}
-
-static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
-                            struct rsnd_dai *rdai)
-{
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-
-       ssi->cr_etc = 0;
-
-       rsnd_ssi_hw_stop(ssi, rdai);
-
-       rsnd_src_ssiu_stop(mod, rdai, 0);
-
-       return 0;
-}
-
 static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
        .name   = SSI_NAME,
        .probe  = rsnd_ssi_pio_probe,
        .init   = rsnd_ssi_init,
        .quit   = rsnd_ssi_quit,
-       .start  = rsnd_ssi_pio_start,
-       .stop   = rsnd_ssi_pio_stop,
+       .start  = rsnd_ssi_start,
+       .stop   = rsnd_ssi_stop,
 };
 
 static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
@@ -442,15 +476,28 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
        int dma_id = ssi->info->dma_id;
        int ret;
 
+       ret = devm_request_irq(dev, ssi->info->irq,
+                              rsnd_ssi_interrupt,
+                              IRQF_SHARED,
+                              dev_name(dev), ssi);
+       if (ret)
+               goto rsnd_ssi_dma_probe_fail;
+
        ret = rsnd_dma_init(
                priv, rsnd_mod_to_dma(mod),
                rsnd_info_is_playback(priv, ssi),
                dma_id);
+       if (ret)
+               goto rsnd_ssi_dma_probe_fail;
 
-       if (ret < 0)
-               dev_err(dev, "SSI DMA failed\n");
+       dev_dbg(dev, "%s[%d] (DMA) is probed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+       return ret;
 
-       dev_dbg(dev, "%s (DMA) is probed\n", rsnd_mod_name(mod));
+rsnd_ssi_dma_probe_fail:
+       dev_err(dev, "%s[%d] (DMA) is failed\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 
        return ret;
 }
@@ -458,30 +505,48 @@ static int rsnd_ssi_dma_probe(struct rsnd_mod *mod,
 static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
                               struct rsnd_dai *rdai)
 {
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       int irq = ssi->info->irq;
+
        rsnd_dma_quit(rsnd_mod_to_priv(mod), rsnd_mod_to_dma(mod));
 
+       /* PIO will request IRQ again */
+       devm_free_irq(dev, irq, ssi);
+
        return 0;
 }
 
-static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
-                             struct rsnd_dai *rdai)
+static int rsnd_ssi_fallback(struct rsnd_mod *mod,
+                            struct rsnd_dai *rdai)
 {
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
-       struct rsnd_dai_stream *io = rsnd_mod_to_io(mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
 
-       /* enable DMA transfer */
-       ssi->cr_etc = DMEN;
+       /*
+        * fallback to PIO
+        *
+        * SSI .probe might be called again.
+        * see
+        *      rsnd_rdai_continuance_probe()
+        */
+       mod->ops = &rsnd_ssi_pio_ops;
 
-       rsnd_src_ssiu_start(mod, rdai, rsnd_ssi_use_busif(mod));
+       dev_info(dev, "%s[%d] fallback to PIO mode\n",
+                rsnd_mod_name(mod), rsnd_mod_id(mod));
 
-       rsnd_dma_start(dma);
+       return 0;
+}
 
-       rsnd_ssi_hw_start(ssi, ssi->rdai, io);
+static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
+                             struct rsnd_dai *rdai)
+{
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
 
-       /* enable WS continue */
-       if (rsnd_dai_is_clk_master(rdai))
-               rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+       rsnd_ssi_start(mod, rdai);
+
+       rsnd_dma_start(dma);
 
        return 0;
 }
@@ -489,18 +554,11 @@ static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
 static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
                             struct rsnd_dai *rdai)
 {
-       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
-       struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
-
-       ssi->cr_etc = 0;
-
-       rsnd_ssi_record_error(ssi, rsnd_mod_read(mod, SSISR));
-
-       rsnd_ssi_hw_stop(ssi, rdai);
+       struct rsnd_dma *dma = rsnd_mod_to_dma(mod);
 
        rsnd_dma_stop(dma);
 
-       rsnd_src_ssiu_stop(mod, rdai, 1);
+       rsnd_ssi_stop(mod, rdai);
 
        return 0;
 }
@@ -519,8 +577,15 @@ static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
        .quit   = rsnd_ssi_quit,
        .start  = rsnd_ssi_dma_start,
        .stop   = rsnd_ssi_dma_stop,
+       .fallback = rsnd_ssi_fallback,
 };
 
+int rsnd_ssi_is_dma_mode(struct rsnd_mod *mod)
+{
+       return mod->ops == &rsnd_ssi_dma_ops;
+}
+
+
 /*
  *             Non SSI
  */
@@ -614,7 +679,7 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
                /*
                 * irq
                 */
-               ssi_info->pio_irq = irq_of_parse_and_map(np, 0);
+               ssi_info->irq = irq_of_parse_and_map(np, 0);
 
                /*
                 * DMA
diff --git a/sound/soc/soc-ac97.c b/sound/soc/soc-ac97.c
new file mode 100644 (file)
index 0000000..2e10e9a
--- /dev/null
@@ -0,0 +1,256 @@
+/*
+ * soc-ac97.c  --  ALSA SoC Audio Layer AC97 support
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright (C) 2010 Slimlogic Ltd.
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ *         with code, comments and ideas from :-
+ *         Richard Purdie <richard@openedhand.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/ctype.h>
+#include <linux/delay.h>
+#include <linux/export.h>
+#include <linux/gpio.h>
+#include <linux/init.h>
+#include <linux/of_gpio.h>
+#include <linux/of.h>
+#include <linux/pinctrl/consumer.h>
+#include <linux/slab.h>
+#include <sound/ac97_codec.h>
+#include <sound/soc.h>
+
+struct snd_ac97_reset_cfg {
+       struct pinctrl *pctl;
+       struct pinctrl_state *pstate_reset;
+       struct pinctrl_state *pstate_warm_reset;
+       struct pinctrl_state *pstate_run;
+       int gpio_sdata;
+       int gpio_sync;
+       int gpio_reset;
+};
+
+static struct snd_ac97_bus soc_ac97_bus = {
+       .ops = NULL, /* Gets initialized in snd_soc_set_ac97_ops() */
+};
+
+static void soc_ac97_device_release(struct device *dev)
+{
+       kfree(to_ac97_t(dev));
+}
+
+/**
+ * snd_soc_new_ac97_codec - initailise AC97 device
+ * @codec: audio codec
+ *
+ * Initialises AC97 codec resources for use by ad-hoc devices only.
+ */
+struct snd_ac97 *snd_soc_new_ac97_codec(struct snd_soc_codec *codec)
+{
+       struct snd_ac97 *ac97;
+       int ret;
+
+       ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
+       if (ac97 == NULL)
+               return ERR_PTR(-ENOMEM);
+
+       ac97->bus = &soc_ac97_bus;
+       ac97->num = 0;
+
+       ac97->dev.bus = &ac97_bus_type;
+       ac97->dev.parent = codec->component.card->dev;
+       ac97->dev.release = soc_ac97_device_release;
+
+       dev_set_name(&ac97->dev, "%d-%d:%s",
+                    codec->component.card->snd_card->number, 0,
+                    codec->component.name);
+
+       ret = device_register(&ac97->dev);
+       if (ret) {
+               put_device(&ac97->dev);
+               return ERR_PTR(ret);
+       }
+
+       return ac97;
+}
+EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
+
+/**
+ * snd_soc_free_ac97_codec - free AC97 codec device
+ * @codec: audio codec
+ *
+ * Frees AC97 codec device resources.
+ */
+void snd_soc_free_ac97_codec(struct snd_ac97 *ac97)
+{
+       device_del(&ac97->dev);
+       ac97->bus = NULL;
+       put_device(&ac97->dev);
+}
+EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
+
+static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
+
+static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
+
+       udelay(10);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+       msleep(2);
+}
+
+static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
+{
+       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
+
+       udelay(10);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+       msleep(2);
+}
+
+static int snd_soc_ac97_parse_pinctl(struct device *dev,
+               struct snd_ac97_reset_cfg *cfg)
+{
+       struct pinctrl *p;
+       struct pinctrl_state *state;
+       int gpio;
+       int ret;
+
+       p = devm_pinctrl_get(dev);
+       if (IS_ERR(p)) {
+               dev_err(dev, "Failed to get pinctrl\n");
+               return PTR_ERR(p);
+       }
+       cfg->pctl = p;
+
+       state = pinctrl_lookup_state(p, "ac97-reset");
+       if (IS_ERR(state)) {
+               dev_err(dev, "Can't find pinctrl state ac97-reset\n");
+               return PTR_ERR(state);
+       }
+       cfg->pstate_reset = state;
+
+       state = pinctrl_lookup_state(p, "ac97-warm-reset");
+       if (IS_ERR(state)) {
+               dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
+               return PTR_ERR(state);
+       }
+       cfg->pstate_warm_reset = state;
+
+       state = pinctrl_lookup_state(p, "ac97-running");
+       if (IS_ERR(state)) {
+               dev_err(dev, "Can't find pinctrl state ac97-running\n");
+               return PTR_ERR(state);
+       }
+       cfg->pstate_run = state;
+
+       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
+       if (gpio < 0) {
+               dev_err(dev, "Can't find ac97-sync gpio\n");
+               return gpio;
+       }
+       ret = devm_gpio_request(dev, gpio, "AC97 link sync");
+       if (ret) {
+               dev_err(dev, "Failed requesting ac97-sync gpio\n");
+               return ret;
+       }
+       cfg->gpio_sync = gpio;
+
+       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
+       if (gpio < 0) {
+               dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
+               return gpio;
+       }
+       ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
+       if (ret) {
+               dev_err(dev, "Failed requesting ac97-sdata gpio\n");
+               return ret;
+       }
+       cfg->gpio_sdata = gpio;
+
+       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
+       if (gpio < 0) {
+               dev_err(dev, "Can't find ac97-reset gpio\n");
+               return gpio;
+       }
+       ret = devm_gpio_request(dev, gpio, "AC97 link reset");
+       if (ret) {
+               dev_err(dev, "Failed requesting ac97-reset gpio\n");
+               return ret;
+       }
+       cfg->gpio_reset = gpio;
+
+       return 0;
+}
+
+struct snd_ac97_bus_ops *soc_ac97_ops;
+EXPORT_SYMBOL_GPL(soc_ac97_ops);
+
+int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
+{
+       if (ops == soc_ac97_ops)
+               return 0;
+
+       if (soc_ac97_ops && ops)
+               return -EBUSY;
+
+       soc_ac97_ops = ops;
+       soc_ac97_bus.ops = ops;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
+
+/**
+ * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
+ *
+ * This function sets the reset and warm_reset properties of ops and parses
+ * the device node of pdev to get pinctrl states and gpio numbers to use.
+ */
+int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
+               struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct snd_ac97_reset_cfg cfg;
+       int ret;
+
+       ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_set_ac97_ops(ops);
+       if (ret)
+               return ret;
+
+       ops->warm_reset = snd_soc_ac97_warm_reset;
+       ops->reset = snd_soc_ac97_reset;
+
+       snd_ac97_rst_cfg = cfg;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
index a9f82b5..07f4335 100644 (file)
 #include <linux/export.h>
 #include <linux/slab.h>
 
-#include <trace/events/asoc.h>
-
-static bool snd_soc_set_cache_val(void *base, unsigned int idx,
-                                 unsigned int val, unsigned int word_size)
-{
-       switch (word_size) {
-       case 1: {
-               u8 *cache = base;
-               if (cache[idx] == val)
-                       return true;
-               cache[idx] = val;
-               break;
-       }
-       case 2: {
-               u16 *cache = base;
-               if (cache[idx] == val)
-                       return true;
-               cache[idx] = val;
-               break;
-       }
-       default:
-               WARN(1, "Invalid word_size %d\n", word_size);
-               break;
-       }
-       return false;
-}
-
-static unsigned int snd_soc_get_cache_val(const void *base, unsigned int idx,
-               unsigned int word_size)
-{
-       if (!base)
-               return -1;
-
-       switch (word_size) {
-       case 1: {
-               const u8 *cache = base;
-               return cache[idx];
-       }
-       case 2: {
-               const u16 *cache = base;
-               return cache[idx];
-       }
-       default:
-               WARN(1, "Invalid word_size %d\n", word_size);
-               break;
-       }
-       /* unreachable */
-       return -1;
-}
-
 int snd_soc_cache_init(struct snd_soc_codec *codec)
 {
        const struct snd_soc_codec_driver *codec_drv = codec->driver;
@@ -75,8 +25,6 @@ int snd_soc_cache_init(struct snd_soc_codec *codec)
        if (!reg_size)
                return 0;
 
-       mutex_init(&codec->cache_rw_mutex);
-
        dev_dbg(codec->dev, "ASoC: Initializing cache for %s codec\n",
                                codec->component.name);
 
@@ -103,100 +51,3 @@ int snd_soc_cache_exit(struct snd_soc_codec *codec)
        codec->reg_cache = NULL;
        return 0;
 }
-
-/**
- * snd_soc_cache_read: Fetch the value of a given register from the cache.
- *
- * @codec: CODEC to configure.
- * @reg: The register index.
- * @value: The value to be returned.
- */
-int snd_soc_cache_read(struct snd_soc_codec *codec,
-                      unsigned int reg, unsigned int *value)
-{
-       if (!value)
-               return -EINVAL;
-
-       mutex_lock(&codec->cache_rw_mutex);
-       if (!ZERO_OR_NULL_PTR(codec->reg_cache))
-               *value = snd_soc_get_cache_val(codec->reg_cache, reg,
-                                              codec->driver->reg_word_size);
-       mutex_unlock(&codec->cache_rw_mutex);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_cache_read);
-
-/**
- * snd_soc_cache_write: Set the value of a given register in the cache.
- *
- * @codec: CODEC to configure.
- * @reg: The register index.
- * @value: The new register value.
- */
-int snd_soc_cache_write(struct snd_soc_codec *codec,
-                       unsigned int reg, unsigned int value)
-{
-       mutex_lock(&codec->cache_rw_mutex);
-       if (!ZERO_OR_NULL_PTR(codec->reg_cache))
-               snd_soc_set_cache_val(codec->reg_cache, reg, value,
-                                     codec->driver->reg_word_size);
-       mutex_unlock(&codec->cache_rw_mutex);
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_cache_write);
-
-static int snd_soc_flat_cache_sync(struct snd_soc_codec *codec)
-{
-       int i;
-       int ret;
-       const struct snd_soc_codec_driver *codec_drv;
-       unsigned int val;
-
-       codec_drv = codec->driver;
-       for (i = 0; i < codec_drv->reg_cache_size; ++i) {
-               ret = snd_soc_cache_read(codec, i, &val);
-               if (ret)
-                       return ret;
-               if (codec_drv->reg_cache_default)
-                       if (snd_soc_get_cache_val(codec_drv->reg_cache_default,
-                                                 i, codec_drv->reg_word_size) == val)
-                               continue;
-
-               ret = snd_soc_write(codec, i, val);
-               if (ret)
-                       return ret;
-               dev_dbg(codec->dev, "ASoC: Synced register %#x, value = %#x\n",
-                       i, val);
-       }
-       return 0;
-}
-
-/**
- * snd_soc_cache_sync: Sync the register cache with the hardware.
- *
- * @codec: CODEC to configure.
- *
- * Any registers that should not be synced should be marked as
- * volatile.  In general drivers can choose not to use the provided
- * syncing functionality if they so require.
- */
-int snd_soc_cache_sync(struct snd_soc_codec *codec)
-{
-       const char *name = "flat";
-       int ret;
-
-       if (!codec->cache_sync)
-               return 0;
-
-       dev_dbg(codec->dev, "ASoC: Syncing cache for %s codec\n",
-               codec->component.name);
-       trace_snd_soc_cache_sync(codec, name, "start");
-       ret = snd_soc_flat_cache_sync(codec);
-       if (!ret)
-               codec->cache_sync = 0;
-       trace_snd_soc_cache_sync(codec, name, "end");
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_cache_sync);
index cecfab3..590a82f 100644 (file)
@@ -258,10 +258,7 @@ static int soc_compr_free_fe(struct snd_compr_stream *cstream)
        list_for_each_entry(dpcm, &fe->dpcm[stream].be_clients, list_be)
                dpcm->state = SND_SOC_DPCM_LINK_STATE_FREE;
 
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-               dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
-       else
-               dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
+       dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_STOP);
 
        fe->dpcm[stream].state = SND_SOC_DPCM_STATE_CLOSE;
        fe->dpcm[stream].runtime_update = SND_SOC_DPCM_UPDATE_NO;
@@ -456,11 +453,7 @@ static int soc_compr_set_params_fe(struct snd_compr_stream *cstream,
        if (ret < 0)
                goto out;
 
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
-               dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
-       else
-               dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
-
+       dpcm_dapm_stream_event(fe, stream, SND_SOC_DAPM_STREAM_START);
        fe->dpcm[stream].state = SND_SOC_DPCM_STATE_PREPARE;
 
 out:
index b60ff56..9357210 100644 (file)
@@ -34,9 +34,6 @@
 #include <linux/ctype.h>
 #include <linux/slab.h>
 #include <linux/of.h>
-#include <linux/gpio.h>
-#include <linux/of_gpio.h>
-#include <sound/ac97_codec.h>
 #include <sound/core.h>
 #include <sound/jack.h>
 #include <sound/pcm.h>
@@ -69,16 +66,6 @@ static int pmdown_time = 5000;
 module_param(pmdown_time, int, 0);
 MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
 
-struct snd_ac97_reset_cfg {
-       struct pinctrl *pctl;
-       struct pinctrl_state *pstate_reset;
-       struct pinctrl_state *pstate_warm_reset;
-       struct pinctrl_state *pstate_run;
-       int gpio_sdata;
-       int gpio_sync;
-       int gpio_reset;
-};
-
 /* returns the minimum number of bytes needed to represent
  * a particular given value */
 static int min_bytes_needed(unsigned long val)
@@ -309,9 +296,6 @@ static void soc_init_codec_debugfs(struct snd_soc_component *component)
 {
        struct snd_soc_codec *codec = snd_soc_component_to_codec(component);
 
-       debugfs_create_bool("cache_sync", 0444, codec->component.debugfs_root,
-                           &codec->cache_sync);
-
        codec->debugfs_reg = debugfs_create_file("codec_reg", 0644,
                                                 codec->component.debugfs_root,
                                                 codec, &codec_reg_fops);
@@ -499,40 +483,6 @@ struct snd_soc_pcm_runtime *snd_soc_get_pcm_runtime(struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_GPL(snd_soc_get_pcm_runtime);
 
-#ifdef CONFIG_SND_SOC_AC97_BUS
-/* unregister ac97 codec */
-static int soc_ac97_dev_unregister(struct snd_soc_codec *codec)
-{
-       if (codec->ac97->dev.bus)
-               device_unregister(&codec->ac97->dev);
-       return 0;
-}
-
-/* stop no dev release warning */
-static void soc_ac97_device_release(struct device *dev){}
-
-/* register ac97 codec to bus */
-static int soc_ac97_dev_register(struct snd_soc_codec *codec)
-{
-       int err;
-
-       codec->ac97->dev.bus = &ac97_bus_type;
-       codec->ac97->dev.parent = codec->component.card->dev;
-       codec->ac97->dev.release = soc_ac97_device_release;
-
-       dev_set_name(&codec->ac97->dev, "%d-%d:%s",
-                    codec->component.card->snd_card->number, 0,
-                    codec->component.name);
-       err = device_register(&codec->ac97->dev);
-       if (err < 0) {
-               dev_err(codec->dev, "ASoC: Can't register ac97 bus\n");
-               codec->ac97->dev.bus = NULL;
-               return err;
-       }
-       return 0;
-}
-#endif
-
 static void codec2codec_close_delayed_work(struct work_struct *work)
 {
        /* Currently nothing to do for c2c links
@@ -592,17 +542,12 @@ int snd_soc_suspend(struct device *dev)
 
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-               struct snd_soc_platform *platform = card->rtd[i].platform;
 
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (cpu_dai->driver->suspend && !cpu_dai->driver->ac97_control)
+               if (cpu_dai->driver->suspend && !cpu_dai->driver->bus_control)
                        cpu_dai->driver->suspend(cpu_dai);
-               if (platform->driver->suspend && !platform->suspended) {
-                       platform->driver->suspend(cpu_dai);
-                       platform->suspended = 1;
-               }
        }
 
        /* close any waiting streams and save state */
@@ -629,8 +574,8 @@ int snd_soc_suspend(struct device *dev)
                                          SND_SOC_DAPM_STREAM_SUSPEND);
        }
 
-       /* Recheck all analogue paths too */
-       dapm_mark_io_dirty(&card->dapm);
+       /* Recheck all endpoints too, their state is affected by suspend */
+       dapm_mark_endpoints_dirty(card);
        snd_soc_dapm_sync(&card->dapm);
 
        /* suspend all CODECs */
@@ -656,7 +601,6 @@ int snd_soc_suspend(struct device *dev)
                                if (codec->driver->suspend)
                                        codec->driver->suspend(codec);
                                codec->suspended = 1;
-                               codec->cache_sync = 1;
                                if (codec->component.regmap)
                                        regcache_mark_dirty(codec->component.regmap);
                                /* deactivate pins to sleep state */
@@ -676,7 +620,7 @@ int snd_soc_suspend(struct device *dev)
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (cpu_dai->driver->suspend && cpu_dai->driver->ac97_control)
+               if (cpu_dai->driver->suspend && cpu_dai->driver->bus_control)
                        cpu_dai->driver->suspend(cpu_dai);
 
                /* deactivate pins to sleep state */
@@ -712,14 +656,14 @@ static void soc_resume_deferred(struct work_struct *work)
        if (card->resume_pre)
                card->resume_pre(card);
 
-       /* resume AC97 DAIs */
+       /* resume control bus DAIs */
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
 
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (cpu_dai->driver->resume && cpu_dai->driver->ac97_control)
+               if (cpu_dai->driver->resume && cpu_dai->driver->bus_control)
                        cpu_dai->driver->resume(cpu_dai);
        }
 
@@ -775,17 +719,12 @@ static void soc_resume_deferred(struct work_struct *work)
 
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-               struct snd_soc_platform *platform = card->rtd[i].platform;
 
                if (card->rtd[i].dai_link->ignore_suspend)
                        continue;
 
-               if (cpu_dai->driver->resume && !cpu_dai->driver->ac97_control)
+               if (cpu_dai->driver->resume && !cpu_dai->driver->bus_control)
                        cpu_dai->driver->resume(cpu_dai);
-               if (platform->driver->resume && platform->suspended) {
-                       platform->driver->resume(cpu_dai);
-                       platform->suspended = 0;
-               }
        }
 
        if (card->resume_post)
@@ -796,8 +735,8 @@ static void soc_resume_deferred(struct work_struct *work)
        /* userspace can access us now we are back as we were before */
        snd_power_change_state(card->snd_card, SNDRV_CTL_POWER_D0);
 
-       /* Recheck all analogue paths too */
-       dapm_mark_io_dirty(&card->dapm);
+       /* Recheck all endpoints too, their state is affected by suspend */
+       dapm_mark_endpoints_dirty(card);
        snd_soc_dapm_sync(&card->dapm);
 }
 
@@ -805,7 +744,8 @@ static void soc_resume_deferred(struct work_struct *work)
 int snd_soc_resume(struct device *dev)
 {
        struct snd_soc_card *card = dev_get_drvdata(dev);
-       int i, ac97_control = 0;
+       bool bus_control = false;
+       int i;
 
        /* If the card is not initialized yet there is nothing to do */
        if (!card->instantiated)
@@ -828,17 +768,18 @@ int snd_soc_resume(struct device *dev)
                }
        }
 
-       /* AC97 devices might have other drivers hanging off them so
-        * need to resume immediately.  Other drivers don't have that
-        * problem and may take a substantial amount of time to resume
+       /*
+        * DAIs that also act as the control bus master might have other drivers
+        * hanging off them so need to resume immediately. Other drivers don't
+        * have that problem and may take a substantial amount of time to resume
         * due to I/O costs and anti-pop so handle them out of line.
         */
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_dai *cpu_dai = card->rtd[i].cpu_dai;
-               ac97_control |= cpu_dai->driver->ac97_control;
+               bus_control |= cpu_dai->driver->bus_control;
        }
-       if (ac97_control) {
-               dev_dbg(dev, "ASoC: Resuming AC97 immediately\n");
+       if (bus_control) {
+               dev_dbg(dev, "ASoC: Resuming control bus master immediately\n");
                soc_resume_deferred(&card->deferred_resume_work);
        } else {
                dev_dbg(dev, "ASoC: Scheduling resume work\n");
@@ -1251,25 +1192,22 @@ static int soc_probe_link_components(struct snd_soc_card *card, int num,
        return 0;
 }
 
-static int soc_probe_codec_dai(struct snd_soc_card *card,
-                              struct snd_soc_dai *codec_dai,
-                              int order)
+static int soc_probe_dai(struct snd_soc_dai *dai, int order)
 {
        int ret;
 
-       if (!codec_dai->probed && codec_dai->driver->probe_order == order) {
-               if (codec_dai->driver->probe) {
-                       ret = codec_dai->driver->probe(codec_dai);
+       if (!dai->probed && dai->driver->probe_order == order) {
+               if (dai->driver->probe) {
+                       ret = dai->driver->probe(dai);
                        if (ret < 0) {
-                               dev_err(codec_dai->dev,
-                                       "ASoC: failed to probe CODEC DAI %s: %d\n",
-                                       codec_dai->name, ret);
+                               dev_err(dai->dev,
+                                       "ASoC: failed to probe DAI %s: %d\n",
+                                       dai->name, ret);
                                return ret;
                        }
                }
 
-               /* mark codec_dai as probed and add to card dai list */
-               codec_dai->probed = 1;
+               dai->probed = 1;
        }
 
        return 0;
@@ -1319,40 +1257,22 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
 {
        struct snd_soc_dai_link *dai_link = &card->dai_link[num];
        struct snd_soc_pcm_runtime *rtd = &card->rtd[num];
-       struct snd_soc_platform *platform = rtd->platform;
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        int i, ret;
 
        dev_dbg(card->dev, "ASoC: probe %s dai link %d late %d\n",
                        card->name, num, order);
 
-       /* config components */
-       cpu_dai->platform = platform;
-       cpu_dai->card = card;
-       for (i = 0; i < rtd->num_codecs; i++)
-               rtd->codec_dais[i]->card = card;
-
        /* set default power off timeout */
        rtd->pmdown_time = pmdown_time;
 
-       /* probe the cpu_dai */
-       if (!cpu_dai->probed &&
-                       cpu_dai->driver->probe_order == order) {
-               if (cpu_dai->driver->probe) {
-                       ret = cpu_dai->driver->probe(cpu_dai);
-                       if (ret < 0) {
-                               dev_err(cpu_dai->dev,
-                                       "ASoC: failed to probe CPU DAI %s: %d\n",
-                                       cpu_dai->name, ret);
-                               return ret;
-                       }
-               }
-               cpu_dai->probed = 1;
-       }
+       ret = soc_probe_dai(cpu_dai, order);
+       if (ret)
+               return ret;
 
        /* probe the CODEC DAI */
        for (i = 0; i < rtd->num_codecs; i++) {
-               ret = soc_probe_codec_dai(card, rtd->codec_dais[i], order);
+               ret = soc_probe_dai(rtd->codec_dais[i], order);
                if (ret)
                        return ret;
        }
@@ -1422,84 +1342,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
                }
        }
 
-       /* add platform data for AC97 devices */
-       for (i = 0; i < rtd->num_codecs; i++) {
-               if (rtd->codec_dais[i]->driver->ac97_control)
-                       snd_ac97_dev_add_pdata(rtd->codec_dais[i]->codec->ac97,
-                                              rtd->cpu_dai->ac97_pdata);
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_SND_SOC_AC97_BUS
-static int soc_register_ac97_codec(struct snd_soc_codec *codec,
-                                  struct snd_soc_dai *codec_dai)
-{
-       int ret;
-
-       /* Only instantiate AC97 if not already done by the adaptor
-        * for the generic AC97 subsystem.
-        */
-       if (codec_dai->driver->ac97_control && !codec->ac97_registered) {
-               /*
-                * It is possible that the AC97 device is already registered to
-                * the device subsystem. This happens when the device is created
-                * via snd_ac97_mixer(). Currently only SoC codec that does so
-                * is the generic AC97 glue but others migh emerge.
-                *
-                * In those cases we don't try to register the device again.
-                */
-               if (!codec->ac97_created)
-                       return 0;
-
-               ret = soc_ac97_dev_register(codec);
-               if (ret < 0) {
-                       dev_err(codec->dev,
-                               "ASoC: AC97 device register failed: %d\n", ret);
-                       return ret;
-               }
-
-               codec->ac97_registered = 1;
-       }
-       return 0;
-}
-
-static void soc_unregister_ac97_codec(struct snd_soc_codec *codec)
-{
-       if (codec->ac97_registered) {
-               soc_ac97_dev_unregister(codec);
-               codec->ac97_registered = 0;
-       }
-}
-
-static int soc_register_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
-       int i, ret;
-
-       for (i = 0; i < rtd->num_codecs; i++) {
-               struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
-
-               ret = soc_register_ac97_codec(codec_dai->codec, codec_dai);
-               if (ret) {
-                       while (--i >= 0)
-                               soc_unregister_ac97_codec(codec_dai->codec);
-                       return ret;
-               }
-       }
-
        return 0;
 }
 
-static void soc_unregister_ac97_dai_link(struct snd_soc_pcm_runtime *rtd)
-{
-       int i;
-
-       for (i = 0; i < rtd->num_codecs; i++)
-               soc_unregister_ac97_codec(rtd->codec_dais[i]->codec);
-}
-#endif
-
 static int soc_bind_aux_dev(struct snd_soc_card *card, int num)
 {
        struct snd_soc_pcm_runtime *rtd = &card->rtd_aux[num];
@@ -1793,20 +1638,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                goto probe_aux_dev_err;
        }
 
-#ifdef CONFIG_SND_SOC_AC97_BUS
-       /* register any AC97 codecs */
-       for (i = 0; i < card->num_rtd; i++) {
-               ret = soc_register_ac97_dai_link(&card->rtd[i]);
-               if (ret < 0) {
-                       dev_err(card->dev,
-                               "ASoC: failed to register AC97: %d\n", ret);
-                       while (--i >= 0)
-                               soc_unregister_ac97_dai_link(&card->rtd[i]);
-                       goto probe_aux_dev_err;
-               }
-       }
-#endif
-
        card->instantiated = 1;
        snd_soc_dapm_sync(&card->dapm);
        mutex_unlock(&card->mutex);
@@ -1948,216 +1779,6 @@ static struct platform_driver soc_driver = {
        .remove         = soc_remove,
 };
 
-/**
- * snd_soc_new_ac97_codec - initailise AC97 device
- * @codec: audio codec
- * @ops: AC97 bus operations
- * @num: AC97 codec number
- *
- * Initialises AC97 codec resources for use by ad-hoc devices only.
- */
-int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
-       struct snd_ac97_bus_ops *ops, int num)
-{
-       codec->ac97 = kzalloc(sizeof(struct snd_ac97), GFP_KERNEL);
-       if (codec->ac97 == NULL)
-               return -ENOMEM;
-
-       codec->ac97->bus = kzalloc(sizeof(struct snd_ac97_bus), GFP_KERNEL);
-       if (codec->ac97->bus == NULL) {
-               kfree(codec->ac97);
-               codec->ac97 = NULL;
-               return -ENOMEM;
-       }
-
-       codec->ac97->bus->ops = ops;
-       codec->ac97->num = num;
-
-       /*
-        * Mark the AC97 device to be created by us. This way we ensure that the
-        * device will be registered with the device subsystem later on.
-        */
-       codec->ac97_created = 1;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
-
-static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
-
-static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
-{
-       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
-
-       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
-
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
-
-       udelay(10);
-
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
-
-       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
-       msleep(2);
-}
-
-static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
-{
-       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
-
-       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
-
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
-
-       udelay(10);
-
-       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
-
-       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
-       msleep(2);
-}
-
-static int snd_soc_ac97_parse_pinctl(struct device *dev,
-               struct snd_ac97_reset_cfg *cfg)
-{
-       struct pinctrl *p;
-       struct pinctrl_state *state;
-       int gpio;
-       int ret;
-
-       p = devm_pinctrl_get(dev);
-       if (IS_ERR(p)) {
-               dev_err(dev, "Failed to get pinctrl\n");
-               return PTR_ERR(p);
-       }
-       cfg->pctl = p;
-
-       state = pinctrl_lookup_state(p, "ac97-reset");
-       if (IS_ERR(state)) {
-               dev_err(dev, "Can't find pinctrl state ac97-reset\n");
-               return PTR_ERR(state);
-       }
-       cfg->pstate_reset = state;
-
-       state = pinctrl_lookup_state(p, "ac97-warm-reset");
-       if (IS_ERR(state)) {
-               dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
-               return PTR_ERR(state);
-       }
-       cfg->pstate_warm_reset = state;
-
-       state = pinctrl_lookup_state(p, "ac97-running");
-       if (IS_ERR(state)) {
-               dev_err(dev, "Can't find pinctrl state ac97-running\n");
-               return PTR_ERR(state);
-       }
-       cfg->pstate_run = state;
-
-       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
-       if (gpio < 0) {
-               dev_err(dev, "Can't find ac97-sync gpio\n");
-               return gpio;
-       }
-       ret = devm_gpio_request(dev, gpio, "AC97 link sync");
-       if (ret) {
-               dev_err(dev, "Failed requesting ac97-sync gpio\n");
-               return ret;
-       }
-       cfg->gpio_sync = gpio;
-
-       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
-       if (gpio < 0) {
-               dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
-               return gpio;
-       }
-       ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
-       if (ret) {
-               dev_err(dev, "Failed requesting ac97-sdata gpio\n");
-               return ret;
-       }
-       cfg->gpio_sdata = gpio;
-
-       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
-       if (gpio < 0) {
-               dev_err(dev, "Can't find ac97-reset gpio\n");
-               return gpio;
-       }
-       ret = devm_gpio_request(dev, gpio, "AC97 link reset");
-       if (ret) {
-               dev_err(dev, "Failed requesting ac97-reset gpio\n");
-               return ret;
-       }
-       cfg->gpio_reset = gpio;
-
-       return 0;
-}
-
-struct snd_ac97_bus_ops *soc_ac97_ops;
-EXPORT_SYMBOL_GPL(soc_ac97_ops);
-
-int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
-{
-       if (ops == soc_ac97_ops)
-               return 0;
-
-       if (soc_ac97_ops && ops)
-               return -EBUSY;
-
-       soc_ac97_ops = ops;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
-
-/**
- * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
- *
- * This function sets the reset and warm_reset properties of ops and parses
- * the device node of pdev to get pinctrl states and gpio numbers to use.
- */
-int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
-               struct platform_device *pdev)
-{
-       struct device *dev = &pdev->dev;
-       struct snd_ac97_reset_cfg cfg;
-       int ret;
-
-       ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
-       if (ret)
-               return ret;
-
-       ret = snd_soc_set_ac97_ops(ops);
-       if (ret)
-               return ret;
-
-       ops->warm_reset = snd_soc_ac97_warm_reset;
-       ops->reset = snd_soc_ac97_reset;
-
-       snd_ac97_rst_cfg = cfg;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
-
-/**
- * snd_soc_free_ac97_codec - free AC97 codec device
- * @codec: audio codec
- *
- * Frees AC97 codec device resources.
- */
-void snd_soc_free_ac97_codec(struct snd_soc_codec *codec)
-{
-#ifdef CONFIG_SND_SOC_AC97_BUS
-       soc_unregister_ac97_codec(codec);
-#endif
-       kfree(codec->ac97->bus);
-       kfree(codec->ac97);
-       codec->ac97 = NULL;
-       codec->ac97_created = 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_free_ac97_codec);
-
 /**
  * snd_soc_cnew - create new control
  * @_template: control template
@@ -2326,7 +1947,7 @@ EXPORT_SYMBOL_GPL(snd_soc_add_card_controls);
 int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
        const struct snd_kcontrol_new *controls, int num_controls)
 {
-       struct snd_card *card = dai->card->snd_card;
+       struct snd_card *card = dai->component->card->snd_card;
 
        return snd_soc_add_controls(card, dai->dev, controls, num_controls,
                        NULL, dai);
@@ -2334,1105 +1955,91 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
 EXPORT_SYMBOL_GPL(snd_soc_add_dai_controls);
 
 /**
- * snd_soc_info_enum_double - enumerated double mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a double enumerated
- * mixer control.
+ * snd_soc_dai_set_sysclk - configure DAI system or master clock.
+ * @dai: DAI
+ * @clk_id: DAI specific clock ID
+ * @freq: new clock frequency in Hz
+ * @dir: new clock direction - input/output.
  *
- * Returns 0 for success.
+ * Configures the DAI master (MCLK) or system (SYSCLK) clocking.
  */
-int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
+int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
+       unsigned int freq, int dir)
 {
-       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-       uinfo->count = e->shift_l == e->shift_r ? 1 : 2;
-       uinfo->value.enumerated.items = e->items;
-
-       if (uinfo->value.enumerated.item >= e->items)
-               uinfo->value.enumerated.item = e->items - 1;
-       strlcpy(uinfo->value.enumerated.name,
-               e->texts[uinfo->value.enumerated.item],
-               sizeof(uinfo->value.enumerated.name));
-       return 0;
+       if (dai->driver && dai->driver->ops->set_sysclk)
+               return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir);
+       else if (dai->codec && dai->codec->driver->set_sysclk)
+               return dai->codec->driver->set_sysclk(dai->codec, clk_id, 0,
+                                                     freq, dir);
+       else
+               return -ENOTSUPP;
 }
-EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
 
 /**
- * snd_soc_get_enum_double - enumerated double mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a double enumerated mixer.
+ * snd_soc_codec_set_sysclk - configure CODEC system or master clock.
+ * @codec: CODEC
+ * @clk_id: DAI specific clock ID
+ * @source: Source for the clock
+ * @freq: new clock frequency in Hz
+ * @dir: new clock direction - input/output.
  *
- * Returns 0 for success.
+ * Configures the CODEC master (MCLK) or system (SYSCLK) clocking.
  */
-int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
+int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+                            int source, unsigned int freq, int dir)
 {
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned int val, item;
-       unsigned int reg_val;
-       int ret;
-
-       ret = snd_soc_component_read(component, e->reg, &reg_val);
-       if (ret)
-               return ret;
-       val = (reg_val >> e->shift_l) & e->mask;
-       item = snd_soc_enum_val_to_item(e, val);
-       ucontrol->value.enumerated.item[0] = item;
-       if (e->shift_l != e->shift_r) {
-               val = (reg_val >> e->shift_l) & e->mask;
-               item = snd_soc_enum_val_to_item(e, val);
-               ucontrol->value.enumerated.item[1] = item;
-       }
-
-       return 0;
+       if (codec->driver->set_sysclk)
+               return codec->driver->set_sysclk(codec, clk_id, source,
+                                                freq, dir);
+       else
+               return -ENOTSUPP;
 }
-EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
+EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk);
 
 /**
- * snd_soc_put_enum_double - enumerated double mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a double enumerated mixer.
+ * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
+ * @dai: DAI
+ * @div_id: DAI specific clock divider ID
+ * @div: new clock divisor.
  *
- * Returns 0 for success.
+ * Configures the clock dividers. This is used to derive the best DAI bit and
+ * frame clocks from the system or master clock. It's best to set the DAI bit
+ * and frame clocks as low as possible to save system power.
  */
-int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
+int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
+       int div_id, int div)
 {
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-       unsigned int *item = ucontrol->value.enumerated.item;
-       unsigned int val;
-       unsigned int mask;
-
-       if (item[0] >= e->items)
+       if (dai->driver && dai->driver->ops->set_clkdiv)
+               return dai->driver->ops->set_clkdiv(dai, div_id, div);
+       else
                return -EINVAL;
-       val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
-       mask = e->mask << e->shift_l;
-       if (e->shift_l != e->shift_r) {
-               if (item[1] >= e->items)
-                       return -EINVAL;
-               val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
-               mask |= e->mask << e->shift_r;
-       }
-
-       return snd_soc_component_update_bits(component, e->reg, mask, val);
 }
-EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
 
 /**
- * snd_soc_read_signed - Read a codec register and interprete as signed value
- * @component: component
- * @reg: Register to read
- * @mask: Mask to use after shifting the register value
- * @shift: Right shift of register value
- * @sign_bit: Bit that describes if a number is negative or not.
- * @signed_val: Pointer to where the read value should be stored
- *
- * This functions reads a codec register. The register value is shifted right
- * by 'shift' bits and masked with the given 'mask'. Afterwards it translates
- * the given registervalue into a signed integer if sign_bit is non-zero.
+ * snd_soc_dai_set_pll - configure DAI PLL.
+ * @dai: DAI
+ * @pll_id: DAI specific PLL ID
+ * @source: DAI specific source for the PLL
+ * @freq_in: PLL input clock frequency in Hz
+ * @freq_out: requested PLL output clock frequency in Hz
  *
- * Returns 0 on sucess, otherwise an error value
+ * Configures and enables PLL to generate output clock based on input clock.
  */
-static int snd_soc_read_signed(struct snd_soc_component *component,
-       unsigned int reg, unsigned int mask, unsigned int shift,
-       unsigned int sign_bit, int *signed_val)
+int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
+       unsigned int freq_in, unsigned int freq_out)
 {
-       int ret;
-       unsigned int val;
-
-       ret = snd_soc_component_read(component, reg, &val);
-       if (ret < 0)
-               return ret;
-
-       val = (val >> shift) & mask;
-
-       if (!sign_bit) {
-               *signed_val = val;
-               return 0;
-       }
-
-       /* non-negative number */
-       if (!(val & BIT(sign_bit))) {
-               *signed_val = val;
-               return 0;
-       }
-
-       ret = val;
-
-       /*
-        * The register most probably does not contain a full-sized int.
-        * Instead we have an arbitrary number of bits in a signed
-        * representation which has to be translated into a full-sized int.
-        * This is done by filling up all bits above the sign-bit.
-        */
-       ret |= ~((int)(BIT(sign_bit) - 1));
-
-       *signed_val = ret;
-
-       return 0;
-}
-
-/**
- * snd_soc_info_volsw - single mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a single mixer control, or a double
- * mixer control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       int platform_max;
-
-       if (!mc->platform_max)
-               mc->platform_max = mc->max;
-       platform_max = mc->platform_max;
-
-       if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-       else
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-
-       uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = platform_max - mc->min;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
-
-/**
- * snd_soc_get_volsw - single mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       unsigned int rshift = mc->rshift;
-       int max = mc->max;
-       int min = mc->min;
-       int sign_bit = mc->sign_bit;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-       int val;
-       int ret;
-
-       if (sign_bit)
-               mask = BIT(sign_bit + 1) - 1;
-
-       ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
-       if (ret)
-               return ret;
-
-       ucontrol->value.integer.value[0] = val - min;
-       if (invert)
-               ucontrol->value.integer.value[0] =
-                       max - ucontrol->value.integer.value[0];
-
-       if (snd_soc_volsw_is_stereo(mc)) {
-               if (reg == reg2)
-                       ret = snd_soc_read_signed(component, reg, mask, rshift,
-                               sign_bit, &val);
-               else
-                       ret = snd_soc_read_signed(component, reg2, mask, shift,
-                               sign_bit, &val);
-               if (ret)
-                       return ret;
-
-               ucontrol->value.integer.value[1] = val - min;
-               if (invert)
-                       ucontrol->value.integer.value[1] =
-                               max - ucontrol->value.integer.value[1];
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
-
-/**
- * snd_soc_put_volsw - single mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       unsigned int rshift = mc->rshift;
-       int max = mc->max;
-       int min = mc->min;
-       unsigned int sign_bit = mc->sign_bit;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-       int err;
-       bool type_2r = false;
-       unsigned int val2 = 0;
-       unsigned int val, val_mask;
-
-       if (sign_bit)
-               mask = BIT(sign_bit + 1) - 1;
-
-       val = ((ucontrol->value.integer.value[0] + min) & mask);
-       if (invert)
-               val = max - val;
-       val_mask = mask << shift;
-       val = val << shift;
-       if (snd_soc_volsw_is_stereo(mc)) {
-               val2 = ((ucontrol->value.integer.value[1] + min) & mask);
-               if (invert)
-                       val2 = max - val2;
-               if (reg == reg2) {
-                       val_mask |= mask << rshift;
-                       val |= val2 << rshift;
-               } else {
-                       val2 = val2 << shift;
-                       type_2r = true;
-               }
-       }
-       err = snd_soc_component_update_bits(component, reg, val_mask, val);
-       if (err < 0)
-               return err;
-
-       if (type_2r)
-               err = snd_soc_component_update_bits(component, reg2, val_mask,
-                       val2);
-
-       return err;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
-
-/**
- * snd_soc_get_volsw_sx - single mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a single mixer control, or a double mixer
- * control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
-                     struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-           (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       unsigned int rshift = mc->rshift;
-       int max = mc->max;
-       int min = mc->min;
-       int mask = (1 << (fls(min + max) - 1)) - 1;
-       unsigned int val;
-       int ret;
-
-       ret = snd_soc_component_read(component, reg, &val);
-       if (ret < 0)
-               return ret;
-
-       ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
-
-       if (snd_soc_volsw_is_stereo(mc)) {
-               ret = snd_soc_component_read(component, reg2, &val);
-               if (ret < 0)
-                       return ret;
-
-               val = ((val >> rshift) - min) & mask;
-               ucontrol->value.integer.value[1] = val;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
-
-/**
- * snd_soc_put_volsw_sx - double mixer set callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to set the value of a double mixer control that spans 2 registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
-                        struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-           (struct soc_mixer_control *)kcontrol->private_value;
-
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       unsigned int rshift = mc->rshift;
-       int max = mc->max;
-       int min = mc->min;
-       int mask = (1 << (fls(min + max) - 1)) - 1;
-       int err = 0;
-       unsigned int val, val_mask, val2 = 0;
-
-       val_mask = mask << shift;
-       val = (ucontrol->value.integer.value[0] + min) & mask;
-       val = val << shift;
-
-       err = snd_soc_component_update_bits(component, reg, val_mask, val);
-       if (err < 0)
-               return err;
-
-       if (snd_soc_volsw_is_stereo(mc)) {
-               val_mask = mask << rshift;
-               val2 = (ucontrol->value.integer.value[1] + min) & mask;
-               val2 = val2 << rshift;
-
-               err = snd_soc_component_update_bits(component, reg2, val_mask,
-                       val2);
-       }
-       return err;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
-
-/**
- * snd_soc_info_volsw_s8 - signed mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw_s8(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       int platform_max;
-       int min = mc->min;
-
-       if (!mc->platform_max)
-               mc->platform_max = mc->max;
-       platform_max = mc->platform_max;
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = 2;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = platform_max - min;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_s8);
-
-/**
- * snd_soc_get_volsw_s8 - signed mixer get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value of a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_s8(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       unsigned int reg = mc->reg;
-       unsigned int val;
-       int min = mc->min;
-       int ret;
-
-       ret = snd_soc_component_read(component, reg, &val);
-       if (ret)
-               return ret;
-
-       ucontrol->value.integer.value[0] =
-               ((signed char)(val & 0xff))-min;
-       ucontrol->value.integer.value[1] =
-               ((signed char)((val >> 8) & 0xff))-min;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_s8);
-
-/**
- * snd_soc_put_volsw_sgn - signed mixer put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value of a signed mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_s8(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       unsigned int reg = mc->reg;
-       int min = mc->min;
-       unsigned int val;
-
-       val = (ucontrol->value.integer.value[0]+min) & 0xff;
-       val |= ((ucontrol->value.integer.value[1]+min) & 0xff) << 8;
-
-       return snd_soc_component_update_bits(component, reg, 0xffff, val);
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_s8);
-
-/**
- * snd_soc_info_volsw_range - single mixer info callback with range.
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information, within a range, about a single
- * mixer control.
- *
- * returns 0 for success.
- */
-int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       int platform_max;
-       int min = mc->min;
-
-       if (!mc->platform_max)
-               mc->platform_max = mc->max;
-       platform_max = mc->platform_max;
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = platform_max - min;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range);
-
-/**
- * snd_soc_put_volsw_range - single mixer put value callback with range.
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to set the value, within a range, for a single mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       unsigned int reg = mc->reg;
-       unsigned int rreg = mc->rreg;
-       unsigned int shift = mc->shift;
-       int min = mc->min;
-       int max = mc->max;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-       unsigned int val, val_mask;
-       int ret;
-
-       if (invert)
-               val = (max - ucontrol->value.integer.value[0]) & mask;
-       else
-               val = ((ucontrol->value.integer.value[0] + min) & mask);
-       val_mask = mask << shift;
-       val = val << shift;
-
-       ret = snd_soc_component_update_bits(component, reg, val_mask, val);
-       if (ret < 0)
-               return ret;
-
-       if (snd_soc_volsw_is_stereo(mc)) {
-               if (invert)
-                       val = (max - ucontrol->value.integer.value[1]) & mask;
-               else
-                       val = ((ucontrol->value.integer.value[1] + min) & mask);
-               val_mask = mask << shift;
-               val = val << shift;
-
-               ret = snd_soc_component_update_bits(component, rreg, val_mask,
-                       val);
-       }
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
-
-/**
- * snd_soc_get_volsw_range - single mixer get callback with range
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback to get the value, within a range, of a single mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int rreg = mc->rreg;
-       unsigned int shift = mc->shift;
-       int min = mc->min;
-       int max = mc->max;
-       unsigned int mask = (1 << fls(max)) - 1;
-       unsigned int invert = mc->invert;
-       unsigned int val;
-       int ret;
-
-       ret = snd_soc_component_read(component, reg, &val);
-       if (ret)
-               return ret;
-
-       ucontrol->value.integer.value[0] = (val >> shift) & mask;
-       if (invert)
-               ucontrol->value.integer.value[0] =
-                       max - ucontrol->value.integer.value[0];
-       else
-               ucontrol->value.integer.value[0] =
-                       ucontrol->value.integer.value[0] - min;
-
-       if (snd_soc_volsw_is_stereo(mc)) {
-               ret = snd_soc_component_read(component, rreg, &val);
-               if (ret)
-                       return ret;
-
-               ucontrol->value.integer.value[1] = (val >> shift) & mask;
-               if (invert)
-                       ucontrol->value.integer.value[1] =
-                               max - ucontrol->value.integer.value[1];
-               else
-                       ucontrol->value.integer.value[1] =
-                               ucontrol->value.integer.value[1] - min;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
-
-/**
- * snd_soc_limit_volume - Set new limit to an existing volume control.
- *
- * @codec: where to look for the control
- * @name: Name of the control
- * @max: new maximum limit
- *
- * Return 0 for success, else error.
- */
-int snd_soc_limit_volume(struct snd_soc_codec *codec,
-       const char *name, int max)
-{
-       struct snd_card *card = codec->component.card->snd_card;
-       struct snd_kcontrol *kctl;
-       struct soc_mixer_control *mc;
-       int found = 0;
-       int ret = -EINVAL;
-
-       /* Sanity check for name and max */
-       if (unlikely(!name || max <= 0))
-               return -EINVAL;
-
-       list_for_each_entry(kctl, &card->controls, list) {
-               if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
-                       found = 1;
-                       break;
-               }
-       }
-       if (found) {
-               mc = (struct soc_mixer_control *)kctl->private_value;
-               if (max <= mc->max) {
-                       mc->platform_max = max;
-                       ret = 0;
-               }
-       }
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
-
-int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
-                      struct snd_ctl_elem_info *uinfo)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_bytes *params = (void *)kcontrol->private_value;
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
-       uinfo->count = params->num_regs * component->val_bytes;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
-
-int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
-                     struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_bytes *params = (void *)kcontrol->private_value;
-       int ret;
-
-       if (component->regmap)
-               ret = regmap_raw_read(component->regmap, params->base,
-                                     ucontrol->value.bytes.data,
-                                     params->num_regs * component->val_bytes);
-       else
-               ret = -EINVAL;
-
-       /* Hide any masked bytes to ensure consistent data reporting */
-       if (ret == 0 && params->mask) {
-               switch (component->val_bytes) {
-               case 1:
-                       ucontrol->value.bytes.data[0] &= ~params->mask;
-                       break;
-               case 2:
-                       ((u16 *)(&ucontrol->value.bytes.data))[0]
-                               &= cpu_to_be16(~params->mask);
-                       break;
-               case 4:
-                       ((u32 *)(&ucontrol->value.bytes.data))[0]
-                               &= cpu_to_be32(~params->mask);
-                       break;
-               default:
-                       return -EINVAL;
-               }
-       }
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
-
-int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
-                     struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_bytes *params = (void *)kcontrol->private_value;
-       int ret, len;
-       unsigned int val, mask;
-       void *data;
-
-       if (!component->regmap || !params->num_regs)
-               return -EINVAL;
-
-       len = params->num_regs * component->val_bytes;
-
-       data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
-       if (!data)
-               return -ENOMEM;
-
-       /*
-        * If we've got a mask then we need to preserve the register
-        * bits.  We shouldn't modify the incoming data so take a
-        * copy.
-        */
-       if (params->mask) {
-               ret = regmap_read(component->regmap, params->base, &val);
-               if (ret != 0)
-                       goto out;
-
-               val &= params->mask;
-
-               switch (component->val_bytes) {
-               case 1:
-                       ((u8 *)data)[0] &= ~params->mask;
-                       ((u8 *)data)[0] |= val;
-                       break;
-               case 2:
-                       mask = ~params->mask;
-                       ret = regmap_parse_val(component->regmap,
-                                                       &mask, &mask);
-                       if (ret != 0)
-                               goto out;
-
-                       ((u16 *)data)[0] &= mask;
-
-                       ret = regmap_parse_val(component->regmap,
-                                                       &val, &val);
-                       if (ret != 0)
-                               goto out;
-
-                       ((u16 *)data)[0] |= val;
-                       break;
-               case 4:
-                       mask = ~params->mask;
-                       ret = regmap_parse_val(component->regmap,
-                                                       &mask, &mask);
-                       if (ret != 0)
-                               goto out;
-
-                       ((u32 *)data)[0] &= mask;
-
-                       ret = regmap_parse_val(component->regmap,
-                                                       &val, &val);
-                       if (ret != 0)
-                               goto out;
-
-                       ((u32 *)data)[0] |= val;
-                       break;
-               default:
-                       ret = -EINVAL;
-                       goto out;
-               }
-       }
-
-       ret = regmap_raw_write(component->regmap, params->base,
-                              data, len);
-
-out:
-       kfree(data);
-
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
-
-int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
-                       struct snd_ctl_elem_info *ucontrol)
-{
-       struct soc_bytes_ext *params = (void *)kcontrol->private_value;
-
-       ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
-       ucontrol->count = params->max;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
-
-int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag,
-                               unsigned int size, unsigned int __user *tlv)
-{
-       struct soc_bytes_ext *params = (void *)kcontrol->private_value;
-       unsigned int count = size < params->max ? size : params->max;
-       int ret = -ENXIO;
-
-       switch (op_flag) {
-       case SNDRV_CTL_TLV_OP_READ:
-               if (params->get)
-                       ret = params->get(tlv, count);
-               break;
-       case SNDRV_CTL_TLV_OP_WRITE:
-               if (params->put)
-                       ret = params->put(tlv, count);
-               break;
-       }
-       return ret;
-}
-EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback);
-
-/**
- * snd_soc_info_xr_sx - signed multi register info callback
- * @kcontrol: mreg control
- * @uinfo: control element information
- *
- * Callback to provide information of a control that can
- * span multiple codec registers which together
- * forms a single signed value in a MSB/LSB manner.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
-{
-       struct soc_mreg_control *mc =
-               (struct soc_mreg_control *)kcontrol->private_value;
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-       uinfo->count = 1;
-       uinfo->value.integer.min = mc->min;
-       uinfo->value.integer.max = mc->max;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
-
-/**
- * snd_soc_get_xr_sx - signed multi register get callback
- * @kcontrol: mreg control
- * @ucontrol: control element information
- *
- * Callback to get the value of a control that can span
- * multiple codec registers which together forms a single
- * signed value in a MSB/LSB manner. The control supports
- * specifying total no of bits used to allow for bitfields
- * across the multiple codec registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mreg_control *mc =
-               (struct soc_mreg_control *)kcontrol->private_value;
-       unsigned int regbase = mc->regbase;
-       unsigned int regcount = mc->regcount;
-       unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
-       unsigned int regwmask = (1<<regwshift)-1;
-       unsigned int invert = mc->invert;
-       unsigned long mask = (1UL<<mc->nbits)-1;
-       long min = mc->min;
-       long max = mc->max;
-       long val = 0;
-       unsigned int regval;
-       unsigned int i;
-       int ret;
-
-       for (i = 0; i < regcount; i++) {
-               ret = snd_soc_component_read(component, regbase+i, &regval);
-               if (ret)
-                       return ret;
-               val |= (regval & regwmask) << (regwshift*(regcount-i-1));
-       }
-       val &= mask;
-       if (min < 0 && val > max)
-               val |= ~mask;
-       if (invert)
-               val = max - val;
-       ucontrol->value.integer.value[0] = val;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
-
-/**
- * snd_soc_put_xr_sx - signed multi register get callback
- * @kcontrol: mreg control
- * @ucontrol: control element information
- *
- * Callback to set the value of a control that can span
- * multiple codec registers which together forms a single
- * signed value in a MSB/LSB manner. The control supports
- * specifying total no of bits used to allow for bitfields
- * across the multiple codec registers.
- *
- * Returns 0 for success.
- */
-int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mreg_control *mc =
-               (struct soc_mreg_control *)kcontrol->private_value;
-       unsigned int regbase = mc->regbase;
-       unsigned int regcount = mc->regcount;
-       unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
-       unsigned int regwmask = (1<<regwshift)-1;
-       unsigned int invert = mc->invert;
-       unsigned long mask = (1UL<<mc->nbits)-1;
-       long max = mc->max;
-       long val = ucontrol->value.integer.value[0];
-       unsigned int i, regval, regmask;
-       int err;
-
-       if (invert)
-               val = max - val;
-       val &= mask;
-       for (i = 0; i < regcount; i++) {
-               regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
-               regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
-               err = snd_soc_component_update_bits(component, regbase+i,
-                               regmask, regval);
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
-
-/**
- * snd_soc_get_strobe - strobe get callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback get the value of a strobe mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int shift = mc->shift;
-       unsigned int mask = 1 << shift;
-       unsigned int invert = mc->invert != 0;
-       unsigned int val;
-       int ret;
-
-       ret = snd_soc_component_read(component, reg, &val);
-       if (ret)
-               return ret;
-
-       val &= mask;
-
-       if (shift != 0 && val != 0)
-               val = val >> shift;
-       ucontrol->value.enumerated.item[0] = val ^ invert;
-
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
-
-/**
- * snd_soc_put_strobe - strobe put callback
- * @kcontrol: mixer control
- * @ucontrol: control element information
- *
- * Callback strobe a register bit to high then low (or the inverse)
- * in one pass of a single mixer enum control.
- *
- * Returns 1 for success.
- */
-int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_value *ucontrol)
-{
-       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int shift = mc->shift;
-       unsigned int mask = 1 << shift;
-       unsigned int invert = mc->invert != 0;
-       unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
-       unsigned int val1 = (strobe ^ invert) ? mask : 0;
-       unsigned int val2 = (strobe ^ invert) ? 0 : mask;
-       int err;
-
-       err = snd_soc_component_update_bits(component, reg, mask, val1);
-       if (err < 0)
-               return err;
-
-       return snd_soc_component_update_bits(component, reg, mask, val2);
-}
-EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
-
-/**
- * snd_soc_dai_set_sysclk - configure DAI system or master clock.
- * @dai: DAI
- * @clk_id: DAI specific clock ID
- * @freq: new clock frequency in Hz
- * @dir: new clock direction - input/output.
- *
- * Configures the DAI master (MCLK) or system (SYSCLK) clocking.
- */
-int snd_soc_dai_set_sysclk(struct snd_soc_dai *dai, int clk_id,
-       unsigned int freq, int dir)
-{
-       if (dai->driver && dai->driver->ops->set_sysclk)
-               return dai->driver->ops->set_sysclk(dai, clk_id, freq, dir);
-       else if (dai->codec && dai->codec->driver->set_sysclk)
-               return dai->codec->driver->set_sysclk(dai->codec, clk_id, 0,
-                                                     freq, dir);
-       else
-               return -ENOTSUPP;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dai_set_sysclk);
-
-/**
- * snd_soc_codec_set_sysclk - configure CODEC system or master clock.
- * @codec: CODEC
- * @clk_id: DAI specific clock ID
- * @source: Source for the clock
- * @freq: new clock frequency in Hz
- * @dir: new clock direction - input/output.
- *
- * Configures the CODEC master (MCLK) or system (SYSCLK) clocking.
- */
-int snd_soc_codec_set_sysclk(struct snd_soc_codec *codec, int clk_id,
-                            int source, unsigned int freq, int dir)
-{
-       if (codec->driver->set_sysclk)
-               return codec->driver->set_sysclk(codec, clk_id, source,
-                                                freq, dir);
-       else
-               return -ENOTSUPP;
-}
-EXPORT_SYMBOL_GPL(snd_soc_codec_set_sysclk);
-
-/**
- * snd_soc_dai_set_clkdiv - configure DAI clock dividers.
- * @dai: DAI
- * @div_id: DAI specific clock divider ID
- * @div: new clock divisor.
- *
- * Configures the clock dividers. This is used to derive the best DAI bit and
- * frame clocks from the system or master clock. It's best to set the DAI bit
- * and frame clocks as low as possible to save system power.
- */
-int snd_soc_dai_set_clkdiv(struct snd_soc_dai *dai,
-       int div_id, int div)
-{
-       if (dai->driver && dai->driver->ops->set_clkdiv)
-               return dai->driver->ops->set_clkdiv(dai, div_id, div);
-       else
-               return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dai_set_clkdiv);
-
-/**
- * snd_soc_dai_set_pll - configure DAI PLL.
- * @dai: DAI
- * @pll_id: DAI specific PLL ID
- * @source: DAI specific source for the PLL
- * @freq_in: PLL input clock frequency in Hz
- * @freq_out: requested PLL output clock frequency in Hz
- *
- * Configures and enables PLL to generate output clock based on input clock.
- */
-int snd_soc_dai_set_pll(struct snd_soc_dai *dai, int pll_id, int source,
-       unsigned int freq_in, unsigned int freq_out)
-{
-       if (dai->driver && dai->driver->ops->set_pll)
-               return dai->driver->ops->set_pll(dai, pll_id, source,
-                                        freq_in, freq_out);
-       else if (dai->codec && dai->codec->driver->set_pll)
-               return dai->codec->driver->set_pll(dai->codec, pll_id, source,
-                                                  freq_in, freq_out);
-       else
-               return -EINVAL;
-}
-EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
+       if (dai->driver && dai->driver->ops->set_pll)
+               return dai->driver->ops->set_pll(dai, pll_id, source,
+                                        freq_in, freq_out);
+       else if (dai->codec && dai->codec->driver->set_pll)
+               return dai->codec->driver->set_pll(dai->codec, pll_id, source,
+                                                  freq_in, freq_out);
+       else
+               return -EINVAL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dai_set_pll);
 
 /*
  * snd_soc_codec_set_pll - configure codec PLL.
@@ -3996,22 +2603,62 @@ static int snd_soc_component_initialize(struct snd_soc_component *component,
        return 0;
 }
 
-static void snd_soc_component_init_regmap(struct snd_soc_component *component)
+static void snd_soc_component_setup_regmap(struct snd_soc_component *component)
 {
-       if (!component->regmap)
-               component->regmap = dev_get_regmap(component->dev, NULL);
-       if (component->regmap) {
-               int val_bytes = regmap_get_val_bytes(component->regmap);
-               /* Errors are legitimate for non-integer byte multiples */
-               if (val_bytes > 0)
-                       component->val_bytes = val_bytes;
-       }
+       int val_bytes = regmap_get_val_bytes(component->regmap);
+
+       /* Errors are legitimate for non-integer byte multiples */
+       if (val_bytes > 0)
+               component->val_bytes = val_bytes;
+}
+
+#ifdef CONFIG_REGMAP
+
+/**
+ * snd_soc_component_init_regmap() - Initialize regmap instance for the component
+ * @component: The component for which to initialize the regmap instance
+ * @regmap: The regmap instance that should be used by the component
+ *
+ * This function allows deferred assignment of the regmap instance that is
+ * associated with the component. Only use this if the regmap instance is not
+ * yet ready when the component is registered. The function must also be called
+ * before the first IO attempt of the component.
+ */
+void snd_soc_component_init_regmap(struct snd_soc_component *component,
+       struct regmap *regmap)
+{
+       component->regmap = regmap;
+       snd_soc_component_setup_regmap(component);
 }
+EXPORT_SYMBOL_GPL(snd_soc_component_init_regmap);
+
+/**
+ * snd_soc_component_exit_regmap() - De-initialize regmap instance for the component
+ * @component: The component for which to de-initialize the regmap instance
+ *
+ * Calls regmap_exit() on the regmap instance associated to the component and
+ * removes the regmap instance from the component.
+ *
+ * This function should only be used if snd_soc_component_init_regmap() was used
+ * to initialize the regmap instance.
+ */
+void snd_soc_component_exit_regmap(struct snd_soc_component *component)
+{
+       regmap_exit(component->regmap);
+       component->regmap = NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_component_exit_regmap);
+
+#endif
 
 static void snd_soc_component_add_unlocked(struct snd_soc_component *component)
 {
-       if (!component->write && !component->read)
-               snd_soc_component_init_regmap(component);
+       if (!component->write && !component->read) {
+               if (!component->regmap)
+                       component->regmap = dev_get_regmap(component->dev, NULL);
+               if (component->regmap)
+                       snd_soc_component_setup_regmap(component);
+       }
 
        list_add(&component->list, &component_list);
 }
@@ -4362,7 +3009,6 @@ int snd_soc_register_codec(struct device *dev,
        codec->dev = dev;
        codec->driver = codec_drv;
        codec->component.val_bytes = codec_drv->reg_word_size;
-       mutex_init(&codec->mutex);
 
 #ifdef CONFIG_DEBUG_FS
        codec->component.init_debugfs = soc_init_codec_debugfs;
@@ -4585,7 +3231,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                                   const char *propname)
 {
        struct device_node *np = card->dev->of_node;
-       int num_routes;
+       int num_routes, old_routes;
        struct snd_soc_dapm_route *routes;
        int i, ret;
 
@@ -4603,7 +3249,9 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                return -EINVAL;
        }
 
-       routes = devm_kzalloc(card->dev, num_routes * sizeof(*routes),
+       old_routes = card->num_dapm_routes;
+       routes = devm_kzalloc(card->dev,
+                             (old_routes + num_routes) * sizeof(*routes),
                              GFP_KERNEL);
        if (!routes) {
                dev_err(card->dev,
@@ -4611,9 +3259,11 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                return -EINVAL;
        }
 
+       memcpy(routes, card->dapm_routes, old_routes * sizeof(*routes));
+
        for (i = 0; i < num_routes; i++) {
                ret = of_property_read_string_index(np, propname,
-                       2 * i, &routes[i].sink);
+                       2 * i, &routes[old_routes + i].sink);
                if (ret) {
                        dev_err(card->dev,
                                "ASoC: Property '%s' index %d could not be read: %d\n",
@@ -4621,7 +3271,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                        return -EINVAL;
                }
                ret = of_property_read_string_index(np, propname,
-                       (2 * i) + 1, &routes[i].source);
+                       (2 * i) + 1, &routes[old_routes + i].source);
                if (ret) {
                        dev_err(card->dev,
                                "ASoC: Property '%s' index %d could not be read: %d\n",
@@ -4630,7 +3280,7 @@ int snd_soc_of_parse_audio_routing(struct snd_soc_card *card,
                }
        }
 
-       card->num_dapm_routes = num_routes;
+       card->num_dapm_routes += num_routes;
        card->dapm_routes = routes;
 
        return 0;
@@ -4750,36 +3400,30 @@ unsigned int snd_soc_of_parse_daifmt(struct device_node *np,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_parse_daifmt);
 
-int snd_soc_of_get_dai_name(struct device_node *of_node,
-                           const char **dai_name)
+static int snd_soc_get_dai_name(struct of_phandle_args *args,
+                               const char **dai_name)
 {
        struct snd_soc_component *pos;
-       struct of_phandle_args args;
-       int ret;
-
-       ret = of_parse_phandle_with_args(of_node, "sound-dai",
-                                        "#sound-dai-cells", 0, &args);
-       if (ret)
-               return ret;
-
-       ret = -EPROBE_DEFER;
+       int ret = -EPROBE_DEFER;
 
        mutex_lock(&client_mutex);
        list_for_each_entry(pos, &component_list, list) {
-               if (pos->dev->of_node != args.np)
+               if (pos->dev->of_node != args->np)
                        continue;
 
                if (pos->driver->of_xlate_dai_name) {
-                       ret = pos->driver->of_xlate_dai_name(pos, &args, dai_name);
+                       ret = pos->driver->of_xlate_dai_name(pos,
+                                                            args,
+                                                            dai_name);
                } else {
                        int id = -1;
 
-                       switch (args.args_count) {
+                       switch (args->args_count) {
                        case 0:
                                id = 0; /* same as dai_drv[0] */
                                break;
                        case 1:
-                               id = args.args[0];
+                               id = args->args[0];
                                break;
                        default:
                                /* not supported */
@@ -4801,6 +3445,21 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
                break;
        }
        mutex_unlock(&client_mutex);
+       return ret;
+}
+
+int snd_soc_of_get_dai_name(struct device_node *of_node,
+                           const char **dai_name)
+{
+       struct of_phandle_args args;
+       int ret;
+
+       ret = of_parse_phandle_with_args(of_node, "sound-dai",
+                                        "#sound-dai-cells", 0, &args);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_get_dai_name(&args, dai_name);
 
        of_node_put(args.np);
 
@@ -4808,6 +3467,77 @@ int snd_soc_of_get_dai_name(struct device_node *of_node,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_name);
 
+/*
+ * snd_soc_of_get_dai_link_codecs - Parse a list of CODECs in the devicetree
+ * @dev: Card device
+ * @of_node: Device node
+ * @dai_link: DAI link
+ *
+ * Builds an array of CODEC DAI components from the DAI link property
+ * 'sound-dai'.
+ * The array is set in the DAI link and the number of DAIs is set accordingly.
+ * The device nodes in the array (of_node) must be dereferenced by the caller.
+ *
+ * Returns 0 for success
+ */
+int snd_soc_of_get_dai_link_codecs(struct device *dev,
+                                  struct device_node *of_node,
+                                  struct snd_soc_dai_link *dai_link)
+{
+       struct of_phandle_args args;
+       struct snd_soc_dai_link_component *component;
+       char *name;
+       int index, num_codecs, ret;
+
+       /* Count the number of CODECs */
+       name = "sound-dai";
+       num_codecs = of_count_phandle_with_args(of_node, name,
+                                               "#sound-dai-cells");
+       if (num_codecs <= 0) {
+               if (num_codecs == -ENOENT)
+                       dev_err(dev, "No 'sound-dai' property\n");
+               else
+                       dev_err(dev, "Bad phandle in 'sound-dai'\n");
+               return num_codecs;
+       }
+       component = devm_kzalloc(dev,
+                                sizeof *component * num_codecs,
+                                GFP_KERNEL);
+       if (!component)
+               return -ENOMEM;
+       dai_link->codecs = component;
+       dai_link->num_codecs = num_codecs;
+
+       /* Parse the list */
+       for (index = 0, component = dai_link->codecs;
+            index < dai_link->num_codecs;
+            index++, component++) {
+               ret = of_parse_phandle_with_args(of_node, name,
+                                                "#sound-dai-cells",
+                                                 index, &args);
+               if (ret)
+                       goto err;
+               component->of_node = args.np;
+               ret = snd_soc_get_dai_name(&args, &component->dai_name);
+               if (ret < 0)
+                       goto err;
+       }
+       return 0;
+err:
+       for (index = 0, component = dai_link->codecs;
+            index < dai_link->num_codecs;
+            index++, component++) {
+               if (!component->of_node)
+                       break;
+               of_node_put(component->of_node);
+               component->of_node = NULL;
+       }
+       dai_link->codecs = NULL;
+       dai_link->num_codecs = 0;
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_of_get_dai_link_codecs);
+
 static int __init snd_soc_init(void)
 {
 #ifdef CONFIG_DEBUG_FS
index c61cb9c..c5136bb 100644 (file)
@@ -159,27 +159,135 @@ static void dapm_mark_dirty(struct snd_soc_dapm_widget *w, const char *reason)
        }
 }
 
-void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm)
+/*
+ * dapm_widget_invalidate_input_paths() - Invalidate the cached number of input
+ *  paths
+ * @w: The widget for which to invalidate the cached number of input paths
+ *
+ * The function resets the cached number of inputs for the specified widget and
+ * all widgets that can be reached via outgoing paths from the widget.
+ *
+ * This function must be called if the number of input paths for a widget might
+ * have changed. E.g. if the source state of a widget changes or a path is added
+ * or activated with the widget as the sink.
+ */
+static void dapm_widget_invalidate_input_paths(struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_dapm_widget *sink;
+       struct snd_soc_dapm_path *p;
+       LIST_HEAD(list);
+
+       dapm_assert_locked(w->dapm);
+
+       if (w->inputs == -1)
+               return;
+
+       w->inputs = -1;
+       list_add_tail(&w->work_list, &list);
+
+       list_for_each_entry(w, &list, work_list) {
+               list_for_each_entry(p, &w->sinks, list_source) {
+                       if (p->is_supply || p->weak || !p->connect)
+                               continue;
+                       sink = p->sink;
+                       if (sink->inputs != -1) {
+                               sink->inputs = -1;
+                               list_add_tail(&sink->work_list, &list);
+                       }
+               }
+       }
+}
+
+/*
+ * dapm_widget_invalidate_output_paths() - Invalidate the cached number of
+ *  output paths
+ * @w: The widget for which to invalidate the cached number of output paths
+ *
+ * Resets the cached number of outputs for the specified widget and all widgets
+ * that can be reached via incoming paths from the widget.
+ *
+ * This function must be called if the number of output paths for a widget might
+ * have changed. E.g. if the sink state of a widget changes or a path is added
+ * or activated with the widget as the source.
+ */
+static void dapm_widget_invalidate_output_paths(struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_dapm_widget *source;
+       struct snd_soc_dapm_path *p;
+       LIST_HEAD(list);
+
+       dapm_assert_locked(w->dapm);
+
+       if (w->outputs == -1)
+               return;
+
+       w->outputs = -1;
+       list_add_tail(&w->work_list, &list);
+
+       list_for_each_entry(w, &list, work_list) {
+               list_for_each_entry(p, &w->sources, list_sink) {
+                       if (p->is_supply || p->weak || !p->connect)
+                               continue;
+                       source = p->source;
+                       if (source->outputs != -1) {
+                               source->outputs = -1;
+                               list_add_tail(&source->work_list, &list);
+                       }
+               }
+       }
+}
+
+/*
+ * dapm_path_invalidate() - Invalidates the cached number of inputs and outputs
+ *  for the widgets connected to a path
+ * @p: The path to invalidate
+ *
+ * Resets the cached number of inputs for the sink of the path and the cached
+ * number of outputs for the source of the path.
+ *
+ * This function must be called when a path is added, removed or the connected
+ * state changes.
+ */
+static void dapm_path_invalidate(struct snd_soc_dapm_path *p)
+{
+       /*
+        * Weak paths or supply paths do not influence the number of input or
+        * output paths of their neighbors.
+        */
+       if (p->weak || p->is_supply)
+               return;
+
+       /*
+        * The number of connected endpoints is the sum of the number of
+        * connected endpoints of all neighbors. If a node with 0 connected
+        * endpoints is either connected or disconnected that sum won't change,
+        * so there is no need to re-check the path.
+        */
+       if (p->source->inputs != 0)
+               dapm_widget_invalidate_input_paths(p->sink);
+       if (p->sink->outputs != 0)
+               dapm_widget_invalidate_output_paths(p->source);
+}
+
+void dapm_mark_endpoints_dirty(struct snd_soc_card *card)
 {
-       struct snd_soc_card *card = dapm->card;
        struct snd_soc_dapm_widget *w;
 
        mutex_lock(&card->dapm_mutex);
 
        list_for_each_entry(w, &card->widgets, list) {
-               switch (w->id) {
-               case snd_soc_dapm_input:
-               case snd_soc_dapm_output:
-                       dapm_mark_dirty(w, "Rechecking inputs and outputs");
-                       break;
-               default:
-                       break;
+               if (w->is_sink || w->is_source) {
+                       dapm_mark_dirty(w, "Rechecking endpoints");
+                       if (w->is_sink)
+                               dapm_widget_invalidate_output_paths(w);
+                       if (w->is_source)
+                               dapm_widget_invalidate_input_paths(w);
                }
        }
 
        mutex_unlock(&card->dapm_mutex);
 }
-EXPORT_SYMBOL_GPL(dapm_mark_io_dirty);
+EXPORT_SYMBOL_GPL(dapm_mark_endpoints_dirty);
 
 /* create a new dapm widget */
 static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
@@ -386,8 +494,6 @@ static void dapm_reset(struct snd_soc_card *card)
        list_for_each_entry(w, &card->widgets, list) {
                w->new_power = w->power;
                w->power_checked = false;
-               w->inputs = -1;
-               w->outputs = -1;
        }
 }
 
@@ -469,10 +575,9 @@ out:
 
 /* connect mux widget to its interconnecting audio paths */
 static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
-       struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
-       struct snd_soc_dapm_path *path, const char *control_name,
-       const struct snd_kcontrol_new *kcontrol)
+       struct snd_soc_dapm_path *path, const char *control_name)
 {
+       const struct snd_kcontrol_new *kcontrol = &path->sink->kcontrol_news[0];
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val, item;
        int i;
@@ -493,10 +598,7 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
 
        for (i = 0; i < e->items; i++) {
                if (!(strcmp(control_name, e->texts[i]))) {
-                       list_add(&path->list, &dapm->card->paths);
-                       list_add(&path->list_sink, &dest->sources);
-                       list_add(&path->list_source, &src->sinks);
-                       path->name = (char*)e->texts[i];
+                       path->name = e->texts[i];
                        if (i == item)
                                path->connect = 1;
                        else
@@ -509,11 +611,10 @@ static int dapm_connect_mux(struct snd_soc_dapm_context *dapm,
 }
 
 /* set up initial codec paths */
-static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
-       struct snd_soc_dapm_path *p, int i)
+static void dapm_set_mixer_path_status(struct snd_soc_dapm_path *p, int i)
 {
        struct soc_mixer_control *mc = (struct soc_mixer_control *)
-               w->kcontrol_news[i].private_value;
+               p->sink->kcontrol_news[i].private_value;
        unsigned int reg = mc->reg;
        unsigned int shift = mc->shift;
        unsigned int max = mc->max;
@@ -522,7 +623,7 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
        unsigned int val;
 
        if (reg != SND_SOC_NOPM) {
-               soc_dapm_read(w->dapm, reg, &val);
+               soc_dapm_read(p->sink->dapm, reg, &val);
                val = (val >> shift) & mask;
                if (invert)
                        val = max - val;
@@ -534,19 +635,15 @@ static void dapm_set_mixer_path_status(struct snd_soc_dapm_widget *w,
 
 /* connect mixer widget to its interconnecting audio paths */
 static int dapm_connect_mixer(struct snd_soc_dapm_context *dapm,
-       struct snd_soc_dapm_widget *src, struct snd_soc_dapm_widget *dest,
        struct snd_soc_dapm_path *path, const char *control_name)
 {
        int i;
 
        /* search for mixer kcontrol */
-       for (i = 0; i < dest->num_kcontrols; i++) {
-               if (!strcmp(control_name, dest->kcontrol_news[i].name)) {
-                       list_add(&path->list, &dapm->card->paths);
-                       list_add(&path->list_sink, &dest->sources);
-                       list_add(&path->list_source, &src->sinks);
-                       path->name = dest->kcontrol_news[i].name;
-                       dapm_set_mixer_path_status(dest, path, i);
+       for (i = 0; i < path->sink->num_kcontrols; i++) {
+               if (!strcmp(control_name, path->sink->kcontrol_news[i].name)) {
+                       path->name = path->sink->kcontrol_news[i].name;
+                       dapm_set_mixer_path_status(path, i);
                        return 0;
                }
        }
@@ -738,8 +835,10 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
        if (ret < 0)
                return ret;
 
-       list_for_each_entry(path, &w->sources, list_sink)
-               dapm_kcontrol_add_path(w->kcontrols[0], path);
+       list_for_each_entry(path, &w->sources, list_sink) {
+               if (path->name)
+                       dapm_kcontrol_add_path(w->kcontrols[0], path);
+       }
 
        return 0;
 }
@@ -754,34 +853,6 @@ static int dapm_new_pga(struct snd_soc_dapm_widget *w)
        return 0;
 }
 
-/* reset 'walked' bit for each dapm path */
-static void dapm_clear_walk_output(struct snd_soc_dapm_context *dapm,
-                                  struct list_head *sink)
-{
-       struct snd_soc_dapm_path *p;
-
-       list_for_each_entry(p, sink, list_source) {
-               if (p->walked) {
-                       p->walked = 0;
-                       dapm_clear_walk_output(dapm, &p->sink->sinks);
-               }
-       }
-}
-
-static void dapm_clear_walk_input(struct snd_soc_dapm_context *dapm,
-                                 struct list_head *source)
-{
-       struct snd_soc_dapm_path *p;
-
-       list_for_each_entry(p, source, list_sink) {
-               if (p->walked) {
-                       p->walked = 0;
-                       dapm_clear_walk_input(dapm, &p->source->sources);
-               }
-       }
-}
-
-
 /* We implement power down on suspend by checking the power state of
  * the ALSA card - when we are suspending the ALSA state for the card
  * is set to D3.
@@ -856,61 +927,23 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
 
        DAPM_UPDATE_STAT(widget, path_checks);
 
-       switch (widget->id) {
-       case snd_soc_dapm_supply:
-       case snd_soc_dapm_regulator_supply:
-       case snd_soc_dapm_clock_supply:
-       case snd_soc_dapm_kcontrol:
-               return 0;
-       default:
-               break;
-       }
-
-       switch (widget->id) {
-       case snd_soc_dapm_adc:
-       case snd_soc_dapm_aif_out:
-       case snd_soc_dapm_dai_out:
-               if (widget->active) {
-                       widget->outputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->outputs;
-               }
-       default:
-               break;
-       }
-
-       if (widget->connected) {
-               /* connected pin ? */
-               if (widget->id == snd_soc_dapm_output && !widget->ext) {
-                       widget->outputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->outputs;
-               }
-
-               /* connected jack or spk ? */
-               if (widget->id == snd_soc_dapm_hp ||
-                   widget->id == snd_soc_dapm_spk ||
-                   (widget->id == snd_soc_dapm_line &&
-                    !list_empty(&widget->sources))) {
-                       widget->outputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->outputs;
-               }
+       if (widget->is_sink && widget->connected) {
+               widget->outputs = snd_soc_dapm_suspend_check(widget);
+               return widget->outputs;
        }
 
        list_for_each_entry(path, &widget->sinks, list_source) {
                DAPM_UPDATE_STAT(widget, neighbour_checks);
 
-               if (path->weak)
+               if (path->weak || path->is_supply)
                        continue;
 
                if (path->walking)
                        return 1;
 
-               if (path->walked)
-                       continue;
-
                trace_snd_soc_dapm_output_path(widget, path);
 
-               if (path->sink && path->connect) {
-                       path->walked = 1;
+               if (path->connect) {
                        path->walking = 1;
 
                        /* do we need to add this widget to the list ? */
@@ -952,73 +985,23 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
 
        DAPM_UPDATE_STAT(widget, path_checks);
 
-       switch (widget->id) {
-       case snd_soc_dapm_supply:
-       case snd_soc_dapm_regulator_supply:
-       case snd_soc_dapm_clock_supply:
-       case snd_soc_dapm_kcontrol:
-               return 0;
-       default:
-               break;
-       }
-
-       /* active stream ? */
-       switch (widget->id) {
-       case snd_soc_dapm_dac:
-       case snd_soc_dapm_aif_in:
-       case snd_soc_dapm_dai_in:
-               if (widget->active) {
-                       widget->inputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->inputs;
-               }
-       default:
-               break;
-       }
-
-       if (widget->connected) {
-               /* connected pin ? */
-               if (widget->id == snd_soc_dapm_input && !widget->ext) {
-                       widget->inputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->inputs;
-               }
-
-               /* connected VMID/Bias for lower pops */
-               if (widget->id == snd_soc_dapm_vmid) {
-                       widget->inputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->inputs;
-               }
-
-               /* connected jack ? */
-               if (widget->id == snd_soc_dapm_mic ||
-                   (widget->id == snd_soc_dapm_line &&
-                    !list_empty(&widget->sinks))) {
-                       widget->inputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->inputs;
-               }
-
-               /* signal generator */
-               if (widget->id == snd_soc_dapm_siggen) {
-                       widget->inputs = snd_soc_dapm_suspend_check(widget);
-                       return widget->inputs;
-               }
+       if (widget->is_source && widget->connected) {
+               widget->inputs = snd_soc_dapm_suspend_check(widget);
+               return widget->inputs;
        }
 
        list_for_each_entry(path, &widget->sources, list_sink) {
                DAPM_UPDATE_STAT(widget, neighbour_checks);
 
-               if (path->weak)
+               if (path->weak || path->is_supply)
                        continue;
 
                if (path->walking)
                        return 1;
 
-               if (path->walked)
-                       continue;
-
                trace_snd_soc_dapm_input_path(widget, path);
 
-               if (path->source && path->connect) {
-                       path->walked = 1;
+               if (path->connect) {
                        path->walking = 1;
 
                        /* do we need to add this widget to the list ? */
@@ -1060,21 +1043,25 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
 int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
        struct snd_soc_dapm_widget_list **list)
 {
-       struct snd_soc_card *card = dai->card;
+       struct snd_soc_card *card = dai->component->card;
+       struct snd_soc_dapm_widget *w;
        int paths;
 
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
-       dapm_reset(card);
 
-       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+       /*
+        * For is_connected_{output,input}_ep fully discover the graph we need
+        * to reset the cached number of inputs and outputs.
+        */
+       list_for_each_entry(w, &card->widgets, list) {
+               w->inputs = -1;
+               w->outputs = -1;
+       }
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
                paths = is_connected_output_ep(dai->playback_widget, list);
-               dapm_clear_walk_output(&card->dapm,
-                                      &dai->playback_widget->sinks);
-       } else {
+       else
                paths = is_connected_input_ep(dai->capture_widget, list);
-               dapm_clear_walk_input(&card->dapm,
-                                     &dai->capture_widget->sources);
-       }
 
        trace_snd_soc_dapm_connected(paths, stream);
        mutex_unlock(&card->dapm_mutex);
@@ -1163,44 +1150,10 @@ static int dapm_generic_check_power(struct snd_soc_dapm_widget *w)
        DAPM_UPDATE_STAT(w, power_checks);
 
        in = is_connected_input_ep(w, NULL);
-       dapm_clear_walk_input(w->dapm, &w->sources);
        out = is_connected_output_ep(w, NULL);
-       dapm_clear_walk_output(w->dapm, &w->sinks);
        return out != 0 && in != 0;
 }
 
-/* Check to see if an ADC has power */
-static int dapm_adc_check_power(struct snd_soc_dapm_widget *w)
-{
-       int in;
-
-       DAPM_UPDATE_STAT(w, power_checks);
-
-       if (w->active) {
-               in = is_connected_input_ep(w, NULL);
-               dapm_clear_walk_input(w->dapm, &w->sources);
-               return in != 0;
-       } else {
-               return dapm_generic_check_power(w);
-       }
-}
-
-/* Check to see if a DAC has power */
-static int dapm_dac_check_power(struct snd_soc_dapm_widget *w)
-{
-       int out;
-
-       DAPM_UPDATE_STAT(w, power_checks);
-
-       if (w->active) {
-               out = is_connected_output_ep(w, NULL);
-               dapm_clear_walk_output(w->dapm, &w->sinks);
-               return out != 0;
-       } else {
-               return dapm_generic_check_power(w);
-       }
-}
-
 /* Check to see if a power supply is needed */
 static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
 {
@@ -1219,9 +1172,6 @@ static int dapm_supply_check_power(struct snd_soc_dapm_widget *w)
                    !path->connected(path->source, path->sink))
                        continue;
 
-               if (!path->sink)
-                       continue;
-
                if (dapm_widget_power_check(path->sink))
                        return 1;
        }
@@ -1636,27 +1586,14 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
        /* If we changed our power state perhaps our neigbours changed
         * also.
         */
-       list_for_each_entry(path, &w->sources, list_sink) {
-               if (path->source) {
-                       dapm_widget_set_peer_power(path->source, power,
+       list_for_each_entry(path, &w->sources, list_sink)
+               dapm_widget_set_peer_power(path->source, power, path->connect);
+
+       /* Supplies can't affect their outputs, only their inputs */
+       if (!w->is_supply) {
+               list_for_each_entry(path, &w->sinks, list_source)
+                       dapm_widget_set_peer_power(path->sink, power,
                                                   path->connect);
-               }
-       }
-       switch (w->id) {
-       case snd_soc_dapm_supply:
-       case snd_soc_dapm_regulator_supply:
-       case snd_soc_dapm_clock_supply:
-       case snd_soc_dapm_kcontrol:
-               /* Supplies can't affect their outputs, only their inputs */
-               break;
-       default:
-               list_for_each_entry(path, &w->sinks, list_source) {
-                       if (path->sink) {
-                               dapm_widget_set_peer_power(path->sink, power,
-                                                          path->connect);
-                       }
-               }
-               break;
        }
 
        if (power)
@@ -1863,10 +1800,14 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
        if (!buf)
                return -ENOMEM;
 
-       in = is_connected_input_ep(w, NULL);
-       dapm_clear_walk_input(w->dapm, &w->sources);
-       out = is_connected_output_ep(w, NULL);
-       dapm_clear_walk_output(w->dapm, &w->sinks);
+       /* Supply widgets are not handled by is_connected_{input,output}_ep() */
+       if (w->is_supply) {
+               in = 0;
+               out = 0;
+       } else {
+               in = is_connected_input_ep(w, NULL);
+               out = is_connected_output_ep(w, NULL);
+       }
 
        ret = snprintf(buf, PAGE_SIZE, "%s: %s%s  in %d out %d",
                       w->name, w->power ? "On" : "Off",
@@ -2011,32 +1952,45 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
 
 #endif
 
+/*
+ * soc_dapm_connect_path() - Connects or disconnects a path
+ * @path: The path to update
+ * @connect: The new connect state of the path. True if the path is connected,
+ *  false if it is disconneted.
+ * @reason: The reason why the path changed (for debugging only)
+ */
+static void soc_dapm_connect_path(struct snd_soc_dapm_path *path,
+       bool connect, const char *reason)
+{
+       if (path->connect == connect)
+               return;
+
+       path->connect = connect;
+       dapm_mark_dirty(path->source, reason);
+       dapm_mark_dirty(path->sink, reason);
+       dapm_path_invalidate(path);
+}
+
 /* test and update the power status of a mux widget */
 static int soc_dapm_mux_update_power(struct snd_soc_card *card,
                                 struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
 {
        struct snd_soc_dapm_path *path;
        int found = 0;
+       bool connect;
 
        lockdep_assert_held(&card->dapm_mutex);
 
        /* find dapm widget path assoc with kcontrol */
        dapm_kcontrol_for_each_path(path, kcontrol) {
-               if (!path->name || !e->texts[mux])
-                       continue;
-
                found = 1;
                /* we now need to match the string in the enum to the path */
-               if (!(strcmp(path->name, e->texts[mux]))) {
-                       path->connect = 1; /* new connection */
-                       dapm_mark_dirty(path->source, "mux connection");
-               } else {
-                       if (path->connect)
-                               dapm_mark_dirty(path->source,
-                                               "mux disconnection");
-                       path->connect = 0; /* old connection must be powered down */
-               }
-               dapm_mark_dirty(path->sink, "mux change");
+               if (!(strcmp(path->name, e->texts[mux])))
+                       connect = true;
+               else
+                       connect = false;
+
+               soc_dapm_connect_path(path, connect, "mux update");
        }
 
        if (found)
@@ -2075,9 +2029,7 @@ static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
        /* find dapm widget path assoc with kcontrol */
        dapm_kcontrol_for_each_path(path, kcontrol) {
                found = 1;
-               path->connect = connect;
-               dapm_mark_dirty(path->source, "mixer connection");
-               dapm_mark_dirty(path->sink, "mixer update");
+               soc_dapm_connect_path(path, connect, "mixer update");
        }
 
        if (found)
@@ -2255,8 +2207,11 @@ static int snd_soc_dapm_set_pin(struct snd_soc_dapm_context *dapm,
                return -EINVAL;
        }
 
-       if (w->connected != status)
+       if (w->connected != status) {
                dapm_mark_dirty(w, "pin configuration");
+               dapm_widget_invalidate_input_paths(w);
+               dapm_widget_invalidate_output_paths(w);
+       }
 
        w->connected = status;
        if (status == 0)
@@ -2309,6 +2264,53 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
+/*
+ * dapm_update_widget_flags() - Re-compute widget sink and source flags
+ * @w: The widget for which to update the flags
+ *
+ * Some widgets have a dynamic category which depends on which neighbors they
+ * are connected to. This function update the category for these widgets.
+ *
+ * This function must be called whenever a path is added or removed to a widget.
+ */
+static void dapm_update_widget_flags(struct snd_soc_dapm_widget *w)
+{
+       struct snd_soc_dapm_path *p;
+
+       switch (w->id) {
+       case snd_soc_dapm_input:
+               w->is_source = 1;
+               list_for_each_entry(p, &w->sources, list_sink) {
+                       if (p->source->id == snd_soc_dapm_micbias ||
+                               p->source->id == snd_soc_dapm_mic ||
+                               p->source->id == snd_soc_dapm_line ||
+                               p->source->id == snd_soc_dapm_output) {
+                                       w->is_source = 0;
+                                       break;
+                       }
+               }
+               break;
+       case snd_soc_dapm_output:
+               w->is_sink = 1;
+               list_for_each_entry(p, &w->sinks, list_source) {
+                       if (p->sink->id == snd_soc_dapm_spk ||
+                               p->sink->id == snd_soc_dapm_hp ||
+                               p->sink->id == snd_soc_dapm_line ||
+                               p->sink->id == snd_soc_dapm_input) {
+                                       w->is_sink = 0;
+                                       break;
+                       }
+               }
+               break;
+       case snd_soc_dapm_line:
+               w->is_sink = !list_empty(&w->sources);
+               w->is_source = !list_empty(&w->sinks);
+               break;
+       default:
+               break;
+       }
+}
+
 static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
        struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
        const char *control,
@@ -2318,6 +2320,27 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
        struct snd_soc_dapm_path *path;
        int ret;
 
+       if (wsink->is_supply && !wsource->is_supply) {
+               dev_err(dapm->dev,
+                       "Connecting non-supply widget to supply widget is not supported (%s -> %s)\n",
+                       wsource->name, wsink->name);
+               return -EINVAL;
+       }
+
+       if (connected && !wsource->is_supply) {
+               dev_err(dapm->dev,
+                       "connected() callback only supported for supply widgets (%s -> %s)\n",
+                       wsource->name, wsink->name);
+               return -EINVAL;
+       }
+
+       if (wsource->is_supply && control) {
+               dev_err(dapm->dev,
+                       "Conditional paths are not supported for supply widgets (%s -> [%s] -> %s)\n",
+                       wsource->name, control, wsink->name);
+               return -EINVAL;
+       }
+
        path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
        if (!path)
                return -ENOMEM;
@@ -2330,85 +2353,49 @@ static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
        INIT_LIST_HEAD(&path->list_source);
        INIT_LIST_HEAD(&path->list_sink);
 
-       /* check for external widgets */
-       if (wsink->id == snd_soc_dapm_input) {
-               if (wsource->id == snd_soc_dapm_micbias ||
-                       wsource->id == snd_soc_dapm_mic ||
-                       wsource->id == snd_soc_dapm_line ||
-                       wsource->id == snd_soc_dapm_output)
-                       wsink->ext = 1;
-       }
-       if (wsource->id == snd_soc_dapm_output) {
-               if (wsink->id == snd_soc_dapm_spk ||
-                       wsink->id == snd_soc_dapm_hp ||
-                       wsink->id == snd_soc_dapm_line ||
-                       wsink->id == snd_soc_dapm_input)
-                       wsource->ext = 1;
-       }
-
-       dapm_mark_dirty(wsource, "Route added");
-       dapm_mark_dirty(wsink, "Route added");
+       if (wsource->is_supply || wsink->is_supply)
+               path->is_supply = 1;
 
        /* connect static paths */
        if (control == NULL) {
-               list_add(&path->list, &dapm->card->paths);
-               list_add(&path->list_sink, &wsink->sources);
-               list_add(&path->list_source, &wsource->sinks);
                path->connect = 1;
-               return 0;
-       }
-
-       /* connect dynamic paths */
-       switch (wsink->id) {
-       case snd_soc_dapm_adc:
-       case snd_soc_dapm_dac:
-       case snd_soc_dapm_pga:
-       case snd_soc_dapm_out_drv:
-       case snd_soc_dapm_input:
-       case snd_soc_dapm_output:
-       case snd_soc_dapm_siggen:
-       case snd_soc_dapm_micbias:
-       case snd_soc_dapm_vmid:
-       case snd_soc_dapm_pre:
-       case snd_soc_dapm_post:
-       case snd_soc_dapm_supply:
-       case snd_soc_dapm_regulator_supply:
-       case snd_soc_dapm_clock_supply:
-       case snd_soc_dapm_aif_in:
-       case snd_soc_dapm_aif_out:
-       case snd_soc_dapm_dai_in:
-       case snd_soc_dapm_dai_out:
-       case snd_soc_dapm_dai_link:
-       case snd_soc_dapm_kcontrol:
-               list_add(&path->list, &dapm->card->paths);
-               list_add(&path->list_sink, &wsink->sources);
-               list_add(&path->list_source, &wsource->sinks);
-               path->connect = 1;
-               return 0;
-       case snd_soc_dapm_mux:
-               ret = dapm_connect_mux(dapm, wsource, wsink, path, control,
-                       &wsink->kcontrol_news[0]);
-               if (ret != 0)
-                       goto err;
-               break;
-       case snd_soc_dapm_switch:
-       case snd_soc_dapm_mixer:
-       case snd_soc_dapm_mixer_named_ctl:
-               ret = dapm_connect_mixer(dapm, wsource, wsink, path, control);
-               if (ret != 0)
+       } else {
+               /* connect dynamic paths */
+               switch (wsink->id) {
+               case snd_soc_dapm_mux:
+                       ret = dapm_connect_mux(dapm, path, control);
+                       if (ret != 0)
+                               goto err;
+                       break;
+               case snd_soc_dapm_switch:
+               case snd_soc_dapm_mixer:
+               case snd_soc_dapm_mixer_named_ctl:
+                       ret = dapm_connect_mixer(dapm, path, control);
+                       if (ret != 0)
+                               goto err;
+                       break;
+               default:
+                       dev_err(dapm->dev,
+                               "Control not supported for path %s -> [%s] -> %s\n",
+                               wsource->name, control, wsink->name);
+                       ret = -EINVAL;
                        goto err;
-               break;
-       case snd_soc_dapm_hp:
-       case snd_soc_dapm_mic:
-       case snd_soc_dapm_line:
-       case snd_soc_dapm_spk:
-               list_add(&path->list, &dapm->card->paths);
-               list_add(&path->list_sink, &wsink->sources);
-               list_add(&path->list_source, &wsource->sinks);
-               path->connect = 0;
-               return 0;
+               }
        }
 
+       list_add(&path->list, &dapm->card->paths);
+       list_add(&path->list_sink, &wsink->sources);
+       list_add(&path->list_source, &wsource->sinks);
+
+       dapm_update_widget_flags(wsource);
+       dapm_update_widget_flags(wsink);
+
+       dapm_mark_dirty(wsource, "Route added");
+       dapm_mark_dirty(wsink, "Route added");
+
+       if (dapm->card->instantiated && path->connect)
+               dapm_path_invalidate(path);
+
        return 0;
 err:
        kfree(path);
@@ -2489,6 +2476,7 @@ err:
 static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
                                  const struct snd_soc_dapm_route *route)
 {
+       struct snd_soc_dapm_widget *wsource, *wsink;
        struct snd_soc_dapm_path *path, *p;
        const char *sink;
        const char *source;
@@ -2526,10 +2514,19 @@ static int snd_soc_dapm_del_route(struct snd_soc_dapm_context *dapm,
        }
 
        if (path) {
-               dapm_mark_dirty(path->source, "Route removed");
-               dapm_mark_dirty(path->sink, "Route removed");
+               wsource = path->source;
+               wsink = path->sink;
+
+               dapm_mark_dirty(wsource, "Route removed");
+               dapm_mark_dirty(wsink, "Route removed");
+               if (path->connect)
+                       dapm_path_invalidate(path);
 
                dapm_free_path(path);
+
+               /* Update any path related flags */
+               dapm_update_widget_flags(wsource);
+               dapm_update_widget_flags(wsink);
        } else {
                dev_warn(dapm->dev, "ASoC: Route %s->%s does not exist\n",
                         source, sink);
@@ -3087,40 +3084,44 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
        }
 
        switch (w->id) {
-       case snd_soc_dapm_switch:
-       case snd_soc_dapm_mixer:
-       case snd_soc_dapm_mixer_named_ctl:
+       case snd_soc_dapm_mic:
+       case snd_soc_dapm_input:
+               w->is_source = 1;
                w->power_check = dapm_generic_check_power;
                break;
-       case snd_soc_dapm_mux:
+       case snd_soc_dapm_spk:
+       case snd_soc_dapm_hp:
+       case snd_soc_dapm_output:
+               w->is_sink = 1;
                w->power_check = dapm_generic_check_power;
                break;
-       case snd_soc_dapm_dai_out:
-               w->power_check = dapm_adc_check_power;
-               break;
-       case snd_soc_dapm_dai_in:
-               w->power_check = dapm_dac_check_power;
+       case snd_soc_dapm_vmid:
+       case snd_soc_dapm_siggen:
+               w->is_source = 1;
+               w->power_check = dapm_always_on_check_power;
                break;
+       case snd_soc_dapm_mux:
+       case snd_soc_dapm_switch:
+       case snd_soc_dapm_mixer:
+       case snd_soc_dapm_mixer_named_ctl:
        case snd_soc_dapm_adc:
        case snd_soc_dapm_aif_out:
        case snd_soc_dapm_dac:
        case snd_soc_dapm_aif_in:
        case snd_soc_dapm_pga:
        case snd_soc_dapm_out_drv:
-       case snd_soc_dapm_input:
-       case snd_soc_dapm_output:
        case snd_soc_dapm_micbias:
-       case snd_soc_dapm_spk:
-       case snd_soc_dapm_hp:
-       case snd_soc_dapm_mic:
        case snd_soc_dapm_line:
        case snd_soc_dapm_dai_link:
+       case snd_soc_dapm_dai_out:
+       case snd_soc_dapm_dai_in:
                w->power_check = dapm_generic_check_power;
                break;
        case snd_soc_dapm_supply:
        case snd_soc_dapm_regulator_supply:
        case snd_soc_dapm_clock_supply:
        case snd_soc_dapm_kcontrol:
+               w->is_supply = 1;
                w->power_check = dapm_supply_check_power;
                break;
        default:
@@ -3137,6 +3138,9 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
        INIT_LIST_HEAD(&w->dirty);
        list_add(&w->list, &dapm->card->widgets);
 
+       w->inputs = -1;
+       w->outputs = -1;
+
        /* machine layer set ups unconnected pins and insertions */
        w->connected = 1;
        return w;
@@ -3484,6 +3488,14 @@ static void soc_dapm_dai_stream_event(struct snd_soc_dai *dai, int stream,
                case SND_SOC_DAPM_STREAM_PAUSE_RELEASE:
                        break;
                }
+
+               if (w->id == snd_soc_dapm_dai_in) {
+                       w->is_source = w->active;
+                       dapm_widget_invalidate_input_paths(w);
+               } else {
+                       w->is_sink = w->active;
+                       dapm_widget_invalidate_output_paths(w);
+               }
        }
 }
 
@@ -3610,7 +3622,15 @@ int snd_soc_dapm_force_enable_pin_unlocked(struct snd_soc_dapm_context *dapm,
        }
 
        dev_dbg(w->dapm->dev, "ASoC: force enable pin %s\n", pin);
-       w->connected = 1;
+       if (!w->connected) {
+               /*
+                * w->force does not affect the number of input or output paths,
+                * so we only have to recheck if w->connected is changed
+                */
+               dapm_widget_invalidate_input_paths(w);
+               dapm_widget_invalidate_output_paths(w);
+               w->connected = 1;
+       }
        w->force = 1;
        dapm_mark_dirty(w, "force enable");
 
@@ -3788,35 +3808,54 @@ int snd_soc_dapm_ignore_suspend(struct snd_soc_dapm_context *dapm,
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_ignore_suspend);
 
+/**
+ * dapm_is_external_path() - Checks if a path is a external path
+ * @card: The card the path belongs to
+ * @path: The path to check
+ *
+ * Returns true if the path is either between two different DAPM contexts or
+ * between two external pins of the same DAPM context. Otherwise returns
+ * false.
+ */
+static bool dapm_is_external_path(struct snd_soc_card *card,
+       struct snd_soc_dapm_path *path)
+{
+       dev_dbg(card->dev,
+               "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n",
+               path->source->name, path->source->id, path->source->dapm,
+               path->sink->name, path->sink->id, path->sink->dapm);
+
+       /* Connection between two different DAPM contexts */
+       if (path->source->dapm != path->sink->dapm)
+               return true;
+
+       /* Loopback connection from external pin to external pin */
+       if (path->sink->id == snd_soc_dapm_input) {
+               switch (path->source->id) {
+               case snd_soc_dapm_output:
+               case snd_soc_dapm_micbias:
+                       return true;
+               default:
+                       break;
+               }
+       }
+
+       return false;
+}
+
 static bool snd_soc_dapm_widget_in_card_paths(struct snd_soc_card *card,
                                              struct snd_soc_dapm_widget *w)
 {
        struct snd_soc_dapm_path *p;
 
-       list_for_each_entry(p, &card->paths, list) {
-               if ((p->source == w) || (p->sink == w)) {
-                       dev_dbg(card->dev,
-                           "... Path %s(id:%d dapm:%p) - %s(id:%d dapm:%p)\n",
-                           p->source->name, p->source->id, p->source->dapm,
-                           p->sink->name, p->sink->id, p->sink->dapm);
+       list_for_each_entry(p, &w->sources, list_sink) {
+               if (dapm_is_external_path(card, p))
+                       return true;
+       }
 
-                       /* Connected to something other than the codec */
-                       if (p->source->dapm != p->sink->dapm)
-                               return true;
-                       /*
-                        * Loopback connection from codec external pin to
-                        * codec external pin
-                        */
-                       if (p->sink->id == snd_soc_dapm_input) {
-                               switch (p->source->id) {
-                               case snd_soc_dapm_output:
-                               case snd_soc_dapm_micbias:
-                                       return true;
-                               default:
-                                       break;
-                               }
-                       }
-               }
+       list_for_each_entry(p, &w->sinks, list_source) {
+               if (dapm_is_external_path(card, p))
+                       return true;
        }
 
        return false;
index ab47fea..4380dcc 100644 (file)
@@ -116,7 +116,7 @@ EXPORT_SYMBOL_GPL(snd_soc_jack_report);
  *
  * @jack:  ASoC jack
  * @count: Number of zones
- * @zone:  Array of zones
+ * @zones:  Array of zones
  *
  * After this function has been called the zones specified in the
  * array will be associated with the jack.
@@ -309,7 +309,7 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
                        /* GPIO descriptor */
                        gpios[i].desc = gpiod_get_index(gpios[i].gpiod_dev,
                                                        gpios[i].name,
-                                                       gpios[i].idx);
+                                                       gpios[i].idx, GPIOD_IN);
                        if (IS_ERR(gpios[i].desc)) {
                                ret = PTR_ERR(gpios[i].desc);
                                dev_err(gpios[i].gpiod_dev,
@@ -327,17 +327,14 @@ int snd_soc_jack_add_gpios(struct snd_soc_jack *jack, int count,
                                goto undo;
                        }
 
-                       ret = gpio_request(gpios[i].gpio, gpios[i].name);
+                       ret = gpio_request_one(gpios[i].gpio, GPIOF_IN,
+                                              gpios[i].name);
                        if (ret)
                                goto undo;
 
                        gpios[i].desc = gpio_to_desc(gpios[i].gpio);
                }
 
-               ret = gpiod_direction_input(gpios[i].desc);
-               if (ret)
-                       goto err;
-
                INIT_DELAYED_WORK(&gpios[i].work, gpio_work);
                gpios[i].jack = jack;
 
diff --git a/sound/soc/soc-ops.c b/sound/soc/soc-ops.c
new file mode 100644 (file)
index 0000000..100d92b
--- /dev/null
@@ -0,0 +1,952 @@
+/*
+ * soc-ops.c  --  Generic ASoC operations
+ *
+ * Copyright 2005 Wolfson Microelectronics PLC.
+ * Copyright 2005 Openedhand Ltd.
+ * Copyright (C) 2010 Slimlogic Ltd.
+ * Copyright (C) 2010 Texas Instruments Inc.
+ *
+ * Author: Liam Girdwood <lrg@slimlogic.co.uk>
+ *         with code, comments and ideas from :-
+ *         Richard Purdie <richard@openedhand.com>
+ *
+ *  This program is free software; you can redistribute  it and/or modify it
+ *  under  the terms of  the GNU General  Public License as published by the
+ *  Free Software Foundation;  either version 2 of the  License, or (at your
+ *  option) any later version.
+ */
+
+#include <linux/module.h>
+#include <linux/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/bitops.h>
+#include <linux/ctype.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/jack.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dpcm.h>
+#include <sound/initval.h>
+
+/**
+ * snd_soc_info_enum_double - enumerated double mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a double enumerated
+ * mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+
+       return snd_ctl_enum_info(uinfo, e->shift_l == e->shift_r ? 1 : 2,
+                                e->items, e->texts);
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_enum_double);
+
+/**
+ * snd_soc_get_enum_double - enumerated double mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a double enumerated mixer.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int val, item;
+       unsigned int reg_val;
+       int ret;
+
+       ret = snd_soc_component_read(component, e->reg, &reg_val);
+       if (ret)
+               return ret;
+       val = (reg_val >> e->shift_l) & e->mask;
+       item = snd_soc_enum_val_to_item(e, val);
+       ucontrol->value.enumerated.item[0] = item;
+       if (e->shift_l != e->shift_r) {
+               val = (reg_val >> e->shift_l) & e->mask;
+               item = snd_soc_enum_val_to_item(e, val);
+               ucontrol->value.enumerated.item[1] = item;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_enum_double);
+
+/**
+ * snd_soc_put_enum_double - enumerated double mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a double enumerated mixer.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int *item = ucontrol->value.enumerated.item;
+       unsigned int val;
+       unsigned int mask;
+
+       if (item[0] >= e->items)
+               return -EINVAL;
+       val = snd_soc_enum_item_to_val(e, item[0]) << e->shift_l;
+       mask = e->mask << e->shift_l;
+       if (e->shift_l != e->shift_r) {
+               if (item[1] >= e->items)
+                       return -EINVAL;
+               val |= snd_soc_enum_item_to_val(e, item[1]) << e->shift_r;
+               mask |= e->mask << e->shift_r;
+       }
+
+       return snd_soc_component_update_bits(component, e->reg, mask, val);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_enum_double);
+
+/**
+ * snd_soc_read_signed - Read a codec register and interprete as signed value
+ * @component: component
+ * @reg: Register to read
+ * @mask: Mask to use after shifting the register value
+ * @shift: Right shift of register value
+ * @sign_bit: Bit that describes if a number is negative or not.
+ * @signed_val: Pointer to where the read value should be stored
+ *
+ * This functions reads a codec register. The register value is shifted right
+ * by 'shift' bits and masked with the given 'mask'. Afterwards it translates
+ * the given registervalue into a signed integer if sign_bit is non-zero.
+ *
+ * Returns 0 on sucess, otherwise an error value
+ */
+static int snd_soc_read_signed(struct snd_soc_component *component,
+       unsigned int reg, unsigned int mask, unsigned int shift,
+       unsigned int sign_bit, int *signed_val)
+{
+       int ret;
+       unsigned int val;
+
+       ret = snd_soc_component_read(component, reg, &val);
+       if (ret < 0)
+               return ret;
+
+       val = (val >> shift) & mask;
+
+       if (!sign_bit) {
+               *signed_val = val;
+               return 0;
+       }
+
+       /* non-negative number */
+       if (!(val & BIT(sign_bit))) {
+               *signed_val = val;
+               return 0;
+       }
+
+       ret = val;
+
+       /*
+        * The register most probably does not contain a full-sized int.
+        * Instead we have an arbitrary number of bits in a signed
+        * representation which has to be translated into a full-sized int.
+        * This is done by filling up all bits above the sign-bit.
+        */
+       ret |= ~((int)(BIT(sign_bit) - 1));
+
+       *signed_val = ret;
+
+       return 0;
+}
+
+/**
+ * snd_soc_info_volsw - single mixer info callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information about a single mixer control, or a double
+ * mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       int platform_max;
+
+       if (!mc->platform_max)
+               mc->platform_max = mc->max;
+       platform_max = mc->platform_max;
+
+       if (platform_max == 1 && !strstr(kcontrol->id.name, " Volume"))
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       else
+               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+
+       uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = platform_max - mc->min;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw);
+
+/**
+ * snd_soc_get_volsw - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       unsigned int rshift = mc->rshift;
+       int max = mc->max;
+       int min = mc->min;
+       int sign_bit = mc->sign_bit;
+       unsigned int mask = (1 << fls(max)) - 1;
+       unsigned int invert = mc->invert;
+       int val;
+       int ret;
+
+       if (sign_bit)
+               mask = BIT(sign_bit + 1) - 1;
+
+       ret = snd_soc_read_signed(component, reg, mask, shift, sign_bit, &val);
+       if (ret)
+               return ret;
+
+       ucontrol->value.integer.value[0] = val - min;
+       if (invert)
+               ucontrol->value.integer.value[0] =
+                       max - ucontrol->value.integer.value[0];
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               if (reg == reg2)
+                       ret = snd_soc_read_signed(component, reg, mask, rshift,
+                               sign_bit, &val);
+               else
+                       ret = snd_soc_read_signed(component, reg2, mask, shift,
+                               sign_bit, &val);
+               if (ret)
+                       return ret;
+
+               ucontrol->value.integer.value[1] = val - min;
+               if (invert)
+                       ucontrol->value.integer.value[1] =
+                               max - ucontrol->value.integer.value[1];
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw);
+
+/**
+ * snd_soc_put_volsw - single mixer put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       unsigned int rshift = mc->rshift;
+       int max = mc->max;
+       int min = mc->min;
+       unsigned int sign_bit = mc->sign_bit;
+       unsigned int mask = (1 << fls(max)) - 1;
+       unsigned int invert = mc->invert;
+       int err;
+       bool type_2r = false;
+       unsigned int val2 = 0;
+       unsigned int val, val_mask;
+
+       if (sign_bit)
+               mask = BIT(sign_bit + 1) - 1;
+
+       val = ((ucontrol->value.integer.value[0] + min) & mask);
+       if (invert)
+               val = max - val;
+       val_mask = mask << shift;
+       val = val << shift;
+       if (snd_soc_volsw_is_stereo(mc)) {
+               val2 = ((ucontrol->value.integer.value[1] + min) & mask);
+               if (invert)
+                       val2 = max - val2;
+               if (reg == reg2) {
+                       val_mask |= mask << rshift;
+                       val |= val2 << rshift;
+               } else {
+                       val2 = val2 << shift;
+                       type_2r = true;
+               }
+       }
+       err = snd_soc_component_update_bits(component, reg, val_mask, val);
+       if (err < 0)
+               return err;
+
+       if (type_2r)
+               err = snd_soc_component_update_bits(component, reg2, val_mask,
+                       val2);
+
+       return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw);
+
+/**
+ * snd_soc_get_volsw_sx - single mixer get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a single mixer control, or a double mixer
+ * control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_sx(struct snd_kcontrol *kcontrol,
+                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+           (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       unsigned int rshift = mc->rshift;
+       int max = mc->max;
+       int min = mc->min;
+       int mask = (1 << (fls(min + max) - 1)) - 1;
+       unsigned int val;
+       int ret;
+
+       ret = snd_soc_component_read(component, reg, &val);
+       if (ret < 0)
+               return ret;
+
+       ucontrol->value.integer.value[0] = ((val >> shift) - min) & mask;
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               ret = snd_soc_component_read(component, reg2, &val);
+               if (ret < 0)
+                       return ret;
+
+               val = ((val >> rshift) - min) & mask;
+               ucontrol->value.integer.value[1] = val;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_sx);
+
+/**
+ * snd_soc_put_volsw_sx - double mixer set callback
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to set the value of a double mixer control that spans 2 registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_sx(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+           (struct soc_mixer_control *)kcontrol->private_value;
+
+       unsigned int reg = mc->reg;
+       unsigned int reg2 = mc->rreg;
+       unsigned int shift = mc->shift;
+       unsigned int rshift = mc->rshift;
+       int max = mc->max;
+       int min = mc->min;
+       int mask = (1 << (fls(min + max) - 1)) - 1;
+       int err = 0;
+       unsigned int val, val_mask, val2 = 0;
+
+       val_mask = mask << shift;
+       val = (ucontrol->value.integer.value[0] + min) & mask;
+       val = val << shift;
+
+       err = snd_soc_component_update_bits(component, reg, val_mask, val);
+       if (err < 0)
+               return err;
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               val_mask = mask << rshift;
+               val2 = (ucontrol->value.integer.value[1] + min) & mask;
+               val2 = val2 << rshift;
+
+               err = snd_soc_component_update_bits(component, reg2, val_mask,
+                       val2);
+       }
+       return err;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_sx);
+
+/**
+ * snd_soc_info_volsw_range - single mixer info callback with range.
+ * @kcontrol: mixer control
+ * @uinfo: control element information
+ *
+ * Callback to provide information, within a range, about a single
+ * mixer control.
+ *
+ * returns 0 for success.
+ */
+int snd_soc_info_volsw_range(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       int platform_max;
+       int min = mc->min;
+
+       if (!mc->platform_max)
+               mc->platform_max = mc->max;
+       platform_max = mc->platform_max;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = snd_soc_volsw_is_stereo(mc) ? 2 : 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = platform_max - min;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_volsw_range);
+
+/**
+ * snd_soc_put_volsw_range - single mixer put value callback with range.
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value, within a range, for a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       unsigned int reg = mc->reg;
+       unsigned int rreg = mc->rreg;
+       unsigned int shift = mc->shift;
+       int min = mc->min;
+       int max = mc->max;
+       unsigned int mask = (1 << fls(max)) - 1;
+       unsigned int invert = mc->invert;
+       unsigned int val, val_mask;
+       int ret;
+
+       if (invert)
+               val = (max - ucontrol->value.integer.value[0]) & mask;
+       else
+               val = ((ucontrol->value.integer.value[0] + min) & mask);
+       val_mask = mask << shift;
+       val = val << shift;
+
+       ret = snd_soc_component_update_bits(component, reg, val_mask, val);
+       if (ret < 0)
+               return ret;
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               if (invert)
+                       val = (max - ucontrol->value.integer.value[1]) & mask;
+               else
+                       val = ((ucontrol->value.integer.value[1] + min) & mask);
+               val_mask = mask << shift;
+               val = val << shift;
+
+               ret = snd_soc_component_update_bits(component, rreg, val_mask,
+                       val);
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_volsw_range);
+
+/**
+ * snd_soc_get_volsw_range - single mixer get callback with range
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value, within a range, of a single mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int rreg = mc->rreg;
+       unsigned int shift = mc->shift;
+       int min = mc->min;
+       int max = mc->max;
+       unsigned int mask = (1 << fls(max)) - 1;
+       unsigned int invert = mc->invert;
+       unsigned int val;
+       int ret;
+
+       ret = snd_soc_component_read(component, reg, &val);
+       if (ret)
+               return ret;
+
+       ucontrol->value.integer.value[0] = (val >> shift) & mask;
+       if (invert)
+               ucontrol->value.integer.value[0] =
+                       max - ucontrol->value.integer.value[0];
+       else
+               ucontrol->value.integer.value[0] =
+                       ucontrol->value.integer.value[0] - min;
+
+       if (snd_soc_volsw_is_stereo(mc)) {
+               ret = snd_soc_component_read(component, rreg, &val);
+               if (ret)
+                       return ret;
+
+               ucontrol->value.integer.value[1] = (val >> shift) & mask;
+               if (invert)
+                       ucontrol->value.integer.value[1] =
+                               max - ucontrol->value.integer.value[1];
+               else
+                       ucontrol->value.integer.value[1] =
+                               ucontrol->value.integer.value[1] - min;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_volsw_range);
+
+/**
+ * snd_soc_limit_volume - Set new limit to an existing volume control.
+ *
+ * @codec: where to look for the control
+ * @name: Name of the control
+ * @max: new maximum limit
+ *
+ * Return 0 for success, else error.
+ */
+int snd_soc_limit_volume(struct snd_soc_codec *codec,
+       const char *name, int max)
+{
+       struct snd_card *card = codec->component.card->snd_card;
+       struct snd_kcontrol *kctl;
+       struct soc_mixer_control *mc;
+       int found = 0;
+       int ret = -EINVAL;
+
+       /* Sanity check for name and max */
+       if (unlikely(!name || max <= 0))
+               return -EINVAL;
+
+       list_for_each_entry(kctl, &card->controls, list) {
+               if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
+                       found = 1;
+                       break;
+               }
+       }
+       if (found) {
+               mc = (struct soc_mixer_control *)kctl->private_value;
+               if (max <= mc->max) {
+                       mc->platform_max = max;
+                       ret = 0;
+               }
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_limit_volume);
+
+int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
+                      struct snd_ctl_elem_info *uinfo)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_bytes *params = (void *)kcontrol->private_value;
+
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       uinfo->count = params->num_regs * component->val_bytes;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_info);
+
+int snd_soc_bytes_get(struct snd_kcontrol *kcontrol,
+                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_bytes *params = (void *)kcontrol->private_value;
+       int ret;
+
+       if (component->regmap)
+               ret = regmap_raw_read(component->regmap, params->base,
+                                     ucontrol->value.bytes.data,
+                                     params->num_regs * component->val_bytes);
+       else
+               ret = -EINVAL;
+
+       /* Hide any masked bytes to ensure consistent data reporting */
+       if (ret == 0 && params->mask) {
+               switch (component->val_bytes) {
+               case 1:
+                       ucontrol->value.bytes.data[0] &= ~params->mask;
+                       break;
+               case 2:
+                       ((u16 *)(&ucontrol->value.bytes.data))[0]
+                               &= cpu_to_be16(~params->mask);
+                       break;
+               case 4:
+                       ((u32 *)(&ucontrol->value.bytes.data))[0]
+                               &= cpu_to_be32(~params->mask);
+                       break;
+               default:
+                       return -EINVAL;
+               }
+       }
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_get);
+
+int snd_soc_bytes_put(struct snd_kcontrol *kcontrol,
+                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_bytes *params = (void *)kcontrol->private_value;
+       int ret, len;
+       unsigned int val, mask;
+       void *data;
+
+       if (!component->regmap || !params->num_regs)
+               return -EINVAL;
+
+       len = params->num_regs * component->val_bytes;
+
+       data = kmemdup(ucontrol->value.bytes.data, len, GFP_KERNEL | GFP_DMA);
+       if (!data)
+               return -ENOMEM;
+
+       /*
+        * If we've got a mask then we need to preserve the register
+        * bits.  We shouldn't modify the incoming data so take a
+        * copy.
+        */
+       if (params->mask) {
+               ret = regmap_read(component->regmap, params->base, &val);
+               if (ret != 0)
+                       goto out;
+
+               val &= params->mask;
+
+               switch (component->val_bytes) {
+               case 1:
+                       ((u8 *)data)[0] &= ~params->mask;
+                       ((u8 *)data)[0] |= val;
+                       break;
+               case 2:
+                       mask = ~params->mask;
+                       ret = regmap_parse_val(component->regmap,
+                                                       &mask, &mask);
+                       if (ret != 0)
+                               goto out;
+
+                       ((u16 *)data)[0] &= mask;
+
+                       ret = regmap_parse_val(component->regmap,
+                                                       &val, &val);
+                       if (ret != 0)
+                               goto out;
+
+                       ((u16 *)data)[0] |= val;
+                       break;
+               case 4:
+                       mask = ~params->mask;
+                       ret = regmap_parse_val(component->regmap,
+                                                       &mask, &mask);
+                       if (ret != 0)
+                               goto out;
+
+                       ((u32 *)data)[0] &= mask;
+
+                       ret = regmap_parse_val(component->regmap,
+                                                       &val, &val);
+                       if (ret != 0)
+                               goto out;
+
+                       ((u32 *)data)[0] |= val;
+                       break;
+               default:
+                       ret = -EINVAL;
+                       goto out;
+               }
+       }
+
+       ret = regmap_raw_write(component->regmap, params->base,
+                              data, len);
+
+out:
+       kfree(data);
+
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_put);
+
+int snd_soc_bytes_info_ext(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_info *ucontrol)
+{
+       struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+
+       ucontrol->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       ucontrol->count = params->max;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_info_ext);
+
+int snd_soc_bytes_tlv_callback(struct snd_kcontrol *kcontrol, int op_flag,
+                               unsigned int size, unsigned int __user *tlv)
+{
+       struct soc_bytes_ext *params = (void *)kcontrol->private_value;
+       unsigned int count = size < params->max ? size : params->max;
+       int ret = -ENXIO;
+
+       switch (op_flag) {
+       case SNDRV_CTL_TLV_OP_READ:
+               if (params->get)
+                       ret = params->get(tlv, count);
+               break;
+       case SNDRV_CTL_TLV_OP_WRITE:
+               if (params->put)
+                       ret = params->put(tlv, count);
+               break;
+       }
+       return ret;
+}
+EXPORT_SYMBOL_GPL(snd_soc_bytes_tlv_callback);
+
+/**
+ * snd_soc_info_xr_sx - signed multi register info callback
+ * @kcontrol: mreg control
+ * @uinfo: control element information
+ *
+ * Callback to provide information of a control that can
+ * span multiple codec registers which together
+ * forms a single signed value in a MSB/LSB manner.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_info_xr_sx(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_info *uinfo)
+{
+       struct soc_mreg_control *mc =
+               (struct soc_mreg_control *)kcontrol->private_value;
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = mc->min;
+       uinfo->value.integer.max = mc->max;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_info_xr_sx);
+
+/**
+ * snd_soc_get_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to get the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_xr_sx(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mreg_control *mc =
+               (struct soc_mreg_control *)kcontrol->private_value;
+       unsigned int regbase = mc->regbase;
+       unsigned int regcount = mc->regcount;
+       unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
+       unsigned int regwmask = (1<<regwshift)-1;
+       unsigned int invert = mc->invert;
+       unsigned long mask = (1UL<<mc->nbits)-1;
+       long min = mc->min;
+       long max = mc->max;
+       long val = 0;
+       unsigned int regval;
+       unsigned int i;
+       int ret;
+
+       for (i = 0; i < regcount; i++) {
+               ret = snd_soc_component_read(component, regbase+i, &regval);
+               if (ret)
+                       return ret;
+               val |= (regval & regwmask) << (regwshift*(regcount-i-1));
+       }
+       val &= mask;
+       if (min < 0 && val > max)
+               val |= ~mask;
+       if (invert)
+               val = max - val;
+       ucontrol->value.integer.value[0] = val;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_xr_sx);
+
+/**
+ * snd_soc_put_xr_sx - signed multi register get callback
+ * @kcontrol: mreg control
+ * @ucontrol: control element information
+ *
+ * Callback to set the value of a control that can span
+ * multiple codec registers which together forms a single
+ * signed value in a MSB/LSB manner. The control supports
+ * specifying total no of bits used to allow for bitfields
+ * across the multiple codec registers.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_put_xr_sx(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mreg_control *mc =
+               (struct soc_mreg_control *)kcontrol->private_value;
+       unsigned int regbase = mc->regbase;
+       unsigned int regcount = mc->regcount;
+       unsigned int regwshift = component->val_bytes * BITS_PER_BYTE;
+       unsigned int regwmask = (1<<regwshift)-1;
+       unsigned int invert = mc->invert;
+       unsigned long mask = (1UL<<mc->nbits)-1;
+       long max = mc->max;
+       long val = ucontrol->value.integer.value[0];
+       unsigned int i, regval, regmask;
+       int err;
+
+       if (invert)
+               val = max - val;
+       val &= mask;
+       for (i = 0; i < regcount; i++) {
+               regval = (val >> (regwshift*(regcount-i-1))) & regwmask;
+               regmask = (mask >> (regwshift*(regcount-i-1))) & regwmask;
+               err = snd_soc_component_update_bits(component, regbase+i,
+                               regmask, regval);
+               if (err < 0)
+                       return err;
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_xr_sx);
+
+/**
+ * snd_soc_get_strobe - strobe get callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback get the value of a strobe mixer control.
+ *
+ * Returns 0 for success.
+ */
+int snd_soc_get_strobe(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
+       unsigned int mask = 1 << shift;
+       unsigned int invert = mc->invert != 0;
+       unsigned int val;
+       int ret;
+
+       ret = snd_soc_component_read(component, reg, &val);
+       if (ret)
+               return ret;
+
+       val &= mask;
+
+       if (shift != 0 && val != 0)
+               val = val >> shift;
+       ucontrol->value.enumerated.item[0] = val ^ invert;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_get_strobe);
+
+/**
+ * snd_soc_put_strobe - strobe put callback
+ * @kcontrol: mixer control
+ * @ucontrol: control element information
+ *
+ * Callback strobe a register bit to high then low (or the inverse)
+ * in one pass of a single mixer enum control.
+ *
+ * Returns 1 for success.
+ */
+int snd_soc_put_strobe(struct snd_kcontrol *kcontrol,
+       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct soc_mixer_control *mc =
+               (struct soc_mixer_control *)kcontrol->private_value;
+       unsigned int reg = mc->reg;
+       unsigned int shift = mc->shift;
+       unsigned int mask = 1 << shift;
+       unsigned int invert = mc->invert != 0;
+       unsigned int strobe = ucontrol->value.enumerated.item[0] != 0;
+       unsigned int val1 = (strobe ^ invert) ? mask : 0;
+       unsigned int val2 = (strobe ^ invert) ? 0 : mask;
+       int err;
+
+       err = snd_soc_component_update_bits(component, reg, mask, val1);
+       if (err < 0)
+               return err;
+
+       return snd_soc_component_update_bits(component, reg, mask, val2);
+}
+EXPORT_SYMBOL_GPL(snd_soc_put_strobe);
index 57277dd..eb87d96 100644 (file)
@@ -654,6 +654,8 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
                        codec_dai->rate = 0;
        }
 
+       snd_soc_dai_digital_mute(cpu_dai, 1, substream->stream);
+
        if (cpu_dai->driver->ops->shutdown)
                cpu_dai->driver->ops->shutdown(substream, cpu_dai);
 
@@ -772,6 +774,7 @@ static int soc_pcm_prepare(struct snd_pcm_substream *substream)
        for (i = 0; i < rtd->num_codecs; i++)
                snd_soc_dai_digital_mute(rtd->codec_dais[i], 0,
                                         substream->stream);
+       snd_soc_dai_digital_mute(cpu_dai, 0, substream->stream);
 
 out:
        mutex_unlock(&rtd->pcm_mutex);
@@ -1664,6 +1667,10 @@ int dpcm_be_dai_hw_free(struct snd_soc_pcm_runtime *fe, int stream)
                if (!snd_soc_dpcm_can_be_free_stop(fe, be, stream))
                                continue;
 
+               /* do not free hw if this BE is used by other FE */
+               if (be->dpcm[stream].users > 1)
+                       continue;
+
                if ((be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_PARAMS) &&
                    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_PREPARE) &&
                    (be->dpcm[stream].state != SND_SOC_DPCM_STATE_HW_FREE) &&
@@ -2288,7 +2295,13 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card)
                        fe->dai_link->name);
 
                /* skip if FE doesn't have playback capability */
-               if (!fe->cpu_dai->driver->playback.channels_min)
+               if (!fe->cpu_dai->driver->playback.channels_min
+                   || !fe->codec_dai->driver->playback.channels_min)
+                       goto capture;
+
+               /* skip if FE isn't currently playing */
+               if (!fe->cpu_dai->playback_active
+                   || !fe->codec_dai->playback_active)
                        goto capture;
 
                paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_PLAYBACK, &list);
@@ -2318,7 +2331,13 @@ int soc_dpcm_runtime_update(struct snd_soc_card *card)
                dpcm_path_put(&list);
 capture:
                /* skip if FE doesn't have capture capability */
-               if (!fe->cpu_dai->driver->capture.channels_min)
+               if (!fe->cpu_dai->driver->capture.channels_min
+                   || !fe->codec_dai->driver->capture.channels_min)
+                       continue;
+
+               /* skip if FE isn't currently capturing */
+               if (!fe->cpu_dai->capture_active
+                   || !fe->codec_dai->capture_active)
                        continue;
 
                paths = dpcm_path_get(fe, SNDRV_PCM_STREAM_CAPTURE, &list);
index 3b0fa12..29a9957 100644 (file)
@@ -228,7 +228,7 @@ static int tegra20_ac97_probe(struct snd_soc_dai *dai)
 
 static struct snd_soc_dai_driver tegra20_ac97_dai = {
        .name = "tegra-ac97-pcm",
-       .ac97_control = 1,
+       .bus_control = true,
        .probe = tegra20_ac97_probe,
        .playback = {
                .stream_name = "PCM Playback",
index a689883..4ebe387 100644 (file)
@@ -44,6 +44,7 @@
 struct tegra_rt5640 {
        struct tegra_asoc_utils_data util_data;
        int gpio_hp_det;
+       enum of_gpio_flags gpio_hp_det_flags;
 };
 
 static int tegra_rt5640_asoc_hw_params(struct snd_pcm_substream *substream,
@@ -119,6 +120,8 @@ static int tegra_rt5640_asoc_init(struct snd_soc_pcm_runtime *rtd)
 
        if (gpio_is_valid(machine->gpio_hp_det)) {
                tegra_rt5640_hp_jack_gpio.gpio = machine->gpio_hp_det;
+               tegra_rt5640_hp_jack_gpio.invert =
+                       !!(machine->gpio_hp_det_flags & OF_GPIO_ACTIVE_LOW);
                snd_soc_jack_add_gpios(&tegra_rt5640_hp_jack,
                                                1,
                                                &tegra_rt5640_hp_jack_gpio);
@@ -180,7 +183,8 @@ static int tegra_rt5640_probe(struct platform_device *pdev)
        platform_set_drvdata(pdev, card);
        snd_soc_card_set_drvdata(card, machine);
 
-       machine->gpio_hp_det = of_get_named_gpio(np, "nvidia,hp-det-gpios", 0);
+       machine->gpio_hp_det = of_get_named_gpio_flags(
+               np, "nvidia,hp-det-gpios", 0, &machine->gpio_hp_det_flags);
        if (machine->gpio_hp_det == -EPROBE_DEFER)
                return -EPROBE_DEFER;
 
index 9edd68d..f7135cd 100644 (file)
@@ -152,7 +152,7 @@ static int txx9aclc_ac97_remove(struct snd_soc_dai *dai)
 }
 
 static struct snd_soc_dai_driver txx9aclc_ac97_dai = {
-       .ac97_control           = 1,
+       .bus_control            = true,
        .probe                  = txx9aclc_ac97_probe,
        .remove                 = txx9aclc_ac97_remove,
        .playback = {
index cd71fd8..00b7e2d 100644 (file)
@@ -292,7 +292,7 @@ static int txx9aclc_pcm_new(struct snd_soc_pcm_runtime *rtd)
        struct snd_card *card = rtd->card->snd_card;
        struct snd_soc_dai *dai = rtd->cpu_dai;
        struct snd_pcm *pcm = rtd->pcm;
-       struct platform_device *pdev = to_platform_device(dai->platform->dev);
+       struct platform_device *pdev = to_platform_device(rtd->platform->dev);
        struct txx9aclc_soc_device *dev;
        struct resource *r;
        int i;
index b3b66aa..9f2d045 100644 (file)
@@ -63,12 +63,8 @@ static void mop500_of_node_put(void)
        int i;
 
        for (i = 0; i < 2; i++) {
-               if (mop500_dai_links[i].cpu_of_node)
-                       of_node_put((struct device_node *)
-                               mop500_dai_links[i].cpu_of_node);
-               if (mop500_dai_links[i].codec_of_node)
-                       of_node_put((struct device_node *)
-                               mop500_dai_links[i].codec_of_node);
+               of_node_put(mop500_dai_links[i].cpu_of_node);
+               of_node_put(mop500_dai_links[i].codec_of_node);
        }
 }