Merge branch 'for-next' into for-linus
authorTakashi Iwai <tiwai@suse.de>
Mon, 2 Nov 2015 08:00:37 +0000 (09:00 +0100)
committerTakashi Iwai <tiwai@suse.de>
Mon, 2 Nov 2015 08:00:37 +0000 (09:00 +0100)
310 files changed:
Documentation/DocBook/alsa-driver-api.tmpl
Documentation/DocBook/writing-an-alsa-driver.tmpl
Documentation/devicetree/bindings/sound/ak4613.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/ak4642.txt
Documentation/devicetree/bindings/sound/atmel-classd.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/da7213.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/da7219.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/fsl-asoc-card.txt
Documentation/devicetree/bindings/sound/nau8825.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/renesas,rsnd.txt
Documentation/devicetree/bindings/sound/rockchip-i2s.txt
Documentation/devicetree/bindings/sound/rockchip-spdif.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/rt5640.txt
Documentation/devicetree/bindings/sound/sun4i-codec.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/tdm-slot.txt
Documentation/sound/alsa/hda_codec.txt [deleted file]
MAINTAINERS
drivers/gpu/drm/i915/i915_dma.c
drivers/gpu/drm/i915/i915_drv.h
drivers/gpu/drm/i915/intel_audio.c
drivers/spi/spi-atmel.c
include/drm/i915_component.h
include/linux/mod_devicetable.h
include/sound/da7213.h
include/sound/da7219-aad.h [new file with mode: 0644]
include/sound/da7219.h [new file with mode: 0644]
include/sound/designware_i2s.h
include/sound/hda_regmap.h
include/sound/hdaudio.h
include/sound/hdaudio_ext.h
include/sound/pcm.h
include/sound/pxa2xx-lib.h
include/sound/rcar_snd.h [deleted file]
include/sound/rt5640.h
include/sound/rt5645.h
include/sound/simple_card.h
include/sound/soc-dai.h
include/sound/soc-dapm.h
include/sound/soc.h
include/uapi/sound/asoc.h
include/uapi/sound/asound.h
include/uapi/sound/emu10k1.h
include/uapi/sound/firewire.h
include/uapi/sound/hdspm.h
scripts/mod/devicetable-offsets.c
scripts/mod/file2alias.c
sound/arm/pxa2xx-ac97.c
sound/arm/pxa2xx-pcm-lib.c
sound/arm/pxa2xx-pcm.c
sound/arm/pxa2xx-pcm.h
sound/core/Kconfig
sound/core/Makefile
sound/core/oss/mixer_oss.c
sound/core/pcm.c
sound/core/pcm_lib.c
sound/core/pcm_native.c
sound/core/seq/oss/seq_oss_readq.c
sound/core/seq/oss/seq_oss_writeq.c
sound/firewire/Kconfig
sound/firewire/Makefile
sound/firewire/amdtp-am824.c [new file with mode: 0644]
sound/firewire/amdtp-am824.h [new file with mode: 0644]
sound/firewire/amdtp-stream.c [new file with mode: 0644]
sound/firewire/amdtp-stream.h [new file with mode: 0644]
sound/firewire/amdtp.c [deleted file]
sound/firewire/amdtp.h [deleted file]
sound/firewire/bebob/Makefile
sound/firewire/bebob/bebob.c
sound/firewire/bebob/bebob.h
sound/firewire/bebob/bebob_focusrite.c
sound/firewire/bebob/bebob_maudio.c
sound/firewire/bebob/bebob_midi.c
sound/firewire/bebob/bebob_pcm.c
sound/firewire/bebob/bebob_proc.c
sound/firewire/bebob/bebob_stream.c
sound/firewire/bebob/bebob_terratec.c
sound/firewire/bebob/bebob_yamaha.c
sound/firewire/dice/Makefile
sound/firewire/dice/dice-midi.c
sound/firewire/dice/dice-pcm.c
sound/firewire/dice/dice-stream.c
sound/firewire/dice/dice.c
sound/firewire/dice/dice.h
sound/firewire/digi00x/Makefile [new file with mode: 0644]
sound/firewire/digi00x/amdtp-dot.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x-hwdep.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x-midi.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x-pcm.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x-proc.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x-stream.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x-transaction.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x.c [new file with mode: 0644]
sound/firewire/digi00x/digi00x.h [new file with mode: 0644]
sound/firewire/fcp.c
sound/firewire/fireworks/Makefile
sound/firewire/fireworks/fireworks.c
sound/firewire/fireworks/fireworks.h
sound/firewire/fireworks/fireworks_command.c
sound/firewire/fireworks/fireworks_midi.c
sound/firewire/fireworks/fireworks_pcm.c
sound/firewire/fireworks/fireworks_stream.c
sound/firewire/lib.c
sound/firewire/lib.h
sound/firewire/oxfw/Makefile
sound/firewire/oxfw/oxfw-midi.c
sound/firewire/oxfw/oxfw-pcm.c
sound/firewire/oxfw/oxfw-stream.c
sound/firewire/oxfw/oxfw.c
sound/firewire/oxfw/oxfw.h
sound/firewire/tascam/Makefile [new file with mode: 0644]
sound/firewire/tascam/amdtp-tascam.c [new file with mode: 0644]
sound/firewire/tascam/tascam-hwdep.c [new file with mode: 0644]
sound/firewire/tascam/tascam-midi.c [new file with mode: 0644]
sound/firewire/tascam/tascam-pcm.c [new file with mode: 0644]
sound/firewire/tascam/tascam-proc.c [new file with mode: 0644]
sound/firewire/tascam/tascam-stream.c [new file with mode: 0644]
sound/firewire/tascam/tascam-transaction.c [new file with mode: 0644]
sound/firewire/tascam/tascam.c [new file with mode: 0644]
sound/firewire/tascam/tascam.h [new file with mode: 0644]
sound/hda/ext/hdac_ext_stream.c
sound/hda/hda_bus_type.c
sound/hda/hdac_bus.c
sound/hda/hdac_device.c
sound/hda/hdac_i915.c
sound/hda/hdac_regmap.c
sound/hda/hdac_stream.c
sound/hda/hdac_sysfs.c
sound/pci/cs46xx/cs46xx_lib.c
sound/pci/hda/hda_bind.c
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_controller.c
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_local.h
sound/pci/hda/hda_sysfs.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_ca0110.c
sound/pci/hda/patch_ca0132.c
sound/pci/hda/patch_cirrus.c
sound/pci/hda/patch_cmedia.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_si3054.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c
sound/pci/korg1212/korg1212.c
sound/pci/lx6464es/lx6464es.c
sound/pci/maestro3.c
sound/pci/rme32.c
sound/pci/rme96.c
sound/pci/rme9652/hdsp.c
sound/pci/rme9652/hdspm.c
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/atmel/Kconfig
sound/soc/atmel/Makefile
sound/soc/atmel/atmel-classd.c [new file with mode: 0644]
sound/soc/atmel/atmel-classd.h [new file with mode: 0644]
sound/soc/atmel/atmel_wm8904.c
sound/soc/au1x/db1000.c
sound/soc/au1x/db1200.c
sound/soc/blackfin/bf5xx-ad1836.c
sound/soc/blackfin/bfin-eval-adau1373.c
sound/soc/blackfin/bfin-eval-adau1701.c
sound/soc/blackfin/bfin-eval-adav80x.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ad193x-i2c.c
sound/soc/codecs/ad193x-spi.c
sound/soc/codecs/ad193x.c
sound/soc/codecs/ad193x.h
sound/soc/codecs/adav80x.c
sound/soc/codecs/ak4613.c [new file with mode: 0644]
sound/soc/codecs/ak4642.c
sound/soc/codecs/arizona.c
sound/soc/codecs/arizona.h
sound/soc/codecs/da7213.c
sound/soc/codecs/da7213.h
sound/soc/codecs/da7219-aad.c [new file with mode: 0644]
sound/soc/codecs/da7219-aad.h [new file with mode: 0644]
sound/soc/codecs/da7219.c [new file with mode: 0644]
sound/soc/codecs/da7219.h [new file with mode: 0644]
sound/soc/codecs/es8328.c
sound/soc/codecs/hdmi.c [deleted file]
sound/soc/codecs/nau8825.c [new file with mode: 0644]
sound/soc/codecs/nau8825.h [new file with mode: 0644]
sound/soc/codecs/rl6347a.c
sound/soc/codecs/rl6347a.h
sound/soc/codecs/rt286.c
sound/soc/codecs/rt298.c
sound/soc/codecs/rt5640.c
sound/soc/codecs/rt5645.c
sound/soc/codecs/rt5645.h
sound/soc/codecs/ssm2518.c
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/twl4030.c
sound/soc/codecs/uda134x.c
sound/soc/codecs/wl1273.c
sound/soc/codecs/wm2000.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8904.c
sound/soc/codecs/wm8955.c
sound/soc/codecs/wm8960.c
sound/soc/codecs/wm8998.c [new file with mode: 0644]
sound/soc/codecs/wm8998.h [new file with mode: 0644]
sound/soc/davinci/davinci-mcasp.c
sound/soc/dwc/designware_i2s.c
sound/soc/fsl/fsl-asoc-card.c
sound/soc/fsl/fsl_esai.c
sound/soc/fsl/fsl_sai.c
sound/soc/fsl/fsl_spdif.c
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/imx-spdif.c
sound/soc/generic/simple-card.c
sound/soc/intel/Kconfig
sound/soc/intel/atom/sst-mfld-platform-pcm.c
sound/soc/intel/boards/Makefile
sound/soc/intel/boards/broadwell.c
sound/soc/intel/boards/bytcr_rt5640.c
sound/soc/intel/boards/cht_bsw_max98090_ti.c
sound/soc/intel/boards/cht_bsw_rt5645.c
sound/soc/intel/boards/cht_bsw_rt5672.c
sound/soc/intel/boards/skl_rt286.c [new file with mode: 0644]
sound/soc/intel/common/Makefile
sound/soc/intel/common/sst-dsp-priv.h
sound/soc/intel/common/sst-dsp.c
sound/soc/intel/common/sst-dsp.h
sound/soc/intel/common/sst-firmware.c
sound/soc/intel/skylake/Makefile
sound/soc/intel/skylake/skl-messages.c
sound/soc/intel/skylake/skl-nhlt.c
sound/soc/intel/skylake/skl-pcm.c
sound/soc/intel/skylake/skl-sst-dsp.c
sound/soc/intel/skylake/skl-sst-ipc.c
sound/soc/intel/skylake/skl-sst-ipc.h
sound/soc/intel/skylake/skl-sst.c
sound/soc/intel/skylake/skl-topology.c [new file with mode: 0644]
sound/soc/intel/skylake/skl-topology.h
sound/soc/intel/skylake/skl-tplg-interface.h
sound/soc/intel/skylake/skl.c
sound/soc/intel/skylake/skl.h
sound/soc/jz4740/jz4740-i2s.c
sound/soc/kirkwood/armada-370-db.c
sound/soc/mediatek/mt8173-max98090.c
sound/soc/mediatek/mt8173-rt5650-rt5676.c
sound/soc/mxs/mxs-sgtl5000.c
sound/soc/omap/n810.c
sound/soc/omap/rx51.c
sound/soc/pxa/brownstone.c
sound/soc/pxa/corgi.c
sound/soc/pxa/e740_wm9705.c
sound/soc/pxa/e750_wm9705.c
sound/soc/pxa/e800_wm9712.c
sound/soc/pxa/hx4700.c
sound/soc/pxa/imote2.c
sound/soc/pxa/mioa701_wm9713.c
sound/soc/pxa/palm27x.c
sound/soc/pxa/poodle.c
sound/soc/pxa/pxa-ssp.c
sound/soc/pxa/pxa2xx-ac97.c
sound/soc/pxa/pxa2xx-i2s.c
sound/soc/pxa/pxa2xx-pcm.c
sound/soc/pxa/spitz.c
sound/soc/pxa/tosa.c
sound/soc/pxa/ttc-dkb.c
sound/soc/qcom/lpass-cpu.c
sound/soc/rockchip/Kconfig
sound/soc/rockchip/Makefile
sound/soc/rockchip/rockchip_i2s.c
sound/soc/rockchip/rockchip_i2s.h
sound/soc/rockchip/rockchip_spdif.c [new file with mode: 0644]
sound/soc/rockchip/rockchip_spdif.h [new file with mode: 0644]
sound/soc/samsung/h1940_uda1380.c
sound/soc/samsung/rx1950_uda1380.c
sound/soc/sh/Kconfig
sound/soc/sh/rcar/adg.c
sound/soc/sh/rcar/core.c
sound/soc/sh/rcar/ctu.c
sound/soc/sh/rcar/dma.c
sound/soc/sh/rcar/dvc.c
sound/soc/sh/rcar/gen.c
sound/soc/sh/rcar/mix.c
sound/soc/sh/rcar/rcar_snd.h [new file with mode: 0644]
sound/soc/sh/rcar/rsnd.h
sound/soc/sh/rcar/src.c
sound/soc/sh/rcar/ssi.c
sound/soc/sh/siu_dai.c
sound/soc/soc-compress.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-ops.c
sound/soc/soc-pcm.c
sound/soc/soc-topology.c
sound/soc/sunxi/Kconfig [new file with mode: 0644]
sound/soc/sunxi/Makefile [new file with mode: 0644]
sound/soc/sunxi/sun4i-codec.c [new file with mode: 0644]
sound/soc/ux500/mop500.c
sound/soc/ux500/ux500_msp_dai.c
sound/usb/card.h
sound/usb/endpoint.c
sound/usb/midi.c
sound/usb/mixer_quirks.c
sound/usb/pcm.c
sound/usb/quirks-table.h
sound/usb/quirks.c
sound/usb/stream.c
sound/usb/usbaudio.h

index e94a10bb4a9e9d0f6fb9ff3e1aa47af4892f11c2..53f439dcc94b7d4e3a7c9ba237f03e94519c1315 100644 (file)
 !Esound/soc/soc-devres.c
 !Esound/soc/soc-io.c
 !Esound/soc/soc-pcm.c
+!Esound/soc/soc-ops.c
+!Esound/soc/soc-compress.c
      </sect1>
      <sect1><title>ASoC DAPM API</title>
 !Esound/soc/soc-dapm.c
index 84ef6a90131c2ea44410f9f3c53f6b56a499bfe5..a27ab9f53fb68a62652bff8ed847322a838cd41d 100644 (file)
@@ -2181,10 +2181,6 @@ struct _snd_pcm_runtime {
        struct snd_pcm_hardware hw;
        struct snd_pcm_hw_constraints hw_constraints;
 
-       /* -- interrupt callbacks -- */
-       void (*transfer_ack_begin)(struct snd_pcm_substream *substream);
-       void (*transfer_ack_end)(struct snd_pcm_substream *substream);
-
        /* -- timer -- */
        unsigned int timer_resolution;  /* timer resolution */
 
@@ -2209,9 +2205,8 @@ struct _snd_pcm_runtime {
          For the operators (callbacks) of each sound driver, most of
        these records are supposed to be read-only.  Only the PCM
        middle-layer changes / updates them.  The exceptions are
-       the hardware description (hw), interrupt callbacks
-       (transfer_ack_xxx), DMA buffer information, and the private
-       data.  Besides, if you use the standard buffer allocation
+       the hardware description (hw) DMA buffer information and the
+       private data.  Besides, if you use the standard buffer allocation
        method via <function>snd_pcm_lib_malloc_pages()</function>,
        you don't need to set the DMA buffer information by yourself.
        </para>
@@ -2538,16 +2533,6 @@ struct _snd_pcm_runtime {
         </para>
        </section>
 
-       <section id="pcm-interface-runtime-intr">
-       <title>Interrupt Callbacks</title>
-       <para>
-       The field <structfield>transfer_ack_begin</structfield> and
-       <structfield>transfer_ack_end</structfield> are called at
-       the beginning and at the end of
-       <function>snd_pcm_period_elapsed()</function>, respectively. 
-       </para>
-       </section>
-
     </section>
 
     <section id="pcm-interface-operators">
diff --git a/Documentation/devicetree/bindings/sound/ak4613.txt b/Documentation/devicetree/bindings/sound/ak4613.txt
new file mode 100644 (file)
index 0000000..15a9195
--- /dev/null
@@ -0,0 +1,17 @@
+AK4613 I2C transmitter
+
+This device supports I2C mode only.
+
+Required properties:
+
+- compatible : "asahi-kasei,ak4613"
+- reg : The chip select number on the I2C bus
+
+Example:
+
+&i2c {
+       ak4613: ak4613@0x10 {
+               compatible = "asahi-kasei,ak4613";
+               reg = <0x10>;
+       };
+};
index 623d4e70ae11d9ef5ee6231c09f0e8293fb32680..340784db6808809eecf4ae0e310023550713e2c6 100644 (file)
@@ -7,7 +7,14 @@ Required properties:
   - compatible : "asahi-kasei,ak4642" or "asahi-kasei,ak4643" or "asahi-kasei,ak4648"
   - reg : The chip select number on the I2C bus
 
-Example:
+Optional properties:
+
+  - #clock-cells :             common clock binding; shall be set to 0
+  - clocks :                   common clock binding; MCKI clock
+  - clock-frequency :          common clock binding; frequency of MCKO
+  - clock-output-names :       common clock binding; MCKO clock name
+
+Example 1:
 
 &i2c {
        ak4648: ak4648@0x12 {
@@ -15,3 +22,16 @@ Example:
                reg = <0x12>;
        };
 };
+
+Example 2:
+
+&i2c {
+       ak4643: codec@12 {
+               compatible = "asahi-kasei,ak4643";
+               reg = <0x12>;
+               #clock-cells = <0>;
+               clocks = <&audio_clock>;
+               clock-frequency = <12288000>;
+               clock-output-names = "ak4643_mcko";
+       };
+};
diff --git a/Documentation/devicetree/bindings/sound/atmel-classd.txt b/Documentation/devicetree/bindings/sound/atmel-classd.txt
new file mode 100644 (file)
index 0000000..0018451
--- /dev/null
@@ -0,0 +1,52 @@
+* Atmel ClassD driver under ALSA SoC architecture
+
+Required properties:
+- compatible
+       Should be "atmel,sama5d2-classd".
+- reg
+       Should contain ClassD registers location and length.
+- interrupts
+       Should contain the IRQ line for the ClassD.
+- dmas
+       One DMA specifiers as described in atmel-dma.txt and dma.txt files.
+- dma-names
+       Must be "tx".
+- clock-names
+       Tuple listing input clock names.
+       Required elements: "pclk", "gclk" and "aclk".
+- clocks
+       Please refer to clock-bindings.txt.
+
+Optional properties:
+- pinctrl-names, pinctrl-0
+       Please refer to pinctrl-bindings.txt.
+- atmel,model
+       The user-visible name of this sound complex.
+       The default value is "CLASSD".
+- atmel,pwm-type
+       PWM modulation type, "single" or "diff".
+       The default value is "single".
+- atmel,non-overlap-time
+       Set non-overlapping time, the unit is nanosecond(ns).
+       There are four values,
+       <5>, <10>, <15>, <20>, the default value is <10>.
+       Non-overlapping will be disabled if not specified.
+
+Example:
+classd: classd@fc048000 {
+               compatible = "atmel,sama5d2-classd";
+               reg = <0xfc048000 0x100>;
+               interrupts = <59 IRQ_TYPE_LEVEL_HIGH 7>;
+               dmas = <&dma0
+                       (AT91_XDMAC_DT_MEM_IF(0) | AT91_XDMAC_DT_PER_IF(1)
+                       | AT91_XDMAC_DT_PERID(47))>;
+               dma-names = "tx";
+               clocks = <&classd_clk>, <&classd_gclk>, <&audio_pll_pmc>;
+               clock-names = "pclk", "gclk", "aclk";
+
+               pinctrl-names = "default";
+               pinctrl-0 = <&pinctrl_classd_default>;
+               atmel,model = "classd @ SAMA5D2-Xplained";
+               atmel,pwm-type = "diff";
+               atmel,non-overlap-time = <10>;
+};
diff --git a/Documentation/devicetree/bindings/sound/da7213.txt b/Documentation/devicetree/bindings/sound/da7213.txt
new file mode 100644 (file)
index 0000000..5890280
--- /dev/null
@@ -0,0 +1,41 @@
+Dialog Semiconductor DA7213 Audio Codec bindings
+
+======
+
+Required properties:
+- compatible : Should be "dlg,da7213"
+- reg: Specifies the I2C slave address
+
+Optional properties:
+- clocks : phandle and clock specifier for codec MCLK.
+- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
+
+- dlg,micbias1-lvl : Voltage (mV) for Mic Bias 1
+       [<1600>, <2200>, <2500>, <3000>]
+- dlg,micbias2-lvl : Voltage (mV) for Mic Bias 2
+       [<1600>, <2200>, <2500>, <3000>]
+- dlg,dmic-data-sel : DMIC channel select based on clock edge.
+       ["lrise_rfall", "lfall_rrise"]
+- dlg,dmic-samplephase : When to sample audio from DMIC.
+       ["on_clkedge", "between_clkedge"]
+- dlg,dmic-clkrate : DMIC clock frequency (Hz).
+       [<1500000>, <3000000>]
+
+======
+
+Example:
+
+       codec_i2c: da7213@1a {
+               compatible = "dlg,da7213";
+               reg = <0x1a>;
+
+               clocks = <&clks 201>;
+               clock-names = "mclk";
+
+               dlg,micbias1-lvl = <2500>;
+               dlg,micbias2-lvl = <2500>;
+
+               dlg,dmic-data-sel = "lrise_rfall";
+               dlg,dmic-samplephase = "between_clkedge";
+               dlg,dmic-clkrate = <3000000>;
+       };
diff --git a/Documentation/devicetree/bindings/sound/da7219.txt b/Documentation/devicetree/bindings/sound/da7219.txt
new file mode 100644 (file)
index 0000000..1b70309
--- /dev/null
@@ -0,0 +1,106 @@
+Dialog Semiconductor DA7219 Audio Codec bindings
+
+DA7219 is an audio codec with advanced accessory detect features.
+
+======
+
+Required properties:
+- compatible : Should be "dlg,da7219"
+- reg: Specifies the I2C slave address
+
+- interrupt-parent : Specifies the phandle of the interrupt controller to which
+  the IRQs from DA7219 are delivered to.
+- interrupts : IRQ line info for DA7219.
+  (See Documentation/devicetree/bindings/interrupt-controller/interrupts.txt for
+   further information relating to interrupt properties)
+
+- VDD-supply: VDD power supply for the device
+- VDDMIC-supply: VDDMIC power supply for the device
+- VDDIO-supply: VDDIO power supply for the device
+  (See Documentation/devicetree/bindings/regulator/regulator.txt for further
+   information relating to regulators)
+
+Optional properties:
+- interrupt-names : Name associated with interrupt line. Should be "wakeup" if
+  interrupt is to be used to wake system, otherwise "irq" should be used.
+- wakeup-source: Flag to indicate this device can wake system (suspend/resume).
+
+- clocks : phandle and clock specifier for codec MCLK.
+- clock-names : Clock name string for 'clocks' attribute, should be "mclk".
+
+- dlg,ldo-lvl : Required internal LDO voltage (mV) level for digital engine
+       [<1050>, <1100>, <1200>, <1400>]
+- dlg,micbias-lvl : Voltage (mV) for Mic Bias
+       [<1800>, <2000>, <2200>, <2400>, <2600>]
+- dlg,mic-amp-in-sel : Mic input source type
+       ["diff", "se_p", "se_n"]
+
+======
+
+Child node - 'da7219_aad':
+
+Optional properties:
+- dlg,micbias-pulse-lvl : Mic bias higher voltage pulse level (mV).
+       [<2800>, <2900>]
+- dlg,micbias-pulse-time : Mic bias higher voltage pulse duration (ms)
+- dlg,btn-cfg : Periodic button press measurements for 4-pole jack (ms)
+       [<2>, <5>, <10>, <50>, <100>, <200>, <500>]
+- dlg,mic-det-thr : Impedance threshold for mic detection measurement (Ohms)
+       [<200>, <500>, <750>, <1000>]
+- dlg,jack-ins-deb : Debounce time for jack insertion (ms)
+       [<5>, <10>, <20>, <50>, <100>, <200>, <500>, <1000>]
+- dlg,jack-det-rate: Jack type detection latency (3/4 pole)
+       ["32ms_64ms", "64ms_128ms", "128ms_256ms", "256ms_512ms"]
+- dlg,jack-rem-deb : Debounce time for jack removal (ms)
+       [<1>, <5>, <10>, <20>]
+- dlg,a-d-btn-thr : Impedance threshold between buttons A and D
+       [0x0 - 0xFF]
+- dlg,d-b-btn-thr : Impedance threshold between buttons D and B
+       [0x0 - 0xFF]
+- dlg,b-c-btn-thr : Impedance threshold between buttons B and C
+       [0x0 - 0xFF]
+- dlg,c-mic-btn-thr : Impedance threshold between button C and Mic
+       [0x0 - 0xFF]
+- dlg,btn-avg : Number of 8-bit readings for averaged button measurement
+       [<1>, <2>, <4>, <8>]
+- dlg,adc-1bit-rpt : Repeat count for 1-bit button measurement
+       [<1>, <2>, <4>, <8>]
+
+======
+
+Example:
+
+       codec: da7219@1a {
+               compatible = "dlg,da7219";
+               reg = <0x1a>;
+
+               interrupt-parent = <&gpio6>;
+               interrupts = <11 IRQ_TYPE_LEVEL_HIGH>;
+
+               VDD-supply = <&reg_audio>;
+               VDDMIC-supply = <&reg_audio>;
+               VDDIO-supply = <&reg_audio>;
+
+               clocks = <&clks 201>;
+               clock-names = "mclk";
+
+               dlg,ldo-lvl = <1200>;
+               dlg,micbias-lvl = <2600>;
+               dlg,mic-amp-in-sel = "diff";
+
+               da7219_aad {
+                       dlg,btn-cfg = <50>;
+                       dlg,mic-det-thr = <500>;
+                       dlg,jack-ins-deb = <20>;
+                       dlg,jack-det-rate = "32ms_64ms";
+                       dlg,jack-rem-deb = <1>;
+
+                       dlg,a-d-btn-thr = <0xa>;
+                       dlg,d-b-btn-thr = <0x16>;
+                       dlg,b-c-btn-thr = <0x21>;
+                       dlg,c-mic-btn-thr = <0x3E>;
+
+                       dlg,btn-avg = <4>;
+                       dlg,adc-1bit-rpt = <1>;
+               };
+       };
index a96774c194c8be9f242c1db4ca0398283e10a203..ce55c0a6f7578ee192a207a6b247095d605072b8 100644 (file)
@@ -13,13 +13,15 @@ So having this generic sound card allows all Freescale SoC users to benefit
 from the simplification of a new card support and the capability of the wide
 sample rates support through ASRC.
 
-Note: The card is initially designed for those sound cards who use I2S and
-      PCM DAI formats. However, it'll be also possible to support those non
-      I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as long
-      as the driver has been properly upgraded.
+Note: The card is initially designed for those sound cards who use AC'97, I2S
+      and PCM DAI formats. However, it'll be also possible to support those non
+      AC'97/I2S/PCM type sound cards, such as S/PDIF audio and HDMI audio, as
+      long as the driver has been properly upgraded.
 
 
 The compatible list for this generic sound card currently:
+ "fsl,imx-audio-ac97"
+
  "fsl,imx-audio-cs42888"
 
  "fsl,imx-audio-wm8962"
diff --git a/Documentation/devicetree/bindings/sound/nau8825.txt b/Documentation/devicetree/bindings/sound/nau8825.txt
new file mode 100644 (file)
index 0000000..d337423
--- /dev/null
@@ -0,0 +1,102 @@
+Nuvoton NAU8825 audio codec
+
+This device supports I2C only.
+
+Required properties:
+  - compatible : Must be "nuvoton,nau8825"
+
+  - reg : the I2C address of the device. This is either 0x1a (CSB=0) or 0x1b (CSB=1).
+
+Optional properties:
+  - nuvoton,jkdet-enable: Enable jack detection via JKDET pin.
+  - nuvoton,jkdet-pull-enable: Enable JKDET pin pull. If set - pin pull enabled,
+      otherwise pin in high impedance state.
+  - nuvoton,jkdet-pull-up: Pull-up JKDET pin. If set then JKDET pin is pull up, otherwise pull down.
+  - nuvoton,jkdet-polarity: JKDET pin polarity. 0 - active high, 1 - active low.
+
+  - nuvoton,vref-impedance: VREF Impedance selection
+      0 - Open
+      1 - 25 kOhm
+      2 - 125 kOhm
+      3 - 2.5 kOhm
+
+  - nuvoton,micbias-voltage: Micbias voltage level.
+      0 - VDDA
+      1 - VDDA
+      2 - VDDA * 1.1
+      3 - VDDA * 1.2
+      4 - VDDA * 1.3
+      5 - VDDA * 1.4
+      6 - VDDA * 1.53
+      7 - VDDA * 1.53
+
+  - nuvoton,sar-threshold-num: Number of buttons supported
+  - nuvoton,sar-threshold: Impedance threshold for each button. Array that contains up to 8 buttons configuration. SAR value is calculated as
+    SAR = 255 * MICBIAS / SAR_VOLTAGE * R / (2000 + R)
+    where MICBIAS is configured by 'nuvoton,micbias-voltage', SAR_VOLTAGE is configured by 'nuvoton,sar-voltage', R - button impedance.
+    Refer datasheet section 10.2 for more information about threshold calculation.
+
+  - nuvoton,sar-hysteresis: Button impedance measurement hysteresis.
+
+  - nuvoton,sar-voltage: Reference voltage for button impedance measurement.
+      0 - VDDA
+      1 - VDDA
+      2 - VDDA * 1.1
+      3 - VDDA * 1.2
+      4 - VDDA * 1.3
+      5 - VDDA * 1.4
+      6 - VDDA * 1.53
+      7 - VDDA * 1.53
+
+  - nuvoton,sar-compare-time: SAR compare time
+      0 - 500 ns
+      1 - 1 us
+      2 - 2 us
+      3 - 4 us
+
+  - nuvoton,sar-sampling-time: SAR sampling time
+      0 - 2 us
+      1 - 4 us
+      2 - 8 us
+      3 - 16 us
+
+  - nuvoton,short-key-debounce: Button short key press debounce time.
+      0 - 30 ms
+      1 - 50 ms
+      2 - 100 ms
+      3 - 30 ms
+
+  - nuvoton,jack-insert-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms
+  - nuvoton,jack-eject-debounce: number from 0 to 7 that sets debounce time to 2^(n+2) ms
+
+  - clocks: list of phandle and clock specifier pairs according to common clock bindings for the
+      clocks described in clock-names
+  - clock-names: should include "mclk" for the MCLK master clock
+
+Example:
+
+  headset: nau8825@1a {
+      compatible = "nuvoton,nau8825";
+      reg = <0x1a>;
+      interrupt-parent = <&gpio>;
+      interrupts = <TEGRA_GPIO(E, 6) IRQ_TYPE_LEVEL_LOW>;
+      nuvoton,jkdet-enable;
+      nuvoton,jkdet-pull-enable;
+      nuvoton,jkdet-pull-up;
+      nuvoton,jkdet-polarity = <GPIO_ACTIVE_LOW>;
+      nuvoton,vref-impedance = <2>;
+      nuvoton,micbias-voltage = <6>;
+      // Setup 4 buttons impedance according to Android specification
+      nuvoton,sar-threshold-num = <4>;
+      nuvoton,sar-threshold = <0xc 0x1e 0x38 0x60>;
+      nuvoton,sar-hysteresis = <1>;
+      nuvoton,sar-voltage = <0>;
+      nuvoton,sar-compare-time = <0>;
+      nuvoton,sar-sampling-time = <0>;
+      nuvoton,short-key-debounce = <2>;
+      nuvoton,jack-insert-debounce = <7>;
+      nuvoton,jack-eject-debounce = <7>;
+
+      clock-names = "mclk";
+      clocks = <&tegra_car TEGRA210_CLK_CLK_OUT_2>;
+  };
index 1173395b5e5c16c070885b06ae06100eb0aa7ff2..c57cbd65736cfaf4bd67c0801fcd86dca19ef106 100644 (file)
@@ -4,10 +4,12 @@ Required properties:
 - compatible                   : "renesas,rcar_sound-<soctype>", fallbacks
                                  "renesas,rcar_sound-gen1" if generation1, and
                                  "renesas,rcar_sound-gen2" if generation2
+                                 "renesas,rcar_sound-gen3" if generation3
                                  Examples with soctypes are:
                                    - "renesas,rcar_sound-r8a7778" (R-Car M1A)
                                    - "renesas,rcar_sound-r8a7790" (R-Car H2)
                                    - "renesas,rcar_sound-r8a7791" (R-Car M2-W)
+                                   - "renesas,rcar_sound-r8a7795" (R-Car H3)
 - reg                          : Should contain the register physical address.
                                  required register is
                                   SRU/ADG/SSI      if generation1
@@ -30,6 +32,11 @@ Required properties:
 - rcar_sound,dai               : DAI contents.
                                  The number of DAI subnode should be same as HW.
                                  see below for detail.
+- #sound-dai-cells             : it must be 0 if your system is using single DAI
+                                 it must be 1 if your system is using multi  DAI
+- #clock-cells                 : it must be 0 if your system has audio_clkout
+                                 it must be 1 if your system has audio_clkout0/1/2/3
+- clock-frequency              : for all audio_clkout0/1/2/3
 
 SSI subnode properties:
 - interrupts                   : Should contain SSI interrupt for PIO transfer
index 9b82c20b306bb1e10e6bf8be84dc2a9c97933438..2267d249ca0ee20959b525d1d2075e9e09909d9e 100644 (file)
@@ -12,8 +12,6 @@ Required properties:
 - reg: physical base address of the controller and length of memory mapped
   region.
 - interrupts: should contain the I2S interrupt.
-- #address-cells: should be 1.
-- #size-cells: should be 0.
 - dmas: DMA specifiers for tx and rx dma. See the DMA client binding,
        Documentation/devicetree/bindings/dma/dma.txt
 - dma-names: should include "tx" and "rx".
@@ -21,6 +19,7 @@ Required properties:
 - clock-names: should contain followings:
    - "i2s_hclk": clock for I2S BUS
    - "i2s_clk" : clock for I2S controller
+- rockchip,capture-channels: max capture channels, if not set, 2 channels default.
 
 Example for rk3288 I2S controller:
 
@@ -28,10 +27,9 @@ i2s@ff890000 {
        compatible = "rockchip,rk3288-i2s", "rockchip,rk3066-i2s";
        reg = <0xff890000 0x10000>;
        interrupts = <GIC_SPI 85 IRQ_TYPE_LEVEL_HIGH>;
-       #address-cells = <1>;
-       #size-cells = <0>;
        dmas = <&pdma1 0>, <&pdma1 1>;
        dma-names = "tx", "rx";
        clock-names = "i2s_hclk", "i2s_clk";
        clocks = <&cru HCLK_I2S0>, <&cru SCLK_I2S0>;
+       rockchip,capture-channels = <2>;
 };
diff --git a/Documentation/devicetree/bindings/sound/rockchip-spdif.txt b/Documentation/devicetree/bindings/sound/rockchip-spdif.txt
new file mode 100644 (file)
index 0000000..e64dbde
--- /dev/null
@@ -0,0 +1,40 @@
+* Rockchip SPDIF transceiver
+
+The S/PDIF audio block is a stereo transceiver that allows the
+processor to receive and transmit digital audio via an coaxial cable or
+a fibre cable.
+
+Required properties:
+
+- compatible: should be one of the following:
+   - "rockchip,rk3288-spdif", "rockchip,rk3188-spdif" or
+     "rockchip,rk3066-spdif"
+- reg: physical base address of the controller and length of memory mapped
+  region.
+- interrupts: should contain the SPDIF interrupt.
+- dmas: DMA specifiers for tx dma. See the DMA client binding,
+  Documentation/devicetree/bindings/dma/dma.txt
+- dma-names: should be "tx"
+- clocks: a list of phandle + clock-specifier pairs, one for each entry
+  in clock-names.
+- clock-names: should contain following:
+   - "hclk": clock for SPDIF controller
+   - "mclk" : clock for SPDIF bus
+
+Required properties on RK3288:
+  - rockchip,grf: the phandle of the syscon node for the general register
+                   file (GRF)
+
+Example for the rk3188 SPDIF controller:
+
+spdif: spdif@0x1011e000 {
+       compatible = "rockchip,rk3188-spdif", "rockchip,rk3066-spdif";
+       reg = <0x1011e000 0x2000>;
+       interrupts = <GIC_SPI 32 IRQ_TYPE_LEVEL_HIGH>;
+       dmas = <&dmac1_s 8>;
+       dma-names = "tx";
+       clock-names = "hclk", "mclk";
+       clocks = <&cru HCLK_SPDIF>, <&cru SCLK_SPDIF>;
+       status = "disabled";
+       #sound-dai-cells = <0>;
+};
index bac4d9ac1edc8c45df26134e59c9537b1ce6b3af..9e62f6eb348f9499923dbcb473dad6ff22678bfa 100644 (file)
@@ -14,7 +14,8 @@ Optional properties:
 
 - realtek,in1-differential
 - realtek,in2-differential
-  Boolean. Indicate MIC1/2 input are differential, rather than single-ended.
+- realtek,in3-differential
+  Boolean. Indicate MIC1/2/3 input are differential, rather than single-ended.
 
 - realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin.
 
@@ -24,9 +25,11 @@ Pins on the device (for linking into audio routes) for RT5639/RT5640:
   * DMIC2
   * MICBIAS1
   * IN1P
-  * IN1R
+  * IN1N
   * IN2P
-  * IN2R
+  * IN2N
+  * IN3P
+  * IN3N
   * HPOL
   * HPOR
   * LOUTL
diff --git a/Documentation/devicetree/bindings/sound/sun4i-codec.txt b/Documentation/devicetree/bindings/sound/sun4i-codec.txt
new file mode 100644 (file)
index 0000000..c92966b
--- /dev/null
@@ -0,0 +1,27 @@
+* Allwinner A10 Codec
+
+Required properties:
+- compatible: must be either "allwinner,sun4i-a10-codec" or
+  "allwinner,sun7i-a20-codec"
+- reg: must contain the registers location and length
+- interrupts: must contain the codec interrupt
+- dmas: DMA channels for tx and rx dma. See the DMA client binding,
+       Documentation/devicetree/bindings/dma/dma.txt
+- dma-names: should include "tx" and "rx".
+- clocks: a list of phandle + clock-specifer pairs, one for each entry
+  in clock-names.
+- clock-names: should contain followings:
+   - "apb": the parent APB clock for this controller
+   - "codec": the parent module clock
+
+Example:
+codec: codec@01c22c00 {
+       #sound-dai-cells = <0>;
+       compatible = "allwinner,sun7i-a20-codec";
+       reg = <0x01c22c00 0x40>;
+       interrupts = <0 30 4>;
+       clocks = <&apb0_gates 0>, <&codec_clk>;
+       clock-names = "apb", "codec";
+       dmas = <&dma 0 19>, <&dma 0 19>;
+       dma-names = "rx", "tx";
+};
index 6a2c84247f91520a4e7bd2fc90e232e8a9e6cb05..34cf70e2cbc4720510cf03e05eba6f3bcb6b2cae 100644 (file)
@@ -4,11 +4,15 @@ This specifies audio DAI's TDM slot.
 
 TDM slot properties:
 dai-tdm-slot-num : Number of slots in use.
-dai-tdm-slot-width :  Width in bits for each slot.
+dai-tdm-slot-width : Width in bits for each slot.
+dai-tdm-slot-tx-mask : Transmit direction slot mask, optional
+dai-tdm-slot-rx-mask : Receive direction slot mask, optional
 
 For instance:
        dai-tdm-slot-num = <2>;
        dai-tdm-slot-width = <8>;
+       dai-tdm-slot-tx-mask = <0 1>;
+       dai-tdm-slot-rx-mask = <1 0>;
 
 And for each spcified driver, there could be one .of_xlate_tdm_slot_mask()
 to specify a explicit mapping of the channels and the slots. If it's absent
@@ -18,3 +22,8 @@ tx and rx masks.
 For snd_soc_of_xlate_tdm_slot_mask(), the tx and rx masks will use a 1 bit
 for an active slot as default, and the default active bits are at the LSB of
 the masks.
+
+The explicit masks are given as array of integers, where the first
+number presents bit-0 (LSB), second presents bit-1, etc. Any non zero
+number is considered 1 and 0 is 0. snd_soc_of_xlate_tdm_slot_mask()
+does not do anything, if either mask is set non zero value.
diff --git a/Documentation/sound/alsa/hda_codec.txt b/Documentation/sound/alsa/hda_codec.txt
deleted file mode 100644 (file)
index de8efbc..0000000
+++ /dev/null
@@ -1,322 +0,0 @@
-Notes on Universal Interface for Intel High Definition Audio Codec
-------------------------------------------------------------------
-
-Takashi Iwai <tiwai@suse.de>
-
-
-[Still a draft version]
-
-
-General
-=======
-
-The snd-hda-codec module supports the generic access function for the
-High Definition (HD) audio codecs.  It's designed to be independent
-from the controller code like ac97 codec module.  The real accessors
-from/to the controller must be implemented in the lowlevel driver.
-
-The structure of this module is similar with ac97_codec module.
-Each codec chip belongs to a bus class which communicates with the
-controller.
-
-
-Initialization of Bus Instance
-==============================
-
-The card driver has to create struct hda_bus at first.  The template
-struct should be filled and passed to the constructor:
-
-struct hda_bus_template {
-       void *private_data;
-       struct pci_dev *pci;
-       const char *modelname;
-       struct hda_bus_ops ops;
-};
-
-The card driver can set and use the private_data field to retrieve its
-own data in callback functions.  The pci field is used when the patch
-needs to check the PCI subsystem IDs, so on.  For non-PCI system, it
-doesn't have to be set, of course.
-The modelname field specifies the board's specific configuration.  The
-string is passed to the codec parser, and it depends on the parser how
-the string is used.
-These fields, private_data, pci and modelname are all optional.
-
-The ops field contains the callback functions as the following:
-
-struct hda_bus_ops {
-       int (*command)(struct hda_codec *codec, hda_nid_t nid, int direct,
-                      unsigned int verb, unsigned int parm);
-       unsigned int (*get_response)(struct hda_codec *codec);
-       void (*private_free)(struct hda_bus *);
-#ifdef CONFIG_SND_HDA_POWER_SAVE
-       void (*pm_notify)(struct hda_codec *codec);
-#endif
-};
-
-The command callback is called when the codec module needs to send a
-VERB to the controller.  It's always a single command.
-The get_response callback is called when the codec requires the answer
-for the last command.  These two callbacks are mandatory and have to
-be given.
-The third, private_free callback, is optional.  It's called in the
-destructor to release any necessary data in the lowlevel driver.
-
-The pm_notify callback is available only with
-CONFIG_SND_HDA_POWER_SAVE kconfig.  It's called when the codec needs
-to power up or may power down.  The controller should check the all
-belonging codecs on the bus whether they are actually powered off
-(check codec->power_on), and optionally the driver may power down the
-controller side, too.
-
-The bus instance is created via snd_hda_bus_new().  You need to pass
-the card instance, the template, and the pointer to store the
-resultant bus instance.
-
-int snd_hda_bus_new(struct snd_card *card, const struct hda_bus_template *temp,
-                   struct hda_bus **busp);
-
-It returns zero if successful.  A negative return value means any
-error during creation.
-
-
-Creation of Codec Instance
-==========================
-
-Each codec chip on the board is then created on the BUS instance.
-To create a codec instance, call snd_hda_codec_new().
-
-int snd_hda_codec_new(struct hda_bus *bus, unsigned int codec_addr,
-                     struct hda_codec **codecp);
-
-The first argument is the BUS instance, the second argument is the
-address of the codec, and the last one is the pointer to store the
-resultant codec instance (can be NULL if not needed).
-
-The codec is stored in a linked list of bus instance.  You can follow
-the codec list like:
-
-       struct hda_codec *codec;
-       list_for_each_entry(codec, &bus->codec_list, list) {
-               ...
-       }
-
-The codec isn't initialized at this stage properly.  The
-initialization sequence is called when the controls are built later.
-
-
-Codec Access
-============
-
-To access codec, use snd_hda_codec_read() and snd_hda_codec_write().
-snd_hda_param_read() is for reading parameters.
-For writing a sequence of verbs, use snd_hda_sequence_write().
-
-There are variants of cached read/write, snd_hda_codec_write_cache(),
-snd_hda_sequence_write_cache().  These are used for recording the
-register states for the power-management resume.  When no PM is needed,
-these are equivalent with non-cached version.
-
-To retrieve the number of sub nodes connected to the given node, use
-snd_hda_get_sub_nodes().  The connection list can be obtained via
-snd_hda_get_connections() call.
-
-When an unsolicited event happens, pass the event via
-snd_hda_queue_unsol_event() so that the codec routines will process it
-later.
-
-
-(Mixer) Controls
-================
-
-To create mixer controls of all codecs, call
-snd_hda_build_controls().  It then builds the mixers and does
-initialization stuff on each codec.
-
-
-PCM Stuff
-=========
-
-snd_hda_build_pcms() gives the necessary information to create PCM
-streams.  When it's called, each codec belonging to the bus stores 
-codec->num_pcms and codec->pcm_info fields.  The num_pcms indicates
-the number of elements in pcm_info array.  The card driver is supposed
-to traverse the codec linked list, read the pcm information in
-pcm_info array, and build pcm instances according to them. 
-
-The pcm_info array contains the following record:
-
-/* PCM information for each substream */
-struct hda_pcm_stream {
-       unsigned int substreams;        /* number of substreams, 0 = not exist */
-       unsigned int channels_min;      /* min. number of channels */
-       unsigned int channels_max;      /* max. number of channels */
-       hda_nid_t nid;  /* default NID to query rates/formats/bps, or set up */
-       u32 rates;      /* supported rates */
-       u64 formats;    /* supported formats (SNDRV_PCM_FMTBIT_) */
-       unsigned int maxbps;    /* supported max. bit per sample */
-       struct hda_pcm_ops ops;
-};
-
-/* for PCM creation */
-struct hda_pcm {
-       char *name;
-       struct hda_pcm_stream stream[2];
-};
-
-The name can be passed to snd_pcm_new().  The stream field contains
-the information  for playback (SNDRV_PCM_STREAM_PLAYBACK = 0) and
-capture (SNDRV_PCM_STREAM_CAPTURE = 1) directions.  The card driver
-should pass substreams to snd_pcm_new() for the number of substreams
-to create.
-
-The channels_min, channels_max, rates and formats should be copied to
-runtime->hw record.  They and maxbps fields are used also to compute
-the format value for the HDA codec and controller.  Call
-snd_hda_calc_stream_format() to get the format value.
-
-The ops field contains the following callback functions:
-
-struct hda_pcm_ops {
-       int (*open)(struct hda_pcm_stream *info, struct hda_codec *codec,
-                   struct snd_pcm_substream *substream);
-       int (*close)(struct hda_pcm_stream *info, struct hda_codec *codec,
-                    struct snd_pcm_substream *substream);
-       int (*prepare)(struct hda_pcm_stream *info, struct hda_codec *codec,
-                      unsigned int stream_tag, unsigned int format,
-                      struct snd_pcm_substream *substream);
-       int (*cleanup)(struct hda_pcm_stream *info, struct hda_codec *codec,
-                      struct snd_pcm_substream *substream);
-};
-
-All are non-NULL, so you can call them safely without NULL check.
-
-The open callback should be called in PCM open after runtime->hw is
-set up.  It may override some setting and constraints additionally.
-Similarly, the close callback should be called in the PCM close.
-
-The prepare callback should be called in PCM prepare.  This will set
-up the codec chip properly for the operation.  The cleanup should be
-called in hw_free to clean up the configuration.
-
-The caller should check the return value, at least for open and
-prepare callbacks.  When a negative value is returned, some error
-occurred.
-
-
-Proc Files
-==========
-
-Each codec dumps the widget node information in
-/proc/asound/card*/codec#* file.  This information would be really
-helpful for debugging.  Please provide its contents together with the
-bug report.
-
-
-Power Management
-================
-
-It's simple:
-Call snd_hda_suspend() in the PM suspend callback.
-Call snd_hda_resume() in the PM resume callback.
-
-
-Codec Preset (Patch)
-====================
-
-To set up and handle the codec functionality fully, each codec may
-have a codec preset (patch).  It's defined in struct hda_codec_preset:
-
-       struct hda_codec_preset {
-               unsigned int id;
-               unsigned int mask;
-               unsigned int subs;
-               unsigned int subs_mask;
-               unsigned int rev;
-               const char *name;
-               int (*patch)(struct hda_codec *codec);
-       };
-
-When the codec id and codec subsystem id match with the given id and
-subs fields bitwise (with bitmask mask and subs_mask), the callback
-patch is called.  The patch callback should initialize the codec and
-set the codec->patch_ops field.  This is defined as below:
-
-       struct hda_codec_ops {
-               int (*build_controls)(struct hda_codec *codec);
-               int (*build_pcms)(struct hda_codec *codec);
-               int (*init)(struct hda_codec *codec);
-               void (*free)(struct hda_codec *codec);
-               void (*unsol_event)(struct hda_codec *codec, unsigned int res);
-       #ifdef CONFIG_PM
-               int (*suspend)(struct hda_codec *codec, pm_message_t state);
-               int (*resume)(struct hda_codec *codec);
-       #endif
-       #ifdef CONFIG_SND_HDA_POWER_SAVE
-               int (*check_power_status)(struct hda_codec *codec,
-                                         hda_nid_t nid);
-       #endif
-       };
-
-The build_controls callback is called from snd_hda_build_controls().
-Similarly, the build_pcms callback is called from
-snd_hda_build_pcms().  The init callback is called after
-build_controls to initialize the hardware.
-The free callback is called as a destructor.
-
-The unsol_event callback is called when an unsolicited event is
-received.
-
-The suspend and resume callbacks are for power management.
-They can be NULL if no special sequence is required.  When the resume
-callback is NULL, the driver calls the init callback and resumes the
-registers from the cache.  If other handling is needed, you'd need to
-write your own resume callback.  There, the amp values can be resumed
-via
-       void snd_hda_codec_resume_amp(struct hda_codec *codec);
-and the other codec registers via
-       void snd_hda_codec_resume_cache(struct hda_codec *codec);
-
-The check_power_status callback is called when the amp value of the
-given widget NID is changed.  The codec code can turn on/off the power
-appropriately from this information.
-
-Each entry can be NULL if not necessary to be called.
-
-
-Generic Parser
-==============
-
-When the device doesn't match with any given presets, the widgets are
-parsed via th generic parser (hda_generic.c).  Its support is
-limited: no multi-channel support, for example.
-
-
-Digital I/O
-===========
-
-Call snd_hda_create_spdif_out_ctls() from the patch to create controls
-related with SPDIF out.
-
-
-Helper Functions
-================
-
-snd_hda_get_codec_name() stores the codec name on the given string.
-
-snd_hda_check_board_config() can be used to obtain the configuration
-information matching with the device.  Define the model string table
-and the table with struct snd_pci_quirk entries (zero-terminated),
-and pass it to the function.  The function checks the modelname given
-as a module parameter, and PCI subsystem IDs.  If the matching entry
-is found, it returns the config field value.
-
-snd_hda_add_new_ctls() can be used to create and add control entries.
-Pass the zero-terminated array of struct snd_kcontrol_new
-
-Macros HDA_CODEC_VOLUME(), HDA_CODEC_MUTE() and their variables can be
-used for the entry of struct snd_kcontrol_new.
-
-The input MUX helper callbacks for such a control are provided, too:
-snd_hda_input_mux_info() and snd_hda_input_mux_put().  See
-patch_realtek.c for example.
index 9de185da5f5b0dfe1be0730b8248b3aff44e0c84..cb1a432a40812a54d4073d3873de40e1c13f7859 100644 (file)
@@ -3368,6 +3368,7 @@ M:        Support Opensource <support.opensource@diasemi.com>
 W:     http://www.dialog-semiconductor.com/products
 S:     Supported
 F:     Documentation/hwmon/da90??
+F:     Documentation/devicetree/bindings/sound/da[79]*.txt
 F:     drivers/gpio/gpio-da90??.c
 F:     drivers/hwmon/da90??-hwmon.c
 F:     drivers/iio/adc/da91??-*.c
index ab37d1121be8277728bff5d25a0cb4a4599de0aa..990f656e6ab051eda034dd2cf24045de7813cd36 100644 (file)
@@ -832,6 +832,7 @@ int i915_driver_load(struct drm_device *dev, unsigned long flags)
        mutex_init(&dev_priv->sb_lock);
        mutex_init(&dev_priv->modeset_restore_lock);
        mutex_init(&dev_priv->csr_lock);
+       mutex_init(&dev_priv->av_mutex);
 
        intel_pm_setup(dev);
 
index e1db8de52851b979c31607abf7b6995bd1febe0b..22dd7043c9efb428120406d2aa5f73d025dc97dd 100644 (file)
@@ -1885,6 +1885,11 @@ struct drm_i915_private {
        /* hda/i915 audio component */
        struct i915_audio_component *audio_component;
        bool audio_component_registered;
+       /**
+        * av_mutex - mutex for audio/video sync
+        *
+        */
+       struct mutex av_mutex;
 
        uint32_t hw_context_size;
        struct list_head context_list;
index 2a5c76faf9f8dbc19ec6d17c490ff833d61d6afd..ae8df0a43de68a154685d84001f29f31b66a463c 100644 (file)
@@ -68,6 +68,31 @@ static const struct {
        { 148500, AUD_CONFIG_PIXEL_CLOCK_HDMI_148500 },
 };
 
+/* HDMI N/CTS table */
+#define TMDS_297M 297000
+#define TMDS_296M DIV_ROUND_UP(297000 * 1000, 1001)
+static const struct {
+       int sample_rate;
+       int clock;
+       int n;
+       int cts;
+} aud_ncts[] = {
+       { 44100, TMDS_296M, 4459, 234375 },
+       { 44100, TMDS_297M, 4704, 247500 },
+       { 48000, TMDS_296M, 5824, 281250 },
+       { 48000, TMDS_297M, 5120, 247500 },
+       { 32000, TMDS_296M, 5824, 421875 },
+       { 32000, TMDS_297M, 3072, 222750 },
+       { 88200, TMDS_296M, 8918, 234375 },
+       { 88200, TMDS_297M, 9408, 247500 },
+       { 96000, TMDS_296M, 11648, 281250 },
+       { 96000, TMDS_297M, 10240, 247500 },
+       { 176400, TMDS_296M, 17836, 234375 },
+       { 176400, TMDS_297M, 18816, 247500 },
+       { 192000, TMDS_296M, 23296, 281250 },
+       { 192000, TMDS_297M, 20480, 247500 },
+};
+
 /* get AUD_CONFIG_PIXEL_CLOCK_HDMI_* value for mode */
 static u32 audio_config_hdmi_pixel_clock(struct drm_display_mode *mode)
 {
@@ -90,6 +115,45 @@ static u32 audio_config_hdmi_pixel_clock(struct drm_display_mode *mode)
        return hdmi_audio_clock[i].config;
 }
 
+static int audio_config_get_n(const struct drm_display_mode *mode, int rate)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(aud_ncts); i++) {
+               if ((rate == aud_ncts[i].sample_rate) &&
+                       (mode->clock == aud_ncts[i].clock)) {
+                       return aud_ncts[i].n;
+               }
+       }
+       return 0;
+}
+
+static uint32_t audio_config_setup_n_reg(int n, uint32_t val)
+{
+       int n_low, n_up;
+       uint32_t tmp = val;
+
+       n_low = n & 0xfff;
+       n_up = (n >> 12) & 0xff;
+       tmp &= ~(AUD_CONFIG_UPPER_N_MASK | AUD_CONFIG_LOWER_N_MASK);
+       tmp |= ((n_up << AUD_CONFIG_UPPER_N_SHIFT) |
+                       (n_low << AUD_CONFIG_LOWER_N_SHIFT) |
+                       AUD_CONFIG_N_PROG_ENABLE);
+       return tmp;
+}
+
+/* check whether N/CTS/M need be set manually */
+static bool audio_rate_need_prog(struct intel_crtc *crtc,
+                                const struct drm_display_mode *mode)
+{
+       if (((mode->clock == TMDS_297M) ||
+                (mode->clock == TMDS_296M)) &&
+               intel_pipe_has_type(crtc, INTEL_OUTPUT_HDMI))
+               return true;
+       else
+               return false;
+}
+
 static bool intel_eld_uptodate(struct drm_connector *connector,
                               int reg_eldv, uint32_t bits_eldv,
                               int reg_elda, uint32_t bits_elda,
@@ -184,6 +248,8 @@ static void hsw_audio_codec_disable(struct intel_encoder *encoder)
 
        DRM_DEBUG_KMS("Disable audio codec on pipe %c\n", pipe_name(pipe));
 
+       mutex_lock(&dev_priv->av_mutex);
+
        /* Disable timestamps */
        tmp = I915_READ(HSW_AUD_CFG(pipe));
        tmp &= ~AUD_CONFIG_N_VALUE_INDEX;
@@ -199,6 +265,8 @@ static void hsw_audio_codec_disable(struct intel_encoder *encoder)
        tmp &= ~AUDIO_ELD_VALID(pipe);
        tmp &= ~AUDIO_OUTPUT_ENABLE(pipe);
        I915_WRITE(HSW_AUD_PIN_ELD_CP_VLD, tmp);
+
+       mutex_unlock(&dev_priv->av_mutex);
 }
 
 static void hsw_audio_codec_enable(struct drm_connector *connector,
@@ -208,13 +276,20 @@ static void hsw_audio_codec_enable(struct drm_connector *connector,
        struct drm_i915_private *dev_priv = connector->dev->dev_private;
        struct intel_crtc *intel_crtc = to_intel_crtc(encoder->base.crtc);
        enum pipe pipe = intel_crtc->pipe;
+       struct i915_audio_component *acomp = dev_priv->audio_component;
        const uint8_t *eld = connector->eld;
+       struct intel_digital_port *intel_dig_port =
+               enc_to_dig_port(&encoder->base);
+       enum port port = intel_dig_port->port;
        uint32_t tmp;
        int len, i;
+       int n, rate;
 
        DRM_DEBUG_KMS("Enable audio codec on pipe %c, %u bytes ELD\n",
                      pipe_name(pipe), drm_eld_size(eld));
 
+       mutex_lock(&dev_priv->av_mutex);
+
        /* Enable audio presence detect, invalidate ELD */
        tmp = I915_READ(HSW_AUD_PIN_ELD_CP_VLD);
        tmp |= AUDIO_OUTPUT_ENABLE(pipe);
@@ -246,13 +321,32 @@ static void hsw_audio_codec_enable(struct drm_connector *connector,
        /* Enable timestamps */
        tmp = I915_READ(HSW_AUD_CFG(pipe));
        tmp &= ~AUD_CONFIG_N_VALUE_INDEX;
-       tmp &= ~AUD_CONFIG_N_PROG_ENABLE;
        tmp &= ~AUD_CONFIG_PIXEL_CLOCK_HDMI_MASK;
        if (intel_pipe_has_type(intel_crtc, INTEL_OUTPUT_DISPLAYPORT))
                tmp |= AUD_CONFIG_N_VALUE_INDEX;
        else
                tmp |= audio_config_hdmi_pixel_clock(mode);
+
+       tmp &= ~AUD_CONFIG_N_PROG_ENABLE;
+       if (audio_rate_need_prog(intel_crtc, mode)) {
+               if (!acomp)
+                       rate = 0;
+               else if (port >= PORT_A && port <= PORT_E)
+                       rate = acomp->aud_sample_rate[port];
+               else {
+                       DRM_ERROR("invalid port: %d\n", port);
+                       rate = 0;
+               }
+               n = audio_config_get_n(mode, rate);
+               if (n != 0)
+                       tmp = audio_config_setup_n_reg(n, tmp);
+               else
+                       DRM_DEBUG_KMS("no suitable N value is found\n");
+       }
+
        I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+
+       mutex_unlock(&dev_priv->av_mutex);
 }
 
 static void ilk_audio_codec_disable(struct intel_encoder *encoder)
@@ -527,12 +621,91 @@ static int i915_audio_component_get_cdclk_freq(struct device *dev)
        return ret;
 }
 
+static int i915_audio_component_sync_audio_rate(struct device *dev,
+                                               int port, int rate)
+{
+       struct drm_i915_private *dev_priv = dev_to_i915(dev);
+       struct drm_device *drm_dev = dev_priv->dev;
+       struct intel_encoder *intel_encoder;
+       struct intel_digital_port *intel_dig_port;
+       struct intel_crtc *crtc;
+       struct drm_display_mode *mode;
+       struct i915_audio_component *acomp = dev_priv->audio_component;
+       enum pipe pipe = -1;
+       u32 tmp;
+       int n;
+
+       /* HSW, BDW SKL need this fix */
+       if (!IS_SKYLAKE(dev_priv) &&
+               !IS_BROADWELL(dev_priv) &&
+               !IS_HASWELL(dev_priv))
+               return 0;
+
+       mutex_lock(&dev_priv->av_mutex);
+       /* 1. get the pipe */
+       for_each_intel_encoder(drm_dev, intel_encoder) {
+               if (intel_encoder->type != INTEL_OUTPUT_HDMI)
+                       continue;
+               intel_dig_port = enc_to_dig_port(&intel_encoder->base);
+               if (port == intel_dig_port->port) {
+                       crtc = to_intel_crtc(intel_encoder->base.crtc);
+                       if (!crtc) {
+                               DRM_DEBUG_KMS("%s: crtc is NULL\n", __func__);
+                               continue;
+                       }
+                       pipe = crtc->pipe;
+                       break;
+               }
+       }
+
+       if (pipe == INVALID_PIPE) {
+               DRM_DEBUG_KMS("no pipe for the port %c\n", port_name(port));
+               mutex_unlock(&dev_priv->av_mutex);
+               return -ENODEV;
+       }
+       DRM_DEBUG_KMS("pipe %c connects port %c\n",
+                                 pipe_name(pipe), port_name(port));
+       mode = &crtc->config->base.adjusted_mode;
+
+       /* port must be valid now, otherwise the pipe will be invalid */
+       acomp->aud_sample_rate[port] = rate;
+
+       /* 2. check whether to set the N/CTS/M manually or not */
+       if (!audio_rate_need_prog(crtc, mode)) {
+               tmp = I915_READ(HSW_AUD_CFG(pipe));
+               tmp &= ~AUD_CONFIG_N_PROG_ENABLE;
+               I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+               mutex_unlock(&dev_priv->av_mutex);
+               return 0;
+       }
+
+       n = audio_config_get_n(mode, rate);
+       if (n == 0) {
+               DRM_DEBUG_KMS("Using automatic mode for N value on port %c\n",
+                                         port_name(port));
+               tmp = I915_READ(HSW_AUD_CFG(pipe));
+               tmp &= ~AUD_CONFIG_N_PROG_ENABLE;
+               I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+               mutex_unlock(&dev_priv->av_mutex);
+               return 0;
+       }
+
+       /* 3. set the N/CTS/M */
+       tmp = I915_READ(HSW_AUD_CFG(pipe));
+       tmp = audio_config_setup_n_reg(n, tmp);
+       I915_WRITE(HSW_AUD_CFG(pipe), tmp);
+
+       mutex_unlock(&dev_priv->av_mutex);
+       return 0;
+}
+
 static const struct i915_audio_component_ops i915_audio_component_ops = {
        .owner          = THIS_MODULE,
        .get_power      = i915_audio_component_get_power,
        .put_power      = i915_audio_component_put_power,
        .codec_wake_override = i915_audio_component_codec_wake_override,
        .get_cdclk_freq = i915_audio_component_get_cdclk_freq,
+       .sync_audio_rate = i915_audio_component_sync_audio_rate,
 };
 
 static int i915_audio_component_bind(struct device *i915_dev,
@@ -540,6 +713,7 @@ static int i915_audio_component_bind(struct device *i915_dev,
 {
        struct i915_audio_component *acomp = data;
        struct drm_i915_private *dev_priv = dev_to_i915(i915_dev);
+       int i;
 
        if (WARN_ON(acomp->ops || acomp->dev))
                return -EEXIST;
@@ -547,6 +721,9 @@ static int i915_audio_component_bind(struct device *i915_dev,
        drm_modeset_lock_all(dev_priv->dev);
        acomp->ops = &i915_audio_component_ops;
        acomp->dev = i915_dev;
+       BUILD_BUG_ON(MAX_PORTS != I915_MAX_PORTS);
+       for (i = 0; i < ARRAY_SIZE(acomp->aud_sample_rate); i++)
+               acomp->aud_sample_rate[i] = 0;
        dev_priv->audio_component = acomp;
        drm_modeset_unlock_all(dev_priv->dev);
 
index 63318e2afba1900bc3923d3b4023143699a4842e..41e37a6a1368b273e19fd8eec798f2458e6ae1e4 100644 (file)
@@ -871,14 +871,7 @@ static int atmel_spi_set_xfer_speed(struct atmel_spi *as,
         * Calculate the lowest divider that satisfies the
         * constraint, assuming div32/fdiv/mbz == 0.
         */
-       if (xfer->speed_hz)
-               scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz);
-       else
-               /*
-                * This can happend if max_speed is null.
-                * In this case, we set the lowest possible speed
-                */
-               scbr = 0xff;
+       scbr = DIV_ROUND_UP(bus_hz, xfer->speed_hz);
 
        /*
         * If the resulting divider doesn't fit into the
@@ -1300,14 +1293,12 @@ static int atmel_spi_one_transfer(struct spi_master *master,
                return -EINVAL;
        }
 
-       if (xfer->bits_per_word) {
-               asd = spi->controller_state;
-               bits = (asd->csr >> 4) & 0xf;
-               if (bits != xfer->bits_per_word - 8) {
-                       dev_dbg(&spi->dev,
+       asd = spi->controller_state;
+       bits = (asd->csr >> 4) & 0xf;
+       if (bits != xfer->bits_per_word - 8) {
+               dev_dbg(&spi->dev,
                        "you can't yet change bits_per_word in transfers\n");
-                       return -ENOPROTOOPT;
-               }
+               return -ENOPROTOOPT;
        }
 
        /*
index b2d56dd483d9c8ad197b6a59071c2dd2378652da..89dc7d6bc1ccb94a8a65f06a45a1d35e18d8562c 100644 (file)
 #ifndef _I915_COMPONENT_H_
 #define _I915_COMPONENT_H_
 
+/* MAX_PORT is the number of port
+ * It must be sync with I915_MAX_PORTS defined i915_drv.h
+ * 5 should be enough as only HSW, BDW, SKL need such fix.
+ */
+#define MAX_PORTS 5
+
 struct i915_audio_component {
        struct device *dev;
+       /**
+        * @aud_sample_rate: the array of audio sample rate per port
+        */
+       int aud_sample_rate[MAX_PORTS];
 
        const struct i915_audio_component_ops {
                struct module *owner;
@@ -33,6 +43,13 @@ struct i915_audio_component {
                void (*put_power)(struct device *);
                void (*codec_wake_override)(struct device *, bool enable);
                int (*get_cdclk_freq)(struct device *);
+               /**
+                * @sync_audio_rate: set n/cts based on the sample rate
+                *
+                * Called from audio driver. After audio driver sets the
+                * sample rate, it will call this function to set n/cts
+                */
+               int (*sync_audio_rate)(struct device *, int port, int rate);
        } *ops;
 
        const struct i915_audio_component_audio_ops {
index 688997a24aadde7e131e7027a1036eb24e48a9e4..00825672d256eb26b6a29a40214b5fb37b910be3 100644 (file)
@@ -219,6 +219,14 @@ struct serio_device_id {
        __u8 proto;
 };
 
+struct hda_device_id {
+       __u32 vendor_id;
+       __u32 rev_id;
+       __u8 api_version;
+       const char *name;
+       unsigned long driver_data;
+};
+
 /*
  * Struct used for matching a device
  */
index 673f5c39cbf28d975cf5022bc3e1b3f934df1fbb..e7eac897999504a6bdb0fc317d74c8ad7e6785a7 100644 (file)
@@ -44,9 +44,6 @@ struct da7213_platform_data {
        enum da7213_dmic_data_sel dmic_data_sel;
        enum da7213_dmic_samplephase dmic_samplephase;
        enum da7213_dmic_clk_rate dmic_clk_rate;
-
-       /* MCLK squaring config */
-       bool mclk_squaring;
 };
 
 #endif /* _DA7213_PDATA_H */
diff --git a/include/sound/da7219-aad.h b/include/sound/da7219-aad.h
new file mode 100644 (file)
index 0000000..17802fb
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * da7219-aad.h - DA7322 ASoC Codec AAD Driver Platform Data
+ *
+ * Copyright (c) 2015 Dialog Semiconductor Ltd.
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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.
+ */
+
+#ifndef __DA7219_AAD_PDATA_H
+#define __DA7219_AAD_PDATA_H
+
+enum da7219_aad_micbias_pulse_lvl {
+       DA7219_AAD_MICBIAS_PULSE_LVL_OFF = 0,
+       DA7219_AAD_MICBIAS_PULSE_LVL_2_8V = 6,
+       DA7219_AAD_MICBIAS_PULSE_LVL_2_9V,
+};
+
+enum da7219_aad_btn_cfg {
+       DA7219_AAD_BTN_CFG_2MS = 1,
+       DA7219_AAD_BTN_CFG_5MS,
+       DA7219_AAD_BTN_CFG_10MS,
+       DA7219_AAD_BTN_CFG_50MS,
+       DA7219_AAD_BTN_CFG_100MS,
+       DA7219_AAD_BTN_CFG_200MS,
+       DA7219_AAD_BTN_CFG_500MS,
+};
+
+enum da7219_aad_mic_det_thr {
+       DA7219_AAD_MIC_DET_THR_200_OHMS = 0,
+       DA7219_AAD_MIC_DET_THR_500_OHMS,
+       DA7219_AAD_MIC_DET_THR_750_OHMS,
+       DA7219_AAD_MIC_DET_THR_1000_OHMS,
+};
+
+enum da7219_aad_jack_ins_deb {
+       DA7219_AAD_JACK_INS_DEB_5MS = 0,
+       DA7219_AAD_JACK_INS_DEB_10MS,
+       DA7219_AAD_JACK_INS_DEB_20MS,
+       DA7219_AAD_JACK_INS_DEB_50MS,
+       DA7219_AAD_JACK_INS_DEB_100MS,
+       DA7219_AAD_JACK_INS_DEB_200MS,
+       DA7219_AAD_JACK_INS_DEB_500MS,
+       DA7219_AAD_JACK_INS_DEB_1S,
+};
+
+enum da7219_aad_jack_det_rate {
+       DA7219_AAD_JACK_DET_RATE_32_64MS = 0,
+       DA7219_AAD_JACK_DET_RATE_64_128MS,
+       DA7219_AAD_JACK_DET_RATE_128_256MS,
+       DA7219_AAD_JACK_DET_RATE_256_512MS,
+};
+
+enum da7219_aad_jack_rem_deb {
+       DA7219_AAD_JACK_REM_DEB_1MS = 0,
+       DA7219_AAD_JACK_REM_DEB_5MS,
+       DA7219_AAD_JACK_REM_DEB_10MS,
+       DA7219_AAD_JACK_REM_DEB_20MS,
+};
+
+enum da7219_aad_btn_avg {
+       DA7219_AAD_BTN_AVG_1 = 0,
+       DA7219_AAD_BTN_AVG_2,
+       DA7219_AAD_BTN_AVG_4,
+       DA7219_AAD_BTN_AVG_8,
+};
+
+enum da7219_aad_adc_1bit_rpt {
+       DA7219_AAD_ADC_1BIT_RPT_1 = 0,
+       DA7219_AAD_ADC_1BIT_RPT_2,
+       DA7219_AAD_ADC_1BIT_RPT_4,
+       DA7219_AAD_ADC_1BIT_RPT_8,
+};
+
+struct da7219_aad_pdata {
+       int irq;
+
+       enum da7219_aad_micbias_pulse_lvl micbias_pulse_lvl;
+       u32 micbias_pulse_time;
+       enum da7219_aad_btn_cfg btn_cfg;
+       enum da7219_aad_mic_det_thr mic_det_thr;
+       enum da7219_aad_jack_ins_deb jack_ins_deb;
+       enum da7219_aad_jack_det_rate jack_det_rate;
+       enum da7219_aad_jack_rem_deb jack_rem_deb;
+
+       u8 a_d_btn_thr;
+       u8 d_b_btn_thr;
+       u8 b_c_btn_thr;
+       u8 c_mic_btn_thr;
+
+       enum da7219_aad_btn_avg btn_avg;
+       enum da7219_aad_adc_1bit_rpt adc_1bit_rpt;
+};
+
+#endif /* __DA7219_AAD_PDATA_H */
diff --git a/include/sound/da7219.h b/include/sound/da7219.h
new file mode 100644 (file)
index 0000000..3f39e13
--- /dev/null
@@ -0,0 +1,55 @@
+/*
+ * da7219.h - DA7219 ASoC Codec Driver Platform Data
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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.
+ */
+
+#ifndef __DA7219_PDATA_H
+#define __DA7219_PDATA_H
+
+/* LDO */
+enum da7219_ldo_lvl_sel {
+       DA7219_LDO_LVL_SEL_1_05V = 0,
+       DA7219_LDO_LVL_SEL_1_10V,
+       DA7219_LDO_LVL_SEL_1_20V,
+       DA7219_LDO_LVL_SEL_1_40V,
+};
+
+/* Mic Bias */
+enum da7219_micbias_voltage {
+       DA7219_MICBIAS_1_8V = 1,
+       DA7219_MICBIAS_2_0V,
+       DA7219_MICBIAS_2_2V,
+       DA7219_MICBIAS_2_4V,
+       DA7219_MICBIAS_2_6V,
+};
+
+/* Mic input type */
+enum da7219_mic_amp_in_sel {
+       DA7219_MIC_AMP_IN_SEL_DIFF = 0,
+       DA7219_MIC_AMP_IN_SEL_SE_P,
+       DA7219_MIC_AMP_IN_SEL_SE_N,
+};
+
+struct da7219_aad_pdata;
+
+struct da7219_pdata {
+       /* Internal LDO */
+       enum da7219_ldo_lvl_sel ldo_lvl_sel;
+
+       /* Mic */
+       enum da7219_micbias_voltage micbias_lvl;
+       enum da7219_mic_amp_in_sel mic_amp_in_sel;
+
+       /* AAD */
+       struct da7219_aad_pdata *aad_pdata;
+};
+
+#endif /* __DA7219_PDATA_H */
index 3a8fca9409a7a0c1fad472155854b26f0a122487..8966ba7c962951de6fd1f972c092266a38b823d2 100644 (file)
@@ -38,6 +38,8 @@ struct i2s_clk_config_data {
 struct i2s_platform_data {
        #define DWC_I2S_PLAY    (1 << 0)
        #define DWC_I2S_RECORD  (1 << 1)
+       #define DW_I2S_SLAVE    (1 << 2)
+       #define DW_I2S_MASTER   (1 << 3)
        unsigned int cap;
        int channel;
        u32 snd_fmts;
index df705908480aebbf754900731834162fa8097f75..2767c55a641edd64b7018a0d7693826ae60dbad9 100644 (file)
@@ -67,7 +67,7 @@ int snd_hdac_regmap_update_raw(struct hdac_device *codec, unsigned int reg,
  * @reg: verb to write
  * @val: value to write
  *
- * For writing an amp value, use snd_hda_regmap_amp_update().
+ * For writing an amp value, use snd_hdac_regmap_update_amp().
  */
 static inline int
 snd_hdac_regmap_write(struct hdac_device *codec, hda_nid_t nid,
@@ -85,7 +85,7 @@ snd_hdac_regmap_write(struct hdac_device *codec, hda_nid_t nid,
  * @mask: bit mask to update
  * @val: value to update
  *
- * For updating an amp value, use snd_hda_regmap_amp_update().
+ * For updating an amp value, use snd_hdac_regmap_update_amp().
  */
 static inline int
 snd_hdac_regmap_update(struct hdac_device *codec, hda_nid_t nid,
index 49bc836fcd8483d522c48dc604fc3ba0d1bf153e..e2b712c90d3f22d4bcbb75ded3a296304d23d7c6 100644 (file)
@@ -21,22 +21,13 @@ struct hdac_stream;
 struct hdac_device;
 struct hdac_driver;
 struct hdac_widget_tree;
+struct hda_device_id;
 
 /*
  * exported bus type
  */
 extern struct bus_type snd_hda_bus_type;
 
-/*
- * HDA device table
- */
-struct hda_device_id {
-       __u32 vendor_id;
-       __u32 rev_id;
-       const char *name;
-       unsigned long driver_data;
-};
-
 /*
  * generic arrays
  */
@@ -117,6 +108,8 @@ int snd_hdac_device_init(struct hdac_device *dev, struct hdac_bus *bus,
 void snd_hdac_device_exit(struct hdac_device *dev);
 int snd_hdac_device_register(struct hdac_device *codec);
 void snd_hdac_device_unregister(struct hdac_device *codec);
+int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name);
+int snd_hdac_codec_modalias(struct hdac_device *hdac, char *buf, size_t size);
 
 int snd_hdac_refresh_widgets(struct hdac_device *codec);
 int snd_hdac_refresh_widget_sysfs(struct hdac_device *codec);
@@ -147,6 +140,12 @@ int snd_hdac_query_supported_pcm(struct hdac_device *codec, hda_nid_t nid,
 bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
                                  unsigned int format);
 
+int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm);
+int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm);
+bool snd_hdac_check_power_state(struct hdac_device *hdac,
+               hda_nid_t nid, unsigned int target_state);
 /**
  * snd_hdac_read_parm - read a codec parameter
  * @codec: the codec object
index 94210dcdb6eacf5329fa8250f42a470a84d600ff..a4cadd9c297a810e1bb548d2cb02e4ab92b5eea2 100644 (file)
@@ -40,6 +40,13 @@ void snd_hdac_ext_bus_device_remove(struct hdac_ext_bus *ebus);
 #define hbus_to_ebus(_bus) \
        container_of(_bus, struct hdac_ext_bus, bus)
 
+#define HDA_CODEC_REV_EXT_ENTRY(_vid, _rev, _name, drv_data) \
+       { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \
+         .api_version = HDA_DEV_ASOC, \
+         .driver_data = (unsigned long)(drv_data) }
+#define HDA_CODEC_EXT_ENTRY(_vid, _revid, _name, _drv_data) \
+       HDA_CODEC_REV_EXT_ENTRY(_vid, _revid, _name, _drv_data)
+
 int snd_hdac_ext_bus_parse_capabilities(struct hdac_ext_bus *sbus);
 void snd_hdac_ext_bus_ppcap_enable(struct hdac_ext_bus *chip, bool enable);
 void snd_hdac_ext_bus_ppcap_int_enable(struct hdac_ext_bus *chip, bool enable);
index 691e7ee0a510ae6354ccb4d311823a687d47ee33..b0be09279943fc1a911a6071507ebdd79eed2912 100644 (file)
@@ -265,12 +265,12 @@ struct snd_ratden {
 
 struct snd_pcm_hw_constraint_ratnums {
        int nrats;
-       struct snd_ratnum *rats;
+       const struct snd_ratnum *rats;
 };
 
 struct snd_pcm_hw_constraint_ratdens {
        int nrats;
-       struct snd_ratden *rats;
+       const struct snd_ratden *rats;
 };
 
 struct snd_pcm_hw_constraint_list {
@@ -285,8 +285,6 @@ struct snd_pcm_hw_constraint_ranges {
        unsigned int mask;
 };
 
-struct snd_pcm_hwptr_log;
-
 /*
  * userspace-provided audio timestamp config to kernel,
  * structure is for internal use only and filled with dedicated unpack routine
@@ -404,10 +402,6 @@ struct snd_pcm_runtime {
        struct snd_pcm_hardware hw;
        struct snd_pcm_hw_constraints hw_constraints;
 
-       /* -- interrupt callbacks -- */
-       void (*transfer_ack_begin)(struct snd_pcm_substream *substream);
-       void (*transfer_ack_end)(struct snd_pcm_substream *substream);
-
        /* -- timer -- */
        unsigned int timer_resolution;  /* timer resolution */
        int tstamp_type;                /* timestamp type */
@@ -428,10 +422,6 @@ struct snd_pcm_runtime {
        /* -- OSS things -- */
        struct snd_pcm_oss_runtime oss;
 #endif
-
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
-       struct snd_pcm_hwptr_log *hwptr_log;
-#endif
 };
 
 struct snd_pcm_group {         /* keep linked substreams */
@@ -980,7 +970,7 @@ int snd_interval_list(struct snd_interval *i, unsigned int count,
 int snd_interval_ranges(struct snd_interval *i, unsigned int count,
                        const struct snd_interval *list, unsigned int mask);
 int snd_interval_ratnum(struct snd_interval *i,
-                       unsigned int rats_count, struct snd_ratnum *rats,
+                       unsigned int rats_count, const struct snd_ratnum *rats,
                        unsigned int *nump, unsigned int *denp);
 
 void _snd_pcm_hw_params_any(struct snd_pcm_hw_params *params);
@@ -1010,11 +1000,11 @@ int snd_pcm_hw_constraint_ranges(struct snd_pcm_runtime *runtime,
 int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, 
                                  unsigned int cond,
                                  snd_pcm_hw_param_t var,
-                                 struct snd_pcm_hw_constraint_ratnums *r);
+                                 const struct snd_pcm_hw_constraint_ratnums *r);
 int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, 
                                  unsigned int cond,
                                  snd_pcm_hw_param_t var,
-                                 struct snd_pcm_hw_constraint_ratdens *r);
+                                 const struct snd_pcm_hw_constraint_ratdens *r);
 int snd_pcm_hw_constraint_msbits(struct snd_pcm_runtime *runtime, 
                                 unsigned int cond,
                                 unsigned int width,
@@ -1034,6 +1024,22 @@ int snd_pcm_hw_rule_add(struct snd_pcm_runtime *runtime,
                        snd_pcm_hw_rule_func_t func, void *private,
                        int dep, ...);
 
+/**
+ * snd_pcm_hw_constraint_single() - Constrain parameter to a single value
+ * @runtime: PCM runtime instance
+ * @var: The hw_params variable to constrain
+ * @val: The value to constrain to
+ *
+ * Return: Positive if the value is changed, zero if it's not changed, or a
+ * negative error code.
+ */
+static inline int snd_pcm_hw_constraint_single(
+       struct snd_pcm_runtime *runtime, snd_pcm_hw_param_t var,
+       unsigned int val)
+{
+       return snd_pcm_hw_constraint_minmax(runtime, var, val, val);
+}
+
 int snd_pcm_format_signed(snd_pcm_format_t format);
 int snd_pcm_format_unsigned(snd_pcm_format_t format);
 int snd_pcm_format_linear(snd_pcm_format_t format);
@@ -1117,10 +1123,16 @@ static inline void snd_pcm_set_runtime_buffer(struct snd_pcm_substream *substrea
  *  Timer interface
  */
 
+#ifdef CONFIG_SND_PCM_TIMER
 void snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream);
 void snd_pcm_timer_init(struct snd_pcm_substream *substream);
 void snd_pcm_timer_done(struct snd_pcm_substream *substream);
-
+#else
+static inline void
+snd_pcm_timer_resolution_change(struct snd_pcm_substream *substream) {}
+static inline void snd_pcm_timer_init(struct snd_pcm_substream *substream) {}
+static inline void snd_pcm_timer_done(struct snd_pcm_substream *substream) {}
+#endif
 /**
  * snd_pcm_gettime - Fill the timespec depending on the timestamp mode
  * @runtime: PCM runtime instance
index 56e818e4a1cbf90270d21c3a67e44ff3a0f32d73..6ef629bde164c19ebb675e506fec7d98715d4956 100644 (file)
@@ -12,7 +12,6 @@ extern int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream);
 extern int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd);
 extern snd_pcm_uframes_t pxa2xx_pcm_pointer(struct snd_pcm_substream *substream);
 extern int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream);
-extern void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id);
 extern int __pxa2xx_pcm_open(struct snd_pcm_substream *substream);
 extern int __pxa2xx_pcm_close(struct snd_pcm_substream *substream);
 extern int pxa2xx_pcm_mmap(struct snd_pcm_substream *substream,
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h
deleted file mode 100644 (file)
index bb7b2eb..0000000
+++ /dev/null
@@ -1,118 +0,0 @@
-/*
- * Renesas R-Car SRU/SCU/SSIU/SSI support
- *
- * Copyright (C) 2013 Renesas Solutions Corp.
- * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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 RCAR_SND_H
-#define RCAR_SND_H
-
-#include <linux/sh_clk.h>
-
-#define RSND_GEN1_SRU  0
-#define RSND_GEN1_ADG  1
-#define RSND_GEN1_SSI  2
-
-#define RSND_GEN2_SCU  0
-#define RSND_GEN2_ADG  1
-#define RSND_GEN2_SSIU 2
-#define RSND_GEN2_SSI  3
-
-#define RSND_BASE_MAX  4
-
-/*
- * flags
- *
- * 0xAB000000
- *
- * A : clock sharing settings
- * B : SSI direction
- */
-#define RSND_SSI_CLK_PIN_SHARE         (1 << 31)
-#define RSND_SSI_NO_BUSIF              (1 << 30) /* SSI+DMA without BUSIF */
-
-#define RSND_SSI(_dma_id, _irq, _flags)                \
-{ .dma_id = _dma_id, .irq = _irq, .flags = _flags }
-#define RSND_SSI_UNUSED \
-{ .dma_id = -1, .irq = -1, .flags = 0 }
-
-struct rsnd_ssi_platform_info {
-       int dma_id;
-       int irq;
-       u32 flags;
-};
-
-#define RSND_SRC(rate, _dma_id)                                                \
-{ .convert_rate = rate, .dma_id = _dma_id, }
-#define RSND_SRC_UNUSED                                \
-{ .convert_rate = 0, .dma_id = -1, }
-
-struct rsnd_src_platform_info {
-       u32 convert_rate; /* sampling rate convert */
-       int dma_id; /* for Gen2 SCU */
-       int irq;
-};
-
-/*
- * flags
- */
-struct rsnd_ctu_platform_info {
-       u32 flags;
-};
-
-struct rsnd_mix_platform_info {
-       u32 flags;
-};
-
-struct rsnd_dvc_platform_info {
-       u32 flags;
-};
-
-struct rsnd_dai_path_info {
-       struct rsnd_ssi_platform_info *ssi;
-       struct rsnd_src_platform_info *src;
-       struct rsnd_ctu_platform_info *ctu;
-       struct rsnd_mix_platform_info *mix;
-       struct rsnd_dvc_platform_info *dvc;
-};
-
-struct rsnd_dai_platform_info {
-       struct rsnd_dai_path_info playback;
-       struct rsnd_dai_path_info capture;
-};
-
-/*
- * flags
- *
- * 0x0000000A
- *
- * A : generation
- */
-#define RSND_GEN_MASK  (0xF << 0)
-#define RSND_GEN1      (1 << 0) /* fixme */
-#define RSND_GEN2      (2 << 0) /* fixme */
-
-struct rcar_snd_info {
-       u32 flags;
-       struct rsnd_ssi_platform_info *ssi_info;
-       int ssi_info_nr;
-       struct rsnd_src_platform_info *src_info;
-       int src_info_nr;
-       struct rsnd_ctu_platform_info *ctu_info;
-       int ctu_info_nr;
-       struct rsnd_mix_platform_info *mix_info;
-       int mix_info_nr;
-       struct rsnd_dvc_platform_info *dvc_info;
-       int dvc_info_nr;
-       struct rsnd_dai_platform_info *dai_info;
-       int dai_info_nr;
-       int (*start)(int id);
-       int (*stop)(int id);
-};
-
-#endif
index 59d26dd81e45333e1730910a30493df4480059d5..e3c84b92ff70edd60d9b3c5204c9df80a991436d 100644 (file)
 #define __LINUX_SND_RT5640_H
 
 struct rt5640_platform_data {
-       /* IN1 & IN2 can optionally be differential */
+       /* IN1 & IN2 & IN3 can optionally be differential */
        bool in1_diff;
        bool in2_diff;
+       bool in3_diff;
 
        bool dmic_en;
        bool dmic1_data_pin; /* 0 = IN1P; 1 = GPIO3 */
index 22734bc3ffd4a2e72ec1a1979ec1adf7df8014c6..a5cf6152e778929d572250462b2252a4369b08ca 100644 (file)
@@ -21,6 +21,8 @@ struct rt5645_platform_data {
        /* 0 = IN2P; 1 = GPIO6; 2 = GPIO10; 3 = GPIO12 */
 
        unsigned int jd_mode;
+       /* Invert JD when jack insert */
+       bool jd_invert;
 };
 
 #endif
index b9b4f289fe6b32130848cf4062ac94e4864f5bfb..0399352f3a6243569c87d3691fd664480749802c 100644 (file)
@@ -19,6 +19,8 @@ struct asoc_simple_dai {
        unsigned int sysclk;
        int slots;
        int slot_width;
+       unsigned int tx_slot_mask;
+       unsigned int rx_slot_mask;
        struct clk *clk;
 };
 
index 2df96b1384c718fdecc2a26d423a484dc5a9d727..212eaaf172ed49b8ac26d619ef3e0bce9553bee2 100644 (file)
@@ -48,10 +48,25 @@ struct snd_compr_stream;
 #define SND_SOC_DAIFMT_GATED           (0 << 4) /* clock is gated */
 
 /*
- * DAI hardware signal inversions.
+ * DAI hardware signal polarity.
  *
  * Specifies whether the DAI can also support inverted clocks for the specified
  * format.
+ *
+ * BCLK:
+ * - "normal" polarity means signal is available at rising edge of BCLK
+ * - "inverted" polarity means signal is available at falling edge of BCLK
+ *
+ * FSYNC "normal" polarity depends on the frame format:
+ * - I2S: frame consists of left then right channel data. Left channel starts
+ *      with falling FSYNC edge, right channel starts with rising FSYNC edge.
+ * - Left/Right Justified: frame consists of left then right channel data.
+ *      Left channel starts with rising FSYNC edge, right channel starts with
+ *      falling FSYNC edge.
+ * - DSP A/B: Frame starts with rising FSYNC edge.
+ * - AC97: Frame starts with rising FSYNC edge.
+ *
+ * "Negative" FSYNC polarity is the one opposite of "normal" polarity.
  */
 #define SND_SOC_DAIFMT_NB_NF           (0 << 8) /* normal bit clock + frame */
 #define SND_SOC_DAIFMT_NB_IF           (2 << 8) /* normal BCLK + inv FRM */
@@ -214,7 +229,7 @@ struct snd_soc_dai_driver {
        int (*suspend)(struct snd_soc_dai *dai);
        int (*resume)(struct snd_soc_dai *dai);
        /* compress dai */
-       bool compress_dai;
+       int (*compress_new)(struct snd_soc_pcm_runtime *rtd, int num);
        /* DAI is also used for the control bus */
        bool bus_control;
 
index 5abba037d2456fc2616ae5df1270fd928299c1de..7855cfe46b69a044040f9ac5be92591e33af1799 100644 (file)
@@ -451,6 +451,9 @@ int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
 struct snd_soc_dapm_context *snd_soc_dapm_kcontrol_dapm(
        struct snd_kcontrol *kcontrol);
 
+struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(
+               struct snd_kcontrol *kcontrol);
+
 int snd_soc_dapm_force_bias_level(struct snd_soc_dapm_context *dapm,
        enum snd_soc_bias_level level);
 
index 26ede14597daba32f8bf2cfcc84a821d094cc3d3..a8b4b9c8b1d2415e7220913715fc2cd4bd95212c 100644 (file)
        .get = xhandler_get, .put = xhandler_put, \
        .private_value = \
                SOC_DOUBLE_VALUE(reg, shift_left, shift_right, max, invert, 0) }
+#define SOC_DOUBLE_R_EXT(xname, reg_left, reg_right, xshift, xmax, xinvert,\
+        xhandler_get, xhandler_put) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
+       .info = snd_soc_info_volsw, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = SOC_DOUBLE_R_VALUE(reg_left, reg_right, xshift, \
+                                           xmax, xinvert) }
 #define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\
         xhandler_get, xhandler_put, tlv_array) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_volsw, \
        .get = xhandler_get, .put = xhandler_put, \
        .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) }
+#define SOC_SINGLE_RANGE_EXT_TLV(xname, xreg, xshift, xmin, xmax, xinvert, \
+                                xhandler_get, xhandler_put, tlv_array) \
+{      .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 = snd_soc_info_volsw_range, \
+       .get = xhandler_get, .put = xhandler_put, \
+       .private_value = (unsigned long)&(struct soc_mixer_control) \
+               {.reg = xreg, .rreg = xreg, .shift = xshift, \
+                .rshift = xshift, .min = xmin, .max = xmax, \
+                .platform_max = xmax, .invert = xinvert} }
 #define SOC_DOUBLE_EXT_TLV(xname, xreg, shift_left, shift_right, xmax, xinvert,\
         xhandler_get, xhandler_put, tlv_array) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
@@ -440,7 +459,9 @@ int snd_soc_platform_read(struct snd_soc_platform *platform,
 int snd_soc_platform_write(struct snd_soc_platform *platform,
                                        unsigned int reg, unsigned int val);
 int soc_new_pcm(struct snd_soc_pcm_runtime *rtd, int num);
-int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num);
+#ifdef CONFIG_SND_SOC_COMPRESS
+int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num);
+#endif
 
 struct snd_pcm_substream *snd_soc_get_dai_substream(struct snd_soc_card *card,
                const char *dai_link, int stream);
@@ -593,7 +614,7 @@ int snd_soc_put_volsw_range(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 int snd_soc_get_volsw_range(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
-int snd_soc_limit_volume(struct snd_soc_codec *codec,
+int snd_soc_limit_volume(struct snd_soc_card *card,
        const char *name, int max);
 int snd_soc_bytes_info(struct snd_kcontrol *kcontrol,
                       struct snd_ctl_elem_info *uinfo);
@@ -1603,6 +1624,8 @@ int snd_soc_of_parse_card_name(struct snd_soc_card *card,
 int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
                                          const char *propname);
 int snd_soc_of_parse_tdm_slot(struct device_node *np,
+                             unsigned int *tx_mask,
+                             unsigned int *rx_mask,
                              unsigned int *slots,
                              unsigned int *slot_width);
 void snd_soc_of_parse_audio_prefix(struct snd_soc_card *card,
index 247c50bd60f0d067ad8884dbe4574adf0bfcf596..26539a7e488081933f151c570220ff4561deec40 100644 (file)
@@ -83,7 +83,7 @@
 #define SND_SOC_TPLG_NUM_TEXTS         16
 
 /* ABI version */
-#define SND_SOC_TPLG_ABI_VERSION       0x3
+#define SND_SOC_TPLG_ABI_VERSION       0x4
 
 /* Max size of TLV data */
 #define SND_SOC_TPLG_TLV_SIZE          32
 #define SND_SOC_TPLG_TYPE_PCM          7
 #define SND_SOC_TPLG_TYPE_MANIFEST     8
 #define SND_SOC_TPLG_TYPE_CODEC_LINK   9
-#define SND_SOC_TPLG_TYPE_PDATA                10
+#define SND_SOC_TPLG_TYPE_BACKEND_LINK 10
+#define SND_SOC_TPLG_TYPE_PDATA                11
 #define SND_SOC_TPLG_TYPE_MAX  SND_SOC_TPLG_TYPE_PDATA
 
 /* vendor block IDs - please add new vendor types to end */
@@ -198,7 +199,7 @@ struct snd_soc_tplg_ctl_hdr {
 struct snd_soc_tplg_stream_caps {
        __le32 size;            /* in bytes of this structure */
        char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
-       __le64 formats[SND_SOC_TPLG_MAX_FORMATS];       /* supported formats SNDRV_PCM_FMTBIT_* */
+       __le64 formats; /* supported formats SNDRV_PCM_FMTBIT_* */
        __le32 rates;           /* supported rates SNDRV_PCM_RATE_* */
        __le32 rate_min;        /* min rate */
        __le32 rate_max;        /* max rate */
@@ -217,23 +218,12 @@ struct snd_soc_tplg_stream_caps {
  */
 struct snd_soc_tplg_stream {
        __le32 size;            /* in bytes of this structure */
+       char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN]; /* Name of the stream */
        __le64 format;          /* SNDRV_PCM_FMTBIT_* */
        __le32 rate;            /* SNDRV_PCM_RATE_* */
        __le32 period_bytes;    /* size of period in bytes */
        __le32 buffer_bytes;    /* size of buffer in bytes */
        __le32 channels;        /* channels */
-       __le32 tdm_slot;        /* optional BE bitmask of supported TDM slots */
-       __le32 dai_fmt;         /* SND_SOC_DAIFMT_  */
-} __attribute__((packed));
-
-/*
- * Duplex stream configuration supported by SW/FW.
- */
-struct snd_soc_tplg_stream_config {
-       __le32 size;            /* in bytes of this structure */
-       char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
-       struct snd_soc_tplg_stream playback;
-       struct snd_soc_tplg_stream capture;
 } __attribute__((packed));
 
 /*
@@ -366,11 +356,11 @@ struct snd_soc_tplg_dapm_widget {
        __le32 shift;           /* bits to shift */
        __le32 mask;            /* non-shifted mask */
        __le32 subseq;          /* sort within widget type */
-       __u32 invert;           /* invert the power bit */
-       __u32 ignore_suspend;   /* kept enabled over suspend */
-       __u16 event_flags;
-       __u16 event_type;
-       __u16 num_kcontrols;
+       __le32 invert;          /* invert the power bit */
+       __le32 ignore_suspend;  /* kept enabled over suspend */
+       __le16 event_flags;
+       __le16 event_type;
+       __le32 num_kcontrols;
        struct snd_soc_tplg_private priv;
        /*
         * kcontrols that relate to this widget
@@ -378,30 +368,46 @@ struct snd_soc_tplg_dapm_widget {
         */
 } __attribute__((packed));
 
-struct snd_soc_tplg_pcm_cfg_caps {
-       struct snd_soc_tplg_stream_caps caps;
-       struct snd_soc_tplg_stream_config configs[SND_SOC_TPLG_STREAM_CONFIG_MAX];
-       __le32 num_configs;     /* number of configs */
-} __attribute__((packed));
 
 /*
- * Describes SW/FW specific features of PCM or DAI link.
+ * Describes SW/FW specific features of PCM (FE DAI & DAI link).
  *
- * File block representation for PCM/DAI-Link :-
+ * File block representation for PCM :-
  * +-----------------------------------+-----+
  * | struct snd_soc_tplg_hdr           |  1  |
  * +-----------------------------------+-----+
- * | struct snd_soc_tplg_dapm_pcm_dai  |  N  |
+ * | struct snd_soc_tplg_pcm           |  N  |
  * +-----------------------------------+-----+
  */
-struct snd_soc_tplg_pcm_dai {
+struct snd_soc_tplg_pcm {
        __le32 size;            /* in bytes of this structure */
-       char name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
-       __le32 id;                      /* unique ID - used to match */
-       __le32 playback;                /* supports playback mode */
-       __le32 capture;                 /* supports capture mode */
-       __le32 compress;                /* 1 = compressed; 0 = PCM */
-       struct snd_soc_tplg_pcm_cfg_caps capconf[2];    /* capabilities and configs */
+       char pcm_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+       char dai_name[SNDRV_CTL_ELEM_ID_NAME_MAXLEN];
+       __le32 pcm_id;          /* unique ID - used to match */
+       __le32 dai_id;          /* unique ID - used to match */
+       __le32 playback;        /* supports playback mode */
+       __le32 capture;         /* supports capture mode */
+       __le32 compress;        /* 1 = compressed; 0 = PCM */
+       struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* for DAI link */
+       __le32 num_streams;     /* number of streams */
+       struct snd_soc_tplg_stream_caps caps[2]; /* playback and capture for DAI */
 } __attribute__((packed));
 
+
+/*
+ * Describes the BE or CC link runtime supported configs or params
+ *
+ * File block representation for BE/CC link config :-
+ * +-----------------------------------+-----+
+ * | struct snd_soc_tplg_hdr           |  1  |
+ * +-----------------------------------+-----+
+ * | struct snd_soc_tplg_link_config   |  N  |
+ * +-----------------------------------+-----+
+ */
+struct snd_soc_tplg_link_config {
+       __le32 size;            /* in bytes of this structure */
+       __le32 id;              /* unique ID - used to match */
+       struct snd_soc_tplg_stream stream[SND_SOC_TPLG_STREAM_CONFIG_MAX]; /* supported configs playback and captrure */
+       __le32 num_streams;     /* number of streams */
+} __attribute__((packed));
 #endif
index a45be6bdcf5bb88b2871a764e070ed9737818088..a82108e5d1c0ccfb627daf84ac80b153fe123f66 100644 (file)
@@ -100,9 +100,11 @@ enum {
        SNDRV_HWDEP_IFACE_FW_FIREWORKS, /* Echo Audio Fireworks based device */
        SNDRV_HWDEP_IFACE_FW_BEBOB,     /* BridgeCo BeBoB based device */
        SNDRV_HWDEP_IFACE_FW_OXFW,      /* Oxford OXFW970/971 based device */
+       SNDRV_HWDEP_IFACE_FW_DIGI00X,   /* Digidesign Digi 002/003 family */
+       SNDRV_HWDEP_IFACE_FW_TASCAM,    /* TASCAM FireWire series */
 
        /* Don't forget to change the following: */
-       SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_OXFW
+       SNDRV_HWDEP_IFACE_LAST = SNDRV_HWDEP_IFACE_FW_TASCAM
 };
 
 struct snd_hwdep_info {
index ec1535bb6aedd043df4d5b4950805d8fc2aeaa05..5175e166987d173fb88ed1279614d89602ac22dc 100644 (file)
 
 #define EMU10K1_FX8010_PCM_COUNT               8
 
+/*
+ * Following definition is copied from linux/types.h to support compiling
+ * this header file in userspace since they are not generally available for
+ * uapi headers.
+ */
+#define __EMU10K1_DECLARE_BITMAP(name,bits) \
+       unsigned long name[(bits) / (sizeof(unsigned long) * 8)]
+
 /* instruction set */
 #define iMAC0   0x00   /* R = A + (X * Y >> 31)   ; saturation */
 #define iMAC1   0x01   /* R = A + (-X * Y >> 31)  ; saturation */
@@ -300,7 +308,7 @@ struct snd_emu10k1_fx8010_control_old_gpr {
 struct snd_emu10k1_fx8010_code {
        char name[128];
 
-       DECLARE_BITMAP(gpr_valid, 0x200); /* bitmask of valid initializers */
+       __EMU10K1_DECLARE_BITMAP(gpr_valid, 0x200); /* bitmask of valid initializers */
        __u32 __user *gpr_map;          /* initializers */
 
        unsigned int gpr_add_control_count; /* count of GPR controls to add/replace */
@@ -313,11 +321,11 @@ struct snd_emu10k1_fx8010_code {
        unsigned int gpr_list_control_total; /* total count of GPR controls */
        struct snd_emu10k1_fx8010_control_gpr __user *gpr_list_controls; /* listed GPR controls */
 
-       DECLARE_BITMAP(tram_valid, 0x100); /* bitmask of valid initializers */
+       __EMU10K1_DECLARE_BITMAP(tram_valid, 0x100); /* bitmask of valid initializers */
        __u32 __user *tram_data_map;      /* data initializers */
        __u32 __user *tram_addr_map;      /* map initializers */
 
-       DECLARE_BITMAP(code_valid, 1024); /* bitmask of valid instructions */
+       __EMU10K1_DECLARE_BITMAP(code_valid, 1024); /* bitmask of valid instructions */
        __u32 __user *code;               /* one instruction - 64 bits */
 };
 
index 49122df3b56b31efcd7e37b739d67ccb13f02b00..db79a12fcc78db70d234b89d4b8bf6f573598224 100644 (file)
@@ -9,6 +9,7 @@
 #define SNDRV_FIREWIRE_EVENT_LOCK_STATUS       0x000010cc
 #define SNDRV_FIREWIRE_EVENT_DICE_NOTIFICATION 0xd1ce004e
 #define SNDRV_FIREWIRE_EVENT_EFW_RESPONSE      0x4e617475
+#define SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE   0x746e736c
 
 struct snd_firewire_event_common {
        unsigned int type; /* SNDRV_FIREWIRE_EVENT_xxx */
@@ -40,11 +41,17 @@ struct snd_firewire_event_efw_response {
        __be32 response[0];     /* some responses */
 };
 
+struct snd_firewire_event_digi00x_message {
+       unsigned int type;
+       __u32 message;  /* Digi00x-specific message */
+};
+
 union snd_firewire_event {
        struct snd_firewire_event_common            common;
        struct snd_firewire_event_lock_status       lock_status;
        struct snd_firewire_event_dice_notification dice_notification;
        struct snd_firewire_event_efw_response      efw_response;
+       struct snd_firewire_event_digi00x_message   digi00x_message;
 };
 
 
@@ -56,6 +63,8 @@ union snd_firewire_event {
 #define SNDRV_FIREWIRE_TYPE_FIREWORKS  2
 #define SNDRV_FIREWIRE_TYPE_BEBOB      3
 #define SNDRV_FIREWIRE_TYPE_OXFW       4
+#define SNDRV_FIREWIRE_TYPE_DIGI00X    5
+#define SNDRV_FIREWIRE_TYPE_TASCAM     6
 /* RME, MOTU, ... */
 
 struct snd_firewire_get_info {
index 5737332d38f2cea1f8c2f47f14e9d01282c87a52..c4db6f5b306ecb951cfa7cda58ab44add749147b 100644 (file)
  *   Foundation, Inc., 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#ifdef __KERNEL__
 #include <linux/types.h>
-#else
-#include <stdint.h>
-#endif
 
 /* Maximum channels is 64 even on 56Mode you have 64playbacks to matrix */
 #define HDSPM_MAX_CHANNELS      64
@@ -46,15 +42,15 @@ enum hdspm_speed {
 /* -------------------- IOCTL Peak/RMS Meters -------------------- */
 
 struct hdspm_peak_rms {
-       uint32_t input_peaks[64];
-       uint32_t playback_peaks[64];
-       uint32_t output_peaks[64];
+       __u32 input_peaks[64];
+       __u32 playback_peaks[64];
+       __u32 output_peaks[64];
 
-       uint64_t input_rms[64];
-       uint64_t playback_rms[64];
-       uint64_t output_rms[64];
+       __u64 input_rms[64];
+       __u64 playback_rms[64];
+       __u64 output_rms[64];
 
-       uint8_t speed; /* enum {ss, ds, qs} */
+       __u8 speed; /* enum {ss, ds, qs} */
        int status2;
 };
 
@@ -155,21 +151,21 @@ enum hdspm_syncsource {
 };
 
 struct hdspm_status {
-       uint8_t card_type; /* enum hdspm_io_type */
+       __u8 card_type; /* enum hdspm_io_type */
        enum hdspm_syncsource autosync_source;
 
-       uint64_t card_clock;
-       uint32_t master_period;
+       __u64 card_clock;
+       __u32 master_period;
 
        union {
                struct {
-                       uint8_t sync_wc; /* enum hdspm_sync */
-                       uint8_t sync_madi; /* enum hdspm_sync */
-                       uint8_t sync_tco; /* enum hdspm_sync */
-                       uint8_t sync_in; /* enum hdspm_sync */
-                       uint8_t madi_input; /* enum hdspm_madi_input */
-                       uint8_t channel_format; /* enum hdspm_madi_channel_format */
-                       uint8_t frame_format; /* enum hdspm_madi_frame_format */
+                       __u8 sync_wc; /* enum hdspm_sync */
+                       __u8 sync_madi; /* enum hdspm_sync */
+                       __u8 sync_tco; /* enum hdspm_sync */
+                       __u8 sync_in; /* enum hdspm_sync */
+                       __u8 madi_input; /* enum hdspm_madi_input */
+                       __u8 channel_format; /* enum hdspm_madi_channel_format */
+                       __u8 frame_format; /* enum hdspm_madi_frame_format */
                } madi;
        } card_specific;
 };
@@ -184,7 +180,7 @@ struct hdspm_status {
 #define HDSPM_ADDON_TCO 1
 
 struct hdspm_version {
-       uint8_t card_type; /* enum hdspm_io_type */
+       __u8 card_type; /* enum hdspm_io_type */
        char cardname[20];
        unsigned int serial;
        unsigned short firmware_rev;
index e70fcd12eeebe813bafd7e56f014bcf5b4e33647..e1a5110bd63b03cdd19ae4324a51291adc738651 100644 (file)
@@ -196,5 +196,10 @@ int main(void)
        DEVID_FIELD(ulpi_device_id, vendor);
        DEVID_FIELD(ulpi_device_id, product);
 
+       DEVID(hda_device_id);
+       DEVID_FIELD(hda_device_id, vendor_id);
+       DEVID_FIELD(hda_device_id, rev_id);
+       DEVID_FIELD(hda_device_id, api_version);
+
        return 0;
 }
index 5f208820913259651246d71a3def44f49fa8da44..fc51d4bff3f879fdea3a7861e8a3f82dbf982c44 100644 (file)
@@ -1250,6 +1250,23 @@ static int do_ulpi_entry(const char *filename, void *symval,
 }
 ADD_TO_DEVTABLE("ulpi", ulpi_device_id, do_ulpi_entry);
 
+/* Looks like: hdaudio:vNrNaN */
+static int do_hda_entry(const char *filename, void *symval, char *alias)
+{
+       DEF_FIELD(symval, hda_device_id, vendor_id);
+       DEF_FIELD(symval, hda_device_id, rev_id);
+       DEF_FIELD(symval, hda_device_id, api_version);
+
+       strcpy(alias, "hdaudio:");
+       ADD(alias, "v", vendor_id != 0, vendor_id);
+       ADD(alias, "r", rev_id != 0, rev_id);
+       ADD(alias, "a", api_version != 0, api_version);
+
+       add_wildcard(alias);
+       return 1;
+}
+ADD_TO_DEVTABLE("hdaudio", hda_device_id, do_hda_entry);
+
 /* Does namelen bytes of name exactly match the symbol? */
 static bool sym_is(const char *name, unsigned namelen, const char *symbol)
 {
index 38590b322c544bbec8a2c6f2852eced1b8986dfb..fbd5dad0c484006c1881156e2930d5020d589672 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -43,7 +44,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
        .reset  = pxa2xx_ac97_reset,
 };
 
-static unsigned long pxa2xx_ac97_pcm_out_req = 12;
+static struct pxad_param pxa2xx_ac97_pcm_out_req = {
+       .prio = PXAD_PRIO_LOWEST,
+       .drcmr = 12,
+};
+
 static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
        .addr           = __PREG(PCDR),
        .addr_width     = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -51,7 +56,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
        .filter_data    = &pxa2xx_ac97_pcm_out_req,
 };
 
-static unsigned long pxa2xx_ac97_pcm_in_req = 11;
+static struct pxad_param pxa2xx_ac97_pcm_in_req = {
+       .prio = PXAD_PRIO_LOWEST,
+       .drcmr = 11,
+};
+
 static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = {
        .addr           = __PREG(PCDR),
        .addr_width     = DMA_SLAVE_BUSWIDTH_4_BYTES,
index 01f8fdc42b1b8a3e2c5a83c955bae76f23256071..e9b98af6b52c83faecccd4cc1011286a75560849 100644 (file)
@@ -8,6 +8,7 @@
 #include <linux/module.h>
 #include <linux/dma-mapping.h>
 #include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -15,8 +16,6 @@
 #include <sound/pxa2xx-lib.h>
 #include <sound/dmaengine_pcm.h>
 
-#include <mach/dma.h>
-
 #include "pxa2xx-pcm.h"
 
 static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
@@ -31,7 +30,7 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
        .period_bytes_min       = 32,
        .period_bytes_max       = 8192 - 32,
        .periods_min            = 1,
-       .periods_max            = PAGE_SIZE/sizeof(pxa_dma_desc),
+       .periods_max            = 256,
        .buffer_bytes_max       = 128 * 1024,
        .fifo_size              = 32,
 };
@@ -39,65 +38,29 @@ static const struct snd_pcm_hardware pxa2xx_pcm_hardware = {
 int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params)
 {
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct pxa2xx_runtime_data *rtd = runtime->private_data;
-       size_t totsize = params_buffer_bytes(params);
-       size_t period = params_period_bytes(params);
-       pxa_dma_desc *dma_desc;
-       dma_addr_t dma_buff_phys, next_desc_phys;
-       u32 dcmd = DCMD_INCSRCADDR | DCMD_FLOWTRG;
+       struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct snd_dmaengine_dai_dma_data *dma_params;
+       struct dma_slave_config config;
+       int ret;
 
-       /* temporary transition hack */
-       switch (rtd->params->addr_width) {
-       case DMA_SLAVE_BUSWIDTH_1_BYTE:
-               dcmd |= DCMD_WIDTH1;
-               break;
-       case DMA_SLAVE_BUSWIDTH_2_BYTES:
-               dcmd |= DCMD_WIDTH2;
-               break;
-       case DMA_SLAVE_BUSWIDTH_4_BYTES:
-               dcmd |= DCMD_WIDTH4;
-               break;
-       default:
-               /* can't happen */
-               break;
-       }
+       dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+       if (!dma_params)
+               return 0;
 
-       switch (rtd->params->maxburst) {
-       case 8:
-               dcmd |= DCMD_BURST8;
-               break;
-       case 16:
-               dcmd |= DCMD_BURST16;
-               break;
-       case 32:
-               dcmd |= DCMD_BURST32;
-               break;
-       }
+       ret = snd_hwparams_to_dma_slave_config(substream, params, &config);
+       if (ret)
+               return ret;
 
-       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-       runtime->dma_bytes = totsize;
+       snd_dmaengine_pcm_set_config_from_dai_data(substream,
+                       snd_soc_dai_get_dma_data(rtd->cpu_dai, substream),
+                       &config);
 
-       dma_desc = rtd->dma_desc_array;
-       next_desc_phys = rtd->dma_desc_array_phys;
-       dma_buff_phys = runtime->dma_addr;
-       do {
-               next_desc_phys += sizeof(pxa_dma_desc);
-               dma_desc->ddadr = next_desc_phys;
-               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-                       dma_desc->dsadr = dma_buff_phys;
-                       dma_desc->dtadr = rtd->params->addr;
-               } else {
-                       dma_desc->dsadr = rtd->params->addr;
-                       dma_desc->dtadr = dma_buff_phys;
-               }
-               if (period > totsize)
-                       period = totsize;
-               dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN;
-               dma_desc++;
-               dma_buff_phys += period;
-       } while (totsize -= period);
-       dma_desc[-1].ddadr = rtd->dma_desc_array_phys;
+       ret = dmaengine_slave_config(chan, &config);
+       if (ret)
+               return ret;
+
+       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
 
        return 0;
 }
@@ -105,13 +68,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_params);
 
 int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
 {
-       struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
-
-       if (rtd && rtd->params && rtd->params->filter_data) {
-               unsigned long req = *(unsigned long *) rtd->params->filter_data;
-               DRCMR(req) = 0;
-       }
-
        snd_pcm_set_runtime_buffer(substream, NULL);
        return 0;
 }
@@ -119,100 +75,36 @@ EXPORT_SYMBOL(__pxa2xx_pcm_hw_free);
 
 int pxa2xx_pcm_trigger(struct snd_pcm_substream *substream, int cmd)
 {
-       struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
-       int ret = 0;
-
-       switch (cmd) {
-       case SNDRV_PCM_TRIGGER_START:
-               DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
-               DCSR(prtd->dma_ch) = DCSR_RUN;
-               break;
-
-       case SNDRV_PCM_TRIGGER_STOP:
-       case SNDRV_PCM_TRIGGER_SUSPEND:
-       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               DCSR(prtd->dma_ch) &= ~DCSR_RUN;
-               break;
-
-       case SNDRV_PCM_TRIGGER_RESUME:
-               DCSR(prtd->dma_ch) |= DCSR_RUN;
-               break;
-       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               DDADR(prtd->dma_ch) = prtd->dma_desc_array_phys;
-               DCSR(prtd->dma_ch) |= DCSR_RUN;
-               break;
-
-       default:
-               ret = -EINVAL;
-       }
-
-       return ret;
+       return snd_dmaengine_pcm_trigger(substream, cmd);
 }
 EXPORT_SYMBOL(pxa2xx_pcm_trigger);
 
 snd_pcm_uframes_t
 pxa2xx_pcm_pointer(struct snd_pcm_substream *substream)
 {
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct pxa2xx_runtime_data *prtd = runtime->private_data;
-
-       dma_addr_t ptr = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
-                        DSADR(prtd->dma_ch) : DTADR(prtd->dma_ch);
-       snd_pcm_uframes_t x = bytes_to_frames(runtime, ptr - runtime->dma_addr);
-
-       if (x == runtime->buffer_size)
-               x = 0;
-       return x;
+       return snd_dmaengine_pcm_pointer(substream);
 }
 EXPORT_SYMBOL(pxa2xx_pcm_pointer);
 
 int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
 {
-       struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
-       unsigned long req;
-
-       if (!prtd || !prtd->params)
-               return 0;
-
-       if (prtd->dma_ch == -1)
-               return -EINVAL;
-
-       DCSR(prtd->dma_ch) &= ~DCSR_RUN;
-       DCSR(prtd->dma_ch) = 0;
-       DCMD(prtd->dma_ch) = 0;
-       req = *(unsigned long *) prtd->params->filter_data;
-       DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD;
-
        return 0;
 }
 EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
 
-void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
-{
-       struct snd_pcm_substream *substream = dev_id;
-       int dcsr;
-
-       dcsr = DCSR(dma_ch);
-       DCSR(dma_ch) = dcsr & ~DCSR_STOPIRQEN;
-
-       if (dcsr & DCSR_ENDINTR) {
-               snd_pcm_period_elapsed(substream);
-       } else {
-               printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n",
-                       dma_ch, dcsr);
-               snd_pcm_stop_xrun(substream);
-       }
-}
-EXPORT_SYMBOL(pxa2xx_pcm_dma_irq);
-
 int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
 {
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       struct pxa2xx_runtime_data *rtd;
+       struct snd_dmaengine_dai_dma_data *dma_params;
        int ret;
 
        runtime->hw = pxa2xx_pcm_hardware;
 
+       dma_params = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
+       if (!dma_params)
+               return 0;
+
        /*
         * For mysterious reasons (and despite what the manual says)
         * playback samples are lost if the DMA count is not a multiple
@@ -221,48 +113,27 @@ int __pxa2xx_pcm_open(struct snd_pcm_substream *substream)
        ret = snd_pcm_hw_constraint_step(runtime, 0,
                SNDRV_PCM_HW_PARAM_PERIOD_BYTES, 32);
        if (ret)
-               goto out;
+               return ret;
 
        ret = snd_pcm_hw_constraint_step(runtime, 0,
                SNDRV_PCM_HW_PARAM_BUFFER_BYTES, 32);
        if (ret)
-               goto out;
+               return ret;
 
        ret = snd_pcm_hw_constraint_integer(runtime,
                                            SNDRV_PCM_HW_PARAM_PERIODS);
        if (ret < 0)
-               goto out;
-
-       ret = -ENOMEM;
-       rtd = kzalloc(sizeof(*rtd), GFP_KERNEL);
-       if (!rtd)
-               goto out;
-       rtd->dma_desc_array =
-               dma_alloc_writecombine(substream->pcm->card->dev, PAGE_SIZE,
-                                      &rtd->dma_desc_array_phys, GFP_KERNEL);
-       if (!rtd->dma_desc_array)
-               goto err1;
+               return ret;
 
-       rtd->dma_ch = -1;
-       runtime->private_data = rtd;
-       return 0;
-
- err1:
-       kfree(rtd);
- out:
-       return ret;
+       return snd_dmaengine_pcm_open_request_chan(substream,
+                                       pxad_filter_fn,
+                                       dma_params->filter_data);
 }
 EXPORT_SYMBOL(__pxa2xx_pcm_open);
 
 int __pxa2xx_pcm_close(struct snd_pcm_substream *substream)
 {
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct pxa2xx_runtime_data *rtd = runtime->private_data;
-
-       dma_free_writecombine(substream->pcm->card->dev, PAGE_SIZE,
-                             rtd->dma_desc_array, rtd->dma_desc_array_phys);
-       kfree(rtd);
-       return 0;
+       return snd_dmaengine_pcm_close_release_chan(substream);
 }
 EXPORT_SYMBOL(__pxa2xx_pcm_close);
 
index 83be8e3f095ef46fb58d7567c84c5cb1b0db1b73..83fcfac977396cd1d365289d70ca0857bb3127c4 100644 (file)
@@ -46,17 +46,13 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
 
        rtd->params = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) ?
                      client->playback_params : client->capture_params;
-       ret = pxa_request_dma("dma", DMA_PRIO_LOW,
-                             pxa2xx_pcm_dma_irq, substream);
-       if (ret < 0)
-               goto err2;
-       rtd->dma_ch = ret;
 
        ret = client->startup(substream);
        if (!ret)
-               goto out;
+               goto err2;
+
+       return 0;
 
-       pxa_free_dma(rtd->dma_ch);
  err2:
        __pxa2xx_pcm_close(substream);
  out:
@@ -66,9 +62,7 @@ static int pxa2xx_pcm_open(struct snd_pcm_substream *substream)
 static int pxa2xx_pcm_close(struct snd_pcm_substream *substream)
 {
        struct pxa2xx_pcm_client *client = substream->private_data;
-       struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
 
-       pxa_free_dma(rtd->dma_ch);
        client->shutdown(substream);
 
        return __pxa2xx_pcm_close(substream);
index 00330985beec1f7ac0e07adb076904f98a35d51a..8fa2b7c9e6b881a5576b373d060f3766277d33e3 100644 (file)
@@ -13,8 +13,6 @@
 struct pxa2xx_runtime_data {
        int dma_ch;
        struct snd_dmaengine_dai_dma_data *params;
-       struct pxa_dma_desc *dma_desc_array;
-       dma_addr_t dma_desc_array_phys;
 };
 
 struct pxa2xx_pcm_client {
index 6c96feeaf01eb08794be6321fa0ac35aba66b3e8..e3e949126a5639c6a4a623750ec35922704c5110 100644 (file)
@@ -4,7 +4,7 @@ config SND_TIMER
 
 config SND_PCM
        tristate
-       select SND_TIMER
+       select SND_TIMER if SND_PCM_TIMER
 
 config SND_PCM_ELD
        bool
@@ -93,6 +93,17 @@ config SND_PCM_OSS_PLUGINS
           support conversion of channels, formats and rates. It will
           behave like most of new OSS/Free drivers in 2.4/2.6 kernels.
 
+config SND_PCM_TIMER
+       bool "PCM timer interface" if EXPERT
+       default y
+       help
+         If you disable this option, pcm timer will be inavailable, so
+         those stubs used pcm timer (e.g. dmix, dsnoop & co) may work
+         incorrectlly.
+
+         For some embedded device, we may disable it to reduce memory
+         footprint, about 20KB on x86_64 platform.
+
 config SND_SEQUENCER_OSS
        bool "OSS Sequencer API"
        depends on SND_SEQUENCER
index 3354f91e003ad13e100887c9bf9050c5ae51252a..48ab4b8f82790809838c3a87535587636ea69ed7 100644 (file)
@@ -13,8 +13,9 @@ snd-$(CONFIG_SND_OSSEMUL) += sound_oss.o
 snd-$(CONFIG_SND_VMASTER) += vmaster.o
 snd-$(CONFIG_SND_JACK)   += ctljack.o jack.o
 
-snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
+snd-pcm-y := pcm.o pcm_native.o pcm_lib.o pcm_misc.o \
                pcm_memory.o memalloc.o
+snd-pcm-$(CONFIG_SND_PCM_TIMER) += pcm_timer.o
 snd-pcm-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
 snd-pcm-$(CONFIG_SND_PCM_ELD) += pcm_drm_eld.o
 snd-pcm-$(CONFIG_SND_PCM_IEC958) += pcm_iec958.o
index a99f7200ff3f2929dd17bc85a64c296460346e02..7a8c79dd9734039d2e14ae1a7f23763e7d6fc519 100644 (file)
@@ -1177,7 +1177,8 @@ static void snd_mixer_oss_proc_write(struct snd_info_entry *entry,
        struct snd_mixer_oss *mixer = entry->private_data;
        char line[128], str[32], idxstr[16];
        const char *cptr;
-       int ch, idx;
+       unsigned int idx;
+       int ch;
        struct snd_mixer_oss_assign_table *tbl;
        struct slot *slot;
 
index 02bd96954dc42c05266ef9d04a3d77918cf9cf6c..308c9ecf73db18415aa1157ee4e895731a5abc36 100644 (file)
@@ -1014,9 +1014,6 @@ void snd_pcm_detach_substream(struct snd_pcm_substream *substream)
        snd_free_pages((void*)runtime->control,
                       PAGE_ALIGN(sizeof(struct snd_pcm_mmap_control)));
        kfree(runtime->hw_constraints.rules);
-#ifdef CONFIG_SND_PCM_XRUN_DEBUG
-       kfree(runtime->hwptr_log);
-#endif
        kfree(runtime);
        substream->runtime = NULL;
        put_pid(substream->pid);
index 7d45645f10ba99e33b54b3218559778db3aca36a..6b5a811e01a544f3379829b8ebfa3773904691bb 100644 (file)
@@ -801,7 +801,7 @@ void snd_interval_mulkdiv(const struct snd_interval *a, unsigned int k,
  * negative error code.
  */
 int snd_interval_ratnum(struct snd_interval *i,
-                       unsigned int rats_count, struct snd_ratnum *rats,
+                       unsigned int rats_count, const struct snd_ratnum *rats,
                        unsigned int *nump, unsigned int *denp)
 {
        unsigned int best_num, best_den;
@@ -920,7 +920,8 @@ EXPORT_SYMBOL(snd_interval_ratnum);
  * negative error code.
  */
 static int snd_interval_ratden(struct snd_interval *i,
-                              unsigned int rats_count, struct snd_ratden *rats,
+                              unsigned int rats_count,
+                              const struct snd_ratden *rats,
                               unsigned int *nump, unsigned int *denp)
 {
        unsigned int best_num, best_diff, best_den;
@@ -1339,7 +1340,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_ranges);
 static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
                                   struct snd_pcm_hw_rule *rule)
 {
-       struct snd_pcm_hw_constraint_ratnums *r = rule->private;
+       const struct snd_pcm_hw_constraint_ratnums *r = rule->private;
        unsigned int num = 0, den = 0;
        int err;
        err = snd_interval_ratnum(hw_param_interval(params, rule->var),
@@ -1363,10 +1364,10 @@ static int snd_pcm_hw_rule_ratnums(struct snd_pcm_hw_params *params,
 int snd_pcm_hw_constraint_ratnums(struct snd_pcm_runtime *runtime, 
                                  unsigned int cond,
                                  snd_pcm_hw_param_t var,
-                                 struct snd_pcm_hw_constraint_ratnums *r)
+                                 const struct snd_pcm_hw_constraint_ratnums *r)
 {
        return snd_pcm_hw_rule_add(runtime, cond, var,
-                                  snd_pcm_hw_rule_ratnums, r,
+                                  snd_pcm_hw_rule_ratnums, (void *)r,
                                   var, -1);
 }
 
@@ -1375,7 +1376,7 @@ EXPORT_SYMBOL(snd_pcm_hw_constraint_ratnums);
 static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
                                   struct snd_pcm_hw_rule *rule)
 {
-       struct snd_pcm_hw_constraint_ratdens *r = rule->private;
+       const struct snd_pcm_hw_constraint_ratdens *r = rule->private;
        unsigned int num = 0, den = 0;
        int err = snd_interval_ratden(hw_param_interval(params, rule->var),
                                  r->nrats, r->rats, &num, &den);
@@ -1398,10 +1399,10 @@ static int snd_pcm_hw_rule_ratdens(struct snd_pcm_hw_params *params,
 int snd_pcm_hw_constraint_ratdens(struct snd_pcm_runtime *runtime, 
                                  unsigned int cond,
                                  snd_pcm_hw_param_t var,
-                                 struct snd_pcm_hw_constraint_ratdens *r)
+                                 const struct snd_pcm_hw_constraint_ratdens *r)
 {
        return snd_pcm_hw_rule_add(runtime, cond, var,
-                                  snd_pcm_hw_rule_ratdens, r,
+                                  snd_pcm_hw_rule_ratdens, (void *)r,
                                   var, -1);
 }
 
@@ -1875,20 +1876,17 @@ void snd_pcm_period_elapsed(struct snd_pcm_substream *substream)
                return;
        runtime = substream->runtime;
 
-       if (runtime->transfer_ack_begin)
-               runtime->transfer_ack_begin(substream);
-
        snd_pcm_stream_lock_irqsave(substream, flags);
        if (!snd_pcm_running(substream) ||
            snd_pcm_update_hw_ptr0(substream, 1) < 0)
                goto _end;
 
+#ifdef CONFIG_SND_PCM_TIMER
        if (substream->timer_running)
                snd_timer_interrupt(substream->timer, 1);
+#endif
  _end:
        snd_pcm_stream_unlock_irqrestore(substream, flags);
-       if (runtime->transfer_ack_end)
-               runtime->transfer_ack_end(substream);
        kill_fasync(&runtime->fasync, SIGIO, POLL_IN);
 }
 
index 75888dd38a7fc87acf5fcd7789570ad353dce482..a8b27cdc2844855fb2d030a246bf2374e7e27269 100644 (file)
@@ -486,6 +486,16 @@ static void snd_pcm_set_state(struct snd_pcm_substream *substream, int state)
        snd_pcm_stream_unlock_irq(substream);
 }
 
+static inline void snd_pcm_timer_notify(struct snd_pcm_substream *substream,
+                                       int event)
+{
+#ifdef CONFIG_SND_PCM_TIMER
+       if (substream->timer)
+               snd_timer_notify(substream->timer, event,
+                                       &substream->runtime->trigger_tstamp);
+#endif
+}
+
 static int snd_pcm_hw_params(struct snd_pcm_substream *substream,
                             struct snd_pcm_hw_params *params)
 {
@@ -650,7 +660,8 @@ static int snd_pcm_sw_params(struct snd_pcm_substream *substream,
        }
        snd_pcm_stream_unlock_irq(substream);
 
-       if (params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
+       if (params->tstamp_mode < 0 ||
+           params->tstamp_mode > SNDRV_PCM_TSTAMP_LAST)
                return -EINVAL;
        if (params->proto >= SNDRV_PROTOCOL_VERSION(2, 0, 12) &&
            params->tstamp_type > SNDRV_PCM_TSTAMP_TYPE_LAST)
@@ -1042,9 +1053,7 @@ static void snd_pcm_post_start(struct snd_pcm_substream *substream, int state)
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK &&
            runtime->silence_size > 0)
                snd_pcm_playback_silence(substream, ULONG_MAX);
-       if (substream->timer)
-               snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTART,
-                                &runtime->trigger_tstamp);
+       snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTART);
 }
 
 static struct action_ops snd_pcm_action_start = {
@@ -1092,9 +1101,7 @@ static void snd_pcm_post_stop(struct snd_pcm_substream *substream, int state)
        if (runtime->status->state != state) {
                snd_pcm_trigger_tstamp(substream);
                runtime->status->state = state;
-               if (substream->timer)
-                       snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSTOP,
-                                        &runtime->trigger_tstamp);
+               snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSTOP);
        }
        wake_up(&runtime->sleep);
        wake_up(&runtime->tsleep);
@@ -1208,18 +1215,12 @@ static void snd_pcm_post_pause(struct snd_pcm_substream *substream, int push)
        snd_pcm_trigger_tstamp(substream);
        if (push) {
                runtime->status->state = SNDRV_PCM_STATE_PAUSED;
-               if (substream->timer)
-                       snd_timer_notify(substream->timer,
-                                        SNDRV_TIMER_EVENT_MPAUSE,
-                                        &runtime->trigger_tstamp);
+               snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MPAUSE);
                wake_up(&runtime->sleep);
                wake_up(&runtime->tsleep);
        } else {
                runtime->status->state = SNDRV_PCM_STATE_RUNNING;
-               if (substream->timer)
-                       snd_timer_notify(substream->timer,
-                                        SNDRV_TIMER_EVENT_MCONTINUE,
-                                        &runtime->trigger_tstamp);
+               snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MCONTINUE);
        }
 }
 
@@ -1267,9 +1268,7 @@ static void snd_pcm_post_suspend(struct snd_pcm_substream *substream, int state)
        snd_pcm_trigger_tstamp(substream);
        runtime->status->suspended_state = runtime->status->state;
        runtime->status->state = SNDRV_PCM_STATE_SUSPENDED;
-       if (substream->timer)
-               snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MSUSPEND,
-                                &runtime->trigger_tstamp);
+       snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MSUSPEND);
        wake_up(&runtime->sleep);
        wake_up(&runtime->tsleep);
 }
@@ -1373,9 +1372,7 @@ static void snd_pcm_post_resume(struct snd_pcm_substream *substream, int state)
        struct snd_pcm_runtime *runtime = substream->runtime;
        snd_pcm_trigger_tstamp(substream);
        runtime->status->state = runtime->status->suspended_state;
-       if (substream->timer)
-               snd_timer_notify(substream->timer, SNDRV_TIMER_EVENT_MRESUME,
-                                &runtime->trigger_tstamp);
+       snd_pcm_timer_notify(substream, SNDRV_TIMER_EVENT_MRESUME);
 }
 
 static struct action_ops snd_pcm_action_resume = {
@@ -2226,7 +2223,8 @@ void snd_pcm_release_substream(struct snd_pcm_substream *substream)
 
        snd_pcm_drop(substream);
        if (substream->hw_opened) {
-               if (substream->ops->hw_free != NULL)
+               if (substream->ops->hw_free &&
+                   substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
                        substream->ops->hw_free(substream);
                substream->ops->close(substream);
                substream->hw_opened = 0;
index ccd893566f1decbe80d21d350c7041d97c969988..046cb586fb2f7271c88dc1e02354625d6dcca864 100644 (file)
@@ -91,8 +91,7 @@ snd_seq_oss_readq_clear(struct seq_oss_readq *q)
                q->head = q->tail = 0;
        }
        /* if someone sleeping, wake'em up */
-       if (waitqueue_active(&q->midi_sleep))
-               wake_up(&q->midi_sleep);
+       wake_up(&q->midi_sleep);
        q->input_time = (unsigned long)-1;
 }
 
@@ -138,8 +137,7 @@ snd_seq_oss_readq_put_event(struct seq_oss_readq *q, union evrec *ev)
        q->qlen++;
 
        /* wake up sleeper */
-       if (waitqueue_active(&q->midi_sleep))
-               wake_up(&q->midi_sleep);
+       wake_up(&q->midi_sleep);
 
        spin_unlock_irqrestore(&q->lock, flags);
 
index d50338bbc21f1dbc2637ae9b2938716679b919c9..1f6788a18444111c724bc417741c2a398da89900 100644 (file)
@@ -138,9 +138,7 @@ snd_seq_oss_writeq_wakeup(struct seq_oss_writeq *q, abstime_t time)
        spin_lock_irqsave(&q->sync_lock, flags);
        q->sync_time = time;
        q->sync_event_put = 0;
-       if (waitqueue_active(&q->sync_sleep)) {
-               wake_up(&q->sync_sleep);
-       }
+       wake_up(&q->sync_sleep);
        spin_unlock_irqrestore(&q->sync_lock, flags);
 }
 
index 8850b7de1d380585a784e6f72a5dccc5812b5ad4..bee0e5f1a1166d18e443f8294754f01bf55101ca 100644 (file)
@@ -120,4 +120,31 @@ config SND_BEBOB
           To compile this driver as a module, choose M here: the module
           will be called snd-bebob.
 
+config SND_FIREWIRE_DIGI00X
+       tristate "Digidesign Digi 002/003 family support"
+       select SND_FIREWIRE_LIB
+       select SND_HWDEP
+       help
+        Say Y here to include support for Digidesign Digi 002/003 family.
+         * Digi 002 Console
+         * Digi 002 Rack
+         * Digi 003 Console
+         * Digi 003 Rack
+         * Digi 003 Rack+
+
+        To compile this driver as a module, choose M here: the module
+        will be called snd-firewire-digi00x.
+
+config SND_FIREWIRE_TASCAM
+       tristate "TASCAM FireWire series support"
+       select SND_FIREWIRE_LIB
+       select SND_HWDEP
+       help
+        Say Y here to include support for TASCAM.
+         * FW-1884
+         * FW-1082
+
+        To compile this driver as a module, choose M here: the module
+        will be called snd-firewire-tascam.
+
 endif # SND_FIREWIRE
index 8b37f084b2aba800fb4c82a34089b4957609b2d2..f5fb62551c600cd02664244108470f8613a512ef 100644 (file)
@@ -1,6 +1,5 @@
 snd-firewire-lib-objs := lib.o iso-resources.o packets-buffer.o \
-                        fcp.o cmp.o amdtp.o
-snd-oxfw-objs := oxfw.o
+                        fcp.o cmp.o amdtp-stream.o amdtp-am824.o
 snd-isight-objs := isight.o
 snd-scs1x-objs := scs1x.o
 
@@ -11,3 +10,5 @@ obj-$(CONFIG_SND_ISIGHT) += snd-isight.o
 obj-$(CONFIG_SND_SCS1X) += snd-scs1x.o
 obj-$(CONFIG_SND_FIREWORKS) += fireworks/
 obj-$(CONFIG_SND_BEBOB) += bebob/
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += digi00x/
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += tascam/
diff --git a/sound/firewire/amdtp-am824.c b/sound/firewire/amdtp-am824.c
new file mode 100644 (file)
index 0000000..bebddc6
--- /dev/null
@@ -0,0 +1,465 @@
+/*
+ * AM824 format in Audio and Music Data Transmission Protocol (IEC 61883-6)
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Copyright (c) 2015 Takashi Sakamoto <o-takashi@sakamocchi.jp>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/slab.h>
+
+#include "amdtp-am824.h"
+
+#define CIP_FMT_AM             0x10
+
+/* "Clock-based rate control mode" is just supported. */
+#define AMDTP_FDF_AM824                0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND  3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS     8
+
+struct amdtp_am824 {
+       struct snd_rawmidi_substream *midi[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+       int midi_fifo_limit;
+       int midi_fifo_used[AM824_MAX_CHANNELS_FOR_MIDI * 8];
+       unsigned int pcm_channels;
+       unsigned int midi_ports;
+
+       u8 pcm_positions[AM824_MAX_CHANNELS_FOR_PCM];
+       u8 midi_position;
+
+       void (*transfer_samples)(struct amdtp_stream *s,
+                                struct snd_pcm_substream *pcm,
+                                __be32 *buffer, unsigned int frames);
+
+       unsigned int frame_multiplier;
+};
+
+/**
+ * amdtp_am824_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
+ * @rate: the sample rate
+ * @pcm_channels: the number of PCM samples in each data block, to be encoded
+ *                as AM824 multi-bit linear audio
+ * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
+ * @double_pcm_frames: one data block transfers two PCM frames
+ *
+ * The parameters must be set before the stream is started, and must not be
+ * changed while the stream is running.
+ */
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+                              unsigned int pcm_channels,
+                              unsigned int midi_ports,
+                              bool double_pcm_frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       unsigned int midi_channels;
+       unsigned int i;
+       int err;
+
+       if (amdtp_stream_running(s))
+               return -EINVAL;
+
+       if (pcm_channels > AM824_MAX_CHANNELS_FOR_PCM)
+               return -EINVAL;
+
+       midi_channels = DIV_ROUND_UP(midi_ports, 8);
+       if (midi_channels > AM824_MAX_CHANNELS_FOR_MIDI)
+               return -EINVAL;
+
+       if (WARN_ON(amdtp_stream_running(s)) ||
+           WARN_ON(pcm_channels > AM824_MAX_CHANNELS_FOR_PCM) ||
+           WARN_ON(midi_channels > AM824_MAX_CHANNELS_FOR_MIDI))
+               return -EINVAL;
+
+       err = amdtp_stream_set_parameters(s, rate,
+                                         pcm_channels + midi_channels);
+       if (err < 0)
+               return err;
+
+       s->fdf = AMDTP_FDF_AM824 | s->sfc;
+
+       p->pcm_channels = pcm_channels;
+       p->midi_ports = midi_ports;
+
+       /*
+        * In IEC 61883-6, one data block represents one event. In ALSA, one
+        * event equals to one PCM frame. But Dice has a quirk at higher
+        * sampling rate to transfer two PCM frames in one data block.
+        */
+       if (double_pcm_frames)
+               p->frame_multiplier = 2;
+       else
+               p->frame_multiplier = 1;
+
+       /* init the position map for PCM and MIDI channels */
+       for (i = 0; i < pcm_channels; i++)
+               p->pcm_positions[i] = i;
+       p->midi_position = p->pcm_channels;
+
+       /*
+        * We do not know the actual MIDI FIFO size of most devices.  Just
+        * assume two bytes, i.e., one byte can be received over the bus while
+        * the previous one is transmitted over MIDI.
+        * (The value here is adjusted for midi_ratelimit_per_packet().)
+        */
+       p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_parameters);
+
+/**
+ * amdtp_am824_set_pcm_position - set an index of data channel for a channel
+ *                               of PCM frame
+ * @s: the AMDTP stream
+ * @index: the index of data channel in an data block
+ * @position: the channel of PCM frame
+ */
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+                                unsigned int position)
+{
+       struct amdtp_am824 *p = s->protocol;
+
+       if (index < p->pcm_channels)
+               p->pcm_positions[index] = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_position);
+
+/**
+ * amdtp_am824_set_midi_position - set a index of data channel for MIDI
+ *                                conformant data channel
+ * @s: the AMDTP stream
+ * @position: the index of data channel in an data block
+ */
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+                                  unsigned int position)
+{
+       struct amdtp_am824 *p = s->protocol;
+
+       p->midi_position = position;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_midi_position);
+
+static void write_pcm_s32(struct amdtp_stream *s,
+                         struct snd_pcm_substream *pcm,
+                         __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       const u32 *src;
+
+       channels = p->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       buffer[p->pcm_positions[c]] =
+                                       cpu_to_be32((*src >> 8) | 0x40000000);
+                       src++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s,
+                         struct snd_pcm_substream *pcm,
+                         __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       const u16 *src;
+
+       channels = p->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       buffer[p->pcm_positions[c]] =
+                                       cpu_to_be32((*src << 8) | 0x42000000);
+                       src++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s,
+                        struct snd_pcm_substream *pcm,
+                        __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       u32 *dst;
+
+       channels = p->pcm_channels;
+       dst  = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       *dst = be32_to_cpu(buffer[p->pcm_positions[c]]) << 8;
+                       dst++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       dst = (void *)runtime->dma_area;
+       }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s,
+                             __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       unsigned int i, c, channels = p->pcm_channels;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c)
+                       buffer[p->pcm_positions[c]] = cpu_to_be32(0x40000000);
+               buffer += s->data_block_quadlets;
+       }
+}
+
+/**
+ * amdtp_am824_set_pcm_format - set the PCM format
+ * @s: the AMDTP stream to configure
+ * @format: the format of the ALSA PCM device
+ *
+ * The sample format must be set after the other parameters (rate/PCM channels/
+ * MIDI) and before the stream is started, and must not be changed while the
+ * stream is running.
+ */
+void amdtp_am824_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+       struct amdtp_am824 *p = s->protocol;
+
+       if (WARN_ON(amdtp_stream_pcm_running(s)))
+               return;
+
+       switch (format) {
+       default:
+               WARN_ON(1);
+               /* fall through */
+       case SNDRV_PCM_FORMAT_S16:
+               if (s->direction == AMDTP_OUT_STREAM) {
+                       p->transfer_samples = write_pcm_s16;
+                       break;
+               }
+               WARN_ON(1);
+               /* fall through */
+       case SNDRV_PCM_FORMAT_S32:
+               if (s->direction == AMDTP_OUT_STREAM)
+                       p->transfer_samples = write_pcm_s32;
+               else
+                       p->transfer_samples = read_pcm_s32;
+               break;
+       }
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_set_pcm_format);
+
+/**
+ * amdtp_am824_add_pcm_hw_constraints - add hw constraints for PCM substream
+ * @s:         the AMDTP stream for AM824 data block, must be initialized.
+ * @runtime:   the PCM substream runtime
+ *
+ */
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                      struct snd_pcm_runtime *runtime)
+{
+       int err;
+
+       err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+       if (err < 0)
+               return err;
+
+       /* AM824 in IEC 61883-6 can deliver 24bit data. */
+       return snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_add_pcm_hw_constraints);
+
+/**
+ * amdtp_am824_midi_trigger - start/stop playback/capture with a MIDI device
+ * @s: the AMDTP stream
+ * @port: index of MIDI port
+ * @midi: the MIDI device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of MIDI data.  This function should be called from the MIDI
+ * device's .trigger callback.
+ */
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+                             struct snd_rawmidi_substream *midi)
+{
+       struct amdtp_am824 *p = s->protocol;
+
+       if (port < p->midi_ports)
+               ACCESS_ONCE(p->midi[port]) = midi;
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_midi_trigger);
+
+/*
+ * To avoid sending MIDI bytes at too high a rate, assume that the receiving
+ * device has a FIFO, and track how much it is filled.  This values increases
+ * by one whenever we send one byte in a packet, but the FIFO empties at
+ * a constant rate independent of our packet rate.  One packet has syt_interval
+ * samples, so the number of bytes that empty out of the FIFO, per packet(!),
+ * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate.  To avoid storing
+ * fractional values, the values in midi_fifo_used[] are measured in bytes
+ * multiplied by the sample rate.
+ */
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+       struct amdtp_am824 *p = s->protocol;
+       int used;
+
+       used = p->midi_fifo_used[port];
+       if (used == 0) /* common shortcut */
+               return true;
+
+       used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+       used = max(used, 0);
+       p->midi_fifo_used[port] = used;
+
+       return used < p->midi_fifo_limit;
+}
+
+static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
+{
+       struct amdtp_am824 *p = s->protocol;
+
+       p->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+                               unsigned int frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       unsigned int f, port;
+       u8 *b;
+
+       for (f = 0; f < frames; f++) {
+               b = (u8 *)&buffer[p->midi_position];
+
+               port = (s->data_block_counter + f) % 8;
+               if (f < MAX_MIDI_RX_BLOCKS &&
+                   midi_ratelimit_per_packet(s, port) &&
+                   p->midi[port] != NULL &&
+                   snd_rawmidi_transmit(p->midi[port], &b[1], 1) == 1) {
+                       midi_rate_use_one_byte(s, port);
+                       b[0] = 0x81;
+               } else {
+                       b[0] = 0x80;
+                       b[1] = 0;
+               }
+               b[2] = 0;
+               b[3] = 0;
+
+               buffer += s->data_block_quadlets;
+       }
+}
+
+static void read_midi_messages(struct amdtp_stream *s,
+                              __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_am824 *p = s->protocol;
+       unsigned int f, port;
+       int len;
+       u8 *b;
+
+       for (f = 0; f < frames; f++) {
+               port = (s->data_block_counter + f) % 8;
+               b = (u8 *)&buffer[p->midi_position];
+
+               len = b[0] - 0x80;
+               if ((1 <= len) &&  (len <= 3) && (p->midi[port]))
+                       snd_rawmidi_receive(p->midi[port], b + 1, len);
+
+               buffer += s->data_block_quadlets;
+       }
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
+                                          unsigned int data_blocks, unsigned int *syt)
+{
+       struct amdtp_am824 *p = s->protocol;
+       struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+       unsigned int pcm_frames;
+
+       if (pcm) {
+               p->transfer_samples(s, pcm, buffer, data_blocks);
+               pcm_frames = data_blocks * p->frame_multiplier;
+       } else {
+               write_pcm_silence(s, buffer, data_blocks);
+               pcm_frames = 0;
+       }
+
+       if (p->midi_ports)
+               write_midi_messages(s, buffer, data_blocks);
+
+       return pcm_frames;
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s, __be32 *buffer,
+                                          unsigned int data_blocks, unsigned int *syt)
+{
+       struct amdtp_am824 *p = s->protocol;
+       struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+       unsigned int pcm_frames;
+
+       if (pcm) {
+               p->transfer_samples(s, pcm, buffer, data_blocks);
+               pcm_frames = data_blocks * p->frame_multiplier;
+       } else {
+               pcm_frames = 0;
+       }
+
+       if (p->midi_ports)
+               read_midi_messages(s, buffer, data_blocks);
+
+       return pcm_frames;
+}
+
+/**
+ * amdtp_am824_init - initialize an AMDTP stream structure to handle AM824
+ *                   data block
+ * @s: the AMDTP stream to initialize
+ * @unit: the target of the stream
+ * @dir: the direction of stream
+ * @flags: the packet transmission method to use
+ */
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+                    enum amdtp_stream_direction dir, enum cip_flags flags)
+{
+       amdtp_stream_process_data_blocks_t process_data_blocks;
+
+       if (dir == AMDTP_IN_STREAM)
+               process_data_blocks = process_tx_data_blocks;
+       else
+               process_data_blocks = process_rx_data_blocks;
+
+       return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+                                process_data_blocks,
+                                sizeof(struct amdtp_am824));
+}
+EXPORT_SYMBOL_GPL(amdtp_am824_init);
diff --git a/sound/firewire/amdtp-am824.h b/sound/firewire/amdtp-am824.h
new file mode 100644 (file)
index 0000000..73b07b3
--- /dev/null
@@ -0,0 +1,52 @@
+#ifndef SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+#define SOUND_FIREWIRE_AMDTP_AM824_H_INCLUDED
+
+#include <sound/pcm.h>
+#include <sound/rawmidi.h>
+
+#include "amdtp-stream.h"
+
+#define AM824_IN_PCM_FORMAT_BITS       SNDRV_PCM_FMTBIT_S32
+
+#define AM824_OUT_PCM_FORMAT_BITS      (SNDRV_PCM_FMTBIT_S16 | \
+                                        SNDRV_PCM_FMTBIT_S32)
+
+/*
+ * This module supports maximum 64 PCM channels for one PCM stream
+ * This is for our convenience.
+ */
+#define AM824_MAX_CHANNELS_FOR_PCM     64
+
+/*
+ * AMDTP packet can include channels for MIDI conformant data.
+ * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
+ * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
+ *
+ * This module supports maximum 1 MIDI conformant data channels.
+ * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
+ */
+#define AM824_MAX_CHANNELS_FOR_MIDI    1
+
+int amdtp_am824_set_parameters(struct amdtp_stream *s, unsigned int rate,
+                              unsigned int pcm_channels,
+                              unsigned int midi_ports,
+                              bool double_pcm_frames);
+
+void amdtp_am824_set_pcm_position(struct amdtp_stream *s, unsigned int index,
+                                unsigned int position);
+
+void amdtp_am824_set_midi_position(struct amdtp_stream *s,
+                                  unsigned int position);
+
+int amdtp_am824_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                      struct snd_pcm_runtime *runtime);
+
+void amdtp_am824_set_pcm_format(struct amdtp_stream *s,
+                               snd_pcm_format_t format);
+
+void amdtp_am824_midi_trigger(struct amdtp_stream *s, unsigned int port,
+                             struct snd_rawmidi_substream *midi);
+
+int amdtp_am824_init(struct amdtp_stream *s, struct fw_unit *unit,
+                    enum amdtp_stream_direction dir, enum cip_flags flags);
+#endif
diff --git a/sound/firewire/amdtp-stream.c b/sound/firewire/amdtp-stream.c
new file mode 100644 (file)
index 0000000..ed29026
--- /dev/null
@@ -0,0 +1,863 @@
+/*
+ * Audio and Music Data Transmission Protocol (IEC 61883-6) streams
+ * with Common Isochronous Packet (IEC 61883-1) headers
+ *
+ * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/device.h>
+#include <linux/err.h>
+#include <linux/firewire.h>
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include "amdtp-stream.h"
+
+#define TICKS_PER_CYCLE                3072
+#define CYCLES_PER_SECOND      8000
+#define TICKS_PER_SECOND       (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
+
+#define TRANSFER_DELAY_TICKS   0x2e00 /* 479.17 microseconds */
+
+/* isochronous header parameters */
+#define ISO_DATA_LENGTH_SHIFT  16
+#define TAG_CIP                        1
+
+/* common isochronous packet header parameters */
+#define CIP_EOH_SHIFT          31
+#define CIP_EOH                        (1u << CIP_EOH_SHIFT)
+#define CIP_EOH_MASK           0x80000000
+#define CIP_SID_SHIFT          24
+#define CIP_SID_MASK           0x3f000000
+#define CIP_DBS_MASK           0x00ff0000
+#define CIP_DBS_SHIFT          16
+#define CIP_DBC_MASK           0x000000ff
+#define CIP_FMT_SHIFT          24
+#define CIP_FMT_MASK           0x3f000000
+#define CIP_FDF_MASK           0x00ff0000
+#define CIP_FDF_SHIFT          16
+#define CIP_SYT_MASK           0x0000ffff
+#define CIP_SYT_NO_INFO                0xffff
+
+/* Audio and Music transfer protocol specific parameters */
+#define CIP_FMT_AM             0x10
+#define AMDTP_FDF_NO_DATA      0xff
+
+/* TODO: make these configurable */
+#define INTERRUPT_INTERVAL     16
+#define QUEUE_LENGTH           48
+
+#define IN_PACKET_HEADER_SIZE  4
+#define OUT_PACKET_HEADER_SIZE 0
+
+static void pcm_period_tasklet(unsigned long data);
+
+/**
+ * amdtp_stream_init - initialize an AMDTP stream structure
+ * @s: the AMDTP stream to initialize
+ * @unit: the target of the stream
+ * @dir: the direction of stream
+ * @flags: the packet transmission method to use
+ * @fmt: the value of fmt field in CIP header
+ * @process_data_blocks: callback handler to process data blocks
+ * @protocol_size: the size to allocate newly for protocol
+ */
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+                     enum amdtp_stream_direction dir, enum cip_flags flags,
+                     unsigned int fmt,
+                     amdtp_stream_process_data_blocks_t process_data_blocks,
+                     unsigned int protocol_size)
+{
+       if (process_data_blocks == NULL)
+               return -EINVAL;
+
+       s->protocol = kzalloc(protocol_size, GFP_KERNEL);
+       if (!s->protocol)
+               return -ENOMEM;
+
+       s->unit = unit;
+       s->direction = dir;
+       s->flags = flags;
+       s->context = ERR_PTR(-1);
+       mutex_init(&s->mutex);
+       tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
+       s->packet_index = 0;
+
+       init_waitqueue_head(&s->callback_wait);
+       s->callbacked = false;
+       s->sync_slave = NULL;
+
+       s->fmt = fmt;
+       s->process_data_blocks = process_data_blocks;
+
+       return 0;
+}
+EXPORT_SYMBOL(amdtp_stream_init);
+
+/**
+ * amdtp_stream_destroy - free stream resources
+ * @s: the AMDTP stream to destroy
+ */
+void amdtp_stream_destroy(struct amdtp_stream *s)
+{
+       WARN_ON(amdtp_stream_running(s));
+       kfree(s->protocol);
+       mutex_destroy(&s->mutex);
+}
+EXPORT_SYMBOL(amdtp_stream_destroy);
+
+const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
+       [CIP_SFC_32000]  =  8,
+       [CIP_SFC_44100]  =  8,
+       [CIP_SFC_48000]  =  8,
+       [CIP_SFC_88200]  = 16,
+       [CIP_SFC_96000]  = 16,
+       [CIP_SFC_176400] = 32,
+       [CIP_SFC_192000] = 32,
+};
+EXPORT_SYMBOL(amdtp_syt_intervals);
+
+const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = {
+       [CIP_SFC_32000]  =  32000,
+       [CIP_SFC_44100]  =  44100,
+       [CIP_SFC_48000]  =  48000,
+       [CIP_SFC_88200]  =  88200,
+       [CIP_SFC_96000]  =  96000,
+       [CIP_SFC_176400] = 176400,
+       [CIP_SFC_192000] = 192000,
+};
+EXPORT_SYMBOL(amdtp_rate_table);
+
+/**
+ * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream
+ * @s:         the AMDTP stream, which must be initialized.
+ * @runtime:   the PCM substream runtime
+ */
+int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                       struct snd_pcm_runtime *runtime)
+{
+       int err;
+
+       /*
+        * Currently firewire-lib processes 16 packets in one software
+        * interrupt callback. This equals to 2msec but actually the
+        * interval of the interrupts has a jitter.
+        * Additionally, even if adding a constraint to fit period size to
+        * 2msec, actual calculated frames per period doesn't equal to 2msec,
+        * depending on sampling rate.
+        * Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec.
+        * Here let us use 5msec for safe period interrupt.
+        */
+       err = snd_pcm_hw_constraint_minmax(runtime,
+                                          SNDRV_PCM_HW_PARAM_PERIOD_TIME,
+                                          5000, UINT_MAX);
+       if (err < 0)
+               goto end;
+
+       /* Non-Blocking stream has no more constraints */
+       if (!(s->flags & CIP_BLOCKING))
+               goto end;
+
+       /*
+        * One AMDTP packet can include some frames. In blocking mode, the
+        * number equals to SYT_INTERVAL. So the number is 8, 16 or 32,
+        * depending on its sampling rate. For accurate period interrupt, it's
+        * preferrable to align period/buffer sizes to current SYT_INTERVAL.
+        *
+        * TODO: These constraints can be improved with proper rules.
+        * Currently apply LCM of SYT_INTERVALs.
+        */
+       err = snd_pcm_hw_constraint_step(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
+       if (err < 0)
+               goto end;
+       err = snd_pcm_hw_constraint_step(runtime, 0,
+                                        SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
+end:
+       return err;
+}
+EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
+
+/**
+ * amdtp_stream_set_parameters - set stream parameters
+ * @s: the AMDTP stream to configure
+ * @rate: the sample rate
+ * @data_block_quadlets: the size of a data block in quadlet unit
+ *
+ * The parameters must be set before the stream is started, and must not be
+ * changed while the stream is running.
+ */
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+                               unsigned int data_block_quadlets)
+{
+       unsigned int sfc;
+
+       for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc) {
+               if (amdtp_rate_table[sfc] == rate)
+                       break;
+       }
+       if (sfc == ARRAY_SIZE(amdtp_rate_table))
+               return -EINVAL;
+
+       s->sfc = sfc;
+       s->data_block_quadlets = data_block_quadlets;
+       s->syt_interval = amdtp_syt_intervals[sfc];
+
+       /* default buffering in the device */
+       s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
+       if (s->flags & CIP_BLOCKING)
+               /* additional buffering needed to adjust for no-data packets */
+               s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
+
+       return 0;
+}
+EXPORT_SYMBOL(amdtp_stream_set_parameters);
+
+/**
+ * amdtp_stream_get_max_payload - get the stream's packet size
+ * @s: the AMDTP stream
+ *
+ * This function must not be called before the stream has been configured
+ * with amdtp_stream_set_parameters().
+ */
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
+{
+       unsigned int multiplier = 1;
+
+       if (s->flags & CIP_JUMBO_PAYLOAD)
+               multiplier = 5;
+
+       return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier;
+}
+EXPORT_SYMBOL(amdtp_stream_get_max_payload);
+
+/**
+ * amdtp_stream_pcm_prepare - prepare PCM device for running
+ * @s: the AMDTP stream
+ *
+ * This function should be called from the PCM device's .prepare callback.
+ */
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
+{
+       tasklet_kill(&s->period_tasklet);
+       s->pcm_buffer_pointer = 0;
+       s->pcm_period_pointer = 0;
+       s->pointer_flush = true;
+}
+EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
+
+static unsigned int calculate_data_blocks(struct amdtp_stream *s,
+                                         unsigned int syt)
+{
+       unsigned int phase, data_blocks;
+
+       /* Blocking mode. */
+       if (s->flags & CIP_BLOCKING) {
+               /* This module generate empty packet for 'no data'. */
+               if (syt == CIP_SYT_NO_INFO)
+                       data_blocks = 0;
+               else
+                       data_blocks = s->syt_interval;
+       /* Non-blocking mode. */
+       } else {
+               if (!cip_sfc_is_base_44100(s->sfc)) {
+                       /* Sample_rate / 8000 is an integer, and precomputed. */
+                       data_blocks = s->data_block_state;
+               } else {
+                       phase = s->data_block_state;
+
+               /*
+                * This calculates the number of data blocks per packet so that
+                * 1) the overall rate is correct and exactly synchronized to
+                *    the bus clock, and
+                * 2) packets with a rounded-up number of blocks occur as early
+                *    as possible in the sequence (to prevent underruns of the
+                *    device's buffer).
+                */
+                       if (s->sfc == CIP_SFC_44100)
+                               /* 6 6 5 6 5 6 5 ... */
+                               data_blocks = 5 + ((phase & 1) ^
+                                                  (phase == 0 || phase >= 40));
+                       else
+                               /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
+                               data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
+                       if (++phase >= (80 >> (s->sfc >> 1)))
+                               phase = 0;
+                       s->data_block_state = phase;
+               }
+       }
+
+       return data_blocks;
+}
+
+static unsigned int calculate_syt(struct amdtp_stream *s,
+                                 unsigned int cycle)
+{
+       unsigned int syt_offset, phase, index, syt;
+
+       if (s->last_syt_offset < TICKS_PER_CYCLE) {
+               if (!cip_sfc_is_base_44100(s->sfc))
+                       syt_offset = s->last_syt_offset + s->syt_offset_state;
+               else {
+               /*
+                * The time, in ticks, of the n'th SYT_INTERVAL sample is:
+                *   n * SYT_INTERVAL * 24576000 / sample_rate
+                * Modulo TICKS_PER_CYCLE, the difference between successive
+                * elements is about 1386.23.  Rounding the results of this
+                * formula to the SYT precision results in a sequence of
+                * differences that begins with:
+                *   1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ...
+                * This code generates _exactly_ the same sequence.
+                */
+                       phase = s->syt_offset_state;
+                       index = phase % 13;
+                       syt_offset = s->last_syt_offset;
+                       syt_offset += 1386 + ((index && !(index & 3)) ||
+                                             phase == 146);
+                       if (++phase >= 147)
+                               phase = 0;
+                       s->syt_offset_state = phase;
+               }
+       } else
+               syt_offset = s->last_syt_offset - TICKS_PER_CYCLE;
+       s->last_syt_offset = syt_offset;
+
+       if (syt_offset < TICKS_PER_CYCLE) {
+               syt_offset += s->transfer_delay;
+               syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
+               syt += syt_offset % TICKS_PER_CYCLE;
+
+               return syt & CIP_SYT_MASK;
+       } else {
+               return CIP_SYT_NO_INFO;
+       }
+}
+
+static void update_pcm_pointers(struct amdtp_stream *s,
+                               struct snd_pcm_substream *pcm,
+                               unsigned int frames)
+{
+       unsigned int ptr;
+
+       ptr = s->pcm_buffer_pointer + frames;
+       if (ptr >= pcm->runtime->buffer_size)
+               ptr -= pcm->runtime->buffer_size;
+       ACCESS_ONCE(s->pcm_buffer_pointer) = ptr;
+
+       s->pcm_period_pointer += frames;
+       if (s->pcm_period_pointer >= pcm->runtime->period_size) {
+               s->pcm_period_pointer -= pcm->runtime->period_size;
+               s->pointer_flush = false;
+               tasklet_hi_schedule(&s->period_tasklet);
+       }
+}
+
+static void pcm_period_tasklet(unsigned long data)
+{
+       struct amdtp_stream *s = (void *)data;
+       struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
+
+       if (pcm)
+               snd_pcm_period_elapsed(pcm);
+}
+
+static int queue_packet(struct amdtp_stream *s,
+                       unsigned int header_length,
+                       unsigned int payload_length, bool skip)
+{
+       struct fw_iso_packet p = {0};
+       int err = 0;
+
+       if (IS_ERR(s->context))
+               goto end;
+
+       p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
+       p.tag = TAG_CIP;
+       p.header_length = header_length;
+       p.payload_length = (!skip) ? payload_length : 0;
+       p.skip = skip;
+       err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
+                                  s->buffer.packets[s->packet_index].offset);
+       if (err < 0) {
+               dev_err(&s->unit->device, "queueing error: %d\n", err);
+               goto end;
+       }
+
+       if (++s->packet_index >= QUEUE_LENGTH)
+               s->packet_index = 0;
+end:
+       return err;
+}
+
+static inline int queue_out_packet(struct amdtp_stream *s,
+                                  unsigned int payload_length, bool skip)
+{
+       return queue_packet(s, OUT_PACKET_HEADER_SIZE,
+                           payload_length, skip);
+}
+
+static inline int queue_in_packet(struct amdtp_stream *s)
+{
+       return queue_packet(s, IN_PACKET_HEADER_SIZE,
+                           amdtp_stream_get_max_payload(s), false);
+}
+
+static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
+                            unsigned int syt)
+{
+       __be32 *buffer;
+       unsigned int payload_length;
+       unsigned int pcm_frames;
+       struct snd_pcm_substream *pcm;
+
+       buffer = s->buffer.packets[s->packet_index].buffer;
+       pcm_frames = s->process_data_blocks(s, buffer + 2, data_blocks, &syt);
+
+       buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
+                               (s->data_block_quadlets << CIP_DBS_SHIFT) |
+                               s->data_block_counter);
+       buffer[1] = cpu_to_be32(CIP_EOH |
+                               ((s->fmt << CIP_FMT_SHIFT) & CIP_FMT_MASK) |
+                               ((s->fdf << CIP_FDF_SHIFT) & CIP_FDF_MASK) |
+                               (syt & CIP_SYT_MASK));
+
+       s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
+
+       payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
+       if (queue_out_packet(s, payload_length, false) < 0)
+               return -EIO;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm && pcm_frames > 0)
+               update_pcm_pointers(s, pcm, pcm_frames);
+
+       /* No need to return the number of handled data blocks. */
+       return 0;
+}
+
+static int handle_in_packet(struct amdtp_stream *s,
+                           unsigned int payload_quadlets, __be32 *buffer,
+                           unsigned int *data_blocks, unsigned int syt)
+{
+       u32 cip_header[2];
+       unsigned int fmt, fdf;
+       unsigned int data_block_quadlets, data_block_counter, dbc_interval;
+       struct snd_pcm_substream *pcm;
+       unsigned int pcm_frames;
+       bool lost;
+
+       cip_header[0] = be32_to_cpu(buffer[0]);
+       cip_header[1] = be32_to_cpu(buffer[1]);
+
+       /*
+        * This module supports 'Two-quadlet CIP header with SYT field'.
+        * For convenience, also check FMT field is AM824 or not.
+        */
+       if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
+           ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH)) {
+               dev_info_ratelimited(&s->unit->device,
+                               "Invalid CIP header for AMDTP: %08X:%08X\n",
+                               cip_header[0], cip_header[1]);
+               *data_blocks = 0;
+               pcm_frames = 0;
+               goto end;
+       }
+
+       /* Check valid protocol or not. */
+       fmt = (cip_header[1] & CIP_FMT_MASK) >> CIP_FMT_SHIFT;
+       if (fmt != s->fmt) {
+               dev_info_ratelimited(&s->unit->device,
+                                    "Detect unexpected protocol: %08x %08x\n",
+                                    cip_header[0], cip_header[1]);
+               *data_blocks = 0;
+               pcm_frames = 0;
+               goto end;
+       }
+
+       /* Calculate data blocks */
+       fdf = (cip_header[1] & CIP_FDF_MASK) >> CIP_FDF_SHIFT;
+       if (payload_quadlets < 3 ||
+           (fmt == CIP_FMT_AM && fdf == AMDTP_FDF_NO_DATA)) {
+               *data_blocks = 0;
+       } else {
+               data_block_quadlets =
+                       (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
+               /* avoid division by zero */
+               if (data_block_quadlets == 0) {
+                       dev_err(&s->unit->device,
+                               "Detect invalid value in dbs field: %08X\n",
+                               cip_header[0]);
+                       return -EPROTO;
+               }
+               if (s->flags & CIP_WRONG_DBS)
+                       data_block_quadlets = s->data_block_quadlets;
+
+               *data_blocks = (payload_quadlets - 2) / data_block_quadlets;
+       }
+
+       /* Check data block counter continuity */
+       data_block_counter = cip_header[0] & CIP_DBC_MASK;
+       if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
+           s->data_block_counter != UINT_MAX)
+               data_block_counter = s->data_block_counter;
+
+       if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) &&
+            data_block_counter == s->tx_first_dbc) ||
+           s->data_block_counter == UINT_MAX) {
+               lost = false;
+       } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
+               lost = data_block_counter != s->data_block_counter;
+       } else {
+               if ((*data_blocks > 0) && (s->tx_dbc_interval > 0))
+                       dbc_interval = s->tx_dbc_interval;
+               else
+                       dbc_interval = *data_blocks;
+
+               lost = data_block_counter !=
+                      ((s->data_block_counter + dbc_interval) & 0xff);
+       }
+
+       if (lost) {
+               dev_err(&s->unit->device,
+                       "Detect discontinuity of CIP: %02X %02X\n",
+                       s->data_block_counter, data_block_counter);
+               return -EIO;
+       }
+
+       pcm_frames = s->process_data_blocks(s, buffer + 2, *data_blocks, &syt);
+
+       if (s->flags & CIP_DBC_IS_END_EVENT)
+               s->data_block_counter = data_block_counter;
+       else
+               s->data_block_counter =
+                               (data_block_counter + *data_blocks) & 0xff;
+end:
+       if (queue_in_packet(s) < 0)
+               return -EIO;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm && pcm_frames > 0)
+               update_pcm_pointers(s, pcm, pcm_frames);
+
+       return 0;
+}
+
+static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
+                               size_t header_length, void *header,
+                               void *private_data)
+{
+       struct amdtp_stream *s = private_data;
+       unsigned int i, syt, packets = header_length / 4;
+       unsigned int data_blocks;
+
+       if (s->packet_index < 0)
+               return;
+
+       /*
+        * Compute the cycle of the last queued packet.
+        * (We need only the four lowest bits for the SYT, so we can ignore
+        * that bits 0-11 must wrap around at 3072.)
+        */
+       cycle += QUEUE_LENGTH - packets;
+
+       for (i = 0; i < packets; ++i) {
+               syt = calculate_syt(s, ++cycle);
+               data_blocks = calculate_data_blocks(s, syt);
+
+               if (handle_out_packet(s, data_blocks, syt) < 0) {
+                       s->packet_index = -1;
+                       amdtp_stream_pcm_abort(s);
+                       return;
+               }
+       }
+
+       fw_iso_context_queue_flush(s->context);
+}
+
+static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
+                              size_t header_length, void *header,
+                              void *private_data)
+{
+       struct amdtp_stream *s = private_data;
+       unsigned int p, syt, packets;
+       unsigned int payload_quadlets, max_payload_quadlets;
+       unsigned int data_blocks;
+       __be32 *buffer, *headers = header;
+
+       if (s->packet_index < 0)
+               return;
+
+       /* The number of packets in buffer */
+       packets = header_length / IN_PACKET_HEADER_SIZE;
+
+       /* For buffer-over-run prevention. */
+       max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
+
+       for (p = 0; p < packets; p++) {
+               buffer = s->buffer.packets[s->packet_index].buffer;
+
+               /* The number of quadlets in this packet */
+               payload_quadlets =
+                       (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
+               if (payload_quadlets > max_payload_quadlets) {
+                       dev_err(&s->unit->device,
+                               "Detect jumbo payload: %02x %02x\n",
+                               payload_quadlets, max_payload_quadlets);
+                       s->packet_index = -1;
+                       break;
+               }
+
+               syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
+               if (handle_in_packet(s, payload_quadlets, buffer,
+                                               &data_blocks, syt) < 0) {
+                       s->packet_index = -1;
+                       break;
+               }
+
+               /* Process sync slave stream */
+               if (s->sync_slave && s->sync_slave->callbacked) {
+                       if (handle_out_packet(s->sync_slave,
+                                             data_blocks, syt) < 0) {
+                               s->packet_index = -1;
+                               break;
+                       }
+               }
+       }
+
+       /* Queueing error or detecting discontinuity */
+       if (s->packet_index < 0) {
+               amdtp_stream_pcm_abort(s);
+
+               /* Abort sync slave. */
+               if (s->sync_slave) {
+                       s->sync_slave->packet_index = -1;
+                       amdtp_stream_pcm_abort(s->sync_slave);
+               }
+               return;
+       }
+
+       /* when sync to device, flush the packets for slave stream */
+       if (s->sync_slave && s->sync_slave->callbacked)
+               fw_iso_context_queue_flush(s->sync_slave->context);
+
+       fw_iso_context_queue_flush(s->context);
+}
+
+/* processing is done by master callback */
+static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
+                                 size_t header_length, void *header,
+                                 void *private_data)
+{
+       return;
+}
+
+/* this is executed one time */
+static void amdtp_stream_first_callback(struct fw_iso_context *context,
+                                       u32 cycle, size_t header_length,
+                                       void *header, void *private_data)
+{
+       struct amdtp_stream *s = private_data;
+
+       /*
+        * For in-stream, first packet has come.
+        * For out-stream, prepared to transmit first packet
+        */
+       s->callbacked = true;
+       wake_up(&s->callback_wait);
+
+       if (s->direction == AMDTP_IN_STREAM)
+               context->callback.sc = in_stream_callback;
+       else if (s->flags & CIP_SYNC_TO_DEVICE)
+               context->callback.sc = slave_stream_callback;
+       else
+               context->callback.sc = out_stream_callback;
+
+       context->callback.sc(context, cycle, header_length, header, s);
+}
+
+/**
+ * amdtp_stream_start - start transferring packets
+ * @s: the AMDTP stream to start
+ * @channel: the isochronous channel on the bus
+ * @speed: firewire speed code
+ *
+ * The stream cannot be started until it has been configured with
+ * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
+ * device can be started.
+ */
+int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
+{
+       static const struct {
+               unsigned int data_block;
+               unsigned int syt_offset;
+       } initial_state[] = {
+               [CIP_SFC_32000]  = {  4, 3072 },
+               [CIP_SFC_48000]  = {  6, 1024 },
+               [CIP_SFC_96000]  = { 12, 1024 },
+               [CIP_SFC_192000] = { 24, 1024 },
+               [CIP_SFC_44100]  = {  0,   67 },
+               [CIP_SFC_88200]  = {  0,   67 },
+               [CIP_SFC_176400] = {  0,   67 },
+       };
+       unsigned int header_size;
+       enum dma_data_direction dir;
+       int type, tag, err;
+
+       mutex_lock(&s->mutex);
+
+       if (WARN_ON(amdtp_stream_running(s) ||
+                   (s->data_block_quadlets < 1))) {
+               err = -EBADFD;
+               goto err_unlock;
+       }
+
+       if (s->direction == AMDTP_IN_STREAM &&
+           s->flags & CIP_SKIP_INIT_DBC_CHECK)
+               s->data_block_counter = UINT_MAX;
+       else
+               s->data_block_counter = 0;
+       s->data_block_state = initial_state[s->sfc].data_block;
+       s->syt_offset_state = initial_state[s->sfc].syt_offset;
+       s->last_syt_offset = TICKS_PER_CYCLE;
+
+       /* initialize packet buffer */
+       if (s->direction == AMDTP_IN_STREAM) {
+               dir = DMA_FROM_DEVICE;
+               type = FW_ISO_CONTEXT_RECEIVE;
+               header_size = IN_PACKET_HEADER_SIZE;
+       } else {
+               dir = DMA_TO_DEVICE;
+               type = FW_ISO_CONTEXT_TRANSMIT;
+               header_size = OUT_PACKET_HEADER_SIZE;
+       }
+       err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
+                                     amdtp_stream_get_max_payload(s), dir);
+       if (err < 0)
+               goto err_unlock;
+
+       s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
+                                          type, channel, speed, header_size,
+                                          amdtp_stream_first_callback, s);
+       if (IS_ERR(s->context)) {
+               err = PTR_ERR(s->context);
+               if (err == -EBUSY)
+                       dev_err(&s->unit->device,
+                               "no free stream on this controller\n");
+               goto err_buffer;
+       }
+
+       amdtp_stream_update(s);
+
+       s->packet_index = 0;
+       do {
+               if (s->direction == AMDTP_IN_STREAM)
+                       err = queue_in_packet(s);
+               else
+                       err = queue_out_packet(s, 0, true);
+               if (err < 0)
+                       goto err_context;
+       } while (s->packet_index > 0);
+
+       /* NOTE: TAG1 matches CIP. This just affects in stream. */
+       tag = FW_ISO_CONTEXT_MATCH_TAG1;
+       if (s->flags & CIP_EMPTY_WITH_TAG0)
+               tag |= FW_ISO_CONTEXT_MATCH_TAG0;
+
+       s->callbacked = false;
+       err = fw_iso_context_start(s->context, -1, 0, tag);
+       if (err < 0)
+               goto err_context;
+
+       mutex_unlock(&s->mutex);
+
+       return 0;
+
+err_context:
+       fw_iso_context_destroy(s->context);
+       s->context = ERR_PTR(-1);
+err_buffer:
+       iso_packets_buffer_destroy(&s->buffer, s->unit);
+err_unlock:
+       mutex_unlock(&s->mutex);
+
+       return err;
+}
+EXPORT_SYMBOL(amdtp_stream_start);
+
+/**
+ * amdtp_stream_pcm_pointer - get the PCM buffer position
+ * @s: the AMDTP stream that transports the PCM data
+ *
+ * Returns the current buffer position, in frames.
+ */
+unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
+{
+       /* this optimization is allowed to be racy */
+       if (s->pointer_flush && amdtp_stream_running(s))
+               fw_iso_context_flush_completions(s->context);
+       else
+               s->pointer_flush = true;
+
+       return ACCESS_ONCE(s->pcm_buffer_pointer);
+}
+EXPORT_SYMBOL(amdtp_stream_pcm_pointer);
+
+/**
+ * amdtp_stream_update - update the stream after a bus reset
+ * @s: the AMDTP stream
+ */
+void amdtp_stream_update(struct amdtp_stream *s)
+{
+       /* Precomputing. */
+       ACCESS_ONCE(s->source_node_id_field) =
+               (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) &
+                                                               CIP_SID_MASK;
+}
+EXPORT_SYMBOL(amdtp_stream_update);
+
+/**
+ * amdtp_stream_stop - stop sending packets
+ * @s: the AMDTP stream to stop
+ *
+ * All PCM and MIDI devices of the stream must be stopped before the stream
+ * itself can be stopped.
+ */
+void amdtp_stream_stop(struct amdtp_stream *s)
+{
+       mutex_lock(&s->mutex);
+
+       if (!amdtp_stream_running(s)) {
+               mutex_unlock(&s->mutex);
+               return;
+       }
+
+       tasklet_kill(&s->period_tasklet);
+       fw_iso_context_stop(s->context);
+       fw_iso_context_destroy(s->context);
+       s->context = ERR_PTR(-1);
+       iso_packets_buffer_destroy(&s->buffer, s->unit);
+
+       s->callbacked = false;
+
+       mutex_unlock(&s->mutex);
+}
+EXPORT_SYMBOL(amdtp_stream_stop);
+
+/**
+ * amdtp_stream_pcm_abort - abort the running PCM device
+ * @s: the AMDTP stream about to be stopped
+ *
+ * If the isochronous stream needs to be stopped asynchronously, call this
+ * function first to stop the PCM device.
+ */
+void amdtp_stream_pcm_abort(struct amdtp_stream *s)
+{
+       struct snd_pcm_substream *pcm;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm)
+               snd_pcm_stop_xrun(pcm);
+}
+EXPORT_SYMBOL(amdtp_stream_pcm_abort);
diff --git a/sound/firewire/amdtp-stream.h b/sound/firewire/amdtp-stream.h
new file mode 100644 (file)
index 0000000..8775704
--- /dev/null
@@ -0,0 +1,258 @@
+#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
+#define SOUND_FIREWIRE_AMDTP_H_INCLUDED
+
+#include <linux/err.h>
+#include <linux/interrupt.h>
+#include <linux/mutex.h>
+#include <linux/sched.h>
+#include <sound/asound.h>
+#include "packets-buffer.h"
+
+/**
+ * enum cip_flags - describes details of the streaming protocol
+ * @CIP_NONBLOCKING: In non-blocking mode, each packet contains
+ *     sample_rate/8000 samples, with rounding up or down to adjust
+ *     for clock skew and left-over fractional samples.  This should
+ *     be used if supported by the device.
+ * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
+ *     SYT_INTERVAL samples, with these two types alternating so that
+ *     the overall sample rate comes out right.
+ * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
+ *     generated by in packets. Defaultly this driver generates timestamp.
+ * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
+ * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
+ *     corresponds to the end of event in the packet. Out of IEC 61883.
+ * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets.
+ *     The value of data_block_quadlets is used instead of reported value.
+ * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream.  Packets with zero in dbc is
+ *     skipped for detecting discontinuity.
+ * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first
+ *     packet is not continuous from an initial value.
+ * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
+ *     packet is wrong but the others are correct.
+ * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
+ *     packet is larger than IEC 61883-6 defines. Current implementation
+ *     allows 5 times as large as IEC 61883-6 defines.
+ */
+enum cip_flags {
+       CIP_NONBLOCKING         = 0x00,
+       CIP_BLOCKING            = 0x01,
+       CIP_SYNC_TO_DEVICE      = 0x02,
+       CIP_EMPTY_WITH_TAG0     = 0x04,
+       CIP_DBC_IS_END_EVENT    = 0x08,
+       CIP_WRONG_DBS           = 0x10,
+       CIP_SKIP_DBC_ZERO_CHECK = 0x20,
+       CIP_SKIP_INIT_DBC_CHECK = 0x40,
+       CIP_EMPTY_HAS_WRONG_DBC = 0x80,
+       CIP_JUMBO_PAYLOAD       = 0x100,
+};
+
+/**
+ * enum cip_sfc - supported Sampling Frequency Codes (SFCs)
+ * @CIP_SFC_32000:   32,000 data blocks
+ * @CIP_SFC_44100:   44,100 data blocks
+ * @CIP_SFC_48000:   48,000 data blocks
+ * @CIP_SFC_88200:   88,200 data blocks
+ * @CIP_SFC_96000:   96,000 data blocks
+ * @CIP_SFC_176400: 176,400 data blocks
+ * @CIP_SFC_192000: 192,000 data blocks
+ * @CIP_SFC_COUNT: the number of supported SFCs
+ *
+ * These values are used to show nominal Sampling Frequency Code in
+ * Format Dependent Field (FDF) of AMDTP packet header. In IEC 61883-6:2002,
+ * this code means the number of events per second. Actually the code
+ * represents the number of data blocks transferred per second in an AMDTP
+ * stream.
+ *
+ * In IEC 61883-6:2005, some extensions were added to support more types of
+ * data such as 'One Bit LInear Audio', therefore the meaning of SFC became
+ * different depending on the types.
+ *
+ * Currently our implementation is compatible with IEC 61883-6:2002.
+ */
+enum cip_sfc {
+       CIP_SFC_32000  = 0,
+       CIP_SFC_44100  = 1,
+       CIP_SFC_48000  = 2,
+       CIP_SFC_88200  = 3,
+       CIP_SFC_96000  = 4,
+       CIP_SFC_176400 = 5,
+       CIP_SFC_192000 = 6,
+       CIP_SFC_COUNT
+};
+
+struct fw_unit;
+struct fw_iso_context;
+struct snd_pcm_substream;
+struct snd_pcm_runtime;
+
+enum amdtp_stream_direction {
+       AMDTP_OUT_STREAM = 0,
+       AMDTP_IN_STREAM
+};
+
+struct amdtp_stream;
+typedef unsigned int (*amdtp_stream_process_data_blocks_t)(
+                                               struct amdtp_stream *s,
+                                               __be32 *buffer,
+                                               unsigned int data_blocks,
+                                               unsigned int *syt);
+struct amdtp_stream {
+       struct fw_unit *unit;
+       enum cip_flags flags;
+       enum amdtp_stream_direction direction;
+       struct mutex mutex;
+
+       /* For packet processing. */
+       struct fw_iso_context *context;
+       struct iso_packets_buffer buffer;
+       int packet_index;
+
+       /* For CIP headers. */
+       unsigned int source_node_id_field;
+       unsigned int data_block_quadlets;
+       unsigned int data_block_counter;
+       unsigned int fmt;
+       unsigned int fdf;
+       /* quirk: fixed interval of dbc between previos/current packets. */
+       unsigned int tx_dbc_interval;
+       /* quirk: indicate the value of dbc field in a first packet. */
+       unsigned int tx_first_dbc;
+
+       /* Internal flags. */
+       enum cip_sfc sfc;
+       unsigned int syt_interval;
+       unsigned int transfer_delay;
+       unsigned int data_block_state;
+       unsigned int last_syt_offset;
+       unsigned int syt_offset_state;
+
+       /* For a PCM substream processing. */
+       struct snd_pcm_substream *pcm;
+       struct tasklet_struct period_tasklet;
+       unsigned int pcm_buffer_pointer;
+       unsigned int pcm_period_pointer;
+       bool pointer_flush;
+
+       /* To wait for first packet. */
+       bool callbacked;
+       wait_queue_head_t callback_wait;
+       struct amdtp_stream *sync_slave;
+
+       /* For backends to process data blocks. */
+       void *protocol;
+       amdtp_stream_process_data_blocks_t process_data_blocks;
+};
+
+int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
+                     enum amdtp_stream_direction dir, enum cip_flags flags,
+                     unsigned int fmt,
+                     amdtp_stream_process_data_blocks_t process_data_blocks,
+                     unsigned int protocol_size);
+void amdtp_stream_destroy(struct amdtp_stream *s);
+
+int amdtp_stream_set_parameters(struct amdtp_stream *s, unsigned int rate,
+                               unsigned int data_block_quadlets);
+unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
+
+int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
+void amdtp_stream_update(struct amdtp_stream *s);
+void amdtp_stream_stop(struct amdtp_stream *s);
+
+int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                       struct snd_pcm_runtime *runtime);
+
+void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
+unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
+void amdtp_stream_pcm_abort(struct amdtp_stream *s);
+
+extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
+extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT];
+
+/**
+ * amdtp_stream_running - check stream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, the stream is running.
+ */
+static inline bool amdtp_stream_running(struct amdtp_stream *s)
+{
+       return !IS_ERR(s->context);
+}
+
+/**
+ * amdtp_streaming_error - check for streaming error
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, the stream's packet queue has stopped due to
+ * an asynchronous error.
+ */
+static inline bool amdtp_streaming_error(struct amdtp_stream *s)
+{
+       return s->packet_index < 0;
+}
+
+/**
+ * amdtp_stream_pcm_running - check PCM substream is running or not
+ * @s: the AMDTP stream
+ *
+ * If this function returns true, PCM substream in the AMDTP stream is running.
+ */
+static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s)
+{
+       return !!s->pcm;
+}
+
+/**
+ * amdtp_stream_pcm_trigger - start/stop playback from a PCM device
+ * @s: the AMDTP stream
+ * @pcm: the PCM device to be started, or %NULL to stop the current device
+ *
+ * Call this function on a running isochronous stream to enable the actual
+ * transmission of PCM data.  This function should be called from the PCM
+ * device's .trigger callback.
+ */
+static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
+                                           struct snd_pcm_substream *pcm)
+{
+       ACCESS_ONCE(s->pcm) = pcm;
+}
+
+static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
+{
+       return sfc & 1;
+}
+
+static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
+                                        struct amdtp_stream *master,
+                                        struct amdtp_stream *slave)
+{
+       if (sync_mode == CIP_SYNC_TO_DEVICE) {
+               master->flags |= CIP_SYNC_TO_DEVICE;
+               slave->flags |= CIP_SYNC_TO_DEVICE;
+               master->sync_slave = slave;
+       } else {
+               master->flags &= ~CIP_SYNC_TO_DEVICE;
+               slave->flags &= ~CIP_SYNC_TO_DEVICE;
+               master->sync_slave = NULL;
+       }
+
+       slave->sync_slave = NULL;
+}
+
+/**
+ * amdtp_stream_wait_callback - sleep till callbacked or timeout
+ * @s: the AMDTP stream
+ * @timeout: msec till timeout
+ *
+ * If this function return false, the AMDTP stream should be stopped.
+ */
+static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
+                                             unsigned int timeout)
+{
+       return wait_event_timeout(s->callback_wait,
+                                 s->callbacked == true,
+                                 msecs_to_jiffies(timeout)) > 0;
+}
+
+#endif
diff --git a/sound/firewire/amdtp.c b/sound/firewire/amdtp.c
deleted file mode 100644 (file)
index 2a153d2..0000000
+++ /dev/null
@@ -1,1108 +0,0 @@
-/*
- * Audio and Music Data Transmission Protocol (IEC 61883-6) streams
- * with Common Isochronous Packet (IEC 61883-1) headers
- *
- * Copyright (c) Clemens Ladisch <clemens@ladisch.de>
- * Licensed under the terms of the GNU General Public License, version 2.
- */
-
-#include <linux/device.h>
-#include <linux/err.h>
-#include <linux/firewire.h>
-#include <linux/module.h>
-#include <linux/slab.h>
-#include <linux/sched.h>
-#include <sound/pcm.h>
-#include <sound/pcm_params.h>
-#include <sound/rawmidi.h>
-#include "amdtp.h"
-
-#define TICKS_PER_CYCLE                3072
-#define CYCLES_PER_SECOND      8000
-#define TICKS_PER_SECOND       (TICKS_PER_CYCLE * CYCLES_PER_SECOND)
-
-/*
- * Nominally 3125 bytes/second, but the MIDI port's clock might be
- * 1% too slow, and the bus clock 100 ppm too fast.
- */
-#define MIDI_BYTES_PER_SECOND  3093
-
-/*
- * Several devices look only at the first eight data blocks.
- * In any case, this is more than enough for the MIDI data rate.
- */
-#define MAX_MIDI_RX_BLOCKS     8
-
-#define TRANSFER_DELAY_TICKS   0x2e00 /* 479.17 microseconds */
-
-/* isochronous header parameters */
-#define ISO_DATA_LENGTH_SHIFT  16
-#define TAG_CIP                        1
-
-/* common isochronous packet header parameters */
-#define CIP_EOH_SHIFT          31
-#define CIP_EOH                        (1u << CIP_EOH_SHIFT)
-#define CIP_EOH_MASK           0x80000000
-#define CIP_SID_SHIFT          24
-#define CIP_SID_MASK           0x3f000000
-#define CIP_DBS_MASK           0x00ff0000
-#define CIP_DBS_SHIFT          16
-#define CIP_DBC_MASK           0x000000ff
-#define CIP_FMT_SHIFT          24
-#define CIP_FMT_MASK           0x3f000000
-#define CIP_FDF_MASK           0x00ff0000
-#define CIP_FDF_SHIFT          16
-#define CIP_SYT_MASK           0x0000ffff
-#define CIP_SYT_NO_INFO                0xffff
-
-/*
- * Audio and Music transfer protocol specific parameters
- * only "Clock-based rate control mode" is supported
- */
-#define CIP_FMT_AM             (0x10 << CIP_FMT_SHIFT)
-#define AMDTP_FDF_AM824                (0 << (CIP_FDF_SHIFT + 3))
-#define AMDTP_FDF_NO_DATA      0xff
-
-/* TODO: make these configurable */
-#define INTERRUPT_INTERVAL     16
-#define QUEUE_LENGTH           48
-
-#define IN_PACKET_HEADER_SIZE  4
-#define OUT_PACKET_HEADER_SIZE 0
-
-static void pcm_period_tasklet(unsigned long data);
-
-/**
- * amdtp_stream_init - initialize an AMDTP stream structure
- * @s: the AMDTP stream to initialize
- * @unit: the target of the stream
- * @dir: the direction of stream
- * @flags: the packet transmission method to use
- */
-int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
-                     enum amdtp_stream_direction dir, enum cip_flags flags)
-{
-       s->unit = unit;
-       s->direction = dir;
-       s->flags = flags;
-       s->context = ERR_PTR(-1);
-       mutex_init(&s->mutex);
-       tasklet_init(&s->period_tasklet, pcm_period_tasklet, (unsigned long)s);
-       s->packet_index = 0;
-
-       init_waitqueue_head(&s->callback_wait);
-       s->callbacked = false;
-       s->sync_slave = NULL;
-
-       return 0;
-}
-EXPORT_SYMBOL(amdtp_stream_init);
-
-/**
- * amdtp_stream_destroy - free stream resources
- * @s: the AMDTP stream to destroy
- */
-void amdtp_stream_destroy(struct amdtp_stream *s)
-{
-       WARN_ON(amdtp_stream_running(s));
-       mutex_destroy(&s->mutex);
-}
-EXPORT_SYMBOL(amdtp_stream_destroy);
-
-const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT] = {
-       [CIP_SFC_32000]  =  8,
-       [CIP_SFC_44100]  =  8,
-       [CIP_SFC_48000]  =  8,
-       [CIP_SFC_88200]  = 16,
-       [CIP_SFC_96000]  = 16,
-       [CIP_SFC_176400] = 32,
-       [CIP_SFC_192000] = 32,
-};
-EXPORT_SYMBOL(amdtp_syt_intervals);
-
-const unsigned int amdtp_rate_table[CIP_SFC_COUNT] = {
-       [CIP_SFC_32000]  =  32000,
-       [CIP_SFC_44100]  =  44100,
-       [CIP_SFC_48000]  =  48000,
-       [CIP_SFC_88200]  =  88200,
-       [CIP_SFC_96000]  =  96000,
-       [CIP_SFC_176400] = 176400,
-       [CIP_SFC_192000] = 192000,
-};
-EXPORT_SYMBOL(amdtp_rate_table);
-
-/**
- * amdtp_stream_add_pcm_hw_constraints - add hw constraints for PCM substream
- * @s:         the AMDTP stream, which must be initialized.
- * @runtime:   the PCM substream runtime
- */
-int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
-                                       struct snd_pcm_runtime *runtime)
-{
-       int err;
-
-       /* AM824 in IEC 61883-6 can deliver 24bit data */
-       err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
-       if (err < 0)
-               goto end;
-
-       /*
-        * Currently firewire-lib processes 16 packets in one software
-        * interrupt callback. This equals to 2msec but actually the
-        * interval of the interrupts has a jitter.
-        * Additionally, even if adding a constraint to fit period size to
-        * 2msec, actual calculated frames per period doesn't equal to 2msec,
-        * depending on sampling rate.
-        * Anyway, the interval to call snd_pcm_period_elapsed() cannot 2msec.
-        * Here let us use 5msec for safe period interrupt.
-        */
-       err = snd_pcm_hw_constraint_minmax(runtime,
-                                          SNDRV_PCM_HW_PARAM_PERIOD_TIME,
-                                          5000, UINT_MAX);
-       if (err < 0)
-               goto end;
-
-       /* Non-Blocking stream has no more constraints */
-       if (!(s->flags & CIP_BLOCKING))
-               goto end;
-
-       /*
-        * One AMDTP packet can include some frames. In blocking mode, the
-        * number equals to SYT_INTERVAL. So the number is 8, 16 or 32,
-        * depending on its sampling rate. For accurate period interrupt, it's
-        * preferrable to align period/buffer sizes to current SYT_INTERVAL.
-        *
-        * TODO: These constraints can be improved with proper rules.
-        * Currently apply LCM of SYT_INTERVALs.
-        */
-       err = snd_pcm_hw_constraint_step(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_PERIOD_SIZE, 32);
-       if (err < 0)
-               goto end;
-       err = snd_pcm_hw_constraint_step(runtime, 0,
-                                        SNDRV_PCM_HW_PARAM_BUFFER_SIZE, 32);
-end:
-       return err;
-}
-EXPORT_SYMBOL(amdtp_stream_add_pcm_hw_constraints);
-
-/**
- * amdtp_stream_set_parameters - set stream parameters
- * @s: the AMDTP stream to configure
- * @rate: the sample rate
- * @pcm_channels: the number of PCM samples in each data block, to be encoded
- *                as AM824 multi-bit linear audio
- * @midi_ports: the number of MIDI ports (i.e., MPX-MIDI Data Channels)
- *
- * The parameters must be set before the stream is started, and must not be
- * changed while the stream is running.
- */
-void amdtp_stream_set_parameters(struct amdtp_stream *s,
-                                unsigned int rate,
-                                unsigned int pcm_channels,
-                                unsigned int midi_ports)
-{
-       unsigned int i, sfc, midi_channels;
-
-       midi_channels = DIV_ROUND_UP(midi_ports, 8);
-
-       if (WARN_ON(amdtp_stream_running(s)) |
-           WARN_ON(pcm_channels > AMDTP_MAX_CHANNELS_FOR_PCM) |
-           WARN_ON(midi_channels > AMDTP_MAX_CHANNELS_FOR_MIDI))
-               return;
-
-       for (sfc = 0; sfc < ARRAY_SIZE(amdtp_rate_table); ++sfc)
-               if (amdtp_rate_table[sfc] == rate)
-                       goto sfc_found;
-       WARN_ON(1);
-       return;
-
-sfc_found:
-       s->pcm_channels = pcm_channels;
-       s->sfc = sfc;
-       s->data_block_quadlets = s->pcm_channels + midi_channels;
-       s->midi_ports = midi_ports;
-
-       s->syt_interval = amdtp_syt_intervals[sfc];
-
-       /* default buffering in the device */
-       s->transfer_delay = TRANSFER_DELAY_TICKS - TICKS_PER_CYCLE;
-       if (s->flags & CIP_BLOCKING)
-               /* additional buffering needed to adjust for no-data packets */
-               s->transfer_delay += TICKS_PER_SECOND * s->syt_interval / rate;
-
-       /* init the position map for PCM and MIDI channels */
-       for (i = 0; i < pcm_channels; i++)
-               s->pcm_positions[i] = i;
-       s->midi_position = s->pcm_channels;
-
-       /*
-        * We do not know the actual MIDI FIFO size of most devices.  Just
-        * assume two bytes, i.e., one byte can be received over the bus while
-        * the previous one is transmitted over MIDI.
-        * (The value here is adjusted for midi_ratelimit_per_packet().)
-        */
-       s->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
-}
-EXPORT_SYMBOL(amdtp_stream_set_parameters);
-
-/**
- * amdtp_stream_get_max_payload - get the stream's packet size
- * @s: the AMDTP stream
- *
- * This function must not be called before the stream has been configured
- * with amdtp_stream_set_parameters().
- */
-unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s)
-{
-       unsigned int multiplier = 1;
-
-       if (s->flags & CIP_JUMBO_PAYLOAD)
-               multiplier = 5;
-
-       return 8 + s->syt_interval * s->data_block_quadlets * 4 * multiplier;
-}
-EXPORT_SYMBOL(amdtp_stream_get_max_payload);
-
-static void write_pcm_s16(struct amdtp_stream *s,
-                         struct snd_pcm_substream *pcm,
-                         __be32 *buffer, unsigned int frames);
-static void write_pcm_s32(struct amdtp_stream *s,
-                         struct snd_pcm_substream *pcm,
-                         __be32 *buffer, unsigned int frames);
-static void read_pcm_s32(struct amdtp_stream *s,
-                        struct snd_pcm_substream *pcm,
-                        __be32 *buffer, unsigned int frames);
-
-/**
- * amdtp_stream_set_pcm_format - set the PCM format
- * @s: the AMDTP stream to configure
- * @format: the format of the ALSA PCM device
- *
- * The sample format must be set after the other parameters (rate/PCM channels/
- * MIDI) and before the stream is started, and must not be changed while the
- * stream is running.
- */
-void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
-                                snd_pcm_format_t format)
-{
-       if (WARN_ON(amdtp_stream_pcm_running(s)))
-               return;
-
-       switch (format) {
-       default:
-               WARN_ON(1);
-               /* fall through */
-       case SNDRV_PCM_FORMAT_S16:
-               if (s->direction == AMDTP_OUT_STREAM) {
-                       s->transfer_samples = write_pcm_s16;
-                       break;
-               }
-               WARN_ON(1);
-               /* fall through */
-       case SNDRV_PCM_FORMAT_S32:
-               if (s->direction == AMDTP_OUT_STREAM)
-                       s->transfer_samples = write_pcm_s32;
-               else
-                       s->transfer_samples = read_pcm_s32;
-               break;
-       }
-}
-EXPORT_SYMBOL(amdtp_stream_set_pcm_format);
-
-/**
- * amdtp_stream_pcm_prepare - prepare PCM device for running
- * @s: the AMDTP stream
- *
- * This function should be called from the PCM device's .prepare callback.
- */
-void amdtp_stream_pcm_prepare(struct amdtp_stream *s)
-{
-       tasklet_kill(&s->period_tasklet);
-       s->pcm_buffer_pointer = 0;
-       s->pcm_period_pointer = 0;
-       s->pointer_flush = true;
-}
-EXPORT_SYMBOL(amdtp_stream_pcm_prepare);
-
-static unsigned int calculate_data_blocks(struct amdtp_stream *s,
-                                         unsigned int syt)
-{
-       unsigned int phase, data_blocks;
-
-       /* Blocking mode. */
-       if (s->flags & CIP_BLOCKING) {
-               /* This module generate empty packet for 'no data'. */
-               if (syt == CIP_SYT_NO_INFO)
-                       data_blocks = 0;
-               else
-                       data_blocks = s->syt_interval;
-       /* Non-blocking mode. */
-       } else {
-               if (!cip_sfc_is_base_44100(s->sfc)) {
-                       /* Sample_rate / 8000 is an integer, and precomputed. */
-                       data_blocks = s->data_block_state;
-               } else {
-                       phase = s->data_block_state;
-
-               /*
-                * This calculates the number of data blocks per packet so that
-                * 1) the overall rate is correct and exactly synchronized to
-                *    the bus clock, and
-                * 2) packets with a rounded-up number of blocks occur as early
-                *    as possible in the sequence (to prevent underruns of the
-                *    device's buffer).
-                */
-                       if (s->sfc == CIP_SFC_44100)
-                               /* 6 6 5 6 5 6 5 ... */
-                               data_blocks = 5 + ((phase & 1) ^
-                                                  (phase == 0 || phase >= 40));
-                       else
-                               /* 12 11 11 11 11 ... or 23 22 22 22 22 ... */
-                               data_blocks = 11 * (s->sfc >> 1) + (phase == 0);
-                       if (++phase >= (80 >> (s->sfc >> 1)))
-                               phase = 0;
-                       s->data_block_state = phase;
-               }
-       }
-
-       return data_blocks;
-}
-
-static unsigned int calculate_syt(struct amdtp_stream *s,
-                                 unsigned int cycle)
-{
-       unsigned int syt_offset, phase, index, syt;
-
-       if (s->last_syt_offset < TICKS_PER_CYCLE) {
-               if (!cip_sfc_is_base_44100(s->sfc))
-                       syt_offset = s->last_syt_offset + s->syt_offset_state;
-               else {
-               /*
-                * The time, in ticks, of the n'th SYT_INTERVAL sample is:
-                *   n * SYT_INTERVAL * 24576000 / sample_rate
-                * Modulo TICKS_PER_CYCLE, the difference between successive
-                * elements is about 1386.23.  Rounding the results of this
-                * formula to the SYT precision results in a sequence of
-                * differences that begins with:
-                *   1386 1386 1387 1386 1386 1386 1387 1386 1386 1386 1387 ...
-                * This code generates _exactly_ the same sequence.
-                */
-                       phase = s->syt_offset_state;
-                       index = phase % 13;
-                       syt_offset = s->last_syt_offset;
-                       syt_offset += 1386 + ((index && !(index & 3)) ||
-                                             phase == 146);
-                       if (++phase >= 147)
-                               phase = 0;
-                       s->syt_offset_state = phase;
-               }
-       } else
-               syt_offset = s->last_syt_offset - TICKS_PER_CYCLE;
-       s->last_syt_offset = syt_offset;
-
-       if (syt_offset < TICKS_PER_CYCLE) {
-               syt_offset += s->transfer_delay;
-               syt = (cycle + syt_offset / TICKS_PER_CYCLE) << 12;
-               syt += syt_offset % TICKS_PER_CYCLE;
-
-               return syt & CIP_SYT_MASK;
-       } else {
-               return CIP_SYT_NO_INFO;
-       }
-}
-
-static void write_pcm_s32(struct amdtp_stream *s,
-                         struct snd_pcm_substream *pcm,
-                         __be32 *buffer, unsigned int frames)
-{
-       struct snd_pcm_runtime *runtime = pcm->runtime;
-       unsigned int channels, remaining_frames, i, c;
-       const u32 *src;
-
-       channels = s->pcm_channels;
-       src = (void *)runtime->dma_area +
-                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
-       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
-       for (i = 0; i < frames; ++i) {
-               for (c = 0; c < channels; ++c) {
-                       buffer[s->pcm_positions[c]] =
-                                       cpu_to_be32((*src >> 8) | 0x40000000);
-                       src++;
-               }
-               buffer += s->data_block_quadlets;
-               if (--remaining_frames == 0)
-                       src = (void *)runtime->dma_area;
-       }
-}
-
-static void write_pcm_s16(struct amdtp_stream *s,
-                         struct snd_pcm_substream *pcm,
-                         __be32 *buffer, unsigned int frames)
-{
-       struct snd_pcm_runtime *runtime = pcm->runtime;
-       unsigned int channels, remaining_frames, i, c;
-       const u16 *src;
-
-       channels = s->pcm_channels;
-       src = (void *)runtime->dma_area +
-                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
-       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
-       for (i = 0; i < frames; ++i) {
-               for (c = 0; c < channels; ++c) {
-                       buffer[s->pcm_positions[c]] =
-                                       cpu_to_be32((*src << 8) | 0x42000000);
-                       src++;
-               }
-               buffer += s->data_block_quadlets;
-               if (--remaining_frames == 0)
-                       src = (void *)runtime->dma_area;
-       }
-}
-
-static void read_pcm_s32(struct amdtp_stream *s,
-                        struct snd_pcm_substream *pcm,
-                        __be32 *buffer, unsigned int frames)
-{
-       struct snd_pcm_runtime *runtime = pcm->runtime;
-       unsigned int channels, remaining_frames, i, c;
-       u32 *dst;
-
-       channels = s->pcm_channels;
-       dst  = (void *)runtime->dma_area +
-                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
-       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
-
-       for (i = 0; i < frames; ++i) {
-               for (c = 0; c < channels; ++c) {
-                       *dst = be32_to_cpu(buffer[s->pcm_positions[c]]) << 8;
-                       dst++;
-               }
-               buffer += s->data_block_quadlets;
-               if (--remaining_frames == 0)
-                       dst = (void *)runtime->dma_area;
-       }
-}
-
-static void write_pcm_silence(struct amdtp_stream *s,
-                             __be32 *buffer, unsigned int frames)
-{
-       unsigned int i, c;
-
-       for (i = 0; i < frames; ++i) {
-               for (c = 0; c < s->pcm_channels; ++c)
-                       buffer[s->pcm_positions[c]] = cpu_to_be32(0x40000000);
-               buffer += s->data_block_quadlets;
-       }
-}
-
-/*
- * To avoid sending MIDI bytes at too high a rate, assume that the receiving
- * device has a FIFO, and track how much it is filled.  This values increases
- * by one whenever we send one byte in a packet, but the FIFO empties at
- * a constant rate independent of our packet rate.  One packet has syt_interval
- * samples, so the number of bytes that empty out of the FIFO, per packet(!),
- * is MIDI_BYTES_PER_SECOND * syt_interval / sample_rate.  To avoid storing
- * fractional values, the values in midi_fifo_used[] are measured in bytes
- * multiplied by the sample rate.
- */
-static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
-{
-       int used;
-
-       used = s->midi_fifo_used[port];
-       if (used == 0) /* common shortcut */
-               return true;
-
-       used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
-       used = max(used, 0);
-       s->midi_fifo_used[port] = used;
-
-       return used < s->midi_fifo_limit;
-}
-
-static void midi_rate_use_one_byte(struct amdtp_stream *s, unsigned int port)
-{
-       s->midi_fifo_used[port] += amdtp_rate_table[s->sfc];
-}
-
-static void write_midi_messages(struct amdtp_stream *s,
-                               __be32 *buffer, unsigned int frames)
-{
-       unsigned int f, port;
-       u8 *b;
-
-       for (f = 0; f < frames; f++) {
-               b = (u8 *)&buffer[s->midi_position];
-
-               port = (s->data_block_counter + f) % 8;
-               if (f < MAX_MIDI_RX_BLOCKS &&
-                   midi_ratelimit_per_packet(s, port) &&
-                   s->midi[port] != NULL &&
-                   snd_rawmidi_transmit(s->midi[port], &b[1], 1) == 1) {
-                       midi_rate_use_one_byte(s, port);
-                       b[0] = 0x81;
-               } else {
-                       b[0] = 0x80;
-                       b[1] = 0;
-               }
-               b[2] = 0;
-               b[3] = 0;
-
-               buffer += s->data_block_quadlets;
-       }
-}
-
-static void read_midi_messages(struct amdtp_stream *s,
-                              __be32 *buffer, unsigned int frames)
-{
-       unsigned int f, port;
-       int len;
-       u8 *b;
-
-       for (f = 0; f < frames; f++) {
-               port = (s->data_block_counter + f) % 8;
-               b = (u8 *)&buffer[s->midi_position];
-
-               len = b[0] - 0x80;
-               if ((1 <= len) &&  (len <= 3) && (s->midi[port]))
-                       snd_rawmidi_receive(s->midi[port], b + 1, len);
-
-               buffer += s->data_block_quadlets;
-       }
-}
-
-static void update_pcm_pointers(struct amdtp_stream *s,
-                               struct snd_pcm_substream *pcm,
-                               unsigned int frames)
-{
-       unsigned int ptr;
-
-       /*
-        * In IEC 61883-6, one data block represents one event. In ALSA, one
-        * event equals to one PCM frame. But Dice has a quirk to transfer
-        * two PCM frames in one data block.
-        */
-       if (s->double_pcm_frames)
-               frames *= 2;
-
-       ptr = s->pcm_buffer_pointer + frames;
-       if (ptr >= pcm->runtime->buffer_size)
-               ptr -= pcm->runtime->buffer_size;
-       ACCESS_ONCE(s->pcm_buffer_pointer) = ptr;
-
-       s->pcm_period_pointer += frames;
-       if (s->pcm_period_pointer >= pcm->runtime->period_size) {
-               s->pcm_period_pointer -= pcm->runtime->period_size;
-               s->pointer_flush = false;
-               tasklet_hi_schedule(&s->period_tasklet);
-       }
-}
-
-static void pcm_period_tasklet(unsigned long data)
-{
-       struct amdtp_stream *s = (void *)data;
-       struct snd_pcm_substream *pcm = ACCESS_ONCE(s->pcm);
-
-       if (pcm)
-               snd_pcm_period_elapsed(pcm);
-}
-
-static int queue_packet(struct amdtp_stream *s,
-                       unsigned int header_length,
-                       unsigned int payload_length, bool skip)
-{
-       struct fw_iso_packet p = {0};
-       int err = 0;
-
-       if (IS_ERR(s->context))
-               goto end;
-
-       p.interrupt = IS_ALIGNED(s->packet_index + 1, INTERRUPT_INTERVAL);
-       p.tag = TAG_CIP;
-       p.header_length = header_length;
-       p.payload_length = (!skip) ? payload_length : 0;
-       p.skip = skip;
-       err = fw_iso_context_queue(s->context, &p, &s->buffer.iso_buffer,
-                                  s->buffer.packets[s->packet_index].offset);
-       if (err < 0) {
-               dev_err(&s->unit->device, "queueing error: %d\n", err);
-               goto end;
-       }
-
-       if (++s->packet_index >= QUEUE_LENGTH)
-               s->packet_index = 0;
-end:
-       return err;
-}
-
-static inline int queue_out_packet(struct amdtp_stream *s,
-                                  unsigned int payload_length, bool skip)
-{
-       return queue_packet(s, OUT_PACKET_HEADER_SIZE,
-                           payload_length, skip);
-}
-
-static inline int queue_in_packet(struct amdtp_stream *s)
-{
-       return queue_packet(s, IN_PACKET_HEADER_SIZE,
-                           amdtp_stream_get_max_payload(s), false);
-}
-
-static int handle_out_packet(struct amdtp_stream *s, unsigned int data_blocks,
-                            unsigned int syt)
-{
-       __be32 *buffer;
-       unsigned int payload_length;
-       struct snd_pcm_substream *pcm;
-
-       buffer = s->buffer.packets[s->packet_index].buffer;
-       buffer[0] = cpu_to_be32(ACCESS_ONCE(s->source_node_id_field) |
-                               (s->data_block_quadlets << CIP_DBS_SHIFT) |
-                               s->data_block_counter);
-       buffer[1] = cpu_to_be32(CIP_EOH | CIP_FMT_AM | AMDTP_FDF_AM824 |
-                               (s->sfc << CIP_FDF_SHIFT) | syt);
-       buffer += 2;
-
-       pcm = ACCESS_ONCE(s->pcm);
-       if (pcm)
-               s->transfer_samples(s, pcm, buffer, data_blocks);
-       else
-               write_pcm_silence(s, buffer, data_blocks);
-       if (s->midi_ports)
-               write_midi_messages(s, buffer, data_blocks);
-
-       s->data_block_counter = (s->data_block_counter + data_blocks) & 0xff;
-
-       payload_length = 8 + data_blocks * 4 * s->data_block_quadlets;
-       if (queue_out_packet(s, payload_length, false) < 0)
-               return -EIO;
-
-       if (pcm)
-               update_pcm_pointers(s, pcm, data_blocks);
-
-       /* No need to return the number of handled data blocks. */
-       return 0;
-}
-
-static int handle_in_packet(struct amdtp_stream *s,
-                           unsigned int payload_quadlets, __be32 *buffer,
-                           unsigned int *data_blocks)
-{
-       u32 cip_header[2];
-       unsigned int data_block_quadlets, data_block_counter, dbc_interval;
-       struct snd_pcm_substream *pcm = NULL;
-       bool lost;
-
-       cip_header[0] = be32_to_cpu(buffer[0]);
-       cip_header[1] = be32_to_cpu(buffer[1]);
-
-       /*
-        * This module supports 'Two-quadlet CIP header with SYT field'.
-        * For convenience, also check FMT field is AM824 or not.
-        */
-       if (((cip_header[0] & CIP_EOH_MASK) == CIP_EOH) ||
-           ((cip_header[1] & CIP_EOH_MASK) != CIP_EOH) ||
-           ((cip_header[1] & CIP_FMT_MASK) != CIP_FMT_AM)) {
-               dev_info_ratelimited(&s->unit->device,
-                               "Invalid CIP header for AMDTP: %08X:%08X\n",
-                               cip_header[0], cip_header[1]);
-               *data_blocks = 0;
-               goto end;
-       }
-
-       /* Calculate data blocks */
-       if (payload_quadlets < 3 ||
-           ((cip_header[1] & CIP_FDF_MASK) ==
-                               (AMDTP_FDF_NO_DATA << CIP_FDF_SHIFT))) {
-               *data_blocks = 0;
-       } else {
-               data_block_quadlets =
-                       (cip_header[0] & CIP_DBS_MASK) >> CIP_DBS_SHIFT;
-               /* avoid division by zero */
-               if (data_block_quadlets == 0) {
-                       dev_err(&s->unit->device,
-                               "Detect invalid value in dbs field: %08X\n",
-                               cip_header[0]);
-                       return -EPROTO;
-               }
-               if (s->flags & CIP_WRONG_DBS)
-                       data_block_quadlets = s->data_block_quadlets;
-
-               *data_blocks = (payload_quadlets - 2) / data_block_quadlets;
-       }
-
-       /* Check data block counter continuity */
-       data_block_counter = cip_header[0] & CIP_DBC_MASK;
-       if (*data_blocks == 0 && (s->flags & CIP_EMPTY_HAS_WRONG_DBC) &&
-           s->data_block_counter != UINT_MAX)
-               data_block_counter = s->data_block_counter;
-
-       if (((s->flags & CIP_SKIP_DBC_ZERO_CHECK) &&
-            data_block_counter == s->tx_first_dbc) ||
-           s->data_block_counter == UINT_MAX) {
-               lost = false;
-       } else if (!(s->flags & CIP_DBC_IS_END_EVENT)) {
-               lost = data_block_counter != s->data_block_counter;
-       } else {
-               if ((*data_blocks > 0) && (s->tx_dbc_interval > 0))
-                       dbc_interval = s->tx_dbc_interval;
-               else
-                       dbc_interval = *data_blocks;
-
-               lost = data_block_counter !=
-                      ((s->data_block_counter + dbc_interval) & 0xff);
-       }
-
-       if (lost) {
-               dev_err(&s->unit->device,
-                       "Detect discontinuity of CIP: %02X %02X\n",
-                       s->data_block_counter, data_block_counter);
-               return -EIO;
-       }
-
-       if (*data_blocks > 0) {
-               buffer += 2;
-
-               pcm = ACCESS_ONCE(s->pcm);
-               if (pcm)
-                       s->transfer_samples(s, pcm, buffer, *data_blocks);
-
-               if (s->midi_ports)
-                       read_midi_messages(s, buffer, *data_blocks);
-       }
-
-       if (s->flags & CIP_DBC_IS_END_EVENT)
-               s->data_block_counter = data_block_counter;
-       else
-               s->data_block_counter =
-                               (data_block_counter + *data_blocks) & 0xff;
-end:
-       if (queue_in_packet(s) < 0)
-               return -EIO;
-
-       if (pcm)
-               update_pcm_pointers(s, pcm, *data_blocks);
-
-       return 0;
-}
-
-static void out_stream_callback(struct fw_iso_context *context, u32 cycle,
-                               size_t header_length, void *header,
-                               void *private_data)
-{
-       struct amdtp_stream *s = private_data;
-       unsigned int i, syt, packets = header_length / 4;
-       unsigned int data_blocks;
-
-       if (s->packet_index < 0)
-               return;
-
-       /*
-        * Compute the cycle of the last queued packet.
-        * (We need only the four lowest bits for the SYT, so we can ignore
-        * that bits 0-11 must wrap around at 3072.)
-        */
-       cycle += QUEUE_LENGTH - packets;
-
-       for (i = 0; i < packets; ++i) {
-               syt = calculate_syt(s, ++cycle);
-               data_blocks = calculate_data_blocks(s, syt);
-
-               if (handle_out_packet(s, data_blocks, syt) < 0) {
-                       s->packet_index = -1;
-                       amdtp_stream_pcm_abort(s);
-                       return;
-               }
-       }
-
-       fw_iso_context_queue_flush(s->context);
-}
-
-static void in_stream_callback(struct fw_iso_context *context, u32 cycle,
-                              size_t header_length, void *header,
-                              void *private_data)
-{
-       struct amdtp_stream *s = private_data;
-       unsigned int p, syt, packets;
-       unsigned int payload_quadlets, max_payload_quadlets;
-       unsigned int data_blocks;
-       __be32 *buffer, *headers = header;
-
-       if (s->packet_index < 0)
-               return;
-
-       /* The number of packets in buffer */
-       packets = header_length / IN_PACKET_HEADER_SIZE;
-
-       /* For buffer-over-run prevention. */
-       max_payload_quadlets = amdtp_stream_get_max_payload(s) / 4;
-
-       for (p = 0; p < packets; p++) {
-               buffer = s->buffer.packets[s->packet_index].buffer;
-
-               /* The number of quadlets in this packet */
-               payload_quadlets =
-                       (be32_to_cpu(headers[p]) >> ISO_DATA_LENGTH_SHIFT) / 4;
-               if (payload_quadlets > max_payload_quadlets) {
-                       dev_err(&s->unit->device,
-                               "Detect jumbo payload: %02x %02x\n",
-                               payload_quadlets, max_payload_quadlets);
-                       s->packet_index = -1;
-                       break;
-               }
-
-               if (handle_in_packet(s, payload_quadlets, buffer,
-                                                       &data_blocks) < 0) {
-                       s->packet_index = -1;
-                       break;
-               }
-
-               /* Process sync slave stream */
-               if (s->sync_slave && s->sync_slave->callbacked) {
-                       syt = be32_to_cpu(buffer[1]) & CIP_SYT_MASK;
-                       if (handle_out_packet(s->sync_slave,
-                                             data_blocks, syt) < 0) {
-                               s->packet_index = -1;
-                               break;
-                       }
-               }
-       }
-
-       /* Queueing error or detecting discontinuity */
-       if (s->packet_index < 0) {
-               amdtp_stream_pcm_abort(s);
-
-               /* Abort sync slave. */
-               if (s->sync_slave) {
-                       s->sync_slave->packet_index = -1;
-                       amdtp_stream_pcm_abort(s->sync_slave);
-               }
-               return;
-       }
-
-       /* when sync to device, flush the packets for slave stream */
-       if (s->sync_slave && s->sync_slave->callbacked)
-               fw_iso_context_queue_flush(s->sync_slave->context);
-
-       fw_iso_context_queue_flush(s->context);
-}
-
-/* processing is done by master callback */
-static void slave_stream_callback(struct fw_iso_context *context, u32 cycle,
-                                 size_t header_length, void *header,
-                                 void *private_data)
-{
-       return;
-}
-
-/* this is executed one time */
-static void amdtp_stream_first_callback(struct fw_iso_context *context,
-                                       u32 cycle, size_t header_length,
-                                       void *header, void *private_data)
-{
-       struct amdtp_stream *s = private_data;
-
-       /*
-        * For in-stream, first packet has come.
-        * For out-stream, prepared to transmit first packet
-        */
-       s->callbacked = true;
-       wake_up(&s->callback_wait);
-
-       if (s->direction == AMDTP_IN_STREAM)
-               context->callback.sc = in_stream_callback;
-       else if (s->flags & CIP_SYNC_TO_DEVICE)
-               context->callback.sc = slave_stream_callback;
-       else
-               context->callback.sc = out_stream_callback;
-
-       context->callback.sc(context, cycle, header_length, header, s);
-}
-
-/**
- * amdtp_stream_start - start transferring packets
- * @s: the AMDTP stream to start
- * @channel: the isochronous channel on the bus
- * @speed: firewire speed code
- *
- * The stream cannot be started until it has been configured with
- * amdtp_stream_set_parameters() and it must be started before any PCM or MIDI
- * device can be started.
- */
-int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed)
-{
-       static const struct {
-               unsigned int data_block;
-               unsigned int syt_offset;
-       } initial_state[] = {
-               [CIP_SFC_32000]  = {  4, 3072 },
-               [CIP_SFC_48000]  = {  6, 1024 },
-               [CIP_SFC_96000]  = { 12, 1024 },
-               [CIP_SFC_192000] = { 24, 1024 },
-               [CIP_SFC_44100]  = {  0,   67 },
-               [CIP_SFC_88200]  = {  0,   67 },
-               [CIP_SFC_176400] = {  0,   67 },
-       };
-       unsigned int header_size;
-       enum dma_data_direction dir;
-       int type, tag, err;
-
-       mutex_lock(&s->mutex);
-
-       if (WARN_ON(amdtp_stream_running(s) ||
-                   (s->data_block_quadlets < 1))) {
-               err = -EBADFD;
-               goto err_unlock;
-       }
-
-       if (s->direction == AMDTP_IN_STREAM &&
-           s->flags & CIP_SKIP_INIT_DBC_CHECK)
-               s->data_block_counter = UINT_MAX;
-       else
-               s->data_block_counter = 0;
-       s->data_block_state = initial_state[s->sfc].data_block;
-       s->syt_offset_state = initial_state[s->sfc].syt_offset;
-       s->last_syt_offset = TICKS_PER_CYCLE;
-
-       /* initialize packet buffer */
-       if (s->direction == AMDTP_IN_STREAM) {
-               dir = DMA_FROM_DEVICE;
-               type = FW_ISO_CONTEXT_RECEIVE;
-               header_size = IN_PACKET_HEADER_SIZE;
-       } else {
-               dir = DMA_TO_DEVICE;
-               type = FW_ISO_CONTEXT_TRANSMIT;
-               header_size = OUT_PACKET_HEADER_SIZE;
-       }
-       err = iso_packets_buffer_init(&s->buffer, s->unit, QUEUE_LENGTH,
-                                     amdtp_stream_get_max_payload(s), dir);
-       if (err < 0)
-               goto err_unlock;
-
-       s->context = fw_iso_context_create(fw_parent_device(s->unit)->card,
-                                          type, channel, speed, header_size,
-                                          amdtp_stream_first_callback, s);
-       if (IS_ERR(s->context)) {
-               err = PTR_ERR(s->context);
-               if (err == -EBUSY)
-                       dev_err(&s->unit->device,
-                               "no free stream on this controller\n");
-               goto err_buffer;
-       }
-
-       amdtp_stream_update(s);
-
-       s->packet_index = 0;
-       do {
-               if (s->direction == AMDTP_IN_STREAM)
-                       err = queue_in_packet(s);
-               else
-                       err = queue_out_packet(s, 0, true);
-               if (err < 0)
-                       goto err_context;
-       } while (s->packet_index > 0);
-
-       /* NOTE: TAG1 matches CIP. This just affects in stream. */
-       tag = FW_ISO_CONTEXT_MATCH_TAG1;
-       if (s->flags & CIP_EMPTY_WITH_TAG0)
-               tag |= FW_ISO_CONTEXT_MATCH_TAG0;
-
-       s->callbacked = false;
-       err = fw_iso_context_start(s->context, -1, 0, tag);
-       if (err < 0)
-               goto err_context;
-
-       mutex_unlock(&s->mutex);
-
-       return 0;
-
-err_context:
-       fw_iso_context_destroy(s->context);
-       s->context = ERR_PTR(-1);
-err_buffer:
-       iso_packets_buffer_destroy(&s->buffer, s->unit);
-err_unlock:
-       mutex_unlock(&s->mutex);
-
-       return err;
-}
-EXPORT_SYMBOL(amdtp_stream_start);
-
-/**
- * amdtp_stream_pcm_pointer - get the PCM buffer position
- * @s: the AMDTP stream that transports the PCM data
- *
- * Returns the current buffer position, in frames.
- */
-unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s)
-{
-       /* this optimization is allowed to be racy */
-       if (s->pointer_flush && amdtp_stream_running(s))
-               fw_iso_context_flush_completions(s->context);
-       else
-               s->pointer_flush = true;
-
-       return ACCESS_ONCE(s->pcm_buffer_pointer);
-}
-EXPORT_SYMBOL(amdtp_stream_pcm_pointer);
-
-/**
- * amdtp_stream_update - update the stream after a bus reset
- * @s: the AMDTP stream
- */
-void amdtp_stream_update(struct amdtp_stream *s)
-{
-       /* Precomputing. */
-       ACCESS_ONCE(s->source_node_id_field) =
-               (fw_parent_device(s->unit)->card->node_id << CIP_SID_SHIFT) &
-                                                               CIP_SID_MASK;
-}
-EXPORT_SYMBOL(amdtp_stream_update);
-
-/**
- * amdtp_stream_stop - stop sending packets
- * @s: the AMDTP stream to stop
- *
- * All PCM and MIDI devices of the stream must be stopped before the stream
- * itself can be stopped.
- */
-void amdtp_stream_stop(struct amdtp_stream *s)
-{
-       mutex_lock(&s->mutex);
-
-       if (!amdtp_stream_running(s)) {
-               mutex_unlock(&s->mutex);
-               return;
-       }
-
-       tasklet_kill(&s->period_tasklet);
-       fw_iso_context_stop(s->context);
-       fw_iso_context_destroy(s->context);
-       s->context = ERR_PTR(-1);
-       iso_packets_buffer_destroy(&s->buffer, s->unit);
-
-       s->callbacked = false;
-
-       mutex_unlock(&s->mutex);
-}
-EXPORT_SYMBOL(amdtp_stream_stop);
-
-/**
- * amdtp_stream_pcm_abort - abort the running PCM device
- * @s: the AMDTP stream about to be stopped
- *
- * If the isochronous stream needs to be stopped asynchronously, call this
- * function first to stop the PCM device.
- */
-void amdtp_stream_pcm_abort(struct amdtp_stream *s)
-{
-       struct snd_pcm_substream *pcm;
-
-       pcm = ACCESS_ONCE(s->pcm);
-       if (pcm)
-               snd_pcm_stop_xrun(pcm);
-}
-EXPORT_SYMBOL(amdtp_stream_pcm_abort);
diff --git a/sound/firewire/amdtp.h b/sound/firewire/amdtp.h
deleted file mode 100644 (file)
index b2cf9e7..0000000
+++ /dev/null
@@ -1,298 +0,0 @@
-#ifndef SOUND_FIREWIRE_AMDTP_H_INCLUDED
-#define SOUND_FIREWIRE_AMDTP_H_INCLUDED
-
-#include <linux/err.h>
-#include <linux/interrupt.h>
-#include <linux/mutex.h>
-#include <sound/asound.h>
-#include "packets-buffer.h"
-
-/**
- * enum cip_flags - describes details of the streaming protocol
- * @CIP_NONBLOCKING: In non-blocking mode, each packet contains
- *     sample_rate/8000 samples, with rounding up or down to adjust
- *     for clock skew and left-over fractional samples.  This should
- *     be used if supported by the device.
- * @CIP_BLOCKING: In blocking mode, each packet contains either zero or
- *     SYT_INTERVAL samples, with these two types alternating so that
- *     the overall sample rate comes out right.
- * @CIP_SYNC_TO_DEVICE: In sync to device mode, time stamp in out packets is
- *     generated by in packets. Defaultly this driver generates timestamp.
- * @CIP_EMPTY_WITH_TAG0: Only for in-stream. Empty in-packets have TAG0.
- * @CIP_DBC_IS_END_EVENT: Only for in-stream. The value of dbc in an in-packet
- *     corresponds to the end of event in the packet. Out of IEC 61883.
- * @CIP_WRONG_DBS: Only for in-stream. The value of dbs is wrong in in-packets.
- *     The value of data_block_quadlets is used instead of reported value.
- * @CIP_SKIP_DBC_ZERO_CHECK: Only for in-stream.  Packets with zero in dbc is
- *     skipped for detecting discontinuity.
- * @CIP_SKIP_INIT_DBC_CHECK: Only for in-stream. The value of dbc in first
- *     packet is not continuous from an initial value.
- * @CIP_EMPTY_HAS_WRONG_DBC: Only for in-stream. The value of dbc in empty
- *     packet is wrong but the others are correct.
- * @CIP_JUMBO_PAYLOAD: Only for in-stream. The number of data blocks in an
- *     packet is larger than IEC 61883-6 defines. Current implementation
- *     allows 5 times as large as IEC 61883-6 defines.
- */
-enum cip_flags {
-       CIP_NONBLOCKING         = 0x00,
-       CIP_BLOCKING            = 0x01,
-       CIP_SYNC_TO_DEVICE      = 0x02,
-       CIP_EMPTY_WITH_TAG0     = 0x04,
-       CIP_DBC_IS_END_EVENT    = 0x08,
-       CIP_WRONG_DBS           = 0x10,
-       CIP_SKIP_DBC_ZERO_CHECK = 0x20,
-       CIP_SKIP_INIT_DBC_CHECK = 0x40,
-       CIP_EMPTY_HAS_WRONG_DBC = 0x80,
-       CIP_JUMBO_PAYLOAD       = 0x100,
-};
-
-/**
- * enum cip_sfc - supported Sampling Frequency Codes (SFCs)
- * @CIP_SFC_32000:   32,000 data blocks
- * @CIP_SFC_44100:   44,100 data blocks
- * @CIP_SFC_48000:   48,000 data blocks
- * @CIP_SFC_88200:   88,200 data blocks
- * @CIP_SFC_96000:   96,000 data blocks
- * @CIP_SFC_176400: 176,400 data blocks
- * @CIP_SFC_192000: 192,000 data blocks
- * @CIP_SFC_COUNT: the number of supported SFCs
- *
- * These values are used to show nominal Sampling Frequency Code in
- * Format Dependent Field (FDF) of AMDTP packet header. In IEC 61883-6:2002,
- * this code means the number of events per second. Actually the code
- * represents the number of data blocks transferred per second in an AMDTP
- * stream.
- *
- * In IEC 61883-6:2005, some extensions were added to support more types of
- * data such as 'One Bit LInear Audio', therefore the meaning of SFC became
- * different depending on the types.
- *
- * Currently our implementation is compatible with IEC 61883-6:2002.
- */
-enum cip_sfc {
-       CIP_SFC_32000  = 0,
-       CIP_SFC_44100  = 1,
-       CIP_SFC_48000  = 2,
-       CIP_SFC_88200  = 3,
-       CIP_SFC_96000  = 4,
-       CIP_SFC_176400 = 5,
-       CIP_SFC_192000 = 6,
-       CIP_SFC_COUNT
-};
-
-#define AMDTP_IN_PCM_FORMAT_BITS       SNDRV_PCM_FMTBIT_S32
-
-#define AMDTP_OUT_PCM_FORMAT_BITS      (SNDRV_PCM_FMTBIT_S16 | \
-                                        SNDRV_PCM_FMTBIT_S32)
-
-
-/*
- * This module supports maximum 64 PCM channels for one PCM stream
- * This is for our convenience.
- */
-#define AMDTP_MAX_CHANNELS_FOR_PCM     64
-
-/*
- * AMDTP packet can include channels for MIDI conformant data.
- * Each MIDI conformant data channel includes 8 MPX-MIDI data stream.
- * Each MPX-MIDI data stream includes one data stream from/to MIDI ports.
- *
- * This module supports maximum 1 MIDI conformant data channels.
- * Then this AMDTP packets can transfer maximum 8 MIDI data streams.
- */
-#define AMDTP_MAX_CHANNELS_FOR_MIDI    1
-
-struct fw_unit;
-struct fw_iso_context;
-struct snd_pcm_substream;
-struct snd_pcm_runtime;
-struct snd_rawmidi_substream;
-
-enum amdtp_stream_direction {
-       AMDTP_OUT_STREAM = 0,
-       AMDTP_IN_STREAM
-};
-
-struct amdtp_stream {
-       struct fw_unit *unit;
-       enum cip_flags flags;
-       enum amdtp_stream_direction direction;
-       struct fw_iso_context *context;
-       struct mutex mutex;
-
-       enum cip_sfc sfc;
-       unsigned int data_block_quadlets;
-       unsigned int pcm_channels;
-       unsigned int midi_ports;
-       void (*transfer_samples)(struct amdtp_stream *s,
-                                struct snd_pcm_substream *pcm,
-                                __be32 *buffer, unsigned int frames);
-       u8 pcm_positions[AMDTP_MAX_CHANNELS_FOR_PCM];
-       u8 midi_position;
-
-       unsigned int syt_interval;
-       unsigned int transfer_delay;
-       unsigned int source_node_id_field;
-       struct iso_packets_buffer buffer;
-
-       struct snd_pcm_substream *pcm;
-       struct tasklet_struct period_tasklet;
-
-       int packet_index;
-       unsigned int data_block_counter;
-
-       unsigned int data_block_state;
-
-       unsigned int last_syt_offset;
-       unsigned int syt_offset_state;
-
-       unsigned int pcm_buffer_pointer;
-       unsigned int pcm_period_pointer;
-       bool pointer_flush;
-       bool double_pcm_frames;
-
-       struct snd_rawmidi_substream *midi[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
-       int midi_fifo_limit;
-       int midi_fifo_used[AMDTP_MAX_CHANNELS_FOR_MIDI * 8];
-
-       /* quirk: fixed interval of dbc between previos/current packets. */
-       unsigned int tx_dbc_interval;
-       /* quirk: indicate the value of dbc field in a first packet. */
-       unsigned int tx_first_dbc;
-
-       bool callbacked;
-       wait_queue_head_t callback_wait;
-       struct amdtp_stream *sync_slave;
-};
-
-int amdtp_stream_init(struct amdtp_stream *s, struct fw_unit *unit,
-                     enum amdtp_stream_direction dir,
-                     enum cip_flags flags);
-void amdtp_stream_destroy(struct amdtp_stream *s);
-
-void amdtp_stream_set_parameters(struct amdtp_stream *s,
-                                unsigned int rate,
-                                unsigned int pcm_channels,
-                                unsigned int midi_ports);
-unsigned int amdtp_stream_get_max_payload(struct amdtp_stream *s);
-
-int amdtp_stream_start(struct amdtp_stream *s, int channel, int speed);
-void amdtp_stream_update(struct amdtp_stream *s);
-void amdtp_stream_stop(struct amdtp_stream *s);
-
-int amdtp_stream_add_pcm_hw_constraints(struct amdtp_stream *s,
-                                       struct snd_pcm_runtime *runtime);
-void amdtp_stream_set_pcm_format(struct amdtp_stream *s,
-                                snd_pcm_format_t format);
-void amdtp_stream_pcm_prepare(struct amdtp_stream *s);
-unsigned long amdtp_stream_pcm_pointer(struct amdtp_stream *s);
-void amdtp_stream_pcm_abort(struct amdtp_stream *s);
-
-extern const unsigned int amdtp_syt_intervals[CIP_SFC_COUNT];
-extern const unsigned int amdtp_rate_table[CIP_SFC_COUNT];
-
-/**
- * amdtp_stream_running - check stream is running or not
- * @s: the AMDTP stream
- *
- * If this function returns true, the stream is running.
- */
-static inline bool amdtp_stream_running(struct amdtp_stream *s)
-{
-       return !IS_ERR(s->context);
-}
-
-/**
- * amdtp_streaming_error - check for streaming error
- * @s: the AMDTP stream
- *
- * If this function returns true, the stream's packet queue has stopped due to
- * an asynchronous error.
- */
-static inline bool amdtp_streaming_error(struct amdtp_stream *s)
-{
-       return s->packet_index < 0;
-}
-
-/**
- * amdtp_stream_pcm_running - check PCM substream is running or not
- * @s: the AMDTP stream
- *
- * If this function returns true, PCM substream in the AMDTP stream is running.
- */
-static inline bool amdtp_stream_pcm_running(struct amdtp_stream *s)
-{
-       return !!s->pcm;
-}
-
-/**
- * amdtp_stream_pcm_trigger - start/stop playback from a PCM device
- * @s: the AMDTP stream
- * @pcm: the PCM device to be started, or %NULL to stop the current device
- *
- * Call this function on a running isochronous stream to enable the actual
- * transmission of PCM data.  This function should be called from the PCM
- * device's .trigger callback.
- */
-static inline void amdtp_stream_pcm_trigger(struct amdtp_stream *s,
-                                           struct snd_pcm_substream *pcm)
-{
-       ACCESS_ONCE(s->pcm) = pcm;
-}
-
-/**
- * amdtp_stream_midi_trigger - start/stop playback/capture with a MIDI device
- * @s: the AMDTP stream
- * @port: index of MIDI port
- * @midi: the MIDI device to be started, or %NULL to stop the current device
- *
- * Call this function on a running isochronous stream to enable the actual
- * transmission of MIDI data.  This function should be called from the MIDI
- * device's .trigger callback.
- */
-static inline void amdtp_stream_midi_trigger(struct amdtp_stream *s,
-                                            unsigned int port,
-                                            struct snd_rawmidi_substream *midi)
-{
-       if (port < s->midi_ports)
-               ACCESS_ONCE(s->midi[port]) = midi;
-}
-
-static inline bool cip_sfc_is_base_44100(enum cip_sfc sfc)
-{
-       return sfc & 1;
-}
-
-static inline void amdtp_stream_set_sync(enum cip_flags sync_mode,
-                                        struct amdtp_stream *master,
-                                        struct amdtp_stream *slave)
-{
-       if (sync_mode == CIP_SYNC_TO_DEVICE) {
-               master->flags |= CIP_SYNC_TO_DEVICE;
-               slave->flags |= CIP_SYNC_TO_DEVICE;
-               master->sync_slave = slave;
-       } else {
-               master->flags &= ~CIP_SYNC_TO_DEVICE;
-               slave->flags &= ~CIP_SYNC_TO_DEVICE;
-               master->sync_slave = NULL;
-       }
-
-       slave->sync_slave = NULL;
-}
-
-/**
- * amdtp_stream_wait_callback - sleep till callbacked or timeout
- * @s: the AMDTP stream
- * @timeout: msec till timeout
- *
- * If this function return false, the AMDTP stream should be stopped.
- */
-static inline bool amdtp_stream_wait_callback(struct amdtp_stream *s,
-                                             unsigned int timeout)
-{
-       return wait_event_timeout(s->callback_wait,
-                                 s->callbacked == true,
-                                 msecs_to_jiffies(timeout)) > 0;
-}
-
-#endif
index 6cf470c80d1fd1e37f92b65d933838da405524cd..af7ed66432661d307cded6269f9072d17ee99b35 100644 (file)
@@ -1,4 +1,4 @@
 snd-bebob-objs := bebob_command.o bebob_stream.o bebob_proc.o bebob_midi.o \
                  bebob_pcm.o bebob_hwdep.o bebob_terratec.o bebob_yamaha.o \
                  bebob_focusrite.o bebob_maudio.o bebob.o
-obj-m += snd-bebob.o
+obj-$(CONFIG_SND_BEBOB) += snd-bebob.o
index 27a04ac8ffee938fc5e1cd023f6472c3f57f8c2e..091290d1f3ea0a99b8a6d81d501a385863efe64d 100644 (file)
@@ -41,7 +41,8 @@ static DECLARE_BITMAP(devices_used, SNDRV_CARDS);
 #define VEN_EDIROL     0x000040ab
 #define VEN_PRESONUS   0x00000a92
 #define VEN_BRIDGECO   0x000007f5
-#define VEN_MACKIE     0x0000000f
+#define VEN_MACKIE1    0x0000000f
+#define VEN_MACKIE2    0x00000ff2
 #define VEN_STANTON    0x00001260
 #define VEN_TASCAM     0x0000022e
 #define VEN_BEHRINGER  0x00001564
@@ -334,7 +335,7 @@ static void bebob_remove(struct fw_unit *unit)
        snd_card_free_when_closed(bebob->card);
 }
 
-static struct snd_bebob_rate_spec normal_rate_spec = {
+static const struct snd_bebob_rate_spec normal_rate_spec = {
        .get    = &snd_bebob_stream_get_rate,
        .set    = &snd_bebob_stream_set_rate
 };
@@ -360,9 +361,9 @@ static const struct ieee1394_device_id bebob_id_table[] = {
        /* BridgeCo, Audio5 */
        SND_BEBOB_DEV_ENTRY(VEN_BRIDGECO, 0x00010049, &spec_normal),
        /* Mackie, Onyx 1220/1620/1640 (Firewire I/O Card) */
-       SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010065, &spec_normal),
+       SND_BEBOB_DEV_ENTRY(VEN_MACKIE2, 0x00010065, &spec_normal),
        /* Mackie, d.2 (Firewire Option) */
-       SND_BEBOB_DEV_ENTRY(VEN_MACKIE, 0x00010067, &spec_normal),
+       SND_BEBOB_DEV_ENTRY(VEN_MACKIE1, 0x00010067, &spec_normal),
        /* Stanton, ScratchAmp */
        SND_BEBOB_DEV_ENTRY(VEN_STANTON, 0x00000001, &spec_normal),
        /* Tascam, IF-FW DM */
index d23caca7f369076fe5f9bd7c76c01f5c76e259ab..4d8fcc78e747cb5d4232ed9f76c3662a242d72d3 100644 (file)
@@ -31,7 +31,7 @@
 #include "../fcp.h"
 #include "../packets-buffer.h"
 #include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
 #include "../cmp.h"
 
 /* basic register addresses on DM1000/DM1100/DM1500 */
@@ -70,9 +70,9 @@ struct snd_bebob_meter_spec {
        int (*get)(struct snd_bebob *bebob, u32 *target, unsigned int size);
 };
 struct snd_bebob_spec {
-       struct snd_bebob_clock_spec *clock;
-       struct snd_bebob_rate_spec *rate;
-       struct snd_bebob_meter_spec *meter;
+       const struct snd_bebob_clock_spec *clock;
+       const struct snd_bebob_rate_spec *rate;
+       const struct snd_bebob_meter_spec *meter;
 };
 
 struct snd_bebob {
@@ -235,19 +235,19 @@ int snd_bebob_create_pcm_devices(struct snd_bebob *bebob);
 int snd_bebob_create_hwdep_device(struct snd_bebob *bebob);
 
 /* model specific operations */
-extern struct snd_bebob_spec phase88_rack_spec;
-extern struct snd_bebob_spec phase24_series_spec;
-extern struct snd_bebob_spec yamaha_go_spec;
-extern struct snd_bebob_spec saffirepro_26_spec;
-extern struct snd_bebob_spec saffirepro_10_spec;
-extern struct snd_bebob_spec saffire_le_spec;
-extern struct snd_bebob_spec saffire_spec;
-extern struct snd_bebob_spec maudio_fw410_spec;
-extern struct snd_bebob_spec maudio_audiophile_spec;
-extern struct snd_bebob_spec maudio_solo_spec;
-extern struct snd_bebob_spec maudio_ozonic_spec;
-extern struct snd_bebob_spec maudio_nrv10_spec;
-extern struct snd_bebob_spec maudio_special_spec;
+extern const struct snd_bebob_spec phase88_rack_spec;
+extern const struct snd_bebob_spec phase24_series_spec;
+extern const struct snd_bebob_spec yamaha_go_spec;
+extern const struct snd_bebob_spec saffirepro_26_spec;
+extern const struct snd_bebob_spec saffirepro_10_spec;
+extern const struct snd_bebob_spec saffire_le_spec;
+extern const struct snd_bebob_spec saffire_spec;
+extern const struct snd_bebob_spec maudio_fw410_spec;
+extern const struct snd_bebob_spec maudio_audiophile_spec;
+extern const struct snd_bebob_spec maudio_solo_spec;
+extern const struct snd_bebob_spec maudio_ozonic_spec;
+extern const struct snd_bebob_spec maudio_nrv10_spec;
+extern const struct snd_bebob_spec maudio_special_spec;
 int snd_bebob_maudio_special_discover(struct snd_bebob *bebob, bool is1814);
 int snd_bebob_maudio_load_firmware(struct fw_unit *unit);
 
index a1a39494ea6c9bb985815f9ef18f799dd7b0062f..f1109005794944cd759b90ede92a6fc601ab251e 100644 (file)
@@ -200,7 +200,7 @@ end:
        return err;
 }
 
-struct snd_bebob_spec saffire_le_spec;
+const struct snd_bebob_spec saffire_le_spec;
 static enum snd_bebob_clock_type saffire_both_clk_src_types[] = {
        SND_BEBOB_CLOCK_TYPE_INTERNAL,
        SND_BEBOB_CLOCK_TYPE_EXTERNAL,
@@ -229,7 +229,7 @@ static const char *const saffire_meter_labels[] = {
 static int
 saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
 {
-       struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+       const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
        unsigned int channels;
        u64 offset;
        int err;
@@ -260,60 +260,60 @@ saffire_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
        return err;
 }
 
-static struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
+static const struct snd_bebob_rate_spec saffirepro_both_rate_spec = {
        .get    = &saffirepro_both_clk_freq_get,
        .set    = &saffirepro_both_clk_freq_set,
 };
 /* Saffire Pro 26 I/O  */
-static struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
+static const struct snd_bebob_clock_spec saffirepro_26_clk_spec = {
        .num    = ARRAY_SIZE(saffirepro_26_clk_src_types),
        .types  = saffirepro_26_clk_src_types,
        .get    = &saffirepro_both_clk_src_get,
 };
-struct snd_bebob_spec saffirepro_26_spec = {
+const struct snd_bebob_spec saffirepro_26_spec = {
        .clock  = &saffirepro_26_clk_spec,
        .rate   = &saffirepro_both_rate_spec,
        .meter  = NULL
 };
 /* Saffire Pro 10 I/O */
-static struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
+static const struct snd_bebob_clock_spec saffirepro_10_clk_spec = {
        .num    = ARRAY_SIZE(saffirepro_10_clk_src_types),
        .types  = saffirepro_10_clk_src_types,
        .get    = &saffirepro_both_clk_src_get,
 };
-struct snd_bebob_spec saffirepro_10_spec = {
+const struct snd_bebob_spec saffirepro_10_spec = {
        .clock  = &saffirepro_10_clk_spec,
        .rate   = &saffirepro_both_rate_spec,
        .meter  = NULL
 };
 
-static struct snd_bebob_rate_spec saffire_both_rate_spec = {
+static const struct snd_bebob_rate_spec saffire_both_rate_spec = {
        .get    = &snd_bebob_stream_get_rate,
        .set    = &snd_bebob_stream_set_rate,
 };
-static struct snd_bebob_clock_spec saffire_both_clk_spec = {
+static const struct snd_bebob_clock_spec saffire_both_clk_spec = {
        .num    = ARRAY_SIZE(saffire_both_clk_src_types),
        .types  = saffire_both_clk_src_types,
        .get    = &saffire_both_clk_src_get,
 };
 /* Saffire LE */
-static struct snd_bebob_meter_spec saffire_le_meter_spec = {
+static const struct snd_bebob_meter_spec saffire_le_meter_spec = {
        .num    = ARRAY_SIZE(saffire_le_meter_labels),
        .labels = saffire_le_meter_labels,
        .get    = &saffire_meter_get,
 };
-struct snd_bebob_spec saffire_le_spec = {
+const struct snd_bebob_spec saffire_le_spec = {
        .clock  = &saffire_both_clk_spec,
        .rate   = &saffire_both_rate_spec,
        .meter  = &saffire_le_meter_spec
 };
 /* Saffire */
-static struct snd_bebob_meter_spec saffire_meter_spec = {
+static const struct snd_bebob_meter_spec saffire_meter_spec = {
        .num    = ARRAY_SIZE(saffire_meter_labels),
        .labels = saffire_meter_labels,
        .get    = &saffire_meter_get,
 };
-struct snd_bebob_spec saffire_spec = {
+const struct snd_bebob_spec saffire_spec = {
        .clock  = &saffire_both_clk_spec,
        .rate   = &saffire_both_rate_spec,
        .meter  = &saffire_meter_spec
index 057495d54ab029e7f1fc5b1b7cf630ee20c3c817..07e5abdbceb59cec189726d79ee5d200d452e53b 100644 (file)
@@ -628,7 +628,7 @@ static const char *const special_meter_labels[] = {
 static int
 special_meter_get(struct snd_bebob *bebob, u32 *target, unsigned int size)
 {
-       u16 *buf;
+       __be16 *buf;
        unsigned int i, c, channels;
        int err;
 
@@ -687,7 +687,7 @@ static const char *const nrv10_meter_labels[] = {
 static int
 normal_meter_get(struct snd_bebob *bebob, u32 *buf, unsigned int size)
 {
-       struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+       const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
        unsigned int c, channels;
        int err;
 
@@ -712,85 +712,85 @@ end:
 }
 
 /* for special customized devices */
-static struct snd_bebob_rate_spec special_rate_spec = {
+static const struct snd_bebob_rate_spec special_rate_spec = {
        .get    = &special_get_rate,
        .set    = &special_set_rate,
 };
-static struct snd_bebob_clock_spec special_clk_spec = {
+static const struct snd_bebob_clock_spec special_clk_spec = {
        .num    = ARRAY_SIZE(special_clk_types),
        .types  = special_clk_types,
        .get    = &special_clk_get,
 };
-static struct snd_bebob_meter_spec special_meter_spec = {
+static const struct snd_bebob_meter_spec special_meter_spec = {
        .num    = ARRAY_SIZE(special_meter_labels),
        .labels = special_meter_labels,
        .get    = &special_meter_get
 };
-struct snd_bebob_spec maudio_special_spec = {
+const struct snd_bebob_spec maudio_special_spec = {
        .clock  = &special_clk_spec,
        .rate   = &special_rate_spec,
        .meter  = &special_meter_spec
 };
 
 /* Firewire 410 specification */
-static struct snd_bebob_rate_spec usual_rate_spec = {
+static const struct snd_bebob_rate_spec usual_rate_spec = {
        .get    = &snd_bebob_stream_get_rate,
        .set    = &snd_bebob_stream_set_rate,
 };
-static struct snd_bebob_meter_spec fw410_meter_spec = {
+static const struct snd_bebob_meter_spec fw410_meter_spec = {
        .num    = ARRAY_SIZE(fw410_meter_labels),
        .labels = fw410_meter_labels,
        .get    = &normal_meter_get
 };
-struct snd_bebob_spec maudio_fw410_spec = {
+const struct snd_bebob_spec maudio_fw410_spec = {
        .clock  = NULL,
        .rate   = &usual_rate_spec,
        .meter  = &fw410_meter_spec
 };
 
 /* Firewire Audiophile specification */
-static struct snd_bebob_meter_spec audiophile_meter_spec = {
+static const struct snd_bebob_meter_spec audiophile_meter_spec = {
        .num    = ARRAY_SIZE(audiophile_meter_labels),
        .labels = audiophile_meter_labels,
        .get    = &normal_meter_get
 };
-struct snd_bebob_spec maudio_audiophile_spec = {
+const struct snd_bebob_spec maudio_audiophile_spec = {
        .clock  = NULL,
        .rate   = &usual_rate_spec,
        .meter  = &audiophile_meter_spec
 };
 
 /* Firewire Solo specification */
-static struct snd_bebob_meter_spec solo_meter_spec = {
+static const struct snd_bebob_meter_spec solo_meter_spec = {
        .num    = ARRAY_SIZE(solo_meter_labels),
        .labels = solo_meter_labels,
        .get    = &normal_meter_get
 };
-struct snd_bebob_spec maudio_solo_spec = {
+const struct snd_bebob_spec maudio_solo_spec = {
        .clock  = NULL,
        .rate   = &usual_rate_spec,
        .meter  = &solo_meter_spec
 };
 
 /* Ozonic specification */
-static struct snd_bebob_meter_spec ozonic_meter_spec = {
+static const struct snd_bebob_meter_spec ozonic_meter_spec = {
        .num    = ARRAY_SIZE(ozonic_meter_labels),
        .labels = ozonic_meter_labels,
        .get    = &normal_meter_get
 };
-struct snd_bebob_spec maudio_ozonic_spec = {
+const struct snd_bebob_spec maudio_ozonic_spec = {
        .clock  = NULL,
        .rate   = &usual_rate_spec,
        .meter  = &ozonic_meter_spec
 };
 
 /* NRV10 specification */
-static struct snd_bebob_meter_spec nrv10_meter_spec = {
+static const struct snd_bebob_meter_spec nrv10_meter_spec = {
        .num    = ARRAY_SIZE(nrv10_meter_labels),
        .labels = nrv10_meter_labels,
        .get    = &normal_meter_get
 };
-struct snd_bebob_spec maudio_nrv10_spec = {
+const struct snd_bebob_spec maudio_nrv10_spec = {
        .clock  = NULL,
        .rate   = &usual_rate_spec,
        .meter  = &nrv10_meter_spec
index 5681143925cda919987350a9a054fc47cf1b0a1e..90d95be499b079c5d32c99b1b7b56a409ac377ec 100644 (file)
@@ -72,11 +72,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&bebob->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&bebob->tx_stream,
-                                         substrm->number, substrm);
+               amdtp_am824_midi_trigger(&bebob->tx_stream,
+                                        substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&bebob->tx_stream,
-                                         substrm->number, NULL);
+               amdtp_am824_midi_trigger(&bebob->tx_stream,
+                                        substrm->number, NULL);
 
        spin_unlock_irqrestore(&bebob->lock, flags);
 }
@@ -89,11 +89,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&bebob->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&bebob->rx_stream,
-                                         substrm->number, substrm);
+               amdtp_am824_midi_trigger(&bebob->rx_stream,
+                                        substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&bebob->rx_stream,
-                                         substrm->number, NULL);
+               amdtp_am824_midi_trigger(&bebob->rx_stream,
+                                        substrm->number, NULL);
 
        spin_unlock_irqrestore(&bebob->lock, flags);
 }
index c0f018a61fdc039b0e331a40d9bed4e305645320..ef224d6f5c248d0d28cf9754f96f20b8fc99df5c 100644 (file)
@@ -122,11 +122,11 @@ pcm_init_hw_params(struct snd_bebob *bebob,
                           SNDRV_PCM_INFO_MMAP_VALID;
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+               runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
                s = &bebob->tx_stream;
                formations = bebob->tx_stream_formations;
        } else {
-               runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+               runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
                s = &bebob->rx_stream;
                formations = bebob->rx_stream_formations;
        }
@@ -146,7 +146,7 @@ pcm_init_hw_params(struct snd_bebob *bebob,
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+       err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
 end:
        return err;
 }
@@ -155,7 +155,7 @@ static int
 pcm_open(struct snd_pcm_substream *substream)
 {
        struct snd_bebob *bebob = substream->private_data;
-       struct snd_bebob_rate_spec *spec = bebob->spec->rate;
+       const struct snd_bebob_rate_spec *spec = bebob->spec->rate;
        unsigned int sampling_rate;
        enum snd_bebob_clock_type src;
        int err;
@@ -220,8 +220,8 @@ pcm_capture_hw_params(struct snd_pcm_substream *substream,
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
                atomic_inc(&bebob->substreams_counter);
-       amdtp_stream_set_pcm_format(&bebob->tx_stream,
-                                   params_format(hw_params));
+
+       amdtp_am824_set_pcm_format(&bebob->tx_stream, params_format(hw_params));
 
        return 0;
 }
@@ -239,8 +239,8 @@ pcm_playback_hw_params(struct snd_pcm_substream *substream,
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
                atomic_inc(&bebob->substreams_counter);
-       amdtp_stream_set_pcm_format(&bebob->rx_stream,
-                                   params_format(hw_params));
+
+       amdtp_am824_set_pcm_format(&bebob->rx_stream, params_format(hw_params));
 
        return 0;
 }
index 301cc6a9394542817c9d17feda1b87da4505b65c..ec24f96794f50794737578c93d06261a9d93330c 100644 (file)
@@ -73,7 +73,7 @@ proc_read_meters(struct snd_info_entry *entry,
                 struct snd_info_buffer *buffer)
 {
        struct snd_bebob *bebob = entry->private_data;
-       struct snd_bebob_meter_spec *spec = bebob->spec->meter;
+       const struct snd_bebob_meter_spec *spec = bebob->spec->meter;
        u32 *buf;
        unsigned int i, c, channels, size;
 
@@ -138,8 +138,8 @@ proc_read_clock(struct snd_info_entry *entry,
                "SYT-Match",
        };
        struct snd_bebob *bebob = entry->private_data;
-       struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
-       struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+       const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+       const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
        enum snd_bebob_clock_type src;
        unsigned int rate;
 
index 5be5242e1ed86a535b875346429f5113d37dcad5..926e5dcbb66a1a3ec523482ceb5e199f8c455b64 100644 (file)
@@ -119,7 +119,7 @@ end:
 int snd_bebob_stream_get_clock_src(struct snd_bebob *bebob,
                                   enum snd_bebob_clock_type *src)
 {
-       struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+       const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
        u8 addr[AVC_BRIDGECO_ADDR_BYTES], input[7];
        unsigned int id;
        enum avc_bridgeco_plug_type type;
@@ -338,7 +338,7 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
                                        err = -ENOSYS;
                                        goto end;
                                }
-                               s->midi_position = stm_pos;
+                               amdtp_am824_set_midi_position(s, stm_pos);
                                midi = stm_pos;
                                break;
                        /* for PCM data channel */
@@ -354,11 +354,12 @@ map_data_channels(struct snd_bebob *bebob, struct amdtp_stream *s)
                        case 0x09:      /* Digital */
                        default:
                                location = pcm + sec_loc;
-                               if (location >= AMDTP_MAX_CHANNELS_FOR_PCM) {
+                               if (location >= AM824_MAX_CHANNELS_FOR_PCM) {
                                        err = -ENOSYS;
                                        goto end;
                                }
-                               s->pcm_positions[location] = stm_pos;
+                               amdtp_am824_set_pcm_position(s, location,
+                                                            stm_pos);
                                break;
                        }
                }
@@ -427,12 +428,19 @@ make_both_connections(struct snd_bebob *bebob, unsigned int rate)
        index = get_formation_index(rate);
        pcm_channels = bebob->tx_stream_formations[index].pcm;
        midi_channels = bebob->tx_stream_formations[index].midi;
-       amdtp_stream_set_parameters(&bebob->tx_stream,
-                                   rate, pcm_channels, midi_channels * 8);
+       err = amdtp_am824_set_parameters(&bebob->tx_stream, rate,
+                                        pcm_channels, midi_channels * 8,
+                                        false);
+       if (err < 0)
+               goto end;
+
        pcm_channels = bebob->rx_stream_formations[index].pcm;
        midi_channels = bebob->rx_stream_formations[index].midi;
-       amdtp_stream_set_parameters(&bebob->rx_stream,
-                                   rate, pcm_channels, midi_channels * 8);
+       err = amdtp_am824_set_parameters(&bebob->rx_stream, rate,
+                                        pcm_channels, midi_channels * 8,
+                                        false);
+       if (err < 0)
+               goto end;
 
        /* establish connections for both streams */
        err = cmp_connection_establish(&bebob->out_conn,
@@ -530,8 +538,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_init(&bebob->tx_stream, bebob->unit,
-                               AMDTP_IN_STREAM, CIP_BLOCKING);
+       err = amdtp_am824_init(&bebob->tx_stream, bebob->unit,
+                              AMDTP_IN_STREAM, CIP_BLOCKING);
        if (err < 0) {
                amdtp_stream_destroy(&bebob->tx_stream);
                destroy_both_connections(bebob);
@@ -559,8 +567,8 @@ int snd_bebob_stream_init_duplex(struct snd_bebob *bebob)
        if (bebob->maudio_special_quirk)
                bebob->tx_stream.flags |= CIP_EMPTY_HAS_WRONG_DBC;
 
-       err = amdtp_stream_init(&bebob->rx_stream, bebob->unit,
-                               AMDTP_OUT_STREAM, CIP_BLOCKING);
+       err = amdtp_am824_init(&bebob->rx_stream, bebob->unit,
+                              AMDTP_OUT_STREAM, CIP_BLOCKING);
        if (err < 0) {
                amdtp_stream_destroy(&bebob->tx_stream);
                amdtp_stream_destroy(&bebob->rx_stream);
@@ -572,7 +580,7 @@ end:
 
 int snd_bebob_stream_start_duplex(struct snd_bebob *bebob, unsigned int rate)
 {
-       struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
+       const struct snd_bebob_rate_spec *rate_spec = bebob->spec->rate;
        struct amdtp_stream *master, *slave;
        enum cip_flags sync_mode;
        unsigned int curr_rate;
@@ -864,8 +872,8 @@ parse_stream_formation(u8 *buf, unsigned int len,
                }
        }
 
-       if (formation[i].pcm  > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           formation[i].midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+       if (formation[i].pcm  > AM824_MAX_CHANNELS_FOR_PCM ||
+           formation[i].midi > AM824_MAX_CHANNELS_FOR_MIDI)
                return -ENOSYS;
 
        return 0;
@@ -959,7 +967,7 @@ end:
 
 int snd_bebob_stream_discover(struct snd_bebob *bebob)
 {
-       struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
+       const struct snd_bebob_clock_spec *clk_spec = bebob->spec->clock;
        u8 plugs[AVC_PLUG_INFO_BUF_BYTES], addr[AVC_BRIDGECO_ADDR_BYTES];
        enum avc_bridgeco_plug_type type;
        unsigned int i;
index 9242e33d2cf1c1d26dc83680315c82c006332164..c38358b82ada0ac909ff9e999ee3f57761b5a632 100644 (file)
@@ -55,30 +55,30 @@ phase24_series_clk_src_get(struct snd_bebob *bebob, unsigned int *id)
        return 0;
 }
 
-static struct snd_bebob_rate_spec phase_series_rate_spec = {
+static const struct snd_bebob_rate_spec phase_series_rate_spec = {
        .get    = &snd_bebob_stream_get_rate,
        .set    = &snd_bebob_stream_set_rate,
 };
 
 /* PHASE 88 Rack FW */
-static struct snd_bebob_clock_spec phase88_rack_clk = {
+static const struct snd_bebob_clock_spec phase88_rack_clk = {
        .num    = ARRAY_SIZE(phase88_rack_clk_src_types),
        .types  = phase88_rack_clk_src_types,
        .get    = &phase88_rack_clk_src_get,
 };
-struct snd_bebob_spec phase88_rack_spec = {
+const struct snd_bebob_spec phase88_rack_spec = {
        .clock  = &phase88_rack_clk,
        .rate   = &phase_series_rate_spec,
        .meter  = NULL
 };
 
 /* 'PHASE 24 FW' and 'PHASE X24 FW' */
-static struct snd_bebob_clock_spec phase24_series_clk = {
+static const struct snd_bebob_clock_spec phase24_series_clk = {
        .num    = ARRAY_SIZE(phase24_series_clk_src_types),
        .types  = phase24_series_clk_src_types,
        .get    = &phase24_series_clk_src_get,
 };
-struct snd_bebob_spec phase24_series_spec = {
+const struct snd_bebob_spec phase24_series_spec = {
        .clock  = &phase24_series_clk,
        .rate   = &phase_series_rate_spec,
        .meter  = NULL
index 58101702410b64b17e1733fc3a643221f9d62e86..90d4404f77ce07d1ec98af09aea4463d7fdce25d 100644 (file)
@@ -46,16 +46,16 @@ clk_src_get(struct snd_bebob *bebob, unsigned int *id)
 
        return 0;
 }
-static struct snd_bebob_clock_spec clock_spec = {
+static const struct snd_bebob_clock_spec clock_spec = {
        .num    = ARRAY_SIZE(clk_src_types),
        .types  = clk_src_types,
        .get    = &clk_src_get,
 };
-static struct snd_bebob_rate_spec rate_spec = {
+static const struct snd_bebob_rate_spec rate_spec = {
        .get    = &snd_bebob_stream_get_rate,
        .set    = &snd_bebob_stream_set_rate,
 };
-struct snd_bebob_spec yamaha_go_spec = {
+const struct snd_bebob_spec yamaha_go_spec = {
        .clock  = &clock_spec,
        .rate   = &rate_spec,
        .meter  = NULL
index 9ef228ef7baf2728b4bd8fb9e932c0f3e00dc791..55b4be9b0034093bc221ff1955057f739e299520 100644 (file)
@@ -1,3 +1,3 @@
 snd-dice-objs := dice-transaction.o dice-stream.o dice-proc.o dice-midi.o \
                 dice-pcm.o dice-hwdep.o dice.o
-obj-m += snd-dice.o
+obj-$(CONFIG_SND_DICE) += snd-dice.o
index fe43ce791f845dfccdb7e10ce6f9df72ffc1f549..151b09f240f280d14233817ff64ef77d14d3a31b 100644 (file)
@@ -52,10 +52,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&dice->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&dice->tx_stream,
+               amdtp_am824_midi_trigger(&dice->tx_stream,
                                          substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&dice->tx_stream,
+               amdtp_am824_midi_trigger(&dice->tx_stream,
                                          substrm->number, NULL);
 
        spin_unlock_irqrestore(&dice->lock, flags);
@@ -69,11 +69,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&dice->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&dice->rx_stream,
-                                         substrm->number, substrm);
+               amdtp_am824_midi_trigger(&dice->rx_stream,
+                                        substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&dice->rx_stream,
-                                         substrm->number, NULL);
+               amdtp_am824_midi_trigger(&dice->rx_stream,
+                                        substrm->number, NULL);
 
        spin_unlock_irqrestore(&dice->lock, flags);
 }
index 4e67b1da0fe6ffbe9350b0e17a71a87f2b03e73b..9b3431999fc8b6df8dac3b58c42d6e1604c617e4 100644 (file)
@@ -133,11 +133,11 @@ static int init_hw_info(struct snd_dice *dice,
                   SNDRV_PCM_INFO_BLOCK_TRANSFER;
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               hw->formats = AMDTP_IN_PCM_FORMAT_BITS;
+               hw->formats = AM824_IN_PCM_FORMAT_BITS;
                stream = &dice->tx_stream;
                pcm_channels = dice->tx_channels;
        } else {
-               hw->formats = AMDTP_OUT_PCM_FORMAT_BITS;
+               hw->formats = AM824_OUT_PCM_FORMAT_BITS;
                stream = &dice->rx_stream;
                pcm_channels = dice->rx_channels;
        }
@@ -156,7 +156,7 @@ static int init_hw_info(struct snd_dice *dice,
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
+       err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
 end:
        return err;
 }
@@ -243,8 +243,7 @@ static int capture_hw_params(struct snd_pcm_substream *substream,
                mutex_unlock(&dice->mutex);
        }
 
-       amdtp_stream_set_pcm_format(&dice->tx_stream,
-                                   params_format(hw_params));
+       amdtp_am824_set_pcm_format(&dice->tx_stream, params_format(hw_params));
 
        return 0;
 }
@@ -265,8 +264,7 @@ static int playback_hw_params(struct snd_pcm_substream *substream,
                mutex_unlock(&dice->mutex);
        }
 
-       amdtp_stream_set_pcm_format(&dice->rx_stream,
-                                   params_format(hw_params));
+       amdtp_am824_set_pcm_format(&dice->rx_stream, params_format(hw_params));
 
        return 0;
 }
index 07dbd01d7a6bd336d901fa78b83365b44307b1a0..a6a39f7ef58d8e32cf6e6ff60d45bcbbb23b1e04 100644 (file)
@@ -44,16 +44,16 @@ int snd_dice_stream_get_rate_mode(struct snd_dice *dice, unsigned int rate,
 static void release_resources(struct snd_dice *dice,
                              struct fw_iso_resources *resources)
 {
-       unsigned int channel;
+       __be32 channel;
 
        /* Reset channel number */
        channel = cpu_to_be32((u32)-1);
        if (resources == &dice->tx_resources)
                snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
-                                             &channel, 4);
+                                             &channel, sizeof(channel));
        else
                snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
-                                             &channel, 4);
+                                             &channel, sizeof(channel));
 
        fw_iso_resources_free(resources);
 }
@@ -62,7 +62,7 @@ static int keep_resources(struct snd_dice *dice,
                          struct fw_iso_resources *resources,
                          unsigned int max_payload_bytes)
 {
-       unsigned int channel;
+       __be32 channel;
        int err;
 
        err = fw_iso_resources_allocate(resources, max_payload_bytes,
@@ -74,10 +74,10 @@ static int keep_resources(struct snd_dice *dice,
        channel = cpu_to_be32(resources->channel);
        if (resources == &dice->tx_resources)
                err = snd_dice_transaction_write_tx(dice, TX_ISOCHRONOUS,
-                                                   &channel, 4);
+                                                   &channel, sizeof(channel));
        else
                err = snd_dice_transaction_write_rx(dice, RX_ISOCHRONOUS,
-                                                   &channel, 4);
+                                                   &channel, sizeof(channel));
        if (err < 0)
                release_resources(dice, resources);
 end:
@@ -100,6 +100,7 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
 {
        struct fw_iso_resources *resources;
        unsigned int i, mode, pcm_chs, midi_ports;
+       bool double_pcm_frames;
        int err;
 
        err = snd_dice_stream_get_rate_mode(dice, rate, &mode);
@@ -125,21 +126,24 @@ static int start_stream(struct snd_dice *dice, struct amdtp_stream *stream,
         * For this quirk, blocking mode is required and PCM buffer size should
         * be aligned to SYT_INTERVAL.
         */
-       if (mode > 1) {
+       double_pcm_frames = mode > 1;
+       if (double_pcm_frames) {
                rate /= 2;
                pcm_chs *= 2;
-               stream->double_pcm_frames = true;
-       } else {
-               stream->double_pcm_frames = false;
        }
 
-       amdtp_stream_set_parameters(stream, rate, pcm_chs, midi_ports);
-       if (mode > 1) {
+       err = amdtp_am824_set_parameters(stream, rate, pcm_chs, midi_ports,
+                                        double_pcm_frames);
+       if (err < 0)
+               goto end;
+
+       if (double_pcm_frames) {
                pcm_chs /= 2;
 
                for (i = 0; i < pcm_chs; i++) {
-                       stream->pcm_positions[i] = i * 2;
-                       stream->pcm_positions[i + pcm_chs] = i * 2 + 1;
+                       amdtp_am824_set_pcm_position(stream, i, i * 2);
+                       amdtp_am824_set_pcm_position(stream, i + pcm_chs,
+                                                    i * 2 + 1);
                }
        }
 
@@ -302,7 +306,7 @@ static int init_stream(struct snd_dice *dice, struct amdtp_stream *stream)
                goto end;
        resources->channels_mask = 0x00000000ffffffffuLL;
 
-       err = amdtp_stream_init(stream, dice->unit, dir, CIP_BLOCKING);
+       err = amdtp_am824_init(stream, dice->unit, dir, CIP_BLOCKING);
        if (err < 0) {
                amdtp_stream_destroy(stream);
                fw_iso_resources_destroy(resources);
index 70a111d7f428af4a0487f80cf350767935c8cac0..5d99436dfcaee5e70f3957a8f8b59be3d9e213d3 100644 (file)
@@ -29,7 +29,8 @@ static int dice_interface_check(struct fw_unit *unit)
        struct fw_csr_iterator it;
        int key, val, vendor = -1, model = -1, err;
        unsigned int category, i;
-       __be32 *pointers, value;
+       __be32 *pointers;
+       u32 value;
        __be32 version;
 
        pointers = kmalloc_array(ARRAY_SIZE(min_values), sizeof(__be32),
index ecf5dc86223544ed848fcfa9918630e74f17e7f9..101550ac1a242fc35af22f9f291b0fc2771d4532 100644 (file)
@@ -34,7 +34,7 @@
 #include <sound/pcm_params.h>
 #include <sound/rawmidi.h>
 
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
 #include "../iso-resources.h"
 #include "../lib.h"
 #include "dice-interface.h"
diff --git a/sound/firewire/digi00x/Makefile b/sound/firewire/digi00x/Makefile
new file mode 100644 (file)
index 0000000..1123e68
--- /dev/null
@@ -0,0 +1,4 @@
+snd-firewire-digi00x-objs := amdtp-dot.o digi00x-stream.o digi00x-proc.o \
+                            digi00x-pcm.o digi00x-hwdep.o \
+                            digi00x-transaction.o digi00x-midi.o digi00x.o
+obj-$(CONFIG_SND_FIREWIRE_DIGI00X) += snd-firewire-digi00x.o
diff --git a/sound/firewire/digi00x/amdtp-dot.c b/sound/firewire/digi00x/amdtp-dot.c
new file mode 100644 (file)
index 0000000..b02a5e8
--- /dev/null
@@ -0,0 +1,442 @@
+/*
+ * amdtp-dot.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ * Copyright (C) 2012 Robin Gareus <robin@gareus.org>
+ * Copyright (C) 2012 Damien Zammit <damien@zamaudio.com>
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/pcm.h>
+#include "digi00x.h"
+
+#define CIP_FMT_AM             0x10
+
+/* 'Clock-based rate control mode' is just supported. */
+#define AMDTP_FDF_AM824                0x00
+
+/*
+ * Nominally 3125 bytes/second, but the MIDI port's clock might be
+ * 1% too slow, and the bus clock 100 ppm too fast.
+ */
+#define MIDI_BYTES_PER_SECOND  3093
+
+/*
+ * Several devices look only at the first eight data blocks.
+ * In any case, this is more than enough for the MIDI data rate.
+ */
+#define MAX_MIDI_RX_BLOCKS     8
+
+/*
+ * The double-oh-three algorithm was discovered by Robin Gareus and Damien
+ * Zammit in 2012, with reverse-engineering for Digi 003 Rack.
+ */
+struct dot_state {
+       u8 carry;
+       u8 idx;
+       unsigned int off;
+};
+
+struct amdtp_dot {
+       unsigned int pcm_channels;
+       struct dot_state state;
+
+       unsigned int midi_ports;
+       /* 2 = MAX(DOT_MIDI_IN_PORTS, DOT_MIDI_OUT_PORTS) */
+       struct snd_rawmidi_substream *midi[2];
+       int midi_fifo_used[2];
+       int midi_fifo_limit;
+
+       void (*transfer_samples)(struct amdtp_stream *s,
+                                struct snd_pcm_substream *pcm,
+                                __be32 *buffer, unsigned int frames);
+};
+
+/*
+ * double-oh-three look up table
+ *
+ * @param idx index byte (audio-sample data) 0x00..0xff
+ * @param off channel offset shift
+ * @return salt to XOR with given data
+ */
+#define BYTE_PER_SAMPLE (4)
+#define MAGIC_DOT_BYTE (2)
+#define MAGIC_BYTE_OFF(x) (((x) * BYTE_PER_SAMPLE) + MAGIC_DOT_BYTE)
+static const u8 dot_scrt(const u8 idx, const unsigned int off)
+{
+       /*
+        * the length of the added pattern only depends on the lower nibble
+        * of the last non-zero data
+        */
+       static const u8 len[16] = {0, 1, 3, 5, 7, 9, 11, 13, 14,
+                                  12, 10, 8, 6, 4, 2, 0};
+
+       /*
+        * the lower nibble of the salt. Interleaved sequence.
+        * this is walked backwards according to len[]
+        */
+       static const u8 nib[15] = {0x8, 0x7, 0x9, 0x6, 0xa, 0x5, 0xb, 0x4,
+                                  0xc, 0x3, 0xd, 0x2, 0xe, 0x1, 0xf};
+
+       /* circular list for the salt's hi nibble. */
+       static const u8 hir[15] = {0x0, 0x6, 0xf, 0x8, 0x7, 0x5, 0x3, 0x4,
+                                  0xc, 0xd, 0xe, 0x1, 0x2, 0xb, 0xa};
+
+       /*
+        * start offset for upper nibble mapping.
+        * note: 9 is /special/. In the case where the high nibble == 0x9,
+        * hir[] is not used and - coincidentally - the salt's hi nibble is
+        * 0x09 regardless of the offset.
+        */
+       static const u8 hio[16] = {0, 11, 12, 6, 7, 5, 1, 4,
+                                  3, 0x00, 14, 13, 8, 9, 10, 2};
+
+       const u8 ln = idx & 0xf;
+       const u8 hn = (idx >> 4) & 0xf;
+       const u8 hr = (hn == 0x9) ? 0x9 : hir[(hio[hn] + off) % 15];
+
+       if (len[ln] < off)
+               return 0x00;
+
+       return ((nib[14 + off - len[ln]]) | (hr << 4));
+}
+
+static void dot_encode_step(struct dot_state *state, __be32 *const buffer)
+{
+       u8 * const data = (u8 *) buffer;
+
+       if (data[MAGIC_DOT_BYTE] != 0x00) {
+               state->off = 0;
+               state->idx = data[MAGIC_DOT_BYTE] ^ state->carry;
+       }
+       data[MAGIC_DOT_BYTE] ^= state->carry;
+       state->carry = dot_scrt(state->idx, ++(state->off));
+}
+
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+                            unsigned int pcm_channels)
+{
+       struct amdtp_dot *p = s->protocol;
+       int err;
+
+       if (amdtp_stream_running(s))
+               return -EBUSY;
+
+       /*
+        * A first data channel is for MIDI conformant data channel, the rest is
+        * Multi Bit Linear Audio data channel.
+        */
+       err = amdtp_stream_set_parameters(s, rate, pcm_channels + 1);
+       if (err < 0)
+               return err;
+
+       s->fdf = AMDTP_FDF_AM824 | s->sfc;
+
+       p->pcm_channels = pcm_channels;
+
+       if (s->direction == AMDTP_IN_STREAM)
+               p->midi_ports = DOT_MIDI_IN_PORTS;
+       else
+               p->midi_ports = DOT_MIDI_OUT_PORTS;
+
+       /*
+        * We do not know the actual MIDI FIFO size of most devices.  Just
+        * assume two bytes, i.e., one byte can be received over the bus while
+        * the previous one is transmitted over MIDI.
+        * (The value here is adjusted for midi_ratelimit_per_packet().)
+        */
+       p->midi_fifo_limit = rate - MIDI_BYTES_PER_SECOND * s->syt_interval + 1;
+
+       return 0;
+}
+
+static void write_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+                         __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_dot *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       const u32 *src;
+
+       channels = p->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       buffer++;
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       buffer[c] = cpu_to_be32((*src >> 8) | 0x40000000);
+                       dot_encode_step(&p->state, &buffer[c]);
+                       src++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+                         __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_dot *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       const u16 *src;
+
+       channels = p->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       buffer++;
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       buffer[c] = cpu_to_be32((*src << 8) | 0x40000000);
+                       dot_encode_step(&p->state, &buffer[c]);
+                       src++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s, struct snd_pcm_substream *pcm,
+                        __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_dot *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       u32 *dst;
+
+       channels = p->pcm_channels;
+       dst  = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       buffer++;
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       *dst = be32_to_cpu(buffer[c]) << 8;
+                       dst++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       dst = (void *)runtime->dma_area;
+       }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+                             unsigned int data_blocks)
+{
+       struct amdtp_dot *p = s->protocol;
+       unsigned int channels, i, c;
+
+       channels = p->pcm_channels;
+
+       buffer++;
+       for (i = 0; i < data_blocks; ++i) {
+               for (c = 0; c < channels; ++c)
+                       buffer[c] = cpu_to_be32(0x40000000);
+               buffer += s->data_block_quadlets;
+       }
+}
+
+static bool midi_ratelimit_per_packet(struct amdtp_stream *s, unsigned int port)
+{
+       struct amdtp_dot *p = s->protocol;
+       int used;
+
+       used = p->midi_fifo_used[port];
+       if (used == 0)
+               return true;
+
+       used -= MIDI_BYTES_PER_SECOND * s->syt_interval;
+       used = max(used, 0);
+       p->midi_fifo_used[port] = used;
+
+       return used < p->midi_fifo_limit;
+}
+
+static inline void midi_use_bytes(struct amdtp_stream *s,
+                                 unsigned int port, unsigned int count)
+{
+       struct amdtp_dot *p = s->protocol;
+
+       p->midi_fifo_used[port] += amdtp_rate_table[s->sfc] * count;
+}
+
+static void write_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+                               unsigned int data_blocks)
+{
+       struct amdtp_dot *p = s->protocol;
+       unsigned int f, port;
+       int len;
+       u8 *b;
+
+       for (f = 0; f < data_blocks; f++) {
+               port = (s->data_block_counter + f) % 8;
+               b = (u8 *)&buffer[0];
+
+               len = 0;
+               if (port < p->midi_ports &&
+                   midi_ratelimit_per_packet(s, port) &&
+                   p->midi[port] != NULL)
+                       len = snd_rawmidi_transmit(p->midi[port], b + 1, 2);
+
+               if (len > 0) {
+                       b[3] = (0x10 << port) | len;
+                       midi_use_bytes(s, port, len);
+               } else {
+                       b[1] = 0;
+                       b[2] = 0;
+                       b[3] = 0;
+               }
+               b[0] = 0x80;
+
+               buffer += s->data_block_quadlets;
+       }
+}
+
+static void read_midi_messages(struct amdtp_stream *s, __be32 *buffer,
+                              unsigned int data_blocks)
+{
+       struct amdtp_dot *p = s->protocol;
+       unsigned int f, port, len;
+       u8 *b;
+
+       for (f = 0; f < data_blocks; f++) {
+               b = (u8 *)&buffer[0];
+               port = b[3] >> 4;
+               len = b[3] & 0x0f;
+
+               if (port < p->midi_ports && p->midi[port] && len > 0)
+                       snd_rawmidi_receive(p->midi[port], b + 1, len);
+
+               buffer += s->data_block_quadlets;
+       }
+}
+
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                    struct snd_pcm_runtime *runtime)
+{
+       int err;
+
+       /* This protocol delivers 24 bit data in 32bit data channel. */
+       err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+       if (err < 0)
+               return err;
+
+       return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+       struct amdtp_dot *p = s->protocol;
+
+       if (WARN_ON(amdtp_stream_pcm_running(s)))
+               return;
+
+       switch (format) {
+       default:
+               WARN_ON(1);
+               /* fall through */
+       case SNDRV_PCM_FORMAT_S16:
+               if (s->direction == AMDTP_OUT_STREAM) {
+                       p->transfer_samples = write_pcm_s16;
+                       break;
+               }
+               WARN_ON(1);
+               /* fall through */
+       case SNDRV_PCM_FORMAT_S32:
+               if (s->direction == AMDTP_OUT_STREAM)
+                       p->transfer_samples = write_pcm_s32;
+               else
+                       p->transfer_samples = read_pcm_s32;
+               break;
+       }
+}
+
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+                         struct snd_rawmidi_substream *midi)
+{
+       struct amdtp_dot *p = s->protocol;
+
+       if (port < p->midi_ports)
+               ACCESS_ONCE(p->midi[port]) = midi;
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
+                                          __be32 *buffer,
+                                          unsigned int data_blocks,
+                                          unsigned int *syt)
+{
+       struct amdtp_dot *p = (struct amdtp_dot *)s->protocol;
+       struct snd_pcm_substream *pcm;
+       unsigned int pcm_frames;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm) {
+               p->transfer_samples(s, pcm, buffer, data_blocks);
+               pcm_frames = data_blocks;
+       } else {
+               pcm_frames = 0;
+       }
+
+       read_midi_messages(s, buffer, data_blocks);
+
+       return pcm_frames;
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
+                                          __be32 *buffer,
+                                          unsigned int data_blocks,
+                                          unsigned int *syt)
+{
+       struct amdtp_dot *p = (struct amdtp_dot *)s->protocol;
+       struct snd_pcm_substream *pcm;
+       unsigned int pcm_frames;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm) {
+               p->transfer_samples(s, pcm, buffer, data_blocks);
+               pcm_frames = data_blocks;
+       } else {
+               write_pcm_silence(s, buffer, data_blocks);
+               pcm_frames = 0;
+       }
+
+       write_midi_messages(s, buffer, data_blocks);
+
+       return pcm_frames;
+}
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+                enum amdtp_stream_direction dir)
+{
+       amdtp_stream_process_data_blocks_t process_data_blocks;
+       enum cip_flags flags;
+
+       /* Use different mode between incoming/outgoing. */
+       if (dir == AMDTP_IN_STREAM) {
+               flags = CIP_NONBLOCKING | CIP_SKIP_INIT_DBC_CHECK;
+               process_data_blocks = process_tx_data_blocks;
+       } else {
+               flags = CIP_BLOCKING;
+               process_data_blocks = process_rx_data_blocks;
+       }
+
+       return amdtp_stream_init(s, unit, dir, flags, CIP_FMT_AM,
+                                process_data_blocks, sizeof(struct amdtp_dot));
+}
+
+void amdtp_dot_reset(struct amdtp_stream *s)
+{
+       struct amdtp_dot *p = s->protocol;
+
+       p->state.carry = 0x00;
+       p->state.idx = 0x00;
+       p->state.off = 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-hwdep.c b/sound/firewire/digi00x/digi00x-hwdep.c
new file mode 100644 (file)
index 0000000..f188e47
--- /dev/null
@@ -0,0 +1,200 @@
+/*
+ * digi00x-hwdep.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ * 4.get asynchronous messaging
+ */
+
+#include "digi00x.h"
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf,  long count,
+                      loff_t *offset)
+{
+       struct snd_dg00x *dg00x = hwdep->private_data;
+       DEFINE_WAIT(wait);
+       union snd_firewire_event event;
+
+       spin_lock_irq(&dg00x->lock);
+
+       while (!dg00x->dev_lock_changed && dg00x->msg == 0) {
+               prepare_to_wait(&dg00x->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+               spin_unlock_irq(&dg00x->lock);
+               schedule();
+               finish_wait(&dg00x->hwdep_wait, &wait);
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+               spin_lock_irq(&dg00x->lock);
+       }
+
+       memset(&event, 0, sizeof(event));
+       if (dg00x->dev_lock_changed) {
+               event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+               event.lock_status.status = (dg00x->dev_lock_count > 0);
+               dg00x->dev_lock_changed = false;
+
+               count = min_t(long, count, sizeof(event.lock_status));
+       } else {
+               event.digi00x_message.type =
+                                       SNDRV_FIREWIRE_EVENT_DIGI00X_MESSAGE;
+               event.digi00x_message.message = dg00x->msg;
+               dg00x->msg = 0;
+
+               count = min_t(long, count, sizeof(event.digi00x_message));
+       }
+
+       spin_unlock_irq(&dg00x->lock);
+
+       if (copy_to_user(buf, &event, count))
+               return -EFAULT;
+
+       return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+                              poll_table *wait)
+{
+       struct snd_dg00x *dg00x = hwdep->private_data;
+       unsigned int events;
+
+       poll_wait(file, &dg00x->hwdep_wait, wait);
+
+       spin_lock_irq(&dg00x->lock);
+       if (dg00x->dev_lock_changed || dg00x->msg)
+               events = POLLIN | POLLRDNORM;
+       else
+               events = 0;
+       spin_unlock_irq(&dg00x->lock);
+
+       return events;
+}
+
+static int hwdep_get_info(struct snd_dg00x *dg00x, void __user *arg)
+{
+       struct fw_device *dev = fw_parent_device(dg00x->unit);
+       struct snd_firewire_get_info info;
+
+       memset(&info, 0, sizeof(info));
+       info.type = SNDRV_FIREWIRE_TYPE_DIGI00X;
+       info.card = dev->card->index;
+       *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+       *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+       strlcpy(info.device_name, dev_name(&dev->device),
+               sizeof(info.device_name));
+
+       if (copy_to_user(arg, &info, sizeof(info)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int hwdep_lock(struct snd_dg00x *dg00x)
+{
+       int err;
+
+       spin_lock_irq(&dg00x->lock);
+
+       if (dg00x->dev_lock_count == 0) {
+               dg00x->dev_lock_count = -1;
+               err = 0;
+       } else {
+               err = -EBUSY;
+       }
+
+       spin_unlock_irq(&dg00x->lock);
+
+       return err;
+}
+
+static int hwdep_unlock(struct snd_dg00x *dg00x)
+{
+       int err;
+
+       spin_lock_irq(&dg00x->lock);
+
+       if (dg00x->dev_lock_count == -1) {
+               dg00x->dev_lock_count = 0;
+               err = 0;
+       } else {
+               err = -EBADFD;
+       }
+
+       spin_unlock_irq(&dg00x->lock);
+
+       return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+       struct snd_dg00x *dg00x = hwdep->private_data;
+
+       spin_lock_irq(&dg00x->lock);
+       if (dg00x->dev_lock_count == -1)
+               dg00x->dev_lock_count = 0;
+       spin_unlock_irq(&dg00x->lock);
+
+       return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+           unsigned int cmd, unsigned long arg)
+{
+       struct snd_dg00x *dg00x = hwdep->private_data;
+
+       switch (cmd) {
+       case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+               return hwdep_get_info(dg00x, (void __user *)arg);
+       case SNDRV_FIREWIRE_IOCTL_LOCK:
+               return hwdep_lock(dg00x);
+       case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+               return hwdep_unlock(dg00x);
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+                             unsigned int cmd, unsigned long arg)
+{
+       return hwdep_ioctl(hwdep, file, cmd,
+                          (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+       .read           = hwdep_read,
+       .release        = hwdep_release,
+       .poll           = hwdep_poll,
+       .ioctl          = hwdep_ioctl,
+       .ioctl_compat   = hwdep_compat_ioctl,
+};
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x)
+{
+       struct snd_hwdep *hwdep;
+       int err;
+
+       err = snd_hwdep_new(dg00x->card, "Digi00x", 0, &hwdep);
+       if (err < 0)
+               return err;
+
+       strcpy(hwdep->name, "Digi00x");
+       hwdep->iface = SNDRV_HWDEP_IFACE_FW_DIGI00X;
+       hwdep->ops = hwdep_ops;
+       hwdep->private_data = dg00x;
+       hwdep->exclusive = true;
+
+       return err;
+}
diff --git a/sound/firewire/digi00x/digi00x-midi.c b/sound/firewire/digi00x/digi00x-midi.c
new file mode 100644 (file)
index 0000000..1a72a38
--- /dev/null
@@ -0,0 +1,223 @@
+/*
+ * digi00x-midi.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int midi_phys_open(struct snd_rawmidi_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->rmidi->private_data;
+       int err;
+
+       err = snd_dg00x_stream_lock_try(dg00x);
+       if (err < 0)
+               return err;
+
+       mutex_lock(&dg00x->mutex);
+       dg00x->substreams_counter++;
+       err = snd_dg00x_stream_start_duplex(dg00x, 0);
+       mutex_unlock(&dg00x->mutex);
+       if (err < 0)
+               snd_dg00x_stream_lock_release(dg00x);
+
+       return err;
+}
+
+static int midi_phys_close(struct snd_rawmidi_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->rmidi->private_data;
+
+       mutex_lock(&dg00x->mutex);
+       dg00x->substreams_counter--;
+       snd_dg00x_stream_stop_duplex(dg00x);
+       mutex_unlock(&dg00x->mutex);
+
+       snd_dg00x_stream_lock_release(dg00x);
+       return 0;
+}
+
+static void midi_phys_capture_trigger(struct snd_rawmidi_substream *substream,
+                                     int up)
+{
+       struct snd_dg00x *dg00x = substream->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dg00x->lock, flags);
+
+       if (up)
+               amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
+                                      substream);
+       else
+               amdtp_dot_midi_trigger(&dg00x->tx_stream, substream->number,
+                                      NULL);
+
+       spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static void midi_phys_playback_trigger(struct snd_rawmidi_substream *substream,
+                                      int up)
+{
+       struct snd_dg00x *dg00x = substream->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dg00x->lock, flags);
+
+       if (up)
+               amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
+                                      substream);
+       else
+               amdtp_dot_midi_trigger(&dg00x->rx_stream, substream->number,
+                                      NULL);
+
+       spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_phys_capture_ops = {
+       .open           = midi_phys_open,
+       .close          = midi_phys_close,
+       .trigger        = midi_phys_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_phys_playback_ops = {
+       .open           = midi_phys_open,
+       .close          = midi_phys_close,
+       .trigger        = midi_phys_playback_trigger,
+};
+
+static int midi_ctl_open(struct snd_rawmidi_substream *substream)
+{
+       /* Do nothing. */
+       return 0;
+}
+
+static int midi_ctl_capture_close(struct snd_rawmidi_substream *substream)
+{
+       /* Do nothing. */
+       return 0;
+}
+
+static int midi_ctl_playback_close(struct snd_rawmidi_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->rmidi->private_data;
+
+       snd_fw_async_midi_port_finish(&dg00x->out_control);
+
+       return 0;
+}
+
+static void midi_ctl_capture_trigger(struct snd_rawmidi_substream *substream,
+                                    int up)
+{
+       struct snd_dg00x *dg00x = substream->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dg00x->lock, flags);
+
+       if (up)
+               dg00x->in_control = substream;
+       else
+               dg00x->in_control = NULL;
+
+       spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static void midi_ctl_playback_trigger(struct snd_rawmidi_substream *substream,
+                                     int up)
+{
+       struct snd_dg00x *dg00x = substream->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&dg00x->lock, flags);
+
+       if (up)
+               snd_fw_async_midi_port_run(&dg00x->out_control, substream);
+
+       spin_unlock_irqrestore(&dg00x->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_ctl_capture_ops = {
+       .open           = midi_ctl_open,
+       .close          = midi_ctl_capture_close,
+       .trigger        = midi_ctl_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_ctl_playback_ops = {
+       .open           = midi_ctl_open,
+       .close          = midi_ctl_playback_close,
+       .trigger        = midi_ctl_playback_trigger,
+};
+
+static void set_midi_substream_names(struct snd_dg00x *dg00x,
+                                    struct snd_rawmidi_str *str,
+                                    bool is_ctl)
+{
+       struct snd_rawmidi_substream *subs;
+
+       list_for_each_entry(subs, &str->substreams, list) {
+               if (!is_ctl)
+                       snprintf(subs->name, sizeof(subs->name),
+                                "%s MIDI %d",
+                                dg00x->card->shortname, subs->number + 1);
+               else
+                       /* This port is for asynchronous transaction. */
+                       snprintf(subs->name, sizeof(subs->name),
+                                "%s control",
+                                dg00x->card->shortname);
+       }
+}
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x)
+{
+       struct snd_rawmidi *rmidi[2];
+       struct snd_rawmidi_str *str;
+       unsigned int i;
+       int err;
+
+       /* Add physical midi ports. */
+       err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 0,
+                       DOT_MIDI_OUT_PORTS, DOT_MIDI_IN_PORTS, &rmidi[0]);
+       if (err < 0)
+               return err;
+
+       snprintf(rmidi[0]->name, sizeof(rmidi[0]->name),
+                "%s MIDI", dg00x->card->shortname);
+
+       snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_INPUT,
+                           &midi_phys_capture_ops);
+       snd_rawmidi_set_ops(rmidi[0], SNDRV_RAWMIDI_STREAM_OUTPUT,
+                           &midi_phys_playback_ops);
+
+       /* Add a pair of control midi ports. */
+       err = snd_rawmidi_new(dg00x->card, dg00x->card->driver, 1,
+                             1, 1, &rmidi[1]);
+       if (err < 0)
+               return err;
+
+       snprintf(rmidi[1]->name, sizeof(rmidi[1]->name),
+                "%s control", dg00x->card->shortname);
+
+       snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_INPUT,
+                           &midi_ctl_capture_ops);
+       snd_rawmidi_set_ops(rmidi[1], SNDRV_RAWMIDI_STREAM_OUTPUT,
+                           &midi_ctl_playback_ops);
+
+       for (i = 0; i < ARRAY_SIZE(rmidi); i++) {
+               rmidi[i]->private_data = dg00x;
+
+               rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+               str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+               set_midi_substream_names(dg00x, str, i);
+
+               rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+               str = &rmidi[i]->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+               set_midi_substream_names(dg00x, str, i);
+
+               rmidi[i]->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+       }
+
+       return 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-pcm.c b/sound/firewire/digi00x/digi00x-pcm.c
new file mode 100644 (file)
index 0000000..cac28f7
--- /dev/null
@@ -0,0 +1,373 @@
+/*
+ * digi00x-pcm.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int hw_rule_rate(struct snd_pcm_hw_params *params,
+                       struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *r =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
+       const struct snd_interval *c =
+               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       struct snd_interval t = {
+               .min = UINT_MAX, .max = 0, .integer = 1,
+       };
+       unsigned int i;
+
+       for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+               if (!snd_interval_test(c,
+                                      snd_dg00x_stream_pcm_channels[i]))
+                       continue;
+
+               t.min = min(t.min, snd_dg00x_stream_rates[i]);
+               t.max = max(t.max, snd_dg00x_stream_rates[i]);
+       }
+
+       return snd_interval_refine(r, &t);
+}
+
+static int hw_rule_channels(struct snd_pcm_hw_params *params,
+                           struct snd_pcm_hw_rule *rule)
+{
+       struct snd_interval *c =
+               hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
+       const struct snd_interval *r =
+               hw_param_interval_c(params, SNDRV_PCM_HW_PARAM_RATE);
+       struct snd_interval t = {
+               .min = UINT_MAX, .max = 0, .integer = 1,
+       };
+       unsigned int i;
+
+       for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+               if (!snd_interval_test(r, snd_dg00x_stream_rates[i]))
+                       continue;
+
+               t.min = min(t.min, snd_dg00x_stream_pcm_channels[i]);
+               t.max = max(t.max, snd_dg00x_stream_pcm_channels[i]);
+       }
+
+       return snd_interval_refine(c, &t);
+}
+
+static int pcm_init_hw_params(struct snd_dg00x *dg00x,
+                             struct snd_pcm_substream *substream)
+{
+       static const struct snd_pcm_hardware hardware = {
+               .info = SNDRV_PCM_INFO_BATCH |
+                       SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                       SNDRV_PCM_INFO_INTERLEAVED |
+                       SNDRV_PCM_INFO_JOINT_DUPLEX |
+                       SNDRV_PCM_INFO_MMAP |
+                       SNDRV_PCM_INFO_MMAP_VALID,
+               .rates = SNDRV_PCM_RATE_44100 |
+                        SNDRV_PCM_RATE_48000 |
+                        SNDRV_PCM_RATE_88200 |
+                        SNDRV_PCM_RATE_96000,
+               .rate_min = 44100,
+               .rate_max = 96000,
+               .channels_min = 10,
+               .channels_max = 18,
+               .period_bytes_min = 4 * 18,
+               .period_bytes_max = 4 * 18 * 2048,
+               .buffer_bytes_max = 4 * 18 * 2048 * 2,
+               .periods_min = 2,
+               .periods_max = UINT_MAX,
+       };
+       struct amdtp_stream *s;
+       int err;
+
+       substream->runtime->hw = hardware;
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+               s = &dg00x->tx_stream;
+       } else {
+               substream->runtime->hw.formats = SNDRV_PCM_FMTBIT_S16 |
+                                                SNDRV_PCM_FMTBIT_S32;
+               s = &dg00x->rx_stream;
+       }
+
+       err = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS,
+                                 hw_rule_channels, NULL,
+                                 SNDRV_PCM_HW_PARAM_RATE, -1);
+       if (err < 0)
+               return err;
+
+       err = snd_pcm_hw_rule_add(substream->runtime, 0,
+                                 SNDRV_PCM_HW_PARAM_RATE,
+                                 hw_rule_rate, NULL,
+                                 SNDRV_PCM_HW_PARAM_CHANNELS, -1);
+       if (err < 0)
+               return err;
+
+       return amdtp_dot_add_pcm_hw_constraints(s, substream->runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+       enum snd_dg00x_clock clock;
+       bool detect;
+       unsigned int rate;
+       int err;
+
+       err = snd_dg00x_stream_lock_try(dg00x);
+       if (err < 0)
+               goto end;
+
+       err = pcm_init_hw_params(dg00x, substream);
+       if (err < 0)
+               goto err_locked;
+
+       /* Check current clock source. */
+       err = snd_dg00x_stream_get_clock(dg00x, &clock);
+       if (err < 0)
+               goto err_locked;
+       if (clock != SND_DG00X_CLOCK_INTERNAL) {
+               err = snd_dg00x_stream_check_external_clock(dg00x, &detect);
+               if (err < 0)
+                       goto err_locked;
+               if (!detect) {
+                       err = -EBUSY;
+                       goto err_locked;
+               }
+       }
+
+       if ((clock != SND_DG00X_CLOCK_INTERNAL) ||
+           amdtp_stream_pcm_running(&dg00x->rx_stream) ||
+           amdtp_stream_pcm_running(&dg00x->tx_stream)) {
+               err = snd_dg00x_stream_get_external_rate(dg00x, &rate);
+               if (err < 0)
+                       goto err_locked;
+               substream->runtime->hw.rate_min = rate;
+               substream->runtime->hw.rate_max = rate;
+       }
+
+       snd_pcm_set_sync(substream);
+end:
+       return err;
+err_locked:
+       snd_dg00x_stream_lock_release(dg00x);
+       return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+
+       snd_dg00x_stream_lock_release(dg00x);
+
+       return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+       int err;
+
+       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                              params_buffer_bytes(hw_params));
+       if (err < 0)
+               return err;
+
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&dg00x->mutex);
+               dg00x->substreams_counter++;
+               mutex_unlock(&dg00x->mutex);
+       }
+
+       amdtp_dot_set_pcm_format(&dg00x->tx_stream, params_format(hw_params));
+
+       return 0;
+}
+
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+       int err;
+
+       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                              params_buffer_bytes(hw_params));
+       if (err < 0)
+               return err;
+
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&dg00x->mutex);
+               dg00x->substreams_counter++;
+               mutex_unlock(&dg00x->mutex);
+       }
+
+       amdtp_dot_set_pcm_format(&dg00x->rx_stream, params_format(hw_params));
+
+       return 0;
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+
+       mutex_lock(&dg00x->mutex);
+
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+               dg00x->substreams_counter--;
+
+       snd_dg00x_stream_stop_duplex(dg00x);
+
+       mutex_unlock(&dg00x->mutex);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+
+       mutex_lock(&dg00x->mutex);
+
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+               dg00x->substreams_counter--;
+
+       snd_dg00x_stream_stop_duplex(dg00x);
+
+       mutex_unlock(&dg00x->mutex);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       mutex_lock(&dg00x->mutex);
+
+       err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+       if (err >= 0)
+               amdtp_stream_pcm_prepare(&dg00x->tx_stream);
+
+       mutex_unlock(&dg00x->mutex);
+
+       return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       mutex_lock(&dg00x->mutex);
+
+       err = snd_dg00x_stream_start_duplex(dg00x, runtime->rate);
+       if (err >= 0) {
+               amdtp_stream_pcm_prepare(&dg00x->rx_stream);
+               amdtp_dot_reset(&dg00x->rx_stream);
+       }
+
+       mutex_unlock(&dg00x->mutex);
+
+       return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               amdtp_stream_pcm_trigger(&dg00x->tx_stream, substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               amdtp_stream_pcm_trigger(&dg00x->tx_stream, NULL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_dg00x *dg00x = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               amdtp_stream_pcm_trigger(&dg00x->rx_stream, substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               amdtp_stream_pcm_trigger(&dg00x->rx_stream, NULL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+       struct snd_dg00x *dg00x = sbstrm->private_data;
+
+       return amdtp_stream_pcm_pointer(&dg00x->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+       struct snd_dg00x *dg00x = sbstrm->private_data;
+
+       return amdtp_stream_pcm_pointer(&dg00x->rx_stream);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+       .open           = pcm_open,
+       .close          = pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = pcm_capture_hw_params,
+       .hw_free        = pcm_capture_hw_free,
+       .prepare        = pcm_capture_prepare,
+       .trigger        = pcm_capture_trigger,
+       .pointer        = pcm_capture_pointer,
+       .page           = snd_pcm_lib_get_vmalloc_page,
+};
+
+static struct snd_pcm_ops pcm_playback_ops = {
+       .open           = pcm_open,
+       .close          = pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = pcm_playback_hw_params,
+       .hw_free        = pcm_playback_hw_free,
+       .prepare        = pcm_playback_prepare,
+       .trigger        = pcm_playback_trigger,
+       .pointer        = pcm_playback_pointer,
+       .page           = snd_pcm_lib_get_vmalloc_page,
+       .mmap           = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x)
+{
+       struct snd_pcm *pcm;
+       int err;
+
+       err = snd_pcm_new(dg00x->card, dg00x->card->driver, 0, 1, 1, &pcm);
+       if (err < 0)
+               return err;
+
+       pcm->private_data = dg00x;
+       snprintf(pcm->name, sizeof(pcm->name),
+                "%s PCM", dg00x->card->shortname);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+
+       return 0;
+}
diff --git a/sound/firewire/digi00x/digi00x-proc.c b/sound/firewire/digi00x/digi00x-proc.c
new file mode 100644 (file)
index 0000000..a1d601f
--- /dev/null
@@ -0,0 +1,99 @@
+/*
+ * digi00x-proc.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+static int get_optical_iface_mode(struct snd_dg00x *dg00x,
+                                 enum snd_dg00x_optical_mode *mode)
+{
+       __be32 data;
+       int err;
+
+       err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_OPT_IFACE_MODE,
+                                &data, sizeof(data), 0);
+       if (err >= 0)
+               *mode = be32_to_cpu(data) & 0x01;
+
+       return err;
+}
+
+static void proc_read_clock(struct snd_info_entry *entry,
+                           struct snd_info_buffer *buf)
+{
+       static const char *const source_name[] = {
+               [SND_DG00X_CLOCK_INTERNAL] = "internal",
+               [SND_DG00X_CLOCK_SPDIF] = "s/pdif",
+               [SND_DG00X_CLOCK_ADAT] = "adat",
+               [SND_DG00X_CLOCK_WORD] = "word clock",
+       };
+       static const char *const optical_name[] = {
+               [SND_DG00X_OPT_IFACE_MODE_ADAT] = "adat",
+               [SND_DG00X_OPT_IFACE_MODE_SPDIF] = "s/pdif",
+       };
+       struct snd_dg00x *dg00x = entry->private_data;
+       enum snd_dg00x_optical_mode mode;
+       unsigned int rate;
+       enum snd_dg00x_clock clock;
+       bool detect;
+
+       if (get_optical_iface_mode(dg00x, &mode) < 0)
+               return;
+       if (snd_dg00x_stream_get_local_rate(dg00x, &rate) < 0)
+               return;
+       if (snd_dg00x_stream_get_clock(dg00x, &clock) < 0)
+               return;
+
+       snd_iprintf(buf, "Optical mode: %s\n", optical_name[mode]);
+       snd_iprintf(buf, "Sampling Rate: %d\n", rate);
+       snd_iprintf(buf, "Clock Source: %s\n", source_name[clock]);
+
+       if (clock == SND_DG00X_CLOCK_INTERNAL)
+               return;
+
+       if (snd_dg00x_stream_check_external_clock(dg00x, &detect) < 0)
+               return;
+       snd_iprintf(buf, "External source: %s\n", detect ? "detected" : "not");
+       if (!detect)
+               return;
+
+       if (snd_dg00x_stream_get_external_rate(dg00x, &rate) >= 0)
+               snd_iprintf(buf, "External sampling rate: %d\n", rate);
+}
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x)
+{
+       struct snd_info_entry *root, *entry;
+
+       /*
+        * All nodes are automatically removed at snd_card_disconnect(),
+        * by following to link list.
+        */
+       root = snd_info_create_card_entry(dg00x->card, "firewire",
+                                         dg00x->card->proc_root);
+       if (root == NULL)
+               return;
+
+       root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+       if (snd_info_register(root) < 0) {
+               snd_info_free_entry(root);
+               return;
+       }
+
+       entry = snd_info_create_card_entry(dg00x->card, "clock", root);
+       if (entry == NULL) {
+               snd_info_free_entry(root);
+               return;
+       }
+
+       snd_info_set_text_ops(entry, dg00x, proc_read_clock);
+       if (snd_info_register(entry) < 0) {
+               snd_info_free_entry(entry);
+               snd_info_free_entry(root);
+       }
+}
diff --git a/sound/firewire/digi00x/digi00x-stream.c b/sound/firewire/digi00x/digi00x-stream.c
new file mode 100644 (file)
index 0000000..4d3b4eb
--- /dev/null
@@ -0,0 +1,422 @@
+/*
+ * digi00x-stream.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+#define CALLBACK_TIMEOUT 500
+
+const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT] = {
+       [SND_DG00X_RATE_44100] = 44100,
+       [SND_DG00X_RATE_48000] = 48000,
+       [SND_DG00X_RATE_88200] = 88200,
+       [SND_DG00X_RATE_96000] = 96000,
+};
+
+/* Multi Bit Linear Audio data channels for each sampling transfer frequency. */
+const unsigned int
+snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT] = {
+       /* Analog/ADAT/SPDIF */
+       [SND_DG00X_RATE_44100] = (8 + 8 + 2),
+       [SND_DG00X_RATE_48000] = (8 + 8 + 2),
+       /* Analog/SPDIF */
+       [SND_DG00X_RATE_88200] = (8 + 2),
+       [SND_DG00X_RATE_96000] = (8 + 2),
+};
+
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x, unsigned int *rate)
+{
+       u32 data;
+       __be32 reg;
+       int err;
+
+       err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       data = be32_to_cpu(reg) & 0x0f;
+       if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+               *rate = snd_dg00x_stream_rates[data];
+       else
+               err = -EIO;
+
+       return err;
+}
+
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate)
+{
+       __be32 reg;
+       unsigned int i;
+
+       for (i = 0; i < ARRAY_SIZE(snd_dg00x_stream_rates); i++) {
+               if (rate == snd_dg00x_stream_rates[i])
+                       break;
+       }
+       if (i == ARRAY_SIZE(snd_dg00x_stream_rates))
+               return -EINVAL;
+
+       reg = cpu_to_be32(i);
+       return snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                 DG00X_ADDR_BASE + DG00X_OFFSET_LOCAL_RATE,
+                                 &reg, sizeof(reg), 0);
+}
+
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+                              enum snd_dg00x_clock *clock)
+{
+       __be32 reg;
+       int err;
+
+       err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_CLOCK_SOURCE,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       *clock = be32_to_cpu(reg) & 0x0f;
+       if (*clock >= SND_DG00X_CLOCK_COUNT)
+               err = -EIO;
+
+       return err;
+}
+
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x, bool *detect)
+{
+       __be32 reg;
+       int err;
+
+       err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_DETECT_EXTERNAL,
+                                &reg, sizeof(reg), 0);
+       if (err >= 0)
+               *detect = be32_to_cpu(reg) > 0;
+
+       return err;
+}
+
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+                                      unsigned int *rate)
+{
+       u32 data;
+       __be32 reg;
+       int err;
+
+       err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_EXTERNAL_RATE,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       data = be32_to_cpu(reg) & 0x0f;
+       if (data < ARRAY_SIZE(snd_dg00x_stream_rates))
+               *rate = snd_dg00x_stream_rates[data];
+       /* This means desync. */
+       else
+               err = -EBUSY;
+
+       return err;
+}
+
+static void finish_session(struct snd_dg00x *dg00x)
+{
+       __be32 data = cpu_to_be32(0x00000003);
+
+       snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_SET,
+                          &data, sizeof(data), 0);
+}
+
+static int begin_session(struct snd_dg00x *dg00x)
+{
+       __be32 data;
+       u32 curr;
+       int err;
+
+       err = snd_fw_transaction(dg00x->unit, TCODE_READ_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_STREAMING_STATE,
+                                &data, sizeof(data), 0);
+       if (err < 0)
+               goto error;
+       curr = be32_to_cpu(data);
+
+       if (curr == 0)
+               curr = 2;
+
+       curr--;
+       while (curr > 0) {
+               data = cpu_to_be32(curr);
+               err = snd_fw_transaction(dg00x->unit,
+                                        TCODE_WRITE_QUADLET_REQUEST,
+                                        DG00X_ADDR_BASE +
+                                        DG00X_OFFSET_STREAMING_SET,
+                                        &data, sizeof(data), 0);
+               if (err < 0)
+                       goto error;
+
+               msleep(20);
+               curr--;
+       }
+
+       return 0;
+error:
+       finish_session(dg00x);
+       return err;
+}
+
+static void release_resources(struct snd_dg00x *dg00x)
+{
+       __be32 data = 0;
+
+       /* Unregister isochronous channels for both direction. */
+       snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+                          &data, sizeof(data), 0);
+
+       /* Release isochronous resources. */
+       fw_iso_resources_free(&dg00x->tx_resources);
+       fw_iso_resources_free(&dg00x->rx_resources);
+}
+
+static int keep_resources(struct snd_dg00x *dg00x, unsigned int rate)
+{
+       unsigned int i;
+       __be32 data;
+       int err;
+
+       /* Check sampling rate. */
+       for (i = 0; i < SND_DG00X_RATE_COUNT; i++) {
+               if (snd_dg00x_stream_rates[i] == rate)
+                       break;
+       }
+       if (i == SND_DG00X_RATE_COUNT)
+               return -EINVAL;
+
+       /* Keep resources for out-stream. */
+       err = amdtp_dot_set_parameters(&dg00x->rx_stream, rate,
+                                      snd_dg00x_stream_pcm_channels[i]);
+       if (err < 0)
+               return err;
+       err = fw_iso_resources_allocate(&dg00x->rx_resources,
+                               amdtp_stream_get_max_payload(&dg00x->rx_stream),
+                               fw_parent_device(dg00x->unit)->max_speed);
+       if (err < 0)
+               return err;
+
+       /* Keep resources for in-stream. */
+       err = amdtp_dot_set_parameters(&dg00x->tx_stream, rate,
+                                      snd_dg00x_stream_pcm_channels[i]);
+       if (err < 0)
+               return err;
+       err = fw_iso_resources_allocate(&dg00x->tx_resources,
+                               amdtp_stream_get_max_payload(&dg00x->tx_stream),
+                               fw_parent_device(dg00x->unit)->max_speed);
+       if (err < 0)
+               goto error;
+
+       /* Register isochronous channels for both direction. */
+       data = cpu_to_be32((dg00x->tx_resources.channel << 16) |
+                          dg00x->rx_resources.channel);
+       err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_ISOC_CHANNELS,
+                                &data, sizeof(data), 0);
+       if (err < 0)
+               goto error;
+
+       return 0;
+error:
+       release_resources(dg00x);
+       return err;
+}
+
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x)
+{
+       int err;
+
+       /* For out-stream. */
+       err = fw_iso_resources_init(&dg00x->rx_resources, dg00x->unit);
+       if (err < 0)
+               goto error;
+       err = amdtp_dot_init(&dg00x->rx_stream, dg00x->unit, AMDTP_OUT_STREAM);
+       if (err < 0)
+               goto error;
+
+       /* For in-stream. */
+       err = fw_iso_resources_init(&dg00x->tx_resources, dg00x->unit);
+       if (err < 0)
+               goto error;
+       err = amdtp_dot_init(&dg00x->tx_stream, dg00x->unit, AMDTP_IN_STREAM);
+       if (err < 0)
+               goto error;
+
+       return 0;
+error:
+       snd_dg00x_stream_destroy_duplex(dg00x);
+       return err;
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x)
+{
+       amdtp_stream_destroy(&dg00x->rx_stream);
+       fw_iso_resources_destroy(&dg00x->rx_resources);
+
+       amdtp_stream_destroy(&dg00x->tx_stream);
+       fw_iso_resources_destroy(&dg00x->tx_resources);
+}
+
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate)
+{
+       unsigned int curr_rate;
+       int err = 0;
+
+       if (dg00x->substreams_counter == 0)
+               goto end;
+
+       /* Check current sampling rate. */
+       err = snd_dg00x_stream_get_local_rate(dg00x, &curr_rate);
+       if (err < 0)
+               goto error;
+       if (rate == 0)
+               rate = curr_rate;
+       if (curr_rate != rate ||
+           amdtp_streaming_error(&dg00x->tx_stream) ||
+           amdtp_streaming_error(&dg00x->rx_stream)) {
+               finish_session(dg00x);
+
+               amdtp_stream_stop(&dg00x->tx_stream);
+               amdtp_stream_stop(&dg00x->rx_stream);
+               release_resources(dg00x);
+       }
+
+       /*
+        * No packets are transmitted without receiving packets, reagardless of
+        * which source of clock is used.
+        */
+       if (!amdtp_stream_running(&dg00x->rx_stream)) {
+               err = snd_dg00x_stream_set_local_rate(dg00x, rate);
+               if (err < 0)
+                       goto error;
+
+               err = keep_resources(dg00x, rate);
+               if (err < 0)
+                       goto error;
+
+               err = begin_session(dg00x);
+               if (err < 0)
+                       goto error;
+
+               err = amdtp_stream_start(&dg00x->rx_stream,
+                               dg00x->rx_resources.channel,
+                               fw_parent_device(dg00x->unit)->max_speed);
+               if (err < 0)
+                       goto error;
+
+               if (!amdtp_stream_wait_callback(&dg00x->rx_stream,
+                                             CALLBACK_TIMEOUT)) {
+                       err = -ETIMEDOUT;
+                       goto error;
+               }
+       }
+
+       /*
+        * The value of SYT field in transmitted packets is always 0x0000. Thus,
+        * duplex streams with timestamp synchronization cannot be built.
+        */
+       if (!amdtp_stream_running(&dg00x->tx_stream)) {
+               err = amdtp_stream_start(&dg00x->tx_stream,
+                               dg00x->tx_resources.channel,
+                               fw_parent_device(dg00x->unit)->max_speed);
+               if (err < 0)
+                       goto error;
+
+               if (!amdtp_stream_wait_callback(&dg00x->tx_stream,
+                                             CALLBACK_TIMEOUT)) {
+                       err = -ETIMEDOUT;
+                       goto error;
+               }
+       }
+end:
+       return err;
+error:
+       finish_session(dg00x);
+
+       amdtp_stream_stop(&dg00x->tx_stream);
+       amdtp_stream_stop(&dg00x->rx_stream);
+       release_resources(dg00x);
+
+       return err;
+}
+
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x)
+{
+       if (dg00x->substreams_counter > 0)
+               return;
+
+       amdtp_stream_stop(&dg00x->tx_stream);
+       amdtp_stream_stop(&dg00x->rx_stream);
+       finish_session(dg00x);
+       release_resources(dg00x);
+
+       /*
+        * Just after finishing the session, the device may lost transmitting
+        * functionality for a short time.
+        */
+       msleep(50);
+}
+
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x)
+{
+       fw_iso_resources_update(&dg00x->tx_resources);
+       fw_iso_resources_update(&dg00x->rx_resources);
+
+       amdtp_stream_update(&dg00x->tx_stream);
+       amdtp_stream_update(&dg00x->rx_stream);
+}
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x)
+{
+       dg00x->dev_lock_changed = true;
+       wake_up(&dg00x->hwdep_wait);
+}
+
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x)
+{
+       int err;
+
+       spin_lock_irq(&dg00x->lock);
+
+       /* user land lock this */
+       if (dg00x->dev_lock_count < 0) {
+               err = -EBUSY;
+               goto end;
+       }
+
+       /* this is the first time */
+       if (dg00x->dev_lock_count++ == 0)
+               snd_dg00x_stream_lock_changed(dg00x);
+       err = 0;
+end:
+       spin_unlock_irq(&dg00x->lock);
+       return err;
+}
+
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x)
+{
+       spin_lock_irq(&dg00x->lock);
+
+       if (WARN_ON(dg00x->dev_lock_count <= 0))
+               goto end;
+       if (--dg00x->dev_lock_count == 0)
+               snd_dg00x_stream_lock_changed(dg00x);
+end:
+       spin_unlock_irq(&dg00x->lock);
+}
diff --git a/sound/firewire/digi00x/digi00x-transaction.c b/sound/firewire/digi00x/digi00x-transaction.c
new file mode 100644 (file)
index 0000000..554324d
--- /dev/null
@@ -0,0 +1,137 @@
+/*
+ * digi00x-transaction.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/asound.h>
+#include "digi00x.h"
+
+static int fill_midi_message(struct snd_rawmidi_substream *substream, u8 *buf)
+{
+       int bytes;
+
+       buf[0] = 0x80;
+       bytes = snd_rawmidi_transmit_peek(substream, buf + 1, 2);
+       if (bytes >= 0)
+               buf[3] = 0xc0 | bytes;
+
+       return bytes;
+}
+
+static void handle_midi_control(struct snd_dg00x *dg00x, __be32 *buf,
+                               unsigned int length)
+{
+       struct snd_rawmidi_substream *substream;
+       unsigned int i;
+       unsigned int len;
+       u8 *b;
+
+       substream = ACCESS_ONCE(dg00x->in_control);
+       if (substream == NULL)
+               return;
+
+       length /= 4;
+
+       for (i = 0; i < length; i++) {
+               b = (u8 *)&buf[i];
+               len = b[3] & 0xf;
+               if (len > 0)
+                       snd_rawmidi_receive(dg00x->in_control, b + 1, len);
+       }
+}
+
+static void handle_unknown_message(struct snd_dg00x *dg00x,
+                                  unsigned long long offset, __be32 *buf)
+{
+       unsigned long flags;
+
+       spin_lock_irqsave(&dg00x->lock, flags);
+       dg00x->msg = be32_to_cpu(*buf);
+       spin_unlock_irqrestore(&dg00x->lock, flags);
+
+       wake_up(&dg00x->hwdep_wait);
+}
+
+static void handle_message(struct fw_card *card, struct fw_request *request,
+                          int tcode, int destination, int source,
+                          int generation, unsigned long long offset,
+                          void *data, size_t length, void *callback_data)
+{
+       struct snd_dg00x *dg00x = callback_data;
+       __be32 *buf = (__be32 *)data;
+
+       if (offset == dg00x->async_handler.offset)
+               handle_unknown_message(dg00x, offset, buf);
+       else if (offset == dg00x->async_handler.offset + 4)
+               handle_midi_control(dg00x, buf, length);
+
+       fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x)
+{
+       struct fw_device *device = fw_parent_device(dg00x->unit);
+       __be32 data[2];
+       int err;
+
+       /* Unknown. 4bytes. */
+       data[0] = cpu_to_be32((device->card->node_id << 16) |
+                             (dg00x->async_handler.offset >> 32));
+       data[1] = cpu_to_be32(dg00x->async_handler.offset);
+       err = snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
+                                DG00X_ADDR_BASE + DG00X_OFFSET_MESSAGE_ADDR,
+                                &data, sizeof(data), 0);
+       if (err < 0)
+               return err;
+
+       /* Asynchronous transactions for MIDI control message. */
+       data[0] = cpu_to_be32((device->card->node_id << 16) |
+                             (dg00x->async_handler.offset >> 32));
+       data[1] = cpu_to_be32(dg00x->async_handler.offset + 4);
+       return snd_fw_transaction(dg00x->unit, TCODE_WRITE_BLOCK_REQUEST,
+                                 DG00X_ADDR_BASE + DG00X_OFFSET_MIDI_CTL_ADDR,
+                                 &data, sizeof(data), 0);
+}
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x)
+{
+       static const struct fw_address_region resp_register_region = {
+               .start  = 0xffffe0000000ull,
+               .end    = 0xffffe000ffffull,
+       };
+       int err;
+
+       dg00x->async_handler.length = 12;
+       dg00x->async_handler.address_callback = handle_message;
+       dg00x->async_handler.callback_data = dg00x;
+
+       err = fw_core_add_address_handler(&dg00x->async_handler,
+                                         &resp_register_region);
+       if (err < 0)
+               return err;
+
+       err = snd_dg00x_transaction_reregister(dg00x);
+       if (err < 0)
+               goto error;
+
+       err = snd_fw_async_midi_port_init(&dg00x->out_control, dg00x->unit,
+                                         DG00X_ADDR_BASE + DG00X_OFFSET_MMC,
+                                         4, fill_midi_message);
+       if (err < 0)
+               goto error;
+
+       return err;
+error:
+       fw_core_remove_address_handler(&dg00x->async_handler);
+       dg00x->async_handler.address_callback = NULL;
+       return err;
+}
+
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x)
+{
+       snd_fw_async_midi_port_destroy(&dg00x->out_control);
+       fw_core_remove_address_handler(&dg00x->async_handler);
+}
diff --git a/sound/firewire/digi00x/digi00x.c b/sound/firewire/digi00x/digi00x.c
new file mode 100644 (file)
index 0000000..1f33b7a
--- /dev/null
@@ -0,0 +1,170 @@
+/*
+ * digi00x.c - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "digi00x.h"
+
+MODULE_DESCRIPTION("Digidesign Digi 002/003 family Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+#define VENDOR_DIGIDESIGN      0x00a07e
+#define MODEL_DIGI00X          0x000002
+
+static int name_card(struct snd_dg00x *dg00x)
+{
+       struct fw_device *fw_dev = fw_parent_device(dg00x->unit);
+       char name[32] = {0};
+       char *model;
+       int err;
+
+       err = fw_csr_string(dg00x->unit->directory, CSR_MODEL, name,
+                           sizeof(name));
+       if (err < 0)
+               return err;
+
+       model = skip_spaces(name);
+
+       strcpy(dg00x->card->driver, "Digi00x");
+       strcpy(dg00x->card->shortname, model);
+       strcpy(dg00x->card->mixername, model);
+       snprintf(dg00x->card->longname, sizeof(dg00x->card->longname),
+                "Digidesign %s, GUID %08x%08x at %s, S%d", model,
+                fw_dev->config_rom[3], fw_dev->config_rom[4],
+                dev_name(&dg00x->unit->device), 100 << fw_dev->max_speed);
+
+       return 0;
+}
+
+static void dg00x_card_free(struct snd_card *card)
+{
+       struct snd_dg00x *dg00x = card->private_data;
+
+       snd_dg00x_stream_destroy_duplex(dg00x);
+       snd_dg00x_transaction_unregister(dg00x);
+
+       fw_unit_put(dg00x->unit);
+
+       mutex_destroy(&dg00x->mutex);
+}
+
+static int snd_dg00x_probe(struct fw_unit *unit,
+                          const struct ieee1394_device_id *entry)
+{
+       struct snd_card *card;
+       struct snd_dg00x *dg00x;
+       int err;
+
+       /* create card */
+       err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+                          sizeof(struct snd_dg00x), &card);
+       if (err < 0)
+               return err;
+       card->private_free = dg00x_card_free;
+
+       /* initialize myself */
+       dg00x = card->private_data;
+       dg00x->card = card;
+       dg00x->unit = fw_unit_get(unit);
+
+       mutex_init(&dg00x->mutex);
+       spin_lock_init(&dg00x->lock);
+       init_waitqueue_head(&dg00x->hwdep_wait);
+
+       err = name_card(dg00x);
+       if (err < 0)
+               goto error;
+
+       err = snd_dg00x_stream_init_duplex(dg00x);
+       if (err < 0)
+               goto error;
+
+       snd_dg00x_proc_init(dg00x);
+
+       err = snd_dg00x_create_pcm_devices(dg00x);
+       if (err < 0)
+               goto error;
+
+       err = snd_dg00x_create_midi_devices(dg00x);
+       if (err < 0)
+               goto error;
+
+       err = snd_dg00x_create_hwdep_device(dg00x);
+       if (err < 0)
+               goto error;
+
+       err = snd_dg00x_transaction_register(dg00x);
+       if (err < 0)
+               goto error;
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
+
+       dev_set_drvdata(&unit->device, dg00x);
+
+       return err;
+error:
+       snd_card_free(card);
+       return err;
+}
+
+static void snd_dg00x_update(struct fw_unit *unit)
+{
+       struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+       snd_dg00x_transaction_reregister(dg00x);
+
+       mutex_lock(&dg00x->mutex);
+       snd_dg00x_stream_update_duplex(dg00x);
+       mutex_unlock(&dg00x->mutex);
+}
+
+static void snd_dg00x_remove(struct fw_unit *unit)
+{
+       struct snd_dg00x *dg00x = dev_get_drvdata(&unit->device);
+
+       /* No need to wait for releasing card object in this context. */
+       snd_card_free_when_closed(dg00x->card);
+}
+
+static const struct ieee1394_device_id snd_dg00x_id_table[] = {
+       /* Both of 002/003 use the same ID. */
+       {
+               .match_flags = IEEE1394_MATCH_VENDOR_ID |
+                              IEEE1394_MATCH_MODEL_ID,
+               .vendor_id = VENDOR_DIGIDESIGN,
+               .model_id = MODEL_DIGI00X,
+       },
+       {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_dg00x_id_table);
+
+static struct fw_driver dg00x_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "snd-firewire-digi00x",
+               .bus = &fw_bus_type,
+       },
+       .probe    = snd_dg00x_probe,
+       .update   = snd_dg00x_update,
+       .remove   = snd_dg00x_remove,
+       .id_table = snd_dg00x_id_table,
+};
+
+static int __init snd_dg00x_init(void)
+{
+       return driver_register(&dg00x_driver.driver);
+}
+
+static void __exit snd_dg00x_exit(void)
+{
+       driver_unregister(&dg00x_driver.driver);
+}
+
+module_init(snd_dg00x_init);
+module_exit(snd_dg00x_exit);
diff --git a/sound/firewire/digi00x/digi00x.h b/sound/firewire/digi00x/digi00x.h
new file mode 100644 (file)
index 0000000..907e739
--- /dev/null
@@ -0,0 +1,157 @@
+/*
+ * digi00x.h - a part of driver for Digidesign Digi 002/003 family
+ *
+ * Copyright (c) 2014-2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_DIGI00X_H_INCLUDED
+#define SOUND_DIGI00X_H_INCLUDED
+
+#include <linux/compat.h>
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../iso-resources.h"
+#include "../amdtp-stream.h"
+
+struct snd_dg00x {
+       struct snd_card *card;
+       struct fw_unit *unit;
+
+       struct mutex mutex;
+       spinlock_t lock;
+
+       struct amdtp_stream tx_stream;
+       struct fw_iso_resources tx_resources;
+
+       struct amdtp_stream rx_stream;
+       struct fw_iso_resources rx_resources;
+
+       unsigned int substreams_counter;
+
+       /* for uapi */
+       int dev_lock_count;
+       bool dev_lock_changed;
+       wait_queue_head_t hwdep_wait;
+
+       /* For asynchronous messages. */
+       struct fw_address_handler async_handler;
+       u32 msg;
+
+       /* For asynchronous MIDI controls. */
+       struct snd_rawmidi_substream *in_control;
+       struct snd_fw_async_midi_port out_control;
+};
+
+#define DG00X_ADDR_BASE                0xffffe0000000ull
+
+#define DG00X_OFFSET_STREAMING_STATE   0x0000
+#define DG00X_OFFSET_STREAMING_SET     0x0004
+#define DG00X_OFFSET_MIDI_CTL_ADDR     0x0008
+/* For LSB of the address              0x000c */
+/* unknown                             0x0010 */
+#define DG00X_OFFSET_MESSAGE_ADDR      0x0014
+/* For LSB of the address              0x0018 */
+/* unknown                             0x001c */
+/* unknown                             0x0020 */
+/* not used                    0x0024--0x00ff */
+#define DG00X_OFFSET_ISOC_CHANNELS     0x0100
+/* unknown                             0x0104 */
+/* unknown                             0x0108 */
+/* unknown                             0x010c */
+#define DG00X_OFFSET_LOCAL_RATE                0x0110
+#define DG00X_OFFSET_EXTERNAL_RATE     0x0114
+#define DG00X_OFFSET_CLOCK_SOURCE      0x0118
+#define DG00X_OFFSET_OPT_IFACE_MODE    0x011c
+/* unknown                             0x0120 */
+/* Mixer control on/off                        0x0124 */
+/* unknown                             0x0128 */
+#define DG00X_OFFSET_DETECT_EXTERNAL   0x012c
+/* unknown                             0x0138 */
+#define DG00X_OFFSET_MMC               0x0400
+
+enum snd_dg00x_rate {
+       SND_DG00X_RATE_44100 = 0,
+       SND_DG00X_RATE_48000,
+       SND_DG00X_RATE_88200,
+       SND_DG00X_RATE_96000,
+       SND_DG00X_RATE_COUNT,
+};
+
+enum snd_dg00x_clock {
+       SND_DG00X_CLOCK_INTERNAL = 0,
+       SND_DG00X_CLOCK_SPDIF,
+       SND_DG00X_CLOCK_ADAT,
+       SND_DG00X_CLOCK_WORD,
+       SND_DG00X_CLOCK_COUNT,
+};
+
+enum snd_dg00x_optical_mode {
+       SND_DG00X_OPT_IFACE_MODE_ADAT = 0,
+       SND_DG00X_OPT_IFACE_MODE_SPDIF,
+       SND_DG00X_OPT_IFACE_MODE_COUNT,
+};
+
+#define DOT_MIDI_IN_PORTS      1
+#define DOT_MIDI_OUT_PORTS     2
+
+int amdtp_dot_init(struct amdtp_stream *s, struct fw_unit *unit,
+                  enum amdtp_stream_direction dir);
+int amdtp_dot_set_parameters(struct amdtp_stream *s, unsigned int rate,
+                            unsigned int pcm_channels);
+void amdtp_dot_reset(struct amdtp_stream *s);
+int amdtp_dot_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                    struct snd_pcm_runtime *runtime);
+void amdtp_dot_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
+void amdtp_dot_midi_trigger(struct amdtp_stream *s, unsigned int port,
+                         struct snd_rawmidi_substream *midi);
+
+int snd_dg00x_transaction_register(struct snd_dg00x *dg00x);
+int snd_dg00x_transaction_reregister(struct snd_dg00x *dg00x);
+void snd_dg00x_transaction_unregister(struct snd_dg00x *dg00x);
+
+extern const unsigned int snd_dg00x_stream_rates[SND_DG00X_RATE_COUNT];
+extern const unsigned int snd_dg00x_stream_pcm_channels[SND_DG00X_RATE_COUNT];
+int snd_dg00x_stream_get_external_rate(struct snd_dg00x *dg00x,
+                                      unsigned int *rate);
+int snd_dg00x_stream_get_local_rate(struct snd_dg00x *dg00x,
+                                   unsigned int *rate);
+int snd_dg00x_stream_set_local_rate(struct snd_dg00x *dg00x, unsigned int rate);
+int snd_dg00x_stream_get_clock(struct snd_dg00x *dg00x,
+                              enum snd_dg00x_clock *clock);
+int snd_dg00x_stream_check_external_clock(struct snd_dg00x *dg00x,
+                                         bool *detect);
+int snd_dg00x_stream_init_duplex(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_start_duplex(struct snd_dg00x *dg00x, unsigned int rate);
+void snd_dg00x_stream_stop_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_update_duplex(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_destroy_duplex(struct snd_dg00x *dg00x);
+
+void snd_dg00x_stream_lock_changed(struct snd_dg00x *dg00x);
+int snd_dg00x_stream_lock_try(struct snd_dg00x *dg00x);
+void snd_dg00x_stream_lock_release(struct snd_dg00x *dg00x);
+
+void snd_dg00x_proc_init(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_pcm_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_midi_devices(struct snd_dg00x *dg00x);
+
+int snd_dg00x_create_hwdep_device(struct snd_dg00x *dg00x);
+#endif
index 0619597e3a3f250b5e5b40d07d6c0e68de1a8ccb..cce19768f43d0a3f32c9d3c0187c1077f5bbfeab 100644 (file)
@@ -17,7 +17,7 @@
 #include <linux/delay.h>
 #include "fcp.h"
 #include "lib.h"
-#include "amdtp.h"
+#include "amdtp-stream.h"
 
 #define CTS_AVC 0x00
 
index 0c7440826db8e5b7f2e21d087da6fc0cc8ea50f4..15ef7f75a8ef123b5ee335fa4dcd4bef6c3c9122 100644 (file)
@@ -1,4 +1,4 @@
 snd-fireworks-objs := fireworks_transaction.o fireworks_command.o \
                      fireworks_stream.o fireworks_proc.o fireworks_midi.o \
                      fireworks_pcm.o fireworks_hwdep.o fireworks.o
-obj-m += snd-fireworks.o
+obj-$(CONFIG_SND_FIREWORKS) += snd-fireworks.o
index c94a432f7cc653dca45316ea67bdc481b9e8e887..d5b19bc11e5999480f030e682fa4087cc91067ac 100644 (file)
@@ -138,12 +138,12 @@ get_hardware_info(struct snd_efw *efw)
        efw->midi_out_ports = hwinfo->midi_out_ports;
        efw->midi_in_ports = hwinfo->midi_in_ports;
 
-       if (hwinfo->amdtp_tx_pcm_channels    > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           hwinfo->amdtp_tx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           hwinfo->amdtp_tx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           hwinfo->amdtp_rx_pcm_channels    > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           hwinfo->amdtp_rx_pcm_channels_2x > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           hwinfo->amdtp_rx_pcm_channels_4x > AMDTP_MAX_CHANNELS_FOR_PCM) {
+       if (hwinfo->amdtp_tx_pcm_channels    > AM824_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_tx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_tx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_rx_pcm_channels    > AM824_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_rx_pcm_channels_2x > AM824_MAX_CHANNELS_FOR_PCM ||
+           hwinfo->amdtp_rx_pcm_channels_4x > AM824_MAX_CHANNELS_FOR_PCM) {
                err = -ENOSYS;
                goto end;
        }
index 084d414b228cf425dc99440cc081d65459bbe175..c7cb7deafe48487fd802fbf5ce84e34c2f5ab2eb 100644 (file)
@@ -29,7 +29,7 @@
 
 #include "../packets-buffer.h"
 #include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
 #include "../cmp.h"
 #include "../lib.h"
 
index 166f80584c2a923da199990ca0b1920f646e52a0..94bab0476a65ce69011aaad0bf1ac8eac8a60e76 100644 (file)
@@ -257,7 +257,7 @@ int snd_efw_command_get_phys_meters(struct snd_efw *efw,
                                    struct snd_efw_phys_meters *meters,
                                    unsigned int len)
 {
-       __be32 *buf = (__be32 *)meters;
+       u32 *buf = (u32 *)meters;
        unsigned int i;
        int err;
 
index cf9c6526043938aa905019c23992fdf496247403..fba01bbba4564be493af246a3d40957da2c1465d 100644 (file)
@@ -73,10 +73,10 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&efw->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&efw->tx_stream,
+               amdtp_am824_midi_trigger(&efw->tx_stream,
                                          substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&efw->tx_stream,
+               amdtp_am824_midi_trigger(&efw->tx_stream,
                                          substrm->number, NULL);
 
        spin_unlock_irqrestore(&efw->lock, flags);
@@ -90,11 +90,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&efw->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&efw->rx_stream,
-                                         substrm->number, substrm);
+               amdtp_am824_midi_trigger(&efw->rx_stream,
+                                        substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&efw->rx_stream,
-                                         substrm->number, NULL);
+               amdtp_am824_midi_trigger(&efw->rx_stream,
+                                        substrm->number, NULL);
 
        spin_unlock_irqrestore(&efw->lock, flags);
 }
index c30b2ffa8dfb1db8354c25b4681938ac82627194..d27135bac5133e5d57bf5444e06f1e105559932e 100644 (file)
@@ -159,11 +159,11 @@ pcm_init_hw_params(struct snd_efw *efw,
                           SNDRV_PCM_INFO_MMAP_VALID;
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+               runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
                s = &efw->tx_stream;
                pcm_channels = efw->pcm_capture_channels;
        } else {
-               runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+               runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
                s = &efw->rx_stream;
                pcm_channels = efw->pcm_playback_channels;
        }
@@ -187,7 +187,7 @@ pcm_init_hw_params(struct snd_efw *efw,
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_add_pcm_hw_constraints(s, runtime);
+       err = amdtp_am824_add_pcm_hw_constraints(s, runtime);
 end:
        return err;
 }
@@ -253,7 +253,8 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
                atomic_inc(&efw->capture_substreams);
-       amdtp_stream_set_pcm_format(&efw->tx_stream, params_format(hw_params));
+
+       amdtp_am824_set_pcm_format(&efw->tx_stream, params_format(hw_params));
 
        return 0;
 }
@@ -270,7 +271,8 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
 
        if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN)
                atomic_inc(&efw->playback_substreams);
-       amdtp_stream_set_pcm_format(&efw->rx_stream, params_format(hw_params));
+
+       amdtp_am824_set_pcm_format(&efw->rx_stream, params_format(hw_params));
 
        return 0;
 }
index 7e353f1f7bff359ea8845630aeff2521759e508f..759f6e3ed44ab08fb8d480b63dacf135ca734bab 100644 (file)
@@ -31,7 +31,7 @@ init_stream(struct snd_efw *efw, struct amdtp_stream *stream)
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_init(stream, efw->unit, s_dir, CIP_BLOCKING);
+       err = amdtp_am824_init(stream, efw->unit, s_dir, CIP_BLOCKING);
        if (err < 0) {
                amdtp_stream_destroy(stream);
                cmp_connection_destroy(conn);
@@ -73,8 +73,10 @@ start_stream(struct snd_efw *efw, struct amdtp_stream *stream,
                midi_ports = efw->midi_in_ports;
        }
 
-       amdtp_stream_set_parameters(stream, sampling_rate,
-                                   pcm_channels, midi_ports);
+       err = amdtp_am824_set_parameters(stream, sampling_rate,
+                                        pcm_channels, midi_ports, false);
+       if (err < 0)
+               goto end;
 
        /*  establish connection via CMP */
        err = cmp_connection_establish(conn,
index 7409edba9f06761b11d8582bd60fb43bcc8a0ff6..f80aafa44c89499ff92b0b3af0bc761ca49c0567 100644 (file)
@@ -9,6 +9,7 @@
 #include <linux/device.h>
 #include <linux/firewire.h>
 #include <linux/module.h>
+#include <linux/slab.h>
 #include "lib.h"
 
 #define ERROR_RETRY_DELAY_MS   20
@@ -66,6 +67,147 @@ int snd_fw_transaction(struct fw_unit *unit, int tcode,
 }
 EXPORT_SYMBOL(snd_fw_transaction);
 
+static void async_midi_port_callback(struct fw_card *card, int rcode,
+                                    void *data, size_t length,
+                                    void *callback_data)
+{
+       struct snd_fw_async_midi_port *port = callback_data;
+       struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
+
+       /* This port is closed. */
+       if (substream == NULL)
+               return;
+
+       if (rcode == RCODE_COMPLETE)
+               snd_rawmidi_transmit_ack(substream, port->consume_bytes);
+       else if (!rcode_is_permanent_error(rcode))
+               /* To start next transaction immediately for recovery. */
+               port->next_ktime = ktime_set(0, 0);
+       else
+               /* Don't continue processing. */
+               port->error = true;
+
+       port->idling = true;
+
+       if (!snd_rawmidi_transmit_empty(substream))
+               schedule_work(&port->work);
+}
+
+static void midi_port_work(struct work_struct *work)
+{
+       struct snd_fw_async_midi_port *port =
+                       container_of(work, struct snd_fw_async_midi_port, work);
+       struct snd_rawmidi_substream *substream = ACCESS_ONCE(port->substream);
+       int generation;
+       int type;
+
+       /* Under transacting or error state. */
+       if (!port->idling || port->error)
+               return;
+
+       /* Nothing to do. */
+       if (substream == NULL || snd_rawmidi_transmit_empty(substream))
+               return;
+
+       /* Do it in next chance. */
+       if (ktime_after(port->next_ktime, ktime_get())) {
+               schedule_work(&port->work);
+               return;
+       }
+
+       /*
+        * Fill the buffer. The callee must use snd_rawmidi_transmit_peek().
+        * Later, snd_rawmidi_transmit_ack() is called.
+        */
+       memset(port->buf, 0, port->len);
+       port->consume_bytes = port->fill(substream, port->buf);
+       if (port->consume_bytes <= 0) {
+               /* Do it in next chance, immediately. */
+               if (port->consume_bytes == 0) {
+                       port->next_ktime = ktime_set(0, 0);
+                       schedule_work(&port->work);
+               } else {
+                       /* Fatal error. */
+                       port->error = true;
+               }
+               return;
+       }
+
+       /* Calculate type of transaction. */
+       if (port->len == 4)
+               type = TCODE_WRITE_QUADLET_REQUEST;
+       else
+               type = TCODE_WRITE_BLOCK_REQUEST;
+
+       /* Set interval to next transaction. */
+       port->next_ktime = ktime_add_ns(ktime_get(),
+                               port->consume_bytes * 8 * NSEC_PER_SEC / 31250);
+
+       /* Start this transaction. */
+       port->idling = false;
+
+       /*
+        * In Linux FireWire core, when generation is updated with memory
+        * barrier, node id has already been updated. In this module, After
+        * this smp_rmb(), load/store instructions to memory are completed.
+        * Thus, both of generation and node id are available with recent
+        * values. This is a light-serialization solution to handle bus reset
+        * events on IEEE 1394 bus.
+        */
+       generation = port->parent->generation;
+       smp_rmb();
+
+       fw_send_request(port->parent->card, &port->transaction, type,
+                       port->parent->node_id, generation,
+                       port->parent->max_speed, port->addr,
+                       port->buf, port->len, async_midi_port_callback,
+                       port);
+}
+
+/**
+ * snd_fw_async_midi_port_init - initialize asynchronous MIDI port structure
+ * @port: the asynchronous MIDI port to initialize
+ * @unit: the target of the asynchronous transaction
+ * @addr: the address to which transactions are transferred
+ * @len: the length of transaction
+ * @fill: the callback function to fill given buffer, and returns the
+ *            number of consumed bytes for MIDI message.
+ *
+ */
+int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
+               struct fw_unit *unit, u64 addr, unsigned int len,
+               snd_fw_async_midi_port_fill fill)
+{
+       port->len = DIV_ROUND_UP(len, 4) * 4;
+       port->buf = kzalloc(port->len, GFP_KERNEL);
+       if (port->buf == NULL)
+               return -ENOMEM;
+
+       port->parent = fw_parent_device(unit);
+       port->addr = addr;
+       port->fill = fill;
+       port->idling = true;
+       port->next_ktime = ktime_set(0, 0);
+       port->error = false;
+
+       INIT_WORK(&port->work, midi_port_work);
+
+       return 0;
+}
+EXPORT_SYMBOL(snd_fw_async_midi_port_init);
+
+/**
+ * snd_fw_async_midi_port_destroy - free asynchronous MIDI port structure
+ * @port: the asynchronous MIDI port structure
+ */
+void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port)
+{
+       snd_fw_async_midi_port_finish(port);
+       cancel_work_sync(&port->work);
+       kfree(port->buf);
+}
+EXPORT_SYMBOL(snd_fw_async_midi_port_destroy);
+
 MODULE_DESCRIPTION("FireWire audio helper functions");
 MODULE_AUTHOR("Clemens Ladisch <clemens@ladisch.de>");
 MODULE_LICENSE("GPL v2");
index 02cfabc9c3c4ba53d18e7b827600491fd9c2df84..f3f6f84c48d69747cc0fa3bd815d986c79ca0d78 100644 (file)
@@ -3,6 +3,8 @@
 
 #include <linux/firewire-constants.h>
 #include <linux/types.h>
+#include <linux/sched.h>
+#include <sound/rawmidi.h>
 
 struct fw_unit;
 
@@ -20,4 +22,58 @@ static inline bool rcode_is_permanent_error(int rcode)
        return rcode == RCODE_TYPE_ERROR || rcode == RCODE_ADDRESS_ERROR;
 }
 
+struct snd_fw_async_midi_port;
+typedef int (*snd_fw_async_midi_port_fill)(
+                               struct snd_rawmidi_substream *substream,
+                               u8 *buf);
+
+struct snd_fw_async_midi_port {
+       struct fw_device *parent;
+       struct work_struct work;
+       bool idling;
+       ktime_t next_ktime;
+       bool error;
+
+       u64 addr;
+       struct fw_transaction transaction;
+
+       u8 *buf;
+       unsigned int len;
+
+       struct snd_rawmidi_substream *substream;
+       snd_fw_async_midi_port_fill fill;
+       unsigned int consume_bytes;
+};
+
+int snd_fw_async_midi_port_init(struct snd_fw_async_midi_port *port,
+               struct fw_unit *unit, u64 addr, unsigned int len,
+               snd_fw_async_midi_port_fill fill);
+void snd_fw_async_midi_port_destroy(struct snd_fw_async_midi_port *port);
+
+/**
+ * snd_fw_async_midi_port_run - run transactions for the async MIDI port
+ * @port: the asynchronous MIDI port
+ * @substream: the MIDI substream
+ */
+static inline void
+snd_fw_async_midi_port_run(struct snd_fw_async_midi_port *port,
+                          struct snd_rawmidi_substream *substream)
+{
+       if (!port->error) {
+               port->substream = substream;
+               schedule_work(&port->work);
+       }
+}
+
+/**
+ * snd_fw_async_midi_port_finish - finish the asynchronous MIDI port
+ * @port: the asynchronous MIDI port
+ */
+static inline void
+snd_fw_async_midi_port_finish(struct snd_fw_async_midi_port *port)
+{
+       port->substream = NULL;
+       port->error = false;
+}
+
 #endif
index a926850864f6706e45b3245d02c8962ddcd7647c..06ff50f4e6c0b19826caabbfcbd28998927a8929 100644 (file)
@@ -1,3 +1,3 @@
 snd-oxfw-objs := oxfw-command.o oxfw-stream.o oxfw-control.o oxfw-pcm.o \
                 oxfw-proc.o oxfw-midi.o oxfw-hwdep.o oxfw.o
-obj-m += snd-oxfw.o
+obj-$(CONFIG_SND_OXFW) += snd-oxfw.o
index 540a303385169d011a3813aaa8483d7d8b67bbe0..8665e1043d41fa380a8a94f213098cbca29d87ef 100644 (file)
@@ -90,11 +90,11 @@ static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&oxfw->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&oxfw->tx_stream,
-                                         substrm->number, substrm);
+               amdtp_am824_midi_trigger(&oxfw->tx_stream,
+                                        substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&oxfw->tx_stream,
-                                         substrm->number, NULL);
+               amdtp_am824_midi_trigger(&oxfw->tx_stream,
+                                        substrm->number, NULL);
 
        spin_unlock_irqrestore(&oxfw->lock, flags);
 }
@@ -107,11 +107,11 @@ static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
        spin_lock_irqsave(&oxfw->lock, flags);
 
        if (up)
-               amdtp_stream_midi_trigger(&oxfw->rx_stream,
-                                         substrm->number, substrm);
+               amdtp_am824_midi_trigger(&oxfw->rx_stream,
+                                        substrm->number, substrm);
        else
-               amdtp_stream_midi_trigger(&oxfw->rx_stream,
-                                         substrm->number, NULL);
+               amdtp_am824_midi_trigger(&oxfw->rx_stream,
+                                        substrm->number, NULL);
 
        spin_unlock_irqrestore(&oxfw->lock, flags);
 }
@@ -142,29 +142,11 @@ static void set_midi_substream_names(struct snd_oxfw *oxfw,
 
 int snd_oxfw_create_midi(struct snd_oxfw *oxfw)
 {
-       struct snd_oxfw_stream_formation formation;
        struct snd_rawmidi *rmidi;
        struct snd_rawmidi_str *str;
-       u8 *format;
-       int i, err;
-
-       /* If its stream has MIDI conformant data channel, add one MIDI port */
-       for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
-               format = oxfw->tx_stream_formats[i];
-               if (format != NULL) {
-                       err = snd_oxfw_stream_parse_format(format, &formation);
-                       if (err >= 0 && formation.midi > 0)
-                               oxfw->midi_input_ports = 1;
-               }
-
-               format = oxfw->rx_stream_formats[i];
-               if (format != NULL) {
-                       err = snd_oxfw_stream_parse_format(format, &formation);
-                       if (err >= 0 && formation.midi > 0)
-                               oxfw->midi_output_ports = 1;
-               }
-       }
-       if ((oxfw->midi_input_ports == 0) && (oxfw->midi_output_ports == 0))
+       int err;
+
+       if (oxfw->midi_input_ports == 0 && oxfw->midi_output_ports == 0)
                return 0;
 
        /* create midi ports */
index 9c73930d0278820a11fd46a2db2fb877b6a61a88..8d233417695d613141c2a49f44c279fc0b57b45e 100644 (file)
@@ -134,11 +134,11 @@ static int init_hw_params(struct snd_oxfw *oxfw,
                           SNDRV_PCM_INFO_MMAP_VALID;
 
        if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
-               runtime->hw.formats = AMDTP_IN_PCM_FORMAT_BITS;
+               runtime->hw.formats = AM824_IN_PCM_FORMAT_BITS;
                stream = &oxfw->tx_stream;
                formats = oxfw->tx_stream_formats;
        } else {
-               runtime->hw.formats = AMDTP_OUT_PCM_FORMAT_BITS;
+               runtime->hw.formats = AM824_OUT_PCM_FORMAT_BITS;
                stream = &oxfw->rx_stream;
                formats = oxfw->rx_stream_formats;
        }
@@ -158,7 +158,7 @@ static int init_hw_params(struct snd_oxfw *oxfw,
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_add_pcm_hw_constraints(stream, runtime);
+       err = amdtp_am824_add_pcm_hw_constraints(stream, runtime);
 end:
        return err;
 }
@@ -244,7 +244,7 @@ static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
                mutex_unlock(&oxfw->mutex);
        }
 
-       amdtp_stream_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
+       amdtp_am824_set_pcm_format(&oxfw->tx_stream, params_format(hw_params));
 
        return 0;
 }
@@ -265,7 +265,7 @@ static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
                mutex_unlock(&oxfw->mutex);
        }
 
-       amdtp_stream_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
+       amdtp_am824_set_pcm_format(&oxfw->rx_stream, params_format(hw_params));
 
        return 0;
 }
index 77ad5b98e806fa694ddd5c0a4c2ae439c78aebf8..7cb5743c073bbed3f88d2452b6b6a7047f7619f3 100644 (file)
@@ -148,14 +148,17 @@ static int start_stream(struct snd_oxfw *oxfw, struct amdtp_stream *stream,
        }
 
        pcm_channels = formation.pcm;
-       midi_ports = DIV_ROUND_UP(formation.midi, 8);
+       midi_ports = formation.midi * 8;
 
        /* The stream should have one pcm channels at least */
        if (pcm_channels == 0) {
                err = -EINVAL;
                goto end;
        }
-       amdtp_stream_set_parameters(stream, rate, pcm_channels, midi_ports);
+       err = amdtp_am824_set_parameters(stream, rate, pcm_channels, midi_ports,
+                                        false);
+       if (err < 0)
+               goto end;
 
        err = cmp_connection_establish(conn,
                                       amdtp_stream_get_max_payload(stream));
@@ -225,7 +228,7 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
        if (err < 0)
                goto end;
 
-       err = amdtp_stream_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
+       err = amdtp_am824_init(stream, oxfw->unit, s_dir, CIP_NONBLOCKING);
        if (err < 0) {
                amdtp_stream_destroy(stream);
                cmp_connection_destroy(conn);
@@ -238,9 +241,12 @@ int snd_oxfw_stream_init_simplex(struct snd_oxfw *oxfw,
         * packets. As a result, next isochronous packet includes more data
         * blocks than IEC 61883-6 defines.
         */
-       if (stream == &oxfw->tx_stream)
+       if (stream == &oxfw->tx_stream) {
                oxfw->tx_stream.flags |= CIP_SKIP_INIT_DBC_CHECK |
                                         CIP_JUMBO_PAYLOAD;
+               if (oxfw->wrong_dbs)
+                       oxfw->tx_stream.flags |= CIP_WRONG_DBS;
+       }
 end:
        return err;
 }
@@ -480,8 +486,8 @@ int snd_oxfw_stream_parse_format(u8 *format,
                }
        }
 
-       if (formation->pcm  > AMDTP_MAX_CHANNELS_FOR_PCM ||
-           formation->midi > AMDTP_MAX_CHANNELS_FOR_MIDI)
+       if (formation->pcm  > AM824_MAX_CHANNELS_FOR_PCM ||
+           formation->midi > AM824_MAX_CHANNELS_FOR_MIDI)
                return -ENOSYS;
 
        return 0;
@@ -623,6 +629,9 @@ end:
 int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
 {
        u8 plugs[AVC_PLUG_INFO_BUF_BYTES];
+       struct snd_oxfw_stream_formation formation;
+       u8 *format;
+       unsigned int i;
        int err;
 
        /* the number of plugs for isoc in/out, ext in/out  */
@@ -642,12 +651,42 @@ int snd_oxfw_stream_discover(struct snd_oxfw *oxfw)
                err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_OUT, 0);
                if (err < 0)
                        goto end;
+
+               for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+                       format = oxfw->tx_stream_formats[i];
+                       if (format == NULL)
+                               continue;
+                       err = snd_oxfw_stream_parse_format(format, &formation);
+                       if (err < 0)
+                               continue;
+
+                       /* Add one MIDI port. */
+                       if (formation.midi > 0)
+                               oxfw->midi_input_ports = 1;
+               }
+
                oxfw->has_output = true;
        }
 
        /* use iPCR[0] if exists */
-       if (plugs[0] > 0)
+       if (plugs[0] > 0) {
                err = fill_stream_formats(oxfw, AVC_GENERAL_PLUG_DIR_IN, 0);
+               if (err < 0)
+                       goto end;
+
+               for (i = 0; i < SND_OXFW_STREAM_FORMAT_ENTRIES; i++) {
+                       format = oxfw->rx_stream_formats[i];
+                       if (format == NULL)
+                               continue;
+                       err = snd_oxfw_stream_parse_format(format, &formation);
+                       if (err < 0)
+                               continue;
+
+                       /* Add one MIDI port. */
+                       if (formation.midi > 0)
+                               oxfw->midi_output_ports = 1;
+               }
+       }
 end:
        return err;
 }
index 8c6ce019f437c310043a3686b634def68c92e424..588b93f20c2e23e6955ca76c5d0b5076561b6fd5 100644 (file)
@@ -18,6 +18,9 @@
 #define VENDOR_GRIFFIN         0x001292
 #define VENDOR_BEHRINGER       0x001564
 #define VENDOR_LACIE           0x00d04b
+#define VENDOR_TASCAM          0x00022e
+
+#define MODEL_SATELLITE                0x00200f
 
 #define SPECIFIER_1394TA       0x00a02d
 #define VERSION_AVC            0x010001
@@ -129,6 +132,40 @@ static void oxfw_card_free(struct snd_card *card)
        mutex_destroy(&oxfw->mutex);
 }
 
+static void detect_quirks(struct snd_oxfw *oxfw)
+{
+       struct fw_device *fw_dev = fw_parent_device(oxfw->unit);
+       struct fw_csr_iterator it;
+       int key, val;
+       int vendor, model;
+
+       /* Seek from Root Directory of Config ROM. */
+       vendor = model = 0;
+       fw_csr_iterator_init(&it, fw_dev->config_rom + 5);
+       while (fw_csr_iterator_next(&it, &key, &val)) {
+               if (key == CSR_VENDOR)
+                       vendor = val;
+               else if (key == CSR_MODEL)
+                       model = val;
+       }
+
+       /*
+        * Mackie Onyx Satellite with base station has a quirk to report a wrong
+        * value in 'dbs' field of CIP header against its format information.
+        */
+       if (vendor == VENDOR_LOUD && model == MODEL_SATELLITE)
+               oxfw->wrong_dbs = true;
+
+       /*
+        * TASCAM FireOne has physical control and requires a pair of additional
+        * MIDI ports.
+        */
+       if (vendor == VENDOR_TASCAM) {
+               oxfw->midi_input_ports++;
+               oxfw->midi_output_ports++;
+       }
+}
+
 static int oxfw_probe(struct fw_unit *unit,
                       const struct ieee1394_device_id *id)
 {
@@ -157,6 +194,8 @@ static int oxfw_probe(struct fw_unit *unit,
        if (err < 0)
                goto error;
 
+       detect_quirks(oxfw);
+
        err = name_card(oxfw);
        if (err < 0)
                goto error;
@@ -294,6 +333,13 @@ static const struct ieee1394_device_id oxfw_id_table[] = {
                .specifier_id   = SPECIFIER_1394TA,
                .version        = VERSION_AVC,
        },
+       /* TASCAM, FireOne */
+       {
+               .match_flags    = IEEE1394_MATCH_VENDOR_ID |
+                                 IEEE1394_MATCH_MODEL_ID,
+               .vendor_id      = VENDOR_TASCAM,
+               .model_id       = 0x800007,
+       },
        { }
 };
 MODULE_DEVICE_TABLE(ieee1394, oxfw_id_table);
index cace5ad4fe76134a0fb4ece9a27f2c321021d998..8392c424ad1d53bb91ea04f7dc78f67362b2f543 100644 (file)
@@ -28,7 +28,7 @@
 #include "../fcp.h"
 #include "../packets-buffer.h"
 #include "../iso-resources.h"
-#include "../amdtp.h"
+#include "../amdtp-am824.h"
 #include "../cmp.h"
 
 struct device_info {
@@ -49,6 +49,7 @@ struct snd_oxfw {
        struct mutex mutex;
        spinlock_t lock;
 
+       bool wrong_dbs;
        bool has_output;
        u8 *tx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
        u8 *rx_stream_formats[SND_OXFW_STREAM_FORMAT_ENTRIES];
diff --git a/sound/firewire/tascam/Makefile b/sound/firewire/tascam/Makefile
new file mode 100644 (file)
index 0000000..0fc955d
--- /dev/null
@@ -0,0 +1,4 @@
+snd-firewire-tascam-objs := tascam-proc.o amdtp-tascam.o tascam-stream.o \
+                           tascam-pcm.o tascam-hwdep.o tascam-transaction.o \
+                           tascam-midi.o tascam.o
+obj-$(CONFIG_SND_FIREWIRE_TASCAM) += snd-firewire-tascam.o
diff --git a/sound/firewire/tascam/amdtp-tascam.c b/sound/firewire/tascam/amdtp-tascam.c
new file mode 100644 (file)
index 0000000..9dd0fcc
--- /dev/null
@@ -0,0 +1,243 @@
+/*
+ * amdtp-tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <sound/pcm.h>
+#include "tascam.h"
+
+#define AMDTP_FMT_TSCM_TX      0x1e
+#define AMDTP_FMT_TSCM_RX      0x3e
+
+struct amdtp_tscm {
+       unsigned int pcm_channels;
+
+       void (*transfer_samples)(struct amdtp_stream *s,
+                                struct snd_pcm_substream *pcm,
+                                __be32 *buffer, unsigned int frames);
+};
+
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate)
+{
+       struct amdtp_tscm *p = s->protocol;
+       unsigned int data_channels;
+
+       if (amdtp_stream_running(s))
+               return -EBUSY;
+
+       data_channels = p->pcm_channels;
+
+       /* Packets in in-stream have extra 2 data channels. */
+       if (s->direction == AMDTP_IN_STREAM)
+               data_channels += 2;
+
+       return amdtp_stream_set_parameters(s, rate, data_channels);
+}
+
+static void write_pcm_s32(struct amdtp_stream *s,
+                         struct snd_pcm_substream *pcm,
+                         __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_tscm *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       const u32 *src;
+
+       channels = p->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       buffer[c] = cpu_to_be32(*src);
+                       src++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void write_pcm_s16(struct amdtp_stream *s,
+                         struct snd_pcm_substream *pcm,
+                         __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_tscm *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       const u16 *src;
+
+       channels = p->pcm_channels;
+       src = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       buffer[c] = cpu_to_be32(*src << 16);
+                       src++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       src = (void *)runtime->dma_area;
+       }
+}
+
+static void read_pcm_s32(struct amdtp_stream *s,
+                        struct snd_pcm_substream *pcm,
+                        __be32 *buffer, unsigned int frames)
+{
+       struct amdtp_tscm *p = s->protocol;
+       struct snd_pcm_runtime *runtime = pcm->runtime;
+       unsigned int channels, remaining_frames, i, c;
+       u32 *dst;
+
+       channels = p->pcm_channels;
+       dst  = (void *)runtime->dma_area +
+                       frames_to_bytes(runtime, s->pcm_buffer_pointer);
+       remaining_frames = runtime->buffer_size - s->pcm_buffer_pointer;
+
+       /* The first data channel is for event counter. */
+       buffer += 1;
+
+       for (i = 0; i < frames; ++i) {
+               for (c = 0; c < channels; ++c) {
+                       *dst = be32_to_cpu(buffer[c]);
+                       dst++;
+               }
+               buffer += s->data_block_quadlets;
+               if (--remaining_frames == 0)
+                       dst = (void *)runtime->dma_area;
+       }
+}
+
+static void write_pcm_silence(struct amdtp_stream *s, __be32 *buffer,
+                             unsigned int data_blocks)
+{
+       struct amdtp_tscm *p = s->protocol;
+       unsigned int channels, i, c;
+
+       channels = p->pcm_channels;
+
+       for (i = 0; i < data_blocks; ++i) {
+               for (c = 0; c < channels; ++c)
+                       buffer[c] = 0x00000000;
+               buffer += s->data_block_quadlets;
+       }
+}
+
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                     struct snd_pcm_runtime *runtime)
+{
+       int err;
+
+       /*
+        * Our implementation allows this protocol to deliver 24 bit sample in
+        * 32bit data channel.
+        */
+       err = snd_pcm_hw_constraint_msbits(runtime, 0, 32, 24);
+       if (err < 0)
+               return err;
+
+       return amdtp_stream_add_pcm_hw_constraints(s, runtime);
+}
+
+void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format)
+{
+       struct amdtp_tscm *p = s->protocol;
+
+       if (WARN_ON(amdtp_stream_pcm_running(s)))
+               return;
+
+       switch (format) {
+       default:
+               WARN_ON(1);
+               /* fall through */
+       case SNDRV_PCM_FORMAT_S16:
+               if (s->direction == AMDTP_OUT_STREAM) {
+                       p->transfer_samples = write_pcm_s16;
+                       break;
+               }
+               WARN_ON(1);
+               /* fall through */
+       case SNDRV_PCM_FORMAT_S32:
+               if (s->direction == AMDTP_OUT_STREAM)
+                       p->transfer_samples = write_pcm_s32;
+               else
+                       p->transfer_samples = read_pcm_s32;
+               break;
+       }
+}
+
+static unsigned int process_tx_data_blocks(struct amdtp_stream *s,
+                                          __be32 *buffer,
+                                          unsigned int data_blocks,
+                                          unsigned int *syt)
+{
+       struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol;
+       struct snd_pcm_substream *pcm;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (data_blocks > 0 && pcm)
+               p->transfer_samples(s, pcm, buffer, data_blocks);
+
+       /* A place holder for control messages. */
+
+       return data_blocks;
+}
+
+static unsigned int process_rx_data_blocks(struct amdtp_stream *s,
+                                          __be32 *buffer,
+                                          unsigned int data_blocks,
+                                          unsigned int *syt)
+{
+       struct amdtp_tscm *p = (struct amdtp_tscm *)s->protocol;
+       struct snd_pcm_substream *pcm;
+
+       /* This field is not used. */
+       *syt = 0x0000;
+
+       pcm = ACCESS_ONCE(s->pcm);
+       if (pcm)
+               p->transfer_samples(s, pcm, buffer, data_blocks);
+       else
+               write_pcm_silence(s, buffer, data_blocks);
+
+       return data_blocks;
+}
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+                   enum amdtp_stream_direction dir, unsigned int pcm_channels)
+{
+       amdtp_stream_process_data_blocks_t process_data_blocks;
+       struct amdtp_tscm *p;
+       unsigned int fmt;
+       int err;
+
+       if (dir == AMDTP_IN_STREAM) {
+               fmt = AMDTP_FMT_TSCM_TX;
+               process_data_blocks = process_tx_data_blocks;
+       } else {
+               fmt = AMDTP_FMT_TSCM_RX;
+               process_data_blocks = process_rx_data_blocks;
+       }
+
+       err = amdtp_stream_init(s, unit, dir,
+                               CIP_NONBLOCKING | CIP_SKIP_DBC_ZERO_CHECK, fmt,
+                               process_data_blocks, sizeof(struct amdtp_tscm));
+       if (err < 0)
+               return 0;
+
+       /* Use fixed value for FDF field. */
+       s->fdf = 0x00;
+
+       /* This protocol uses fixed number of data channels for PCM samples. */
+       p = s->protocol;
+       p->pcm_channels = pcm_channels;
+
+       return 0;
+}
diff --git a/sound/firewire/tascam/tascam-hwdep.c b/sound/firewire/tascam/tascam-hwdep.c
new file mode 100644 (file)
index 0000000..131267c
--- /dev/null
@@ -0,0 +1,201 @@
+/*
+ * tascam-hwdep.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+/*
+ * This codes give three functionality.
+ *
+ * 1.get firewire node information
+ * 2.get notification about starting/stopping stream
+ * 3.lock/unlock stream
+ */
+
+#include "tascam.h"
+
+static long hwdep_read_locked(struct snd_tscm *tscm, char __user *buf,
+                             long count)
+{
+       union snd_firewire_event event;
+
+       memset(&event, 0, sizeof(event));
+
+       event.lock_status.type = SNDRV_FIREWIRE_EVENT_LOCK_STATUS;
+       event.lock_status.status = (tscm->dev_lock_count > 0);
+       tscm->dev_lock_changed = false;
+
+       count = min_t(long, count, sizeof(event.lock_status));
+
+       if (copy_to_user(buf, &event, count))
+               return -EFAULT;
+
+       return count;
+}
+
+static long hwdep_read(struct snd_hwdep *hwdep, char __user *buf, long count,
+                      loff_t *offset)
+{
+       struct snd_tscm *tscm = hwdep->private_data;
+       DEFINE_WAIT(wait);
+       union snd_firewire_event event;
+
+       spin_lock_irq(&tscm->lock);
+
+       while (!tscm->dev_lock_changed) {
+               prepare_to_wait(&tscm->hwdep_wait, &wait, TASK_INTERRUPTIBLE);
+               spin_unlock_irq(&tscm->lock);
+               schedule();
+               finish_wait(&tscm->hwdep_wait, &wait);
+               if (signal_pending(current))
+                       return -ERESTARTSYS;
+               spin_lock_irq(&tscm->lock);
+       }
+
+       memset(&event, 0, sizeof(event));
+       count = hwdep_read_locked(tscm, buf, count);
+       spin_unlock_irq(&tscm->lock);
+
+       return count;
+}
+
+static unsigned int hwdep_poll(struct snd_hwdep *hwdep, struct file *file,
+                              poll_table *wait)
+{
+       struct snd_tscm *tscm = hwdep->private_data;
+       unsigned int events;
+
+       poll_wait(file, &tscm->hwdep_wait, wait);
+
+       spin_lock_irq(&tscm->lock);
+       if (tscm->dev_lock_changed)
+               events = POLLIN | POLLRDNORM;
+       else
+               events = 0;
+       spin_unlock_irq(&tscm->lock);
+
+       return events;
+}
+
+static int hwdep_get_info(struct snd_tscm *tscm, void __user *arg)
+{
+       struct fw_device *dev = fw_parent_device(tscm->unit);
+       struct snd_firewire_get_info info;
+
+       memset(&info, 0, sizeof(info));
+       info.type = SNDRV_FIREWIRE_TYPE_TASCAM;
+       info.card = dev->card->index;
+       *(__be32 *)&info.guid[0] = cpu_to_be32(dev->config_rom[3]);
+       *(__be32 *)&info.guid[4] = cpu_to_be32(dev->config_rom[4]);
+       strlcpy(info.device_name, dev_name(&dev->device),
+               sizeof(info.device_name));
+
+       if (copy_to_user(arg, &info, sizeof(info)))
+               return -EFAULT;
+
+       return 0;
+}
+
+static int hwdep_lock(struct snd_tscm *tscm)
+{
+       int err;
+
+       spin_lock_irq(&tscm->lock);
+
+       if (tscm->dev_lock_count == 0) {
+               tscm->dev_lock_count = -1;
+               err = 0;
+       } else {
+               err = -EBUSY;
+       }
+
+       spin_unlock_irq(&tscm->lock);
+
+       return err;
+}
+
+static int hwdep_unlock(struct snd_tscm *tscm)
+{
+       int err;
+
+       spin_lock_irq(&tscm->lock);
+
+       if (tscm->dev_lock_count == -1) {
+               tscm->dev_lock_count = 0;
+               err = 0;
+       } else {
+               err = -EBADFD;
+       }
+
+       spin_unlock_irq(&tscm->lock);
+
+       return err;
+}
+
+static int hwdep_release(struct snd_hwdep *hwdep, struct file *file)
+{
+       struct snd_tscm *tscm = hwdep->private_data;
+
+       spin_lock_irq(&tscm->lock);
+       if (tscm->dev_lock_count == -1)
+               tscm->dev_lock_count = 0;
+       spin_unlock_irq(&tscm->lock);
+
+       return 0;
+}
+
+static int hwdep_ioctl(struct snd_hwdep *hwdep, struct file *file,
+           unsigned int cmd, unsigned long arg)
+{
+       struct snd_tscm *tscm = hwdep->private_data;
+
+       switch (cmd) {
+       case SNDRV_FIREWIRE_IOCTL_GET_INFO:
+               return hwdep_get_info(tscm, (void __user *)arg);
+       case SNDRV_FIREWIRE_IOCTL_LOCK:
+               return hwdep_lock(tscm);
+       case SNDRV_FIREWIRE_IOCTL_UNLOCK:
+               return hwdep_unlock(tscm);
+       default:
+               return -ENOIOCTLCMD;
+       }
+}
+
+#ifdef CONFIG_COMPAT
+static int hwdep_compat_ioctl(struct snd_hwdep *hwdep, struct file *file,
+                             unsigned int cmd, unsigned long arg)
+{
+       return hwdep_ioctl(hwdep, file, cmd,
+                          (unsigned long)compat_ptr(arg));
+}
+#else
+#define hwdep_compat_ioctl NULL
+#endif
+
+static const struct snd_hwdep_ops hwdep_ops = {
+       .read           = hwdep_read,
+       .release        = hwdep_release,
+       .poll           = hwdep_poll,
+       .ioctl          = hwdep_ioctl,
+       .ioctl_compat   = hwdep_compat_ioctl,
+};
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm)
+{
+       struct snd_hwdep *hwdep;
+       int err;
+
+       err = snd_hwdep_new(tscm->card, "Tascam", 0, &hwdep);
+       if (err < 0)
+               return err;
+
+       strcpy(hwdep->name, "Tascam");
+       hwdep->iface = SNDRV_HWDEP_IFACE_FW_TASCAM;
+       hwdep->ops = hwdep_ops;
+       hwdep->private_data = tscm;
+       hwdep->exclusive = true;
+
+       return err;
+}
diff --git a/sound/firewire/tascam/tascam-midi.c b/sound/firewire/tascam/tascam-midi.c
new file mode 100644 (file)
index 0000000..41f8420
--- /dev/null
@@ -0,0 +1,135 @@
+/*
+ * tascam-midi.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+static int midi_capture_open(struct snd_rawmidi_substream *substream)
+{
+       /* Do nothing. */
+       return 0;
+}
+
+static int midi_playback_open(struct snd_rawmidi_substream *substream)
+{
+       struct snd_tscm *tscm = substream->rmidi->private_data;
+
+       /* Initialize internal status. */
+       tscm->running_status[substream->number] = 0;
+       tscm->on_sysex[substream->number] = 0;
+       return 0;
+}
+
+static int midi_capture_close(struct snd_rawmidi_substream *substream)
+{
+       /* Do nothing. */
+       return 0;
+}
+
+static int midi_playback_close(struct snd_rawmidi_substream *substream)
+{
+       struct snd_tscm *tscm = substream->rmidi->private_data;
+
+       snd_fw_async_midi_port_finish(&tscm->out_ports[substream->number]);
+
+       return 0;
+}
+
+static void midi_capture_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+       struct snd_tscm *tscm = substrm->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&tscm->lock, flags);
+
+       if (up)
+               tscm->tx_midi_substreams[substrm->number] = substrm;
+       else
+               tscm->tx_midi_substreams[substrm->number] = NULL;
+
+       spin_unlock_irqrestore(&tscm->lock, flags);
+}
+
+static void midi_playback_trigger(struct snd_rawmidi_substream *substrm, int up)
+{
+       struct snd_tscm *tscm = substrm->rmidi->private_data;
+       unsigned long flags;
+
+       spin_lock_irqsave(&tscm->lock, flags);
+
+       if (up)
+               snd_fw_async_midi_port_run(&tscm->out_ports[substrm->number],
+                                          substrm);
+
+       spin_unlock_irqrestore(&tscm->lock, flags);
+}
+
+static struct snd_rawmidi_ops midi_capture_ops = {
+       .open           = midi_capture_open,
+       .close          = midi_capture_close,
+       .trigger        = midi_capture_trigger,
+};
+
+static struct snd_rawmidi_ops midi_playback_ops = {
+       .open           = midi_playback_open,
+       .close          = midi_playback_close,
+       .trigger        = midi_playback_trigger,
+};
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm)
+{
+       struct snd_rawmidi *rmidi;
+       struct snd_rawmidi_str *stream;
+       struct snd_rawmidi_substream *subs;
+       int err;
+
+       err = snd_rawmidi_new(tscm->card, tscm->card->driver, 0,
+                             tscm->spec->midi_playback_ports,
+                             tscm->spec->midi_capture_ports,
+                             &rmidi);
+       if (err < 0)
+               return err;
+
+       snprintf(rmidi->name, sizeof(rmidi->name),
+                "%s MIDI", tscm->card->shortname);
+       rmidi->private_data = tscm;
+
+       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_INPUT;
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_INPUT,
+                           &midi_capture_ops);
+       stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_INPUT];
+
+       /* Set port names for MIDI input. */
+       list_for_each_entry(subs, &stream->substreams, list) {
+               /* TODO: support virtual MIDI ports. */
+               if (subs->number < tscm->spec->midi_capture_ports) {
+                       /* Hardware MIDI ports. */
+                       snprintf(subs->name, sizeof(subs->name),
+                                "%s MIDI %d",
+                                tscm->card->shortname, subs->number + 1);
+               }
+       }
+
+       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_OUTPUT;
+       snd_rawmidi_set_ops(rmidi, SNDRV_RAWMIDI_STREAM_OUTPUT,
+                           &midi_playback_ops);
+       stream = &rmidi->streams[SNDRV_RAWMIDI_STREAM_OUTPUT];
+
+       /* Set port names for MIDI ourput. */
+       list_for_each_entry(subs, &stream->substreams, list) {
+               if (subs->number < tscm->spec->midi_playback_ports) {
+                       /* Hardware MIDI ports only. */
+                       snprintf(subs->name, sizeof(subs->name),
+                                "%s MIDI %d",
+                                tscm->card->shortname, subs->number + 1);
+               }
+       }
+
+       rmidi->info_flags |= SNDRV_RAWMIDI_INFO_DUPLEX;
+
+       return 0;
+}
diff --git a/sound/firewire/tascam/tascam-pcm.c b/sound/firewire/tascam/tascam-pcm.c
new file mode 100644 (file)
index 0000000..380d3db
--- /dev/null
@@ -0,0 +1,312 @@
+/*
+ * tascam-pcm.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+static void set_buffer_params(struct snd_pcm_hardware *hw)
+{
+       hw->period_bytes_min = 4 * hw->channels_min;
+       hw->period_bytes_max = hw->period_bytes_min * 2048;
+       hw->buffer_bytes_max = hw->period_bytes_max * 2;
+
+       hw->periods_min = 2;
+       hw->periods_max = UINT_MAX;
+}
+
+static int pcm_init_hw_params(struct snd_tscm *tscm,
+                             struct snd_pcm_substream *substream)
+{
+       static const struct snd_pcm_hardware hardware = {
+               .info = SNDRV_PCM_INFO_BATCH |
+                       SNDRV_PCM_INFO_BLOCK_TRANSFER |
+                       SNDRV_PCM_INFO_INTERLEAVED |
+                       SNDRV_PCM_INFO_JOINT_DUPLEX |
+                       SNDRV_PCM_INFO_MMAP |
+                       SNDRV_PCM_INFO_MMAP_VALID,
+               .rates = SNDRV_PCM_RATE_44100 |
+                        SNDRV_PCM_RATE_48000 |
+                        SNDRV_PCM_RATE_88200 |
+                        SNDRV_PCM_RATE_96000,
+               .rate_min = 44100,
+               .rate_max = 96000,
+               .channels_min = 10,
+               .channels_max = 18,
+       };
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct amdtp_stream *stream;
+       unsigned int pcm_channels;
+
+       runtime->hw = hardware;
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE) {
+               runtime->hw.formats = SNDRV_PCM_FMTBIT_S32;
+               stream = &tscm->tx_stream;
+               pcm_channels = tscm->spec->pcm_capture_analog_channels;
+       } else {
+               runtime->hw.formats =
+                               SNDRV_PCM_FMTBIT_S16 | SNDRV_PCM_FMTBIT_S32;
+               stream = &tscm->rx_stream;
+               pcm_channels = tscm->spec->pcm_playback_analog_channels;
+       }
+
+       if (tscm->spec->has_adat)
+               pcm_channels += 8;
+       if (tscm->spec->has_spdif)
+               pcm_channels += 2;
+       runtime->hw.channels_min = runtime->hw.channels_max = pcm_channels;
+
+       set_buffer_params(&runtime->hw);
+
+       return amdtp_tscm_add_pcm_hw_constraints(stream, runtime);
+}
+
+static int pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_tscm *tscm = substream->private_data;
+       enum snd_tscm_clock clock;
+       unsigned int rate;
+       int err;
+
+       err = snd_tscm_stream_lock_try(tscm);
+       if (err < 0)
+               goto end;
+
+       err = pcm_init_hw_params(tscm, substream);
+       if (err < 0)
+               goto err_locked;
+
+       err = snd_tscm_stream_get_clock(tscm, &clock);
+       if (clock != SND_TSCM_CLOCK_INTERNAL ||
+           amdtp_stream_pcm_running(&tscm->rx_stream) ||
+           amdtp_stream_pcm_running(&tscm->tx_stream)) {
+               err = snd_tscm_stream_get_rate(tscm, &rate);
+               if (err < 0)
+                       goto err_locked;
+               substream->runtime->hw.rate_min = rate;
+               substream->runtime->hw.rate_max = rate;
+       }
+
+       snd_pcm_set_sync(substream);
+end:
+       return err;
+err_locked:
+       snd_tscm_stream_lock_release(tscm);
+       return err;
+}
+
+static int pcm_close(struct snd_pcm_substream *substream)
+{
+       struct snd_tscm *tscm = substream->private_data;
+
+       snd_tscm_stream_lock_release(tscm);
+
+       return 0;
+}
+
+static int pcm_capture_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_tscm *tscm = substream->private_data;
+       int err;
+
+       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                              params_buffer_bytes(hw_params));
+       if (err < 0)
+               return err;
+
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&tscm->mutex);
+               tscm->substreams_counter++;
+               mutex_unlock(&tscm->mutex);
+       }
+
+       amdtp_tscm_set_pcm_format(&tscm->tx_stream, params_format(hw_params));
+
+       return 0;
+}
+
+static int pcm_playback_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *hw_params)
+{
+       struct snd_tscm *tscm = substream->private_data;
+       int err;
+
+       err = snd_pcm_lib_alloc_vmalloc_buffer(substream,
+                                              params_buffer_bytes(hw_params));
+       if (err < 0)
+               return err;
+
+       if (substream->runtime->status->state == SNDRV_PCM_STATE_OPEN) {
+               mutex_lock(&tscm->mutex);
+               tscm->substreams_counter++;
+               mutex_unlock(&tscm->mutex);
+       }
+
+       amdtp_tscm_set_pcm_format(&tscm->rx_stream, params_format(hw_params));
+
+       return 0;
+}
+
+static int pcm_capture_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_tscm *tscm = substream->private_data;
+
+       mutex_lock(&tscm->mutex);
+
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+               tscm->substreams_counter--;
+
+       snd_tscm_stream_stop_duplex(tscm);
+
+       mutex_unlock(&tscm->mutex);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_playback_hw_free(struct snd_pcm_substream *substream)
+{
+       struct snd_tscm *tscm = substream->private_data;
+
+       mutex_lock(&tscm->mutex);
+
+       if (substream->runtime->status->state != SNDRV_PCM_STATE_OPEN)
+               tscm->substreams_counter--;
+
+       snd_tscm_stream_stop_duplex(tscm);
+
+       mutex_unlock(&tscm->mutex);
+
+       return snd_pcm_lib_free_vmalloc_buffer(substream);
+}
+
+static int pcm_capture_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_tscm *tscm = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       mutex_lock(&tscm->mutex);
+
+       err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+       if (err >= 0)
+               amdtp_stream_pcm_prepare(&tscm->tx_stream);
+
+       mutex_unlock(&tscm->mutex);
+
+       return err;
+}
+
+static int pcm_playback_prepare(struct snd_pcm_substream *substream)
+{
+       struct snd_tscm *tscm = substream->private_data;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int err;
+
+       mutex_lock(&tscm->mutex);
+
+       err = snd_tscm_stream_start_duplex(tscm, runtime->rate);
+       if (err >= 0)
+               amdtp_stream_pcm_prepare(&tscm->rx_stream);
+
+       mutex_unlock(&tscm->mutex);
+
+       return err;
+}
+
+static int pcm_capture_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_tscm *tscm = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               amdtp_stream_pcm_trigger(&tscm->tx_stream, substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               amdtp_stream_pcm_trigger(&tscm->tx_stream, NULL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int pcm_playback_trigger(struct snd_pcm_substream *substream, int cmd)
+{
+       struct snd_tscm *tscm = substream->private_data;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               amdtp_stream_pcm_trigger(&tscm->rx_stream, substream);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               amdtp_stream_pcm_trigger(&tscm->rx_stream, NULL);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static snd_pcm_uframes_t pcm_capture_pointer(struct snd_pcm_substream *sbstrm)
+{
+       struct snd_tscm *tscm = sbstrm->private_data;
+
+       return amdtp_stream_pcm_pointer(&tscm->tx_stream);
+}
+
+static snd_pcm_uframes_t pcm_playback_pointer(struct snd_pcm_substream *sbstrm)
+{
+       struct snd_tscm *tscm = sbstrm->private_data;
+
+       return amdtp_stream_pcm_pointer(&tscm->rx_stream);
+}
+
+static struct snd_pcm_ops pcm_capture_ops = {
+       .open           = pcm_open,
+       .close          = pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = pcm_capture_hw_params,
+       .hw_free        = pcm_capture_hw_free,
+       .prepare        = pcm_capture_prepare,
+       .trigger        = pcm_capture_trigger,
+       .pointer        = pcm_capture_pointer,
+       .page           = snd_pcm_lib_get_vmalloc_page,
+};
+
+static struct snd_pcm_ops pcm_playback_ops = {
+       .open           = pcm_open,
+       .close          = pcm_close,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = pcm_playback_hw_params,
+       .hw_free        = pcm_playback_hw_free,
+       .prepare        = pcm_playback_prepare,
+       .trigger        = pcm_playback_trigger,
+       .pointer        = pcm_playback_pointer,
+       .page           = snd_pcm_lib_get_vmalloc_page,
+       .mmap           = snd_pcm_lib_mmap_vmalloc,
+};
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm)
+{
+       struct snd_pcm *pcm;
+       int err;
+
+       err = snd_pcm_new(tscm->card, tscm->card->driver, 0, 1, 1, &pcm);
+       if (err < 0)
+               return err;
+
+       pcm->private_data = tscm;
+       snprintf(pcm->name, sizeof(pcm->name),
+                "%s PCM", tscm->card->shortname);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &pcm_playback_ops);
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_CAPTURE, &pcm_capture_ops);
+
+       return 0;
+}
diff --git a/sound/firewire/tascam/tascam-proc.c b/sound/firewire/tascam/tascam-proc.c
new file mode 100644 (file)
index 0000000..bfd4a4c
--- /dev/null
@@ -0,0 +1,88 @@
+/*
+ * tascam-proc.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "./tascam.h"
+
+static void proc_read_firmware(struct snd_info_entry *entry,
+                              struct snd_info_buffer *buffer)
+{
+       struct snd_tscm *tscm = entry->private_data;
+       __be32 data;
+       unsigned int reg, fpga, arm, hw;
+       int err;
+
+       err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+                       TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_REGISTER,
+                       &data, sizeof(data), 0);
+       if (err < 0)
+               return;
+       reg = be32_to_cpu(data);
+
+       err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+                       TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_FPGA,
+                       &data, sizeof(data), 0);
+       if (err < 0)
+               return;
+       fpga = be32_to_cpu(data);
+
+       err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+                       TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_ARM,
+                       &data, sizeof(data), 0);
+       if (err < 0)
+               return;
+       arm = be32_to_cpu(data);
+
+       err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+                       TSCM_ADDR_BASE + TSCM_OFFSET_FIRMWARE_HW,
+                       &data, sizeof(data), 0);
+       if (err < 0)
+               return;
+       hw = be32_to_cpu(data);
+
+       snd_iprintf(buffer, "Register: %d (0x%08x)\n", reg & 0xffff, reg);
+       snd_iprintf(buffer, "FPGA:     %d (0x%08x)\n", fpga & 0xffff, fpga);
+       snd_iprintf(buffer, "ARM:      %d (0x%08x)\n", arm & 0xffff, arm);
+       snd_iprintf(buffer, "Hardware: %d (0x%08x)\n", hw >> 16, hw);
+}
+
+static void add_node(struct snd_tscm *tscm, struct snd_info_entry *root,
+                    const char *name,
+                    void (*op)(struct snd_info_entry *e,
+                               struct snd_info_buffer *b))
+{
+       struct snd_info_entry *entry;
+
+       entry = snd_info_create_card_entry(tscm->card, name, root);
+       if (entry == NULL)
+               return;
+
+       snd_info_set_text_ops(entry, tscm, op);
+       if (snd_info_register(entry) < 0)
+               snd_info_free_entry(entry);
+}
+
+void snd_tscm_proc_init(struct snd_tscm *tscm)
+{
+       struct snd_info_entry *root;
+
+       /*
+        * All nodes are automatically removed at snd_card_disconnect(),
+        * by following to link list.
+        */
+       root = snd_info_create_card_entry(tscm->card, "firewire",
+                                         tscm->card->proc_root);
+       if (root == NULL)
+               return;
+       root->mode = S_IFDIR | S_IRUGO | S_IXUGO;
+       if (snd_info_register(root) < 0) {
+               snd_info_free_entry(root);
+               return;
+       }
+
+       add_node(tscm, root, "firmware", proc_read_firmware);
+}
diff --git a/sound/firewire/tascam/tascam-stream.c b/sound/firewire/tascam/tascam-stream.c
new file mode 100644 (file)
index 0000000..0e6dd5c
--- /dev/null
@@ -0,0 +1,496 @@
+/*
+ * tascam-stream.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include <linux/delay.h>
+#include "tascam.h"
+
+#define CALLBACK_TIMEOUT 500
+
+static int get_clock(struct snd_tscm *tscm, u32 *data)
+{
+       __be32 reg;
+       int err;
+
+       err = snd_fw_transaction(tscm->unit, TCODE_READ_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+                                &reg, sizeof(reg), 0);
+       if (err >= 0)
+               *data = be32_to_cpu(reg);
+
+       return err;
+}
+
+static int set_clock(struct snd_tscm *tscm, unsigned int rate,
+                    enum snd_tscm_clock clock)
+{
+       u32 data;
+       __be32 reg;
+       int err;
+
+       err = get_clock(tscm, &data);
+       if (err < 0)
+               return err;
+       data &= 0x0000ffff;
+
+       if (rate > 0) {
+               data &= 0x000000ff;
+               /* Base rate. */
+               if ((rate % 44100) == 0) {
+                       data |= 0x00000100;
+                       /* Multiplier. */
+                       if (rate / 44100 == 2)
+                               data |= 0x00008000;
+               } else if ((rate % 48000) == 0) {
+                       data |= 0x00000200;
+                       /* Multiplier. */
+                       if (rate / 48000 == 2)
+                               data |= 0x00008000;
+               } else {
+                       return -EAGAIN;
+               }
+       }
+
+       if (clock != INT_MAX) {
+               data &= 0x0000ff00;
+               data |= clock + 1;
+       }
+
+       reg = cpu_to_be32(data);
+
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_CLOCK_STATUS,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       if (data & 0x00008000)
+               reg = cpu_to_be32(0x0000001a);
+       else
+               reg = cpu_to_be32(0x0000000d);
+
+       return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                 TSCM_ADDR_BASE + TSCM_OFFSET_MULTIPLEX_MODE,
+                                 &reg, sizeof(reg), 0);
+}
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate)
+{
+       u32 data = 0x0;
+       unsigned int trials = 0;
+       int err;
+
+       while (data == 0x0 || trials++ < 5) {
+               err = get_clock(tscm, &data);
+               if (err < 0)
+                       return err;
+
+               data = (data & 0xff000000) >> 24;
+       }
+
+       /* Check base rate. */
+       if ((data & 0x0f) == 0x01)
+               *rate = 44100;
+       else if ((data & 0x0f) == 0x02)
+               *rate = 48000;
+       else
+               return -EAGAIN;
+
+       /* Check multiplier. */
+       if ((data & 0xf0) == 0x80)
+               *rate *= 2;
+       else if ((data & 0xf0) != 0x00)
+               return -EAGAIN;
+
+       return err;
+}
+
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm, enum snd_tscm_clock *clock)
+{
+       u32 data;
+       int err;
+
+       err = get_clock(tscm, &data);
+       if (err < 0)
+               return err;
+
+       *clock = ((data & 0x00ff0000) >> 16) - 1;
+       if (*clock < 0 || *clock > SND_TSCM_CLOCK_ADAT)
+               return -EIO;
+
+       return 0;
+}
+
+static int enable_data_channels(struct snd_tscm *tscm)
+{
+       __be32 reg;
+       u32 data;
+       unsigned int i;
+       int err;
+
+       data = 0;
+       for (i = 0; i < tscm->spec->pcm_capture_analog_channels; ++i)
+               data |= BIT(i);
+       if (tscm->spec->has_adat)
+               data |= 0x0000ff00;
+       if (tscm->spec->has_spdif)
+               data |= 0x00030000;
+
+       reg = cpu_to_be32(data);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_TX_PCM_CHANNELS,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       data = 0;
+       for (i = 0; i < tscm->spec->pcm_playback_analog_channels; ++i)
+               data |= BIT(i);
+       if (tscm->spec->has_adat)
+               data |= 0x0000ff00;
+       if (tscm->spec->has_spdif)
+               data |= 0x00030000;
+
+       reg = cpu_to_be32(data);
+       return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                 TSCM_ADDR_BASE + TSCM_OFFSET_RX_PCM_CHANNELS,
+                                 &reg, sizeof(reg), 0);
+}
+
+static int set_stream_formats(struct snd_tscm *tscm, unsigned int rate)
+{
+       __be32 reg;
+       int err;
+
+       /* Set an option for unknown purpose. */
+       reg = cpu_to_be32(0x00200000);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       err = enable_data_channels(tscm);
+       if (err < 0)
+               return err;
+
+       return set_clock(tscm, rate, INT_MAX);
+}
+
+static void finish_session(struct snd_tscm *tscm)
+{
+       __be32 reg;
+
+       reg = 0;
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+                          &reg, sizeof(reg), 0);
+
+       reg = 0;
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+                          &reg, sizeof(reg), 0);
+
+}
+
+static int begin_session(struct snd_tscm *tscm)
+{
+       __be32 reg;
+       int err;
+
+       reg = cpu_to_be32(0x00000001);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_START_STREAMING,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       reg = cpu_to_be32(0x00000001);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_ON,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       /* Set an option for unknown purpose. */
+       reg = cpu_to_be32(0x00002000);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_SET_OPTION,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       /* Start multiplexing PCM samples on packets. */
+       reg = cpu_to_be32(0x00000001);
+       return snd_fw_transaction(tscm->unit,
+                                 TCODE_WRITE_QUADLET_REQUEST,
+                                 TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_ON,
+                                 &reg, sizeof(reg), 0);
+}
+
+static void release_resources(struct snd_tscm *tscm)
+{
+       __be32 reg;
+
+       /* Unregister channels. */
+       reg = cpu_to_be32(0x00000000);
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+                          &reg, sizeof(reg), 0);
+       reg = cpu_to_be32(0x00000000);
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+                          &reg, sizeof(reg), 0);
+       reg = cpu_to_be32(0x00000000);
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+                          &reg, sizeof(reg), 0);
+
+       /* Release isochronous resources. */
+       fw_iso_resources_free(&tscm->tx_resources);
+       fw_iso_resources_free(&tscm->rx_resources);
+}
+
+static int keep_resources(struct snd_tscm *tscm, unsigned int rate)
+{
+       __be32 reg;
+       int err;
+
+       /* Keep resources for in-stream. */
+       err = amdtp_tscm_set_parameters(&tscm->tx_stream, rate);
+       if (err < 0)
+               return err;
+       err = fw_iso_resources_allocate(&tscm->tx_resources,
+                       amdtp_stream_get_max_payload(&tscm->tx_stream),
+                       fw_parent_device(tscm->unit)->max_speed);
+       if (err < 0)
+               goto error;
+
+       /* Keep resources for out-stream. */
+       err = amdtp_tscm_set_parameters(&tscm->rx_stream, rate);
+       if (err < 0)
+               return err;
+       err = fw_iso_resources_allocate(&tscm->rx_resources,
+                       amdtp_stream_get_max_payload(&tscm->rx_stream),
+                       fw_parent_device(tscm->unit)->max_speed);
+       if (err < 0)
+               return err;
+
+       /* Register the isochronous channel for transmitting stream. */
+       reg = cpu_to_be32(tscm->tx_resources.channel);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_TX_CH,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               goto error;
+
+       /* Unknown */
+       reg = cpu_to_be32(0x00000002);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_UNKNOWN,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               goto error;
+
+       /* Register the isochronous channel for receiving stream. */
+       reg = cpu_to_be32(tscm->rx_resources.channel);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_ISOC_RX_CH,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               goto error;
+
+       return 0;
+error:
+       release_resources(tscm);
+       return err;
+}
+
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm)
+{
+       unsigned int pcm_channels;
+       int err;
+
+       /* For out-stream. */
+       err = fw_iso_resources_init(&tscm->rx_resources, tscm->unit);
+       if (err < 0)
+               return err;
+       pcm_channels = tscm->spec->pcm_playback_analog_channels;
+       if (tscm->spec->has_adat)
+               pcm_channels += 8;
+       if (tscm->spec->has_spdif)
+               pcm_channels += 2;
+       err = amdtp_tscm_init(&tscm->rx_stream, tscm->unit, AMDTP_OUT_STREAM,
+                             pcm_channels);
+       if (err < 0)
+               return err;
+
+       /* For in-stream. */
+       err = fw_iso_resources_init(&tscm->tx_resources, tscm->unit);
+       if (err < 0)
+               return err;
+       pcm_channels = tscm->spec->pcm_capture_analog_channels;
+       if (tscm->spec->has_adat)
+               pcm_channels += 8;
+       if (tscm->spec->has_spdif)
+               pcm_channels += 2;
+       err = amdtp_tscm_init(&tscm->tx_stream, tscm->unit, AMDTP_IN_STREAM,
+                             pcm_channels);
+       if (err < 0)
+               amdtp_stream_destroy(&tscm->rx_stream);
+
+       return 0;
+}
+
+/* At bus reset, streaming is stopped and some registers are clear. */
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm)
+{
+       amdtp_stream_pcm_abort(&tscm->tx_stream);
+       amdtp_stream_stop(&tscm->tx_stream);
+
+       amdtp_stream_pcm_abort(&tscm->rx_stream);
+       amdtp_stream_stop(&tscm->rx_stream);
+}
+
+/*
+ * This function should be called before starting streams or after stopping
+ * streams.
+ */
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm)
+{
+       amdtp_stream_destroy(&tscm->rx_stream);
+       amdtp_stream_destroy(&tscm->tx_stream);
+
+       fw_iso_resources_destroy(&tscm->rx_resources);
+       fw_iso_resources_destroy(&tscm->tx_resources);
+}
+
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate)
+{
+       unsigned int curr_rate;
+       int err;
+
+       if (tscm->substreams_counter == 0)
+               return 0;
+
+       err = snd_tscm_stream_get_rate(tscm, &curr_rate);
+       if (err < 0)
+               return err;
+       if (curr_rate != rate ||
+           amdtp_streaming_error(&tscm->tx_stream) ||
+           amdtp_streaming_error(&tscm->rx_stream)) {
+               finish_session(tscm);
+
+               amdtp_stream_stop(&tscm->tx_stream);
+               amdtp_stream_stop(&tscm->rx_stream);
+
+               release_resources(tscm);
+       }
+
+       if (!amdtp_stream_running(&tscm->tx_stream)) {
+               amdtp_stream_set_sync(CIP_SYNC_TO_DEVICE,
+                                     &tscm->tx_stream, &tscm->rx_stream);
+               err = keep_resources(tscm, rate);
+               if (err < 0)
+                       goto error;
+
+               err = set_stream_formats(tscm, rate);
+               if (err < 0)
+                       goto error;
+
+               err = begin_session(tscm);
+               if (err < 0)
+                       goto error;
+
+               err = amdtp_stream_start(&tscm->tx_stream,
+                               tscm->tx_resources.channel,
+                               fw_parent_device(tscm->unit)->max_speed);
+               if (err < 0)
+                       goto error;
+
+               if (!amdtp_stream_wait_callback(&tscm->tx_stream,
+                                               CALLBACK_TIMEOUT)) {
+                       err = -ETIMEDOUT;
+                       goto error;
+               }
+       }
+
+       if (!amdtp_stream_running(&tscm->rx_stream)) {
+               err = amdtp_stream_start(&tscm->rx_stream,
+                               tscm->rx_resources.channel,
+                               fw_parent_device(tscm->unit)->max_speed);
+               if (err < 0)
+                       goto error;
+
+               if (!amdtp_stream_wait_callback(&tscm->rx_stream,
+                                               CALLBACK_TIMEOUT)) {
+                       err = -ETIMEDOUT;
+                       goto error;
+               }
+       }
+
+       return 0;
+error:
+       amdtp_stream_stop(&tscm->tx_stream);
+       amdtp_stream_stop(&tscm->rx_stream);
+
+       finish_session(tscm);
+       release_resources(tscm);
+
+       return err;
+}
+
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm)
+{
+       if (tscm->substreams_counter > 0)
+               return;
+
+       amdtp_stream_stop(&tscm->tx_stream);
+       amdtp_stream_stop(&tscm->rx_stream);
+
+       finish_session(tscm);
+       release_resources(tscm);
+}
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm)
+{
+       tscm->dev_lock_changed = true;
+       wake_up(&tscm->hwdep_wait);
+}
+
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm)
+{
+       int err;
+
+       spin_lock_irq(&tscm->lock);
+
+       /* user land lock this */
+       if (tscm->dev_lock_count < 0) {
+               err = -EBUSY;
+               goto end;
+       }
+
+       /* this is the first time */
+       if (tscm->dev_lock_count++ == 0)
+               snd_tscm_stream_lock_changed(tscm);
+       err = 0;
+end:
+       spin_unlock_irq(&tscm->lock);
+       return err;
+}
+
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm)
+{
+       spin_lock_irq(&tscm->lock);
+
+       if (WARN_ON(tscm->dev_lock_count <= 0))
+               goto end;
+       if (--tscm->dev_lock_count == 0)
+               snd_tscm_stream_lock_changed(tscm);
+end:
+       spin_unlock_irq(&tscm->lock);
+}
diff --git a/sound/firewire/tascam/tascam-transaction.c b/sound/firewire/tascam/tascam-transaction.c
new file mode 100644 (file)
index 0000000..904ce03
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * tascam-transaction.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+/*
+ * When return minus value, given argument is not MIDI status.
+ * When return 0, given argument is a beginning of system exclusive.
+ * When return the others, given argument is MIDI data.
+ */
+static inline int calculate_message_bytes(u8 status)
+{
+       switch (status) {
+       case 0xf6:      /* Tune request. */
+       case 0xf8:      /* Timing clock. */
+       case 0xfa:      /* Start. */
+       case 0xfb:      /* Continue. */
+       case 0xfc:      /* Stop. */
+       case 0xfe:      /* Active sensing. */
+       case 0xff:      /* System reset. */
+               return 1;
+       case 0xf1:      /* MIDI time code quarter frame. */
+       case 0xf3:      /* Song select. */
+               return 2;
+       case 0xf2:      /* Song position pointer. */
+               return 3;
+       case 0xf0:      /* Exclusive. */
+               return 0;
+       case 0xf7:      /* End of exclusive. */
+               break;
+       case 0xf4:      /* Undefined. */
+       case 0xf5:      /* Undefined. */
+       case 0xf9:      /* Undefined. */
+       case 0xfd:      /* Undefined. */
+               break;
+       default:
+               switch (status & 0xf0) {
+               case 0x80:      /* Note on. */
+               case 0x90:      /* Note off. */
+               case 0xa0:      /* Polyphonic key pressure. */
+               case 0xb0:      /* Control change and Mode change. */
+               case 0xe0:      /* Pitch bend change. */
+                       return 3;
+               case 0xc0:      /* Program change. */
+               case 0xd0:      /* Channel pressure. */
+                       return 2;
+               default:
+               break;
+               }
+       break;
+       }
+
+       return -EINVAL;
+}
+
+static int fill_message(struct snd_rawmidi_substream *substream, u8 *buf)
+{
+       struct snd_tscm *tscm = substream->rmidi->private_data;
+       unsigned int port = substream->number;
+       int i, len, consume;
+       u8 *label, *msg;
+       u8 status;
+
+       /* The first byte is used for label, the rest for MIDI bytes. */
+       label = buf;
+       msg = buf + 1;
+
+       consume = snd_rawmidi_transmit_peek(substream, msg, 3);
+       if (consume == 0)
+               return 0;
+
+       /* On exclusive message. */
+       if (tscm->on_sysex[port]) {
+               /* Seek the end of exclusives. */
+               for (i = 0; i < consume; ++i) {
+                       if (msg[i] == 0xf7) {
+                               tscm->on_sysex[port] = false;
+                               break;
+                       }
+               }
+
+               /* At the end of exclusive message, use label 0x07. */
+               if (!tscm->on_sysex[port]) {
+                       consume = i + 1;
+                       *label = (port << 4) | 0x07;
+               /* During exclusive message, use label 0x04. */
+               } else if (consume == 3) {
+                       *label = (port << 4) | 0x04;
+               /* We need to fill whole 3 bytes. Go to next change. */
+               } else {
+                       return 0;
+               }
+
+               len = consume;
+       } else {
+               /* The beginning of exclusives. */
+               if (msg[0] == 0xf0) {
+                       /* Transfer it in next chance in another condition. */
+                       tscm->on_sysex[port] = true;
+                       return 0;
+               } else {
+                       /* On running-status. */
+                       if ((msg[0] & 0x80) != 0x80)
+                               status = tscm->running_status[port];
+                       else
+                               status = msg[0];
+
+                       /* Calculate consume bytes. */
+                       len = calculate_message_bytes(status);
+                       if (len <= 0)
+                               return 0;
+
+                       /* On running-status. */
+                       if ((msg[0] & 0x80) != 0x80) {
+                               /* Enough MIDI bytes were not retrieved. */
+                               if (consume < len - 1)
+                                       return 0;
+                               consume = len - 1;
+
+                               msg[2] = msg[1];
+                               msg[1] = msg[0];
+                               msg[0] = tscm->running_status[port];
+                       } else {
+                               /* Enough MIDI bytes were not retrieved. */
+                               if (consume < len)
+                                       return 0;
+                               consume = len;
+
+                               tscm->running_status[port] = msg[0];
+                       }
+               }
+
+               *label = (port << 4) | (msg[0] >> 4);
+       }
+
+       if (len > 0 && len < 3)
+               memset(msg + len, 0, 3 - len);
+
+       return consume;
+}
+
+static void handle_midi_tx(struct fw_card *card, struct fw_request *request,
+                          int tcode, int destination, int source,
+                          int generation, unsigned long long offset,
+                          void *data, size_t length, void *callback_data)
+{
+       struct snd_tscm *tscm = callback_data;
+       u32 *buf = (u32 *)data;
+       unsigned int messages;
+       unsigned int i;
+       unsigned int port;
+       struct snd_rawmidi_substream *substream;
+       u8 *b;
+       int bytes;
+
+       if (offset != tscm->async_handler.offset)
+               goto end;
+
+       messages = length / 8;
+       for (i = 0; i < messages; i++) {
+               b = (u8 *)(buf + i * 2);
+
+               port = b[0] >> 4;
+               /* TODO: support virtual MIDI ports. */
+               if (port >= tscm->spec->midi_capture_ports)
+                       goto end;
+
+               /* Assume the message length. */
+               bytes = calculate_message_bytes(b[1]);
+               /* On MIDI data or exclusives. */
+               if (bytes <= 0) {
+                       /* Seek the end of exclusives. */
+                       for (bytes = 1; bytes < 4; bytes++) {
+                               if (b[bytes] == 0xf7)
+                                       break;
+                       }
+                       if (bytes == 4)
+                               bytes = 3;
+               }
+
+               substream = ACCESS_ONCE(tscm->tx_midi_substreams[port]);
+               if (substream != NULL)
+                       snd_rawmidi_receive(substream, b + 1, bytes);
+       }
+end:
+       fw_send_response(card, request, RCODE_COMPLETE);
+}
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm)
+{
+       static const struct fw_address_region resp_register_region = {
+               .start  = 0xffffe0000000ull,
+               .end    = 0xffffe000ffffull,
+       };
+       unsigned int i;
+       int err;
+
+       /*
+        * Usually, two quadlets are transferred by one transaction. The first
+        * quadlet has MIDI messages, the rest includes timestamp.
+        * Sometimes, 8 set of the data is transferred by a block transaction.
+        */
+       tscm->async_handler.length = 8 * 8;
+       tscm->async_handler.address_callback = handle_midi_tx;
+       tscm->async_handler.callback_data = tscm;
+
+       err = fw_core_add_address_handler(&tscm->async_handler,
+                                         &resp_register_region);
+       if (err < 0)
+               return err;
+
+       err = snd_tscm_transaction_reregister(tscm);
+       if (err < 0)
+               goto error;
+
+       for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++) {
+               err = snd_fw_async_midi_port_init(
+                               &tscm->out_ports[i], tscm->unit,
+                               TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_RX_QUAD,
+                               4, fill_message);
+               if (err < 0)
+                       goto error;
+       }
+
+       return err;
+error:
+       fw_core_remove_address_handler(&tscm->async_handler);
+       return err;
+}
+
+/* At bus reset, these registers are cleared. */
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm)
+{
+       struct fw_device *device = fw_parent_device(tscm->unit);
+       __be32 reg;
+       int err;
+
+       /* Register messaging address. Block transaction is not allowed. */
+       reg = cpu_to_be32((device->card->node_id << 16) |
+                         (tscm->async_handler.offset >> 32));
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       reg = cpu_to_be32(tscm->async_handler.offset);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+                                &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       /* Turn on messaging. */
+       reg = cpu_to_be32(0x00000001);
+       err = snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                 TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+                                 &reg, sizeof(reg), 0);
+       if (err < 0)
+               return err;
+
+       /* Turn on FireWire LED. */
+       reg = cpu_to_be32(0x0001008e);
+       return snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                                 TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+                                 &reg, sizeof(reg), 0);
+}
+
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm)
+{
+       __be32 reg;
+       unsigned int i;
+
+       /* Turn off FireWire LED. */
+       reg = cpu_to_be32(0x0000008e);
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_LED_POWER,
+                          &reg, sizeof(reg), 0);
+
+       /* Turn off messaging. */
+       reg = cpu_to_be32(0x00000000);
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ON,
+                          &reg, sizeof(reg), 0);
+
+       /* Unregister the address. */
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_HI,
+                          &reg, sizeof(reg), 0);
+       snd_fw_transaction(tscm->unit, TCODE_WRITE_QUADLET_REQUEST,
+                          TSCM_ADDR_BASE + TSCM_OFFSET_MIDI_TX_ADDR_LO,
+                          &reg, sizeof(reg), 0);
+
+       fw_core_remove_address_handler(&tscm->async_handler);
+       for (i = 0; i < TSCM_MIDI_OUT_PORT_MAX; i++)
+               snd_fw_async_midi_port_destroy(&tscm->out_ports[i]);
+}
diff --git a/sound/firewire/tascam/tascam.c b/sound/firewire/tascam/tascam.c
new file mode 100644 (file)
index 0000000..ee0bc18
--- /dev/null
@@ -0,0 +1,209 @@
+/*
+ * tascam.c - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#include "tascam.h"
+
+MODULE_DESCRIPTION("TASCAM FireWire series Driver");
+MODULE_AUTHOR("Takashi Sakamoto <o-takashi@sakamocchi.jp>");
+MODULE_LICENSE("GPL v2");
+
+static struct snd_tscm_spec model_specs[] = {
+       {
+               .name = "FW-1884",
+               .has_adat = true,
+               .has_spdif = true,
+               .pcm_capture_analog_channels = 8,
+               .pcm_playback_analog_channels = 8,
+               .midi_capture_ports = 4,
+               .midi_playback_ports = 4,
+               .is_controller = true,
+       },
+       {
+               .name = "FW-1082",
+               .has_adat = false,
+               .has_spdif = true,
+               .pcm_capture_analog_channels = 8,
+               .pcm_playback_analog_channels = 2,
+               .midi_capture_ports = 2,
+               .midi_playback_ports = 2,
+               .is_controller = true,
+       },
+       /* FW-1804 may be supported. */
+};
+
+static int identify_model(struct snd_tscm *tscm)
+{
+       struct fw_device *fw_dev = fw_parent_device(tscm->unit);
+       const u32 *config_rom = fw_dev->config_rom;
+       char model[9];
+       unsigned int i;
+       u8 c;
+
+       if (fw_dev->config_rom_length < 30) {
+               dev_err(&tscm->unit->device,
+                       "Configuration ROM is too short.\n");
+               return -ENODEV;
+       }
+
+       /* Pick up model name from certain addresses. */
+       for (i = 0; i < 8; i++) {
+               c = config_rom[28 + i / 4] >> (24 - 8 * (i % 4));
+               if (c == '\0')
+                       break;
+               model[i] = c;
+       }
+       model[i] = '\0';
+
+       for (i = 0; i < ARRAY_SIZE(model_specs); i++) {
+               if (strcmp(model, model_specs[i].name) == 0) {
+                       tscm->spec = &model_specs[i];
+                       break;
+               }
+       }
+       if (tscm->spec == NULL)
+               return -ENODEV;
+
+       strcpy(tscm->card->driver, "FW-TASCAM");
+       strcpy(tscm->card->shortname, model);
+       strcpy(tscm->card->mixername, model);
+       snprintf(tscm->card->longname, sizeof(tscm->card->longname),
+                "TASCAM %s, GUID %08x%08x at %s, S%d", model,
+                fw_dev->config_rom[3], fw_dev->config_rom[4],
+                dev_name(&tscm->unit->device), 100 << fw_dev->max_speed);
+
+       return 0;
+}
+
+static void tscm_card_free(struct snd_card *card)
+{
+       struct snd_tscm *tscm = card->private_data;
+
+       snd_tscm_transaction_unregister(tscm);
+       snd_tscm_stream_destroy_duplex(tscm);
+
+       fw_unit_put(tscm->unit);
+
+       mutex_destroy(&tscm->mutex);
+}
+
+static int snd_tscm_probe(struct fw_unit *unit,
+                          const struct ieee1394_device_id *entry)
+{
+       struct snd_card *card;
+       struct snd_tscm *tscm;
+       int err;
+
+       /* create card */
+       err = snd_card_new(&unit->device, -1, NULL, THIS_MODULE,
+                          sizeof(struct snd_tscm), &card);
+       if (err < 0)
+               return err;
+       card->private_free = tscm_card_free;
+
+       /* initialize myself */
+       tscm = card->private_data;
+       tscm->card = card;
+       tscm->unit = fw_unit_get(unit);
+
+       mutex_init(&tscm->mutex);
+       spin_lock_init(&tscm->lock);
+       init_waitqueue_head(&tscm->hwdep_wait);
+
+       err = identify_model(tscm);
+       if (err < 0)
+               goto error;
+
+       snd_tscm_proc_init(tscm);
+
+       err = snd_tscm_stream_init_duplex(tscm);
+       if (err < 0)
+               goto error;
+
+       err = snd_tscm_create_pcm_devices(tscm);
+       if (err < 0)
+               goto error;
+
+       err = snd_tscm_transaction_register(tscm);
+       if (err < 0)
+               goto error;
+
+       err = snd_tscm_create_midi_devices(tscm);
+       if (err < 0)
+               goto error;
+
+       err = snd_tscm_create_hwdep_device(tscm);
+       if (err < 0)
+               goto error;
+
+       err = snd_card_register(card);
+       if (err < 0)
+               goto error;
+
+       dev_set_drvdata(&unit->device, tscm);
+
+       return err;
+error:
+       snd_card_free(card);
+       return err;
+}
+
+static void snd_tscm_update(struct fw_unit *unit)
+{
+       struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+       snd_tscm_transaction_reregister(tscm);
+
+       mutex_lock(&tscm->mutex);
+       snd_tscm_stream_update_duplex(tscm);
+       mutex_unlock(&tscm->mutex);
+}
+
+static void snd_tscm_remove(struct fw_unit *unit)
+{
+       struct snd_tscm *tscm = dev_get_drvdata(&unit->device);
+
+       /* No need to wait for releasing card object in this context. */
+       snd_card_free_when_closed(tscm->card);
+}
+
+static const struct ieee1394_device_id snd_tscm_id_table[] = {
+       {
+               .match_flags = IEEE1394_MATCH_VENDOR_ID |
+                              IEEE1394_MATCH_SPECIFIER_ID,
+               .vendor_id = 0x00022e,
+               .specifier_id = 0x00022e,
+       },
+       /* FE-08 requires reverse-engineering because it just has faders. */
+       {}
+};
+MODULE_DEVICE_TABLE(ieee1394, snd_tscm_id_table);
+
+static struct fw_driver tscm_driver = {
+       .driver = {
+               .owner = THIS_MODULE,
+               .name = "snd-firewire-tascam",
+               .bus = &fw_bus_type,
+       },
+       .probe    = snd_tscm_probe,
+       .update   = snd_tscm_update,
+       .remove   = snd_tscm_remove,
+       .id_table = snd_tscm_id_table,
+};
+
+static int __init snd_tscm_init(void)
+{
+       return driver_register(&tscm_driver.driver);
+}
+
+static void __exit snd_tscm_exit(void)
+{
+       driver_unregister(&tscm_driver.driver);
+}
+
+module_init(snd_tscm_init);
+module_exit(snd_tscm_exit);
diff --git a/sound/firewire/tascam/tascam.h b/sound/firewire/tascam/tascam.h
new file mode 100644 (file)
index 0000000..2d028d2
--- /dev/null
@@ -0,0 +1,147 @@
+/*
+ * tascam.h - a part of driver for TASCAM FireWire series
+ *
+ * Copyright (c) 2015 Takashi Sakamoto
+ *
+ * Licensed under the terms of the GNU General Public License, version 2.
+ */
+
+#ifndef SOUND_TASCAM_H_INCLUDED
+#define SOUND_TASCAM_H_INCLUDED
+
+#include <linux/device.h>
+#include <linux/firewire.h>
+#include <linux/firewire-constants.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/mutex.h>
+#include <linux/slab.h>
+#include <linux/compat.h>
+
+#include <sound/core.h>
+#include <sound/initval.h>
+#include <sound/info.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/firewire.h>
+#include <sound/hwdep.h>
+#include <sound/rawmidi.h>
+
+#include "../lib.h"
+#include "../amdtp-stream.h"
+#include "../iso-resources.h"
+
+struct snd_tscm_spec {
+       const char *const name;
+       bool has_adat;
+       bool has_spdif;
+       unsigned int pcm_capture_analog_channels;
+       unsigned int pcm_playback_analog_channels;
+       unsigned int midi_capture_ports;
+       unsigned int midi_playback_ports;
+       bool is_controller;
+};
+
+#define TSCM_MIDI_IN_PORT_MAX  4
+#define TSCM_MIDI_OUT_PORT_MAX 4
+
+struct snd_tscm {
+       struct snd_card *card;
+       struct fw_unit *unit;
+
+       struct mutex mutex;
+       spinlock_t lock;
+
+       const struct snd_tscm_spec *spec;
+
+       struct fw_iso_resources tx_resources;
+       struct fw_iso_resources rx_resources;
+       struct amdtp_stream tx_stream;
+       struct amdtp_stream rx_stream;
+       unsigned int substreams_counter;
+
+       int dev_lock_count;
+       bool dev_lock_changed;
+       wait_queue_head_t hwdep_wait;
+
+       /* For MIDI message incoming transactions. */
+       struct fw_address_handler async_handler;
+       struct snd_rawmidi_substream *tx_midi_substreams[TSCM_MIDI_IN_PORT_MAX];
+
+       /* For MIDI message outgoing transactions. */
+       struct snd_fw_async_midi_port out_ports[TSCM_MIDI_OUT_PORT_MAX];
+       u8 running_status[TSCM_MIDI_OUT_PORT_MAX];
+       bool on_sysex[TSCM_MIDI_OUT_PORT_MAX];
+
+       /* For control messages. */
+       struct snd_firewire_tascam_status *status;
+};
+
+#define TSCM_ADDR_BASE                 0xffff00000000ull
+
+#define TSCM_OFFSET_FIRMWARE_REGISTER  0x0000
+#define TSCM_OFFSET_FIRMWARE_FPGA      0x0004
+#define TSCM_OFFSET_FIRMWARE_ARM       0x0008
+#define TSCM_OFFSET_FIRMWARE_HW                0x000c
+
+#define TSCM_OFFSET_ISOC_TX_CH         0x0200
+#define TSCM_OFFSET_UNKNOWN            0x0204
+#define TSCM_OFFSET_START_STREAMING    0x0208
+#define TSCM_OFFSET_ISOC_RX_CH         0x020c
+#define TSCM_OFFSET_ISOC_RX_ON         0x0210  /* Little conviction. */
+#define TSCM_OFFSET_TX_PCM_CHANNELS    0x0214
+#define TSCM_OFFSET_RX_PCM_CHANNELS    0x0218
+#define TSCM_OFFSET_MULTIPLEX_MODE     0x021c
+#define TSCM_OFFSET_ISOC_TX_ON         0x0220
+/* Unknown                             0x0224 */
+#define TSCM_OFFSET_CLOCK_STATUS       0x0228
+#define TSCM_OFFSET_SET_OPTION         0x022c
+
+#define TSCM_OFFSET_MIDI_TX_ON         0x0300
+#define TSCM_OFFSET_MIDI_TX_ADDR_HI    0x0304
+#define TSCM_OFFSET_MIDI_TX_ADDR_LO    0x0308
+
+#define TSCM_OFFSET_LED_POWER          0x0404
+
+#define TSCM_OFFSET_MIDI_RX_QUAD       0x4000
+
+enum snd_tscm_clock {
+       SND_TSCM_CLOCK_INTERNAL = 0,
+       SND_TSCM_CLOCK_WORD     = 1,
+       SND_TSCM_CLOCK_SPDIF    = 2,
+       SND_TSCM_CLOCK_ADAT     = 3,
+};
+
+int amdtp_tscm_init(struct amdtp_stream *s, struct fw_unit *unit,
+                 enum amdtp_stream_direction dir, unsigned int pcm_channels);
+int amdtp_tscm_set_parameters(struct amdtp_stream *s, unsigned int rate);
+int amdtp_tscm_add_pcm_hw_constraints(struct amdtp_stream *s,
+                                     struct snd_pcm_runtime *runtime);
+void amdtp_tscm_set_pcm_format(struct amdtp_stream *s, snd_pcm_format_t format);
+
+int snd_tscm_stream_get_rate(struct snd_tscm *tscm, unsigned int *rate);
+int snd_tscm_stream_get_clock(struct snd_tscm *tscm,
+                             enum snd_tscm_clock *clock);
+int snd_tscm_stream_init_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_update_duplex(struct snd_tscm *tscm);
+void snd_tscm_stream_destroy_duplex(struct snd_tscm *tscm);
+int snd_tscm_stream_start_duplex(struct snd_tscm *tscm, unsigned int rate);
+void snd_tscm_stream_stop_duplex(struct snd_tscm *tscm);
+
+void snd_tscm_stream_lock_changed(struct snd_tscm *tscm);
+int snd_tscm_stream_lock_try(struct snd_tscm *tscm);
+void snd_tscm_stream_lock_release(struct snd_tscm *tscm);
+
+int snd_tscm_transaction_register(struct snd_tscm *tscm);
+int snd_tscm_transaction_reregister(struct snd_tscm *tscm);
+void snd_tscm_transaction_unregister(struct snd_tscm *tscm);
+
+void snd_tscm_proc_init(struct snd_tscm *tscm);
+
+int snd_tscm_create_pcm_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_midi_devices(struct snd_tscm *tscm);
+
+int snd_tscm_create_hwdep_device(struct snd_tscm *tscm);
+
+#endif
index 33ba77dd32f2f174fd4d57dd52ff979ec9b0b510..cb89ec7c8147b2d44a90061ac531514ef5ce4de2 100644 (file)
@@ -227,7 +227,7 @@ EXPORT_SYMBOL_GPL(snd_hdac_ext_link_stream_setup);
 void snd_hdac_ext_link_set_stream_id(struct hdac_ext_link *link,
                                 int stream)
 {
-       snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 0);
+       snd_hdac_updatew(link->ml_addr, AZX_REG_ML_LOSIDV, (1 << stream), 1 << stream);
 }
 EXPORT_SYMBOL_GPL(snd_hdac_ext_link_set_stream_id);
 
@@ -385,14 +385,13 @@ void snd_hdac_ext_stream_release(struct hdac_ext_stream *stream, int type)
                break;
 
        case HDAC_EXT_STREAM_TYPE_HOST:
-               if (stream->decoupled) {
+               if (stream->decoupled && !stream->link_locked)
                        snd_hdac_ext_stream_decouple(ebus, stream, false);
-                       snd_hdac_stream_release(&stream->hstream);
-               }
+               snd_hdac_stream_release(&stream->hstream);
                break;
 
        case HDAC_EXT_STREAM_TYPE_LINK:
-               if (stream->decoupled)
+               if (stream->decoupled && !stream->hstream.opened)
                        snd_hdac_ext_stream_decouple(ebus, stream, false);
                spin_lock_irq(&bus->reg_lock);
                stream->link_locked = 0;
index 89c2711baaaf40c72753b50e05628e53cd0771ea..3060e2aee36fd736ce3755559d26c7049ba11218 100644 (file)
@@ -4,6 +4,7 @@
 #include <linux/init.h>
 #include <linux/device.h>
 #include <linux/module.h>
+#include <linux/mod_devicetable.h>
 #include <linux/export.h>
 #include <sound/hdaudio.h>
 
@@ -63,9 +64,21 @@ static int hda_bus_match(struct device *dev, struct device_driver *drv)
        return 1;
 }
 
+static int hda_uevent(struct device *dev, struct kobj_uevent_env *env)
+{
+       char modalias[32];
+
+       snd_hdac_codec_modalias(dev_to_hdac_dev(dev), modalias,
+                               sizeof(modalias));
+       if (add_uevent_var(env, "MODALIAS=%s", modalias))
+               return -ENOMEM;
+       return 0;
+}
+
 struct bus_type snd_hda_bus_type = {
        .name = "hdaudio",
        .match = hda_bus_match,
+       .uevent = hda_uevent,
 };
 EXPORT_SYMBOL_GPL(snd_hda_bus_type);
 
index 27c447e4fe5c6af5017389ca26cb2c57e534973c..0e81ea89a5965a49a80e52c6405d9a456cc9decf 100644 (file)
@@ -172,6 +172,15 @@ static void process_unsol_events(struct work_struct *work)
        }
 }
 
+/**
+ * snd_hdac_bus_add_device - Add a codec to bus
+ * @bus: HDA core bus
+ * @codec: HDA core device to add
+ *
+ * Adds the given codec to the list in the bus.  The caddr_tbl array
+ * and codec_powered bits are updated, as well.
+ * Returns zero if success, or a negative error code.
+ */
 int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
 {
        if (bus->caddr_tbl[codec->addr]) {
@@ -188,6 +197,11 @@ int snd_hdac_bus_add_device(struct hdac_bus *bus, struct hdac_device *codec)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_bus_add_device);
 
+/**
+ * snd_hdac_bus_remove_device - Remove a codec from bus
+ * @bus: HDA core bus
+ * @codec: HDA core device to remove
+ */
 void snd_hdac_bus_remove_device(struct hdac_bus *bus,
                                struct hdac_device *codec)
 {
index db96042a497f040059585adb9465d2357b28b265..e361024eabb6371bef25a888bcd8ea58a5499913 100644 (file)
@@ -163,6 +163,43 @@ void snd_hdac_device_unregister(struct hdac_device *codec)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_device_unregister);
 
+/**
+ * snd_hdac_device_set_chip_name - set/update the codec name
+ * @codec: the HDAC device
+ * @name: name string to set
+ *
+ * Returns 0 if the name is set or updated, or a negative error code.
+ */
+int snd_hdac_device_set_chip_name(struct hdac_device *codec, const char *name)
+{
+       char *newname;
+
+       if (!name)
+               return 0;
+       newname = kstrdup(name, GFP_KERNEL);
+       if (!newname)
+               return -ENOMEM;
+       kfree(codec->chip_name);
+       codec->chip_name = newname;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_hdac_device_set_chip_name);
+
+/**
+ * snd_hdac_codec_modalias - give the module alias name
+ * @codec: HDAC device
+ * @buf: string buffer to store
+ * @size: string buffer size
+ *
+ * Returns the size of string, like snprintf(), or a negative error code.
+ */
+int snd_hdac_codec_modalias(struct hdac_device *codec, char *buf, size_t size)
+{
+       return snprintf(buf, size, "hdaudio:v%08Xr%08Xa%02X\n",
+                       codec->vendor_id, codec->revision_id, codec->type);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_modalias);
+
 /**
  * snd_hdac_make_cmd - compose a 32bit command word to be sent to the
  *     HD-audio controller
@@ -592,8 +629,10 @@ int snd_hdac_power_down_pm(struct hdac_device *codec)
 EXPORT_SYMBOL_GPL(snd_hdac_power_down_pm);
 #endif
 
-/*
- * Enable/disable the link power for a codec.
+/**
+ * snd_hdac_link_power - Enable/disable the link power for a codec
+ * @codec: the codec object
+ * @bool: enable or disable the link power
  */
 int snd_hdac_link_power(struct hdac_device *codec, bool enable)
 {
@@ -952,3 +991,84 @@ bool snd_hdac_is_supported_format(struct hdac_device *codec, hda_nid_t nid,
        return true;
 }
 EXPORT_SYMBOL_GPL(snd_hdac_is_supported_format);
+
+static unsigned int codec_read(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm)
+{
+       unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+       unsigned int res;
+
+       if (snd_hdac_exec_verb(hdac, cmd, flags, &res))
+               return -1;
+
+       return res;
+}
+
+static int codec_write(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm)
+{
+       unsigned int cmd = snd_hdac_make_cmd(hdac, nid, verb, parm);
+
+       return snd_hdac_exec_verb(hdac, cmd, flags, NULL);
+}
+
+/**
+ * snd_hdac_codec_read - send a command and get the response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command and read the corresponding response.
+ *
+ * Returns the obtained response value, or -1 for an error.
+ */
+int snd_hdac_codec_read(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm)
+{
+       return codec_read(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_read);
+
+/**
+ * snd_hdac_codec_write - send a single command without waiting for response
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @flags: optional bit flags
+ * @verb: the verb to send
+ * @parm: the parameter for the verb
+ *
+ * Send a single command without waiting for response.
+ *
+ * Returns 0 if successful, or a negative error code.
+ */
+int snd_hdac_codec_write(struct hdac_device *hdac, hda_nid_t nid,
+                       int flags, unsigned int verb, unsigned int parm)
+{
+       return codec_write(hdac, nid, flags, verb, parm);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_codec_write);
+
+/**
+ * snd_hdac_check_power_state - check whether the actual power state matches
+ * with the target state
+ *
+ * @hdac: the HDAC device
+ * @nid: NID to send the command
+ * @target_state: target state to check for
+ *
+ * Return true if state matches, false if not
+ */
+bool snd_hdac_check_power_state(struct hdac_device *hdac,
+               hda_nid_t nid, unsigned int target_state)
+{
+       unsigned int state = codec_read(hdac, nid, 0,
+                               AC_VERB_GET_POWER_STATE, 0);
+
+       if (state & AC_PWRST_ERROR)
+               return true;
+       state = (state >> 4) & 0x0f;
+       return (state == target_state);
+}
+EXPORT_SYMBOL_GPL(snd_hdac_check_power_state);
index 55c3df4458f7936b56ceda126ce8f3a0f0b51abe..8fef1b8d1fd8acc443bc86a2aedd66763d9b84fd 100644 (file)
 
 static struct i915_audio_component *hdac_acomp;
 
+/**
+ * snd_hdac_set_codec_wakeup - Enable / disable HDMI/DP codec wakeup
+ * @bus: HDA core bus
+ * @enable: enable or disable the wakeup
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function should be called during the chip reset, also called at
+ * resume for updating STATESTS register read.
+ *
+ * Returns zero for success or a negative error code.
+ */
 int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
 {
        struct i915_audio_component *acomp = bus->audio_component;
@@ -45,6 +58,19 @@ int snd_hdac_set_codec_wakeup(struct hdac_bus *bus, bool enable)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_set_codec_wakeup);
 
+/**
+ * snd_hdac_display_power - Power up / down the power refcount
+ * @bus: HDA core bus
+ * @enable: power up or down
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function manages a refcount and calls the i915 get_power() and
+ * put_power() ops accordingly, toggling the codec wakeup, too.
+ *
+ * Returns zero for success or a negative error code.
+ */
 int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
 {
        struct i915_audio_component *acomp = bus->audio_component;
@@ -71,6 +97,16 @@ int snd_hdac_display_power(struct hdac_bus *bus, bool enable)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_display_power);
 
+/**
+ * snd_hdac_get_display_clk - Get CDCLK in kHz
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function queries CDCLK value in kHz from the graphics driver and
+ * returns the value.  A negative code is returned in error.
+ */
 int snd_hdac_get_display_clk(struct hdac_bus *bus)
 {
        struct i915_audio_component *acomp = bus->audio_component;
@@ -134,6 +170,17 @@ static int hdac_component_master_match(struct device *dev, void *data)
        return !strcmp(dev->driver->name, "i915");
 }
 
+/**
+ * snd_hdac_i915_register_notifier - Register i915 audio component ops
+ * @aops: i915 audio component ops
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function sets the given ops to be called by the i915 graphics driver.
+ *
+ * Returns zero for success or a negative error code.
+ */
 int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops *aops)
 {
        if (WARN_ON(!hdac_acomp))
@@ -144,6 +191,18 @@ int snd_hdac_i915_register_notifier(const struct i915_audio_component_audio_ops
 }
 EXPORT_SYMBOL_GPL(snd_hdac_i915_register_notifier);
 
+/**
+ * snd_hdac_i915_init - Initialize i915 audio component
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function initializes and sets up the audio component to communicate
+ * with i915 graphics driver.
+ *
+ * Returns zero for success or a negative error code.
+ */
 int snd_hdac_i915_init(struct hdac_bus *bus)
 {
        struct component_match *match = NULL;
@@ -187,6 +246,17 @@ out_err:
 }
 EXPORT_SYMBOL_GPL(snd_hdac_i915_init);
 
+/**
+ * snd_hdac_i915_exit - Finalize i915 audio component
+ * @bus: HDA core bus
+ *
+ * This function is supposed to be used only by a HD-audio controller
+ * driver that needs the interaction with i915 graphics.
+ *
+ * This function releases the i915 audio component that has been used.
+ *
+ * Returns zero for success or a negative error code.
+ */
 int snd_hdac_i915_exit(struct hdac_bus *bus)
 {
        struct device *dev = bus->dev;
index b0ed870ffb88eda0cc47e4c748488f314429375d..eb8f7c30cb0941275ebd495ee41467a0171e3562 100644 (file)
@@ -339,6 +339,12 @@ static const struct regmap_config hda_regmap_cfg = {
        .use_single_rw = true,
 };
 
+/**
+ * snd_hdac_regmap_init - Initialize regmap for HDA register accesses
+ * @codec: the codec object
+ *
+ * Returns zero for success or a negative error code.
+ */
 int snd_hdac_regmap_init(struct hdac_device *codec)
 {
        struct regmap *regmap;
@@ -352,6 +358,10 @@ int snd_hdac_regmap_init(struct hdac_device *codec)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_regmap_init);
 
+/**
+ * snd_hdac_regmap_init - Release the regmap from HDA codec
+ * @codec: the codec object
+ */
 void snd_hdac_regmap_exit(struct hdac_device *codec)
 {
        if (codec->regmap) {
index 8981159813efb7e361d78d8f26bf032b0565f00b..38990a77d7b780081167fa14ded35745025553c1 100644 (file)
@@ -426,7 +426,8 @@ int snd_hdac_stream_setup_periods(struct hdac_stream *azx_dev)
 }
 EXPORT_SYMBOL_GPL(snd_hdac_stream_setup_periods);
 
-/* snd_hdac_stream_set_params - set stream parameters
+/**
+ * snd_hdac_stream_set_params - set stream parameters
  * @azx_dev: HD-audio core stream for which parameters are to be set
  * @format_val: format value parameter
  *
index c71142dea98a18f5355f1395c4c34200a773c52e..42d61bf41969c2176f1e10842e0c8e651d202a23 100644 (file)
@@ -45,6 +45,13 @@ CODEC_ATTR(mfg);
 CODEC_ATTR_STR(vendor_name);
 CODEC_ATTR_STR(chip_name);
 
+static ssize_t modalias_show(struct device *dev, struct device_attribute *attr,
+                            char *buf)
+{
+       return snd_hdac_codec_modalias(dev_to_hdac_dev(dev), buf, 256);
+}
+static DEVICE_ATTR_RO(modalias);
+
 static struct attribute *hdac_dev_attrs[] = {
        &dev_attr_type.attr,
        &dev_attr_vendor_id.attr,
@@ -54,6 +61,7 @@ static struct attribute *hdac_dev_attrs[] = {
        &dev_attr_mfg.attr,
        &dev_attr_vendor_name.attr,
        &dev_attr_chip_name.attr,
+       &dev_attr_modalias.attr,
        NULL
 };
 
index 2a9f4a345171ee6b521c7c153f19991c40381f33..2706f271a83b0f19cf7a83b833b9f4e3a35e4242 100644 (file)
@@ -1864,7 +1864,7 @@ int snd_cs46xx_pcm_iec958(struct snd_cs46xx *chip, int device)
        /* global setup */
        pcm->info_flags = 0;
        strcpy(pcm->name, "CS46xx - IEC958");
-       chip->pcm_rear = pcm;
+       chip->pcm_iec958 = pcm;
 
        snd_pcm_lib_preallocate_pages_for_all(pcm, SNDRV_DMA_TYPE_DEV,
                                              snd_dma_pci_data(chip->pci), 64*1024, 256*1024);
@@ -2528,7 +2528,7 @@ int snd_cs46xx_mixer(struct snd_cs46xx *chip, int spdif_device)
 #ifdef CONFIG_SND_CS46XX_NEW_DSP
        if (chip->nr_ac97_codecs == 1) {
                unsigned int id2 = chip->ac97[CS46XX_PRIMARY_CODEC_INDEX]->id & 0xffff;
-               if (id2 == 0x592b || id2 == 0x592d) {
+               if ((id2 & 0xfff0) == 0x5920) { /* CS4294 and CS4298 */
                        err = snd_ctl_add(card, snd_ctl_new1(&snd_cs46xx_front_dup_ctl, chip));
                        if (err < 0)
                                return err;
@@ -3780,6 +3780,11 @@ static int snd_cs46xx_suspend(struct device *dev)
        snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
        chip->in_suspend = 1;
        snd_pcm_suspend_all(chip->pcm);
+#ifdef CONFIG_SND_CS46XX_NEW_DSP
+       snd_pcm_suspend_all(chip->pcm_rear);
+       snd_pcm_suspend_all(chip->pcm_center_lfe);
+       snd_pcm_suspend_all(chip->pcm_iec958);
+#endif
        // chip->ac97_powerdown = snd_cs46xx_codec_read(chip, AC97_POWER_CONTROL);
        // chip->ac97_general_purpose = snd_cs46xx_codec_read(chip, BA0_AC97_GENERAL_PURPOSE);
 
index d5ac25cc7fee1fbc87b577da634f9f13706091a5..70671ad65d24565fb6c8522bd69444817aac2933 100644 (file)
 #include "hda_local.h"
 
 /*
- * find a matching codec preset
+ * find a matching codec id
  */
 static int hda_codec_match(struct hdac_device *dev, struct hdac_driver *drv)
 {
        struct hda_codec *codec = container_of(dev, struct hda_codec, core);
        struct hda_codec_driver *driver =
                container_of(drv, struct hda_codec_driver, core);
-       const struct hda_codec_preset *preset;
+       const struct hda_device_id *list;
        /* check probe_id instead of vendor_id if set */
        u32 id = codec->probe_id ? codec->probe_id : codec->core.vendor_id;
+       u32 rev_id = codec->core.revision_id;
 
-       for (preset = driver->preset; preset->id; preset++) {
-               if (preset->id == id &&
-                   (!preset->rev || preset->rev == codec->core.revision_id)) {
-                       codec->preset = preset;
+       for (list = driver->id; list->vendor_id; list++) {
+               if (list->vendor_id == id &&
+                   (!list->rev_id || list->rev_id == rev_id)) {
+                       codec->preset = list;
                        return 1;
                }
        }
@@ -45,26 +46,45 @@ static void hda_codec_unsol_event(struct hdac_device *dev, unsigned int ev)
                codec->patch_ops.unsol_event(codec, ev);
 }
 
-/* reset the codec name from the preset */
-static int codec_refresh_name(struct hda_codec *codec, const char *name)
+/**
+ * snd_hda_codec_set_name - set the codec name
+ * @codec: the HDA codec
+ * @name: name string to set
+ */
+int snd_hda_codec_set_name(struct hda_codec *codec, const char *name)
 {
-       if (name) {
-               kfree(codec->core.chip_name);
-               codec->core.chip_name = kstrdup(name, GFP_KERNEL);
+       int err;
+
+       if (!name)
+               return 0;
+       err = snd_hdac_device_set_chip_name(&codec->core, name);
+       if (err < 0)
+               return err;
+
+       /* update the mixer name */
+       if (!*codec->card->mixername ||
+           codec->bus->mixer_assigned >= codec->core.addr) {
+               snprintf(codec->card->mixername,
+                        sizeof(codec->card->mixername), "%s %s",
+                        codec->core.vendor_name, codec->core.chip_name);
+               codec->bus->mixer_assigned = codec->core.addr;
        }
-       return codec->core.chip_name ? 0 : -ENOMEM;
+
+       return 0;
 }
+EXPORT_SYMBOL_GPL(snd_hda_codec_set_name);
 
 static int hda_codec_driver_probe(struct device *dev)
 {
        struct hda_codec *codec = dev_to_hda_codec(dev);
        struct module *owner = dev->driver->owner;
+       hda_codec_patch_t patch;
        int err;
 
        if (WARN_ON(!codec->preset))
                return -EINVAL;
 
-       err = codec_refresh_name(codec, codec->preset->name);
+       err = snd_hda_codec_set_name(codec, codec->preset->name);
        if (err < 0)
                goto error;
        err = snd_hdac_regmap_init(&codec->core);
@@ -76,9 +96,12 @@ static int hda_codec_driver_probe(struct device *dev)
                goto error;
        }
 
-       err = codec->preset->patch(codec);
-       if (err < 0)
-               goto error_module;
+       patch = (hda_codec_patch_t)codec->preset->driver_data;
+       if (patch) {
+               err = patch(codec);
+               if (err < 0)
+                       goto error_module;
+       }
 
        err = snd_hda_codec_build_pcms(codec);
        if (err < 0)
@@ -155,11 +178,10 @@ static inline bool codec_probed(struct hda_codec *codec)
 static void codec_bind_module(struct hda_codec *codec)
 {
 #ifdef MODULE
-       request_module("snd-hda-codec-id:%08x", codec->core.vendor_id);
-       if (codec_probed(codec))
-               return;
-       request_module("snd-hda-codec-id:%04x*",
-                      (codec->core.vendor_id >> 16) & 0xffff);
+       char modalias[32];
+
+       snd_hdac_codec_modalias(&codec->core, modalias, sizeof(modalias));
+       request_module(modalias);
        if (codec_probed(codec))
                return;
 #endif
@@ -251,11 +273,6 @@ int snd_hda_codec_configure(struct hda_codec *codec)
                }
        }
 
-       /* audio codec should override the mixer name */
-       if (codec->core.afg || !*codec->card->mixername)
-               snprintf(codec->card->mixername,
-                        sizeof(codec->card->mixername), "%s %s",
-                        codec->core.vendor_name, codec->core.chip_name);
        return 0;
 
  error:
index a249d5486889dca683af566e0818d95ee49b12ae..83741887faa189c1700abc34bebbd3c55234384d 100644 (file)
@@ -90,50 +90,6 @@ static int codec_exec_verb(struct hdac_device *dev, unsigned int cmd,
        return err;
 }
 
-/**
- * snd_hda_codec_read - send a command and get the response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command and read the corresponding response.
- *
- * Returns the obtained response value, or -1 for an error.
- */
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
-                               int flags,
-                               unsigned int verb, unsigned int parm)
-{
-       unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm);
-       unsigned int res;
-       if (snd_hdac_exec_verb(&codec->core, cmd, flags, &res))
-               return -1;
-       return res;
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_read);
-
-/**
- * snd_hda_codec_write - send a single command without waiting for response
- * @codec: the HDA codec
- * @nid: NID to send the command
- * @flags: optional bit flags
- * @verb: the verb to send
- * @parm: the parameter for the verb
- *
- * Send a single command without waiting for response.
- *
- * Returns 0 if successful, or a negative error code.
- */
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
-                       unsigned int verb, unsigned int parm)
-{
-       unsigned int cmd = snd_hdac_make_cmd(&codec->core, nid, verb, parm);
-       return snd_hdac_exec_verb(&codec->core, cmd, flags, NULL);
-}
-EXPORT_SYMBOL_GPL(snd_hda_codec_write);
-
 /**
  * snd_hda_sequence_write - sequence writes
  * @codec: the HDA codec
index 2970413f18a01817b9500ed7f1c00f61e3b1e92b..373fcad840ea6ff5c18b4c4aa93b6473c7a60f1c 100644 (file)
@@ -22,6 +22,7 @@
 #define __SOUND_HDA_CODEC_H
 
 #include <linux/kref.h>
+#include <linux/mod_devicetable.h>
 #include <sound/info.h>
 #include <sound/control.h>
 #include <sound/pcm.h>
@@ -69,6 +70,7 @@ struct hda_bus {
        unsigned int no_response_fallback:1; /* don't fallback at RIRB error */
 
        int primary_dig_out_type;       /* primary digital out PCM type */
+       unsigned int mixer_assigned;    /* codec addr for mixer name */
 };
 
 /* from hdac_bus to hda_bus */
@@ -80,19 +82,21 @@ struct hda_bus {
  * Known codecs have the patch to build and set up the controls/PCMs
  * better than the generic parser.
  */
-struct hda_codec_preset {
-       unsigned int id;
-       unsigned int rev;
-       const char *name;
-       int (*patch)(struct hda_codec *codec);
-};
+typedef int (*hda_codec_patch_t)(struct hda_codec *);
        
 #define HDA_CODEC_ID_GENERIC_HDMI      0x00000101
 #define HDA_CODEC_ID_GENERIC           0x00000201
 
+#define HDA_CODEC_REV_ENTRY(_vid, _rev, _name, _patch) \
+       { .vendor_id = (_vid), .rev_id = (_rev), .name = (_name), \
+         .api_version = HDA_DEV_LEGACY, \
+         .driver_data = (unsigned long)(_patch) }
+#define HDA_CODEC_ENTRY(_vid, _name, _patch) \
+       HDA_CODEC_REV_ENTRY(_vid, 0, _name, _patch)
+
 struct hda_codec_driver {
        struct hdac_driver core;
-       const struct hda_codec_preset *preset;
+       const struct hda_device_id *id;
 };
 
 int __hda_codec_driver_register(struct hda_codec_driver *drv, const char *name,
@@ -183,7 +187,7 @@ struct hda_codec {
        u32 probe_id; /* overridden id for probing */
 
        /* detected preset */
-       const struct hda_codec_preset *preset;
+       const struct hda_device_id *preset;
        const char *modelname;  /* model name for preset */
 
        /* set by patch */
@@ -297,10 +301,6 @@ struct hda_codec {
 /*
  * constructors
  */
-int snd_hda_bus_new(struct snd_card *card,
-                   const struct hdac_bus_ops *ops,
-                   const struct hdac_io_ops *io_ops,
-                   struct hda_bus **busp);
 int snd_hda_codec_new(struct hda_bus *bus, struct snd_card *card,
                      unsigned int codec_addr, struct hda_codec **codecp);
 int snd_hda_codec_configure(struct hda_codec *codec);
@@ -309,11 +309,21 @@ int snd_hda_codec_update_widgets(struct hda_codec *codec);
 /*
  * low level functions
  */
-unsigned int snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
+static inline unsigned int
+snd_hda_codec_read(struct hda_codec *codec, hda_nid_t nid,
                                int flags,
-                               unsigned int verb, unsigned int parm);
-int snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
-                       unsigned int verb, unsigned int parm);
+                               unsigned int verb, unsigned int parm)
+{
+       return snd_hdac_codec_read(&codec->core, nid, flags, verb, parm);
+}
+
+static inline int
+snd_hda_codec_write(struct hda_codec *codec, hda_nid_t nid, int flags,
+                       unsigned int verb, unsigned int parm)
+{
+       return snd_hdac_codec_write(&codec->core, nid, flags, verb, parm);
+}
+
 #define snd_hda_param_read(codec, nid, param) \
        snd_hdac_read_parm(&(codec)->core, nid, param)
 #define snd_hda_get_sub_nodes(codec, nid, start_nid) \
@@ -453,6 +463,8 @@ void snd_hda_unlock_devices(struct hda_bus *bus);
 void snd_hda_bus_reset(struct hda_bus *bus);
 void snd_hda_bus_reset_codecs(struct hda_bus *bus);
 
+int snd_hda_codec_set_name(struct hda_codec *codec, const char *name);
+
 /*
  * power management
  */
index 944455997fdcc8fd80758b4fb5794488543bd966..d6b93a20361b01695f3964add27e5c384749f00a 100644 (file)
@@ -1045,6 +1045,7 @@ int azx_bus_init(struct azx *chip, const char *model,
        mutex_init(&bus->prepare_mutex);
        bus->pci = chip->pci;
        bus->modelname = model;
+       bus->mixer_assigned = -1;
        bus->core.snoop = azx_snoop(chip);
        if (chip->get_position[0] != azx_get_pos_lpib ||
            chip->get_position[1] != azx_get_pos_lpib)
index 24f91114a32cc73f54d72d8b008db51935519893..c6e8a651cea1357df06a2934d1854c926f6e50c4 100644 (file)
@@ -5877,13 +5877,14 @@ error:
        return err;
 }
 
-static const struct hda_codec_preset snd_hda_preset_generic[] = {
-       { .id = HDA_CODEC_ID_GENERIC, .patch = snd_hda_parse_generic_codec },
+static const struct hda_device_id snd_hda_id_generic[] = {
+       HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC, "Generic", snd_hda_parse_generic_codec),
        {} /* terminator */
 };
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_generic);
 
 static struct hda_codec_driver generic_driver = {
-       .preset = snd_hda_preset_generic,
+       .id = snd_hda_id_generic,
 };
 
 module_hda_codec_driver(generic_driver);
index 4a21c2199e0219d980a69d82a83bd91942c22f83..d0e066e4c9856028b1f9cbc0faeb3dc2e3a5ae9a 100644 (file)
@@ -681,12 +681,7 @@ static inline bool
 snd_hda_check_power_state(struct hda_codec *codec, hda_nid_t nid,
                          unsigned int target_state)
 {
-       unsigned int state = snd_hda_codec_read(codec, nid, 0,
-                                               AC_VERB_GET_POWER_STATE, 0);
-       if (state & AC_PWRST_ERROR)
-               return true;
-       state = (state >> 4) & 0x0f;
-       return (state == target_state);
+       return snd_hdac_check_power_state(&codec->core, nid, target_state);
 }
 
 unsigned int snd_hda_codec_eapd_power_filter(struct hda_codec *codec,
index a6e3d9b511ab5f0a2bfa446cbb004c002b81f029..64e0d1d81ca5afd66625669079bdce05fa23dc63 100644 (file)
@@ -595,8 +595,7 @@ static void parse_model_mode(char *buf, struct hda_bus *bus,
 static void parse_chip_name_mode(char *buf, struct hda_bus *bus,
                                 struct hda_codec **codecp)
 {
-       kfree((*codecp)->core.chip_name);
-       (*codecp)->core.chip_name = kstrdup(buf, GFP_KERNEL);
+       snd_hda_codec_set_name(*codecp, buf);
 }
 
 #define DEFINE_PARSE_ID_MODE(name) \
index c033a4ee65470fb592e77836437262063f00f125..e0fb8c6d1bc274e4ff5f6a8bf55afa9852bf9026 100644 (file)
@@ -1165,32 +1165,31 @@ static int patch_ad1882(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_analog[] = {
-       { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884 },
-       { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
-       { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884 },
-       { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
-       { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884 },
-       { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884 },
-       { .id = 0x11d41981, .name = "AD1981", .patch = patch_ad1981 },
-       { .id = 0x11d41983, .name = "AD1983", .patch = patch_ad1983 },
-       { .id = 0x11d41984, .name = "AD1984", .patch = patch_ad1884 },
-       { .id = 0x11d41986, .name = "AD1986A", .patch = patch_ad1986a },
-       { .id = 0x11d41988, .name = "AD1988", .patch = patch_ad1988 },
-       { .id = 0x11d4198b, .name = "AD1988B", .patch = patch_ad1988 },
-       { .id = 0x11d4882a, .name = "AD1882A", .patch = patch_ad1882 },
-       { .id = 0x11d4989a, .name = "AD1989A", .patch = patch_ad1988 },
-       { .id = 0x11d4989b, .name = "AD1989B", .patch = patch_ad1988 },
+static const struct hda_device_id snd_hda_id_analog[] = {
+       HDA_CODEC_ENTRY(0x11d4184a, "AD1884A", patch_ad1884),
+       HDA_CODEC_ENTRY(0x11d41882, "AD1882", patch_ad1882),
+       HDA_CODEC_ENTRY(0x11d41883, "AD1883", patch_ad1884),
+       HDA_CODEC_ENTRY(0x11d41884, "AD1884", patch_ad1884),
+       HDA_CODEC_ENTRY(0x11d4194a, "AD1984A", patch_ad1884),
+       HDA_CODEC_ENTRY(0x11d4194b, "AD1984B", patch_ad1884),
+       HDA_CODEC_ENTRY(0x11d41981, "AD1981", patch_ad1981),
+       HDA_CODEC_ENTRY(0x11d41983, "AD1983", patch_ad1983),
+       HDA_CODEC_ENTRY(0x11d41984, "AD1984", patch_ad1884),
+       HDA_CODEC_ENTRY(0x11d41986, "AD1986A", patch_ad1986a),
+       HDA_CODEC_ENTRY(0x11d41988, "AD1988", patch_ad1988),
+       HDA_CODEC_ENTRY(0x11d4198b, "AD1988B", patch_ad1988),
+       HDA_CODEC_ENTRY(0x11d4882a, "AD1882A", patch_ad1882),
+       HDA_CODEC_ENTRY(0x11d4989a, "AD1989A", patch_ad1988),
+       HDA_CODEC_ENTRY(0x11d4989b, "AD1989B", patch_ad1988),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:11d4*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_analog);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Analog Devices HD-audio codec");
 
 static struct hda_codec_driver analog_driver = {
-       .preset = snd_hda_preset_analog,
+       .id = snd_hda_id_analog,
 };
 
 module_hda_codec_driver(analog_driver);
index 484bbf4134cd127e8b9a9cf03bbc66ab361755f6..c2d9ee9cfdc00900aee82957fee356442710faa3 100644 (file)
@@ -83,22 +83,19 @@ static int patch_ca0110(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_ca0110[] = {
-       { .id = 0x1102000a, .name = "CA0110-IBG", .patch = patch_ca0110 },
-       { .id = 0x1102000b, .name = "CA0110-IBG", .patch = patch_ca0110 },
-       { .id = 0x1102000d, .name = "SB0880 X-Fi", .patch = patch_ca0110 },
+static const struct hda_device_id snd_hda_id_ca0110[] = {
+       HDA_CODEC_ENTRY(0x1102000a, "CA0110-IBG", patch_ca0110),
+       HDA_CODEC_ENTRY(0x1102000b, "CA0110-IBG", patch_ca0110),
+       HDA_CODEC_ENTRY(0x1102000d, "SB0880 X-Fi", patch_ca0110),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:1102000a");
-MODULE_ALIAS("snd-hda-codec-id:1102000b");
-MODULE_ALIAS("snd-hda-codec-id:1102000d");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0110);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Creative CA0110-IBG HD-audio codec");
 
 static struct hda_codec_driver ca0110_driver = {
-       .preset = snd_hda_preset_ca0110,
+       .id = snd_hda_id_ca0110,
 };
 
 module_hda_codec_driver(ca0110_driver);
index 186792fe226e08d95d8bb67c959e0fbd7d86bd25..f8a12ca477f1ab775a101c8bb41876a11707477e 100644 (file)
@@ -2673,13 +2673,13 @@ static bool dspload_wait_loaded(struct hda_codec *codec)
 
        do {
                if (dspload_is_loaded(codec)) {
-                       pr_info("ca0132 DOWNLOAD OK :-) DSP IS RUNNING.\n");
+                       codec_info(codec, "ca0132 DSP downloaded and running\n");
                        return true;
                }
                msleep(20);
        } while (time_before(jiffies, timeout));
 
-       pr_err("ca0132 DOWNLOAD FAILED!!! DSP IS NOT RUNNING.\n");
+       codec_err(codec, "ca0132 failed to download DSP\n");
        return false;
 }
 
@@ -4375,7 +4375,7 @@ static bool ca0132_download_dsp_images(struct hda_codec *codec)
 
        dsp_os_image = (struct dsp_image_seg *)(fw_entry->data);
        if (dspload_image(codec, dsp_os_image, 0, 0, true, 0)) {
-               pr_err("ca0132 dspload_image failed.\n");
+               codec_err(codec, "ca0132 DSP load image failed\n");
                goto exit_download;
        }
 
@@ -4778,18 +4778,17 @@ static int patch_ca0132(struct hda_codec *codec)
 /*
  * patch entries
  */
-static struct hda_codec_preset snd_hda_preset_ca0132[] = {
-       { .id = 0x11020011, .name = "CA0132",     .patch = patch_ca0132 },
+static struct hda_device_id snd_hda_id_ca0132[] = {
+       HDA_CODEC_ENTRY(0x11020011, "CA0132", patch_ca0132),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:11020011");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_ca0132);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Creative Sound Core3D codec");
 
 static struct hda_codec_driver ca0132_driver = {
-       .preset = snd_hda_preset_ca0132,
+       .id = snd_hda_id_ca0132,
 };
 
 module_hda_codec_driver(ca0132_driver);
index 85813de26da87715df7d1d259339e30450c7815a..a12ae8ac091451261a2ba613543677fabedb8cd0 100644 (file)
@@ -570,6 +570,7 @@ static struct cs_spec *cs_alloc_spec(struct hda_codec *codec, int vendor_nid)
                return NULL;
        codec->spec = spec;
        spec->vendor_nid = vendor_nid;
+       codec->power_save_node = 1;
        snd_hda_gen_spec_init(&spec->gen);
 
        return spec;
@@ -1200,26 +1201,21 @@ static int patch_cs4213(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_cirrus[] = {
-       { .id = 0x10134206, .name = "CS4206", .patch = patch_cs420x },
-       { .id = 0x10134207, .name = "CS4207", .patch = patch_cs420x },
-       { .id = 0x10134208, .name = "CS4208", .patch = patch_cs4208 },
-       { .id = 0x10134210, .name = "CS4210", .patch = patch_cs4210 },
-       { .id = 0x10134213, .name = "CS4213", .patch = patch_cs4213 },
+static const struct hda_device_id snd_hda_id_cirrus[] = {
+       HDA_CODEC_ENTRY(0x10134206, "CS4206", patch_cs420x),
+       HDA_CODEC_ENTRY(0x10134207, "CS4207", patch_cs420x),
+       HDA_CODEC_ENTRY(0x10134208, "CS4208", patch_cs4208),
+       HDA_CODEC_ENTRY(0x10134210, "CS4210", patch_cs4210),
+       HDA_CODEC_ENTRY(0x10134213, "CS4213", patch_cs4213),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:10134206");
-MODULE_ALIAS("snd-hda-codec-id:10134207");
-MODULE_ALIAS("snd-hda-codec-id:10134208");
-MODULE_ALIAS("snd-hda-codec-id:10134210");
-MODULE_ALIAS("snd-hda-codec-id:10134213");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cirrus);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Cirrus Logic HD-audio codec");
 
 static struct hda_codec_driver cirrus_driver = {
-       .preset = snd_hda_preset_cirrus,
+       .id = snd_hda_id_cirrus,
 };
 
 module_hda_codec_driver(cirrus_driver);
index f5ed078710f8494918786657613180f1d75a2286..1b2195dd2b26588d8075f79b14636cef1ced47b3 100644 (file)
@@ -123,22 +123,19 @@ static int patch_cmi8888(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_cmedia[] = {
-       { .id = 0x13f68888, .name = "CMI8888", .patch = patch_cmi8888 },
-       { .id = 0x13f69880, .name = "CMI9880", .patch = patch_cmi9880 },
-       { .id = 0x434d4980, .name = "CMI9880", .patch = patch_cmi9880 },
+static const struct hda_device_id snd_hda_id_cmedia[] = {
+       HDA_CODEC_ENTRY(0x13f68888, "CMI8888", patch_cmi8888),
+       HDA_CODEC_ENTRY(0x13f69880, "CMI9880", patch_cmi9880),
+       HDA_CODEC_ENTRY(0x434d4980, "CMI9880", patch_cmi9880),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:13f68888");
-MODULE_ALIAS("snd-hda-codec-id:13f69880");
-MODULE_ALIAS("snd-hda-codec-id:434d4980");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_cmedia);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("C-Media HD-audio codec");
 
 static struct hda_codec_driver cmedia_driver = {
-       .preset = snd_hda_preset_cmedia,
+       .id = snd_hda_id_cmedia,
 };
 
 module_hda_codec_driver(cmedia_driver);
index 2f0ec7c45fc70d6232339761e5773349d24213a9..c8b8ef5246a6f94c99b281c837e220e118dbf8a9 100644 (file)
@@ -954,100 +954,44 @@ static int patch_conexant_auto(struct hda_codec *codec)
 /*
  */
 
-static const struct hda_codec_preset snd_hda_preset_conexant[] = {
-       { .id = 0x14f15045, .name = "CX20549 (Venice)",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15047, .name = "CX20551 (Waikiki)",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15051, .name = "CX20561 (Hermosa)",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15066, .name = "CX20582 (Pebble)",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15067, .name = "CX20583 (Pebble HSF)",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15068, .name = "CX20584",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15069, .name = "CX20585",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f1506c, .name = "CX20588",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f1506e, .name = "CX20590",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15097, .name = "CX20631",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15098, .name = "CX20632",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150a1, .name = "CX20641",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150a2, .name = "CX20642",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150ab, .name = "CX20651",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150ac, .name = "CX20652",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150b8, .name = "CX20664",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150b9, .name = "CX20665",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150f1, .name = "CX20721",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150f2, .name = "CX20722",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150f3, .name = "CX20723",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f150f4, .name = "CX20724",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f1510f, .name = "CX20751/2",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15110, .name = "CX20751/2",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15111, .name = "CX20753/4",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15113, .name = "CX20755",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15114, .name = "CX20756",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f15115, .name = "CX20757",
-         .patch = patch_conexant_auto },
-       { .id = 0x14f151d7, .name = "CX20952",
-         .patch = patch_conexant_auto },
+static const struct hda_device_id snd_hda_id_conexant[] = {
+       HDA_CODEC_ENTRY(0x14f15045, "CX20549 (Venice)", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15047, "CX20551 (Waikiki)", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15051, "CX20561 (Hermosa)", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15066, "CX20582 (Pebble)", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15067, "CX20583 (Pebble HSF)", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15068, "CX20584", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15069, "CX20585", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f1506c, "CX20588", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f1506e, "CX20590", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15097, "CX20631", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15098, "CX20632", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150a1, "CX20641", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150a2, "CX20642", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150ab, "CX20651", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150ac, "CX20652", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150b8, "CX20664", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150b9, "CX20665", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150f1, "CX20721", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150f2, "CX20722", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150f3, "CX20723", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f150f4, "CX20724", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f1510f, "CX20751/2", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15110, "CX20751/2", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15111, "CX20753/4", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15113, "CX20755", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15114, "CX20756", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f15115, "CX20757", patch_conexant_auto),
+       HDA_CODEC_ENTRY(0x14f151d7, "CX20952", patch_conexant_auto),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:14f15045");
-MODULE_ALIAS("snd-hda-codec-id:14f15047");
-MODULE_ALIAS("snd-hda-codec-id:14f15051");
-MODULE_ALIAS("snd-hda-codec-id:14f15066");
-MODULE_ALIAS("snd-hda-codec-id:14f15067");
-MODULE_ALIAS("snd-hda-codec-id:14f15068");
-MODULE_ALIAS("snd-hda-codec-id:14f15069");
-MODULE_ALIAS("snd-hda-codec-id:14f1506c");
-MODULE_ALIAS("snd-hda-codec-id:14f1506e");
-MODULE_ALIAS("snd-hda-codec-id:14f15097");
-MODULE_ALIAS("snd-hda-codec-id:14f15098");
-MODULE_ALIAS("snd-hda-codec-id:14f150a1");
-MODULE_ALIAS("snd-hda-codec-id:14f150a2");
-MODULE_ALIAS("snd-hda-codec-id:14f150ab");
-MODULE_ALIAS("snd-hda-codec-id:14f150ac");
-MODULE_ALIAS("snd-hda-codec-id:14f150b8");
-MODULE_ALIAS("snd-hda-codec-id:14f150b9");
-MODULE_ALIAS("snd-hda-codec-id:14f150f1");
-MODULE_ALIAS("snd-hda-codec-id:14f150f2");
-MODULE_ALIAS("snd-hda-codec-id:14f150f3");
-MODULE_ALIAS("snd-hda-codec-id:14f150f4");
-MODULE_ALIAS("snd-hda-codec-id:14f1510f");
-MODULE_ALIAS("snd-hda-codec-id:14f15110");
-MODULE_ALIAS("snd-hda-codec-id:14f15111");
-MODULE_ALIAS("snd-hda-codec-id:14f15113");
-MODULE_ALIAS("snd-hda-codec-id:14f15114");
-MODULE_ALIAS("snd-hda-codec-id:14f15115");
-MODULE_ALIAS("snd-hda-codec-id:14f151d7");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_conexant);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Conexant HD-audio codec");
 
 static struct hda_codec_driver conexant_driver = {
-       .preset = snd_hda_preset_conexant,
+       .id = snd_hda_id_conexant,
 };
 
 module_hda_codec_driver(conexant_driver);
index acbfbe087ee86d41f5688b0af85c4ee6f7eec43f..f503a883bef31d0fd55a38dd816f70609edcaf82 100644 (file)
@@ -1775,6 +1775,16 @@ static bool check_non_pcm_per_cvt(struct hda_codec *codec, hda_nid_t cvt_nid)
        return non_pcm;
 }
 
+/* There is a fixed mapping between audio pin node and display port
+ * on current Intel platforms:
+ * Pin Widget 5 - PORT B (port = 1 in i915 driver)
+ * Pin Widget 6 - PORT C (port = 2 in i915 driver)
+ * Pin Widget 7 - PORT D (port = 3 in i915 driver)
+ */
+static int intel_pin2port(hda_nid_t pin_nid)
+{
+       return pin_nid - 4;
+}
 
 /*
  * HDMI callbacks
@@ -1791,6 +1801,8 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
        int pin_idx = hinfo_to_pin_index(codec, hinfo);
        struct hdmi_spec_per_pin *per_pin = get_pin(spec, pin_idx);
        hda_nid_t pin_nid = per_pin->pin_nid;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct i915_audio_component *acomp = codec->bus->core.audio_component;
        bool non_pcm;
        int pinctl;
 
@@ -1807,6 +1819,13 @@ static int generic_hdmi_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
                intel_not_share_assigned_cvt(codec, pin_nid, per_pin->mux_idx);
        }
 
+       /* Call sync_audio_rate to set the N/CTS/M manually if necessary */
+       /* Todo: add DP1.2 MST audio support later */
+       if (acomp && acomp->ops && acomp->ops->sync_audio_rate)
+               acomp->ops->sync_audio_rate(acomp->dev,
+                               intel_pin2port(pin_nid),
+                               runtime->rate);
+
        non_pcm = check_non_pcm_per_cvt(codec, cvt_nid);
        mutex_lock(&per_pin->lock);
        per_pin->channels = substream->runtime->channels;
@@ -2561,7 +2580,7 @@ static int simple_playback_pcm_open(struct hda_pcm_stream *hinfo,
        struct hdmi_spec *spec = codec->spec;
        struct snd_pcm_hw_constraint_list *hw_constraints_channels = NULL;
 
-       switch (codec->preset->id) {
+       switch (codec->preset->vendor_id) {
        case 0x10de0002:
        case 0x10de0003:
        case 0x10de0005:
@@ -2879,7 +2898,7 @@ static int nvhdmi_7x_8ch_build_controls(struct hda_codec *codec)
                                     snd_pcm_alt_chmaps, 8, 0, &chmap);
        if (err < 0)
                return err;
-       switch (codec->preset->id) {
+       switch (codec->preset->vendor_id) {
        case 0x10de0002:
        case 0x10de0003:
        case 0x10de0005:
@@ -3487,138 +3506,77 @@ static int patch_via_hdmi(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_hdmi[] = {
-{ .id = 0x1002793c, .name = "RS600 HDMI",      .patch = patch_atihdmi },
-{ .id = 0x10027919, .name = "RS600 HDMI",      .patch = patch_atihdmi },
-{ .id = 0x1002791a, .name = "RS690/780 HDMI",  .patch = patch_atihdmi },
-{ .id = 0x1002aa01, .name = "R6xx HDMI",       .patch = patch_atihdmi },
-{ .id = 0x10951390, .name = "SiI1390 HDMI",    .patch = patch_generic_hdmi },
-{ .id = 0x10951392, .name = "SiI1392 HDMI",    .patch = patch_generic_hdmi },
-{ .id = 0x17e80047, .name = "Chrontel HDMI",   .patch = patch_generic_hdmi },
-{ .id = 0x10de0002, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0003, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0005, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0006, .name = "MCP77/78 HDMI",   .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de0007, .name = "MCP79/7A HDMI",   .patch = patch_nvhdmi_8ch_7x },
-{ .id = 0x10de000a, .name = "GPU 0a HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de000b, .name = "GPU 0b HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de000c, .name = "MCP89 HDMI",      .patch = patch_nvhdmi },
-{ .id = 0x10de000d, .name = "GPU 0d HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0010, .name = "GPU 10 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0011, .name = "GPU 11 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0012, .name = "GPU 12 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0013, .name = "GPU 13 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0014, .name = "GPU 14 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0015, .name = "GPU 15 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0016, .name = "GPU 16 HDMI/DP",  .patch = patch_nvhdmi },
+static const struct hda_device_id snd_hda_id_hdmi[] = {
+HDA_CODEC_ENTRY(0x1002793c, "RS600 HDMI",      patch_atihdmi),
+HDA_CODEC_ENTRY(0x10027919, "RS600 HDMI",      patch_atihdmi),
+HDA_CODEC_ENTRY(0x1002791a, "RS690/780 HDMI",  patch_atihdmi),
+HDA_CODEC_ENTRY(0x1002aa01, "R6xx HDMI",       patch_atihdmi),
+HDA_CODEC_ENTRY(0x10951390, "SiI1390 HDMI",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x10951392, "SiI1392 HDMI",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x17e80047, "Chrontel HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x10de0002, "MCP77/78 HDMI",   patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0003, "MCP77/78 HDMI",   patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0005, "MCP77/78 HDMI",   patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0006, "MCP77/78 HDMI",   patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de0007, "MCP79/7A HDMI",   patch_nvhdmi_8ch_7x),
+HDA_CODEC_ENTRY(0x10de000a, "GPU 0a HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000b, "GPU 0b HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000c, "MCP89 HDMI",      patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de000d, "GPU 0d HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0010, "GPU 10 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0011, "GPU 11 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0012, "GPU 12 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0013, "GPU 13 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0014, "GPU 14 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0015, "GPU 15 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0016, "GPU 16 HDMI/DP",  patch_nvhdmi),
 /* 17 is known to be absent */
-{ .id = 0x10de0018, .name = "GPU 18 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0019, .name = "GPU 19 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de001a, .name = "GPU 1a HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de001b, .name = "GPU 1b HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de001c, .name = "GPU 1c HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0020, .name = "Tegra30 HDMI",    .patch = patch_tegra_hdmi },
-{ .id = 0x10de0022, .name = "Tegra114 HDMI",   .patch = patch_tegra_hdmi },
-{ .id = 0x10de0028, .name = "Tegra124 HDMI",   .patch = patch_tegra_hdmi },
-{ .id = 0x10de0029, .name = "Tegra210 HDMI/DP",        .patch = patch_tegra_hdmi },
-{ .id = 0x10de0040, .name = "GPU 40 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0041, .name = "GPU 41 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0042, .name = "GPU 42 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0043, .name = "GPU 43 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0044, .name = "GPU 44 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0051, .name = "GPU 51 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0060, .name = "GPU 60 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0067, .name = "MCP67 HDMI",      .patch = patch_nvhdmi_2ch },
-{ .id = 0x10de0070, .name = "GPU 70 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0071, .name = "GPU 71 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de0072, .name = "GPU 72 HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de007d, .name = "GPU 7d HDMI/DP",  .patch = patch_nvhdmi },
-{ .id = 0x10de8001, .name = "MCP73 HDMI",      .patch = patch_nvhdmi_2ch },
-{ .id = 0x11069f80, .name = "VX900 HDMI/DP",   .patch = patch_via_hdmi },
-{ .id = 0x11069f81, .name = "VX900 HDMI/DP",   .patch = patch_via_hdmi },
-{ .id = 0x11069f84, .name = "VX11 HDMI/DP",    .patch = patch_generic_hdmi },
-{ .id = 0x11069f85, .name = "VX11 HDMI/DP",    .patch = patch_generic_hdmi },
-{ .id = 0x80860054, .name = "IbexPeak HDMI",   .patch = patch_generic_hdmi },
-{ .id = 0x80862801, .name = "Bearlake HDMI",   .patch = patch_generic_hdmi },
-{ .id = 0x80862802, .name = "Cantiga HDMI",    .patch = patch_generic_hdmi },
-{ .id = 0x80862803, .name = "Eaglelake HDMI",  .patch = patch_generic_hdmi },
-{ .id = 0x80862804, .name = "IbexPeak HDMI",   .patch = patch_generic_hdmi },
-{ .id = 0x80862805, .name = "CougarPoint HDMI",        .patch = patch_generic_hdmi },
-{ .id = 0x80862806, .name = "PantherPoint HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862807, .name = "Haswell HDMI",    .patch = patch_generic_hdmi },
-{ .id = 0x80862808, .name = "Broadwell HDMI",  .patch = patch_generic_hdmi },
-{ .id = 0x80862809, .name = "Skylake HDMI",    .patch = patch_generic_hdmi },
-{ .id = 0x8086280a, .name = "Broxton HDMI",    .patch = patch_generic_hdmi },
-{ .id = 0x80862880, .name = "CedarTrail HDMI", .patch = patch_generic_hdmi },
-{ .id = 0x80862882, .name = "Valleyview2 HDMI",        .patch = patch_generic_hdmi },
-{ .id = 0x80862883, .name = "Braswell HDMI",   .patch = patch_generic_hdmi },
-{ .id = 0x808629fb, .name = "Crestline HDMI",  .patch = patch_generic_hdmi },
+HDA_CODEC_ENTRY(0x10de0018, "GPU 18 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0019, "GPU 19 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001a, "GPU 1a HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001b, "GPU 1b HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de001c, "GPU 1c HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0020, "Tegra30 HDMI",    patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0022, "Tegra114 HDMI",   patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0028, "Tegra124 HDMI",   patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0029, "Tegra210 HDMI/DP",        patch_tegra_hdmi),
+HDA_CODEC_ENTRY(0x10de0040, "GPU 40 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0041, "GPU 41 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0042, "GPU 42 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0043, "GPU 43 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0044, "GPU 44 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0051, "GPU 51 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0060, "GPU 60 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0067, "MCP67 HDMI",      patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x10de0070, "GPU 70 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0071, "GPU 71 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de0072, "GPU 72 HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de007d, "GPU 7d HDMI/DP",  patch_nvhdmi),
+HDA_CODEC_ENTRY(0x10de8001, "MCP73 HDMI",      patch_nvhdmi_2ch),
+HDA_CODEC_ENTRY(0x11069f80, "VX900 HDMI/DP",   patch_via_hdmi),
+HDA_CODEC_ENTRY(0x11069f81, "VX900 HDMI/DP",   patch_via_hdmi),
+HDA_CODEC_ENTRY(0x11069f84, "VX11 HDMI/DP",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x11069f85, "VX11 HDMI/DP",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80860054, "IbexPeak HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862801, "Bearlake HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862802, "Cantiga HDMI",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862803, "Eaglelake HDMI",  patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862804, "IbexPeak HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862805, "CougarPoint HDMI",        patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862806, "PantherPoint HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862807, "Haswell HDMI",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862808, "Broadwell HDMI",  patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862809, "Skylake HDMI",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x8086280a, "Broxton HDMI",    patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862880, "CedarTrail HDMI", patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862882, "Valleyview2 HDMI",        patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x80862883, "Braswell HDMI",   patch_generic_hdmi),
+HDA_CODEC_ENTRY(0x808629fb, "Crestline HDMI",  patch_generic_hdmi),
 /* special ID for generic HDMI */
-{ .id = HDA_CODEC_ID_GENERIC_HDMI, .patch = patch_generic_hdmi },
+HDA_CODEC_ENTRY(HDA_CODEC_ID_GENERIC_HDMI, "Generic HDMI", patch_generic_hdmi),
 {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:1002793c");
-MODULE_ALIAS("snd-hda-codec-id:10027919");
-MODULE_ALIAS("snd-hda-codec-id:1002791a");
-MODULE_ALIAS("snd-hda-codec-id:1002aa01");
-MODULE_ALIAS("snd-hda-codec-id:10951390");
-MODULE_ALIAS("snd-hda-codec-id:10951392");
-MODULE_ALIAS("snd-hda-codec-id:10de0002");
-MODULE_ALIAS("snd-hda-codec-id:10de0003");
-MODULE_ALIAS("snd-hda-codec-id:10de0005");
-MODULE_ALIAS("snd-hda-codec-id:10de0006");
-MODULE_ALIAS("snd-hda-codec-id:10de0007");
-MODULE_ALIAS("snd-hda-codec-id:10de000a");
-MODULE_ALIAS("snd-hda-codec-id:10de000b");
-MODULE_ALIAS("snd-hda-codec-id:10de000c");
-MODULE_ALIAS("snd-hda-codec-id:10de000d");
-MODULE_ALIAS("snd-hda-codec-id:10de0010");
-MODULE_ALIAS("snd-hda-codec-id:10de0011");
-MODULE_ALIAS("snd-hda-codec-id:10de0012");
-MODULE_ALIAS("snd-hda-codec-id:10de0013");
-MODULE_ALIAS("snd-hda-codec-id:10de0014");
-MODULE_ALIAS("snd-hda-codec-id:10de0015");
-MODULE_ALIAS("snd-hda-codec-id:10de0016");
-MODULE_ALIAS("snd-hda-codec-id:10de0018");
-MODULE_ALIAS("snd-hda-codec-id:10de0019");
-MODULE_ALIAS("snd-hda-codec-id:10de001a");
-MODULE_ALIAS("snd-hda-codec-id:10de001b");
-MODULE_ALIAS("snd-hda-codec-id:10de001c");
-MODULE_ALIAS("snd-hda-codec-id:10de0028");
-MODULE_ALIAS("snd-hda-codec-id:10de0040");
-MODULE_ALIAS("snd-hda-codec-id:10de0041");
-MODULE_ALIAS("snd-hda-codec-id:10de0042");
-MODULE_ALIAS("snd-hda-codec-id:10de0043");
-MODULE_ALIAS("snd-hda-codec-id:10de0044");
-MODULE_ALIAS("snd-hda-codec-id:10de0051");
-MODULE_ALIAS("snd-hda-codec-id:10de0060");
-MODULE_ALIAS("snd-hda-codec-id:10de0067");
-MODULE_ALIAS("snd-hda-codec-id:10de0070");
-MODULE_ALIAS("snd-hda-codec-id:10de0071");
-MODULE_ALIAS("snd-hda-codec-id:10de0072");
-MODULE_ALIAS("snd-hda-codec-id:10de007d");
-MODULE_ALIAS("snd-hda-codec-id:10de8001");
-MODULE_ALIAS("snd-hda-codec-id:11069f80");
-MODULE_ALIAS("snd-hda-codec-id:11069f81");
-MODULE_ALIAS("snd-hda-codec-id:11069f84");
-MODULE_ALIAS("snd-hda-codec-id:11069f85");
-MODULE_ALIAS("snd-hda-codec-id:17e80047");
-MODULE_ALIAS("snd-hda-codec-id:80860054");
-MODULE_ALIAS("snd-hda-codec-id:80862801");
-MODULE_ALIAS("snd-hda-codec-id:80862802");
-MODULE_ALIAS("snd-hda-codec-id:80862803");
-MODULE_ALIAS("snd-hda-codec-id:80862804");
-MODULE_ALIAS("snd-hda-codec-id:80862805");
-MODULE_ALIAS("snd-hda-codec-id:80862806");
-MODULE_ALIAS("snd-hda-codec-id:80862807");
-MODULE_ALIAS("snd-hda-codec-id:80862808");
-MODULE_ALIAS("snd-hda-codec-id:80862809");
-MODULE_ALIAS("snd-hda-codec-id:8086280a");
-MODULE_ALIAS("snd-hda-codec-id:80862880");
-MODULE_ALIAS("snd-hda-codec-id:80862882");
-MODULE_ALIAS("snd-hda-codec-id:80862883");
-MODULE_ALIAS("snd-hda-codec-id:808629fb");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_hdmi);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("HDMI HD-audio codec");
@@ -3627,7 +3585,7 @@ MODULE_ALIAS("snd-hda-codec-nvhdmi");
 MODULE_ALIAS("snd-hda-codec-atihdmi");
 
 static struct hda_codec_driver hdmi_driver = {
-       .preset = snd_hda_preset_hdmi,
+       .id = snd_hda_id_hdmi,
 };
 
 module_hda_codec_driver(hdmi_driver);
index 720a9fb32e2032519da87dfca23d7bcb0ca23c1d..2f7b065f9ac43e88ee19b6845506c066485c679a 100644 (file)
@@ -822,17 +822,7 @@ static const struct hda_codec_ops alc_patch_ops = {
 };
 
 
-/* replace the codec chip_name with the given string */
-static int alc_codec_rename(struct hda_codec *codec, const char *name)
-{
-       kfree(codec->core.chip_name);
-       codec->core.chip_name = kstrdup(name, GFP_KERNEL);
-       if (!codec->core.chip_name) {
-               alc_free(codec);
-               return -ENOMEM;
-       }
-       return 0;
-}
+#define alc_codec_rename(codec, name) snd_hda_codec_set_name(codec, name)
 
 /*
  * Rename codecs appropriately from COEF value or subvendor id
@@ -6640,78 +6630,70 @@ static int patch_alc680(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_realtek[] = {
-       { .id = 0x10ec0221, .name = "ALC221", .patch = patch_alc269 },
-       { .id = 0x10ec0231, .name = "ALC231", .patch = patch_alc269 },
-       { .id = 0x10ec0233, .name = "ALC233", .patch = patch_alc269 },
-       { .id = 0x10ec0235, .name = "ALC233", .patch = patch_alc269 },
-       { .id = 0x10ec0255, .name = "ALC255", .patch = patch_alc269 },
-       { .id = 0x10ec0256, .name = "ALC256", .patch = patch_alc269 },
-       { .id = 0x10ec0260, .name = "ALC260", .patch = patch_alc260 },
-       { .id = 0x10ec0262, .name = "ALC262", .patch = patch_alc262 },
-       { .id = 0x10ec0267, .name = "ALC267", .patch = patch_alc268 },
-       { .id = 0x10ec0268, .name = "ALC268", .patch = patch_alc268 },
-       { .id = 0x10ec0269, .name = "ALC269", .patch = patch_alc269 },
-       { .id = 0x10ec0270, .name = "ALC270", .patch = patch_alc269 },
-       { .id = 0x10ec0272, .name = "ALC272", .patch = patch_alc662 },
-       { .id = 0x10ec0275, .name = "ALC275", .patch = patch_alc269 },
-       { .id = 0x10ec0276, .name = "ALC276", .patch = patch_alc269 },
-       { .id = 0x10ec0280, .name = "ALC280", .patch = patch_alc269 },
-       { .id = 0x10ec0282, .name = "ALC282", .patch = patch_alc269 },
-       { .id = 0x10ec0283, .name = "ALC283", .patch = patch_alc269 },
-       { .id = 0x10ec0284, .name = "ALC284", .patch = patch_alc269 },
-       { .id = 0x10ec0285, .name = "ALC285", .patch = patch_alc269 },
-       { .id = 0x10ec0286, .name = "ALC286", .patch = patch_alc269 },
-       { .id = 0x10ec0288, .name = "ALC288", .patch = patch_alc269 },
-       { .id = 0x10ec0290, .name = "ALC290", .patch = patch_alc269 },
-       { .id = 0x10ec0292, .name = "ALC292", .patch = patch_alc269 },
-       { .id = 0x10ec0293, .name = "ALC293", .patch = patch_alc269 },
-       { .id = 0x10ec0298, .name = "ALC298", .patch = patch_alc269 },
-       { .id = 0x10ec0861, .rev = 0x100340, .name = "ALC660",
-         .patch = patch_alc861 },
-       { .id = 0x10ec0660, .name = "ALC660-VD", .patch = patch_alc861vd },
-       { .id = 0x10ec0861, .name = "ALC861", .patch = patch_alc861 },
-       { .id = 0x10ec0862, .name = "ALC861-VD", .patch = patch_alc861vd },
-       { .id = 0x10ec0662, .rev = 0x100002, .name = "ALC662 rev2",
-         .patch = patch_alc882 },
-       { .id = 0x10ec0662, .rev = 0x100101, .name = "ALC662 rev1",
-         .patch = patch_alc662 },
-       { .id = 0x10ec0662, .rev = 0x100300, .name = "ALC662 rev3",
-         .patch = patch_alc662 },
-       { .id = 0x10ec0663, .name = "ALC663", .patch = patch_alc662 },
-       { .id = 0x10ec0665, .name = "ALC665", .patch = patch_alc662 },
-       { .id = 0x10ec0667, .name = "ALC667", .patch = patch_alc662 },
-       { .id = 0x10ec0668, .name = "ALC668", .patch = patch_alc662 },
-       { .id = 0x10ec0670, .name = "ALC670", .patch = patch_alc662 },
-       { .id = 0x10ec0671, .name = "ALC671", .patch = patch_alc662 },
-       { .id = 0x10ec0680, .name = "ALC680", .patch = patch_alc680 },
-       { .id = 0x10ec0867, .name = "ALC891", .patch = patch_alc882 },
-       { .id = 0x10ec0880, .name = "ALC880", .patch = patch_alc880 },
-       { .id = 0x10ec0882, .name = "ALC882", .patch = patch_alc882 },
-       { .id = 0x10ec0883, .name = "ALC883", .patch = patch_alc882 },
-       { .id = 0x10ec0885, .rev = 0x100101, .name = "ALC889A",
-         .patch = patch_alc882 },
-       { .id = 0x10ec0885, .rev = 0x100103, .name = "ALC889A",
-         .patch = patch_alc882 },
-       { .id = 0x10ec0885, .name = "ALC885", .patch = patch_alc882 },
-       { .id = 0x10ec0887, .name = "ALC887", .patch = patch_alc882 },
-       { .id = 0x10ec0888, .rev = 0x100101, .name = "ALC1200",
-         .patch = patch_alc882 },
-       { .id = 0x10ec0888, .name = "ALC888", .patch = patch_alc882 },
-       { .id = 0x10ec0889, .name = "ALC889", .patch = patch_alc882 },
-       { .id = 0x10ec0892, .name = "ALC892", .patch = patch_alc662 },
-       { .id = 0x10ec0899, .name = "ALC898", .patch = patch_alc882 },
-       { .id = 0x10ec0900, .name = "ALC1150", .patch = patch_alc882 },
+static const struct hda_device_id snd_hda_id_realtek[] = {
+       HDA_CODEC_ENTRY(0x10ec0221, "ALC221", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0231, "ALC231", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0233, "ALC233", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0235, "ALC233", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0255, "ALC255", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0256, "ALC256", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0260, "ALC260", patch_alc260),
+       HDA_CODEC_ENTRY(0x10ec0262, "ALC262", patch_alc262),
+       HDA_CODEC_ENTRY(0x10ec0267, "ALC267", patch_alc268),
+       HDA_CODEC_ENTRY(0x10ec0268, "ALC268", patch_alc268),
+       HDA_CODEC_ENTRY(0x10ec0269, "ALC269", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0270, "ALC270", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0272, "ALC272", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0275, "ALC275", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0276, "ALC276", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0280, "ALC280", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0282, "ALC282", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0283, "ALC283", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0284, "ALC284", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0285, "ALC285", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0286, "ALC286", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0288, "ALC288", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0290, "ALC290", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0292, "ALC292", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0293, "ALC293", patch_alc269),
+       HDA_CODEC_ENTRY(0x10ec0298, "ALC298", patch_alc269),
+       HDA_CODEC_REV_ENTRY(0x10ec0861, 0x100340, "ALC660", patch_alc861),
+       HDA_CODEC_ENTRY(0x10ec0660, "ALC660-VD", patch_alc861vd),
+       HDA_CODEC_ENTRY(0x10ec0861, "ALC861", patch_alc861),
+       HDA_CODEC_ENTRY(0x10ec0862, "ALC861-VD", patch_alc861vd),
+       HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100002, "ALC662 rev2", patch_alc882),
+       HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100101, "ALC662 rev1", patch_alc662),
+       HDA_CODEC_REV_ENTRY(0x10ec0662, 0x100300, "ALC662 rev3", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0663, "ALC663", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0665, "ALC665", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0667, "ALC667", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0668, "ALC668", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0670, "ALC670", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0671, "ALC671", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0680, "ALC680", patch_alc680),
+       HDA_CODEC_ENTRY(0x10ec0867, "ALC891", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0880, "ALC880", patch_alc880),
+       HDA_CODEC_ENTRY(0x10ec0882, "ALC882", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0883, "ALC883", patch_alc882),
+       HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100101, "ALC889A", patch_alc882),
+       HDA_CODEC_REV_ENTRY(0x10ec0885, 0x100103, "ALC889A", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0885, "ALC885", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0887, "ALC887", patch_alc882),
+       HDA_CODEC_REV_ENTRY(0x10ec0888, 0x100101, "ALC1200", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0888, "ALC888", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0889, "ALC889", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0892, "ALC892", patch_alc662),
+       HDA_CODEC_ENTRY(0x10ec0899, "ALC898", patch_alc882),
+       HDA_CODEC_ENTRY(0x10ec0900, "ALC1150", patch_alc882),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:10ec*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_realtek);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Realtek HD-audio codec");
 
 static struct hda_codec_driver realtek_driver = {
-       .preset = snd_hda_preset_realtek,
+       .id = snd_hda_id_realtek,
 };
 
 module_hda_codec_driver(realtek_driver);
index 5104bebb228699f1a04e754429f57e31622a521a..ffda38c45509583dd9e43f5f119c5d835b23e05b 100644 (file)
@@ -289,41 +289,30 @@ static int patch_si3054(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_si3054[] = {
-       { .id = 0x163c3055, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x163c3155, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x11c13026, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x11c13055, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x11c13155, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x10573055, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x10573057, .name = "Si3054", .patch = patch_si3054 },
-       { .id = 0x10573155, .name = "Si3054", .patch = patch_si3054 },
+static const struct hda_device_id snd_hda_id_si3054[] = {
+       HDA_CODEC_ENTRY(0x163c3055, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x163c3155, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x11c13026, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x11c13055, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x11c13155, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x10573055, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x10573057, "Si3054", patch_si3054),
+       HDA_CODEC_ENTRY(0x10573155, "Si3054", patch_si3054),
        /* VIA HDA on Clevo m540 */
-       { .id = 0x11063288, .name = "Si3054", .patch = patch_si3054 },
+       HDA_CODEC_ENTRY(0x11063288, "Si3054", patch_si3054),
        /* Asus A8J Modem (SM56) */
-       { .id = 0x15433155, .name = "Si3054", .patch = patch_si3054 },
+       HDA_CODEC_ENTRY(0x15433155, "Si3054", patch_si3054),
        /* LG LW20 modem */
-       { .id = 0x18540018, .name = "Si3054", .patch = patch_si3054 },
+       HDA_CODEC_ENTRY(0x18540018, "Si3054", patch_si3054),
        {}
 };
-
-MODULE_ALIAS("snd-hda-codec-id:163c3055");
-MODULE_ALIAS("snd-hda-codec-id:163c3155");
-MODULE_ALIAS("snd-hda-codec-id:11c13026");
-MODULE_ALIAS("snd-hda-codec-id:11c13055");
-MODULE_ALIAS("snd-hda-codec-id:11c13155");
-MODULE_ALIAS("snd-hda-codec-id:10573055");
-MODULE_ALIAS("snd-hda-codec-id:10573057");
-MODULE_ALIAS("snd-hda-codec-id:10573155");
-MODULE_ALIAS("snd-hda-codec-id:11063288");
-MODULE_ALIAS("snd-hda-codec-id:15433155");
-MODULE_ALIAS("snd-hda-codec-id:18540018");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_si3054);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("Si3054 HD-audio modem codec");
 
 static struct hda_codec_driver si3054_driver = {
-       .preset = snd_hda_preset_si3054,
+       .id = snd_hda_id_si3054,
 };
 
 module_hda_codec_driver(si3054_driver);
index def5cc8dff0293c2f70c4c3fcf67da1aea1bf55e..08a0f6a35cca5305d106b8f07574d7cc9a7a5960 100644 (file)
@@ -5012,121 +5012,119 @@ static int patch_stac9872(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_sigmatel[] = {
-       { .id = 0x83847690, .name = "STAC9200", .patch = patch_stac9200 },
-       { .id = 0x83847882, .name = "STAC9220 A1", .patch = patch_stac922x },
-       { .id = 0x83847680, .name = "STAC9221 A1", .patch = patch_stac922x },
-       { .id = 0x83847880, .name = "STAC9220 A2", .patch = patch_stac922x },
-       { .id = 0x83847681, .name = "STAC9220D/9223D A2", .patch = patch_stac922x },
-       { .id = 0x83847682, .name = "STAC9221 A2", .patch = patch_stac922x },
-       { .id = 0x83847683, .name = "STAC9221D A2", .patch = patch_stac922x },
-       { .id = 0x83847618, .name = "STAC9227", .patch = patch_stac927x },
-       { .id = 0x83847619, .name = "STAC9227", .patch = patch_stac927x },
-       { .id = 0x83847616, .name = "STAC9228", .patch = patch_stac927x },
-       { .id = 0x83847617, .name = "STAC9228", .patch = patch_stac927x },
-       { .id = 0x83847614, .name = "STAC9229", .patch = patch_stac927x },
-       { .id = 0x83847615, .name = "STAC9229", .patch = patch_stac927x },
-       { .id = 0x83847620, .name = "STAC9274", .patch = patch_stac927x },
-       { .id = 0x83847621, .name = "STAC9274D", .patch = patch_stac927x },
-       { .id = 0x83847622, .name = "STAC9273X", .patch = patch_stac927x },
-       { .id = 0x83847623, .name = "STAC9273D", .patch = patch_stac927x },
-       { .id = 0x83847624, .name = "STAC9272X", .patch = patch_stac927x },
-       { .id = 0x83847625, .name = "STAC9272D", .patch = patch_stac927x },
-       { .id = 0x83847626, .name = "STAC9271X", .patch = patch_stac927x },
-       { .id = 0x83847627, .name = "STAC9271D", .patch = patch_stac927x },
-       { .id = 0x83847628, .name = "STAC9274X5NH", .patch = patch_stac927x },
-       { .id = 0x83847629, .name = "STAC9274D5NH", .patch = patch_stac927x },
-       { .id = 0x83847632, .name = "STAC9202",  .patch = patch_stac925x },
-       { .id = 0x83847633, .name = "STAC9202D", .patch = patch_stac925x },
-       { .id = 0x83847634, .name = "STAC9250", .patch = patch_stac925x },
-       { .id = 0x83847635, .name = "STAC9250D", .patch = patch_stac925x },
-       { .id = 0x83847636, .name = "STAC9251", .patch = patch_stac925x },
-       { .id = 0x83847637, .name = "STAC9250D", .patch = patch_stac925x },
-       { .id = 0x83847645, .name = "92HD206X", .patch = patch_stac927x },
-       { .id = 0x83847646, .name = "92HD206D", .patch = patch_stac927x },
-       /* The following does not take into account .id=0x83847661 when subsys =
-        * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
-        * currently not fully supported.
-        */
-       { .id = 0x83847661, .name = "CXD9872RD/K", .patch = patch_stac9872 },
-       { .id = 0x83847662, .name = "STAC9872AK", .patch = patch_stac9872 },
-       { .id = 0x83847664, .name = "CXD9872AKD", .patch = patch_stac9872 },
-       { .id = 0x83847698, .name = "STAC9205", .patch = patch_stac9205 },
-       { .id = 0x838476a0, .name = "STAC9205", .patch = patch_stac9205 },
-       { .id = 0x838476a1, .name = "STAC9205D", .patch = patch_stac9205 },
-       { .id = 0x838476a2, .name = "STAC9204", .patch = patch_stac9205 },
-       { .id = 0x838476a3, .name = "STAC9204D", .patch = patch_stac9205 },
-       { .id = 0x838476a4, .name = "STAC9255", .patch = patch_stac9205 },
-       { .id = 0x838476a5, .name = "STAC9255D", .patch = patch_stac9205 },
-       { .id = 0x838476a6, .name = "STAC9254", .patch = patch_stac9205 },
-       { .id = 0x838476a7, .name = "STAC9254D", .patch = patch_stac9205 },
-       { .id = 0x111d7603, .name = "92HD75B3X5", .patch = patch_stac92hd71bxx},
-       { .id = 0x111d7604, .name = "92HD83C1X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76d4, .name = "92HD83C1C5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d7605, .name = "92HD81B1X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76d5, .name = "92HD81B1C5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76d1, .name = "92HD87B1/3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76d9, .name = "92HD87B2/4", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d7666, .name = "92HD88B3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d7667, .name = "92HD88B1", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d7668, .name = "92HD88B2", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d7669, .name = "92HD88B4", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d7608, .name = "92HD75B2X5", .patch = patch_stac92hd71bxx},
-       { .id = 0x111d7674, .name = "92HD73D1X5", .patch = patch_stac92hd73xx },
-       { .id = 0x111d7675, .name = "92HD73C1X5", .patch = patch_stac92hd73xx },
-       { .id = 0x111d7676, .name = "92HD73E1X5", .patch = patch_stac92hd73xx },
-       { .id = 0x111d7695, .name = "92HD95", .patch = patch_stac92hd95 },
-       { .id = 0x111d76b0, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b1, .name = "92HD71B8X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b2, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b3, .name = "92HD71B7X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b4, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b5, .name = "92HD71B6X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b6, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76b7, .name = "92HD71B5X", .patch = patch_stac92hd71bxx },
-       { .id = 0x111d76c0, .name = "92HD89C3", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c1, .name = "92HD89C2", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c2, .name = "92HD89C1", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c3, .name = "92HD89B3", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c4, .name = "92HD89B2", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c5, .name = "92HD89B1", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c6, .name = "92HD89E3", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c7, .name = "92HD89E2", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c8, .name = "92HD89E1", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76c9, .name = "92HD89D3", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76ca, .name = "92HD89D2", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76cb, .name = "92HD89D1", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76cc, .name = "92HD89F3", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76cd, .name = "92HD89F2", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76ce, .name = "92HD89F1", .patch = patch_stac92hd73xx },
-       { .id = 0x111d76df, .name = "92HD93BXX", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76e0, .name = "92HD91BXX", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76e3, .name = "92HD98BXX", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76e5, .name = "92HD99BXX", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76e7, .name = "92HD90BXX", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76e8, .name = "92HD66B1X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76e9, .name = "92HD66B2X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76ea, .name = "92HD66B3X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76eb, .name = "92HD66C1X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76ec, .name = "92HD66C2X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76ed, .name = "92HD66C3X5", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76ee, .name = "92HD66B1X3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76ef, .name = "92HD66B2X3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76f0, .name = "92HD66B3X3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76f1, .name = "92HD66C1X3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76f2, .name = "92HD66C2X3", .patch = patch_stac92hd83xxx},
-       { .id = 0x111d76f3, .name = "92HD66C3/65", .patch = patch_stac92hd83xxx},
+static const struct hda_device_id snd_hda_id_sigmatel[] = {
+       HDA_CODEC_ENTRY(0x83847690, "STAC9200", patch_stac9200),
+       HDA_CODEC_ENTRY(0x83847882, "STAC9220 A1", patch_stac922x),
+       HDA_CODEC_ENTRY(0x83847680, "STAC9221 A1", patch_stac922x),
+       HDA_CODEC_ENTRY(0x83847880, "STAC9220 A2", patch_stac922x),
+       HDA_CODEC_ENTRY(0x83847681, "STAC9220D/9223D A2", patch_stac922x),
+       HDA_CODEC_ENTRY(0x83847682, "STAC9221 A2", patch_stac922x),
+       HDA_CODEC_ENTRY(0x83847683, "STAC9221D A2", patch_stac922x),
+       HDA_CODEC_ENTRY(0x83847618, "STAC9227", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847619, "STAC9227", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847616, "STAC9228", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847617, "STAC9228", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847614, "STAC9229", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847615, "STAC9229", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847620, "STAC9274", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847621, "STAC9274D", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847622, "STAC9273X", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847623, "STAC9273D", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847624, "STAC9272X", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847625, "STAC9272D", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847626, "STAC9271X", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847627, "STAC9271D", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847628, "STAC9274X5NH", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847629, "STAC9274D5NH", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847632, "STAC9202",  patch_stac925x),
+       HDA_CODEC_ENTRY(0x83847633, "STAC9202D", patch_stac925x),
+       HDA_CODEC_ENTRY(0x83847634, "STAC9250", patch_stac925x),
+       HDA_CODEC_ENTRY(0x83847635, "STAC9250D", patch_stac925x),
+       HDA_CODEC_ENTRY(0x83847636, "STAC9251", patch_stac925x),
+       HDA_CODEC_ENTRY(0x83847637, "STAC9250D", patch_stac925x),
+       HDA_CODEC_ENTRY(0x83847645, "92HD206X", patch_stac927x),
+       HDA_CODEC_ENTRY(0x83847646, "92HD206D", patch_stac927x),
+       /* The following does not take into account .id=0x83847661 when subsys =
+        * 104D0C00 which is STAC9225s. Because of this, some SZ Notebooks are
+        * currently not fully supported.
+        */
+       HDA_CODEC_ENTRY(0x83847661, "CXD9872RD/K", patch_stac9872),
+       HDA_CODEC_ENTRY(0x83847662, "STAC9872AK", patch_stac9872),
+       HDA_CODEC_ENTRY(0x83847664, "CXD9872AKD", patch_stac9872),
+       HDA_CODEC_ENTRY(0x83847698, "STAC9205", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a0, "STAC9205", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a1, "STAC9205D", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a2, "STAC9204", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a3, "STAC9204D", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a4, "STAC9255", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a5, "STAC9255D", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a6, "STAC9254", patch_stac9205),
+       HDA_CODEC_ENTRY(0x838476a7, "STAC9254D", patch_stac9205),
+       HDA_CODEC_ENTRY(0x111d7603, "92HD75B3X5", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d7604, "92HD83C1X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76d4, "92HD83C1C5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d7605, "92HD81B1X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76d5, "92HD81B1C5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76d1, "92HD87B1/3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76d9, "92HD87B2/4", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d7666, "92HD88B3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d7667, "92HD88B1", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d7668, "92HD88B2", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d7669, "92HD88B4", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d7608, "92HD75B2X5", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d7674, "92HD73D1X5", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d7675, "92HD73C1X5", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d7676, "92HD73E1X5", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d7695, "92HD95", patch_stac92hd95),
+       HDA_CODEC_ENTRY(0x111d76b0, "92HD71B8X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b1, "92HD71B8X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b2, "92HD71B7X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b3, "92HD71B7X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b4, "92HD71B6X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b5, "92HD71B6X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b6, "92HD71B5X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76b7, "92HD71B5X", patch_stac92hd71bxx),
+       HDA_CODEC_ENTRY(0x111d76c0, "92HD89C3", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c1, "92HD89C2", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c2, "92HD89C1", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c3, "92HD89B3", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c4, "92HD89B2", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c5, "92HD89B1", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c6, "92HD89E3", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c7, "92HD89E2", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c8, "92HD89E1", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76c9, "92HD89D3", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76ca, "92HD89D2", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76cb, "92HD89D1", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76cc, "92HD89F3", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76cd, "92HD89F2", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76ce, "92HD89F1", patch_stac92hd73xx),
+       HDA_CODEC_ENTRY(0x111d76df, "92HD93BXX", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76e0, "92HD91BXX", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76e3, "92HD98BXX", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76e5, "92HD99BXX", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76e7, "92HD90BXX", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76e8, "92HD66B1X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76e9, "92HD66B2X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76ea, "92HD66B3X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76eb, "92HD66C1X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76ec, "92HD66C2X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76ed, "92HD66C3X5", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76ee, "92HD66B1X3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76ef, "92HD66B2X3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76f0, "92HD66B3X3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76f1, "92HD66C1X3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76f2, "92HD66C2X3", patch_stac92hd83xxx),
+       HDA_CODEC_ENTRY(0x111d76f3, "92HD66C3/65", patch_stac92hd83xxx),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:8384*");
-MODULE_ALIAS("snd-hda-codec-id:111d*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_sigmatel);
 
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("IDT/Sigmatel HD-audio codec");
 
 static struct hda_codec_driver sigmatel_driver = {
-       .preset = snd_hda_preset_sigmatel,
+       .id = snd_hda_id_sigmatel,
 };
 
 module_hda_codec_driver(sigmatel_driver);
index da5366405eda55a6eccbca0e14bce5631c851cdc..fc30d1e8aa76a6b2aa9ca1de992b5a1c937bb971 100644 (file)
@@ -785,21 +785,11 @@ static int patch_vt1708S(struct hda_codec *codec)
        override_mic_boost(codec, 0x1e, 0, 3, 40);
 
        /* correct names for VT1708BCE */
-       if (get_codec_type(codec) == VT1708BCE) {
-               kfree(codec->core.chip_name);
-               codec->core.chip_name = kstrdup("VT1708BCE", GFP_KERNEL);
-               snprintf(codec->card->mixername,
-                        sizeof(codec->card->mixername),
-                        "%s %s", codec->core.vendor_name, codec->core.chip_name);
-       }
+       if (get_codec_type(codec) == VT1708BCE)
+               snd_hda_codec_set_name(codec, "VT1708BCE");
        /* correct names for VT1705 */
-       if (codec->core.vendor_id == 0x11064397) {
-               kfree(codec->core.chip_name);
-               codec->core.chip_name = kstrdup("VT1705", GFP_KERNEL);
-               snprintf(codec->card->mixername,
-                        sizeof(codec->card->mixername),
-                        "%s %s", codec->core.vendor_name, codec->core.chip_name);
-       }
+       if (codec->core.vendor_id == 0x11064397)
+               snd_hda_codec_set_name(codec, "VT1705");
 
        /* automatic parse from the BIOS config */
        err = via_parse_auto_config(codec);
@@ -1210,109 +1200,64 @@ static int patch_vt3476(struct hda_codec *codec)
 /*
  * patch entries
  */
-static const struct hda_codec_preset snd_hda_preset_via[] = {
-       { .id = 0x11061708, .name = "VT1708", .patch = patch_vt1708},
-       { .id = 0x11061709, .name = "VT1708", .patch = patch_vt1708},
-       { .id = 0x1106170a, .name = "VT1708", .patch = patch_vt1708},
-       { .id = 0x1106170b, .name = "VT1708", .patch = patch_vt1708},
-       { .id = 0x1106e710, .name = "VT1709 10-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e711, .name = "VT1709 10-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e712, .name = "VT1709 10-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e713, .name = "VT1709 10-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e714, .name = "VT1709 6-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e715, .name = "VT1709 6-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e716, .name = "VT1709 6-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e717, .name = "VT1709 6-Ch",
-         .patch = patch_vt1709},
-       { .id = 0x1106e720, .name = "VT1708B 8-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e721, .name = "VT1708B 8-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e722, .name = "VT1708B 8-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e723, .name = "VT1708B 8-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e724, .name = "VT1708B 4-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e725, .name = "VT1708B 4-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e726, .name = "VT1708B 4-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x1106e727, .name = "VT1708B 4-Ch",
-         .patch = patch_vt1708B},
-       { .id = 0x11060397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11061397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11062397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11063397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11064397, .name = "VT1705",
-         .patch = patch_vt1708S},
-       { .id = 0x11065397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11066397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11067397, .name = "VT1708S",
-         .patch = patch_vt1708S},
-       { .id = 0x11060398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11061398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11062398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11063398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11064398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11065398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11066398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11067398, .name = "VT1702",
-         .patch = patch_vt1702},
-       { .id = 0x11060428, .name = "VT1718S",
-         .patch = patch_vt1718S},
-       { .id = 0x11064428, .name = "VT1718S",
-         .patch = patch_vt1718S},
-       { .id = 0x11060441, .name = "VT2020",
-         .patch = patch_vt1718S},
-       { .id = 0x11064441, .name = "VT1828S",
-         .patch = patch_vt1718S},
-       { .id = 0x11060433, .name = "VT1716S",
-         .patch = patch_vt1716S},
-       { .id = 0x1106a721, .name = "VT1716S",
-         .patch = patch_vt1716S},
-       { .id = 0x11060438, .name = "VT2002P", .patch = patch_vt2002P},
-       { .id = 0x11064438, .name = "VT2002P", .patch = patch_vt2002P},
-       { .id = 0x11060448, .name = "VT1812", .patch = patch_vt1812},
-       { .id = 0x11060440, .name = "VT1818S",
-         .patch = patch_vt1708S},
-       { .id = 0x11060446, .name = "VT1802",
-               .patch = patch_vt2002P},
-       { .id = 0x11068446, .name = "VT1802",
-               .patch = patch_vt2002P},
-       { .id = 0x11064760, .name = "VT1705CF",
-               .patch = patch_vt3476},
-       { .id = 0x11064761, .name = "VT1708SCE",
-               .patch = patch_vt3476},
-       { .id = 0x11064762, .name = "VT1808",
-               .patch = patch_vt3476},
+static const struct hda_device_id snd_hda_id_via[] = {
+       HDA_CODEC_ENTRY(0x11061708, "VT1708", patch_vt1708),
+       HDA_CODEC_ENTRY(0x11061709, "VT1708", patch_vt1708),
+       HDA_CODEC_ENTRY(0x1106170a, "VT1708", patch_vt1708),
+       HDA_CODEC_ENTRY(0x1106170b, "VT1708", patch_vt1708),
+       HDA_CODEC_ENTRY(0x1106e710, "VT1709 10-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e711, "VT1709 10-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e712, "VT1709 10-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e713, "VT1709 10-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e714, "VT1709 6-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e715, "VT1709 6-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e716, "VT1709 6-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e717, "VT1709 6-Ch", patch_vt1709),
+       HDA_CODEC_ENTRY(0x1106e720, "VT1708B 8-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e721, "VT1708B 8-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e722, "VT1708B 8-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e723, "VT1708B 8-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e724, "VT1708B 4-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e725, "VT1708B 4-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e726, "VT1708B 4-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x1106e727, "VT1708B 4-Ch", patch_vt1708B),
+       HDA_CODEC_ENTRY(0x11060397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11061397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11062397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11063397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11064397, "VT1705", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11065397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11066397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11067397, "VT1708S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11060398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11061398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11062398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11063398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11064398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11065398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11066398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11067398, "VT1702", patch_vt1702),
+       HDA_CODEC_ENTRY(0x11060428, "VT1718S", patch_vt1718S),
+       HDA_CODEC_ENTRY(0x11064428, "VT1718S", patch_vt1718S),
+       HDA_CODEC_ENTRY(0x11060441, "VT2020", patch_vt1718S),
+       HDA_CODEC_ENTRY(0x11064441, "VT1828S", patch_vt1718S),
+       HDA_CODEC_ENTRY(0x11060433, "VT1716S", patch_vt1716S),
+       HDA_CODEC_ENTRY(0x1106a721, "VT1716S", patch_vt1716S),
+       HDA_CODEC_ENTRY(0x11060438, "VT2002P", patch_vt2002P),
+       HDA_CODEC_ENTRY(0x11064438, "VT2002P", patch_vt2002P),
+       HDA_CODEC_ENTRY(0x11060448, "VT1812", patch_vt1812),
+       HDA_CODEC_ENTRY(0x11060440, "VT1818S", patch_vt1708S),
+       HDA_CODEC_ENTRY(0x11060446, "VT1802", patch_vt2002P),
+       HDA_CODEC_ENTRY(0x11068446, "VT1802", patch_vt2002P),
+       HDA_CODEC_ENTRY(0x11064760, "VT1705CF", patch_vt3476),
+       HDA_CODEC_ENTRY(0x11064761, "VT1708SCE", patch_vt3476),
+       HDA_CODEC_ENTRY(0x11064762, "VT1808", patch_vt3476),
        {} /* terminator */
 };
-
-MODULE_ALIAS("snd-hda-codec-id:1106*");
+MODULE_DEVICE_TABLE(hdaudio, snd_hda_id_via);
 
 static struct hda_codec_driver via_driver = {
-       .preset = snd_hda_preset_via,
+       .id = snd_hda_id_via,
 };
 
 MODULE_LICENSE("GPL");
index 7acbc21d642a53fe8658c52bdcf91992f08816ac..9e1ad119a3ce0430579d648ae34e6eedc3b7af10 100644 (file)
@@ -1394,7 +1394,9 @@ static int snd_korg1212_playback_open(struct snd_pcm_substream *substream)
 
         spin_unlock_irqrestore(&korg1212->lock, flags);
 
-        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE, kPlayBufferFrames, kPlayBufferFrames);
+       snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                    kPlayBufferFrames);
+
         return 0;
 }
 
@@ -1422,8 +1424,8 @@ static int snd_korg1212_capture_open(struct snd_pcm_substream *substream)
 
         spin_unlock_irqrestore(&korg1212->lock, flags);
 
-        snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
-                                    kPlayBufferFrames, kPlayBufferFrames);
+       snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
+                                    kPlayBufferFrames);
         return 0;
 }
 
index cba89beb2b38fac6e376a2231c1e4f68caa644e5..8b8e2e54fba3f1520d3ed6c6f22185274ca6ae5b 100644 (file)
@@ -234,8 +234,8 @@ static int lx_pcm_open(struct snd_pcm_substream *substream)
 
        /* the clock rate cannot be changed */
        board_rate = chip->board_sample_rate;
-       err = snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_RATE,
-                                          board_rate, board_rate);
+       err = snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_RATE,
+                                          board_rate);
 
        if (err < 0) {
                dev_warn(chip->card->dev, "could not constrain periods\n");
index 72e89cedc52de3c1f115454e827e9d4e2c88895c..17ae92613de4bc7502f33ff64d4dc076177ef992 100644 (file)
@@ -1929,15 +1929,32 @@ snd_m3_ac97_write(struct snd_ac97 *ac97, unsigned short reg, unsigned short val)
                return;
        snd_m3_outw(chip, val, CODEC_DATA);
        snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
+       /*
+        * Workaround for buggy ES1988 integrated AC'97 codec. It remains silent
+        * until the MASTER volume or mute is touched (alsactl restore does not
+        * work).
+        */
+       if (ac97->id == 0x45838308 && reg == AC97_MASTER) {
+               snd_m3_ac97_wait(chip);
+               snd_m3_outw(chip, val, CODEC_DATA);
+               snd_m3_outb(chip, reg & 0x7f, CODEC_COMMAND);
+       }
 }
 
 
-static void snd_m3_remote_codec_config(int io, int isremote)
+static void snd_m3_remote_codec_config(struct snd_m3 *chip, int isremote)
 {
+       int io = chip->iobase;
+       u16 tmp;
+
        isremote = isremote ? 1 : 0;
 
-       outw((inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK) | isremote,
-            io + RING_BUS_CTRL_B);
+       tmp = inw(io + RING_BUS_CTRL_B) & ~SECOND_CODEC_ID_MASK;
+       /* enable dock on Dell Latitude C810 */
+       if (chip->pci->subsystem_vendor == 0x1028 &&
+           chip->pci->subsystem_device == 0x00e5)
+               tmp |= M3I_DOCK_ENABLE;
+       outw(tmp | isremote, io + RING_BUS_CTRL_B);
        outw((inw(io + SDO_OUT_DEST_CTRL) & ~COMMAND_ADDR_OUT) | isremote,
             io + SDO_OUT_DEST_CTRL);
        outw((inw(io + SDO_IN_DEST_CTRL) & ~STATUS_ADDR_IN) | isremote,
@@ -1989,7 +2006,7 @@ static void snd_m3_ac97_reset(struct snd_m3 *chip)
                if (!chip->irda_workaround)
                        dir |= 0x10; /* assuming pci bus master? */
 
-               snd_m3_remote_codec_config(io, 0);
+               snd_m3_remote_codec_config(chip, 0);
 
                outw(IO_SRAM_ENABLE, io + RING_BUS_CTRL_A);
                udelay(20);
index 23d7f5d30c4106e107436999673358e0fb65ff8b..cd94ac548ba383a99ee10886c94343877f760211 100644 (file)
@@ -831,9 +831,9 @@ static struct snd_pcm_hw_constraint_list hw_constraints_period_bytes = {
 static void snd_rme32_set_buffer_constraint(struct rme32 *rme32, struct snd_pcm_runtime *runtime)
 {
        if (! rme32->fullduplex_mode) {
-               snd_pcm_hw_constraint_minmax(runtime,
+               snd_pcm_hw_constraint_single(runtime,
                                             SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
-                                            RME32_BUFFER_SIZE, RME32_BUFFER_SIZE);
+                                            RME32_BUFFER_SIZE);
                snd_pcm_hw_constraint_list(runtime, 0,
                                           SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
                                           &hw_constraints_period_bytes);
index 2306ccf7281e29c9e428b4349c34ade9d12e2296..714df906249eab42c54816fed4630cc4890d7603 100644 (file)
@@ -1152,13 +1152,13 @@ rme96_set_buffer_size_constraint(struct rme96 *rme96,
 {
        unsigned int size;
 
-       snd_pcm_hw_constraint_minmax(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
-                                    RME96_BUFFER_SIZE, RME96_BUFFER_SIZE);
+       snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_BUFFER_BYTES,
+                                    RME96_BUFFER_SIZE);
        if ((size = rme96->playback_periodsize) != 0 ||
            (size = rme96->capture_periodsize) != 0)
-               snd_pcm_hw_constraint_minmax(runtime,
+               snd_pcm_hw_constraint_single(runtime,
                                             SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
-                                            size, size);
+                                            size);
        else
                snd_pcm_hw_constraint_list(runtime, 0,
                                           SNDRV_PCM_HW_PARAM_PERIOD_BYTES,
index 9bba275b4c9b08ba3dd4ff763afd6b17669af48d..2875b4f6d8c9e6a792638547d4b6850fa620a857 100644 (file)
@@ -5112,6 +5112,7 @@ static int hdsp_request_fw_loader(struct hdsp *hdsp)
                dev_err(hdsp->card->dev,
                        "too short firmware size %d (expected %d)\n",
                           (int)fw->size, HDSP_FIRMWARE_SIZE);
+               release_firmware(fw);
                return -EINVAL;
        }
 
index cb666c73712d15276b411cdce11c7994380d4ffb..8bc8016c173d6a6005e33222df1381272bb7819e 100644 (file)
@@ -6080,18 +6080,17 @@ static int snd_hdspm_open(struct snd_pcm_substream *substream)
                                             SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
                                             32, 4096);
                /* RayDAT & AIO have a fixed buffer of 16384 samples per channel */
-               snd_pcm_hw_constraint_minmax(runtime,
+               snd_pcm_hw_constraint_single(runtime,
                                             SNDRV_PCM_HW_PARAM_BUFFER_SIZE,
-                                            16384, 16384);
+                                            16384);
                break;
 
        default:
                snd_pcm_hw_constraint_minmax(runtime,
                                             SNDRV_PCM_HW_PARAM_PERIOD_SIZE,
                                             64, 8192);
-               snd_pcm_hw_constraint_minmax(runtime,
-                                            SNDRV_PCM_HW_PARAM_PERIODS,
-                                            2, 2);
+               snd_pcm_hw_constraint_single(runtime,
+                                            SNDRV_PCM_HW_PARAM_PERIODS, 2);
                break;
        }
 
index 225bfda414e98d91da49140451d8bb09e16c9f12..7ff7d88e46ddd60796ada2b64c67bd6e3816ce6f 100644 (file)
@@ -9,7 +9,6 @@ menuconfig SND_SOC
        select SND_JACK if INPUT=y || INPUT=SND
        select REGMAP_I2C if I2C
        select REGMAP_SPI if SPI_MASTER
-       select SND_COMPRESS_OFFLOAD
        ---help---
 
          If you want ASoC support, you should say Y here and also to the
@@ -30,6 +29,10 @@ config SND_SOC_GENERIC_DMAENGINE_PCM
        bool
        select SND_DMAENGINE_PCM
 
+config SND_SOC_COMPRESS
+       bool
+       select SND_COMPRESS_OFFLOAD
+
 config SND_SOC_TOPOLOGY
        bool
 
@@ -58,6 +61,7 @@ source "sound/soc/sh/Kconfig"
 source "sound/soc/sirf/Kconfig"
 source "sound/soc/spear/Kconfig"
 source "sound/soc/sti/Kconfig"
+source "sound/soc/sunxi/Kconfig"
 source "sound/soc/tegra/Kconfig"
 source "sound/soc/txx9/Kconfig"
 source "sound/soc/ux500/Kconfig"
index 134aca150a50bb62ef6d9d13ddc5636b7adde463..8eb06db32fa0dcd53bf2ed7ee3bd94fb92df7adb 100644 (file)
@@ -1,5 +1,6 @@
 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 soc-ops.o
+snd-soc-core-objs += soc-pcm.o soc-io.o soc-devres.o soc-ops.o
+snd-soc-core-$(CONFIG_SND_SOC_COMPRESS) += soc-compress.o
 
 ifneq ($(CONFIG_SND_SOC_TOPOLOGY),)
 snd-soc-core-objs += soc-topology.o
@@ -40,6 +41,7 @@ obj-$(CONFIG_SND_SOC) += sh/
 obj-$(CONFIG_SND_SOC)  += sirf/
 obj-$(CONFIG_SND_SOC)  += spear/
 obj-$(CONFIG_SND_SOC)  += sti/
+obj-$(CONFIG_SND_SOC)  += sunxi/
 obj-$(CONFIG_SND_SOC)  += tegra/
 obj-$(CONFIG_SND_SOC)  += txx9/
 obj-$(CONFIG_SND_SOC)  += ux500/
index 1489cd461aeca365cf746a09bf506668c75c1ec5..2d30464b81cef315b2faf29f6dc21352b38c60d2 100644 (file)
@@ -59,4 +59,13 @@ 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_ATMEL_SOC_CLASSD
+       tristate "Atmel ASoC driver for boards using CLASSD"
+       depends on ARCH_AT91 || COMPILE_TEST
+       select SND_ATMEL_SOC_DMA
+       select REGMAP_MMIO
+       help
+         Say Y if you want to add support for Atmel ASoC driver for boards using
+         CLASSD.
 endif
index b327e5cc8de352b84a8bffc5260e4d6e6ea5f2df..f6f7db4282164a7b15ffcc52535ca64c9296e045 100644 (file)
@@ -11,7 +11,9 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
 snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
 snd-atmel-soc-wm8904-objs := atmel_wm8904.o
 snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
+snd-atmel-soc-classd-objs := atmel-classd.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_ATMEL_SOC_CLASSD) += snd-atmel-soc-classd.o
diff --git a/sound/soc/atmel/atmel-classd.c b/sound/soc/atmel/atmel-classd.c
new file mode 100644 (file)
index 0000000..8276675
--- /dev/null
@@ -0,0 +1,679 @@
+/* Atmel ALSA SoC Audio Class D Amplifier (CLASSD) driver
+ *
+ * Copyright (C) 2015 Atmel
+ *
+ * Author: Songjun Wu <songjun.wu@atmel.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 or later
+ * as published by the Free Software Foundation.
+ */
+
+#include <linux/of.h>
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/regmap.h>
+#include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+#include "atmel-classd.h"
+
+struct atmel_classd_pdata {
+       bool non_overlap_enable;
+       int non_overlap_time;
+       int pwm_type;
+       const char *card_name;
+};
+
+struct atmel_classd {
+       dma_addr_t phy_base;
+       struct regmap *regmap;
+       struct clk *pclk;
+       struct clk *gclk;
+       struct clk *aclk;
+       int irq;
+       const struct atmel_classd_pdata *pdata;
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_classd_of_match[] = {
+       {
+               .compatible = "atmel,sama5d2-classd",
+       }, {
+               /* sentinel */
+       }
+};
+MODULE_DEVICE_TABLE(of, atmel_classd_of_match);
+
+static struct atmel_classd_pdata *atmel_classd_dt_init(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct atmel_classd_pdata *pdata;
+       const char *pwm_type;
+       int ret;
+
+       if (!np) {
+               dev_err(dev, "device node not found\n");
+               return ERR_PTR(-EINVAL);
+       }
+
+       pdata = devm_kzalloc(dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return ERR_PTR(-ENOMEM);
+
+       ret = of_property_read_string(np, "atmel,pwm-type", &pwm_type);
+       if ((ret == 0) && (strcmp(pwm_type, "diff") == 0))
+               pdata->pwm_type = CLASSD_MR_PWMTYP_DIFF;
+       else
+               pdata->pwm_type = CLASSD_MR_PWMTYP_SINGLE;
+
+       ret = of_property_read_u32(np,
+                       "atmel,non-overlap-time", &pdata->non_overlap_time);
+       if (ret)
+               pdata->non_overlap_enable = false;
+       else
+               pdata->non_overlap_enable = true;
+
+       ret = of_property_read_string(np, "atmel,model", &pdata->card_name);
+       if (ret)
+               pdata->card_name = "CLASSD";
+
+       return pdata;
+}
+#else
+static inline struct atmel_classd_pdata *
+atmel_classd_dt_init(struct device *dev)
+{
+       return ERR_PTR(-EINVAL);
+}
+#endif
+
+#define ATMEL_CLASSD_RATES (SNDRV_PCM_RATE_8000 \
+                       | SNDRV_PCM_RATE_16000  | SNDRV_PCM_RATE_22050 \
+                       | SNDRV_PCM_RATE_32000  | SNDRV_PCM_RATE_44100 \
+                       | SNDRV_PCM_RATE_48000  | SNDRV_PCM_RATE_88200 \
+                       | SNDRV_PCM_RATE_96000)
+
+static const struct snd_pcm_hardware atmel_classd_hw = {
+       .info                   = SNDRV_PCM_INFO_MMAP
+                               | SNDRV_PCM_INFO_MMAP_VALID
+                               | SNDRV_PCM_INFO_INTERLEAVED
+                               | SNDRV_PCM_INFO_RESUME
+                               | SNDRV_PCM_INFO_PAUSE,
+       .formats                = (SNDRV_PCM_FMTBIT_S16_LE),
+       .rates                  = ATMEL_CLASSD_RATES,
+       .rate_min               = 8000,
+       .rate_max               = 96000,
+       .channels_min           = 2,
+       .channels_max           = 2,
+       .buffer_bytes_max       = 64 * 1024,
+       .period_bytes_min       = 256,
+       .period_bytes_max       = 32 * 1024,
+       .periods_min            = 2,
+       .periods_max            = 256,
+};
+
+#define ATMEL_CLASSD_PREALLOC_BUF_SIZE  (64 * 1024)
+
+/* cpu dai component */
+static int atmel_classd_cpu_dai_startup(struct snd_pcm_substream *substream,
+                                       struct snd_soc_dai *cpu_dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+       regmap_write(dd->regmap, CLASSD_THR, 0x0);
+
+       return clk_prepare_enable(dd->pclk);
+}
+
+static void atmel_classd_cpu_dai_shutdown(struct snd_pcm_substream *substream,
+                                       struct snd_soc_dai *cpu_dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+       clk_disable_unprepare(dd->pclk);
+}
+
+static const struct snd_soc_dai_ops atmel_classd_cpu_dai_ops = {
+       .startup        = atmel_classd_cpu_dai_startup,
+       .shutdown       = atmel_classd_cpu_dai_shutdown,
+};
+
+static struct snd_soc_dai_driver atmel_classd_cpu_dai = {
+       .playback = {
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = ATMEL_CLASSD_RATES,
+               .formats        = SNDRV_PCM_FMTBIT_S16_LE,},
+       .ops = &atmel_classd_cpu_dai_ops,
+};
+
+static const struct snd_soc_component_driver atmel_classd_cpu_dai_component = {
+       .name = "atmel-classd",
+};
+
+/* platform */
+static int
+atmel_classd_platform_configure_dma(struct snd_pcm_substream *substream,
+       struct snd_pcm_hw_params *params,
+       struct dma_slave_config *slave_config)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+       if (params_physical_width(params) != 16) {
+               dev_err(rtd->platform->dev,
+                       "only supports 16-bit audio data\n");
+               return -EINVAL;
+       }
+
+       slave_config->direction         = DMA_MEM_TO_DEV;
+       slave_config->dst_addr          = dd->phy_base + CLASSD_THR;
+       slave_config->dst_addr_width    = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       slave_config->dst_maxburst      = 1;
+       slave_config->src_maxburst      = 1;
+       slave_config->device_fc         = false;
+
+       return 0;
+}
+
+static const struct snd_dmaengine_pcm_config
+atmel_classd_dmaengine_pcm_config = {
+       .prepare_slave_config   = atmel_classd_platform_configure_dma,
+       .pcm_hardware           = &atmel_classd_hw,
+       .prealloc_buffer_size   = ATMEL_CLASSD_PREALLOC_BUF_SIZE,
+};
+
+/* codec */
+static const char * const mono_mode_text[] = {
+       "mix", "sat", "left", "right"
+};
+
+static SOC_ENUM_SINGLE_DECL(classd_mono_mode_enum,
+                       CLASSD_INTPMR, CLASSD_INTPMR_MONO_MODE_SHIFT,
+                       mono_mode_text);
+
+static const char * const eqcfg_text[] = {
+       "Treble-12dB", "Treble-6dB",
+       "Medium-8dB", "Medium-3dB",
+       "Bass-12dB", "Bass-6dB",
+       "0 dB",
+       "Bass+6dB", "Bass+12dB",
+       "Medium+3dB", "Medium+8dB",
+       "Treble+6dB", "Treble+12dB",
+};
+
+static const unsigned int eqcfg_value[] = {
+       CLASSD_INTPMR_EQCFG_T_CUT_12, CLASSD_INTPMR_EQCFG_T_CUT_6,
+       CLASSD_INTPMR_EQCFG_M_CUT_8, CLASSD_INTPMR_EQCFG_M_CUT_3,
+       CLASSD_INTPMR_EQCFG_B_CUT_12, CLASSD_INTPMR_EQCFG_B_CUT_6,
+       CLASSD_INTPMR_EQCFG_FLAT,
+       CLASSD_INTPMR_EQCFG_B_BOOST_6, CLASSD_INTPMR_EQCFG_B_BOOST_12,
+       CLASSD_INTPMR_EQCFG_M_BOOST_3, CLASSD_INTPMR_EQCFG_M_BOOST_8,
+       CLASSD_INTPMR_EQCFG_T_BOOST_6, CLASSD_INTPMR_EQCFG_T_BOOST_12,
+};
+
+static SOC_VALUE_ENUM_SINGLE_DECL(classd_eqcfg_enum,
+               CLASSD_INTPMR, CLASSD_INTPMR_EQCFG_SHIFT, 0xf,
+               eqcfg_text, eqcfg_value);
+
+static const DECLARE_TLV_DB_SCALE(classd_digital_tlv, -7800, 100, 1);
+
+static const struct snd_kcontrol_new atmel_classd_snd_controls[] = {
+SOC_DOUBLE_TLV("Playback Volume", CLASSD_INTPMR,
+               CLASSD_INTPMR_ATTL_SHIFT, CLASSD_INTPMR_ATTR_SHIFT,
+               78, 1, classd_digital_tlv),
+
+SOC_SINGLE("Deemphasis Switch", CLASSD_INTPMR,
+               CLASSD_INTPMR_DEEMP_SHIFT, 1, 0),
+
+SOC_SINGLE("Mono Switch", CLASSD_INTPMR, CLASSD_INTPMR_MONO_SHIFT, 1, 0),
+
+SOC_SINGLE("Swap Switch", CLASSD_INTPMR, CLASSD_INTPMR_SWAP_SHIFT, 1, 0),
+
+SOC_ENUM("Mono Mode", classd_mono_mode_enum),
+
+SOC_ENUM("EQ", classd_eqcfg_enum),
+};
+
+static const char * const pwm_type[] = {
+       "Single ended", "Differential"
+};
+
+static int atmel_classd_codec_probe(struct snd_soc_codec *codec)
+{
+       struct snd_soc_card *card = snd_soc_codec_get_drvdata(codec);
+       struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
+       const struct atmel_classd_pdata *pdata = dd->pdata;
+       u32 mask, val;
+
+       mask = CLASSD_MR_PWMTYP_MASK;
+       val = pdata->pwm_type << CLASSD_MR_PWMTYP_SHIFT;
+
+       mask |= CLASSD_MR_NON_OVERLAP_MASK;
+       if (pdata->non_overlap_enable) {
+               val |= (CLASSD_MR_NON_OVERLAP_EN
+                       << CLASSD_MR_NON_OVERLAP_SHIFT);
+
+               mask |= CLASSD_MR_NOVR_VAL_MASK;
+               switch (pdata->non_overlap_time) {
+               case 5:
+                       val |= (CLASSD_MR_NOVR_VAL_5NS
+                               << CLASSD_MR_NOVR_VAL_SHIFT);
+                       break;
+               case 10:
+                       val |= (CLASSD_MR_NOVR_VAL_10NS
+                               << CLASSD_MR_NOVR_VAL_SHIFT);
+                       break;
+               case 15:
+                       val |= (CLASSD_MR_NOVR_VAL_15NS
+                               << CLASSD_MR_NOVR_VAL_SHIFT);
+                       break;
+               case 20:
+                       val |= (CLASSD_MR_NOVR_VAL_20NS
+                               << CLASSD_MR_NOVR_VAL_SHIFT);
+                       break;
+               default:
+                       val |= (CLASSD_MR_NOVR_VAL_10NS
+                               << CLASSD_MR_NOVR_VAL_SHIFT);
+                       dev_warn(codec->dev,
+                               "non-overlapping value %d is invalid, the default value 10 is specified\n",
+                               pdata->non_overlap_time);
+                       break;
+               }
+       }
+
+       snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+
+       dev_info(codec->dev,
+               "PWM modulation type is %s, non-overlapping is %s\n",
+               pwm_type[pdata->pwm_type],
+               pdata->non_overlap_enable?"enabled":"disabled");
+
+       return 0;
+}
+
+static struct regmap *atmel_classd_codec_get_remap(struct device *dev)
+{
+       return dev_get_regmap(dev, NULL);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_classd = {
+       .probe          = atmel_classd_codec_probe,
+       .controls       = atmel_classd_snd_controls,
+       .num_controls   = ARRAY_SIZE(atmel_classd_snd_controls),
+       .get_regmap     = atmel_classd_codec_get_remap,
+};
+
+/* codec dai component */
+static int atmel_classd_codec_dai_startup(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *codec_dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+       int ret;
+
+       ret = clk_prepare_enable(dd->aclk);
+       if (ret)
+               return ret;
+
+       return clk_prepare_enable(dd->gclk);
+}
+
+static int atmel_classd_codec_dai_digital_mute(struct snd_soc_dai *codec_dai,
+       int mute)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u32 mask, val;
+
+       mask = CLASSD_MR_LMUTE_MASK | CLASSD_MR_RMUTE_MASK;
+
+       if (mute)
+               val = mask;
+       else
+               val = 0;
+
+       snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+
+       return 0;
+}
+
+#define CLASSD_ACLK_RATE_11M2896_MPY_8 (112896 * 100 * 8)
+#define CLASSD_ACLK_RATE_12M288_MPY_8  (12228 * 1000 * 8)
+
+static struct {
+       int rate;
+       int sample_rate;
+       int dsp_clk;
+       unsigned long aclk_rate;
+} const sample_rates[] = {
+       { 8000,  CLASSD_INTPMR_FRAME_8K,
+       CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+       { 16000, CLASSD_INTPMR_FRAME_16K,
+       CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+       { 32000, CLASSD_INTPMR_FRAME_32K,
+       CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+       { 48000, CLASSD_INTPMR_FRAME_48K,
+       CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+       { 96000, CLASSD_INTPMR_FRAME_96K,
+       CLASSD_INTPMR_DSP_CLK_FREQ_12M288, CLASSD_ACLK_RATE_12M288_MPY_8 },
+       { 22050, CLASSD_INTPMR_FRAME_22K,
+       CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+       { 44100, CLASSD_INTPMR_FRAME_44K,
+       CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+       { 88200, CLASSD_INTPMR_FRAME_88K,
+       CLASSD_INTPMR_DSP_CLK_FREQ_11M2896, CLASSD_ACLK_RATE_11M2896_MPY_8 },
+};
+
+static int
+atmel_classd_codec_dai_hw_params(struct snd_pcm_substream *substream,
+                           struct snd_pcm_hw_params *params,
+                           struct snd_soc_dai *codec_dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+       struct snd_soc_codec *codec = codec_dai->codec;
+       int fs;
+       int i, best, best_val, cur_val, ret;
+       u32 mask, val;
+
+       fs = params_rate(params);
+
+       best = 0;
+       best_val = abs(fs - sample_rates[0].rate);
+       for (i = 1; i < ARRAY_SIZE(sample_rates); i++) {
+               /* Closest match */
+               cur_val = abs(fs - sample_rates[i].rate);
+               if (cur_val < best_val) {
+                       best = i;
+                       best_val = cur_val;
+               }
+       }
+
+       dev_dbg(codec->dev,
+               "Selected SAMPLE_RATE of %dHz, ACLK_RATE of %ldHz\n",
+               sample_rates[best].rate, sample_rates[best].aclk_rate);
+
+       clk_disable_unprepare(dd->gclk);
+       clk_disable_unprepare(dd->aclk);
+
+       ret = clk_set_rate(dd->aclk, sample_rates[best].aclk_rate);
+       if (ret)
+               return ret;
+
+       mask = CLASSD_INTPMR_DSP_CLK_FREQ_MASK | CLASSD_INTPMR_FRAME_MASK;
+       val = (sample_rates[best].dsp_clk << CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT)
+       | (sample_rates[best].sample_rate << CLASSD_INTPMR_FRAME_SHIFT);
+
+       snd_soc_update_bits(codec, CLASSD_INTPMR, mask, val);
+
+       ret = clk_prepare_enable(dd->aclk);
+       if (ret)
+               return ret;
+
+       return clk_prepare_enable(dd->gclk);
+}
+
+static void
+atmel_classd_codec_dai_shutdown(struct snd_pcm_substream *substream,
+                           struct snd_soc_dai *codec_dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct atmel_classd *dd = snd_soc_card_get_drvdata(rtd->card);
+
+       clk_disable_unprepare(dd->gclk);
+       clk_disable_unprepare(dd->aclk);
+}
+
+static int atmel_classd_codec_dai_prepare(struct snd_pcm_substream *substream,
+                                       struct snd_soc_dai *codec_dai)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+
+       snd_soc_update_bits(codec, CLASSD_MR,
+                               CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK,
+                               (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
+                               |(CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT));
+
+       return 0;
+}
+
+static int atmel_classd_codec_dai_trigger(struct snd_pcm_substream *substream,
+                                       int cmd, struct snd_soc_dai *codec_dai)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       u32 mask, val;
+
+       mask = CLASSD_MR_LEN_MASK | CLASSD_MR_REN_MASK;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               val = mask;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               val = (CLASSD_MR_LEN_DIS << CLASSD_MR_LEN_SHIFT)
+                       | (CLASSD_MR_REN_DIS << CLASSD_MR_REN_SHIFT);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, CLASSD_MR, mask, val);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops atmel_classd_codec_dai_ops = {
+       .digital_mute   = atmel_classd_codec_dai_digital_mute,
+       .startup        = atmel_classd_codec_dai_startup,
+       .shutdown       = atmel_classd_codec_dai_shutdown,
+       .hw_params      = atmel_classd_codec_dai_hw_params,
+       .prepare        = atmel_classd_codec_dai_prepare,
+       .trigger        = atmel_classd_codec_dai_trigger,
+};
+
+#define ATMEL_CLASSD_CODEC_DAI_NAME  "atmel-classd-hifi"
+
+static struct snd_soc_dai_driver atmel_classd_codec_dai = {
+       .name = ATMEL_CLASSD_CODEC_DAI_NAME,
+       .playback = {
+               .stream_name    = "Playback",
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = ATMEL_CLASSD_RATES,
+               .formats        = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .ops = &atmel_classd_codec_dai_ops,
+};
+
+/* ASoC sound card */
+static int atmel_classd_asoc_card_init(struct device *dev,
+                                       struct snd_soc_card *card)
+{
+       struct snd_soc_dai_link *dai_link;
+       struct atmel_classd *dd = snd_soc_card_get_drvdata(card);
+
+       dai_link = devm_kzalloc(dev, sizeof(*dai_link), GFP_KERNEL);
+       if (!dai_link)
+               return -ENOMEM;
+
+       dai_link->name                  = "CLASSD";
+       dai_link->stream_name           = "CLASSD PCM";
+       dai_link->codec_dai_name        = ATMEL_CLASSD_CODEC_DAI_NAME;
+       dai_link->cpu_dai_name          = dev_name(dev);
+       dai_link->codec_name            = dev_name(dev);
+       dai_link->platform_name         = dev_name(dev);
+
+       card->dai_link  = dai_link;
+       card->num_links = 1;
+       card->name      = dd->pdata->card_name;
+       card->dev       = dev;
+
+       return 0;
+};
+
+/* regmap configuration */
+static const struct reg_default atmel_classd_reg_defaults[] = {
+       { CLASSD_INTPMR,   0x00301212 },
+};
+
+#define ATMEL_CLASSD_REG_MAX    0xE4
+static const struct regmap_config atmel_classd_regmap_config = {
+       .reg_bits       = 32,
+       .reg_stride     = 4,
+       .val_bits       = 32,
+       .max_register   = ATMEL_CLASSD_REG_MAX,
+
+       .cache_type             = REGCACHE_FLAT,
+       .reg_defaults           = atmel_classd_reg_defaults,
+       .num_reg_defaults       = ARRAY_SIZE(atmel_classd_reg_defaults),
+};
+
+static int atmel_classd_probe(struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct atmel_classd *dd;
+       struct resource *res;
+       void __iomem *io_base;
+       const struct atmel_classd_pdata *pdata;
+       struct snd_soc_card *card;
+       int ret;
+
+       pdata = dev_get_platdata(dev);
+       if (!pdata) {
+               pdata = atmel_classd_dt_init(dev);
+               if (IS_ERR(pdata))
+                       return PTR_ERR(pdata);
+       }
+
+       dd = devm_kzalloc(dev, sizeof(*dd), GFP_KERNEL);
+       if (!dd)
+               return -ENOMEM;
+
+       dd->pdata = pdata;
+
+       dd->irq = platform_get_irq(pdev, 0);
+       if (dd->irq < 0) {
+               ret = dd->irq;
+               dev_err(dev, "failed to could not get irq: %d\n", ret);
+               return ret;
+       }
+
+       dd->pclk = devm_clk_get(dev, "pclk");
+       if (IS_ERR(dd->pclk)) {
+               ret = PTR_ERR(dd->pclk);
+               dev_err(dev, "failed to get peripheral clock: %d\n", ret);
+               return ret;
+       }
+
+       dd->gclk = devm_clk_get(dev, "gclk");
+       if (IS_ERR(dd->gclk)) {
+               ret = PTR_ERR(dd->gclk);
+               dev_err(dev, "failed to get GCK clock: %d\n", ret);
+               return ret;
+       }
+
+       dd->aclk = devm_clk_get(dev, "aclk");
+       if (IS_ERR(dd->aclk)) {
+               ret = PTR_ERR(dd->aclk);
+               dev_err(dev, "failed to get audio clock: %d\n", ret);
+               return ret;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (!res) {
+               dev_err(dev, "no memory resource\n");
+               return -ENXIO;
+       }
+
+       io_base = devm_ioremap_resource(dev, res);
+       if (IS_ERR(io_base)) {
+               ret =  PTR_ERR(io_base);
+               dev_err(dev, "failed to remap register memory: %d\n", ret);
+               return ret;
+       }
+
+       dd->phy_base = res->start;
+
+       dd->regmap = devm_regmap_init_mmio(dev, io_base,
+                                       &atmel_classd_regmap_config);
+       if (IS_ERR(dd->regmap)) {
+               ret = PTR_ERR(dd->regmap);
+               dev_err(dev, "failed to init register map: %d\n", ret);
+               return ret;
+       }
+
+       ret = devm_snd_soc_register_component(dev,
+                                       &atmel_classd_cpu_dai_component,
+                                       &atmel_classd_cpu_dai, 1);
+       if (ret) {
+               dev_err(dev, "could not register CPU DAI: %d\n", ret);
+               return ret;
+       }
+
+       ret = devm_snd_dmaengine_pcm_register(dev,
+                                       &atmel_classd_dmaengine_pcm_config,
+                                       0);
+       if (ret) {
+               dev_err(dev, "could not register platform: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_register_codec(dev, &soc_codec_dev_classd,
+                                       &atmel_classd_codec_dai, 1);
+       if (ret) {
+               dev_err(dev, "could not register codec: %d\n", ret);
+               return ret;
+       }
+
+       /* register sound card */
+       card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+       if (!card)
+               return -ENOMEM;
+
+       snd_soc_card_set_drvdata(card, dd);
+       platform_set_drvdata(pdev, card);
+
+       ret = atmel_classd_asoc_card_init(dev, card);
+       if (ret) {
+               dev_err(dev, "failed to init sound card\n");
+               return ret;
+       }
+
+       ret = devm_snd_soc_register_card(dev, card);
+       if (ret) {
+               dev_err(dev, "failed to register sound card: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int atmel_classd_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+static struct platform_driver atmel_classd_driver = {
+       .driver = {
+               .name           = "atmel-classd",
+               .of_match_table = of_match_ptr(atmel_classd_of_match),
+               .pm             = &snd_soc_pm_ops,
+       },
+       .probe  = atmel_classd_probe,
+       .remove = atmel_classd_remove,
+};
+module_platform_driver(atmel_classd_driver);
+
+MODULE_DESCRIPTION("Atmel ClassD driver under ALSA SoC architecture");
+MODULE_AUTHOR("Songjun Wu <songjun.wu@atmel.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/atmel-classd.h b/sound/soc/atmel/atmel-classd.h
new file mode 100644 (file)
index 0000000..73f8fdd
--- /dev/null
@@ -0,0 +1,120 @@
+#ifndef __ATMEL_CLASSD_H_
+#define __ATMEL_CLASSD_H_
+
+#define CLASSD_CR              0x00000000
+#define CLASSD_CR_RESET                0x1
+
+#define CLASSD_MR                      0x00000004
+
+#define CLASSD_MR_LEN_DIS              0x0
+#define CLASSD_MR_LEN_EN               0x1
+#define CLASSD_MR_LEN_MASK             (0x1 << 0)
+#define CLASSD_MR_LEN_SHIFT            (0)
+
+#define CLASSD_MR_LMUTE_DIS            0x0
+#define CLASSD_MR_LMUTE_EN             0x1
+#define CLASSD_MR_LMUTE_SHIFT          (0x1)
+#define CLASSD_MR_LMUTE_MASK           (0x1 << 1)
+
+#define CLASSD_MR_REN_DIS              0x0
+#define CLASSD_MR_REN_EN               0x1
+#define CLASSD_MR_REN_MASK             (0x1 << 4)
+#define CLASSD_MR_REN_SHIFT            (4)
+
+#define CLASSD_MR_RMUTE_DIS            0x0
+#define CLASSD_MR_RMUTE_EN             0x1
+#define CLASSD_MR_RMUTE_SHIFT          (0x5)
+#define CLASSD_MR_RMUTE_MASK           (0x1 << 5)
+
+#define CLASSD_MR_PWMTYP_SINGLE                0x0
+#define CLASSD_MR_PWMTYP_DIFF          0x1
+#define CLASSD_MR_PWMTYP_MASK          (0x1 << 8)
+#define CLASSD_MR_PWMTYP_SHIFT         (8)
+
+#define CLASSD_MR_NON_OVERLAP_DIS      0x0
+#define CLASSD_MR_NON_OVERLAP_EN       0x1
+#define CLASSD_MR_NON_OVERLAP_MASK     (0x1 << 16)
+#define CLASSD_MR_NON_OVERLAP_SHIFT    (16)
+
+#define CLASSD_MR_NOVR_VAL_5NS         0x0
+#define CLASSD_MR_NOVR_VAL_10NS                0x1
+#define CLASSD_MR_NOVR_VAL_15NS                0x2
+#define CLASSD_MR_NOVR_VAL_20NS                0x3
+#define CLASSD_MR_NOVR_VAL_MASK                (0x3 << 20)
+#define CLASSD_MR_NOVR_VAL_SHIFT       (20)
+
+#define CLASSD_INTPMR                          0x00000008
+
+#define CLASSD_INTPMR_ATTL_MASK                        (0x3f << 0)
+#define CLASSD_INTPMR_ATTL_SHIFT               (0)
+#define CLASSD_INTPMR_ATTR_MASK                        (0x3f << 8)
+#define CLASSD_INTPMR_ATTR_SHIFT               (8)
+
+#define CLASSD_INTPMR_DSP_CLK_FREQ_12M288      0x0
+#define CLASSD_INTPMR_DSP_CLK_FREQ_11M2896     0x1
+#define CLASSD_INTPMR_DSP_CLK_FREQ_MASK                (0x1 << 16)
+#define CLASSD_INTPMR_DSP_CLK_FREQ_SHIFT       (16)
+
+#define CLASSD_INTPMR_DEEMP_DIS                        0x0
+#define CLASSD_INTPMR_DEEMP_EN                 0x1
+#define CLASSD_INTPMR_DEEMP_MASK               (0x1 << 18)
+#define CLASSD_INTPMR_DEEMP_SHIFT              (18)
+
+#define CLASSD_INTPMR_SWAP_LEFT_ON_LSB         0x0
+#define CLASSD_INTPMR_SWAP_RIGHT_ON_LSB                0x1
+#define CLASSD_INTPMR_SWAP_MASK                        (0x1 << 19)
+#define CLASSD_INTPMR_SWAP_SHIFT               (19)
+
+#define CLASSD_INTPMR_FRAME_8K                 0x0
+#define CLASSD_INTPMR_FRAME_16K                        0x1
+#define CLASSD_INTPMR_FRAME_32K                        0x2
+#define CLASSD_INTPMR_FRAME_48K                        0x3
+#define CLASSD_INTPMR_FRAME_96K                        0x4
+#define CLASSD_INTPMR_FRAME_22K                        0x5
+#define CLASSD_INTPMR_FRAME_44K                        0x6
+#define CLASSD_INTPMR_FRAME_88K                        0x7
+#define CLASSD_INTPMR_FRAME_MASK               (0x7 << 20)
+#define CLASSD_INTPMR_FRAME_SHIFT              (20)
+
+#define CLASSD_INTPMR_EQCFG_FLAT               0x0
+#define CLASSD_INTPMR_EQCFG_B_BOOST_12         0x1
+#define CLASSD_INTPMR_EQCFG_B_BOOST_6          0x2
+#define CLASSD_INTPMR_EQCFG_B_CUT_12           0x3
+#define CLASSD_INTPMR_EQCFG_B_CUT_6            0x4
+#define CLASSD_INTPMR_EQCFG_M_BOOST_3          0x5
+#define CLASSD_INTPMR_EQCFG_M_BOOST_8          0x6
+#define CLASSD_INTPMR_EQCFG_M_CUT_3            0x7
+#define CLASSD_INTPMR_EQCFG_M_CUT_8            0x8
+#define CLASSD_INTPMR_EQCFG_T_BOOST_12         0x9
+#define CLASSD_INTPMR_EQCFG_T_BOOST_6          0xa
+#define CLASSD_INTPMR_EQCFG_T_CUT_12           0xb
+#define CLASSD_INTPMR_EQCFG_T_CUT_6            0xc
+#define CLASSD_INTPMR_EQCFG_SHIFT              (24)
+
+#define CLASSD_INTPMR_MONO_DIS                 0x0
+#define CLASSD_INTPMR_MONO_EN                  0x1
+#define CLASSD_INTPMR_MONO_MASK                        (0x1 << 28)
+#define CLASSD_INTPMR_MONO_SHIFT               (28)
+
+#define CLASSD_INTPMR_MONO_MODE_MIX            0x0
+#define CLASSD_INTPMR_MONO_MODE_SAT            0x1
+#define CLASSD_INTPMR_MONO_MODE_LEFT           0x2
+#define CLASSD_INTPMR_MONO_MODE_RIGHT          0x3
+#define CLASSD_INTPMR_MONO_MODE_MASK           (0x3 << 29)
+#define CLASSD_INTPMR_MONO_MODE_SHIFT          (29)
+
+#define CLASSD_INTSR   0x0000000c
+
+#define CLASSD_THR     0x00000010
+
+#define CLASSD_IER     0x00000014
+
+#define CLASSD_IDR     0x00000018
+
+#define CLASSD_IMR     0x0000001c
+
+#define CLASSD_ISR     0x00000020
+
+#define CLASSD_WPMR    0x000000e4
+
+#endif
index aa354e1c6ff7ce3df7ffd898682c3040817a6a8c..1933bcd46cca043d68db0ecc771124da37416908 100644 (file)
@@ -176,6 +176,7 @@ static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = {
        { .compatible = "atmel,asoc-wm8904", },
        { }
 };
+MODULE_DEVICE_TABLE(of, atmel_asoc_wm8904_dt_ids);
 #endif
 
 static struct platform_driver atmel_asoc_wm8904_driver = {
index 452f404abfd283abe56caaae06de856a47d2b1d9..e97c32798e98a066dba2eaf47883584f7eb04dad 100644 (file)
@@ -38,14 +38,7 @@ static int db1000_audio_probe(struct platform_device *pdev)
 {
        struct snd_soc_card *card = &db1000_ac97;
        card->dev = &pdev->dev;
-       return snd_soc_register_card(card);
-}
-
-static int db1000_audio_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-       snd_soc_unregister_card(card);
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, card);
 }
 
 static struct platform_driver db1000_audio_driver = {
@@ -54,7 +47,6 @@ static struct platform_driver db1000_audio_driver = {
                .pm     = &snd_soc_pm_ops,
        },
        .probe          = db1000_audio_probe,
-       .remove         = db1000_audio_remove,
 };
 
 module_platform_driver(db1000_audio_driver);
index 8c907ebea18960ec8e48942f5746fd2121705736..5c73061d912ad55a2cf51e75f9779bad900f9f33 100644 (file)
@@ -178,14 +178,7 @@ static int db1200_audio_probe(struct platform_device *pdev)
 
        card = db1200_cards[pid->driver_data];
        card->dev = &pdev->dev;
-       return snd_soc_register_card(card);
-}
-
-static int db1200_audio_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-       snd_soc_unregister_card(card);
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, card);
 }
 
 static struct platform_driver db1200_audio_driver = {
@@ -195,7 +188,6 @@ static struct platform_driver db1200_audio_driver = {
        },
        .id_table       = db1200_pids,
        .probe          = db1200_audio_probe,
-       .remove         = db1200_audio_remove,
 };
 
 module_platform_driver(db1200_audio_driver);
index 5bf1501e5e3c4833cbebf2f5afe4b8fde1cc216f..864df2616e109ae4e2bb70793e6231abe58fd1bc 100644 (file)
@@ -87,27 +87,18 @@ static int bf5xx_ad1836_driver_probe(struct platform_device *pdev)
        card->dev = &pdev->dev;
        platform_set_drvdata(pdev, card);
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "Failed to register card\n");
        return ret;
 }
 
-static int bf5xx_ad1836_driver_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
 static struct platform_driver bf5xx_ad1836_driver = {
        .driver = {
                .name = "bfin-snd-ad1836",
                .pm = &snd_soc_pm_ops,
        },
        .probe = bf5xx_ad1836_driver_probe,
-       .remove = bf5xx_ad1836_driver_remove,
 };
 module_platform_driver(bf5xx_ad1836_driver);
 
index 523baf5820d737113e3ba17536a788026cf62137..72ac789884262bbdc41e63adfcabe1dee3911814 100644 (file)
@@ -154,16 +154,7 @@ static int bfin_eval_adau1373_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       return snd_soc_register_card(&bfin_eval_adau1373);
-}
-
-static int bfin_eval_adau1373_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1373);
 }
 
 static struct platform_driver bfin_eval_adau1373_driver = {
@@ -172,7 +163,6 @@ static struct platform_driver bfin_eval_adau1373_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = bfin_eval_adau1373_probe,
-       .remove = bfin_eval_adau1373_remove,
 };
 
 module_platform_driver(bfin_eval_adau1373_driver);
index f9e926dfd4ef735af9e84f9e35363a1670e61421..5c67f72cf9a95e521eb070e5e9b1c9a0835873c8 100644 (file)
@@ -94,16 +94,7 @@ static int bfin_eval_adau1701_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       return snd_soc_register_card(&bfin_eval_adau1701);
-}
-
-static int bfin_eval_adau1701_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adau1701);
 }
 
 static struct platform_driver bfin_eval_adau1701_driver = {
@@ -112,7 +103,6 @@ static struct platform_driver bfin_eval_adau1701_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = bfin_eval_adau1701_probe,
-       .remove = bfin_eval_adau1701_remove,
 };
 
 module_platform_driver(bfin_eval_adau1701_driver);
index 27eee66afdb26536aeb41d50e1b29e542bf6b0c5..1037477d10b2f962cf6d7ea13844d01539bdeafb 100644 (file)
@@ -119,16 +119,7 @@ static int bfin_eval_adav80x_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       return snd_soc_register_card(&bfin_eval_adav80x);
-}
-
-static int bfin_eval_adav80x_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, &bfin_eval_adav80x);
 }
 
 static const struct platform_device_id bfin_eval_adav80x_ids[] = {
@@ -144,7 +135,6 @@ static struct platform_driver bfin_eval_adav80x_driver = {
                .pm = &snd_soc_pm_ops,
        },
        .probe = bfin_eval_adav80x_probe,
-       .remove = bfin_eval_adav80x_remove,
        .id_table = bfin_eval_adav80x_ids,
 };
 
index 0c9733ecd17f29a040ec3f2a4c7dfab8c59007e7..cfdafc4c11ea9a64c465eef6d9ac4ed3520c159a 100644 (file)
@@ -36,6 +36,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_AK4104 if SPI_MASTER
        select SND_SOC_AK4535 if I2C
        select SND_SOC_AK4554
+       select SND_SOC_AK4613 if I2C
        select SND_SOC_AK4641 if I2C
        select SND_SOC_AK4642 if I2C
        select SND_SOC_AK4671 if I2C
@@ -57,6 +58,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_CX20442 if TTY
        select SND_SOC_DA7210 if SND_SOC_I2C_AND_SPI
        select SND_SOC_DA7213 if I2C
+       select SND_SOC_DA7219 if I2C
        select SND_SOC_DA732X if I2C
        select SND_SOC_DA9055 if I2C
        select SND_SOC_DMIC
@@ -79,7 +81,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_MAX9877 if I2C
        select SND_SOC_MC13783 if MFD_MC13XXX
        select SND_SOC_ML26124 if I2C
-       select SND_SOC_HDMI_CODEC
+       select SND_SOC_NAU8825 if I2C
        select SND_SOC_PCM1681 if I2C
        select SND_SOC_PCM1792A if SPI_MASTER
        select SND_SOC_PCM3008
@@ -171,6 +173,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_WM8995 if SND_SOC_I2C_AND_SPI
        select SND_SOC_WM8996 if I2C
        select SND_SOC_WM8997 if MFD_WM8997
+       select SND_SOC_WM8998 if MFD_WM8998
        select SND_SOC_WM9081 if I2C
        select SND_SOC_WM9090 if I2C
        select SND_SOC_WM9705 if SND_SOC_AC97_BUS
@@ -195,9 +198,11 @@ config SND_SOC_ARIZONA
        default y if SND_SOC_WM5102=y
        default y if SND_SOC_WM5110=y
        default y if SND_SOC_WM8997=y
+       default y if SND_SOC_WM8998=y
        default m if SND_SOC_WM5102=m
        default m if SND_SOC_WM5110=m
        default m if SND_SOC_WM8997=m
+       default m if SND_SOC_WM8998=m
 
 config SND_SOC_WM_HUBS
        tristate
@@ -319,6 +324,10 @@ config SND_SOC_AK4535
 config SND_SOC_AK4554
        tristate "AKM AK4554 CODEC"
 
+config SND_SOC_AK4613
+       tristate "AKM AK4613 CODEC"
+       depends on I2C
+
 config SND_SOC_AK4641
        tristate
 
@@ -430,6 +439,9 @@ config SND_SOC_DA7210
 config SND_SOC_DA7213
         tristate
 
+config SND_SOC_DA7219
+        tristate
+
 config SND_SOC_DA732X
         tristate
 
@@ -442,9 +454,6 @@ config SND_SOC_BT_SCO
 config SND_SOC_DMIC
        tristate
 
-config SND_SOC_HDMI_CODEC
-       tristate "HDMI stub CODEC"
-
 config SND_SOC_ES8328
        tristate "Everest Semi ES8328 CODEC"
 
@@ -865,6 +874,9 @@ config SND_SOC_WM8996
 config SND_SOC_WM8997
        tristate
 
+config SND_SOC_WM8998
+       tristate
+
 config SND_SOC_WM9081
        tristate
 
@@ -896,6 +908,9 @@ config SND_SOC_MC13783
 config SND_SOC_ML26124
        tristate
 
+config SND_SOC_NAU8825
+       tristate
+
 config SND_SOC_TPA6130A2
        tristate "Texas Instruments TPA6130A2 headphone amplifier"
        depends on I2C
index 4a32077954aee6aee2966ced2573991d1673b02a..f632fc42f59f08d9d6c72f6aac045d9fe28176a1 100644 (file)
@@ -26,6 +26,7 @@ snd-soc-ads117x-objs := ads117x.o
 snd-soc-ak4104-objs := ak4104.o
 snd-soc-ak4535-objs := ak4535.o
 snd-soc-ak4554-objs := ak4554.o
+snd-soc-ak4613-objs := ak4613.o
 snd-soc-ak4641-objs := ak4641.o
 snd-soc-ak4642-objs := ak4642.o
 snd-soc-ak4671-objs := ak4671.o
@@ -49,6 +50,7 @@ snd-soc-cs4349-objs := cs4349.o
 snd-soc-cx20442-objs := cx20442.o
 snd-soc-da7210-objs := da7210.o
 snd-soc-da7213-objs := da7213.o
+snd-soc-da7219-objs := da7219.o da7219-aad.o
 snd-soc-da732x-objs := da732x.o
 snd-soc-da9055-objs := da9055.o
 snd-soc-bt-sco-objs := bt-sco.o
@@ -72,7 +74,7 @@ snd-soc-max98925-objs := max98925.o
 snd-soc-max9850-objs := max9850.o
 snd-soc-mc13783-objs := mc13783.o
 snd-soc-ml26124-objs := ml26124.o
-snd-soc-hdmi-codec-objs := hdmi.o
+snd-soc-nau8825-objs := nau8825.o
 snd-soc-pcm1681-objs := pcm1681.o
 snd-soc-pcm1792a-codec-objs := pcm1792a.o
 snd-soc-pcm3008-objs := pcm3008.o
@@ -176,6 +178,7 @@ snd-soc-wm8993-objs := wm8993.o
 snd-soc-wm8994-objs := wm8994.o wm8958-dsp2.o
 snd-soc-wm8995-objs := wm8995.o
 snd-soc-wm8997-objs := wm8997.o
+snd-soc-wm8998-objs := wm8998.o
 snd-soc-wm9081-objs := wm9081.o
 snd-soc-wm9090-objs := wm9090.o
 snd-soc-wm9705-objs := wm9705.o
@@ -216,6 +219,7 @@ obj-$(CONFIG_SND_SOC_ADS117X)       += snd-soc-ads117x.o
 obj-$(CONFIG_SND_SOC_AK4104)   += snd-soc-ak4104.o
 obj-$(CONFIG_SND_SOC_AK4535)   += snd-soc-ak4535.o
 obj-$(CONFIG_SND_SOC_AK4554)   += snd-soc-ak4554.o
+obj-$(CONFIG_SND_SOC_AK4613)   += snd-soc-ak4613.o
 obj-$(CONFIG_SND_SOC_AK4641)   += snd-soc-ak4641.o
 obj-$(CONFIG_SND_SOC_AK4642)   += snd-soc-ak4642.o
 obj-$(CONFIG_SND_SOC_AK4671)   += snd-soc-ak4671.o
@@ -241,6 +245,7 @@ obj-$(CONFIG_SND_SOC_CS4349)        += snd-soc-cs4349.o
 obj-$(CONFIG_SND_SOC_CX20442)  += snd-soc-cx20442.o
 obj-$(CONFIG_SND_SOC_DA7210)   += snd-soc-da7210.o
 obj-$(CONFIG_SND_SOC_DA7213)   += snd-soc-da7213.o
+obj-$(CONFIG_SND_SOC_DA7219)   += snd-soc-da7219.o
 obj-$(CONFIG_SND_SOC_DA732X)   += snd-soc-da732x.o
 obj-$(CONFIG_SND_SOC_DA9055)   += snd-soc-da9055.o
 obj-$(CONFIG_SND_SOC_BT_SCO)   += snd-soc-bt-sco.o
@@ -264,7 +269,7 @@ obj-$(CONFIG_SND_SOC_MAX98925)      += snd-soc-max98925.o
 obj-$(CONFIG_SND_SOC_MAX9850)  += snd-soc-max9850.o
 obj-$(CONFIG_SND_SOC_MC13783)  += snd-soc-mc13783.o
 obj-$(CONFIG_SND_SOC_ML26124)  += snd-soc-ml26124.o
-obj-$(CONFIG_SND_SOC_HDMI_CODEC) += snd-soc-hdmi-codec.o
+obj-$(CONFIG_SND_SOC_NAU8825)   += snd-soc-nau8825.o
 obj-$(CONFIG_SND_SOC_PCM1681)  += snd-soc-pcm1681.o
 obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
@@ -364,6 +369,7 @@ obj-$(CONFIG_SND_SOC_WM8993)        += snd-soc-wm8993.o
 obj-$(CONFIG_SND_SOC_WM8994)   += snd-soc-wm8994.o
 obj-$(CONFIG_SND_SOC_WM8995)   += snd-soc-wm8995.o
 obj-$(CONFIG_SND_SOC_WM8997)   += snd-soc-wm8997.o
+obj-$(CONFIG_SND_SOC_WM8998)   += snd-soc-wm8998.o
 obj-$(CONFIG_SND_SOC_WM9081)   += snd-soc-wm9081.o
 obj-$(CONFIG_SND_SOC_WM9090)   += snd-soc-wm9090.o
 obj-$(CONFIG_SND_SOC_WM9705)   += snd-soc-wm9705.o
index df3a1a415825bf6c0a599344611a769066e850bc..171313664bc8dac6472dcf423063882fb4ffd8f2 100644 (file)
@@ -15,8 +15,8 @@
 #include "ad193x.h"
 
 static const struct i2c_device_id ad193x_id[] = {
-       { "ad1936", 0 },
-       { "ad1937", 0 },
+       { "ad1936", AD193X },
+       { "ad1937", AD193X },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, ad193x_id);
@@ -30,7 +30,9 @@ static int ad193x_i2c_probe(struct i2c_client *client,
        config.val_bits = 8;
        config.reg_bits = 8;
 
-       return ad193x_probe(&client->dev, devm_regmap_init_i2c(client, &config));
+       return ad193x_probe(&client->dev,
+                           devm_regmap_init_i2c(client, &config),
+                           (enum ad193x_type)id->driver_data);
 }
 
 static int ad193x_i2c_remove(struct i2c_client *client)
index 390cef9b9dc20d3427453e16208a6ffb43e22a70..431f95da1de1f9b0eff5bab82ac2daa07da200dd 100644 (file)
@@ -16,6 +16,7 @@
 
 static int ad193x_spi_probe(struct spi_device *spi)
 {
+       const struct spi_device_id *id = spi_get_device_id(spi);
        struct regmap_config config;
 
        config = ad193x_regmap_config;
@@ -24,7 +25,8 @@ static int ad193x_spi_probe(struct spi_device *spi)
        config.read_flag_mask = 0x09;
        config.write_flag_mask = 0x08;
 
-       return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config));
+       return ad193x_probe(&spi->dev, devm_regmap_init_spi(spi, &config),
+                           (enum ad193x_type)id->driver_data);
 }
 
 static int ad193x_spi_remove(struct spi_device *spi)
@@ -33,6 +35,17 @@ static int ad193x_spi_remove(struct spi_device *spi)
        return 0;
 }
 
+static const struct spi_device_id ad193x_spi_id[] = {
+       { "ad193x", AD193X },
+       { "ad1933", AD1933 },
+       { "ad1934", AD1934 },
+       { "ad1938", AD193X },
+       { "ad1939", AD193X },
+       { "adau1328", AD193X },
+       { }
+};
+MODULE_DEVICE_TABLE(spi, ad193x_spi_id);
+
 static struct spi_driver ad193x_spi_driver = {
        .driver = {
                .name   = "ad193x",
@@ -40,6 +53,7 @@ static struct spi_driver ad193x_spi_driver = {
        },
        .probe          = ad193x_spi_probe,
        .remove         = ad193x_spi_remove,
+       .id_table       = ad193x_spi_id,
 };
 module_spi_driver(ad193x_spi_driver);
 
index 17c9535956609c0df1a04f631da8f79a25a1e044..3a3f3f2343d75e7ee8b9b29abd3ee6887c1437f4 100644 (file)
@@ -23,6 +23,7 @@
 /* codec private data */
 struct ad193x_priv {
        struct regmap *regmap;
+       enum ad193x_type type;
        int sysclk;
 };
 
@@ -47,12 +48,6 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
        SOC_DOUBLE_R_TLV("DAC4 Volume", AD193X_DAC_L4_VOL,
                        AD193X_DAC_R4_VOL, 0, 0xFF, 1, adau193x_tlv),
 
-       /* ADC switch control */
-       SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE,
-               AD193X_ADCR1_MUTE, 1, 1),
-       SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE,
-               AD193X_ADCR2_MUTE, 1, 1),
-
        /* DAC switch control */
        SOC_DOUBLE("DAC1 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL1_MUTE,
                AD193X_DACR1_MUTE, 1, 1),
@@ -63,26 +58,37 @@ static const struct snd_kcontrol_new ad193x_snd_controls[] = {
        SOC_DOUBLE("DAC4 Switch", AD193X_DAC_CHNL_MUTE, AD193X_DACL4_MUTE,
                AD193X_DACR4_MUTE, 1, 1),
 
+       /* DAC de-emphasis */
+       SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
+};
+
+static const struct snd_kcontrol_new ad193x_adc_snd_controls[] = {
+       /* ADC switch control */
+       SOC_DOUBLE("ADC1 Switch", AD193X_ADC_CTRL0, AD193X_ADCL1_MUTE,
+               AD193X_ADCR1_MUTE, 1, 1),
+       SOC_DOUBLE("ADC2 Switch", AD193X_ADC_CTRL0, AD193X_ADCL2_MUTE,
+               AD193X_ADCR2_MUTE, 1, 1),
+
        /* ADC high-pass filter */
        SOC_SINGLE("ADC High Pass Filter Switch", AD193X_ADC_CTRL0,
                        AD193X_ADC_HIGHPASS_FILTER, 1, 0),
-
-       /* DAC de-emphasis */
-       SOC_ENUM("Playback Deemphasis", ad193x_deemp_enum),
 };
 
 static const struct snd_soc_dapm_widget ad193x_dapm_widgets[] = {
        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"),
        SND_SOC_DAPM_OUTPUT("DAC4OUT"),
+};
+
+static const struct snd_soc_dapm_widget ad193x_adc_widgets[] = {
+       SND_SOC_DAPM_ADC("ADC", "Capture", SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_SUPPLY("ADC_PWR", AD193X_ADC_CTRL0, 0, 1, NULL, 0),
        SND_SOC_DAPM_INPUT("ADC1IN"),
        SND_SOC_DAPM_INPUT("ADC2IN"),
 };
@@ -91,18 +97,33 @@ 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 Output" },
        { "DAC2OUT", NULL, "DAC Output" },
        { "DAC3OUT", NULL, "DAC Output" },
        { "DAC4OUT", NULL, "DAC Output" },
+       { "SYSCLK", NULL, "PLL_PWR" },
+};
+
+static const struct snd_soc_dapm_route ad193x_adc_audio_paths[] = {
+       { "ADC", NULL, "SYSCLK" },
+       { "ADC", NULL, "ADC_PWR" },
        { "ADC", NULL, "ADC1IN" },
        { "ADC", NULL, "ADC2IN" },
-       { "SYSCLK", NULL, "PLL_PWR" },
 };
 
+static inline bool ad193x_has_adc(const struct ad193x_priv *ad193x)
+{
+       switch (ad193x->type) {
+       case AD1933:
+       case AD1934:
+               return false;
+       default:
+               break;
+       }
+
+       return true;
+}
+
 /*
  * DAI ops entries
  */
@@ -147,8 +168,10 @@ static int ad193x_set_tdm_slot(struct snd_soc_dai *dai, unsigned int tx_mask,
 
        regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
                AD193X_DAC_CHAN_MASK, channels << AD193X_DAC_CHAN_SHFT);
-       regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
-               AD193X_ADC_CHAN_MASK, channels << AD193X_ADC_CHAN_SHFT);
+       if (ad193x_has_adc(ad193x))
+               regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
+                                  AD193X_ADC_CHAN_MASK,
+                                  channels << AD193X_ADC_CHAN_SHFT);
 
        return 0;
 }
@@ -172,7 +195,9 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
                adc_serfmt |= AD193X_ADC_SERFMT_AUX;
                break;
        default:
-               return -EINVAL;
+               if (ad193x_has_adc(ad193x))
+                       return -EINVAL;
+               break;
        }
 
        switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
@@ -217,10 +242,12 @@ static int ad193x_set_dai_fmt(struct snd_soc_dai *codec_dai,
                return -EINVAL;
        }
 
-       regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
-               AD193X_ADC_SERFMT_MASK, adc_serfmt);
-       regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
-               AD193X_ADC_FMT_MASK, adc_fmt);
+       if (ad193x_has_adc(ad193x)) {
+               regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
+                                  AD193X_ADC_SERFMT_MASK, adc_serfmt);
+               regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL2,
+                                  AD193X_ADC_FMT_MASK, adc_fmt);
+       }
        regmap_update_bits(ad193x->regmap, AD193X_DAC_CTRL1,
                AD193X_DAC_FMT_MASK, dac_fmt);
 
@@ -287,8 +314,9 @@ static int ad193x_hw_params(struct snd_pcm_substream *substream,
                            AD193X_DAC_WORD_LEN_MASK,
                            word_len << AD193X_DAC_WORD_LEN_SHFT);
 
-       regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
-                           AD193X_ADC_WORD_LEN_MASK, word_len);
+       if (ad193x_has_adc(ad193x))
+               regmap_update_bits(ad193x->regmap, AD193X_ADC_CTRL1,
+                                  AD193X_ADC_WORD_LEN_MASK, word_len);
 
        return 0;
 }
@@ -326,6 +354,8 @@ static struct snd_soc_dai_driver ad193x_dai = {
 static int ad193x_codec_probe(struct snd_soc_codec *codec)
 {
        struct ad193x_priv *ad193x = snd_soc_codec_get_drvdata(codec);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       int num, ret;
 
        /* default setting for ad193x */
 
@@ -335,14 +365,46 @@ static int ad193x_codec_probe(struct snd_soc_codec *codec)
        regmap_write(ad193x->regmap, AD193X_DAC_CTRL2, 0x1A);
        /* dac in tdm mode */
        regmap_write(ad193x->regmap, AD193X_DAC_CTRL0, 0x40);
-       /* high-pass filter enable */
-       regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3);
-       /* sata delay=1, adc aux mode */
-       regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43);
+
+       /* adc only */
+       if (ad193x_has_adc(ad193x)) {
+               /* high-pass filter enable */
+               regmap_write(ad193x->regmap, AD193X_ADC_CTRL0, 0x3);
+               /* sata delay=1, adc aux mode */
+               regmap_write(ad193x->regmap, AD193X_ADC_CTRL1, 0x43);
+       }
+
        /* pll input: mclki/xi */
        regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL0, 0x99); /* mclk=24.576Mhz: 0x9D; mclk=12.288Mhz: 0x99 */
        regmap_write(ad193x->regmap, AD193X_PLL_CLK_CTRL1, 0x04);
 
+       /* adc only */
+       if (ad193x_has_adc(ad193x)) {
+               /* add adc controls */
+               num = ARRAY_SIZE(ad193x_adc_snd_controls);
+               ret = snd_soc_add_codec_controls(codec,
+                                                ad193x_adc_snd_controls,
+                                                num);
+               if (ret)
+                       return ret;
+
+               /* add adc widgets */
+               num = ARRAY_SIZE(ad193x_adc_widgets);
+               ret = snd_soc_dapm_new_controls(dapm,
+                                               ad193x_adc_widgets,
+                                               num);
+               if (ret)
+                       return ret;
+
+               /* add adc routes */
+               num = ARRAY_SIZE(ad193x_adc_audio_paths);
+               ret = snd_soc_dapm_add_routes(dapm,
+                                             ad193x_adc_audio_paths,
+                                             num);
+               if (ret)
+                       return ret;
+       }
+
        return 0;
 }
 
@@ -356,18 +418,13 @@ static struct snd_soc_codec_driver soc_codec_dev_ad193x = {
        .num_dapm_routes = ARRAY_SIZE(audio_paths),
 };
 
-static bool adau193x_reg_volatile(struct device *dev, unsigned int reg)
-{
-       return false;
-}
-
 const struct regmap_config ad193x_regmap_config = {
        .max_register = AD193X_NUM_REGS - 1,
-       .volatile_reg = adau193x_reg_volatile,
 };
 EXPORT_SYMBOL_GPL(ad193x_regmap_config);
 
-int ad193x_probe(struct device *dev, struct regmap *regmap)
+int ad193x_probe(struct device *dev, struct regmap *regmap,
+                enum ad193x_type type)
 {
        struct ad193x_priv *ad193x;
 
@@ -379,6 +436,7 @@ int ad193x_probe(struct device *dev, struct regmap *regmap)
                return -ENOMEM;
 
        ad193x->regmap = regmap;
+       ad193x->type = type;
 
        dev_set_drvdata(dev, ad193x);
 
index ab9a998f15beba416338b9571e64601608fe5fc7..8b1e65f928d2ad92cabb6f6773af487c1b94a1bc 100644 (file)
 
 struct device;
 
+enum ad193x_type {
+       AD193X,
+       AD1933,
+       AD1934,
+};
+
 extern const struct regmap_config ad193x_regmap_config;
-int ad193x_probe(struct device *dev, struct regmap *regmap);
+int ad193x_probe(struct device *dev, struct regmap *regmap,
+                enum ad193x_type type);
 
 #define AD193X_PLL_CLK_CTRL0    0x00
 #define AD193X_PLL_POWERDOWN           0x01
index 198c924551b78e268b8480a38d481703fe53d3a9..acff8d62059cf49de24585aeac9c2143412c0175 100644 (file)
@@ -728,8 +728,8 @@ static int adav80x_dai_startup(struct snd_pcm_substream *substream,
        if (!snd_soc_codec_is_active(codec) || !adav80x->rate)
                return 0;
 
-       return snd_pcm_hw_constraint_minmax(substream->runtime,
-                       SNDRV_PCM_HW_PARAM_RATE, adav80x->rate, adav80x->rate);
+       return snd_pcm_hw_constraint_single(substream->runtime,
+                       SNDRV_PCM_HW_PARAM_RATE, adav80x->rate);
 }
 
 static void adav80x_dai_shutdown(struct snd_pcm_substream *substream,
diff --git a/sound/soc/codecs/ak4613.c b/sound/soc/codecs/ak4613.c
new file mode 100644 (file)
index 0000000..07a2664
--- /dev/null
@@ -0,0 +1,497 @@
+/*
+ * ak4613.c  --  Asahi Kasei ALSA Soc Audio driver
+ *
+ * Copyright (C) 2015 Renesas Electronics Corporation
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * Based on ak4642.c by Kuninori Morimoto
+ * Based on wm8731.c by Richard Purdie
+ * Based on ak4535.c by Richard Purdie
+ * Based on wm8753.c by Liam Girdwood
+ *
+ * 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/clk.h>
+#include <linux/i2c.h>
+#include <linux/slab.h>
+#include <linux/of_device.h>
+#include <linux/module.h>
+#include <linux/regmap.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+#include <sound/tlv.h>
+
+#define PW_MGMT1       0x00 /* Power Management 1 */
+#define PW_MGMT2       0x01 /* Power Management 2 */
+#define PW_MGMT3       0x02 /* Power Management 3 */
+#define CTRL1          0x03 /* Control 1 */
+#define CTRL2          0x04 /* Control 2 */
+#define DEMP1          0x05 /* De-emphasis1 */
+#define DEMP2          0x06 /* De-emphasis2 */
+#define OFD            0x07 /* Overflow Detect */
+#define ZRD            0x08 /* Zero Detect */
+#define ICTRL          0x09 /* Input Control */
+#define OCTRL          0x0a /* Output Control */
+#define LOUT1          0x0b /* LOUT1 Volume Control */
+#define ROUT1          0x0c /* ROUT1 Volume Control */
+#define LOUT2          0x0d /* LOUT2 Volume Control */
+#define ROUT2          0x0e /* ROUT2 Volume Control */
+#define LOUT3          0x0f /* LOUT3 Volume Control */
+#define ROUT3          0x10 /* ROUT3 Volume Control */
+#define LOUT4          0x11 /* LOUT4 Volume Control */
+#define ROUT4          0x12 /* ROUT4 Volume Control */
+#define LOUT5          0x13 /* LOUT5 Volume Control */
+#define ROUT5          0x14 /* ROUT5 Volume Control */
+#define LOUT6          0x15 /* LOUT6 Volume Control */
+#define ROUT6          0x16 /* ROUT6 Volume Control */
+
+/* PW_MGMT1 */
+#define RSTN           BIT(0)
+#define PMDAC          BIT(1)
+#define PMADC          BIT(2)
+#define PMVR           BIT(3)
+
+/* PW_MGMT2 */
+#define PMAD_ALL       0x7
+
+/* PW_MGMT3 */
+#define PMDA_ALL       0x3f
+
+/* CTRL1 */
+#define DIF0           BIT(3)
+#define DIF1           BIT(4)
+#define DIF2           BIT(5)
+#define TDM0           BIT(6)
+#define TDM1           BIT(7)
+#define NO_FMT         (0xff)
+#define FMT_MASK       (0xf8)
+
+/* CTRL2 */
+#define DFS_NORMAL_SPEED       (0 << 2)
+#define DFS_DOUBLE_SPEED       (1 << 2)
+#define DFS_QUAD_SPEED         (2 << 2)
+
+struct ak4613_priv {
+       struct mutex lock;
+
+       unsigned int fmt;
+       u8 fmt_ctrl;
+       int cnt;
+};
+
+struct ak4613_formats {
+       unsigned int width;
+       unsigned int fmt;
+};
+
+struct ak4613_interface {
+       struct ak4613_formats capture;
+       struct ak4613_formats playback;
+};
+
+/*
+ * Playback Volume
+ *
+ * max : 0x00 : 0 dB
+ *       ( 0.5 dB step )
+ * min : 0xFE : -127.0 dB
+ * mute: 0xFF
+ */
+static const DECLARE_TLV_DB_SCALE(out_tlv, -12750, 50, 1);
+
+static const struct snd_kcontrol_new ak4613_snd_controls[] = {
+       SOC_DOUBLE_R_TLV("Digital Playback Volume1", LOUT1, ROUT1,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume2", LOUT2, ROUT2,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume3", LOUT3, ROUT3,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume4", LOUT4, ROUT4,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume5", LOUT5, ROUT5,
+                        0, 0xFF, 1, out_tlv),
+       SOC_DOUBLE_R_TLV("Digital Playback Volume6", LOUT6, ROUT6,
+                        0, 0xFF, 1, out_tlv),
+};
+
+static const struct reg_default ak4613_reg[] = {
+       { 0x0,  0x0f }, { 0x1,  0x07 }, { 0x2,  0x3f }, { 0x3,  0x20 },
+       { 0x4,  0x20 }, { 0x5,  0x55 }, { 0x6,  0x05 }, { 0x7,  0x07 },
+       { 0x8,  0x0f }, { 0x9,  0x07 }, { 0xa,  0x3f }, { 0xb,  0x00 },
+       { 0xc,  0x00 }, { 0xd,  0x00 }, { 0xe,  0x00 }, { 0xf,  0x00 },
+       { 0x10, 0x00 }, { 0x11, 0x00 }, { 0x12, 0x00 }, { 0x13, 0x00 },
+       { 0x14, 0x00 }, { 0x15, 0x00 }, { 0x16, 0x00 },
+};
+
+#define AUDIO_IFACE_IDX_TO_VAL(i) (i << 3)
+#define AUDIO_IFACE(b, fmt) { b, SND_SOC_DAIFMT_##fmt }
+static const struct ak4613_interface ak4613_iface[] = {
+       /* capture */                           /* playback */
+       [0] = { AUDIO_IFACE(24, LEFT_J),        AUDIO_IFACE(16, RIGHT_J) },
+       [1] = { AUDIO_IFACE(24, LEFT_J),        AUDIO_IFACE(20, RIGHT_J) },
+       [2] = { AUDIO_IFACE(24, LEFT_J),        AUDIO_IFACE(24, RIGHT_J) },
+       [3] = { AUDIO_IFACE(24, LEFT_J),        AUDIO_IFACE(24, LEFT_J) },
+       [4] = { AUDIO_IFACE(24, I2S),           AUDIO_IFACE(24, I2S) },
+};
+
+static const struct regmap_config ak4613_regmap_cfg = {
+       .reg_bits               = 8,
+       .val_bits               = 8,
+       .max_register           = 0x16,
+       .reg_defaults           = ak4613_reg,
+       .num_reg_defaults       = ARRAY_SIZE(ak4613_reg),
+};
+
+static const struct of_device_id ak4613_of_match[] = {
+       { .compatible = "asahi-kasei,ak4613",   .data = &ak4613_regmap_cfg },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ak4613_of_match);
+
+static const struct i2c_device_id ak4613_i2c_id[] = {
+       { "ak4613", (kernel_ulong_t)&ak4613_regmap_cfg },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, ak4613_i2c_id);
+
+static const struct snd_soc_dapm_widget ak4613_dapm_widgets[] = {
+
+       /* Outputs */
+       SND_SOC_DAPM_OUTPUT("LOUT1"),
+       SND_SOC_DAPM_OUTPUT("LOUT2"),
+       SND_SOC_DAPM_OUTPUT("LOUT3"),
+       SND_SOC_DAPM_OUTPUT("LOUT4"),
+       SND_SOC_DAPM_OUTPUT("LOUT5"),
+       SND_SOC_DAPM_OUTPUT("LOUT6"),
+
+       SND_SOC_DAPM_OUTPUT("ROUT1"),
+       SND_SOC_DAPM_OUTPUT("ROUT2"),
+       SND_SOC_DAPM_OUTPUT("ROUT3"),
+       SND_SOC_DAPM_OUTPUT("ROUT4"),
+       SND_SOC_DAPM_OUTPUT("ROUT5"),
+       SND_SOC_DAPM_OUTPUT("ROUT6"),
+
+       /* Inputs */
+       SND_SOC_DAPM_INPUT("LIN1"),
+       SND_SOC_DAPM_INPUT("LIN2"),
+
+       SND_SOC_DAPM_INPUT("RIN1"),
+       SND_SOC_DAPM_INPUT("RIN2"),
+
+       /* DAC */
+       SND_SOC_DAPM_DAC("DAC1", NULL, PW_MGMT3, 0, 0),
+       SND_SOC_DAPM_DAC("DAC2", NULL, PW_MGMT3, 1, 0),
+       SND_SOC_DAPM_DAC("DAC3", NULL, PW_MGMT3, 2, 0),
+       SND_SOC_DAPM_DAC("DAC4", NULL, PW_MGMT3, 3, 0),
+       SND_SOC_DAPM_DAC("DAC5", NULL, PW_MGMT3, 4, 0),
+       SND_SOC_DAPM_DAC("DAC6", NULL, PW_MGMT3, 5, 0),
+
+       /* ADC */
+       SND_SOC_DAPM_ADC("ADC1", NULL, PW_MGMT2, 0, 0),
+       SND_SOC_DAPM_ADC("ADC2", NULL, PW_MGMT2, 1, 0),
+};
+
+static const struct snd_soc_dapm_route ak4613_intercon[] = {
+       {"LOUT1", NULL, "DAC1"},
+       {"LOUT2", NULL, "DAC2"},
+       {"LOUT3", NULL, "DAC3"},
+       {"LOUT4", NULL, "DAC4"},
+       {"LOUT5", NULL, "DAC5"},
+       {"LOUT6", NULL, "DAC6"},
+
+       {"ROUT1", NULL, "DAC1"},
+       {"ROUT2", NULL, "DAC2"},
+       {"ROUT3", NULL, "DAC3"},
+       {"ROUT4", NULL, "DAC4"},
+       {"ROUT5", NULL, "DAC5"},
+       {"ROUT6", NULL, "DAC6"},
+
+       {"DAC1", NULL, "Playback"},
+       {"DAC2", NULL, "Playback"},
+       {"DAC3", NULL, "Playback"},
+       {"DAC4", NULL, "Playback"},
+       {"DAC5", NULL, "Playback"},
+       {"DAC6", NULL, "Playback"},
+
+       {"Capture", NULL, "ADC1"},
+       {"Capture", NULL, "ADC2"},
+
+       {"ADC1", NULL, "LIN1"},
+       {"ADC2", NULL, "LIN2"},
+
+       {"ADC1", NULL, "RIN1"},
+       {"ADC2", NULL, "RIN2"},
+};
+
+static void ak4613_dai_shutdown(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct device *dev = codec->dev;
+
+       mutex_lock(&priv->lock);
+       priv->cnt--;
+       if (priv->cnt < 0) {
+               dev_err(dev, "unexpected counter error\n");
+               priv->cnt = 0;
+       }
+       if (!priv->cnt)
+               priv->fmt_ctrl = NO_FMT;
+       mutex_unlock(&priv->lock);
+}
+
+static int ak4613_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       fmt &= SND_SOC_DAIFMT_FORMAT_MASK;
+
+       switch (fmt) {
+       case SND_SOC_DAIFMT_RIGHT_J:
+       case SND_SOC_DAIFMT_LEFT_J:
+       case SND_SOC_DAIFMT_I2S:
+               priv->fmt = fmt;
+
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int ak4613_dai_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct ak4613_priv *priv = snd_soc_codec_get_drvdata(codec);
+       const struct ak4613_formats *fmts;
+       struct device *dev = codec->dev;
+       unsigned int width = params_width(params);
+       unsigned int fmt = priv->fmt;
+       unsigned int rate;
+       int is_play = substream->stream == SNDRV_PCM_STREAM_PLAYBACK;
+       int i, ret;
+       u8 fmt_ctrl, ctrl2;
+
+       rate = params_rate(params);
+       switch (rate) {
+       case 32000:
+       case 44100:
+       case 48000:
+               ctrl2 = DFS_NORMAL_SPEED;
+               break;
+       case 88200:
+       case 96000:
+               ctrl2 = DFS_DOUBLE_SPEED;
+               break;
+       case 176400:
+       case 192000:
+               ctrl2 = DFS_QUAD_SPEED;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /*
+        * FIXME
+        *
+        * It doesn't support TDM at this point
+        */
+       fmt_ctrl = NO_FMT;
+       for (i = 0; i < ARRAY_SIZE(ak4613_iface); i++) {
+               fmts = (is_play) ?      &ak4613_iface[i].playback :
+                                       &ak4613_iface[i].capture;
+
+               if (fmts->fmt != fmt)
+                       continue;
+
+               if (fmt == SND_SOC_DAIFMT_RIGHT_J) {
+                       if (fmts->width != width)
+                               continue;
+               } else {
+                       if (fmts->width < width)
+                               continue;
+               }
+
+               fmt_ctrl = AUDIO_IFACE_IDX_TO_VAL(i);
+               break;
+       }
+
+       ret = -EINVAL;
+       if (fmt_ctrl == NO_FMT)
+               goto hw_params_end;
+
+       mutex_lock(&priv->lock);
+       if ((priv->fmt_ctrl == NO_FMT) ||
+           (priv->fmt_ctrl == fmt_ctrl)) {
+               priv->fmt_ctrl = fmt_ctrl;
+               priv->cnt++;
+               ret = 0;
+       }
+       mutex_unlock(&priv->lock);
+
+       if (ret < 0)
+               goto hw_params_end;
+
+       snd_soc_update_bits(codec, CTRL1, FMT_MASK, fmt_ctrl);
+       snd_soc_write(codec, CTRL2, ctrl2);
+
+hw_params_end:
+       if (ret < 0)
+               dev_warn(dev, "unsupported data width/format combination\n");
+
+       return ret;
+}
+
+static int ak4613_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       u8 mgmt1 = 0;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               mgmt1 |= RSTN;
+               /* fall through */
+       case SND_SOC_BIAS_PREPARE:
+               mgmt1 |= PMADC | PMDAC;
+               /* fall through */
+       case SND_SOC_BIAS_STANDBY:
+               mgmt1 |= PMVR;
+               /* fall through */
+       case SND_SOC_BIAS_OFF:
+       default:
+               break;
+       }
+
+       snd_soc_write(codec, PW_MGMT1, mgmt1);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops ak4613_dai_ops = {
+       .shutdown       = ak4613_dai_shutdown,
+       .set_fmt        = ak4613_dai_set_fmt,
+       .hw_params      = ak4613_dai_hw_params,
+};
+
+#define AK4613_PCM_RATE                (SNDRV_PCM_RATE_32000  |\
+                                SNDRV_PCM_RATE_44100  |\
+                                SNDRV_PCM_RATE_48000  |\
+                                SNDRV_PCM_RATE_64000  |\
+                                SNDRV_PCM_RATE_88200  |\
+                                SNDRV_PCM_RATE_96000  |\
+                                SNDRV_PCM_RATE_176400 |\
+                                SNDRV_PCM_RATE_192000)
+#define AK4613_PCM_FMTBIT      (SNDRV_PCM_FMTBIT_S16_LE |\
+                                SNDRV_PCM_FMTBIT_S24_LE)
+
+static struct snd_soc_dai_driver ak4613_dai = {
+       .name = "ak4613-hifi",
+       .playback = {
+               .stream_name    = "Playback",
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = AK4613_PCM_RATE,
+               .formats        = AK4613_PCM_FMTBIT,
+       },
+       .capture = {
+               .stream_name    = "Capture",
+               .channels_min   = 2,
+               .channels_max   = 2,
+               .rates          = AK4613_PCM_RATE,
+               .formats        = AK4613_PCM_FMTBIT,
+       },
+       .ops = &ak4613_dai_ops,
+       .symmetric_rates = 1,
+};
+
+static int ak4613_resume(struct snd_soc_codec *codec)
+{
+       struct regmap *regmap = dev_get_regmap(codec->dev, NULL);
+
+       regcache_mark_dirty(regmap);
+       return regcache_sync(regmap);
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4613 = {
+       .resume                 = ak4613_resume,
+       .set_bias_level         = ak4613_set_bias_level,
+       .controls               = ak4613_snd_controls,
+       .num_controls           = ARRAY_SIZE(ak4613_snd_controls),
+       .dapm_widgets           = ak4613_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(ak4613_dapm_widgets),
+       .dapm_routes            = ak4613_intercon,
+       .num_dapm_routes        = ARRAY_SIZE(ak4613_intercon),
+};
+
+static int ak4613_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct device *dev = &i2c->dev;
+       struct device_node *np = dev->of_node;
+       const struct regmap_config *regmap_cfg;
+       struct regmap *regmap;
+       struct ak4613_priv *priv;
+
+       regmap_cfg = NULL;
+       if (np) {
+               const struct of_device_id *of_id;
+
+               of_id = of_match_device(ak4613_of_match, dev);
+               if (of_id)
+                       regmap_cfg = of_id->data;
+       } else {
+               regmap_cfg = (const struct regmap_config *)id->driver_data;
+       }
+
+       if (!regmap_cfg)
+               return -EINVAL;
+
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->fmt_ctrl          = NO_FMT;
+       priv->cnt               = 0;
+
+       mutex_init(&priv->lock);
+
+       i2c_set_clientdata(i2c, priv);
+
+       regmap = devm_regmap_init_i2c(i2c, regmap_cfg);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
+
+       return snd_soc_register_codec(dev, &soc_codec_dev_ak4613,
+                                     &ak4613_dai, 1);
+}
+
+static int ak4613_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static struct i2c_driver ak4613_i2c_driver = {
+       .driver = {
+               .name = "ak4613-codec",
+               .owner = THIS_MODULE,
+               .of_match_table = ak4613_of_match,
+       },
+       .probe          = ak4613_i2c_probe,
+       .remove         = ak4613_i2c_remove,
+       .id_table       = ak4613_i2c_id,
+};
+
+module_i2c_driver(ak4613_i2c_driver);
+
+MODULE_DESCRIPTION("Soc AK4613 driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_LICENSE("GPL v2");
index 4a90143d0e907b5034d0f4caa506c97000500f71..cda27c22812a71442dd3d927088c2118aa16dc17 100644 (file)
@@ -23,6 +23,8 @@
  * AK4648 is tested.
  */
 
+#include <linux/clk.h>
+#include <linux/clk-provider.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/slab.h>
 #define I2S            (3 << 0)
 
 /* MD_CTL2 */
-#define FS0            (1 << 0)
-#define FS1            (1 << 1)
-#define FS2            (1 << 2)
-#define FS3            (1 << 5)
-#define FS_MASK                (FS0 | FS1 | FS2 | FS3)
+#define FSs(val)       (((val & 0x7) << 0) | ((val & 0x8) << 2))
+#define PSs(val)       ((val & 0x3) << 6)
 
 /* MD_CTL3 */
 #define BST1           (1 << 3)
@@ -147,6 +146,7 @@ struct ak4642_drvdata {
 
 struct ak4642_priv {
        const struct ak4642_drvdata *drvdata;
+       struct clk *mcko;
 };
 
 /*
@@ -430,56 +430,56 @@ static int ak4642_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
        return 0;
 }
 
+static int ak4642_set_mcko(struct snd_soc_codec *codec,
+                          u32 frequency)
+{
+       u32 fs_list[] = {
+               [0] = 8000,
+               [1] = 12000,
+               [2] = 16000,
+               [3] = 24000,
+               [4] = 7350,
+               [5] = 11025,
+               [6] = 14700,
+               [7] = 22050,
+               [10] = 32000,
+               [11] = 48000,
+               [14] = 29400,
+               [15] = 44100,
+       };
+       u32 ps_list[] = {
+               [0] = 256,
+               [1] = 128,
+               [2] = 64,
+               [3] = 32
+       };
+       int ps, fs;
+
+       for (ps = 0; ps < ARRAY_SIZE(ps_list); ps++) {
+               for (fs = 0; fs < ARRAY_SIZE(fs_list); fs++) {
+                       if (frequency == ps_list[ps] * fs_list[fs]) {
+                               snd_soc_write(codec, MD_CTL2,
+                                             PSs(ps) | FSs(fs));
+                               return 0;
+                       }
+               }
+       }
+
+       return 0;
+}
+
 static int ak4642_dai_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_soc_dai *dai)
 {
        struct snd_soc_codec *codec = dai->codec;
-       u8 rate;
+       struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+       u32 rate = clk_get_rate(priv->mcko);
 
-       switch (params_rate(params)) {
-       case 7350:
-               rate = FS2;
-               break;
-       case 8000:
-               rate = 0;
-               break;
-       case 11025:
-               rate = FS2 | FS0;
-               break;
-       case 12000:
-               rate = FS0;
-               break;
-       case 14700:
-               rate = FS2 | FS1;
-               break;
-       case 16000:
-               rate = FS1;
-               break;
-       case 22050:
-               rate = FS2 | FS1 | FS0;
-               break;
-       case 24000:
-               rate = FS1 | FS0;
-               break;
-       case 29400:
-               rate = FS3 | FS2 | FS1;
-               break;
-       case 32000:
-               rate = FS3 | FS1;
-               break;
-       case 44100:
-               rate = FS3 | FS2 | FS1 | FS0;
-               break;
-       case 48000:
-               rate = FS3 | FS1 | FS0;
-               break;
-       default:
-               return -EINVAL;
-       }
-       snd_soc_update_bits(codec, MD_CTL2, FS_MASK, rate);
+       if (!rate)
+               rate = params_rate(params) * 256;
 
-       return 0;
+       return ak4642_set_mcko(codec, rate);
 }
 
 static int ak4642_set_bias_level(struct snd_soc_codec *codec,
@@ -532,7 +532,18 @@ static int ak4642_resume(struct snd_soc_codec *codec)
        return 0;
 }
 
+static int ak4642_probe(struct snd_soc_codec *codec)
+{
+       struct ak4642_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       if (priv->mcko)
+               ak4642_set_mcko(codec, clk_get_rate(priv->mcko));
+
+       return 0;
+}
+
 static struct snd_soc_codec_driver soc_codec_dev_ak4642 = {
+       .probe                  = ak4642_probe,
        .resume                 = ak4642_resume,
        .set_bias_level         = ak4642_set_bias_level,
        .controls               = ak4642_snd_controls,
@@ -580,19 +591,54 @@ static const struct ak4642_drvdata ak4648_drvdata = {
        .extended_frequencies = 1,
 };
 
+#ifdef CONFIG_COMMON_CLK
+static struct clk *ak4642_of_parse_mcko(struct device *dev)
+{
+       struct device_node *np = dev->of_node;
+       struct clk *clk;
+       const char *clk_name = np->name;
+       const char *parent_clk_name = NULL;
+       u32 rate;
+
+       if (of_property_read_u32(np, "clock-frequency", &rate))
+               return NULL;
+
+       if (of_property_read_bool(np, "clocks"))
+               parent_clk_name = of_clk_get_parent_name(np, 0);
+
+       of_property_read_string(np, "clock-output-names", &clk_name);
+
+       clk = clk_register_fixed_rate(dev, clk_name, parent_clk_name,
+                                     (parent_clk_name) ? 0 : CLK_IS_ROOT,
+                                     rate);
+       if (!IS_ERR(clk))
+               of_clk_add_provider(np, of_clk_src_simple_get, clk);
+
+       return clk;
+}
+#else
+#define ak4642_of_parse_mcko(d) 0
+#endif
+
 static const struct of_device_id ak4642_of_match[];
 static int ak4642_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
-       struct device_node *np = i2c->dev.of_node;
+       struct device *dev = &i2c->dev;
+       struct device_node *np = dev->of_node;
        const struct ak4642_drvdata *drvdata = NULL;
        struct regmap *regmap;
        struct ak4642_priv *priv;
+       struct clk *mcko = NULL;
 
        if (np) {
                const struct of_device_id *of_id;
 
-               of_id = of_match_device(ak4642_of_match, &i2c->dev);
+               mcko = ak4642_of_parse_mcko(dev);
+               if (IS_ERR(mcko))
+                       mcko = NULL;
+
+               of_id = of_match_device(ak4642_of_match, dev);
                if (of_id)
                        drvdata = of_id->data;
        } else {
@@ -600,15 +646,16 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
        }
 
        if (!drvdata) {
-               dev_err(&i2c->dev, "Unknown device type\n");
+               dev_err(dev, "Unknown device type\n");
                return -EINVAL;
        }
 
-       priv = devm_kzalloc(&i2c->dev, sizeof(*priv), GFP_KERNEL);
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
        if (!priv)
                return -ENOMEM;
 
        priv->drvdata = drvdata;
+       priv->mcko = mcko;
 
        i2c_set_clientdata(i2c, priv);
 
@@ -616,7 +663,7 @@ static int ak4642_i2c_probe(struct i2c_client *i2c,
        if (IS_ERR(regmap))
                return PTR_ERR(regmap);
 
-       return snd_soc_register_codec(&i2c->dev,
+       return snd_soc_register_codec(dev,
                                      &soc_codec_dev_ak4642, &ak4642_dai, 1);
 }
 
index 8a2221ab3d10b97f5f7a97c21251264aa93eaa5b..9929efc6b9aaa4257a155e844b7feb2f91ea80f9 100644 (file)
@@ -147,6 +147,8 @@ static int arizona_spk_ev(struct snd_soc_dapm_widget *w,
                                                   0x4f5, 0x0da);
                }
                break;
+       default:
+               break;
        }
 
        return 0;
@@ -314,6 +316,7 @@ const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
        "Tone Generator 2",
        "Haptics",
        "AEC",
+       "AEC2",
        "Mic Mute Mixer",
        "Noise Generator",
        "IN1L",
@@ -421,6 +424,7 @@ int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS] = {
        0x05,
        0x06,  /* Haptics */
        0x08,  /* AEC */
+       0x09,  /* AEC2 */
        0x0c,  /* Noise mixer */
        0x0d,  /* Comfort noise */
        0x10,  /* IN1L */
@@ -525,6 +529,32 @@ EXPORT_SYMBOL_GPL(arizona_mixer_values);
 const DECLARE_TLV_DB_SCALE(arizona_mixer_tlv, -3200, 100, 0);
 EXPORT_SYMBOL_GPL(arizona_mixer_tlv);
 
+const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = {
+       "12kHz", "24kHz", "48kHz", "96kHz", "192kHz",
+       "11.025kHz", "22.05kHz", "44.1kHz", "88.2kHz", "176.4kHz",
+       "4kHz", "8kHz", "16kHz", "32kHz",
+};
+EXPORT_SYMBOL_GPL(arizona_sample_rate_text);
+
+const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE] = {
+       0x01, 0x02, 0x03, 0x04, 0x05, 0x09, 0x0A, 0x0B, 0x0C, 0x0D,
+       0x10, 0x11, 0x12, 0x13,
+};
+EXPORT_SYMBOL_GPL(arizona_sample_rate_val);
+
+const char *arizona_sample_rate_val_to_name(unsigned int rate_val)
+{
+       int i;
+
+       for (i = 0; i < ARRAY_SIZE(arizona_sample_rate_val); ++i) {
+               if (arizona_sample_rate_val[i] == rate_val)
+                       return arizona_sample_rate_text[i];
+       }
+
+       return "Illegal";
+}
+EXPORT_SYMBOL_GPL(arizona_sample_rate_val_to_name);
+
 const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE] = {
        "SYNCCLK rate", "8kHz", "16kHz", "ASYNCCLK rate",
 };
@@ -689,6 +719,15 @@ static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
                                    ARIZONA_IN_VU, val);
 }
 
+bool arizona_input_analog(struct snd_soc_codec *codec, int shift)
+{
+       unsigned int reg = ARIZONA_IN1L_CONTROL + ((shift / 2) * 8);
+       unsigned int val = snd_soc_read(codec, reg);
+
+       return !(val & ARIZONA_IN1_MODE_MASK);
+}
+EXPORT_SYMBOL_GPL(arizona_input_analog);
+
 int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
                  int event)
 {
@@ -725,6 +764,9 @@ int arizona_in_ev(struct snd_soc_dapm_widget *w, struct snd_kcontrol *kcontrol,
                reg = snd_soc_read(codec, ARIZONA_INPUT_ENABLES);
                if (reg == 0)
                        arizona_in_set_vu(codec, 0);
+               break;
+       default:
+               break;
        }
 
        return 0;
@@ -806,6 +848,8 @@ int arizona_out_ev(struct snd_soc_dapm_widget *w,
                        break;
                }
                break;
+       default:
+               break;
        }
 
        return 0;
@@ -1868,6 +1912,11 @@ static int arizona_calc_fratio(struct arizona_fll *fll,
                if (fll->arizona->rev < 3 || sync)
                        return init_ratio;
                break;
+       case WM8998:
+       case WM1814:
+               if (sync)
+                       return init_ratio;
+               break;
        default:
                return init_ratio;
        }
index ada0a418ff4b648034841e9b36f4df598d03fac2..fea8b8ae8e1a1517929bca406f20066fa25724bb 100644 (file)
@@ -93,12 +93,17 @@ struct arizona_priv {
        bool dvfs_cached;
 };
 
-#define ARIZONA_NUM_MIXER_INPUTS 103
+#define ARIZONA_NUM_MIXER_INPUTS 104
 
 extern const unsigned int arizona_mixer_tlv[];
 extern const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS];
 extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
 
+#define ARIZONA_GAINMUX_CONTROLS(name, base) \
+       SOC_SINGLE_RANGE_TLV(name " Input Volume", base + 1,            \
+                            ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0,    \
+                            arizona_mixer_tlv)
+
 #define ARIZONA_MIXER_CONTROLS(name, base) \
        SOC_SINGLE_RANGE_TLV(name " Input 1 Volume", base + 1,          \
                             ARIZONA_MIXER_VOL_SHIFT, 0x20, 0x50, 0,    \
@@ -209,8 +214,12 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
         .num_regs = 1 }) }
 
 #define ARIZONA_RATE_ENUM_SIZE 4
+#define ARIZONA_SAMPLE_RATE_ENUM_SIZE 14
+
 extern const char *arizona_rate_text[ARIZONA_RATE_ENUM_SIZE];
 extern const int arizona_rate_val[ARIZONA_RATE_ENUM_SIZE];
+extern const char * const arizona_sample_rate_text[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
+extern const unsigned int arizona_sample_rate_val[ARIZONA_SAMPLE_RATE_ENUM_SIZE];
 
 extern const struct soc_enum arizona_isrc_fsl[];
 extern const struct soc_enum arizona_isrc_fsh[];
@@ -294,4 +303,7 @@ extern int arizona_init_dai(struct arizona_priv *priv, int dai);
 int arizona_set_output_mode(struct snd_soc_codec *codec, int output,
                            bool diff);
 
+extern bool arizona_input_analog(struct snd_soc_codec *codec, int shift);
+
+extern const char *arizona_sample_rate_val_to_name(unsigned int rate_val);
 #endif
index a9c86efb31878a343262a589facc4c5424522ae9..7278f93460c1ee1f5eb48fafaf2f95d0aa406c62 100644 (file)
@@ -12,6 +12,7 @@
  * option) any later version.
  */
 
+#include <linux/clk.h>
 #include <linux/delay.h>
 #include <linux/i2c.h>
 #include <linux/regmap.h>
@@ -1222,23 +1223,44 @@ static int da7213_set_dai_sysclk(struct snd_soc_dai *codec_dai,
 {
        struct snd_soc_codec *codec = codec_dai->codec;
        struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       if ((da7213->clk_src == clk_id) && (da7213->mclk_rate == freq))
+               return 0;
+
+       if (((freq < 5000000) && (freq != 32768)) || (freq > 54000000)) {
+               dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+                       freq);
+               return -EINVAL;
+       }
 
        switch (clk_id) {
        case DA7213_CLKSRC_MCLK:
-               if ((freq == 32768) ||
-                   ((freq >= 5000000) && (freq <= 54000000))) {
-                       da7213->mclk_rate = freq;
-                       return 0;
-               } else {
-                       dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
-                               freq);
-                       return -EINVAL;
-               }
+               da7213->mclk_squarer_en = false;
+               break;
+       case DA7213_CLKSRC_MCLK_SQR:
+               da7213->mclk_squarer_en = true;
                break;
        default:
                dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
                return -EINVAL;
        }
+
+       da7213->clk_src = clk_id;
+
+       if (da7213->mclk) {
+               freq = clk_round_rate(da7213->mclk, freq);
+               ret = clk_set_rate(da7213->mclk, freq);
+               if (ret) {
+                       dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
+                               freq);
+                       return ret;
+               }
+       }
+
+       da7213->mclk_rate = freq;
+
+       return 0;
 }
 
 /* Supported PLL input frequencies are 5MHz - 54MHz. */
@@ -1366,12 +1388,25 @@ static struct snd_soc_dai_driver da7213_dai = {
 static int da7213_set_bias_level(struct snd_soc_codec *codec,
                                 enum snd_soc_bias_level level)
 {
+       struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
        switch (level) {
        case SND_SOC_BIAS_ON:
        case SND_SOC_BIAS_PREPARE:
                break;
        case SND_SOC_BIAS_STANDBY:
                if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+                       /* MCLK */
+                       if (da7213->mclk) {
+                               ret = clk_prepare_enable(da7213->mclk);
+                               if (ret) {
+                                       dev_err(codec->dev,
+                                               "Failed to enable mclk\n");
+                                       return ret;
+                               }
+                       }
+
                        /* Enable VMID reference & master bias */
                        snd_soc_update_bits(codec, DA7213_REFERENCES,
                                            DA7213_VMID_EN | DA7213_BIAS_EN,
@@ -1382,15 +1417,127 @@ static int da7213_set_bias_level(struct snd_soc_codec *codec,
                /* Disable VMID reference & master bias */
                snd_soc_update_bits(codec, DA7213_REFERENCES,
                                    DA7213_VMID_EN | DA7213_BIAS_EN, 0);
+
+               /* MCLK */
+               if (da7213->mclk)
+                       clk_disable_unprepare(da7213->mclk);
                break;
        }
        return 0;
 }
 
+/* DT */
+static const struct of_device_id da7213_of_match[] = {
+       { .compatible = "dlg,da7213", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, da7213_of_match);
+
+static enum da7213_micbias_voltage
+       da7213_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+{
+       switch (val) {
+       case 1600:
+               return DA7213_MICBIAS_1_6V;
+       case 2200:
+               return DA7213_MICBIAS_2_2V;
+       case 2500:
+               return DA7213_MICBIAS_2_5V;
+       case 3000:
+               return DA7213_MICBIAS_3_0V;
+       default:
+               dev_warn(codec->dev, "Invalid micbias level\n");
+               return DA7213_MICBIAS_2_2V;
+       }
+}
+
+static enum da7213_dmic_data_sel
+       da7213_of_dmic_data_sel(struct snd_soc_codec *codec, const char *str)
+{
+       if (!strcmp(str, "lrise_rfall")) {
+               return DA7213_DMIC_DATA_LRISE_RFALL;
+       } else if (!strcmp(str, "lfall_rrise")) {
+               return DA7213_DMIC_DATA_LFALL_RRISE;
+       } else {
+               dev_warn(codec->dev, "Invalid DMIC data select type\n");
+               return DA7213_DMIC_DATA_LRISE_RFALL;
+       }
+}
+
+static enum da7213_dmic_samplephase
+       da7213_of_dmic_samplephase(struct snd_soc_codec *codec, const char *str)
+{
+       if (!strcmp(str, "on_clkedge")) {
+               return DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+       } else if (!strcmp(str, "between_clkedge")) {
+               return DA7213_DMIC_SAMPLE_BETWEEN_CLKEDGE;
+       } else {
+               dev_warn(codec->dev, "Invalid DMIC sample phase\n");
+               return DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+       }
+}
+
+static enum da7213_dmic_clk_rate
+       da7213_of_dmic_clkrate(struct snd_soc_codec *codec, u32 val)
+{
+       switch (val) {
+       case 1500000:
+               return DA7213_DMIC_CLK_1_5MHZ;
+       case 3000000:
+               return DA7213_DMIC_CLK_3_0MHZ;
+       default:
+               dev_warn(codec->dev, "Invalid DMIC clock rate\n");
+               return DA7213_DMIC_CLK_1_5MHZ;
+       }
+}
+
+static struct da7213_platform_data
+       *da7213_of_to_pdata(struct snd_soc_codec *codec)
+{
+       struct device_node *np = codec->dev->of_node;
+       struct da7213_platform_data *pdata;
+       const char *of_str;
+       u32 of_val32;
+
+       pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata) {
+               dev_warn(codec->dev, "Failed to allocate memory for pdata\n");
+               return NULL;
+       }
+
+       if (of_property_read_u32(np, "dlg,micbias1-lvl", &of_val32) >= 0)
+               pdata->micbias1_lvl = da7213_of_micbias_lvl(codec, of_val32);
+       else
+               pdata->micbias1_lvl = DA7213_MICBIAS_2_2V;
+
+       if (of_property_read_u32(np, "dlg,micbias2-lvl", &of_val32) >= 0)
+               pdata->micbias2_lvl = da7213_of_micbias_lvl(codec, of_val32);
+       else
+               pdata->micbias2_lvl = DA7213_MICBIAS_2_2V;
+
+       if (!of_property_read_string(np, "dlg,dmic-data-sel", &of_str))
+               pdata->dmic_data_sel = da7213_of_dmic_data_sel(codec, of_str);
+       else
+               pdata->dmic_data_sel = DA7213_DMIC_DATA_LRISE_RFALL;
+
+       if (!of_property_read_string(np, "dlg,dmic-samplephase", &of_str))
+               pdata->dmic_samplephase =
+                       da7213_of_dmic_samplephase(codec, of_str);
+       else
+               pdata->dmic_samplephase = DA7213_DMIC_SAMPLE_ON_CLKEDGE;
+
+       if (of_property_read_u32(np, "dlg,dmic-clkrate", &of_val32) >= 0)
+               pdata->dmic_clk_rate = da7213_of_dmic_clkrate(codec, of_val32);
+       else
+               pdata->dmic_clk_rate = DA7213_DMIC_CLK_3_0MHZ;
+
+       return pdata;
+}
+
+
 static int da7213_probe(struct snd_soc_codec *codec)
 {
        struct da7213_priv *da7213 = snd_soc_codec_get_drvdata(codec);
-       struct da7213_platform_data *pdata = da7213->pdata;
 
        /* Default to using ALC auto offset calibration mode. */
        snd_soc_update_bits(codec, DA7213_ALC_CTRL1,
@@ -1450,8 +1597,15 @@ static int da7213_probe(struct snd_soc_codec *codec)
        snd_soc_update_bits(codec, DA7213_LINE_CTRL,
                            DA7213_LINE_AMP_OE, DA7213_LINE_AMP_OE);
 
+       /* Handle DT/Platform data */
+       if (codec->dev->of_node)
+               da7213->pdata = da7213_of_to_pdata(codec);
+       else
+               da7213->pdata = dev_get_platdata(codec->dev);
+
        /* Set platform data values */
        if (da7213->pdata) {
+               struct da7213_platform_data *pdata = da7213->pdata;
                u8 micbias_lvl = 0, dmic_cfg = 0;
 
                /* Set Mic Bias voltages */
@@ -1503,10 +1657,17 @@ static int da7213_probe(struct snd_soc_codec *codec)
                                    DA7213_DMIC_DATA_SEL_MASK |
                                    DA7213_DMIC_SAMPLEPHASE_MASK |
                                    DA7213_DMIC_CLK_RATE_MASK, dmic_cfg);
+       }
 
-               /* Set MCLK squaring */
-               da7213->mclk_squarer_en = pdata->mclk_squaring;
+       /* Check if MCLK provided */
+       da7213->mclk = devm_clk_get(codec->dev, "mclk");
+       if (IS_ERR(da7213->mclk)) {
+               if (PTR_ERR(da7213->mclk) != -ENOENT)
+                       return PTR_ERR(da7213->mclk);
+               else
+                       da7213->mclk = NULL;
        }
+
        return 0;
 }
 
@@ -1537,7 +1698,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
        struct da7213_priv *da7213;
-       struct da7213_platform_data *pdata = dev_get_platdata(&i2c->dev);
        int ret;
 
        da7213 = devm_kzalloc(&i2c->dev, sizeof(struct da7213_priv),
@@ -1545,9 +1705,6 @@ static int da7213_i2c_probe(struct i2c_client *i2c,
        if (!da7213)
                return -ENOMEM;
 
-       if (pdata)
-               da7213->pdata = pdata;
-
        i2c_set_clientdata(i2c, da7213);
 
        da7213->regmap = devm_regmap_init_i2c(i2c, &da7213_regmap_config);
@@ -1582,6 +1739,7 @@ MODULE_DEVICE_TABLE(i2c, da7213_i2c_id);
 static struct i2c_driver da7213_i2c_driver = {
        .driver = {
                .name = "da7213",
+               .of_match_table = of_match_ptr(da7213_of_match),
        },
        .probe          = da7213_i2c_probe,
        .remove         = da7213_remove,
index 9cb9ddd012826d4391f7f2669a11294163f16925..030fd691b07626ab599fe53bd0b3a1903268b94b 100644 (file)
@@ -13,6 +13,7 @@
 #ifndef _DA7213_H
 #define _DA7213_H
 
+#include <linux/clk.h>
 #include <linux/regmap.h>
 #include <sound/da7213.h>
 
 #define DA7213_PLL_INDIV_20_40_MHZ_VAL 8
 #define DA7213_PLL_INDIV_40_54_MHZ_VAL 16
 
-enum clk_src {
-       DA7213_CLKSRC_MCLK
+enum da7213_clk_src {
+       DA7213_CLKSRC_MCLK = 0,
+       DA7213_CLKSRC_MCLK_SQR,
 };
 
 /* Codec private data */
 struct da7213_priv {
        struct regmap *regmap;
+       struct clk *mclk;
        unsigned int mclk_rate;
+       int clk_src;
        bool master;
        bool mclk_squarer_en;
        bool srm_en;
diff --git a/sound/soc/codecs/da7219-aad.c b/sound/soc/codecs/da7219-aad.c
new file mode 100644 (file)
index 0000000..9459593
--- /dev/null
@@ -0,0 +1,823 @@
+/*
+ * da7219-aad.c - Dialog DA7219 ALSA SoC AAD Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor Ltd.
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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/platform_device.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+#include <linux/pm_wakeirq.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/workqueue.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/da7219.h>
+
+#include "da7219.h"
+#include "da7219-aad.h"
+
+
+/*
+ * Detection control
+ */
+
+void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack)
+{
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+       da7219->aad->jack = jack;
+       da7219->aad->jack_inserted = false;
+
+       /* Send an initial empty report */
+       snd_soc_jack_report(jack, 0, DA7219_AAD_REPORT_ALL_MASK);
+
+       /* Enable/Disable jack detection */
+       snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+                           DA7219_ACCDET_EN_MASK,
+                           (jack ? DA7219_ACCDET_EN_MASK : 0));
+}
+EXPORT_SYMBOL_GPL(da7219_aad_jack_det);
+
+/*
+ * Button/HPTest work
+ */
+
+static void da7219_aad_btn_det_work(struct work_struct *work)
+{
+       struct da7219_aad_priv *da7219_aad =
+               container_of(work, struct da7219_aad_priv, btn_det_work);
+       struct snd_soc_codec *codec = da7219_aad->codec;
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       u8 statusa, micbias_ctrl;
+       bool micbias_up = false;
+       int retries = 0;
+
+       /* Drive headphones/lineout */
+       snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+                           DA7219_HP_L_AMP_OE_MASK,
+                           DA7219_HP_L_AMP_OE_MASK);
+       snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+                           DA7219_HP_R_AMP_OE_MASK,
+                           DA7219_HP_R_AMP_OE_MASK);
+
+       /* Make sure mic bias is up */
+       snd_soc_dapm_force_enable_pin(dapm, "Mic Bias");
+       snd_soc_dapm_sync(dapm);
+
+       do {
+               statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A);
+               if (statusa & DA7219_MICBIAS_UP_STS_MASK)
+                       micbias_up = true;
+               else if (retries++ < DA7219_AAD_MICBIAS_CHK_RETRIES)
+                       msleep(DA7219_AAD_MICBIAS_CHK_DELAY);
+       } while ((!micbias_up) && (retries < DA7219_AAD_MICBIAS_CHK_RETRIES));
+
+       if (retries >= DA7219_AAD_MICBIAS_CHK_RETRIES)
+               dev_warn(codec->dev, "Mic bias status check timed out");
+
+       /*
+        * Mic bias pulse required to enable mic, must be done before enabling
+        * button detection to prevent erroneous button readings.
+        */
+       if (da7219_aad->micbias_pulse_lvl && da7219_aad->micbias_pulse_time) {
+               /* Pulse higher level voltage */
+               micbias_ctrl = snd_soc_read(codec, DA7219_MICBIAS_CTRL);
+               snd_soc_update_bits(codec, DA7219_MICBIAS_CTRL,
+                                   DA7219_MICBIAS1_LEVEL_MASK,
+                                   da7219_aad->micbias_pulse_lvl);
+               msleep(da7219_aad->micbias_pulse_time);
+               snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_ctrl);
+
+       }
+
+       snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+                           DA7219_BUTTON_CONFIG_MASK,
+                           da7219_aad->btn_cfg);
+}
+
+static void da7219_aad_hptest_work(struct work_struct *work)
+{
+       struct da7219_aad_priv *da7219_aad =
+               container_of(work, struct da7219_aad_priv, hptest_work);
+       struct snd_soc_codec *codec = da7219_aad->codec;
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+       u16 tonegen_freq_hptest;
+       u8 accdet_cfg8;
+       int report = 0;
+
+       /* Lock DAPM and any Kcontrols that are affected by this test */
+       snd_soc_dapm_mutex_lock(dapm);
+       mutex_lock(&da7219->lock);
+
+       /* Bypass cache so it saves current settings */
+       regcache_cache_bypass(da7219->regmap, true);
+
+       /* Make sure Tone Generator is disabled */
+       snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0);
+
+       /* Enable HPTest block, 1KOhms check */
+       snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
+                           DA7219_HPTEST_EN_MASK | DA7219_HPTEST_RES_SEL_MASK,
+                           DA7219_HPTEST_EN_MASK |
+                           DA7219_HPTEST_RES_SEL_1KOHMS);
+
+       /* Set gains to 0db */
+       snd_soc_write(codec, DA7219_DAC_L_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
+       snd_soc_write(codec, DA7219_DAC_R_GAIN, DA7219_DAC_DIGITAL_GAIN_0DB);
+       snd_soc_write(codec, DA7219_HP_L_GAIN, DA7219_HP_AMP_GAIN_0DB);
+       snd_soc_write(codec, DA7219_HP_R_GAIN, DA7219_HP_AMP_GAIN_0DB);
+
+       /* Disable DAC filters, EQs and soft mute */
+       snd_soc_update_bits(codec, DA7219_DAC_FILTERS1, DA7219_HPF_MODE_MASK,
+                           0);
+       snd_soc_update_bits(codec, DA7219_DAC_FILTERS4, DA7219_DAC_EQ_EN_MASK,
+                           0);
+       snd_soc_update_bits(codec, DA7219_DAC_FILTERS5,
+                           DA7219_DAC_SOFTMUTE_EN_MASK, 0);
+
+       /* Enable HP left & right paths */
+       snd_soc_update_bits(codec, DA7219_CP_CTRL, DA7219_CP_EN_MASK,
+                           DA7219_CP_EN_MASK);
+       snd_soc_update_bits(codec, DA7219_DIG_ROUTING_DAC,
+                           DA7219_DAC_L_SRC_MASK | DA7219_DAC_R_SRC_MASK,
+                           DA7219_DAC_L_SRC_TONEGEN |
+                           DA7219_DAC_R_SRC_TONEGEN);
+       snd_soc_update_bits(codec, DA7219_DAC_L_CTRL,
+                           DA7219_DAC_L_EN_MASK | DA7219_DAC_L_MUTE_EN_MASK,
+                           DA7219_DAC_L_EN_MASK);
+       snd_soc_update_bits(codec, DA7219_DAC_R_CTRL,
+                           DA7219_DAC_R_EN_MASK | DA7219_DAC_R_MUTE_EN_MASK,
+                           DA7219_DAC_R_EN_MASK);
+       snd_soc_update_bits(codec, DA7219_MIXOUT_L_SELECT,
+                           DA7219_MIXOUT_L_MIX_SELECT_MASK,
+                           DA7219_MIXOUT_L_MIX_SELECT_MASK);
+       snd_soc_update_bits(codec, DA7219_MIXOUT_R_SELECT,
+                           DA7219_MIXOUT_R_MIX_SELECT_MASK,
+                           DA7219_MIXOUT_R_MIX_SELECT_MASK);
+       snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1L,
+                           DA7219_OUTFILT_ST_1L_SRC_MASK,
+                           DA7219_DMIX_ST_SRC_OUTFILT1L);
+       snd_soc_update_bits(codec, DA7219_DROUTING_ST_OUTFILT_1R,
+                           DA7219_OUTFILT_ST_1R_SRC_MASK,
+                           DA7219_DMIX_ST_SRC_OUTFILT1R);
+       snd_soc_update_bits(codec, DA7219_MIXOUT_L_CTRL,
+                           DA7219_MIXOUT_L_AMP_EN_MASK,
+                           DA7219_MIXOUT_L_AMP_EN_MASK);
+       snd_soc_update_bits(codec, DA7219_MIXOUT_R_CTRL,
+                           DA7219_MIXOUT_R_AMP_EN_MASK,
+                           DA7219_MIXOUT_R_AMP_EN_MASK);
+       snd_soc_write(codec, DA7219_HP_L_CTRL,
+                     DA7219_HP_L_AMP_OE_MASK | DA7219_HP_L_AMP_EN_MASK);
+       snd_soc_write(codec, DA7219_HP_R_CTRL,
+                     DA7219_HP_R_AMP_OE_MASK | DA7219_HP_R_AMP_EN_MASK);
+
+       /* Configure & start Tone Generator */
+       snd_soc_write(codec, DA7219_TONE_GEN_ON_PER, DA7219_BEEP_ON_PER_MASK);
+       tonegen_freq_hptest = cpu_to_le16(DA7219_AAD_HPTEST_RAMP_FREQ);
+       regmap_raw_write(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
+                        &tonegen_freq_hptest, sizeof(tonegen_freq_hptest));
+       snd_soc_update_bits(codec, DA7219_TONE_GEN_CFG2,
+                           DA7219_SWG_SEL_MASK | DA7219_TONE_GEN_GAIN_MASK,
+                           DA7219_SWG_SEL_SRAMP |
+                           DA7219_TONE_GEN_GAIN_MINUS_15DB);
+       snd_soc_write(codec, DA7219_TONE_GEN_CFG1, DA7219_START_STOPN_MASK);
+
+       msleep(DA7219_AAD_HPTEST_PERIOD);
+
+       /* Grab comparator reading */
+       accdet_cfg8 = snd_soc_read(codec, DA7219_ACCDET_CONFIG_8);
+       if (accdet_cfg8 & DA7219_HPTEST_COMP_MASK)
+               report |= SND_JACK_HEADPHONE;
+       else
+               report |= SND_JACK_LINEOUT;
+
+       /* Stop tone generator */
+       snd_soc_write(codec, DA7219_TONE_GEN_CFG1, 0);
+
+       msleep(DA7219_AAD_HPTEST_PERIOD);
+
+       /* Restore original settings from cache */
+       regcache_mark_dirty(da7219->regmap);
+       regcache_sync_region(da7219->regmap, DA7219_HP_L_CTRL,
+                            DA7219_HP_R_CTRL);
+       regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_CTRL,
+                            DA7219_MIXOUT_R_CTRL);
+       regcache_sync_region(da7219->regmap, DA7219_DROUTING_ST_OUTFILT_1L,
+                            DA7219_DROUTING_ST_OUTFILT_1R);
+       regcache_sync_region(da7219->regmap, DA7219_MIXOUT_L_SELECT,
+                            DA7219_MIXOUT_R_SELECT);
+       regcache_sync_region(da7219->regmap, DA7219_DAC_L_CTRL,
+                            DA7219_DAC_R_CTRL);
+       regcache_sync_region(da7219->regmap, DA7219_DIG_ROUTING_DAC,
+                            DA7219_DIG_ROUTING_DAC);
+       regcache_sync_region(da7219->regmap, DA7219_CP_CTRL, DA7219_CP_CTRL);
+       regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS5,
+                            DA7219_DAC_FILTERS5);
+       regcache_sync_region(da7219->regmap, DA7219_DAC_FILTERS4,
+                            DA7219_DAC_FILTERS1);
+       regcache_sync_region(da7219->regmap, DA7219_HP_L_GAIN,
+                            DA7219_HP_R_GAIN);
+       regcache_sync_region(da7219->regmap, DA7219_DAC_L_GAIN,
+                            DA7219_DAC_R_GAIN);
+       regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_ON_PER,
+                            DA7219_TONE_GEN_ON_PER);
+       regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_FREQ1_L,
+                            DA7219_TONE_GEN_FREQ1_U);
+       regcache_sync_region(da7219->regmap, DA7219_TONE_GEN_CFG1,
+                            DA7219_TONE_GEN_CFG2);
+
+       regcache_cache_bypass(da7219->regmap, false);
+
+       /* Disable HPTest block */
+       snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_8,
+                           DA7219_HPTEST_EN_MASK, 0);
+
+       /* Drive Headphones/lineout */
+       snd_soc_update_bits(codec, DA7219_HP_L_CTRL, DA7219_HP_L_AMP_OE_MASK,
+                           DA7219_HP_L_AMP_OE_MASK);
+       snd_soc_update_bits(codec, DA7219_HP_R_CTRL, DA7219_HP_R_AMP_OE_MASK,
+                           DA7219_HP_R_AMP_OE_MASK);
+
+       mutex_unlock(&da7219->lock);
+       snd_soc_dapm_mutex_unlock(dapm);
+
+       /*
+        * Only send report if jack hasn't been removed during process,
+        * otherwise it's invalid and we drop it.
+        */
+       if (da7219_aad->jack_inserted)
+               snd_soc_jack_report(da7219_aad->jack, report,
+                                   SND_JACK_HEADSET | SND_JACK_LINEOUT);
+}
+
+
+/*
+ * IRQ
+ */
+
+static irqreturn_t da7219_aad_irq_thread(int irq, void *data)
+{
+       struct da7219_aad_priv *da7219_aad = data;
+       struct snd_soc_codec *codec = da7219_aad->codec;
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       u8 events[DA7219_AAD_IRQ_REG_MAX];
+       u8 statusa;
+       int i, report = 0, mask = 0;
+
+       /* Read current IRQ events */
+       regmap_bulk_read(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
+                        events, DA7219_AAD_IRQ_REG_MAX);
+
+       if (!events[DA7219_AAD_IRQ_REG_A] && !events[DA7219_AAD_IRQ_REG_B])
+               return IRQ_NONE;
+
+       /* Read status register for jack insertion & type status */
+       statusa = snd_soc_read(codec, DA7219_ACCDET_STATUS_A);
+
+       /* Clear events */
+       regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_EVENT_A,
+                         events, DA7219_AAD_IRQ_REG_MAX);
+
+       dev_dbg(codec->dev, "IRQ events = 0x%x|0x%x, status = 0x%x\n",
+               events[DA7219_AAD_IRQ_REG_A], events[DA7219_AAD_IRQ_REG_B],
+               statusa);
+
+       if (statusa & DA7219_JACK_INSERTION_STS_MASK) {
+               /* Jack Insertion */
+               if (events[DA7219_AAD_IRQ_REG_A] &
+                   DA7219_E_JACK_INSERTED_MASK) {
+                       report |= SND_JACK_MECHANICAL;
+                       mask |= SND_JACK_MECHANICAL;
+                       da7219_aad->jack_inserted = true;
+               }
+
+               /* Jack type detection */
+               if (events[DA7219_AAD_IRQ_REG_A] &
+                   DA7219_E_JACK_DETECT_COMPLETE_MASK) {
+                       /*
+                        * If 4-pole, then enable button detection, else perform
+                        * HP impedance test to determine output type to report.
+                        *
+                        * We schedule work here as the tasks themselves can
+                        * take time to complete, and in particular for hptest
+                        * we want to be able to check if the jack was removed
+                        * during the procedure as this will invalidate the
+                        * result. By doing this as work, the IRQ thread can
+                        * handle a removal, and we can check at the end of
+                        * hptest if we have a valid result or not.
+                        */
+                       if (statusa & DA7219_JACK_TYPE_STS_MASK) {
+                               report |= SND_JACK_HEADSET;
+                               mask |= SND_JACK_HEADSET | SND_JACK_LINEOUT;
+                               schedule_work(&da7219_aad->btn_det_work);
+                       } else {
+                               schedule_work(&da7219_aad->hptest_work);
+                       }
+               }
+
+               /* Button support for 4-pole jack */
+               if (statusa & DA7219_JACK_TYPE_STS_MASK) {
+                       for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
+                               /* Button Press */
+                               if (events[DA7219_AAD_IRQ_REG_B] &
+                                   (DA7219_E_BUTTON_A_PRESSED_MASK << i)) {
+                                       report |= SND_JACK_BTN_0 >> i;
+                                       mask |= SND_JACK_BTN_0 >> i;
+                               }
+                       }
+                       snd_soc_jack_report(da7219_aad->jack, report, mask);
+
+                       for (i = 0; i < DA7219_AAD_MAX_BUTTONS; ++i) {
+                               /* Button Release */
+                               if (events[DA7219_AAD_IRQ_REG_B] &
+                                   (DA7219_E_BUTTON_A_RELEASED_MASK >> i)) {
+                                       report &= ~(SND_JACK_BTN_0 >> i);
+                                       mask |= SND_JACK_BTN_0 >> i;
+                               }
+                       }
+               }
+       } else {
+               /* Jack removal */
+               if (events[DA7219_AAD_IRQ_REG_A] & DA7219_E_JACK_REMOVED_MASK) {
+                       report = 0;
+                       mask |= DA7219_AAD_REPORT_ALL_MASK;
+                       da7219_aad->jack_inserted = false;
+
+                       /* Un-drive headphones/lineout */
+                       snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+                                           DA7219_HP_R_AMP_OE_MASK, 0);
+                       snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+                                           DA7219_HP_L_AMP_OE_MASK, 0);
+
+                       /* Ensure button detection disabled */
+                       snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+                                           DA7219_BUTTON_CONFIG_MASK, 0);
+
+                       /* Disable mic bias */
+                       snd_soc_dapm_disable_pin(dapm, "Mic Bias");
+                       snd_soc_dapm_sync(dapm);
+
+                       /* Cancel any pending work */
+                       cancel_work_sync(&da7219_aad->btn_det_work);
+                       cancel_work_sync(&da7219_aad->hptest_work);
+               }
+       }
+
+       snd_soc_jack_report(da7219_aad->jack, report, mask);
+
+       return IRQ_HANDLED;
+}
+
+/*
+ * DT to pdata conversion
+ */
+
+static enum da7219_aad_micbias_pulse_lvl
+       da7219_aad_of_micbias_pulse_lvl(struct snd_soc_codec *codec, u32 val)
+{
+       switch (val) {
+       case 2800:
+               return DA7219_AAD_MICBIAS_PULSE_LVL_2_8V;
+       case 2900:
+               return DA7219_AAD_MICBIAS_PULSE_LVL_2_9V;
+       default:
+               dev_warn(codec->dev, "Invalid micbias pulse level");
+               return DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
+       }
+}
+
+static enum da7219_aad_btn_cfg
+       da7219_aad_of_btn_cfg(struct snd_soc_codec *codec, u32 val)
+{
+       switch (val) {
+       case 2:
+               return DA7219_AAD_BTN_CFG_2MS;
+       case 5:
+               return DA7219_AAD_BTN_CFG_5MS;
+       case 10:
+               return DA7219_AAD_BTN_CFG_10MS;
+       case 50:
+               return DA7219_AAD_BTN_CFG_50MS;
+       case 100:
+               return DA7219_AAD_BTN_CFG_100MS;
+       case 200:
+               return DA7219_AAD_BTN_CFG_200MS;
+       case 500:
+               return DA7219_AAD_BTN_CFG_500MS;
+       default:
+               dev_warn(codec->dev, "Invalid button config");
+               return DA7219_AAD_BTN_CFG_10MS;
+       }
+}
+
+static enum da7219_aad_mic_det_thr
+       da7219_aad_of_mic_det_thr(struct snd_soc_codec *codec, u32 val)
+{
+       switch (val) {
+       case 200:
+               return DA7219_AAD_MIC_DET_THR_200_OHMS;
+       case 500:
+               return DA7219_AAD_MIC_DET_THR_500_OHMS;
+       case 750:
+               return DA7219_AAD_MIC_DET_THR_750_OHMS;
+       case 1000:
+               return DA7219_AAD_MIC_DET_THR_1000_OHMS;
+       default:
+               dev_warn(codec->dev, "Invalid mic detect threshold");
+               return DA7219_AAD_MIC_DET_THR_500_OHMS;
+       }
+}
+
+static enum da7219_aad_jack_ins_deb
+       da7219_aad_of_jack_ins_deb(struct snd_soc_codec *codec, u32 val)
+{
+       switch (val) {
+       case 5:
+               return DA7219_AAD_JACK_INS_DEB_5MS;
+       case 10:
+               return DA7219_AAD_JACK_INS_DEB_10MS;
+       case 20:
+               return DA7219_AAD_JACK_INS_DEB_20MS;
+       case 50:
+               return DA7219_AAD_JACK_INS_DEB_50MS;
+       case 100:
+               return DA7219_AAD_JACK_INS_DEB_100MS;
+       case 200:
+               return DA7219_AAD_JACK_INS_DEB_200MS;
+       case 500:
+               return DA7219_AAD_JACK_INS_DEB_500MS;
+       case 1000:
+               return DA7219_AAD_JACK_INS_DEB_1S;
+       default:
+               dev_warn(codec->dev, "Invalid jack insert debounce");
+               return DA7219_AAD_JACK_INS_DEB_20MS;
+       }
+}
+
+static enum da7219_aad_jack_det_rate
+       da7219_aad_of_jack_det_rate(struct snd_soc_codec *codec, const char *str)
+{
+       if (!strcmp(str, "32ms_64ms")) {
+               return DA7219_AAD_JACK_DET_RATE_32_64MS;
+       } else if (!strcmp(str, "64ms_128ms")) {
+               return DA7219_AAD_JACK_DET_RATE_64_128MS;
+       } else if (!strcmp(str, "128ms_256ms")) {
+               return DA7219_AAD_JACK_DET_RATE_128_256MS;
+       } else if (!strcmp(str, "256ms_512ms")) {
+               return DA7219_AAD_JACK_DET_RATE_256_512MS;
+       } else {
+               dev_warn(codec->dev, "Invalid jack detect rate");
+               return DA7219_AAD_JACK_DET_RATE_256_512MS;
+       }
+}
+
+static enum da7219_aad_jack_rem_deb
+       da7219_aad_of_jack_rem_deb(struct snd_soc_codec *codec, u32 val)
+{
+       switch (val) {
+       case 1:
+               return DA7219_AAD_JACK_REM_DEB_1MS;
+       case 5:
+               return DA7219_AAD_JACK_REM_DEB_5MS;
+       case 10:
+               return DA7219_AAD_JACK_REM_DEB_10MS;
+       case 20:
+               return DA7219_AAD_JACK_REM_DEB_20MS;
+       default:
+               dev_warn(codec->dev, "Invalid jack removal debounce");
+               return DA7219_AAD_JACK_REM_DEB_1MS;
+       }
+}
+
+static enum da7219_aad_btn_avg
+       da7219_aad_of_btn_avg(struct snd_soc_codec *codec, u32 val)
+{
+       switch (val) {
+       case 1:
+               return DA7219_AAD_BTN_AVG_1;
+       case 2:
+               return DA7219_AAD_BTN_AVG_2;
+       case 4:
+               return DA7219_AAD_BTN_AVG_4;
+       case 8:
+               return DA7219_AAD_BTN_AVG_8;
+       default:
+               dev_warn(codec->dev, "Invalid button average value");
+               return DA7219_AAD_BTN_AVG_2;
+       }
+}
+
+static enum da7219_aad_adc_1bit_rpt
+       da7219_aad_of_adc_1bit_rpt(struct snd_soc_codec *codec, u32 val)
+{
+       switch (val) {
+       case 1:
+               return DA7219_AAD_ADC_1BIT_RPT_1;
+       case 2:
+               return DA7219_AAD_ADC_1BIT_RPT_2;
+       case 4:
+               return DA7219_AAD_ADC_1BIT_RPT_4;
+       case 8:
+               return DA7219_AAD_ADC_1BIT_RPT_8;
+       default:
+               dev_warn(codec->dev, "Invalid ADC 1-bit repeat value");
+               return DA7219_AAD_ADC_1BIT_RPT_1;
+       }
+}
+
+static struct da7219_aad_pdata *da7219_aad_of_to_pdata(struct snd_soc_codec *codec)
+{
+       struct device_node *np = codec->dev->of_node;
+       struct device_node *aad_np = of_find_node_by_name(np, "da7219_aad");
+       struct da7219_aad_pdata *aad_pdata;
+       const char *of_str;
+       u32 of_val32;
+
+       if (!aad_np)
+               return NULL;
+
+       aad_pdata = devm_kzalloc(codec->dev, sizeof(*aad_pdata), GFP_KERNEL);
+       if (!aad_pdata)
+               goto out;
+
+       aad_pdata->irq = irq_of_parse_and_map(np, 0);
+
+       if (of_property_read_u32(aad_np, "dlg,micbias-pulse-lvl",
+                                &of_val32) >= 0)
+               aad_pdata->micbias_pulse_lvl =
+                       da7219_aad_of_micbias_pulse_lvl(codec, of_val32);
+       else
+               aad_pdata->micbias_pulse_lvl = DA7219_AAD_MICBIAS_PULSE_LVL_OFF;
+
+       if (of_property_read_u32(aad_np, "dlg,micbias-pulse-time",
+                                &of_val32) >= 0)
+               aad_pdata->micbias_pulse_time = of_val32;
+
+       if (of_property_read_u32(aad_np, "dlg,btn-cfg", &of_val32) >= 0)
+               aad_pdata->btn_cfg = da7219_aad_of_btn_cfg(codec, of_val32);
+       else
+               aad_pdata->btn_cfg = DA7219_AAD_BTN_CFG_10MS;
+
+       if (of_property_read_u32(aad_np, "dlg,mic-det-thr", &of_val32) >= 0)
+               aad_pdata->mic_det_thr =
+                       da7219_aad_of_mic_det_thr(codec, of_val32);
+       else
+               aad_pdata->mic_det_thr = DA7219_AAD_MIC_DET_THR_500_OHMS;
+
+       if (of_property_read_u32(aad_np, "dlg,jack-ins-deb", &of_val32) >= 0)
+               aad_pdata->jack_ins_deb =
+                       da7219_aad_of_jack_ins_deb(codec, of_val32);
+       else
+               aad_pdata->jack_ins_deb = DA7219_AAD_JACK_INS_DEB_20MS;
+
+       if (!of_property_read_string(aad_np, "dlg,jack-det-rate", &of_str))
+               aad_pdata->jack_det_rate =
+                       da7219_aad_of_jack_det_rate(codec, of_str);
+       else
+               aad_pdata->jack_det_rate = DA7219_AAD_JACK_DET_RATE_256_512MS;
+
+       if (of_property_read_u32(aad_np, "dlg,jack-rem-deb", &of_val32) >= 0)
+               aad_pdata->jack_rem_deb =
+                       da7219_aad_of_jack_rem_deb(codec, of_val32);
+       else
+               aad_pdata->jack_rem_deb = DA7219_AAD_JACK_REM_DEB_1MS;
+
+       if (of_property_read_u32(aad_np, "dlg,a-d-btn-thr", &of_val32) >= 0)
+               aad_pdata->a_d_btn_thr = (u8) of_val32;
+       else
+               aad_pdata->a_d_btn_thr = 0xA;
+
+       if (of_property_read_u32(aad_np, "dlg,d-b-btn-thr", &of_val32) >= 0)
+               aad_pdata->d_b_btn_thr = (u8) of_val32;
+       else
+               aad_pdata->d_b_btn_thr = 0x16;
+
+       if (of_property_read_u32(aad_np, "dlg,b-c-btn-thr", &of_val32) >= 0)
+               aad_pdata->b_c_btn_thr = (u8) of_val32;
+       else
+               aad_pdata->b_c_btn_thr = 0x21;
+
+       if (of_property_read_u32(aad_np, "dlg,c-mic-btn-thr", &of_val32) >= 0)
+               aad_pdata->c_mic_btn_thr = (u8) of_val32;
+       else
+               aad_pdata->c_mic_btn_thr = 0x3E;
+
+       if (of_property_read_u32(aad_np, "dlg,btn-avg", &of_val32) >= 0)
+               aad_pdata->btn_avg = da7219_aad_of_btn_avg(codec, of_val32);
+       else
+               aad_pdata->btn_avg = DA7219_AAD_BTN_AVG_2;
+
+       if (of_property_read_u32(aad_np, "dlg,adc-1bit-rpt", &of_val32) >= 0)
+               aad_pdata->adc_1bit_rpt =
+                       da7219_aad_of_adc_1bit_rpt(codec, of_val32);
+       else
+               aad_pdata->adc_1bit_rpt = DA7219_AAD_ADC_1BIT_RPT_1;
+
+out:
+       of_node_put(aad_np);
+
+       return aad_pdata;
+}
+
+static void da7219_aad_handle_pdata(struct snd_soc_codec *codec)
+{
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       struct da7219_aad_priv *da7219_aad = da7219->aad;
+       struct da7219_pdata *pdata = da7219->pdata;
+
+       if ((pdata) && (pdata->aad_pdata)) {
+               struct da7219_aad_pdata *aad_pdata = pdata->aad_pdata;
+               u8 cfg, mask;
+
+               da7219_aad->irq = aad_pdata->irq;
+
+               switch (aad_pdata->micbias_pulse_lvl) {
+               case DA7219_AAD_MICBIAS_PULSE_LVL_2_8V:
+               case DA7219_AAD_MICBIAS_PULSE_LVL_2_9V:
+                       da7219_aad->micbias_pulse_lvl =
+                               (aad_pdata->micbias_pulse_lvl <<
+                                DA7219_MICBIAS1_LEVEL_SHIFT);
+                       break;
+               default:
+                       break;
+               }
+
+               da7219_aad->micbias_pulse_time = aad_pdata->micbias_pulse_time;
+
+               switch (aad_pdata->btn_cfg) {
+               case DA7219_AAD_BTN_CFG_2MS:
+               case DA7219_AAD_BTN_CFG_5MS:
+               case DA7219_AAD_BTN_CFG_10MS:
+               case DA7219_AAD_BTN_CFG_50MS:
+               case DA7219_AAD_BTN_CFG_100MS:
+               case DA7219_AAD_BTN_CFG_200MS:
+               case DA7219_AAD_BTN_CFG_500MS:
+                       da7219_aad->btn_cfg  = (aad_pdata->btn_cfg <<
+                                               DA7219_BUTTON_CONFIG_SHIFT);
+               }
+
+               cfg = 0;
+               mask = 0;
+               switch (aad_pdata->mic_det_thr) {
+               case DA7219_AAD_MIC_DET_THR_200_OHMS:
+               case DA7219_AAD_MIC_DET_THR_500_OHMS:
+               case DA7219_AAD_MIC_DET_THR_750_OHMS:
+               case DA7219_AAD_MIC_DET_THR_1000_OHMS:
+                       cfg |= (aad_pdata->mic_det_thr <<
+                               DA7219_MIC_DET_THRESH_SHIFT);
+                       mask |= DA7219_MIC_DET_THRESH_MASK;
+               }
+               snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1, mask, cfg);
+
+               cfg = 0;
+               mask = 0;
+               switch (aad_pdata->jack_ins_deb) {
+               case DA7219_AAD_JACK_INS_DEB_5MS:
+               case DA7219_AAD_JACK_INS_DEB_10MS:
+               case DA7219_AAD_JACK_INS_DEB_20MS:
+               case DA7219_AAD_JACK_INS_DEB_50MS:
+               case DA7219_AAD_JACK_INS_DEB_100MS:
+               case DA7219_AAD_JACK_INS_DEB_200MS:
+               case DA7219_AAD_JACK_INS_DEB_500MS:
+               case DA7219_AAD_JACK_INS_DEB_1S:
+                       cfg |= (aad_pdata->jack_ins_deb <<
+                               DA7219_JACKDET_DEBOUNCE_SHIFT);
+                       mask |= DA7219_JACKDET_DEBOUNCE_MASK;
+               }
+               switch (aad_pdata->jack_det_rate) {
+               case DA7219_AAD_JACK_DET_RATE_32_64MS:
+               case DA7219_AAD_JACK_DET_RATE_64_128MS:
+               case DA7219_AAD_JACK_DET_RATE_128_256MS:
+               case DA7219_AAD_JACK_DET_RATE_256_512MS:
+                       cfg |= (aad_pdata->jack_det_rate <<
+                               DA7219_JACK_DETECT_RATE_SHIFT);
+                       mask |= DA7219_JACK_DETECT_RATE_MASK;
+               }
+               switch (aad_pdata->jack_rem_deb) {
+               case DA7219_AAD_JACK_REM_DEB_1MS:
+               case DA7219_AAD_JACK_REM_DEB_5MS:
+               case DA7219_AAD_JACK_REM_DEB_10MS:
+               case DA7219_AAD_JACK_REM_DEB_20MS:
+                       cfg |= (aad_pdata->jack_rem_deb <<
+                               DA7219_JACKDET_REM_DEB_SHIFT);
+                       mask |= DA7219_JACKDET_REM_DEB_MASK;
+               }
+               snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_2, mask, cfg);
+
+               snd_soc_write(codec, DA7219_ACCDET_CONFIG_3,
+                             aad_pdata->a_d_btn_thr);
+               snd_soc_write(codec, DA7219_ACCDET_CONFIG_4,
+                             aad_pdata->d_b_btn_thr);
+               snd_soc_write(codec, DA7219_ACCDET_CONFIG_5,
+                             aad_pdata->b_c_btn_thr);
+               snd_soc_write(codec, DA7219_ACCDET_CONFIG_6,
+                             aad_pdata->c_mic_btn_thr);
+
+               cfg = 0;
+               mask = 0;
+               switch (aad_pdata->btn_avg) {
+               case DA7219_AAD_BTN_AVG_1:
+               case DA7219_AAD_BTN_AVG_2:
+               case DA7219_AAD_BTN_AVG_4:
+               case DA7219_AAD_BTN_AVG_8:
+                       cfg |= (aad_pdata->btn_avg <<
+                               DA7219_BUTTON_AVERAGE_SHIFT);
+                       mask |= DA7219_BUTTON_AVERAGE_MASK;
+               }
+               switch (aad_pdata->adc_1bit_rpt) {
+               case DA7219_AAD_ADC_1BIT_RPT_1:
+               case DA7219_AAD_ADC_1BIT_RPT_2:
+               case DA7219_AAD_ADC_1BIT_RPT_4:
+               case DA7219_AAD_ADC_1BIT_RPT_8:
+                       cfg |= (aad_pdata->adc_1bit_rpt <<
+                              DA7219_ADC_1_BIT_REPEAT_SHIFT);
+                       mask |= DA7219_ADC_1_BIT_REPEAT_MASK;
+               }
+               snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_7, mask, cfg);
+       }
+}
+
+
+/*
+ * Init/Exit
+ */
+
+int da7219_aad_init(struct snd_soc_codec *codec)
+{
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       struct da7219_aad_priv *da7219_aad;
+       u8 mask[DA7219_AAD_IRQ_REG_MAX];
+       int ret;
+
+       da7219_aad = devm_kzalloc(codec->dev, sizeof(*da7219_aad), GFP_KERNEL);
+       if (!da7219_aad)
+               return -ENOMEM;
+
+       da7219->aad = da7219_aad;
+       da7219_aad->codec = codec;
+
+       /* Handle any DT/platform data */
+       if ((codec->dev->of_node) && (da7219->pdata))
+               da7219->pdata->aad_pdata = da7219_aad_of_to_pdata(codec);
+
+       da7219_aad_handle_pdata(codec);
+
+       /* Disable button detection */
+       snd_soc_update_bits(codec, DA7219_ACCDET_CONFIG_1,
+                           DA7219_BUTTON_CONFIG_MASK, 0);
+
+       INIT_WORK(&da7219_aad->btn_det_work, da7219_aad_btn_det_work);
+       INIT_WORK(&da7219_aad->hptest_work, da7219_aad_hptest_work);
+
+       ret = request_threaded_irq(da7219_aad->irq, NULL,
+                                  da7219_aad_irq_thread,
+                                  IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+                                  "da7219-aad", da7219_aad);
+       if (ret) {
+               dev_err(codec->dev, "Failed to request IRQ: %d\n", ret);
+               return ret;
+       }
+
+       /* Unmask AAD IRQs */
+       memset(mask, 0, DA7219_AAD_IRQ_REG_MAX);
+       regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
+                         &mask, DA7219_AAD_IRQ_REG_MAX);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(da7219_aad_init);
+
+void da7219_aad_exit(struct snd_soc_codec *codec)
+{
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       struct da7219_aad_priv *da7219_aad = da7219->aad;
+       u8 mask[DA7219_AAD_IRQ_REG_MAX];
+
+       /* Mask off AAD IRQs */
+       memset(mask, DA7219_BYTE_MASK, DA7219_AAD_IRQ_REG_MAX);
+       regmap_bulk_write(da7219->regmap, DA7219_ACCDET_IRQ_MASK_A,
+                         mask, DA7219_AAD_IRQ_REG_MAX);
+
+       free_irq(da7219_aad->irq, da7219_aad);
+
+       cancel_work_sync(&da7219_aad->btn_det_work);
+       cancel_work_sync(&da7219_aad->hptest_work);
+}
+EXPORT_SYMBOL_GPL(da7219_aad_exit);
+
+MODULE_DESCRIPTION("ASoC DA7219 AAD Driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7219-aad.h b/sound/soc/codecs/da7219-aad.h
new file mode 100644 (file)
index 0000000..4fccf67
--- /dev/null
@@ -0,0 +1,212 @@
+/*
+ * da7219-aad.h - DA7322 ASoC AAD Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor Ltd.
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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.
+ */
+
+#ifndef __DA7219_AAD_H
+#define __DA7219_AAD_H
+
+#include <linux/timer.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/da7219-aad.h>
+
+/*
+ * Registers
+ */
+
+#define DA7219_ACCDET_STATUS_A         0xC0
+#define DA7219_ACCDET_STATUS_B         0xC1
+#define DA7219_ACCDET_IRQ_EVENT_A      0xC2
+#define DA7219_ACCDET_IRQ_EVENT_B      0xC3
+#define DA7219_ACCDET_IRQ_MASK_A       0xC4
+#define DA7219_ACCDET_IRQ_MASK_B       0xC5
+#define DA7219_ACCDET_CONFIG_1         0xC6
+#define DA7219_ACCDET_CONFIG_2         0xC7
+#define DA7219_ACCDET_CONFIG_3         0xC8
+#define DA7219_ACCDET_CONFIG_4         0xC9
+#define DA7219_ACCDET_CONFIG_5         0xCA
+#define DA7219_ACCDET_CONFIG_6         0xCB
+#define DA7219_ACCDET_CONFIG_7         0xCC
+#define DA7219_ACCDET_CONFIG_8         0xCD
+
+
+/*
+ * Bit Fields
+ */
+
+/* DA7219_ACCDET_STATUS_A = 0xC0 */
+#define DA7219_JACK_INSERTION_STS_SHIFT        0
+#define DA7219_JACK_INSERTION_STS_MASK (0x1 << 0)
+#define DA7219_JACK_TYPE_STS_SHIFT     1
+#define DA7219_JACK_TYPE_STS_MASK      (0x1 << 1)
+#define DA7219_JACK_PIN_ORDER_STS_SHIFT        2
+#define DA7219_JACK_PIN_ORDER_STS_MASK (0x1 << 2)
+#define DA7219_MICBIAS_UP_STS_SHIFT    3
+#define DA7219_MICBIAS_UP_STS_MASK     (0x1 << 3)
+
+/* DA7219_ACCDET_STATUS_B = 0xC1 */
+#define DA7219_BUTTON_TYPE_STS_SHIFT   0
+#define DA7219_BUTTON_TYPE_STS_MASK    (0xFF << 0)
+
+/* DA7219_ACCDET_IRQ_EVENT_A = 0xC2 */
+#define DA7219_E_JACK_INSERTED_SHIFT           0
+#define DA7219_E_JACK_INSERTED_MASK            (0x1 << 0)
+#define DA7219_E_JACK_REMOVED_SHIFT            1
+#define DA7219_E_JACK_REMOVED_MASK             (0x1 << 1)
+#define DA7219_E_JACK_DETECT_COMPLETE_SHIFT    2
+#define DA7219_E_JACK_DETECT_COMPLETE_MASK     (0x1 << 2)
+
+/* DA7219_ACCDET_IRQ_EVENT_B = 0xC3 */
+#define DA7219_E_BUTTON_A_PRESSED_SHIFT                0
+#define DA7219_E_BUTTON_A_PRESSED_MASK         (0x1 << 0)
+#define DA7219_E_BUTTON_B_PRESSED_SHIFT                1
+#define DA7219_E_BUTTON_B_PRESSED_MASK         (0x1 << 1)
+#define DA7219_E_BUTTON_C_PRESSED_SHIFT                2
+#define DA7219_E_BUTTON_C_PRESSED_MASK         (0x1 << 2)
+#define DA7219_E_BUTTON_D_PRESSED_SHIFT                3
+#define DA7219_E_BUTTON_D_PRESSED_MASK         (0x1 << 3)
+#define DA7219_E_BUTTON_D_RELEASED_SHIFT       4
+#define DA7219_E_BUTTON_D_RELEASED_MASK                (0x1 << 4)
+#define DA7219_E_BUTTON_C_RELEASED_SHIFT       5
+#define DA7219_E_BUTTON_C_RELEASED_MASK                (0x1 << 5)
+#define DA7219_E_BUTTON_B_RELEASED_SHIFT       6
+#define DA7219_E_BUTTON_B_RELEASED_MASK                (0x1 << 6)
+#define DA7219_E_BUTTON_A_RELEASED_SHIFT       7
+#define DA7219_E_BUTTON_A_RELEASED_MASK                (0x1 << 7)
+
+/* DA7219_ACCDET_IRQ_MASK_A = 0xC4 */
+#define DA7219_M_JACK_INSERTED_SHIFT           0
+#define DA7219_M_JACK_INSERTED_MASK            (0x1 << 0)
+#define DA7219_M_JACK_REMOVED_SHIFT            1
+#define DA7219_M_JACK_REMOVED_MASK             (0x1 << 1)
+#define DA7219_M_JACK_DETECT_COMPLETE_SHIFT    2
+#define DA7219_M_JACK_DETECT_COMPLETE_MASK     (0x1 << 2)
+
+/* DA7219_ACCDET_IRQ_MASK_B = 0xC5 */
+#define DA7219_M_BUTTON_A_PRESSED_SHIFT                0
+#define DA7219_M_BUTTON_A_PRESSED_MASK         (0x1 << 0)
+#define DA7219_M_BUTTON_B_PRESSED_SHIFT                1
+#define DA7219_M_BUTTON_B_PRESSED_MASK         (0x1 << 1)
+#define DA7219_M_BUTTON_C_PRESSED_SHIFT                2
+#define DA7219_M_BUTTON_C_PRESSED_MASK         (0x1 << 2)
+#define DA7219_M_BUTTON_D_PRESSED_SHIFT                3
+#define DA7219_M_BUTTON_D_PRESSED_MASK         (0x1 << 3)
+#define DA7219_M_BUTTON_D_RELEASED_SHIFT       4
+#define DA7219_M_BUTTON_D_RELEASED_MASK                (0x1 << 4)
+#define DA7219_M_BUTTON_C_RELEASED_SHIFT       5
+#define DA7219_M_BUTTON_C_RELEASED_MASK                (0x1 << 5)
+#define DA7219_M_BUTTON_B_RELEASED_SHIFT       6
+#define DA7219_M_BUTTON_B_RELEASED_MASK                (0x1 << 6)
+#define DA7219_M_BUTTON_A_RELEASED_SHIFT       7
+#define DA7219_M_BUTTON_A_RELEASED_MASK                (0x1 << 7)
+
+/* DA7219_ACCDET_CONFIG_1 = 0xC6 */
+#define DA7219_ACCDET_EN_SHIFT         0
+#define DA7219_ACCDET_EN_MASK          (0x1 << 0)
+#define DA7219_BUTTON_CONFIG_SHIFT     1
+#define DA7219_BUTTON_CONFIG_MASK      (0x7 << 1)
+#define DA7219_MIC_DET_THRESH_SHIFT    4
+#define DA7219_MIC_DET_THRESH_MASK     (0x3 << 4)
+#define DA7219_JACK_TYPE_DET_EN_SHIFT  6
+#define DA7219_JACK_TYPE_DET_EN_MASK   (0x1 << 6)
+#define DA7219_PIN_ORDER_DET_EN_SHIFT  7
+#define DA7219_PIN_ORDER_DET_EN_MASK   (0x1 << 7)
+
+/* DA7219_ACCDET_CONFIG_2 = 0xC7 */
+#define DA7219_ACCDET_PAUSE_SHIFT      0
+#define DA7219_ACCDET_PAUSE_MASK       (0x1 << 0)
+#define DA7219_JACKDET_DEBOUNCE_SHIFT  1
+#define DA7219_JACKDET_DEBOUNCE_MASK   (0x7 << 1)
+#define DA7219_JACK_DETECT_RATE_SHIFT  4
+#define DA7219_JACK_DETECT_RATE_MASK   (0x3 << 4)
+#define DA7219_JACKDET_REM_DEB_SHIFT   6
+#define DA7219_JACKDET_REM_DEB_MASK    (0x3 << 6)
+
+/* DA7219_ACCDET_CONFIG_3 = 0xC8 */
+#define DA7219_A_D_BUTTON_THRESH_SHIFT 0
+#define DA7219_A_D_BUTTON_THRESH_MASK  (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_4 = 0xC9 */
+#define DA7219_D_B_BUTTON_THRESH_SHIFT 0
+#define DA7219_D_B_BUTTON_THRESH_MASK  (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_5 = 0xCA */
+#define DA7219_B_C_BUTTON_THRESH_SHIFT 0
+#define DA7219_B_C_BUTTON_THRESH_MASK  (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_6 = 0xCB */
+#define DA7219_C_MIC_BUTTON_THRESH_SHIFT       0
+#define DA7219_C_MIC_BUTTON_THRESH_MASK                (0xFF << 0)
+
+/* DA7219_ACCDET_CONFIG_7 = 0xCC */
+#define DA7219_BUTTON_AVERAGE_SHIFT    0
+#define DA7219_BUTTON_AVERAGE_MASK     (0x3 << 0)
+#define DA7219_ADC_1_BIT_REPEAT_SHIFT  2
+#define DA7219_ADC_1_BIT_REPEAT_MASK   (0x3 << 2)
+#define DA7219_PIN_ORDER_FORCE_SHIFT   4
+#define DA7219_PIN_ORDER_FORCE_MASK    (0x1 << 4)
+#define DA7219_JACK_TYPE_FORCE_SHIFT   5
+#define DA7219_JACK_TYPE_FORCE_MASK    (0x1 << 5)
+
+/* DA7219_ACCDET_CONFIG_8 = 0xCD */
+#define DA7219_HPTEST_EN_SHIFT         0
+#define DA7219_HPTEST_EN_MASK          (0x1 << 0)
+#define DA7219_HPTEST_RES_SEL_SHIFT    1
+#define DA7219_HPTEST_RES_SEL_MASK     (0x3 << 1)
+#define DA7219_HPTEST_RES_SEL_1KOHMS   (0x0 << 1)
+#define DA7219_HPTEST_COMP_SHIFT       4
+#define DA7219_HPTEST_COMP_MASK                (0x1 << 4)
+
+
+#define DA7219_AAD_MAX_BUTTONS         4
+#define DA7219_AAD_REPORT_ALL_MASK     (SND_JACK_MECHANICAL |                  \
+                                        SND_JACK_HEADSET | SND_JACK_LINEOUT |  \
+                                        SND_JACK_BTN_0 | SND_JACK_BTN_1 |      \
+                                        SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+#define DA7219_AAD_MICBIAS_CHK_DELAY   10
+#define DA7219_AAD_MICBIAS_CHK_RETRIES 5
+
+#define DA7219_AAD_HPTEST_RAMP_FREQ    0x28
+#define DA7219_AAD_HPTEST_PERIOD       65
+
+enum da7219_aad_event_regs {
+       DA7219_AAD_IRQ_REG_A = 0,
+       DA7219_AAD_IRQ_REG_B,
+       DA7219_AAD_IRQ_REG_MAX,
+};
+
+/* Private data */
+struct da7219_aad_priv {
+       struct snd_soc_codec *codec;
+       int irq;
+
+       u8 micbias_pulse_lvl;
+       u32 micbias_pulse_time;
+
+       u8 btn_cfg;
+
+       struct work_struct btn_det_work;
+       struct work_struct hptest_work;
+
+       struct snd_soc_jack *jack;
+       bool jack_inserted;
+};
+
+/* AAD control */
+void da7219_aad_jack_det(struct snd_soc_codec *codec, struct snd_soc_jack *jack);
+
+/* Init/Exit */
+int da7219_aad_init(struct snd_soc_codec *codec);
+void da7219_aad_exit(struct snd_soc_codec *codec);
+
+#endif /* __DA7219_AAD_H */
diff --git a/sound/soc/codecs/da7219.c b/sound/soc/codecs/da7219.c
new file mode 100644 (file)
index 0000000..f238c1e
--- /dev/null
@@ -0,0 +1,1955 @@
+/*
+ * da7219.c - DA7219 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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/clk.h>
+#include <linux/i2c.h>
+#include <linux/of_device.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/pm.h>
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/regulator/consumer.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/soc-dapm.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <asm/div64.h>
+
+#include <sound/da7219.h>
+#include "da7219.h"
+#include "da7219-aad.h"
+
+
+/*
+ * TLVs and Enums
+ */
+
+/* Input TLVs */
+static const DECLARE_TLV_DB_SCALE(da7219_mic_gain_tlv, -600, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_mixin_gain_tlv, -450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_adc_dig_gain_tlv, -8325, 75, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_threshold_tlv, -9450, 150, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_gain_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_alc_ana_gain_tlv, 0, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_sidetone_gain_tlv, -4200, 300, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_tonegen_gain_tlv, -4500, 300, 0);
+
+/* Output TLVs */
+static const DECLARE_TLV_DB_SCALE(da7219_dac_eq_band_tlv, -1050, 150, 0);
+
+static const DECLARE_TLV_DB_RANGE(da7219_dac_dig_gain_tlv,
+       0x0, 0x07, TLV_DB_SCALE_ITEM(TLV_DB_GAIN_MUTE, 0, 1),
+       /* -77.25dB to 12dB */
+       0x08, 0x7f, TLV_DB_SCALE_ITEM(-7725, 75, 0)
+);
+
+static const DECLARE_TLV_DB_SCALE(da7219_dac_ng_threshold_tlv, -10200, 600, 0);
+static const DECLARE_TLV_DB_SCALE(da7219_hp_gain_tlv, -5700, 100, 0);
+
+/* Input Enums */
+static const char * const da7219_alc_attack_rate_txt[] = {
+       "7.33/fs", "14.66/fs", "29.32/fs", "58.64/fs", "117.3/fs", "234.6/fs",
+       "469.1/fs", "938.2/fs", "1876/fs", "3753/fs", "7506/fs", "15012/fs",
+       "30024/fs"
+};
+
+static const struct soc_enum da7219_alc_attack_rate =
+       SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_ATTACK_SHIFT,
+                       DA7219_ALC_ATTACK_MAX, da7219_alc_attack_rate_txt);
+
+static const char * const da7219_alc_release_rate_txt[] = {
+       "28.66/fs", "57.33/fs", "114.6/fs", "229.3/fs", "458.6/fs", "917.1/fs",
+       "1834/fs", "3668/fs", "7337/fs", "14674/fs", "29348/fs"
+};
+
+static const struct soc_enum da7219_alc_release_rate =
+       SOC_ENUM_SINGLE(DA7219_ALC_CTRL2, DA7219_ALC_RELEASE_SHIFT,
+                       DA7219_ALC_RELEASE_MAX, da7219_alc_release_rate_txt);
+
+static const char * const da7219_alc_hold_time_txt[] = {
+       "62/fs", "124/fs", "248/fs", "496/fs", "992/fs", "1984/fs", "3968/fs",
+       "7936/fs", "15872/fs", "31744/fs", "63488/fs", "126976/fs",
+       "253952/fs", "507904/fs", "1015808/fs", "2031616/fs"
+};
+
+static const struct soc_enum da7219_alc_hold_time =
+       SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_HOLD_SHIFT,
+                       DA7219_ALC_HOLD_MAX, da7219_alc_hold_time_txt);
+
+static const char * const da7219_alc_env_rate_txt[] = {
+       "1/4", "1/16", "1/256", "1/65536"
+};
+
+static const struct soc_enum da7219_alc_env_attack_rate =
+       SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_ATTACK_SHIFT,
+                       DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt);
+
+static const struct soc_enum da7219_alc_env_release_rate =
+       SOC_ENUM_SINGLE(DA7219_ALC_CTRL3, DA7219_ALC_INTEG_RELEASE_SHIFT,
+                       DA7219_ALC_INTEG_MAX, da7219_alc_env_rate_txt);
+
+static const char * const da7219_alc_anticlip_step_txt[] = {
+       "0.034dB/fs", "0.068dB/fs", "0.136dB/fs", "0.272dB/fs"
+};
+
+static const struct soc_enum da7219_alc_anticlip_step =
+       SOC_ENUM_SINGLE(DA7219_ALC_ANTICLIP_CTRL,
+                       DA7219_ALC_ANTICLIP_STEP_SHIFT,
+                       DA7219_ALC_ANTICLIP_STEP_MAX,
+                       da7219_alc_anticlip_step_txt);
+
+/* Input/Output Enums */
+static const char * const da7219_gain_ramp_rate_txt[] = {
+       "Nominal Rate * 8", "Nominal Rate", "Nominal Rate / 8",
+       "Nominal Rate / 16"
+};
+
+static const struct soc_enum da7219_gain_ramp_rate =
+       SOC_ENUM_SINGLE(DA7219_GAIN_RAMP_CTRL, DA7219_GAIN_RAMP_RATE_SHIFT,
+                       DA7219_GAIN_RAMP_RATE_MAX, da7219_gain_ramp_rate_txt);
+
+static const char * const da7219_hpf_mode_txt[] = {
+       "Disabled", "Audio", "Voice"
+};
+
+static const unsigned int da7219_hpf_mode_val[] = {
+       DA7219_HPF_DISABLED, DA7219_HPF_AUDIO_EN, DA7219_HPF_VOICE_EN,
+};
+
+static const struct soc_enum da7219_adc_hpf_mode =
+       SOC_VALUE_ENUM_SINGLE(DA7219_ADC_FILTERS1, DA7219_HPF_MODE_SHIFT,
+                             DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX,
+                             da7219_hpf_mode_txt, da7219_hpf_mode_val);
+
+static const struct soc_enum da7219_dac_hpf_mode =
+       SOC_VALUE_ENUM_SINGLE(DA7219_DAC_FILTERS1, DA7219_HPF_MODE_SHIFT,
+                             DA7219_HPF_MODE_MASK, DA7219_HPF_MODE_MAX,
+                             da7219_hpf_mode_txt, da7219_hpf_mode_val);
+
+static const char * const da7219_audio_hpf_corner_txt[] = {
+       "2Hz", "4Hz", "8Hz", "16Hz"
+};
+
+static const struct soc_enum da7219_adc_audio_hpf_corner =
+       SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1,
+                       DA7219_ADC_AUDIO_HPF_CORNER_SHIFT,
+                       DA7219_AUDIO_HPF_CORNER_MAX,
+                       da7219_audio_hpf_corner_txt);
+
+static const struct soc_enum da7219_dac_audio_hpf_corner =
+       SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1,
+                       DA7219_DAC_AUDIO_HPF_CORNER_SHIFT,
+                       DA7219_AUDIO_HPF_CORNER_MAX,
+                       da7219_audio_hpf_corner_txt);
+
+static const char * const da7219_voice_hpf_corner_txt[] = {
+       "2.5Hz", "25Hz", "50Hz", "100Hz", "150Hz", "200Hz", "300Hz", "400Hz"
+};
+
+static const struct soc_enum da7219_adc_voice_hpf_corner =
+       SOC_ENUM_SINGLE(DA7219_ADC_FILTERS1,
+                       DA7219_ADC_VOICE_HPF_CORNER_SHIFT,
+                       DA7219_VOICE_HPF_CORNER_MAX,
+                       da7219_voice_hpf_corner_txt);
+
+static const struct soc_enum da7219_dac_voice_hpf_corner =
+       SOC_ENUM_SINGLE(DA7219_DAC_FILTERS1,
+                       DA7219_DAC_VOICE_HPF_CORNER_SHIFT,
+                       DA7219_VOICE_HPF_CORNER_MAX,
+                       da7219_voice_hpf_corner_txt);
+
+static const char * const da7219_tonegen_dtmf_key_txt[] = {
+       "0", "1", "2", "3", "4", "5", "6", "7", "8", "9", "A", "B", "C", "D",
+       "*", "#"
+};
+
+static const struct soc_enum da7219_tonegen_dtmf_key =
+       SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG1, DA7219_DTMF_REG_SHIFT,
+                       DA7219_DTMF_REG_MAX, da7219_tonegen_dtmf_key_txt);
+
+static const char * const da7219_tonegen_swg_sel_txt[] = {
+       "Sum", "SWG1", "SWG2", "SWG1_1-Cos"
+};
+
+static const struct soc_enum da7219_tonegen_swg_sel =
+       SOC_ENUM_SINGLE(DA7219_TONE_GEN_CFG2, DA7219_SWG_SEL_SHIFT,
+                       DA7219_SWG_SEL_MAX, da7219_tonegen_swg_sel_txt);
+
+/* Output Enums */
+static const char * const da7219_dac_softmute_rate_txt[] = {
+       "1 Sample", "2 Samples", "4 Samples", "8 Samples", "16 Samples",
+       "32 Samples", "64 Samples"
+};
+
+static const struct soc_enum da7219_dac_softmute_rate =
+       SOC_ENUM_SINGLE(DA7219_DAC_FILTERS5, DA7219_DAC_SOFTMUTE_RATE_SHIFT,
+                       DA7219_DAC_SOFTMUTE_RATE_MAX,
+                       da7219_dac_softmute_rate_txt);
+
+static const char * const da7219_dac_ng_setup_time_txt[] = {
+       "256 Samples", "512 Samples", "1024 Samples", "2048 Samples"
+};
+
+static const struct soc_enum da7219_dac_ng_setup_time =
+       SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+                       DA7219_DAC_NG_SETUP_TIME_SHIFT,
+                       DA7219_DAC_NG_SETUP_TIME_MAX,
+                       da7219_dac_ng_setup_time_txt);
+
+static const char * const da7219_dac_ng_rampup_txt[] = {
+       "0.22ms/dB", "0.0138ms/dB"
+};
+
+static const struct soc_enum da7219_dac_ng_rampup_rate =
+       SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+                       DA7219_DAC_NG_RAMPUP_RATE_SHIFT,
+                       DA7219_DAC_NG_RAMP_RATE_MAX,
+                       da7219_dac_ng_rampup_txt);
+
+static const char * const da7219_dac_ng_rampdown_txt[] = {
+       "0.88ms/dB", "14.08ms/dB"
+};
+
+static const struct soc_enum da7219_dac_ng_rampdown_rate =
+       SOC_ENUM_SINGLE(DA7219_DAC_NG_SETUP_TIME,
+                       DA7219_DAC_NG_RAMPDN_RATE_SHIFT,
+                       DA7219_DAC_NG_RAMP_RATE_MAX,
+                       da7219_dac_ng_rampdown_txt);
+
+
+static const char * const da7219_cp_track_mode_txt[] = {
+       "Largest Volume", "DAC Volume", "Signal Magnitude"
+};
+
+static const unsigned int da7219_cp_track_mode_val[] = {
+       DA7219_CP_MCHANGE_LARGEST_VOL, DA7219_CP_MCHANGE_DAC_VOL,
+       DA7219_CP_MCHANGE_SIG_MAG
+};
+
+static const struct soc_enum da7219_cp_track_mode =
+       SOC_VALUE_ENUM_SINGLE(DA7219_CP_CTRL, DA7219_CP_MCHANGE_SHIFT,
+                             DA7219_CP_MCHANGE_REL_MASK, DA7219_CP_MCHANGE_MAX,
+                             da7219_cp_track_mode_txt,
+                             da7219_cp_track_mode_val);
+
+
+/*
+ * Control Functions
+ */
+
+/* Locked Kcontrol calls */
+static int da7219_volsw_locked_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       mutex_lock(&da7219->lock);
+       ret = snd_soc_get_volsw(kcontrol, ucontrol);
+       mutex_unlock(&da7219->lock);
+
+       return ret;
+}
+
+static int da7219_volsw_locked_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       mutex_lock(&da7219->lock);
+       ret = snd_soc_put_volsw(kcontrol, ucontrol);
+       mutex_unlock(&da7219->lock);
+
+       return ret;
+}
+
+static int da7219_enum_locked_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       mutex_lock(&da7219->lock);
+       ret = snd_soc_get_enum_double(kcontrol, ucontrol);
+       mutex_unlock(&da7219->lock);
+
+       return ret;
+}
+
+static int da7219_enum_locked_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       mutex_lock(&da7219->lock);
+       ret = snd_soc_put_enum_double(kcontrol, ucontrol);
+       mutex_unlock(&da7219->lock);
+
+       return ret;
+}
+
+/* ALC */
+static void da7219_alc_calib(struct snd_soc_codec *codec)
+{
+       u8 mic_ctrl, mixin_ctrl, adc_ctrl, calib_ctrl;
+
+       /* Save current state of mic control register */
+       mic_ctrl = snd_soc_read(codec, DA7219_MIC_1_CTRL);
+
+       /* Save current state of input mixer control register */
+       mixin_ctrl = snd_soc_read(codec, DA7219_MIXIN_L_CTRL);
+
+       /* Save current state of input ADC control register */
+       adc_ctrl = snd_soc_read(codec, DA7219_ADC_L_CTRL);
+
+       /* Enable then Mute MIC PGAs */
+       snd_soc_update_bits(codec, DA7219_MIC_1_CTRL, DA7219_MIC_1_AMP_EN_MASK,
+                           DA7219_MIC_1_AMP_EN_MASK);
+       snd_soc_update_bits(codec, DA7219_MIC_1_CTRL,
+                           DA7219_MIC_1_AMP_MUTE_EN_MASK,
+                           DA7219_MIC_1_AMP_MUTE_EN_MASK);
+
+       /* Enable input mixers unmuted */
+       snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL,
+                           DA7219_MIXIN_L_AMP_EN_MASK |
+                           DA7219_MIXIN_L_AMP_MUTE_EN_MASK,
+                           DA7219_MIXIN_L_AMP_EN_MASK);
+
+       /* Enable input filters unmuted */
+       snd_soc_update_bits(codec, DA7219_ADC_L_CTRL,
+                           DA7219_ADC_L_MUTE_EN_MASK | DA7219_ADC_L_EN_MASK,
+                           DA7219_ADC_L_EN_MASK);
+
+       /* Perform auto calibration */
+       snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+                           DA7219_ALC_AUTO_CALIB_EN_MASK,
+                           DA7219_ALC_AUTO_CALIB_EN_MASK);
+       do {
+               calib_ctrl = snd_soc_read(codec, DA7219_ALC_CTRL1);
+       } while (calib_ctrl & DA7219_ALC_AUTO_CALIB_EN_MASK);
+
+       /* If auto calibration fails, disable DC offset, hybrid ALC */
+       if (calib_ctrl & DA7219_ALC_CALIB_OVERFLOW_MASK) {
+               dev_warn(codec->dev,
+                        "ALC auto calibration failed with overflow\n");
+               snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+                                   DA7219_ALC_OFFSET_EN_MASK |
+                                   DA7219_ALC_SYNC_MODE_MASK, 0);
+       } else {
+               /* Enable DC offset cancellation, hybrid mode */
+               snd_soc_update_bits(codec, DA7219_ALC_CTRL1,
+                                   DA7219_ALC_OFFSET_EN_MASK |
+                                   DA7219_ALC_SYNC_MODE_MASK,
+                                   DA7219_ALC_OFFSET_EN_MASK |
+                                   DA7219_ALC_SYNC_MODE_MASK);
+       }
+
+       /* Restore input filter control register to original state */
+       snd_soc_write(codec, DA7219_ADC_L_CTRL, adc_ctrl);
+
+       /* Restore input mixer control registers to original state */
+       snd_soc_write(codec, DA7219_MIXIN_L_CTRL, mixin_ctrl);
+
+       /* Restore MIC control registers to original states */
+       snd_soc_write(codec, DA7219_MIC_1_CTRL, mic_ctrl);
+}
+
+static int da7219_mixin_gain_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       ret = snd_soc_put_volsw(kcontrol, ucontrol);
+
+       /*
+        * If ALC in operation and value of control has been updated,
+        * make sure calibrated offsets are updated.
+        */
+       if ((ret == 1) && (da7219->alc_en))
+               da7219_alc_calib(codec);
+
+       return ret;
+}
+
+static int da7219_alc_sw_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+
+       /* Force ALC offset calibration if enabling ALC */
+       if ((ucontrol->value.integer.value[0]) && (!da7219->alc_en)) {
+               da7219_alc_calib(codec);
+               da7219->alc_en = true;
+       } else {
+               da7219->alc_en = false;
+       }
+
+       return snd_soc_put_volsw(kcontrol, ucontrol);
+}
+
+/* ToneGen */
+static int da7219_tonegen_freq_get(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       struct soc_mixer_control *mixer_ctrl =
+               (struct soc_mixer_control *) kcontrol->private_value;
+       unsigned int reg = mixer_ctrl->reg;
+       u16 val;
+       int ret;
+
+       mutex_lock(&da7219->lock);
+       ret = regmap_raw_read(da7219->regmap, reg, &val, sizeof(val));
+       mutex_unlock(&da7219->lock);
+
+       if (ret)
+               return ret;
+
+       /*
+        * Frequency value spans two 8-bit registers, lower then upper byte.
+        * Therefore we need to convert to host endianness here.
+        */
+       ucontrol->value.integer.value[0] = le16_to_cpu(val);
+
+       return 0;
+}
+
+static int da7219_tonegen_freq_put(struct snd_kcontrol *kcontrol,
+                                  struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       struct soc_mixer_control *mixer_ctrl =
+               (struct soc_mixer_control *) kcontrol->private_value;
+       unsigned int reg = mixer_ctrl->reg;
+       u16 val;
+       int ret;
+
+       /*
+        * Frequency value spans two 8-bit registers, lower then upper byte.
+        * Therefore we need to convert to little endian here to align with
+        * HW registers.
+        */
+       val = cpu_to_le16(ucontrol->value.integer.value[0]);
+
+       mutex_lock(&da7219->lock);
+       ret = regmap_raw_write(da7219->regmap, reg, &val, sizeof(val));
+       mutex_unlock(&da7219->lock);
+
+       return ret;
+}
+
+
+/*
+ * KControls
+ */
+
+static const struct snd_kcontrol_new da7219_snd_controls[] = {
+       /* Mics */
+       SOC_SINGLE_TLV("Mic Volume", DA7219_MIC_1_GAIN,
+                      DA7219_MIC_1_AMP_GAIN_SHIFT, DA7219_MIC_1_AMP_GAIN_MAX,
+                      DA7219_NO_INVERT, da7219_mic_gain_tlv),
+       SOC_SINGLE("Mic Switch", DA7219_MIC_1_CTRL,
+                  DA7219_MIC_1_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+                  DA7219_INVERT),
+
+       /* Mixer Input */
+       SOC_SINGLE_EXT_TLV("Mixin Volume", DA7219_MIXIN_L_GAIN,
+                          DA7219_MIXIN_L_AMP_GAIN_SHIFT,
+                          DA7219_MIXIN_L_AMP_GAIN_MAX, DA7219_NO_INVERT,
+                          snd_soc_get_volsw, da7219_mixin_gain_put,
+                          da7219_mixin_gain_tlv),
+       SOC_SINGLE("Mixin Switch", DA7219_MIXIN_L_CTRL,
+                  DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+                  DA7219_INVERT),
+       SOC_SINGLE("Mixin Gain Ramp Switch", DA7219_MIXIN_L_CTRL,
+                  DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+                  DA7219_NO_INVERT),
+       SOC_SINGLE("Mixin ZC Gain Switch", DA7219_MIXIN_L_CTRL,
+                  DA7219_MIXIN_L_AMP_ZC_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+                  DA7219_NO_INVERT),
+
+       /* ADC */
+       SOC_SINGLE_TLV("Capture Digital Volume", DA7219_ADC_L_GAIN,
+                      DA7219_ADC_L_DIGITAL_GAIN_SHIFT,
+                      DA7219_ADC_L_DIGITAL_GAIN_MAX, DA7219_NO_INVERT,
+                      da7219_adc_dig_gain_tlv),
+       SOC_SINGLE("Capture Digital Switch", DA7219_ADC_L_CTRL,
+                  DA7219_ADC_L_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+                  DA7219_INVERT),
+       SOC_SINGLE("Capture Digital Gain Ramp Switch", DA7219_ADC_L_CTRL,
+                  DA7219_ADC_L_RAMP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+                  DA7219_NO_INVERT),
+
+       /* ALC */
+       SOC_ENUM("ALC Attack Rate", da7219_alc_attack_rate),
+       SOC_ENUM("ALC Release Rate", da7219_alc_release_rate),
+       SOC_ENUM("ALC Hold Time", da7219_alc_hold_time),
+       SOC_ENUM("ALC Envelope Attack Rate", da7219_alc_env_attack_rate),
+       SOC_ENUM("ALC Envelope Release Rate", da7219_alc_env_release_rate),
+       SOC_SINGLE_TLV("ALC Noise Threshold", DA7219_ALC_NOISE,
+                      DA7219_ALC_NOISE_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+                      DA7219_INVERT, da7219_alc_threshold_tlv),
+       SOC_SINGLE_TLV("ALC Min Threshold", DA7219_ALC_TARGET_MIN,
+                      DA7219_ALC_THRESHOLD_MIN_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+                      DA7219_INVERT, da7219_alc_threshold_tlv),
+       SOC_SINGLE_TLV("ALC Max Threshold", DA7219_ALC_TARGET_MAX,
+                      DA7219_ALC_THRESHOLD_MAX_SHIFT, DA7219_ALC_THRESHOLD_MAX,
+                      DA7219_INVERT, da7219_alc_threshold_tlv),
+       SOC_SINGLE_TLV("ALC Max Attenuation", DA7219_ALC_GAIN_LIMITS,
+                      DA7219_ALC_ATTEN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX,
+                      DA7219_NO_INVERT, da7219_alc_gain_tlv),
+       SOC_SINGLE_TLV("ALC Max Volume", DA7219_ALC_GAIN_LIMITS,
+                      DA7219_ALC_GAIN_MAX_SHIFT, DA7219_ALC_ATTEN_GAIN_MAX,
+                      DA7219_NO_INVERT, da7219_alc_gain_tlv),
+       SOC_SINGLE_RANGE_TLV("ALC Min Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS,
+                            DA7219_ALC_ANA_GAIN_MIN_SHIFT,
+                            DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX,
+                            DA7219_NO_INVERT, da7219_alc_ana_gain_tlv),
+       SOC_SINGLE_RANGE_TLV("ALC Max Analog Volume", DA7219_ALC_ANA_GAIN_LIMITS,
+                            DA7219_ALC_ANA_GAIN_MAX_SHIFT,
+                            DA7219_ALC_ANA_GAIN_MIN, DA7219_ALC_ANA_GAIN_MAX,
+                            DA7219_NO_INVERT, da7219_alc_ana_gain_tlv),
+       SOC_ENUM("ALC Anticlip Step", da7219_alc_anticlip_step),
+       SOC_SINGLE("ALC Anticlip Switch", DA7219_ALC_ANTICLIP_CTRL,
+                  DA7219_ALC_ANTIPCLIP_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+                  DA7219_NO_INVERT),
+       SOC_SINGLE_EXT("ALC Switch", DA7219_ALC_CTRL1, DA7219_ALC_EN_SHIFT,
+                      DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT,
+                      snd_soc_get_volsw, da7219_alc_sw_put),
+
+       /* Input High-Pass Filters */
+       SOC_ENUM("ADC HPF Mode", da7219_adc_hpf_mode),
+       SOC_ENUM("ADC HPF Corner Audio", da7219_adc_audio_hpf_corner),
+       SOC_ENUM("ADC HPF Corner Voice", da7219_adc_voice_hpf_corner),
+
+       /* Sidetone Filter */
+       SOC_SINGLE_TLV("Sidetone Volume", DA7219_SIDETONE_GAIN,
+                      DA7219_SIDETONE_GAIN_SHIFT, DA7219_SIDETONE_GAIN_MAX,
+                      DA7219_NO_INVERT, da7219_sidetone_gain_tlv),
+       SOC_SINGLE("Sidetone Switch", DA7219_SIDETONE_CTRL,
+                  DA7219_SIDETONE_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+                  DA7219_INVERT),
+
+       /* Tone Generator */
+       SOC_SINGLE_EXT_TLV("ToneGen Volume", DA7219_TONE_GEN_CFG2,
+                          DA7219_TONE_GEN_GAIN_SHIFT, DA7219_TONE_GEN_GAIN_MAX,
+                          DA7219_NO_INVERT, da7219_volsw_locked_get,
+                          da7219_volsw_locked_put, da7219_tonegen_gain_tlv),
+       SOC_ENUM_EXT("ToneGen DTMF Key", da7219_tonegen_dtmf_key,
+                    da7219_enum_locked_get, da7219_enum_locked_put),
+       SOC_SINGLE_EXT("ToneGen DTMF Switch", DA7219_TONE_GEN_CFG1,
+                      DA7219_DTMF_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+                      DA7219_NO_INVERT, da7219_volsw_locked_get,
+                      da7219_volsw_locked_put),
+       SOC_ENUM_EXT("ToneGen Sinewave Gen Type", da7219_tonegen_swg_sel,
+                    da7219_enum_locked_get, da7219_enum_locked_put),
+       SOC_SINGLE_EXT("ToneGen Sinewave1 Freq", DA7219_TONE_GEN_FREQ1_L,
+                      DA7219_FREQ1_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT,
+                      da7219_tonegen_freq_get, da7219_tonegen_freq_put),
+       SOC_SINGLE_EXT("ToneGen Sinewave2 Freq", DA7219_TONE_GEN_FREQ2_L,
+                      DA7219_FREQ2_L_SHIFT, DA7219_FREQ_MAX, DA7219_NO_INVERT,
+                      da7219_tonegen_freq_get, da7219_tonegen_freq_put),
+       SOC_SINGLE_EXT("ToneGen On Time", DA7219_TONE_GEN_ON_PER,
+                      DA7219_BEEP_ON_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX,
+                      DA7219_NO_INVERT, da7219_volsw_locked_get,
+                      da7219_volsw_locked_put),
+       SOC_SINGLE("ToneGen Off Time", DA7219_TONE_GEN_OFF_PER,
+                  DA7219_BEEP_OFF_PER_SHIFT, DA7219_BEEP_ON_OFF_MAX,
+                  DA7219_NO_INVERT),
+
+       /* Gain ramping */
+       SOC_ENUM("Gain Ramp Rate", da7219_gain_ramp_rate),
+
+       /* DAC High-Pass Filter */
+       SOC_ENUM_EXT("DAC HPF Mode", da7219_dac_hpf_mode,
+                    da7219_enum_locked_get, da7219_enum_locked_put),
+       SOC_ENUM("DAC HPF Corner Audio", da7219_dac_audio_hpf_corner),
+       SOC_ENUM("DAC HPF Corner Voice", da7219_dac_voice_hpf_corner),
+
+       /* DAC 5-Band Equaliser */
+       SOC_SINGLE_TLV("DAC EQ Band1 Volume", DA7219_DAC_FILTERS2,
+                      DA7219_DAC_EQ_BAND1_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+                      DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+       SOC_SINGLE_TLV("DAC EQ Band2 Volume", DA7219_DAC_FILTERS2,
+                      DA7219_DAC_EQ_BAND2_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+                      DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+       SOC_SINGLE_TLV("DAC EQ Band3 Volume", DA7219_DAC_FILTERS3,
+                      DA7219_DAC_EQ_BAND3_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+                      DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+       SOC_SINGLE_TLV("DAC EQ Band4 Volume", DA7219_DAC_FILTERS3,
+                      DA7219_DAC_EQ_BAND4_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+                      DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+       SOC_SINGLE_TLV("DAC EQ Band5 Volume", DA7219_DAC_FILTERS4,
+                      DA7219_DAC_EQ_BAND5_SHIFT, DA7219_DAC_EQ_BAND_MAX,
+                      DA7219_NO_INVERT, da7219_dac_eq_band_tlv),
+       SOC_SINGLE_EXT("DAC EQ Switch", DA7219_DAC_FILTERS4,
+                      DA7219_DAC_EQ_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+                      DA7219_NO_INVERT, da7219_volsw_locked_get,
+                      da7219_volsw_locked_put),
+
+       /* DAC Softmute */
+       SOC_ENUM("DAC Soft Mute Rate", da7219_dac_softmute_rate),
+       SOC_SINGLE_EXT("DAC Soft Mute Switch", DA7219_DAC_FILTERS5,
+                      DA7219_DAC_SOFTMUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+                      DA7219_NO_INVERT, da7219_volsw_locked_get,
+                      da7219_volsw_locked_put),
+
+       /* DAC Noise Gate */
+       SOC_ENUM("DAC NG Setup Time", da7219_dac_ng_setup_time),
+       SOC_ENUM("DAC NG Rampup Rate", da7219_dac_ng_rampup_rate),
+       SOC_ENUM("DAC NG Rampdown Rate", da7219_dac_ng_rampdown_rate),
+       SOC_SINGLE_TLV("DAC NG Off Threshold", DA7219_DAC_NG_OFF_THRESH,
+                      DA7219_DAC_NG_OFF_THRESHOLD_SHIFT,
+                      DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT,
+                      da7219_dac_ng_threshold_tlv),
+       SOC_SINGLE_TLV("DAC NG On Threshold", DA7219_DAC_NG_ON_THRESH,
+                      DA7219_DAC_NG_ON_THRESHOLD_SHIFT,
+                      DA7219_DAC_NG_THRESHOLD_MAX, DA7219_NO_INVERT,
+                      da7219_dac_ng_threshold_tlv),
+       SOC_SINGLE("DAC NG Switch", DA7219_DAC_NG_CTRL, DA7219_DAC_NG_EN_SHIFT,
+                  DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+
+       /* DACs */
+       SOC_DOUBLE_R_EXT_TLV("Playback Digital Volume", DA7219_DAC_L_GAIN,
+                            DA7219_DAC_R_GAIN, DA7219_DAC_L_DIGITAL_GAIN_SHIFT,
+                            DA7219_DAC_DIGITAL_GAIN_MAX, DA7219_NO_INVERT,
+                            da7219_volsw_locked_get, da7219_volsw_locked_put,
+                            da7219_dac_dig_gain_tlv),
+       SOC_DOUBLE_R_EXT("Playback Digital Switch", DA7219_DAC_L_CTRL,
+                        DA7219_DAC_R_CTRL, DA7219_DAC_L_MUTE_EN_SHIFT,
+                        DA7219_SWITCH_EN_MAX, DA7219_INVERT,
+                        da7219_volsw_locked_get, da7219_volsw_locked_put),
+       SOC_DOUBLE_R("Playback Digital Gain Ramp Switch", DA7219_DAC_L_CTRL,
+                    DA7219_DAC_R_CTRL, DA7219_DAC_L_RAMP_EN_SHIFT,
+                    DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+
+       /* CP */
+       SOC_ENUM("Charge Pump Track Mode", da7219_cp_track_mode),
+       SOC_SINGLE("Charge Pump Threshold", DA7219_CP_VOL_THRESHOLD1,
+                  DA7219_CP_THRESH_VDD2_SHIFT, DA7219_CP_THRESH_VDD2_MAX,
+                  DA7219_NO_INVERT),
+
+       /* Headphones */
+       SOC_DOUBLE_R_EXT_TLV("Headphone Volume", DA7219_HP_L_GAIN,
+                            DA7219_HP_R_GAIN, DA7219_HP_L_AMP_GAIN_SHIFT,
+                            DA7219_HP_AMP_GAIN_MAX, DA7219_NO_INVERT,
+                            da7219_volsw_locked_get, da7219_volsw_locked_put,
+                            da7219_hp_gain_tlv),
+       SOC_DOUBLE_R_EXT("Headphone Switch", DA7219_HP_L_CTRL, DA7219_HP_R_CTRL,
+                        DA7219_HP_L_AMP_MUTE_EN_SHIFT, DA7219_SWITCH_EN_MAX,
+                        DA7219_INVERT, da7219_volsw_locked_get,
+                        da7219_volsw_locked_put),
+       SOC_DOUBLE_R("Headphone Gain Ramp Switch", DA7219_HP_L_CTRL,
+                    DA7219_HP_R_CTRL, DA7219_HP_L_AMP_RAMP_EN_SHIFT,
+                    DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+       SOC_DOUBLE_R("Headphone ZC Gain Switch", DA7219_HP_L_CTRL,
+                    DA7219_HP_R_CTRL, DA7219_HP_L_AMP_ZC_EN_SHIFT,
+                    DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+
+/*
+ * DAPM Mux Controls
+ */
+
+static const char * const da7219_out_sel_txt[] = {
+       "ADC", "Tone Generator", "DAIL", "DAIR"
+};
+
+static const struct soc_enum da7219_out_dail_sel =
+       SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI,
+                       DA7219_DAI_L_SRC_SHIFT,
+                       DA7219_OUT_SRC_MAX,
+                       da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dail_sel_mux =
+       SOC_DAPM_ENUM("Out DAIL Mux", da7219_out_dail_sel);
+
+static const struct soc_enum da7219_out_dair_sel =
+       SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAI,
+                       DA7219_DAI_R_SRC_SHIFT,
+                       DA7219_OUT_SRC_MAX,
+                       da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dair_sel_mux =
+       SOC_DAPM_ENUM("Out DAIR Mux", da7219_out_dair_sel);
+
+static const struct soc_enum da7219_out_dacl_sel =
+       SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC,
+                       DA7219_DAC_L_SRC_SHIFT,
+                       DA7219_OUT_SRC_MAX,
+                       da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dacl_sel_mux =
+       SOC_DAPM_ENUM("Out DACL Mux", da7219_out_dacl_sel);
+
+static const struct soc_enum da7219_out_dacr_sel =
+       SOC_ENUM_SINGLE(DA7219_DIG_ROUTING_DAC,
+                       DA7219_DAC_R_SRC_SHIFT,
+                       DA7219_OUT_SRC_MAX,
+                       da7219_out_sel_txt);
+
+static const struct snd_kcontrol_new da7219_out_dacr_sel_mux =
+       SOC_DAPM_ENUM("Out DACR Mux", da7219_out_dacr_sel);
+
+
+/*
+ * DAPM Mixer Controls
+ */
+
+static const struct snd_kcontrol_new da7219_mixin_controls[] = {
+       SOC_DAPM_SINGLE("Mic Switch", DA7219_MIXIN_L_SELECT,
+                       DA7219_MIXIN_L_MIX_SELECT_SHIFT,
+                       DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+static const struct snd_kcontrol_new da7219_mixout_l_controls[] = {
+       SOC_DAPM_SINGLE("DACL Switch", DA7219_MIXOUT_L_SELECT,
+                       DA7219_MIXOUT_L_MIX_SELECT_SHIFT,
+                       DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+static const struct snd_kcontrol_new da7219_mixout_r_controls[] = {
+       SOC_DAPM_SINGLE("DACR Switch", DA7219_MIXOUT_R_SELECT,
+                       DA7219_MIXOUT_R_MIX_SELECT_SHIFT,
+                       DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),
+};
+
+#define DA7219_DMIX_ST_CTRLS(reg)                                      \
+       SOC_DAPM_SINGLE("Out FilterL Switch", reg,                      \
+                       DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT,             \
+                       DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),        \
+       SOC_DAPM_SINGLE("Out FilterR Switch", reg,                      \
+                       DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT,             \
+                       DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT),        \
+       SOC_DAPM_SINGLE("Sidetone Switch", reg,                         \
+                       DA7219_DMIX_ST_SRC_SIDETONE_SHIFT,              \
+                       DA7219_SWITCH_EN_MAX, DA7219_NO_INVERT)         \
+
+static const struct snd_kcontrol_new da7219_st_out_filtl_mix_controls[] = {
+       DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1L),
+};
+
+static const struct snd_kcontrol_new da7219_st_out_filtr_mix_controls[] = {
+       DA7219_DMIX_ST_CTRLS(DA7219_DROUTING_ST_OUTFILT_1R),
+};
+
+
+/*
+ * DAPM Events
+ */
+
+static int da7219_dai_event(struct snd_soc_dapm_widget *w,
+                           struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       u8 pll_ctrl, pll_status;
+       int i = 0;
+       bool srm_lock = false;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               if (da7219->master)
+                       /* Enable DAI clks for master mode */
+                       snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+                                           DA7219_DAI_CLK_EN_MASK,
+                                           DA7219_DAI_CLK_EN_MASK);
+
+               /* PC synchronised to DAI */
+               snd_soc_update_bits(codec, DA7219_PC_COUNT,
+                                   DA7219_PC_FREERUN_MASK, 0);
+
+               /* Slave mode, if SRM not enabled no need for status checks */
+               pll_ctrl = snd_soc_read(codec, DA7219_PLL_CTRL);
+               if ((pll_ctrl & DA7219_PLL_MODE_MASK) != DA7219_PLL_MODE_SRM)
+                       return 0;
+
+               /* Check SRM has locked */
+               do {
+                       pll_status = snd_soc_read(codec, DA7219_PLL_SRM_STS);
+                       if (pll_status & DA7219_PLL_SRM_STS_SRM_LOCK) {
+                               srm_lock = true;
+                       } else {
+                               ++i;
+                               msleep(50);
+                       }
+               } while ((i < DA7219_SRM_CHECK_RETRIES) & (!srm_lock));
+
+               if (!srm_lock)
+                       dev_warn(codec->dev, "SRM failed to lock\n");
+
+               return 0;
+       case SND_SOC_DAPM_POST_PMD:
+               /* PC free-running */
+               snd_soc_update_bits(codec, DA7219_PC_COUNT,
+                                   DA7219_PC_FREERUN_MASK,
+                                   DA7219_PC_FREERUN_MASK);
+
+               /* Disable DAI clks if in master mode */
+               if (da7219->master)
+                       snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+                                           DA7219_DAI_CLK_EN_MASK, 0);
+               return 0;
+       default:
+               return -EINVAL;
+       }
+}
+
+
+/*
+ * DAPM Widgets
+ */
+
+static const struct snd_soc_dapm_widget da7219_dapm_widgets[] = {
+       /* Input Supplies */
+       SND_SOC_DAPM_SUPPLY("Mic Bias", DA7219_MICBIAS_CTRL,
+                           DA7219_MICBIAS1_EN_SHIFT, DA7219_NO_INVERT,
+                           NULL, 0),
+
+       /* Inputs */
+       SND_SOC_DAPM_INPUT("MIC"),
+
+       /* Input PGAs */
+       SND_SOC_DAPM_PGA("Mic PGA", DA7219_MIC_1_CTRL,
+                        DA7219_MIC_1_AMP_EN_SHIFT, DA7219_NO_INVERT,
+                        NULL, 0),
+       SND_SOC_DAPM_PGA("Mixin PGA", DA7219_MIXIN_L_CTRL,
+                        DA7219_MIXIN_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+                        NULL, 0),
+
+       /* Input Filters */
+       SND_SOC_DAPM_ADC("ADC", NULL, DA7219_ADC_L_CTRL, DA7219_ADC_L_EN_SHIFT,
+                        DA7219_NO_INVERT),
+
+       /* Tone Generator */
+       SND_SOC_DAPM_SIGGEN("TONE"),
+       SND_SOC_DAPM_PGA("Tone Generator", DA7219_TONE_GEN_CFG1,
+                        DA7219_START_STOPN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+
+       /* Sidetone Input */
+       SND_SOC_DAPM_ADC("Sidetone Filter", NULL, DA7219_SIDETONE_CTRL,
+                        DA7219_SIDETONE_EN_SHIFT, DA7219_NO_INVERT),
+
+       /* Input Mixer Supply */
+       SND_SOC_DAPM_SUPPLY("Mixer In Supply", DA7219_MIXIN_L_CTRL,
+                           DA7219_MIXIN_L_MIX_EN_SHIFT, DA7219_NO_INVERT,
+                           NULL, 0),
+
+       /* Input Mixer */
+       SND_SOC_DAPM_MIXER("Mixer In", SND_SOC_NOPM, 0, 0,
+                          da7219_mixin_controls,
+                          ARRAY_SIZE(da7219_mixin_controls)),
+
+       /* Input Muxes */
+       SND_SOC_DAPM_MUX("Out DAIL Mux", SND_SOC_NOPM, 0, 0,
+                        &da7219_out_dail_sel_mux),
+       SND_SOC_DAPM_MUX("Out DAIR Mux", SND_SOC_NOPM, 0, 0,
+                        &da7219_out_dair_sel_mux),
+
+       /* DAI Supply */
+       SND_SOC_DAPM_SUPPLY("DAI", DA7219_DAI_CTRL, DA7219_DAI_EN_SHIFT,
+                           DA7219_NO_INVERT, da7219_dai_event,
+                           SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+       /* DAI */
+       SND_SOC_DAPM_AIF_OUT("DAIOUT", "Capture", 0, SND_SOC_NOPM, 0, 0),
+       SND_SOC_DAPM_AIF_IN("DAIIN", "Playback", 0, SND_SOC_NOPM, 0, 0),
+
+       /* Output Muxes */
+       SND_SOC_DAPM_MUX("Out DACL Mux", SND_SOC_NOPM, 0, 0,
+                        &da7219_out_dacl_sel_mux),
+       SND_SOC_DAPM_MUX("Out DACR Mux", SND_SOC_NOPM, 0, 0,
+                        &da7219_out_dacr_sel_mux),
+
+       /* Output Mixers */
+       SND_SOC_DAPM_MIXER("Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+                          da7219_mixout_l_controls,
+                          ARRAY_SIZE(da7219_mixout_l_controls)),
+       SND_SOC_DAPM_MIXER("Mixer Out FilterR", SND_SOC_NOPM, 0, 0,
+                          da7219_mixout_r_controls,
+                          ARRAY_SIZE(da7219_mixout_r_controls)),
+
+       /* Sidetone Mixers */
+       SND_SOC_DAPM_MIXER("ST Mixer Out FilterL", SND_SOC_NOPM, 0, 0,
+                          da7219_st_out_filtl_mix_controls,
+                          ARRAY_SIZE(da7219_st_out_filtl_mix_controls)),
+       SND_SOC_DAPM_MIXER("ST Mixer Out FilterR", SND_SOC_NOPM, 0,
+                          0, da7219_st_out_filtr_mix_controls,
+                          ARRAY_SIZE(da7219_st_out_filtr_mix_controls)),
+
+       /* DACs */
+       SND_SOC_DAPM_DAC("DACL", NULL, DA7219_DAC_L_CTRL, DA7219_DAC_L_EN_SHIFT,
+                        DA7219_NO_INVERT),
+       SND_SOC_DAPM_DAC("DACR", NULL, DA7219_DAC_R_CTRL, DA7219_DAC_R_EN_SHIFT,
+                        DA7219_NO_INVERT),
+
+       /* Output PGAs */
+       SND_SOC_DAPM_PGA("Mixout Left PGA", DA7219_MIXOUT_L_CTRL,
+                        DA7219_MIXOUT_L_AMP_EN_SHIFT, DA7219_NO_INVERT,
+                        NULL, 0),
+       SND_SOC_DAPM_PGA("Mixout Right PGA", DA7219_MIXOUT_R_CTRL,
+                        DA7219_MIXOUT_R_AMP_EN_SHIFT, DA7219_NO_INVERT,
+                        NULL, 0),
+       SND_SOC_DAPM_PGA("Headphone Left PGA", DA7219_HP_L_CTRL,
+                        DA7219_HP_L_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+       SND_SOC_DAPM_PGA("Headphone Right PGA", DA7219_HP_R_CTRL,
+                        DA7219_HP_R_AMP_EN_SHIFT, DA7219_NO_INVERT, NULL, 0),
+
+       /* Output Supplies */
+       SND_SOC_DAPM_SUPPLY("Charge Pump", DA7219_CP_CTRL, DA7219_CP_EN_SHIFT,
+                           DA7219_NO_INVERT, NULL, 0),
+
+       /* Outputs */
+       SND_SOC_DAPM_OUTPUT("HPL"),
+       SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+
+/*
+ * DAPM Mux Routes
+ */
+
+#define DA7219_OUT_DAI_MUX_ROUTES(name)                        \
+       {name, "ADC", "Mixer In"},                      \
+       {name, "Tone Generator", "Tone Generator"},     \
+       {name, "DAIL", "DAIOUT"},                       \
+       {name, "DAIR", "DAIOUT"}
+
+#define DA7219_OUT_DAC_MUX_ROUTES(name)                        \
+       {name, "ADC", "Mixer In"},                      \
+       {name, "Tone Generator", "Tone Generator"},             \
+       {name, "DAIL", "DAIIN"},                        \
+       {name, "DAIR", "DAIIN"}
+
+/*
+ * DAPM Mixer Routes
+ */
+
+#define DA7219_DMIX_ST_ROUTES(name)                            \
+       {name, "Out FilterL Switch", "Mixer Out FilterL"},      \
+       {name, "Out FilterR Switch", "Mixer Out FilterR"},      \
+       {name, "Sidetone Switch", "Sidetone Filter"}
+
+
+/*
+ * DAPM audio route definition
+ */
+
+static const struct snd_soc_dapm_route da7219_audio_map[] = {
+       /* Input paths */
+       {"MIC", NULL, "Mic Bias"},
+       {"Mic PGA", NULL, "MIC"},
+       {"Mixin PGA", NULL, "Mic PGA"},
+       {"ADC", NULL, "Mixin PGA"},
+
+       {"Sidetone Filter", NULL, "ADC"},
+       {"Mixer In", NULL, "Mixer In Supply"},
+       {"Mixer In", "Mic Switch", "ADC"},
+
+       {"Tone Generator", NULL, "TONE"},
+
+       DA7219_OUT_DAI_MUX_ROUTES("Out DAIL Mux"),
+       DA7219_OUT_DAI_MUX_ROUTES("Out DAIR Mux"),
+
+       {"DAIOUT", NULL, "Out DAIL Mux"},
+       {"DAIOUT", NULL, "Out DAIR Mux"},
+       {"DAIOUT", NULL, "DAI"},
+
+       /* Output paths */
+       {"DAIIN", NULL, "DAI"},
+
+       DA7219_OUT_DAC_MUX_ROUTES("Out DACL Mux"),
+       DA7219_OUT_DAC_MUX_ROUTES("Out DACR Mux"),
+
+       {"Mixer Out FilterL", "DACL Switch", "Out DACL Mux"},
+       {"Mixer Out FilterR", "DACR Switch", "Out DACR Mux"},
+
+       DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterL"),
+       DA7219_DMIX_ST_ROUTES("ST Mixer Out FilterR"),
+
+       {"DACL", NULL, "ST Mixer Out FilterL"},
+       {"DACR", NULL, "ST Mixer Out FilterR"},
+
+       {"Mixout Left PGA", NULL, "DACL"},
+       {"Mixout Right PGA", NULL, "DACR"},
+
+       {"Headphone Left PGA", NULL, "Mixout Left PGA"},
+       {"Headphone Right PGA", NULL, "Mixout Right PGA"},
+
+       {"HPL", NULL, "Headphone Left PGA"},
+       {"HPR", NULL, "Headphone Right PGA"},
+
+       {"HPL", NULL, "Charge Pump"},
+       {"HPR", NULL, "Charge Pump"},
+};
+
+
+/*
+ * DAI operations
+ */
+
+static int da7219_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 da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       int ret = 0;
+
+       if ((da7219->clk_src == clk_id) && (da7219->mclk_rate == freq))
+               return 0;
+
+       if (((freq < 2000000) && (freq != 32768)) || (freq > 54000000)) {
+               dev_err(codec_dai->dev, "Unsupported MCLK value %d\n",
+                       freq);
+               return -EINVAL;
+       }
+
+       switch (clk_id) {
+       case DA7219_CLKSRC_MCLK_SQR:
+               snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+                                   DA7219_PLL_MCLK_SQR_EN_MASK,
+                                   DA7219_PLL_MCLK_SQR_EN_MASK);
+               break;
+       case DA7219_CLKSRC_MCLK:
+               snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+                                   DA7219_PLL_MCLK_SQR_EN_MASK, 0);
+               break;
+       default:
+               dev_err(codec_dai->dev, "Unknown clock source %d\n", clk_id);
+               return -EINVAL;
+       }
+
+       da7219->clk_src = clk_id;
+
+       if (da7219->mclk) {
+               freq = clk_round_rate(da7219->mclk, freq);
+               ret = clk_set_rate(da7219->mclk, freq);
+               if (ret) {
+                       dev_err(codec_dai->dev, "Failed to set clock rate %d\n",
+                               freq);
+                       return ret;
+               }
+       }
+
+       da7219->mclk_rate = freq;
+
+       return 0;
+}
+
+static int da7219_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
+                             int source, unsigned int fref, unsigned int fout)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+       u8 pll_ctrl, indiv_bits, indiv;
+       u8 pll_frac_top, pll_frac_bot, pll_integer;
+       u32 freq_ref;
+       u64 frac_div;
+
+       /* Verify 32KHz, 2MHz - 54MHz MCLK provided, and set input divider */
+       if (da7219->mclk_rate == 32768) {
+               indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
+               indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
+       } else if (da7219->mclk_rate < 2000000) {
+               dev_err(codec->dev, "PLL input clock %d below valid range\n",
+                       da7219->mclk_rate);
+               return -EINVAL;
+       } else if (da7219->mclk_rate <= 5000000) {
+               indiv_bits = DA7219_PLL_INDIV_2_5_MHZ;
+               indiv = DA7219_PLL_INDIV_2_5_MHZ_VAL;
+       } else if (da7219->mclk_rate <= 10000000) {
+               indiv_bits = DA7219_PLL_INDIV_5_10_MHZ;
+               indiv = DA7219_PLL_INDIV_5_10_MHZ_VAL;
+       } else if (da7219->mclk_rate <= 20000000) {
+               indiv_bits = DA7219_PLL_INDIV_10_20_MHZ;
+               indiv = DA7219_PLL_INDIV_10_20_MHZ_VAL;
+       } else if (da7219->mclk_rate <= 40000000) {
+               indiv_bits = DA7219_PLL_INDIV_20_40_MHZ;
+               indiv = DA7219_PLL_INDIV_20_40_MHZ_VAL;
+       } else if (da7219->mclk_rate <= 54000000) {
+               indiv_bits = DA7219_PLL_INDIV_40_54_MHZ;
+               indiv = DA7219_PLL_INDIV_40_54_MHZ_VAL;
+       } else {
+               dev_err(codec->dev, "PLL input clock %d above valid range\n",
+                       da7219->mclk_rate);
+               return -EINVAL;
+       }
+       freq_ref = (da7219->mclk_rate / indiv);
+       pll_ctrl = indiv_bits;
+
+       /* Configure PLL */
+       switch (source) {
+       case DA7219_SYSCLK_MCLK:
+               pll_ctrl |= DA7219_PLL_MODE_BYPASS;
+               snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+                                   DA7219_PLL_INDIV_MASK |
+                                   DA7219_PLL_MODE_MASK, pll_ctrl);
+               return 0;
+       case DA7219_SYSCLK_PLL:
+               pll_ctrl |= DA7219_PLL_MODE_NORMAL;
+               break;
+       case DA7219_SYSCLK_PLL_SRM:
+               pll_ctrl |= DA7219_PLL_MODE_SRM;
+               break;
+       case DA7219_SYSCLK_PLL_32KHZ:
+               pll_ctrl |= DA7219_PLL_MODE_32KHZ;
+               break;
+       default:
+               dev_err(codec->dev, "Invalid PLL config\n");
+               return -EINVAL;
+       }
+
+       /* Calculate dividers for PLL */
+       pll_integer = fout / freq_ref;
+       frac_div = (u64)(fout % freq_ref) * 8192ULL;
+       do_div(frac_div, freq_ref);
+       pll_frac_top = (frac_div >> DA7219_BYTE_SHIFT) & DA7219_BYTE_MASK;
+       pll_frac_bot = (frac_div) & DA7219_BYTE_MASK;
+
+       /* Write PLL config & dividers */
+       snd_soc_write(codec, DA7219_PLL_FRAC_TOP, pll_frac_top);
+       snd_soc_write(codec, DA7219_PLL_FRAC_BOT, pll_frac_bot);
+       snd_soc_write(codec, DA7219_PLL_INTEGER, pll_integer);
+       snd_soc_update_bits(codec, DA7219_PLL_CTRL,
+                           DA7219_PLL_INDIV_MASK | DA7219_PLL_MODE_MASK,
+                           pll_ctrl);
+
+       return 0;
+}
+
+static int da7219_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       u8 dai_clk_mode = 0, dai_ctrl = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               da7219->master = true;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               da7219->master = false;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_NB_IF:
+               dai_clk_mode |= DA7219_DAI_WCLK_POL_INV;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               dai_clk_mode |= DA7219_DAI_CLK_POL_INV;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               dai_clk_mode |= DA7219_DAI_WCLK_POL_INV |
+                               DA7219_DAI_CLK_POL_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               dai_ctrl |= DA7219_DAI_FORMAT_I2S;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               dai_ctrl |= DA7219_DAI_FORMAT_LEFT_J;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               dai_ctrl |= DA7219_DAI_FORMAT_RIGHT_J;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               dai_ctrl |= DA7219_DAI_FORMAT_DSP;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* By default 64 BCLKs per WCLK is supported */
+       dai_clk_mode |= DA7219_DAI_BCLKS_PER_WCLK_64;
+
+       snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+                           DA7219_DAI_BCLKS_PER_WCLK_MASK |
+                           DA7219_DAI_CLK_POL_MASK | DA7219_DAI_WCLK_POL_MASK,
+                           dai_clk_mode);
+       snd_soc_update_bits(codec, DA7219_DAI_CTRL, DA7219_DAI_FORMAT_MASK,
+                           dai_ctrl);
+
+       return 0;
+}
+
+static int da7219_set_dai_tdm_slot(struct snd_soc_dai *dai,
+                                  unsigned int tx_mask, unsigned int rx_mask,
+                                  int slots, int slot_width)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       u8 dai_bclks_per_wclk;
+       u16 offset;
+       u32 frame_size;
+
+       /* No channels enabled so disable TDM, revert to 64-bit frames */
+       if (!tx_mask) {
+               snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL,
+                                   DA7219_DAI_TDM_CH_EN_MASK |
+                                   DA7219_DAI_TDM_MODE_EN_MASK, 0);
+               snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+                                   DA7219_DAI_BCLKS_PER_WCLK_MASK,
+                                   DA7219_DAI_BCLKS_PER_WCLK_64);
+               return 0;
+       }
+
+       /* Check we have valid slots */
+       if (fls(tx_mask) > DA7219_DAI_TDM_MAX_SLOTS) {
+               dev_err(codec->dev, "Invalid number of slots, max = %d\n",
+                       DA7219_DAI_TDM_MAX_SLOTS);
+               return -EINVAL;
+       }
+
+       /* Check we have a valid offset given */
+       if (rx_mask > DA7219_DAI_OFFSET_MAX) {
+               dev_err(codec->dev, "Invalid slot offset, max = %d\n",
+                       DA7219_DAI_OFFSET_MAX);
+               return -EINVAL;
+       }
+
+       /* Calculate & validate frame size based on slot info provided. */
+       frame_size = slots * slot_width;
+       switch (frame_size) {
+       case 32:
+               dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_32;
+               break;
+       case 64:
+               dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_64;
+               break;
+       case 128:
+               dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_128;
+               break;
+       case 256:
+               dai_bclks_per_wclk = DA7219_DAI_BCLKS_PER_WCLK_256;
+               break;
+       default:
+               dev_err(codec->dev, "Invalid frame size %d\n", frame_size);
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, DA7219_DAI_CLK_MODE,
+                           DA7219_DAI_BCLKS_PER_WCLK_MASK,
+                           dai_bclks_per_wclk);
+
+       offset = cpu_to_le16(rx_mask);
+       regmap_bulk_write(da7219->regmap, DA7219_DAI_OFFSET_LOWER,
+                         &offset, sizeof(offset));
+
+       snd_soc_update_bits(codec, DA7219_DAI_TDM_CTRL,
+                           DA7219_DAI_TDM_CH_EN_MASK |
+                           DA7219_DAI_TDM_MODE_EN_MASK,
+                           (tx_mask << DA7219_DAI_TDM_CH_EN_SHIFT) |
+                           DA7219_DAI_TDM_MODE_EN_MASK);
+
+       return 0;
+}
+
+static int da7219_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;
+       u8 dai_ctrl = 0, fs;
+       unsigned int channels;
+
+       switch (params_width(params)) {
+       case 16:
+               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S16_LE;
+               break;
+       case 20:
+               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S20_LE;
+               break;
+       case 24:
+               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S24_LE;
+               break;
+       case 32:
+               dai_ctrl |= DA7219_DAI_WORD_LENGTH_S32_LE;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       channels = params_channels(params);
+       if ((channels < 1) | (channels > DA7219_DAI_CH_NUM_MAX)) {
+               dev_err(codec->dev,
+                       "Invalid number of channels, only 1 to %d supported\n",
+                       DA7219_DAI_CH_NUM_MAX);
+               return -EINVAL;
+       }
+       dai_ctrl |= channels << DA7219_DAI_CH_NUM_SHIFT;
+
+       switch (params_rate(params)) {
+       case 8000:
+               fs = DA7219_SR_8000;
+               break;
+       case 11025:
+               fs = DA7219_SR_11025;
+               break;
+       case 12000:
+               fs = DA7219_SR_12000;
+               break;
+       case 16000:
+               fs = DA7219_SR_16000;
+               break;
+       case 22050:
+               fs = DA7219_SR_22050;
+               break;
+       case 24000:
+               fs = DA7219_SR_24000;
+               break;
+       case 32000:
+               fs = DA7219_SR_32000;
+               break;
+       case 44100:
+               fs = DA7219_SR_44100;
+               break;
+       case 48000:
+               fs = DA7219_SR_48000;
+               break;
+       case 88200:
+               fs = DA7219_SR_88200;
+               break;
+       case 96000:
+               fs = DA7219_SR_96000;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       snd_soc_update_bits(codec, DA7219_DAI_CTRL,
+                           DA7219_DAI_WORD_LENGTH_MASK |
+                           DA7219_DAI_CH_NUM_MASK,
+                           dai_ctrl);
+       snd_soc_write(codec, DA7219_SR, fs);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops da7219_dai_ops = {
+       .hw_params      = da7219_hw_params,
+       .set_sysclk     = da7219_set_dai_sysclk,
+       .set_pll        = da7219_set_dai_pll,
+       .set_fmt        = da7219_set_dai_fmt,
+       .set_tdm_slot   = da7219_set_dai_tdm_slot,
+};
+
+#define DA7219_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver da7219_dai = {
+       .name = "da7219-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 1,
+               .channels_max = DA7219_DAI_CH_NUM_MAX,
+               .rates = SNDRV_PCM_RATE_8000_96000,
+               .formats = DA7219_FORMATS,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 1,
+               .channels_max = DA7219_DAI_CH_NUM_MAX,
+               .rates = SNDRV_PCM_RATE_8000_96000,
+               .formats = DA7219_FORMATS,
+       },
+       .ops = &da7219_dai_ops,
+       .symmetric_rates = 1,
+       .symmetric_channels = 1,
+       .symmetric_samplebits = 1,
+};
+
+
+/*
+ * DT
+ */
+
+static const struct of_device_id da7219_of_match[] = {
+       { .compatible = "dlg,da7219", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, da7219_of_match);
+
+static enum da7219_ldo_lvl_sel da7219_of_ldo_lvl(struct snd_soc_codec *codec,
+                                                u32 val)
+{
+       switch (val) {
+       case 1050:
+               return DA7219_LDO_LVL_SEL_1_05V;
+       case 1100:
+               return DA7219_LDO_LVL_SEL_1_10V;
+       case 1200:
+               return DA7219_LDO_LVL_SEL_1_20V;
+       case 1400:
+               return DA7219_LDO_LVL_SEL_1_40V;
+       default:
+               dev_warn(codec->dev, "Invalid LDO level");
+               return DA7219_LDO_LVL_SEL_1_05V;
+       }
+}
+
+static enum da7219_micbias_voltage
+       da7219_of_micbias_lvl(struct snd_soc_codec *codec, u32 val)
+{
+       switch (val) {
+       case 1800:
+               return DA7219_MICBIAS_1_8V;
+       case 2000:
+               return DA7219_MICBIAS_2_0V;
+       case 2200:
+               return DA7219_MICBIAS_2_2V;
+       case 2400:
+               return DA7219_MICBIAS_2_4V;
+       case 2600:
+               return DA7219_MICBIAS_2_6V;
+       default:
+               dev_warn(codec->dev, "Invalid micbias level");
+               return DA7219_MICBIAS_2_2V;
+       }
+}
+
+static enum da7219_mic_amp_in_sel
+       da7219_of_mic_amp_in_sel(struct snd_soc_codec *codec, const char *str)
+{
+       if (!strcmp(str, "diff")) {
+               return DA7219_MIC_AMP_IN_SEL_DIFF;
+       } else if (!strcmp(str, "se_p")) {
+               return DA7219_MIC_AMP_IN_SEL_SE_P;
+       } else if (!strcmp(str, "se_n")) {
+               return DA7219_MIC_AMP_IN_SEL_SE_N;
+       } else {
+               dev_warn(codec->dev, "Invalid mic input type selection");
+               return DA7219_MIC_AMP_IN_SEL_DIFF;
+       }
+}
+
+static struct da7219_pdata *da7219_of_to_pdata(struct snd_soc_codec *codec)
+{
+       struct device_node *np = codec->dev->of_node;
+       struct da7219_pdata *pdata;
+       const char *of_str;
+       u32 of_val32;
+
+       pdata = devm_kzalloc(codec->dev, sizeof(*pdata), GFP_KERNEL);
+       if (!pdata)
+               return NULL;
+
+       if (of_property_read_u32(np, "dlg,ldo-lvl", &of_val32) >= 0)
+               pdata->ldo_lvl_sel = da7219_of_ldo_lvl(codec, of_val32);
+
+       if (of_property_read_u32(np, "dlg,micbias-lvl", &of_val32) >= 0)
+               pdata->micbias_lvl = da7219_of_micbias_lvl(codec, of_val32);
+       else
+               pdata->micbias_lvl = DA7219_MICBIAS_2_2V;
+
+       if (!of_property_read_string(np, "dlg,mic-amp-in-sel", &of_str))
+               pdata->mic_amp_in_sel = da7219_of_mic_amp_in_sel(codec, of_str);
+       else
+               pdata->mic_amp_in_sel = DA7219_MIC_AMP_IN_SEL_DIFF;
+
+       return pdata;
+}
+
+
+/*
+ * Codec driver functions
+ */
+
+static int da7219_set_bias_level(struct snd_soc_codec *codec,
+                                enum snd_soc_bias_level level)
+{
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+       case SND_SOC_BIAS_PREPARE:
+               break;
+       case SND_SOC_BIAS_STANDBY:
+               if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+                       /* MCLK */
+                       if (da7219->mclk) {
+                               ret = clk_prepare_enable(da7219->mclk);
+                               if (ret) {
+                                       dev_err(codec->dev,
+                                               "Failed to enable mclk\n");
+                                       return ret;
+                               }
+                       }
+
+                       /* Master bias */
+                       snd_soc_update_bits(codec, DA7219_REFERENCES,
+                                           DA7219_BIAS_EN_MASK,
+                                           DA7219_BIAS_EN_MASK);
+
+                       /* Enable Internal Digital LDO */
+                       snd_soc_update_bits(codec, DA7219_LDO_CTRL,
+                                           DA7219_LDO_EN_MASK,
+                                           DA7219_LDO_EN_MASK);
+               }
+               break;
+       case SND_SOC_BIAS_OFF:
+               /* Only disable if jack detection not active */
+               if (!da7219->aad->jack) {
+                       /* Bypass Internal Digital LDO */
+                       snd_soc_update_bits(codec, DA7219_LDO_CTRL,
+                                           DA7219_LDO_EN_MASK, 0);
+
+                       /* Master bias */
+                       snd_soc_update_bits(codec, DA7219_REFERENCES,
+                                           DA7219_BIAS_EN_MASK, 0);
+               }
+
+               /* MCLK */
+               if (da7219->mclk)
+                       clk_disable_unprepare(da7219->mclk);
+               break;
+       }
+
+       return 0;
+}
+
+static const char *da7219_supply_names[DA7219_NUM_SUPPLIES] = {
+       [DA7219_SUPPLY_VDD] = "VDD",
+       [DA7219_SUPPLY_VDDMIC] = "VDDMIC",
+       [DA7219_SUPPLY_VDDIO] = "VDDIO",
+};
+
+static int da7219_handle_supplies(struct snd_soc_codec *codec)
+{
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       struct regulator *vddio;
+       u8 io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V;
+       int i, ret;
+
+       /* Get required supplies */
+       for (i = 0; i < DA7219_NUM_SUPPLIES; ++i)
+               da7219->supplies[i].supply = da7219_supply_names[i];
+
+       ret = devm_regulator_bulk_get(codec->dev, DA7219_NUM_SUPPLIES,
+                                     da7219->supplies);
+       if (ret) {
+               dev_err(codec->dev, "Failed to get supplies");
+               return ret;
+       }
+
+       /* Determine VDDIO voltage provided */
+       vddio = da7219->supplies[DA7219_SUPPLY_VDDIO].consumer;
+       ret = regulator_get_voltage(vddio);
+       if (ret < 1200000)
+               dev_warn(codec->dev, "Invalid VDDIO voltage\n");
+       else if (ret < 2800000)
+               io_voltage_lvl = DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V;
+
+       /* Enable main supplies */
+       ret = regulator_bulk_enable(DA7219_NUM_SUPPLIES, da7219->supplies);
+       if (ret) {
+               dev_err(codec->dev, "Failed to enable supplies");
+               return ret;
+       }
+
+       /* Ensure device in active mode */
+       snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, DA7219_SYSTEM_ACTIVE_MASK);
+
+       /* Update IO voltage level range */
+       snd_soc_write(codec, DA7219_IO_CTRL, io_voltage_lvl);
+
+       return 0;
+}
+
+static void da7219_handle_pdata(struct snd_soc_codec *codec)
+{
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       struct da7219_pdata *pdata = da7219->pdata;
+
+       if (pdata) {
+               u8 micbias_lvl = 0;
+
+               /* Internal LDO */
+               switch (pdata->ldo_lvl_sel) {
+               case DA7219_LDO_LVL_SEL_1_05V:
+               case DA7219_LDO_LVL_SEL_1_10V:
+               case DA7219_LDO_LVL_SEL_1_20V:
+               case DA7219_LDO_LVL_SEL_1_40V:
+                       snd_soc_update_bits(codec, DA7219_LDO_CTRL,
+                                           DA7219_LDO_LEVEL_SELECT_MASK,
+                                           (pdata->ldo_lvl_sel <<
+                                            DA7219_LDO_LEVEL_SELECT_SHIFT));
+                       break;
+               }
+
+               /* Mic Bias voltages */
+               switch (pdata->micbias_lvl) {
+               case DA7219_MICBIAS_1_8V:
+               case DA7219_MICBIAS_2_0V:
+               case DA7219_MICBIAS_2_2V:
+               case DA7219_MICBIAS_2_4V:
+               case DA7219_MICBIAS_2_6V:
+                       micbias_lvl |= (pdata->micbias_lvl <<
+                                       DA7219_MICBIAS1_LEVEL_SHIFT);
+                       break;
+               }
+
+               snd_soc_write(codec, DA7219_MICBIAS_CTRL, micbias_lvl);
+
+               /* Mic */
+               switch (pdata->mic_amp_in_sel) {
+               case DA7219_MIC_AMP_IN_SEL_DIFF:
+               case DA7219_MIC_AMP_IN_SEL_SE_P:
+               case DA7219_MIC_AMP_IN_SEL_SE_N:
+                       snd_soc_write(codec, DA7219_MIC_1_SELECT,
+                                     pdata->mic_amp_in_sel);
+                       break;
+               }
+       }
+}
+
+static int da7219_probe(struct snd_soc_codec *codec)
+{
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       mutex_init(&da7219->lock);
+
+       /* Regulator configuration */
+       ret = da7219_handle_supplies(codec);
+       if (ret)
+               return ret;
+
+       /* Handle DT/Platform data */
+       if (codec->dev->of_node)
+               da7219->pdata = da7219_of_to_pdata(codec);
+       else
+               da7219->pdata = dev_get_platdata(codec->dev);
+
+       da7219_handle_pdata(codec);
+
+       /* Check if MCLK provided */
+       da7219->mclk = devm_clk_get(codec->dev, "mclk");
+       if (IS_ERR(da7219->mclk)) {
+               if (PTR_ERR(da7219->mclk) != -ENOENT)
+                       return PTR_ERR(da7219->mclk);
+               else
+                       da7219->mclk = NULL;
+       }
+
+       /* Default PC counter to free-running */
+       snd_soc_update_bits(codec, DA7219_PC_COUNT, DA7219_PC_FREERUN_MASK,
+                           DA7219_PC_FREERUN_MASK);
+
+       /* Default gain ramping */
+       snd_soc_update_bits(codec, DA7219_MIXIN_L_CTRL,
+                           DA7219_MIXIN_L_AMP_RAMP_EN_MASK,
+                           DA7219_MIXIN_L_AMP_RAMP_EN_MASK);
+       snd_soc_update_bits(codec, DA7219_ADC_L_CTRL, DA7219_ADC_L_RAMP_EN_MASK,
+                           DA7219_ADC_L_RAMP_EN_MASK);
+       snd_soc_update_bits(codec, DA7219_DAC_L_CTRL, DA7219_DAC_L_RAMP_EN_MASK,
+                           DA7219_DAC_L_RAMP_EN_MASK);
+       snd_soc_update_bits(codec, DA7219_DAC_R_CTRL, DA7219_DAC_R_RAMP_EN_MASK,
+                           DA7219_DAC_R_RAMP_EN_MASK);
+       snd_soc_update_bits(codec, DA7219_HP_L_CTRL,
+                           DA7219_HP_L_AMP_RAMP_EN_MASK,
+                           DA7219_HP_L_AMP_RAMP_EN_MASK);
+       snd_soc_update_bits(codec, DA7219_HP_R_CTRL,
+                           DA7219_HP_R_AMP_RAMP_EN_MASK,
+                           DA7219_HP_R_AMP_RAMP_EN_MASK);
+
+       /* Default infinite tone gen, start/stop by Kcontrol */
+       snd_soc_write(codec, DA7219_TONE_GEN_CYCLES, DA7219_BEEP_CYCLES_MASK);
+
+       /* Initialise AAD block */
+       return da7219_aad_init(codec);
+}
+
+static int da7219_remove(struct snd_soc_codec *codec)
+{
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+       da7219_aad_exit(codec);
+
+       /* Supplies */
+       return regulator_bulk_disable(DA7219_NUM_SUPPLIES, da7219->supplies);
+}
+
+#ifdef CONFIG_PM
+static int da7219_suspend(struct snd_soc_codec *codec)
+{
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+       snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_OFF);
+
+       /* Put device into standby mode if jack detection disabled */
+       if (!da7219->aad->jack)
+               snd_soc_write(codec, DA7219_SYSTEM_ACTIVE, 0);
+
+       return 0;
+}
+
+static int da7219_resume(struct snd_soc_codec *codec)
+{
+       struct da7219_priv *da7219 = snd_soc_codec_get_drvdata(codec);
+
+       /* Put device into active mode if previously pushed to standby */
+       if (!da7219->aad->jack)
+               snd_soc_write(codec, DA7219_SYSTEM_ACTIVE,
+                             DA7219_SYSTEM_ACTIVE_MASK);
+
+       snd_soc_codec_force_bias_level(codec, SND_SOC_BIAS_STANDBY);
+
+       return 0;
+}
+#else
+#define da7219_suspend NULL
+#define da7219_resume NULL
+#endif
+
+static struct snd_soc_codec_driver soc_codec_dev_da7219 = {
+       .probe                  = da7219_probe,
+       .remove                 = da7219_remove,
+       .suspend                = da7219_suspend,
+       .resume                 = da7219_resume,
+       .set_bias_level         = da7219_set_bias_level,
+
+       .controls               = da7219_snd_controls,
+       .num_controls           = ARRAY_SIZE(da7219_snd_controls),
+
+       .dapm_widgets           = da7219_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(da7219_dapm_widgets),
+       .dapm_routes            = da7219_audio_map,
+       .num_dapm_routes        = ARRAY_SIZE(da7219_audio_map),
+};
+
+
+/*
+ * Regmap configs
+ */
+
+static struct reg_default da7219_reg_defaults[] = {
+       { DA7219_MIC_1_SELECT, 0x00 },
+       { DA7219_CIF_TIMEOUT_CTRL, 0x01 },
+       { DA7219_SR_24_48, 0x00 },
+       { DA7219_SR, 0x0A },
+       { DA7219_CIF_I2C_ADDR_CFG, 0x02 },
+       { DA7219_PLL_CTRL, 0x10 },
+       { DA7219_PLL_FRAC_TOP, 0x00 },
+       { DA7219_PLL_FRAC_BOT, 0x00 },
+       { DA7219_PLL_INTEGER, 0x20 },
+       { DA7219_DIG_ROUTING_DAI, 0x10 },
+       { DA7219_DAI_CLK_MODE, 0x01 },
+       { DA7219_DAI_CTRL, 0x28 },
+       { DA7219_DAI_TDM_CTRL, 0x40 },
+       { DA7219_DIG_ROUTING_DAC, 0x32 },
+       { DA7219_DAI_OFFSET_LOWER, 0x00 },
+       { DA7219_DAI_OFFSET_UPPER, 0x00 },
+       { DA7219_REFERENCES, 0x00 },
+       { DA7219_MIXIN_L_SELECT, 0x00 },
+       { DA7219_MIXIN_L_GAIN, 0x03 },
+       { DA7219_ADC_L_GAIN, 0x6F },
+       { DA7219_ADC_FILTERS1, 0x80 },
+       { DA7219_MIC_1_GAIN, 0x01 },
+       { DA7219_SIDETONE_CTRL, 0x40 },
+       { DA7219_SIDETONE_GAIN, 0x0E },
+       { DA7219_DROUTING_ST_OUTFILT_1L, 0x01 },
+       { DA7219_DROUTING_ST_OUTFILT_1R, 0x02 },
+       { DA7219_DAC_FILTERS5, 0x00 },
+       { DA7219_DAC_FILTERS2, 0x88 },
+       { DA7219_DAC_FILTERS3, 0x88 },
+       { DA7219_DAC_FILTERS4, 0x08 },
+       { DA7219_DAC_FILTERS1, 0x80 },
+       { DA7219_DAC_L_GAIN, 0x6F },
+       { DA7219_DAC_R_GAIN, 0x6F },
+       { DA7219_CP_CTRL, 0x20 },
+       { DA7219_HP_L_GAIN, 0x39 },
+       { DA7219_HP_R_GAIN, 0x39 },
+       { DA7219_MIXOUT_L_SELECT, 0x00 },
+       { DA7219_MIXOUT_R_SELECT, 0x00 },
+       { DA7219_MICBIAS_CTRL, 0x03 },
+       { DA7219_MIC_1_CTRL, 0x40 },
+       { DA7219_MIXIN_L_CTRL, 0x40 },
+       { DA7219_ADC_L_CTRL, 0x40 },
+       { DA7219_DAC_L_CTRL, 0x40 },
+       { DA7219_DAC_R_CTRL, 0x40 },
+       { DA7219_HP_L_CTRL, 0x40 },
+       { DA7219_HP_R_CTRL, 0x40 },
+       { DA7219_MIXOUT_L_CTRL, 0x10 },
+       { DA7219_MIXOUT_R_CTRL, 0x10 },
+       { DA7219_CHIP_ID1, 0x23 },
+       { DA7219_CHIP_ID2, 0x93 },
+       { DA7219_CHIP_REVISION, 0x00 },
+       { DA7219_LDO_CTRL, 0x00 },
+       { DA7219_IO_CTRL, 0x00 },
+       { DA7219_GAIN_RAMP_CTRL, 0x00 },
+       { DA7219_PC_COUNT, 0x02 },
+       { DA7219_CP_VOL_THRESHOLD1, 0x0E },
+       { DA7219_DIG_CTRL, 0x00 },
+       { DA7219_ALC_CTRL2, 0x00 },
+       { DA7219_ALC_CTRL3, 0x00 },
+       { DA7219_ALC_NOISE, 0x3F },
+       { DA7219_ALC_TARGET_MIN, 0x3F },
+       { DA7219_ALC_TARGET_MAX, 0x00 },
+       { DA7219_ALC_GAIN_LIMITS, 0xFF },
+       { DA7219_ALC_ANA_GAIN_LIMITS, 0x71 },
+       { DA7219_ALC_ANTICLIP_CTRL, 0x00 },
+       { DA7219_ALC_ANTICLIP_LEVEL, 0x00 },
+       { DA7219_DAC_NG_SETUP_TIME, 0x00 },
+       { DA7219_DAC_NG_OFF_THRESH, 0x00 },
+       { DA7219_DAC_NG_ON_THRESH, 0x00 },
+       { DA7219_DAC_NG_CTRL, 0x00 },
+       { DA7219_TONE_GEN_CFG1, 0x00 },
+       { DA7219_TONE_GEN_CFG2, 0x00 },
+       { DA7219_TONE_GEN_CYCLES, 0x00 },
+       { DA7219_TONE_GEN_FREQ1_L, 0x55 },
+       { DA7219_TONE_GEN_FREQ1_U, 0x15 },
+       { DA7219_TONE_GEN_FREQ2_L, 0x00 },
+       { DA7219_TONE_GEN_FREQ2_U, 0x40 },
+       { DA7219_TONE_GEN_ON_PER, 0x02 },
+       { DA7219_TONE_GEN_OFF_PER, 0x01 },
+       { DA7219_ACCDET_IRQ_MASK_A, 0x00 },
+       { DA7219_ACCDET_IRQ_MASK_B, 0x00 },
+       { DA7219_ACCDET_CONFIG_1, 0xD6 },
+       { DA7219_ACCDET_CONFIG_2, 0x34 },
+       { DA7219_ACCDET_CONFIG_3, 0x0A },
+       { DA7219_ACCDET_CONFIG_4, 0x16 },
+       { DA7219_ACCDET_CONFIG_5, 0x21 },
+       { DA7219_ACCDET_CONFIG_6, 0x3E },
+       { DA7219_ACCDET_CONFIG_7, 0x01 },
+       { DA7219_SYSTEM_ACTIVE, 0x00 },
+};
+
+static bool da7219_volatile_register(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case DA7219_MIC_1_GAIN_STATUS:
+       case DA7219_MIXIN_L_GAIN_STATUS:
+       case DA7219_ADC_L_GAIN_STATUS:
+       case DA7219_DAC_L_GAIN_STATUS:
+       case DA7219_DAC_R_GAIN_STATUS:
+       case DA7219_HP_L_GAIN_STATUS:
+       case DA7219_HP_R_GAIN_STATUS:
+       case DA7219_CIF_CTRL:
+       case DA7219_PLL_SRM_STS:
+       case DA7219_ALC_CTRL1:
+       case DA7219_SYSTEM_MODES_INPUT:
+       case DA7219_SYSTEM_MODES_OUTPUT:
+       case DA7219_ALC_OFFSET_AUTO_M_L:
+       case DA7219_ALC_OFFSET_AUTO_U_L:
+       case DA7219_TONE_GEN_CFG1:
+       case DA7219_ACCDET_STATUS_A:
+       case DA7219_ACCDET_STATUS_B:
+       case DA7219_ACCDET_IRQ_EVENT_A:
+       case DA7219_ACCDET_IRQ_EVENT_B:
+       case DA7219_ACCDET_CONFIG_8:
+       case DA7219_SYSTEM_STATUS:
+               return 1;
+       default:
+               return 0;
+       }
+}
+
+static const struct regmap_config da7219_regmap_config = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .max_register = DA7219_SYSTEM_ACTIVE,
+       .reg_defaults = da7219_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(da7219_reg_defaults),
+       .volatile_reg = da7219_volatile_register,
+       .cache_type = REGCACHE_RBTREE,
+};
+
+
+/*
+ * I2C layer
+ */
+
+static int da7219_i2c_probe(struct i2c_client *i2c,
+                           const struct i2c_device_id *id)
+{
+       struct da7219_priv *da7219;
+       int ret;
+
+       da7219 = devm_kzalloc(&i2c->dev, sizeof(struct da7219_priv),
+                             GFP_KERNEL);
+       if (!da7219)
+               return -ENOMEM;
+
+       i2c_set_clientdata(i2c, da7219);
+
+       da7219->regmap = devm_regmap_init_i2c(i2c, &da7219_regmap_config);
+       if (IS_ERR(da7219->regmap)) {
+               ret = PTR_ERR(da7219->regmap);
+               dev_err(&i2c->dev, "regmap_init() failed: %d\n", ret);
+               return ret;
+       }
+
+       ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_da7219,
+                                    &da7219_dai, 1);
+       if (ret < 0) {
+               dev_err(&i2c->dev, "Failed to register da7219 codec: %d\n",
+                       ret);
+       }
+       return ret;
+}
+
+static int da7219_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static const struct i2c_device_id da7219_i2c_id[] = {
+       { "da7219", },
+       { }
+};
+MODULE_DEVICE_TABLE(i2c, da7219_i2c_id);
+
+static struct i2c_driver da7219_i2c_driver = {
+       .driver = {
+               .name = "da7219",
+               .of_match_table = of_match_ptr(da7219_of_match),
+       },
+       .probe          = da7219_i2c_probe,
+       .remove         = da7219_i2c_remove,
+       .id_table       = da7219_i2c_id,
+};
+
+module_i2c_driver(da7219_i2c_driver);
+
+MODULE_DESCRIPTION("ASoC DA7219 Codec Driver");
+MODULE_AUTHOR("Adam Thomson <Adam.Thomson.Opensource@diasemi.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/da7219.h b/sound/soc/codecs/da7219.h
new file mode 100644 (file)
index 0000000..b514268
--- /dev/null
@@ -0,0 +1,820 @@
+/*
+ * da7219.h - DA7219 ALSA SoC Codec Driver
+ *
+ * Copyright (c) 2015 Dialog Semiconductor
+ *
+ * Author: Adam Thomson <Adam.Thomson.Opensource@diasemi.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.
+ */
+
+#ifndef __DA7219_H
+#define __DA7219_H
+
+#include <linux/regmap.h>
+#include <linux/regulator/consumer.h>
+#include <sound/da7219.h>
+
+/*
+ * Registers
+ */
+
+#define DA7219_MIC_1_GAIN_STATUS       0x6
+#define DA7219_MIXIN_L_GAIN_STATUS     0x8
+#define DA7219_ADC_L_GAIN_STATUS       0xA
+#define DA7219_DAC_L_GAIN_STATUS       0xC
+#define DA7219_DAC_R_GAIN_STATUS       0xD
+#define DA7219_HP_L_GAIN_STATUS                0xE
+#define DA7219_HP_R_GAIN_STATUS                0xF
+#define DA7219_MIC_1_SELECT            0x10
+#define DA7219_CIF_TIMEOUT_CTRL                0x12
+#define DA7219_CIF_CTRL                        0x13
+#define DA7219_SR_24_48                        0x16
+#define DA7219_SR                      0x17
+#define DA7219_CIF_I2C_ADDR_CFG                0x1B
+#define DA7219_PLL_CTRL                        0x20
+#define DA7219_PLL_FRAC_TOP            0x22
+#define DA7219_PLL_FRAC_BOT            0x23
+#define DA7219_PLL_INTEGER             0x24
+#define DA7219_PLL_SRM_STS             0x25
+#define DA7219_DIG_ROUTING_DAI         0x2A
+#define DA7219_DAI_CLK_MODE            0x2B
+#define DA7219_DAI_CTRL                        0x2C
+#define DA7219_DAI_TDM_CTRL            0x2D
+#define DA7219_DIG_ROUTING_DAC         0x2E
+#define DA7219_ALC_CTRL1               0x2F
+#define DA7219_DAI_OFFSET_LOWER                0x30
+#define DA7219_DAI_OFFSET_UPPER                0x31
+#define DA7219_REFERENCES              0x32
+#define DA7219_MIXIN_L_SELECT          0x33
+#define DA7219_MIXIN_L_GAIN            0x34
+#define DA7219_ADC_L_GAIN              0x36
+#define DA7219_ADC_FILTERS1            0x38
+#define DA7219_MIC_1_GAIN              0x39
+#define DA7219_SIDETONE_CTRL           0x3A
+#define DA7219_SIDETONE_GAIN           0x3B
+#define DA7219_DROUTING_ST_OUTFILT_1L  0x3C
+#define DA7219_DROUTING_ST_OUTFILT_1R  0x3D
+#define DA7219_DAC_FILTERS5            0x40
+#define DA7219_DAC_FILTERS2            0x41
+#define DA7219_DAC_FILTERS3            0x42
+#define DA7219_DAC_FILTERS4            0x43
+#define DA7219_DAC_FILTERS1            0x44
+#define DA7219_DAC_L_GAIN              0x45
+#define DA7219_DAC_R_GAIN              0x46
+#define DA7219_CP_CTRL                 0x47
+#define DA7219_HP_L_GAIN               0x48
+#define DA7219_HP_R_GAIN               0x49
+#define DA7219_MIXOUT_L_SELECT         0x4B
+#define DA7219_MIXOUT_R_SELECT         0x4C
+#define DA7219_SYSTEM_MODES_INPUT      0x50
+#define DA7219_SYSTEM_MODES_OUTPUT     0x51
+#define DA7219_MICBIAS_CTRL            0x62
+#define DA7219_MIC_1_CTRL              0x63
+#define DA7219_MIXIN_L_CTRL            0x65
+#define DA7219_ADC_L_CTRL              0x67
+#define DA7219_DAC_L_CTRL              0x69
+#define DA7219_DAC_R_CTRL              0x6A
+#define DA7219_HP_L_CTRL               0x6B
+#define DA7219_HP_R_CTRL               0x6C
+#define DA7219_MIXOUT_L_CTRL           0x6E
+#define DA7219_MIXOUT_R_CTRL           0x6F
+#define DA7219_CHIP_ID1                        0x81
+#define DA7219_CHIP_ID2                        0x82
+#define DA7219_CHIP_REVISION           0x83
+#define DA7219_LDO_CTRL                        0x90
+#define DA7219_IO_CTRL                 0x91
+#define DA7219_GAIN_RAMP_CTRL          0x92
+#define DA7219_PC_COUNT                        0x94
+#define DA7219_CP_VOL_THRESHOLD1       0x95
+#define DA7219_CP_DELAY                        0x96
+#define DA7219_DIG_CTRL                        0x99
+#define DA7219_ALC_CTRL2               0x9A
+#define DA7219_ALC_CTRL3               0x9B
+#define DA7219_ALC_NOISE               0x9C
+#define DA7219_ALC_TARGET_MIN          0x9D
+#define DA7219_ALC_TARGET_MAX          0x9E
+#define DA7219_ALC_GAIN_LIMITS         0x9F
+#define DA7219_ALC_ANA_GAIN_LIMITS     0xA0
+#define DA7219_ALC_ANTICLIP_CTRL       0xA1
+#define DA7219_ALC_ANTICLIP_LEVEL      0xA2
+#define DA7219_ALC_OFFSET_AUTO_M_L     0xA3
+#define DA7219_ALC_OFFSET_AUTO_U_L     0xA4
+#define DA7219_DAC_NG_SETUP_TIME       0xAF
+#define DA7219_DAC_NG_OFF_THRESH       0xB0
+#define DA7219_DAC_NG_ON_THRESH                0xB1
+#define DA7219_DAC_NG_CTRL             0xB2
+#define DA7219_TONE_GEN_CFG1           0xB4
+#define DA7219_TONE_GEN_CFG2           0xB5
+#define DA7219_TONE_GEN_CYCLES         0xB6
+#define DA7219_TONE_GEN_FREQ1_L                0xB7
+#define DA7219_TONE_GEN_FREQ1_U                0xB8
+#define DA7219_TONE_GEN_FREQ2_L                0xB9
+#define DA7219_TONE_GEN_FREQ2_U                0xBA
+#define DA7219_TONE_GEN_ON_PER         0xBB
+#define DA7219_TONE_GEN_OFF_PER                0xBC
+#define DA7219_SYSTEM_STATUS           0xE0
+#define DA7219_SYSTEM_ACTIVE           0xFD
+
+
+/*
+ * Bit Fields
+ */
+
+#define DA7219_SWITCH_EN_MAX           0x1
+
+/* DA7219_MIC_1_GAIN_STATUS = 0x6 */
+#define DA7219_MIC_1_AMP_GAIN_STATUS_SHIFT     0
+#define DA7219_MIC_1_AMP_GAIN_STATUS_MASK      (0x7 << 0)
+#define DA7219_MIC_1_AMP_GAIN_MAX              0x7
+
+/* DA7219_MIXIN_L_GAIN_STATUS = 0x8 */
+#define DA7219_MIXIN_L_AMP_GAIN_STATUS_SHIFT   0
+#define DA7219_MIXIN_L_AMP_GAIN_STATUS_MASK    (0xF << 0)
+
+/* DA7219_ADC_L_GAIN_STATUS = 0xA */
+#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_ADC_L_DIGITAL_GAIN_STATUS_MASK  (0x7F << 0)
+
+/* DA7219_DAC_L_GAIN_STATUS = 0xC */
+#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_DAC_L_DIGITAL_GAIN_STATUS_MASK  (0x7F << 0)
+
+/* DA7219_DAC_R_GAIN_STATUS = 0xD */
+#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_SHIFT 0
+#define DA7219_DAC_R_DIGITAL_GAIN_STATUS_MASK  (0x7F << 0)
+
+/* DA7219_HP_L_GAIN_STATUS = 0xE */
+#define DA7219_HP_L_AMP_GAIN_STATUS_SHIFT      0
+#define DA7219_HP_L_AMP_GAIN_STATUS_MASK       (0x3F << 0)
+
+/* DA7219_HP_R_GAIN_STATUS = 0xF */
+#define DA7219_HP_R_AMP_GAIN_STATUS_SHIFT      0
+#define DA7219_HP_R_AMP_GAIN_STATUS_MASK       (0x3F << 0)
+
+/* DA7219_MIC_1_SELECT = 0x10 */
+#define DA7219_MIC_1_AMP_IN_SEL_SHIFT  0
+#define DA7219_MIC_1_AMP_IN_SEL_MASK   (0x3 << 0)
+
+/* DA7219_CIF_TIMEOUT_CTRL = 0x12 */
+#define DA7219_I2C_TIMEOUT_EN_SHIFT    0
+#define DA7219_I2C_TIMEOUT_EN_MASK     (0x1 << 0)
+
+/* DA7219_CIF_CTRL = 0x13 */
+#define DA7219_CIF_I2C_WRITE_MODE_SHIFT                0
+#define DA7219_CIF_I2C_WRITE_MODE_MASK         (0x1 << 0)
+#define DA7219_CIF_REG_SOFT_RESET_SHIFT                7
+#define DA7219_CIF_REG_SOFT_RESET_MASK         (0x1 << 7)
+
+/* DA7219_SR_24_48 = 0x16 */
+#define DA7219_SR_24_48_SHIFT  0
+#define DA7219_SR_24_48_MASK   (0x1 << 0)
+
+/* DA7219_SR = 0x17 */
+#define DA7219_SR_SHIFT                0
+#define DA7219_SR_MASK         (0xF << 0)
+#define DA7219_SR_8000         (0x01 << 0)
+#define DA7219_SR_11025                (0x02 << 0)
+#define DA7219_SR_12000                (0x03 << 0)
+#define DA7219_SR_16000                (0x05 << 0)
+#define DA7219_SR_22050                (0x06 << 0)
+#define DA7219_SR_24000                (0x07 << 0)
+#define DA7219_SR_32000                (0x09 << 0)
+#define DA7219_SR_44100                (0x0A << 0)
+#define DA7219_SR_48000                (0x0B << 0)
+#define DA7219_SR_88200                (0x0E << 0)
+#define DA7219_SR_96000                (0x0F << 0)
+
+/* DA7219_CIF_I2C_ADDR_CFG = 0x1B */
+#define DA7219_CIF_I2C_ADDR_CFG_SHIFT  0
+#define DA7219_CIF_I2C_ADDR_CFG_MASK   (0x3 << 0)
+
+/* DA7219_PLL_CTRL = 0x20 */
+#define DA7219_PLL_INDIV_SHIFT         2
+#define DA7219_PLL_INDIV_MASK          (0x7 << 2)
+#define DA7219_PLL_INDIV_2_5_MHZ       (0x0 << 2)
+#define DA7219_PLL_INDIV_5_10_MHZ      (0x1 << 2)
+#define DA7219_PLL_INDIV_10_20_MHZ     (0x2 << 2)
+#define DA7219_PLL_INDIV_20_40_MHZ     (0x3 << 2)
+#define DA7219_PLL_INDIV_40_54_MHZ     (0x4 << 2)
+#define DA7219_PLL_MCLK_SQR_EN_SHIFT   5
+#define DA7219_PLL_MCLK_SQR_EN_MASK    (0x1 << 5)
+#define DA7219_PLL_MODE_SHIFT          6
+#define DA7219_PLL_MODE_MASK           (0x3 << 6)
+#define DA7219_PLL_MODE_BYPASS         (0x0 << 6)
+#define DA7219_PLL_MODE_NORMAL         (0x1 << 6)
+#define DA7219_PLL_MODE_SRM            (0x2 << 6)
+#define DA7219_PLL_MODE_32KHZ          (0x3 << 6)
+
+/* DA7219_PLL_FRAC_TOP = 0x22 */
+#define DA7219_PLL_FBDIV_FRAC_TOP_SHIFT        0
+#define DA7219_PLL_FBDIV_FRAC_TOP_MASK (0x1F << 0)
+
+/* DA7219_PLL_FRAC_BOT = 0x23 */
+#define DA7219_PLL_FBDIV_FRAC_BOT_SHIFT        0
+#define DA7219_PLL_FBDIV_FRAC_BOT_MASK (0xFF << 0)
+
+/* DA7219_PLL_INTEGER = 0x24 */
+#define DA7219_PLL_FBDIV_INTEGER_SHIFT 0
+#define DA7219_PLL_FBDIV_INTEGER_MASK  (0x7F << 0)
+
+/* DA7219_PLL_SRM_STS = 0x25 */
+#define DA7219_PLL_SRM_STATE_SHIFT     0
+#define DA7219_PLL_SRM_STATE_MASK      (0xF << 0)
+#define DA7219_PLL_SRM_STATUS_SHIFT    4
+#define DA7219_PLL_SRM_STATUS_MASK     (0xF << 4)
+#define DA7219_PLL_SRM_STS_SRM_LOCK    (0x1 << 7)
+
+/* DA7219_DIG_ROUTING_DAI = 0x2A */
+#define DA7219_DAI_L_SRC_SHIFT 0
+#define DA7219_DAI_L_SRC_MASK  (0x3 << 0)
+#define DA7219_DAI_R_SRC_SHIFT 4
+#define DA7219_DAI_R_SRC_MASK  (0x3 << 4)
+#define DA7219_OUT_SRC_MAX     4
+
+/* DA7219_DAI_CLK_MODE = 0x2B */
+#define DA7219_DAI_BCLKS_PER_WCLK_SHIFT        0
+#define DA7219_DAI_BCLKS_PER_WCLK_MASK (0x3 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_32   (0x0 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_64   (0x1 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_128  (0x2 << 0)
+#define DA7219_DAI_BCLKS_PER_WCLK_256  (0x3 << 0)
+#define DA7219_DAI_CLK_POL_SHIFT       2
+#define DA7219_DAI_CLK_POL_MASK                (0x1 << 2)
+#define DA7219_DAI_CLK_POL_INV         (0x1 << 2)
+#define DA7219_DAI_WCLK_POL_SHIFT      3
+#define DA7219_DAI_WCLK_POL_MASK       (0x1 << 3)
+#define DA7219_DAI_WCLK_POL_INV                (0x1 << 3)
+#define DA7219_DAI_WCLK_TRI_STATE_SHIFT        4
+#define DA7219_DAI_WCLK_TRI_STATE_MASK (0x1 << 4)
+#define DA7219_DAI_CLK_EN_SHIFT                7
+#define DA7219_DAI_CLK_EN_MASK         (0x1 << 7)
+
+/* DA7219_DAI_CTRL = 0x2C */
+#define DA7219_DAI_FORMAT_SHIFT                0
+#define DA7219_DAI_FORMAT_MASK         (0x3 << 0)
+#define DA7219_DAI_FORMAT_I2S          (0x0 << 0)
+#define DA7219_DAI_FORMAT_LEFT_J       (0x1 << 0)
+#define DA7219_DAI_FORMAT_RIGHT_J      (0x2 << 0)
+#define DA7219_DAI_FORMAT_DSP          (0x3 << 0)
+#define DA7219_DAI_WORD_LENGTH_SHIFT   2
+#define DA7219_DAI_WORD_LENGTH_MASK    (0x3 << 2)
+#define DA7219_DAI_WORD_LENGTH_S16_LE  (0x0 << 2)
+#define DA7219_DAI_WORD_LENGTH_S20_LE  (0x1 << 2)
+#define DA7219_DAI_WORD_LENGTH_S24_LE  (0x2 << 2)
+#define DA7219_DAI_WORD_LENGTH_S32_LE  (0x3 << 2)
+#define DA7219_DAI_CH_NUM_SHIFT                4
+#define DA7219_DAI_CH_NUM_MASK         (0x3 << 4)
+#define DA7219_DAI_CH_NUM_MAX          2
+#define DA7219_DAI_EN_SHIFT            7
+#define DA7219_DAI_EN_MASK             (0x1 << 7)
+
+/* DA7219_DAI_TDM_CTRL = 0x2D */
+#define DA7219_DAI_TDM_CH_EN_SHIFT     0
+#define DA7219_DAI_TDM_CH_EN_MASK      (0x3 << 0)
+#define DA7219_DAI_OE_SHIFT            6
+#define DA7219_DAI_OE_MASK             (0x1 << 6)
+#define DA7219_DAI_TDM_MODE_EN_SHIFT   7
+#define DA7219_DAI_TDM_MODE_EN_MASK    (0x1 << 7)
+#define DA7219_DAI_TDM_MAX_SLOTS       2
+
+/* DA7219_DIG_ROUTING_DAC = 0x2E */
+#define DA7219_DAC_L_SRC_SHIFT         0
+#define DA7219_DAC_L_SRC_MASK          (0x3 << 0)
+#define DA7219_DAC_L_SRC_TONEGEN       (0x1 << 0)
+#define DA7219_DAC_L_MONO_SHIFT                3
+#define DA7219_DAC_L_MONO_MASK         (0x1 << 3)
+#define DA7219_DAC_R_SRC_SHIFT         4
+#define DA7219_DAC_R_SRC_MASK          (0x3 << 4)
+#define DA7219_DAC_R_SRC_TONEGEN       (0x1 << 4)
+#define DA7219_DAC_R_MONO_SHIFT                7
+#define DA7219_DAC_R_MONO_MASK         (0x1 << 7)
+
+/* DA7219_ALC_CTRL1 = 0x2F */
+#define DA7219_ALC_OFFSET_EN_SHIFT     0
+#define DA7219_ALC_OFFSET_EN_MASK      (0x1 << 0)
+#define DA7219_ALC_SYNC_MODE_SHIFT     1
+#define DA7219_ALC_SYNC_MODE_MASK      (0x1 << 1)
+#define DA7219_ALC_EN_SHIFT            3
+#define DA7219_ALC_EN_MASK             (0x1 << 3)
+#define DA7219_ALC_AUTO_CALIB_EN_SHIFT 4
+#define DA7219_ALC_AUTO_CALIB_EN_MASK  (0x1 << 4)
+#define DA7219_ALC_CALIB_OVERFLOW_SHIFT        5
+#define DA7219_ALC_CALIB_OVERFLOW_MASK (0x1 << 5)
+
+/* DA7219_DAI_OFFSET_LOWER = 0x30 */
+#define DA7219_DAI_OFFSET_LOWER_SHIFT  0
+#define DA7219_DAI_OFFSET_LOWER_MASK   (0xFF << 0)
+
+/* DA7219_DAI_OFFSET_UPPER = 0x31 */
+#define DA7219_DAI_OFFSET_UPPER_SHIFT  0
+#define DA7219_DAI_OFFSET_UPPER_MASK   (0x7 << 0)
+#define DA7219_DAI_OFFSET_MAX          0x2FF
+
+/* DA7219_REFERENCES = 0x32 */
+#define DA7219_BIAS_EN_SHIFT           3
+#define DA7219_BIAS_EN_MASK            (0x1 << 3)
+#define DA7219_VMID_FAST_CHARGE_SHIFT  4
+#define DA7219_VMID_FAST_CHARGE_MASK   (0x1 << 4)
+
+/* DA7219_MIXIN_L_SELECT = 0x33 */
+#define DA7219_MIXIN_L_MIX_SELECT_SHIFT        0
+#define DA7219_MIXIN_L_MIX_SELECT_MASK (0x1 << 0)
+
+/* DA7219_MIXIN_L_GAIN = 0x34 */
+#define DA7219_MIXIN_L_AMP_GAIN_SHIFT  0
+#define DA7219_MIXIN_L_AMP_GAIN_MASK   (0xF << 0)
+#define DA7219_MIXIN_L_AMP_GAIN_MAX    0xF
+
+/* DA7219_ADC_L_GAIN = 0x36 */
+#define DA7219_ADC_L_DIGITAL_GAIN_SHIFT        0
+#define DA7219_ADC_L_DIGITAL_GAIN_MASK (0x7F << 0)
+#define DA7219_ADC_L_DIGITAL_GAIN_MAX  0x7F
+
+/* DA7219_ADC_FILTERS1 = 0x38 */
+#define DA7219_ADC_VOICE_HPF_CORNER_SHIFT      0
+#define DA7219_ADC_VOICE_HPF_CORNER_MASK       (0x7 << 0)
+#define DA7219_VOICE_HPF_CORNER_MAX            8
+#define DA7219_ADC_VOICE_EN_SHIFT              3
+#define DA7219_ADC_VOICE_EN_MASK               (0x1 << 3)
+#define DA7219_ADC_AUDIO_HPF_CORNER_SHIFT      4
+#define DA7219_ADC_AUDIO_HPF_CORNER_MASK       (0x3 << 4)
+#define DA7219_AUDIO_HPF_CORNER_MAX            4
+#define DA7219_ADC_HPF_EN_SHIFT                        7
+#define DA7219_ADC_HPF_EN_MASK                 (0x1 << 7)
+#define DA7219_HPF_MODE_SHIFT                  0
+#define DA7219_HPF_DISABLED                    ((0x0 << 3) | (0x0 << 7))
+#define DA7219_HPF_AUDIO_EN                    ((0x0 << 3) | (0x1 << 7))
+#define DA7219_HPF_VOICE_EN                    ((0x1 << 3) | (0x1 << 7))
+#define DA7219_HPF_MODE_MASK                   ((0x1 << 3) | (0x1 << 7))
+#define DA7219_HPF_MODE_MAX                    3
+
+/* DA7219_MIC_1_GAIN = 0x39 */
+#define DA7219_MIC_1_AMP_GAIN_SHIFT    0
+#define DA7219_MIC_1_AMP_GAIN_MASK     (0x7 << 0)
+
+/* DA7219_SIDETONE_CTRL = 0x3A */
+#define DA7219_SIDETONE_MUTE_EN_SHIFT  6
+#define DA7219_SIDETONE_MUTE_EN_MASK   (0x1 << 6)
+#define DA7219_SIDETONE_EN_SHIFT       7
+#define DA7219_SIDETONE_EN_MASK                (0x1 << 7)
+
+/* DA7219_SIDETONE_GAIN = 0x3B */
+#define DA7219_SIDETONE_GAIN_SHIFT     0
+#define DA7219_SIDETONE_GAIN_MASK      (0xF << 0)
+#define DA7219_SIDETONE_GAIN_MAX       0xE
+
+/* DA7219_DROUTING_ST_OUTFILT_1L = 0x3C */
+#define DA7219_OUTFILT_ST_1L_SRC_SHIFT         0
+#define DA7219_OUTFILT_ST_1L_SRC_MASK          (0x7 << 0)
+#define DA7219_DMIX_ST_SRC_OUTFILT1L_SHIFT     0
+#define DA7219_DMIX_ST_SRC_OUTFILT1R_SHIFT     1
+#define DA7219_DMIX_ST_SRC_SIDETONE_SHIFT      2
+#define DA7219_DMIX_ST_SRC_OUTFILT1L           (0x1 << 0)
+#define DA7219_DMIX_ST_SRC_OUTFILT1R           (0x1 << 1)
+
+/* DA7219_DROUTING_ST_OUTFILT_1R = 0x3D */
+#define DA7219_OUTFILT_ST_1R_SRC_SHIFT 0
+#define DA7219_OUTFILT_ST_1R_SRC_MASK  (0x7 << 0)
+
+/* DA7219_DAC_FILTERS5 = 0x40 */
+#define DA7219_DAC_SOFTMUTE_RATE_SHIFT 4
+#define DA7219_DAC_SOFTMUTE_RATE_MASK  (0x7 << 4)
+#define DA7219_DAC_SOFTMUTE_RATE_MAX   7
+#define DA7219_DAC_SOFTMUTE_EN_SHIFT   7
+#define DA7219_DAC_SOFTMUTE_EN_MASK    (0x1 << 7)
+
+/* DA7219_DAC_FILTERS2 = 0x41 */
+#define DA7219_DAC_EQ_BAND1_SHIFT      0
+#define DA7219_DAC_EQ_BAND1_MASK       (0xF << 0)
+#define DA7219_DAC_EQ_BAND2_SHIFT      4
+#define DA7219_DAC_EQ_BAND2_MASK       (0xF << 4)
+#define DA7219_DAC_EQ_BAND_MAX         0xF
+
+/* DA7219_DAC_FILTERS3 = 0x42 */
+#define DA7219_DAC_EQ_BAND3_SHIFT      0
+#define DA7219_DAC_EQ_BAND3_MASK       (0xF << 0)
+#define DA7219_DAC_EQ_BAND4_SHIFT      4
+#define DA7219_DAC_EQ_BAND4_MASK       (0xF << 4)
+
+/* DA7219_DAC_FILTERS4 = 0x43 */
+#define DA7219_DAC_EQ_BAND5_SHIFT      0
+#define DA7219_DAC_EQ_BAND5_MASK       (0xF << 0)
+#define DA7219_DAC_EQ_EN_SHIFT         7
+#define DA7219_DAC_EQ_EN_MASK          (0x1 << 7)
+
+/* DA7219_DAC_FILTERS1 = 0x44 */
+#define DA7219_DAC_VOICE_HPF_CORNER_SHIFT      0
+#define DA7219_DAC_VOICE_HPF_CORNER_MASK       (0x7 << 0)
+#define DA7219_DAC_VOICE_EN_SHIFT              3
+#define DA7219_DAC_VOICE_EN_MASK               (0x1 << 3)
+#define DA7219_DAC_AUDIO_HPF_CORNER_SHIFT      4
+#define DA7219_DAC_AUDIO_HPF_CORNER_MASK       (0x3 << 4)
+#define DA7219_DAC_HPF_EN_SHIFT                        7
+#define DA7219_DAC_HPF_EN_MASK                 (0x1 << 7)
+
+/* DA7219_DAC_L_GAIN = 0x45 */
+#define DA7219_DAC_L_DIGITAL_GAIN_SHIFT        0
+#define DA7219_DAC_L_DIGITAL_GAIN_MASK (0x7F << 0)
+#define DA7219_DAC_DIGITAL_GAIN_MAX    0x7F
+#define DA7219_DAC_DIGITAL_GAIN_0DB    (0x6F << 0)
+
+/* DA7219_DAC_R_GAIN = 0x46 */
+#define DA7219_DAC_R_DIGITAL_GAIN_SHIFT        0
+#define DA7219_DAC_R_DIGITAL_GAIN_MASK (0x7F << 0)
+
+/* DA7219_CP_CTRL = 0x47 */
+#define DA7219_CP_MCHANGE_SHIFT                4
+#define DA7219_CP_MCHANGE_MASK         (0x3 << 4)
+#define DA7219_CP_MCHANGE_REL_MASK     0x3
+#define DA7219_CP_MCHANGE_MAX          3
+#define DA7219_CP_MCHANGE_LARGEST_VOL  0x1
+#define DA7219_CP_MCHANGE_DAC_VOL      0x2
+#define DA7219_CP_MCHANGE_SIG_MAG      0x3
+#define DA7219_CP_EN_SHIFT             7
+#define DA7219_CP_EN_MASK              (0x1 << 7)
+
+/* DA7219_HP_L_GAIN = 0x48 */
+#define DA7219_HP_L_AMP_GAIN_SHIFT     0
+#define DA7219_HP_L_AMP_GAIN_MASK      (0x3F << 0)
+#define DA7219_HP_AMP_GAIN_MAX         0x3F
+#define DA7219_HP_AMP_GAIN_0DB         (0x39 << 0)
+
+/* DA7219_HP_R_GAIN = 0x49 */
+#define DA7219_HP_R_AMP_GAIN_SHIFT     0
+#define DA7219_HP_R_AMP_GAIN_MASK      (0x3F << 0)
+
+/* DA7219_MIXOUT_L_SELECT = 0x4B */
+#define DA7219_MIXOUT_L_MIX_SELECT_SHIFT       0
+#define DA7219_MIXOUT_L_MIX_SELECT_MASK                (0x1 << 0)
+
+/* DA7219_MIXOUT_R_SELECT = 0x4C */
+#define DA7219_MIXOUT_R_MIX_SELECT_SHIFT       0
+#define DA7219_MIXOUT_R_MIX_SELECT_MASK                (0x1 << 0)
+
+/* DA7219_SYSTEM_MODES_INPUT = 0x50 */
+#define DA7219_MODE_SUBMIT_SHIFT       0
+#define DA7219_MODE_SUBMIT_MASK                (0x1 << 0)
+#define DA7219_ADC_MODE_SHIFT          1
+#define DA7219_ADC_MODE_MASK           (0x7F << 1)
+
+/* DA7219_SYSTEM_MODES_OUTPUT = 0x51 */
+#define DA7219_MODE_SUBMIT_SHIFT       0
+#define DA7219_MODE_SUBMIT_MASK                (0x1 << 0)
+#define DA7219_DAC_MODE_SHIFT          1
+#define DA7219_DAC_MODE_MASK           (0x7F << 1)
+
+/* DA7219_MICBIAS_CTRL = 0x62 */
+#define DA7219_MICBIAS1_LEVEL_SHIFT    0
+#define DA7219_MICBIAS1_LEVEL_MASK     (0x7 << 0)
+#define DA7219_MICBIAS1_EN_SHIFT       3
+#define DA7219_MICBIAS1_EN_MASK                (0x1 << 3)
+
+/* DA7219_MIC_1_CTRL = 0x63 */
+#define DA7219_MIC_1_AMP_RAMP_EN_SHIFT 5
+#define DA7219_MIC_1_AMP_RAMP_EN_MASK  (0x1 << 5)
+#define DA7219_MIC_1_AMP_MUTE_EN_SHIFT 6
+#define DA7219_MIC_1_AMP_MUTE_EN_MASK  (0x1 << 6)
+#define DA7219_MIC_1_AMP_EN_SHIFT      7
+#define DA7219_MIC_1_AMP_EN_MASK       (0x1 << 7)
+
+/* DA7219_MIXIN_L_CTRL = 0x65 */
+#define DA7219_MIXIN_L_MIX_EN_SHIFT            3
+#define DA7219_MIXIN_L_MIX_EN_MASK             (0x1 << 3)
+#define DA7219_MIXIN_L_AMP_ZC_EN_SHIFT         4
+#define DA7219_MIXIN_L_AMP_ZC_EN_MASK          (0x1 << 4)
+#define DA7219_MIXIN_L_AMP_RAMP_EN_SHIFT       5
+#define DA7219_MIXIN_L_AMP_RAMP_EN_MASK                (0x1 << 5)
+#define DA7219_MIXIN_L_AMP_MUTE_EN_SHIFT       6
+#define DA7219_MIXIN_L_AMP_MUTE_EN_MASK                (0x1 << 6)
+#define DA7219_MIXIN_L_AMP_EN_SHIFT            7
+#define DA7219_MIXIN_L_AMP_EN_MASK             (0x1 << 7)
+
+/* DA7219_ADC_L_CTRL = 0x67 */
+#define DA7219_ADC_L_BIAS_SHIFT                0
+#define DA7219_ADC_L_BIAS_MASK         (0x3 << 0)
+#define DA7219_ADC_L_RAMP_EN_SHIFT     5
+#define DA7219_ADC_L_RAMP_EN_MASK      (0x1 << 5)
+#define DA7219_ADC_L_MUTE_EN_SHIFT     6
+#define DA7219_ADC_L_MUTE_EN_MASK      (0x1 << 6)
+#define DA7219_ADC_L_EN_SHIFT          7
+#define DA7219_ADC_L_EN_MASK           (0x1 << 7)
+
+/* DA7219_DAC_L_CTRL = 0x69 */
+#define DA7219_DAC_L_RAMP_EN_SHIFT     5
+#define DA7219_DAC_L_RAMP_EN_MASK      (0x1 << 5)
+#define DA7219_DAC_L_MUTE_EN_SHIFT     6
+#define DA7219_DAC_L_MUTE_EN_MASK      (0x1 << 6)
+#define DA7219_DAC_L_EN_SHIFT          7
+#define DA7219_DAC_L_EN_MASK           (0x1 << 7)
+
+/* DA7219_DAC_R_CTRL = 0x6A */
+#define DA7219_DAC_R_RAMP_EN_SHIFT     5
+#define DA7219_DAC_R_RAMP_EN_MASK      (0x1 << 5)
+#define DA7219_DAC_R_MUTE_EN_SHIFT     6
+#define DA7219_DAC_R_MUTE_EN_MASK      (0x1 << 6)
+#define DA7219_DAC_R_EN_SHIFT          7
+#define DA7219_DAC_R_EN_MASK           (0x1 << 7)
+
+/* DA7219_HP_L_CTRL = 0x6B */
+#define DA7219_HP_L_AMP_MIN_GAIN_EN_SHIFT      2
+#define DA7219_HP_L_AMP_MIN_GAIN_EN_MASK       (0x1 << 2)
+#define DA7219_HP_L_AMP_OE_SHIFT               3
+#define DA7219_HP_L_AMP_OE_MASK                        (0x1 << 3)
+#define DA7219_HP_L_AMP_ZC_EN_SHIFT            4
+#define DA7219_HP_L_AMP_ZC_EN_MASK             (0x1 << 4)
+#define DA7219_HP_L_AMP_RAMP_EN_SHIFT          5
+#define DA7219_HP_L_AMP_RAMP_EN_MASK           (0x1 << 5)
+#define DA7219_HP_L_AMP_MUTE_EN_SHIFT          6
+#define DA7219_HP_L_AMP_MUTE_EN_MASK           (0x1 << 6)
+#define DA7219_HP_L_AMP_EN_SHIFT               7
+#define DA7219_HP_L_AMP_EN_MASK                        (0x1 << 7)
+
+/* DA7219_HP_R_CTRL = 0x6C */
+#define DA7219_HP_R_AMP_MIN_GAIN_EN_SHIFT      2
+#define DA7219_HP_R_AMP_MIN_GAIN_EN_MASK       (0x1 << 2)
+#define DA7219_HP_R_AMP_OE_SHIFT               3
+#define DA7219_HP_R_AMP_OE_MASK                        (0x1 << 3)
+#define DA7219_HP_R_AMP_ZC_EN_SHIFT            4
+#define DA7219_HP_R_AMP_ZC_EN_MASK             (0x1 << 4)
+#define DA7219_HP_R_AMP_RAMP_EN_SHIFT          5
+#define DA7219_HP_R_AMP_RAMP_EN_MASK           (0x1 << 5)
+#define DA7219_HP_R_AMP_MUTE_EN_SHIFT          6
+#define DA7219_HP_R_AMP_MUTE_EN_MASK           (0x1 << 6)
+#define DA7219_HP_R_AMP_EN_SHIFT               7
+#define DA7219_HP_R_AMP_EN_MASK                        (0x1 << 7)
+
+/* DA7219_MIXOUT_L_CTRL = 0x6E */
+#define DA7219_MIXOUT_L_AMP_EN_SHIFT   7
+#define DA7219_MIXOUT_L_AMP_EN_MASK    (0x1 << 7)
+
+/* DA7219_MIXOUT_R_CTRL = 0x6F */
+#define DA7219_MIXOUT_R_AMP_EN_SHIFT   7
+#define DA7219_MIXOUT_R_AMP_EN_MASK    (0x1 << 7)
+
+/* DA7219_CHIP_ID1 = 0x81 */
+#define DA7219_CHIP_ID1_SHIFT  0
+#define DA7219_CHIP_ID1_MASK   (0xFF << 0)
+
+/* DA7219_CHIP_ID2 = 0x82 */
+#define DA7219_CHIP_ID2_SHIFT  0
+#define DA7219_CHIP_ID2_MASK   (0xFF << 0)
+
+/* DA7219_CHIP_REVISION = 0x83 */
+#define DA7219_CHIP_MINOR_SHIFT        0
+#define DA7219_CHIP_MINOR_MASK (0xF << 0)
+#define DA7219_CHIP_MAJOR_SHIFT        4
+#define DA7219_CHIP_MAJOR_MASK (0xF << 4)
+
+/* DA7219_LDO_CTRL = 0x90 */
+#define DA7219_LDO_LEVEL_SELECT_SHIFT  4
+#define DA7219_LDO_LEVEL_SELECT_MASK   (0x3 << 4)
+#define DA7219_LDO_EN_SHIFT            7
+#define DA7219_LDO_EN_MASK             (0x1 << 7)
+
+/* DA7219_IO_CTRL = 0x91 */
+#define DA7219_IO_VOLTAGE_LEVEL_SHIFT          0
+#define DA7219_IO_VOLTAGE_LEVEL_MASK           (0x1 << 0)
+#define DA7219_IO_VOLTAGE_LEVEL_2_5V_3_6V      0
+#define DA7219_IO_VOLTAGE_LEVEL_1_2V_2_8V      1
+
+/* DA7219_GAIN_RAMP_CTRL = 0x92 */
+#define DA7219_GAIN_RAMP_RATE_SHIFT    0
+#define DA7219_GAIN_RAMP_RATE_MASK     (0x3 << 0)
+#define DA7219_GAIN_RAMP_RATE_MAX      4
+
+/* DA7219_PC_COUNT = 0x94 */
+#define DA7219_PC_FREERUN_SHIFT                0
+#define DA7219_PC_FREERUN_MASK         (0x1 << 0)
+#define DA7219_PC_RESYNC_AUTO_SHIFT    1
+#define DA7219_PC_RESYNC_AUTO_MASK     (0x1 << 1)
+
+/* DA7219_CP_VOL_THRESHOLD1 = 0x95 */
+#define DA7219_CP_THRESH_VDD2_SHIFT    0
+#define DA7219_CP_THRESH_VDD2_MASK     (0x3F << 0)
+#define DA7219_CP_THRESH_VDD2_MAX      0x3F
+
+/* DA7219_DIG_CTRL = 0x99 */
+#define DA7219_DAC_L_INV_SHIFT 3
+#define DA7219_DAC_L_INV_MASK  (0x1 << 3)
+#define DA7219_DAC_R_INV_SHIFT 7
+#define DA7219_DAC_R_INV_MASK  (0x1 << 7)
+
+/* DA7219_ALC_CTRL2 = 0x9A */
+#define DA7219_ALC_ATTACK_SHIFT                0
+#define DA7219_ALC_ATTACK_MASK         (0xF << 0)
+#define DA7219_ALC_ATTACK_MAX          13
+#define DA7219_ALC_RELEASE_SHIFT       4
+#define DA7219_ALC_RELEASE_MASK                (0xF << 4)
+#define DA7219_ALC_RELEASE_MAX         11
+
+/* DA7219_ALC_CTRL3 = 0x9B */
+#define DA7219_ALC_HOLD_SHIFT          0
+#define DA7219_ALC_HOLD_MASK           (0xF << 0)
+#define DA7219_ALC_HOLD_MAX            16
+#define DA7219_ALC_INTEG_ATTACK_SHIFT  4
+#define DA7219_ALC_INTEG_ATTACK_MASK   (0x3 << 4)
+#define DA7219_ALC_INTEG_RELEASE_SHIFT 6
+#define DA7219_ALC_INTEG_RELEASE_MASK  (0x3 << 6)
+#define DA7219_ALC_INTEG_MAX           4
+
+/* DA7219_ALC_NOISE = 0x9C */
+#define DA7219_ALC_NOISE_SHIFT         0
+#define DA7219_ALC_NOISE_MASK          (0x3F << 0)
+#define DA7219_ALC_THRESHOLD_MAX       0x3F
+
+/* DA7219_ALC_TARGET_MIN = 0x9D */
+#define DA7219_ALC_THRESHOLD_MIN_SHIFT 0
+#define DA7219_ALC_THRESHOLD_MIN_MASK  (0x3F << 0)
+
+/* DA7219_ALC_TARGET_MAX = 0x9E */
+#define DA7219_ALC_THRESHOLD_MAX_SHIFT 0
+#define DA7219_ALC_THRESHOLD_MAX_MASK  (0x3F << 0)
+
+/* DA7219_ALC_GAIN_LIMITS = 0x9F */
+#define DA7219_ALC_ATTEN_MAX_SHIFT     0
+#define DA7219_ALC_ATTEN_MAX_MASK      (0xF << 0)
+#define DA7219_ALC_GAIN_MAX_SHIFT      4
+#define DA7219_ALC_GAIN_MAX_MASK       (0xF << 4)
+#define DA7219_ALC_ATTEN_GAIN_MAX      0xF
+
+/* DA7219_ALC_ANA_GAIN_LIMITS = 0xA0 */
+#define DA7219_ALC_ANA_GAIN_MIN_SHIFT  0
+#define DA7219_ALC_ANA_GAIN_MIN_MASK   (0x7 << 0)
+#define DA7219_ALC_ANA_GAIN_MIN                0x1
+#define DA7219_ALC_ANA_GAIN_MAX_SHIFT  4
+#define DA7219_ALC_ANA_GAIN_MAX_MASK   (0x7 << 4)
+#define DA7219_ALC_ANA_GAIN_MAX                0x7
+
+/* DA7219_ALC_ANTICLIP_CTRL = 0xA1 */
+#define DA7219_ALC_ANTICLIP_STEP_SHIFT 0
+#define DA7219_ALC_ANTICLIP_STEP_MASK  (0x3 << 0)
+#define DA7219_ALC_ANTICLIP_STEP_MAX   4
+#define DA7219_ALC_ANTIPCLIP_EN_SHIFT  7
+#define DA7219_ALC_ANTIPCLIP_EN_MASK   (0x1 << 7)
+
+/* DA7219_ALC_ANTICLIP_LEVEL = 0xA2 */
+#define DA7219_ALC_ANTICLIP_LEVEL_SHIFT        0
+#define DA7219_ALC_ANTICLIP_LEVEL_MASK (0x7F << 0)
+
+/* DA7219_ALC_OFFSET_AUTO_M_L = 0xA3 */
+#define DA7219_ALC_OFFSET_AUTO_M_L_SHIFT       0
+#define DA7219_ALC_OFFSET_AUTO_M_L_MASK                (0xFF << 0)
+
+/* DA7219_ALC_OFFSET_AUTO_U_L = 0xA4 */
+#define DA7219_ALC_OFFSET_AUTO_U_L_SHIFT       0
+#define DA7219_ALC_OFFSET_AUTO_U_L_MASK                (0xF << 0)
+
+/* DA7219_DAC_NG_SETUP_TIME = 0xAF */
+#define DA7219_DAC_NG_SETUP_TIME_SHIFT 0
+#define DA7219_DAC_NG_SETUP_TIME_MASK  (0x3 << 0)
+#define DA7219_DAC_NG_SETUP_TIME_MAX   4
+#define DA7219_DAC_NG_RAMPUP_RATE_SHIFT        2
+#define DA7219_DAC_NG_RAMPUP_RATE_MASK (0x1 << 2)
+#define DA7219_DAC_NG_RAMPDN_RATE_SHIFT        3
+#define DA7219_DAC_NG_RAMPDN_RATE_MASK (0x1 << 3)
+#define DA7219_DAC_NG_RAMP_RATE_MAX    2
+
+/* DA7219_DAC_NG_OFF_THRESH = 0xB0 */
+#define DA7219_DAC_NG_OFF_THRESHOLD_SHIFT      0
+#define DA7219_DAC_NG_OFF_THRESHOLD_MASK       (0x7 << 0)
+#define DA7219_DAC_NG_THRESHOLD_MAX            0x7
+
+/* DA7219_DAC_NG_ON_THRESH = 0xB1 */
+#define DA7219_DAC_NG_ON_THRESHOLD_SHIFT       0
+#define DA7219_DAC_NG_ON_THRESHOLD_MASK                (0x7 << 0)
+
+/* DA7219_DAC_NG_CTRL = 0xB2 */
+#define DA7219_DAC_NG_EN_SHIFT 7
+#define DA7219_DAC_NG_EN_MASK  (0x1 << 7)
+
+/* DA7219_TONE_GEN_CFG1 = 0xB4 */
+#define DA7219_DTMF_REG_SHIFT          0
+#define DA7219_DTMF_REG_MASK           (0xF << 0)
+#define DA7219_DTMF_REG_MAX            16
+#define DA7219_DTMF_EN_SHIFT           4
+#define DA7219_DTMF_EN_MASK            (0x1 << 4)
+#define DA7219_START_STOPN_SHIFT       7
+#define DA7219_START_STOPN_MASK                (0x1 << 7)
+
+/* DA7219_TONE_GEN_CFG2 = 0xB5 */
+#define DA7219_SWG_SEL_SHIFT           0
+#define DA7219_SWG_SEL_MASK            (0x3 << 0)
+#define DA7219_SWG_SEL_MAX             4
+#define DA7219_SWG_SEL_SRAMP           (0x3 << 0)
+#define DA7219_TONE_GEN_GAIN_SHIFT     4
+#define DA7219_TONE_GEN_GAIN_MASK      (0xF << 4)
+#define DA7219_TONE_GEN_GAIN_MAX       0xF
+#define DA7219_TONE_GEN_GAIN_MINUS_9DB (0x3 << 4)
+#define DA7219_TONE_GEN_GAIN_MINUS_15DB        (0x5 << 4)
+
+/* DA7219_TONE_GEN_CYCLES = 0xB6 */
+#define DA7219_BEEP_CYCLES_SHIFT       0
+#define DA7219_BEEP_CYCLES_MASK                (0x7 << 0)
+
+/* DA7219_TONE_GEN_FREQ1_L = 0xB7 */
+#define DA7219_FREQ1_L_SHIFT   0
+#define DA7219_FREQ1_L_MASK    (0xFF << 0)
+#define DA7219_FREQ_MAX                0xFFFF
+
+/* DA7219_TONE_GEN_FREQ1_U = 0xB8 */
+#define DA7219_FREQ1_U_SHIFT   0
+#define DA7219_FREQ1_U_MASK    (0xFF << 0)
+
+/* DA7219_TONE_GEN_FREQ2_L = 0xB9 */
+#define DA7219_FREQ2_L_SHIFT   0
+#define DA7219_FREQ2_L_MASK    (0xFF << 0)
+
+/* DA7219_TONE_GEN_FREQ2_U = 0xBA */
+#define DA7219_FREQ2_U_SHIFT   0
+#define DA7219_FREQ2_U_MASK    (0xFF << 0)
+
+/* DA7219_TONE_GEN_ON_PER = 0xBB */
+#define DA7219_BEEP_ON_PER_SHIFT       0
+#define DA7219_BEEP_ON_PER_MASK                (0x3F << 0)
+#define DA7219_BEEP_ON_OFF_MAX         0x3F
+
+/* DA7219_TONE_GEN_OFF_PER = 0xBC */
+#define DA7219_BEEP_OFF_PER_SHIFT      0
+#define DA7219_BEEP_OFF_PER_MASK       (0x3F << 0)
+
+/* DA7219_SYSTEM_STATUS = 0xE0 */
+#define DA7219_SC1_BUSY_SHIFT  0
+#define DA7219_SC1_BUSY_MASK   (0x1 << 0)
+#define DA7219_SC2_BUSY_SHIFT  1
+#define DA7219_SC2_BUSY_MASK   (0x1 << 1)
+
+/* DA7219_SYSTEM_ACTIVE = 0xFD */
+#define DA7219_SYSTEM_ACTIVE_SHIFT     0
+#define DA7219_SYSTEM_ACTIVE_MASK      (0x1 << 0)
+
+
+/*
+ * General defines & data
+ */
+
+/* Register inversion */
+#define DA7219_NO_INVERT       0
+#define DA7219_INVERT          1
+
+/* Byte related defines */
+#define DA7219_BYTE_SHIFT      8
+#define DA7219_BYTE_MASK       0xFF
+
+/* PLL Output Frequencies */
+#define DA7219_PLL_FREQ_OUT_90316      90316800
+#define DA7219_PLL_FREQ_OUT_98304      98304000
+
+/* PLL Frequency Dividers */
+#define DA7219_PLL_INDIV_2_5_MHZ_VAL   1
+#define DA7219_PLL_INDIV_5_10_MHZ_VAL  2
+#define DA7219_PLL_INDIV_10_20_MHZ_VAL 4
+#define DA7219_PLL_INDIV_20_40_MHZ_VAL 8
+#define DA7219_PLL_INDIV_40_54_MHZ_VAL 16
+
+/* SRM */
+#define DA7219_SRM_CHECK_RETRIES       8
+
+enum da7219_clk_src {
+       DA7219_CLKSRC_MCLK = 0,
+       DA7219_CLKSRC_MCLK_SQR,
+};
+
+enum da7219_sys_clk {
+       DA7219_SYSCLK_MCLK = 0,
+       DA7219_SYSCLK_PLL,
+       DA7219_SYSCLK_PLL_SRM,
+       DA7219_SYSCLK_PLL_32KHZ
+};
+
+/* Regulators */
+enum da7219_supplies {
+       DA7219_SUPPLY_VDD = 0,
+       DA7219_SUPPLY_VDDMIC,
+       DA7219_SUPPLY_VDDIO,
+       DA7219_NUM_SUPPLIES,
+};
+
+struct da7219_aad_priv;
+
+/* Private data */
+struct da7219_priv {
+       struct da7219_aad_priv *aad;
+       struct da7219_pdata *pdata;
+
+       struct regulator_bulk_data supplies[DA7219_NUM_SUPPLIES];
+       struct regmap *regmap;
+       struct mutex lock;
+
+       struct clk *mclk;
+       unsigned int mclk_rate;
+       int clk_src;
+
+       bool master;
+       bool alc_en;
+};
+
+#endif /* __DA7219_H */
index 6a091016e0fc767c2465cfd5c8eaf822d09d2ea3..969e337dc17c131413e43f666edd5b299fc11544 100644 (file)
@@ -129,7 +129,7 @@ static int es8328_put_deemph(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct es8328_priv *es8328 = snd_soc_codec_get_drvdata(codec);
-       int deemph = ucontrol->value.integer.value[0];
+       unsigned int deemph = ucontrol->value.integer.value[0];
        int ret;
 
        if (deemph > 1)
diff --git a/sound/soc/codecs/hdmi.c b/sound/soc/codecs/hdmi.c
deleted file mode 100644 (file)
index bd42ad3..0000000
+++ /dev/null
@@ -1,109 +0,0 @@
-/*
- * ALSA SoC codec driver for HDMI audio codecs.
- * Copyright (C) 2012 Texas Instruments Incorporated - http://www.ti.com/
- * Author: Ricardo Neri <ricardo.neri@ti.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.
- *
- * 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 <sound/soc.h>
-#include <linux/of.h>
-#include <linux/of_device.h>
-
-#define DRV_NAME "hdmi-audio-codec"
-
-static const struct snd_soc_dapm_widget hdmi_widgets[] = {
-       SND_SOC_DAPM_INPUT("RX"),
-       SND_SOC_DAPM_OUTPUT("TX"),
-};
-
-static const struct snd_soc_dapm_route hdmi_routes[] = {
-       { "Capture", NULL, "RX" },
-       { "TX", NULL, "Playback" },
-};
-
-static struct snd_soc_dai_driver hdmi_codec_dai = {
-       .name = "hdmi-hifi",
-       .playback = {
-               .stream_name = "Playback",
-               .channels_min = 2,
-               .channels_max = 8,
-               .rates = SNDRV_PCM_RATE_32000 |
-                       SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
-                       SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
-                       SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE |
-                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE,
-               .sig_bits = 24,
-       },
-       .capture = {
-               .stream_name = "Capture",
-               .channels_min = 2,
-               .channels_max = 2,
-               .rates = SNDRV_PCM_RATE_32000 |
-                       SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
-                       SNDRV_PCM_RATE_88200 | SNDRV_PCM_RATE_96000 |
-                       SNDRV_PCM_RATE_176400 | SNDRV_PCM_RATE_192000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE |
-                       SNDRV_PCM_FMTBIT_S24_LE,
-       },
-
-};
-
-#ifdef CONFIG_OF
-static const struct of_device_id hdmi_audio_codec_ids[] = {
-       { .compatible = "linux,hdmi-audio", },
-       { }
-};
-MODULE_DEVICE_TABLE(of, hdmi_audio_codec_ids);
-#endif
-
-static struct snd_soc_codec_driver hdmi_codec = {
-       .dapm_widgets = hdmi_widgets,
-       .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)
-{
-       return snd_soc_register_codec(&pdev->dev, &hdmi_codec,
-                       &hdmi_codec_dai, 1);
-}
-
-static int hdmi_codec_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_codec(&pdev->dev);
-       return 0;
-}
-
-static struct platform_driver hdmi_codec_driver = {
-       .driver         = {
-               .name   = DRV_NAME,
-               .of_match_table = of_match_ptr(hdmi_audio_codec_ids),
-       },
-
-       .probe          = hdmi_codec_probe,
-       .remove         = hdmi_codec_remove,
-};
-
-module_platform_driver(hdmi_codec_driver);
-
-MODULE_AUTHOR("Ricardo Neri <ricardo.neri@ti.com>");
-MODULE_DESCRIPTION("ASoC generic HDMI codec driver");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:" DRV_NAME);
diff --git a/sound/soc/codecs/nau8825.c b/sound/soc/codecs/nau8825.c
new file mode 100644 (file)
index 0000000..7fc7b4e
--- /dev/null
@@ -0,0 +1,1309 @@
+/*
+ * Nuvoton NAU8825 audio codec driver
+ *
+ * Copyright 2015 Google Chromium project.
+ *  Author: Anatol Pomozov <anatol@chromium.org>
+ * Copyright 2015 Nuvoton Technology Corp.
+ *  Co-author: Meng-Huang Kuo <mhkuo@nuvoton.com>
+ *
+ * Licensed under the GPL-2.
+ */
+
+#include <linux/module.h>
+#include <linux/delay.h>
+#include <linux/init.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <linux/clk.h>
+#include <linux/acpi.h>
+#include <linux/math64.h>
+
+#include <sound/initval.h>
+#include <sound/tlv.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+
+
+#include "nau8825.h"
+
+#define NAU_FREF_MAX 13500000
+#define NAU_FVCO_MAX 100000000
+#define NAU_FVCO_MIN 90000000
+
+struct nau8825_fll {
+       int mclk_src;
+       int ratio;
+       int fll_frac;
+       int fll_int;
+       int clk_ref_div;
+};
+
+struct nau8825_fll_attr {
+       unsigned int param;
+       unsigned int val;
+};
+
+/* scaling for mclk from sysclk_src output */
+static const struct nau8825_fll_attr mclk_src_scaling[] = {
+       { 1, 0x0 },
+       { 2, 0x2 },
+       { 4, 0x3 },
+       { 8, 0x4 },
+       { 16, 0x5 },
+       { 32, 0x6 },
+       { 3, 0x7 },
+       { 6, 0xa },
+       { 12, 0xb },
+       { 24, 0xc },
+       { 48, 0xd },
+       { 96, 0xe },
+       { 5, 0xf },
+};
+
+/* ratio for input clk freq */
+static const struct nau8825_fll_attr fll_ratio[] = {
+       { 512000, 0x01 },
+       { 256000, 0x02 },
+       { 128000, 0x04 },
+       { 64000, 0x08 },
+       { 32000, 0x10 },
+       { 8000, 0x20 },
+       { 4000, 0x40 },
+};
+
+static const struct nau8825_fll_attr fll_pre_scalar[] = {
+       { 1, 0x0 },
+       { 2, 0x1 },
+       { 4, 0x2 },
+       { 8, 0x3 },
+};
+
+static const struct reg_default nau8825_reg_defaults[] = {
+       { NAU8825_REG_ENA_CTRL, 0x00ff },
+       { NAU8825_REG_CLK_DIVIDER, 0x0050 },
+       { NAU8825_REG_FLL1, 0x0 },
+       { NAU8825_REG_FLL2, 0x3126 },
+       { NAU8825_REG_FLL3, 0x0008 },
+       { NAU8825_REG_FLL4, 0x0010 },
+       { NAU8825_REG_FLL5, 0x0 },
+       { NAU8825_REG_FLL6, 0x6000 },
+       { NAU8825_REG_FLL_VCO_RSV, 0xf13c },
+       { NAU8825_REG_HSD_CTRL, 0x000c },
+       { NAU8825_REG_JACK_DET_CTRL, 0x0 },
+       { NAU8825_REG_INTERRUPT_MASK, 0x0 },
+       { NAU8825_REG_INTERRUPT_DIS_CTRL, 0xffff },
+       { NAU8825_REG_SAR_CTRL, 0x0015 },
+       { NAU8825_REG_KEYDET_CTRL, 0x0110 },
+       { NAU8825_REG_VDET_THRESHOLD_1, 0x0 },
+       { NAU8825_REG_VDET_THRESHOLD_2, 0x0 },
+       { NAU8825_REG_VDET_THRESHOLD_3, 0x0 },
+       { NAU8825_REG_VDET_THRESHOLD_4, 0x0 },
+       { NAU8825_REG_GPIO34_CTRL, 0x0 },
+       { NAU8825_REG_GPIO12_CTRL, 0x0 },
+       { NAU8825_REG_TDM_CTRL, 0x0 },
+       { NAU8825_REG_I2S_PCM_CTRL1, 0x000b },
+       { NAU8825_REG_I2S_PCM_CTRL2, 0x8010 },
+       { NAU8825_REG_LEFT_TIME_SLOT, 0x0 },
+       { NAU8825_REG_RIGHT_TIME_SLOT, 0x0 },
+       { NAU8825_REG_BIQ_CTRL, 0x0 },
+       { NAU8825_REG_BIQ_COF1, 0x0 },
+       { NAU8825_REG_BIQ_COF2, 0x0 },
+       { NAU8825_REG_BIQ_COF3, 0x0 },
+       { NAU8825_REG_BIQ_COF4, 0x0 },
+       { NAU8825_REG_BIQ_COF5, 0x0 },
+       { NAU8825_REG_BIQ_COF6, 0x0 },
+       { NAU8825_REG_BIQ_COF7, 0x0 },
+       { NAU8825_REG_BIQ_COF8, 0x0 },
+       { NAU8825_REG_BIQ_COF9, 0x0 },
+       { NAU8825_REG_BIQ_COF10, 0x0 },
+       { NAU8825_REG_ADC_RATE, 0x0010 },
+       { NAU8825_REG_DAC_CTRL1, 0x0001 },
+       { NAU8825_REG_DAC_CTRL2, 0x0 },
+       { NAU8825_REG_DAC_DGAIN_CTRL, 0x0 },
+       { NAU8825_REG_ADC_DGAIN_CTRL, 0x00cf },
+       { NAU8825_REG_MUTE_CTRL, 0x0 },
+       { NAU8825_REG_HSVOL_CTRL, 0x0 },
+       { NAU8825_REG_DACL_CTRL, 0x02cf },
+       { NAU8825_REG_DACR_CTRL, 0x00cf },
+       { NAU8825_REG_ADC_DRC_KNEE_IP12, 0x1486 },
+       { NAU8825_REG_ADC_DRC_KNEE_IP34, 0x0f12 },
+       { NAU8825_REG_ADC_DRC_SLOPES, 0x25ff },
+       { NAU8825_REG_ADC_DRC_ATKDCY, 0x3457 },
+       { NAU8825_REG_DAC_DRC_KNEE_IP12, 0x1486 },
+       { NAU8825_REG_DAC_DRC_KNEE_IP34, 0x0f12 },
+       { NAU8825_REG_DAC_DRC_SLOPES, 0x25f9 },
+       { NAU8825_REG_DAC_DRC_ATKDCY, 0x3457 },
+       { NAU8825_REG_IMM_MODE_CTRL, 0x0 },
+       { NAU8825_REG_CLASSG_CTRL, 0x0 },
+       { NAU8825_REG_OPT_EFUSE_CTRL, 0x0 },
+       { NAU8825_REG_MISC_CTRL, 0x0 },
+       { NAU8825_REG_BIAS_ADJ, 0x0 },
+       { NAU8825_REG_TRIM_SETTINGS, 0x0 },
+       { NAU8825_REG_ANALOG_CONTROL_1, 0x0 },
+       { NAU8825_REG_ANALOG_CONTROL_2, 0x0 },
+       { NAU8825_REG_ANALOG_ADC_1, 0x0011 },
+       { NAU8825_REG_ANALOG_ADC_2, 0x0020 },
+       { NAU8825_REG_RDAC, 0x0008 },
+       { NAU8825_REG_MIC_BIAS, 0x0006 },
+       { NAU8825_REG_BOOST, 0x0 },
+       { NAU8825_REG_FEPGA, 0x0 },
+       { NAU8825_REG_POWER_UP_CONTROL, 0x0 },
+       { NAU8825_REG_CHARGE_PUMP, 0x0 },
+};
+
+static bool nau8825_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case NAU8825_REG_ENA_CTRL:
+       case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+       case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
+       case NAU8825_REG_INTERRUPT_MASK ... NAU8825_REG_KEYDET_CTRL:
+       case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
+       case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY:
+       case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY:
+       case NAU8825_REG_IMM_MODE_CTRL ... NAU8825_REG_IMM_RMS_R:
+       case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
+       case NAU8825_REG_MISC_CTRL:
+       case NAU8825_REG_I2C_DEVICE_ID ... NAU8825_REG_SARDOUT_RAM_STATUS:
+       case NAU8825_REG_BIAS_ADJ:
+       case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
+       case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
+       case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA:
+       case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_GENERAL_STATUS:
+               return true;
+       default:
+               return false;
+       }
+
+}
+
+static bool nau8825_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case NAU8825_REG_RESET ... NAU8825_REG_ENA_CTRL:
+       case NAU8825_REG_CLK_DIVIDER ... NAU8825_REG_FLL_VCO_RSV:
+       case NAU8825_REG_HSD_CTRL ... NAU8825_REG_JACK_DET_CTRL:
+       case NAU8825_REG_INTERRUPT_MASK:
+       case NAU8825_REG_INT_CLR_KEY_STATUS ... NAU8825_REG_KEYDET_CTRL:
+       case NAU8825_REG_VDET_THRESHOLD_1 ... NAU8825_REG_DACR_CTRL:
+       case NAU8825_REG_ADC_DRC_KNEE_IP12 ... NAU8825_REG_ADC_DRC_ATKDCY:
+       case NAU8825_REG_DAC_DRC_KNEE_IP12 ... NAU8825_REG_DAC_DRC_ATKDCY:
+       case NAU8825_REG_IMM_MODE_CTRL:
+       case NAU8825_REG_CLASSG_CTRL ... NAU8825_REG_OPT_EFUSE_CTRL:
+       case NAU8825_REG_MISC_CTRL:
+       case NAU8825_REG_BIAS_ADJ:
+       case NAU8825_REG_TRIM_SETTINGS ... NAU8825_REG_ANALOG_CONTROL_2:
+       case NAU8825_REG_ANALOG_ADC_1 ... NAU8825_REG_MIC_BIAS:
+       case NAU8825_REG_BOOST ... NAU8825_REG_FEPGA:
+       case NAU8825_REG_POWER_UP_CONTROL ... NAU8825_REG_CHARGE_PUMP:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool nau8825_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case NAU8825_REG_RESET:
+       case NAU8825_REG_IRQ_STATUS:
+       case NAU8825_REG_INT_CLR_KEY_STATUS:
+       case NAU8825_REG_IMM_RMS_L:
+       case NAU8825_REG_IMM_RMS_R:
+       case NAU8825_REG_I2C_DEVICE_ID:
+       case NAU8825_REG_SARDOUT_RAM_STATUS:
+       case NAU8825_REG_CHARGE_PUMP_INPUT_READ:
+       case NAU8825_REG_GENERAL_STATUS:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static int nau8825_pump_event(struct snd_soc_dapm_widget *w,
+       struct snd_kcontrol *kcontrol, int event)
+{
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               /* Prevent startup click by letting charge pump to ramp up */
+               msleep(10);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static const char * const nau8825_adc_decimation[] = {
+       "32", "64", "128", "256"
+};
+
+static const struct soc_enum nau8825_adc_decimation_enum =
+       SOC_ENUM_SINGLE(NAU8825_REG_ADC_RATE, NAU8825_ADC_SYNC_DOWN_SFT,
+               ARRAY_SIZE(nau8825_adc_decimation), nau8825_adc_decimation);
+
+static const char * const nau8825_dac_oversampl[] = {
+       "64", "256", "128", "", "32"
+};
+
+static const struct soc_enum nau8825_dac_oversampl_enum =
+       SOC_ENUM_SINGLE(NAU8825_REG_DAC_CTRL1, NAU8825_DAC_OVERSAMPLE_SFT,
+               ARRAY_SIZE(nau8825_dac_oversampl), nau8825_dac_oversampl);
+
+static const DECLARE_TLV_DB_MINMAX_MUTE(adc_vol_tlv, -10300, 2400);
+static const DECLARE_TLV_DB_MINMAX_MUTE(sidetone_vol_tlv, -4200, 0);
+static const DECLARE_TLV_DB_MINMAX(dac_vol_tlv, -5400, 0);
+static const DECLARE_TLV_DB_MINMAX(fepga_gain_tlv, -100, 3600);
+static const DECLARE_TLV_DB_MINMAX_MUTE(crosstalk_vol_tlv, -9600, 2400);
+
+static const struct snd_kcontrol_new nau8825_controls[] = {
+       SOC_SINGLE_TLV("Mic Volume", NAU8825_REG_ADC_DGAIN_CTRL,
+               0, 0xff, 0, adc_vol_tlv),
+       SOC_DOUBLE_TLV("Headphone Bypass Volume", NAU8825_REG_ADC_DGAIN_CTRL,
+               12, 8, 0x0f, 0, sidetone_vol_tlv),
+       SOC_DOUBLE_TLV("Headphone Volume", NAU8825_REG_HSVOL_CTRL,
+               6, 0, 0x3f, 1, dac_vol_tlv),
+       SOC_SINGLE_TLV("Frontend PGA Volume", NAU8825_REG_POWER_UP_CONTROL,
+               8, 37, 0, fepga_gain_tlv),
+       SOC_DOUBLE_TLV("Headphone Crosstalk Volume", NAU8825_REG_DAC_DGAIN_CTRL,
+               0, 8, 0xff, 0, crosstalk_vol_tlv),
+
+       SOC_ENUM("ADC Decimation Rate", nau8825_adc_decimation_enum),
+       SOC_ENUM("DAC Oversampling Rate", nau8825_dac_oversampl_enum),
+};
+
+/* DAC Mux 0x33[9] and 0x34[9] */
+static const char * const nau8825_dac_src[] = {
+       "DACL", "DACR",
+};
+
+static SOC_ENUM_SINGLE_DECL(
+       nau8825_dacl_enum, NAU8825_REG_DACL_CTRL,
+       NAU8825_DACL_CH_SEL_SFT, nau8825_dac_src);
+
+static SOC_ENUM_SINGLE_DECL(
+       nau8825_dacr_enum, NAU8825_REG_DACR_CTRL,
+       NAU8825_DACR_CH_SEL_SFT, nau8825_dac_src);
+
+static const struct snd_kcontrol_new nau8825_dacl_mux =
+       SOC_DAPM_ENUM("DACL Source", nau8825_dacl_enum);
+
+static const struct snd_kcontrol_new nau8825_dacr_mux =
+       SOC_DAPM_ENUM("DACR Source", nau8825_dacr_enum);
+
+
+static const struct snd_soc_dapm_widget nau8825_dapm_widgets[] = {
+       SND_SOC_DAPM_AIF_OUT("AIFTX", "Capture", 0, NAU8825_REG_I2S_PCM_CTRL2,
+               15, 1),
+
+       SND_SOC_DAPM_INPUT("MIC"),
+       SND_SOC_DAPM_MICBIAS("MICBIAS", NAU8825_REG_MIC_BIAS, 8, 0),
+
+       SND_SOC_DAPM_PGA("Frontend PGA", NAU8825_REG_POWER_UP_CONTROL, 14, 0,
+               NULL, 0),
+
+       SND_SOC_DAPM_ADC("ADC", NULL, NAU8825_REG_ENA_CTRL, 8, 0),
+       SND_SOC_DAPM_SUPPLY("ADC Clock", NAU8825_REG_ENA_CTRL, 7, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADC Power", NAU8825_REG_ANALOG_ADC_2, 6, 0, NULL,
+               0),
+
+       /* ADC for button press detection */
+       SND_SOC_DAPM_ADC("SAR", NULL, NAU8825_REG_SAR_CTRL,
+               NAU8825_SAR_ADC_EN_SFT, 0),
+
+       SND_SOC_DAPM_DAC("ADACL", NULL, NAU8825_REG_RDAC, 12, 0),
+       SND_SOC_DAPM_DAC("ADACR", NULL, NAU8825_REG_RDAC, 13, 0),
+       SND_SOC_DAPM_SUPPLY("ADACL Clock", NAU8825_REG_RDAC, 8, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("ADACR Clock", NAU8825_REG_RDAC, 9, 0, NULL, 0),
+
+       SND_SOC_DAPM_DAC("DDACR", NULL, NAU8825_REG_ENA_CTRL,
+               NAU8825_ENABLE_DACR_SFT, 0),
+       SND_SOC_DAPM_DAC("DDACL", NULL, NAU8825_REG_ENA_CTRL,
+               NAU8825_ENABLE_DACL_SFT, 0),
+       SND_SOC_DAPM_SUPPLY("DDAC Clock", NAU8825_REG_ENA_CTRL, 6, 0, NULL, 0),
+
+       SND_SOC_DAPM_MUX("DACL Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacl_mux),
+       SND_SOC_DAPM_MUX("DACR Mux", SND_SOC_NOPM, 0, 0, &nau8825_dacr_mux),
+
+       SND_SOC_DAPM_PGA("HP amp L", NAU8825_REG_CLASSG_CTRL, 1, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("HP amp R", NAU8825_REG_CLASSG_CTRL, 2, 0, NULL, 0),
+       SND_SOC_DAPM_SUPPLY("HP amp power", NAU8825_REG_CLASSG_CTRL, 0, 0, NULL,
+               0),
+
+       SND_SOC_DAPM_SUPPLY("Charge Pump", NAU8825_REG_CHARGE_PUMP, 5, 0,
+               nau8825_pump_event, SND_SOC_DAPM_POST_PMU),
+
+       SND_SOC_DAPM_PGA("Output Driver R Stage 1",
+               NAU8825_REG_POWER_UP_CONTROL, 5, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Output Driver L Stage 1",
+               NAU8825_REG_POWER_UP_CONTROL, 4, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Output Driver R Stage 2",
+               NAU8825_REG_POWER_UP_CONTROL, 3, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("Output Driver L Stage 2",
+               NAU8825_REG_POWER_UP_CONTROL, 2, 0, NULL, 0),
+       SND_SOC_DAPM_PGA_S("Output Driver R Stage 3", 1,
+               NAU8825_REG_POWER_UP_CONTROL, 1, 0, NULL, 0),
+       SND_SOC_DAPM_PGA_S("Output Driver L Stage 3", 1,
+               NAU8825_REG_POWER_UP_CONTROL, 0, 0, NULL, 0),
+
+       SND_SOC_DAPM_PGA_S("Output DACL", 2, NAU8825_REG_CHARGE_PUMP, 8, 1, NULL, 0),
+       SND_SOC_DAPM_PGA_S("Output DACR", 2, NAU8825_REG_CHARGE_PUMP, 9, 1, NULL, 0),
+
+       SND_SOC_DAPM_OUTPUT("HPOL"),
+       SND_SOC_DAPM_OUTPUT("HPOR"),
+};
+
+static const struct snd_soc_dapm_route nau8825_dapm_routes[] = {
+       {"Frontend PGA", NULL, "MIC"},
+       {"ADC", NULL, "Frontend PGA"},
+       {"ADC", NULL, "ADC Clock"},
+       {"ADC", NULL, "ADC Power"},
+       {"AIFTX", NULL, "ADC"},
+
+       {"DDACL", NULL, "Playback"},
+       {"DDACR", NULL, "Playback"},
+       {"DDACL", NULL, "DDAC Clock"},
+       {"DDACR", NULL, "DDAC Clock"},
+       {"DACL Mux", "DACL", "DDACL"},
+       {"DACL Mux", "DACR", "DDACR"},
+       {"DACR Mux", "DACL", "DDACL"},
+       {"DACR Mux", "DACR", "DDACR"},
+       {"HP amp L", NULL, "DACL Mux"},
+       {"HP amp R", NULL, "DACR Mux"},
+       {"HP amp L", NULL, "HP amp power"},
+       {"HP amp R", NULL, "HP amp power"},
+       {"ADACL", NULL, "HP amp L"},
+       {"ADACR", NULL, "HP amp R"},
+       {"ADACL", NULL, "ADACL Clock"},
+       {"ADACR", NULL, "ADACR Clock"},
+       {"Output Driver L Stage 1", NULL, "ADACL"},
+       {"Output Driver R Stage 1", NULL, "ADACR"},
+       {"Output Driver L Stage 2", NULL, "Output Driver L Stage 1"},
+       {"Output Driver R Stage 2", NULL, "Output Driver R Stage 1"},
+       {"Output Driver L Stage 3", NULL, "Output Driver L Stage 2"},
+       {"Output Driver R Stage 3", NULL, "Output Driver R Stage 2"},
+       {"Output DACL", NULL, "Output Driver L Stage 3"},
+       {"Output DACR", NULL, "Output Driver R Stage 3"},
+       {"HPOL", NULL, "Output DACL"},
+       {"HPOR", NULL, "Output DACR"},
+       {"HPOL", NULL, "Charge Pump"},
+       {"HPOR", NULL, "Charge Pump"},
+};
+
+static int nau8825_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 nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+       unsigned int val_len = 0;
+
+       switch (params_width(params)) {
+       case 16:
+               val_len |= NAU8825_I2S_DL_16;
+               break;
+       case 20:
+               val_len |= NAU8825_I2S_DL_20;
+               break;
+       case 24:
+               val_len |= NAU8825_I2S_DL_24;
+               break;
+       case 32:
+               val_len |= NAU8825_I2S_DL_32;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
+               NAU8825_I2S_DL_MASK, val_len);
+
+       return 0;
+}
+
+static int nau8825_set_dai_fmt(struct snd_soc_dai *codec_dai, unsigned int fmt)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+       unsigned int ctrl1_val = 0, ctrl2_val = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               ctrl2_val |= NAU8825_I2S_MS_MASTER;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_NF:
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               ctrl1_val |= NAU8825_I2S_BP_INV;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               ctrl1_val |= NAU8825_I2S_DF_I2S;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               ctrl1_val |= NAU8825_I2S_DF_LEFT;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               ctrl1_val |= NAU8825_I2S_DF_RIGTH;
+               break;
+       case SND_SOC_DAIFMT_DSP_A:
+               ctrl1_val |= NAU8825_I2S_DF_PCM_AB;
+               break;
+       case SND_SOC_DAIFMT_DSP_B:
+               ctrl1_val |= NAU8825_I2S_DF_PCM_AB;
+               ctrl1_val |= NAU8825_I2S_PCMB_EN;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL1,
+               NAU8825_I2S_DL_MASK | NAU8825_I2S_DF_MASK |
+               NAU8825_I2S_BP_MASK | NAU8825_I2S_PCMB_MASK,
+               ctrl1_val);
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_I2S_PCM_CTRL2,
+               NAU8825_I2S_MS_MASK, ctrl2_val);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops nau8825_dai_ops = {
+       .hw_params      = nau8825_hw_params,
+       .set_fmt        = nau8825_set_dai_fmt,
+};
+
+#define NAU8825_RATES  SNDRV_PCM_RATE_8000_192000
+#define NAU8825_FORMATS        (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE \
+                        | SNDRV_PCM_FMTBIT_S24_3LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver nau8825_dai = {
+       .name = "nau8825-hifi",
+       .playback = {
+               .stream_name     = "Playback",
+               .channels_min    = 1,
+               .channels_max    = 2,
+               .rates           = NAU8825_RATES,
+               .formats         = NAU8825_FORMATS,
+       },
+       .capture = {
+               .stream_name     = "Capture",
+               .channels_min    = 1,
+               .channels_max    = 1,
+               .rates           = NAU8825_RATES,
+               .formats         = NAU8825_FORMATS,
+       },
+       .ops = &nau8825_dai_ops,
+};
+
+/**
+ * nau8825_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 will be routed to the given jack.  Jack can be null to stop
+ * reporting.
+ */
+int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
+                               struct snd_soc_jack *jack)
+{
+       struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+       struct regmap *regmap = nau8825->regmap;
+
+       nau8825->jack = jack;
+
+       /* Ground HP Outputs[1:0], needed for headset auto detection
+        * Enable Automatic Mic/Gnd switching reading on insert interrupt[6]
+        */
+       regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL,
+               NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L,
+               NAU8825_HSD_AUTO_MODE | NAU8825_SPKR_DWN1R | NAU8825_SPKR_DWN1L);
+
+       regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+               NAU8825_IRQ_HEADSET_COMPLETE_EN | NAU8825_IRQ_EJECT_EN, 0);
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(nau8825_enable_jack_detect);
+
+
+static bool nau8825_is_jack_inserted(struct regmap *regmap)
+{
+       int status;
+
+       regmap_read(regmap, NAU8825_REG_I2C_DEVICE_ID, &status);
+       return !(status & NAU8825_GPIO2JD1);
+}
+
+static void nau8825_restart_jack_detection(struct regmap *regmap)
+{
+       /* this will restart the entire jack detection process including MIC/GND
+        * switching and create interrupts. We have to go from 0 to 1 and back
+        * to 0 to restart.
+        */
+       regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+               NAU8825_JACK_DET_RESTART, NAU8825_JACK_DET_RESTART);
+       regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+               NAU8825_JACK_DET_RESTART, 0);
+}
+
+static void nau8825_eject_jack(struct nau8825 *nau8825)
+{
+       struct snd_soc_dapm_context *dapm = nau8825->dapm;
+       struct regmap *regmap = nau8825->regmap;
+
+       snd_soc_dapm_disable_pin(dapm, "SAR");
+       snd_soc_dapm_disable_pin(dapm, "MICBIAS");
+       /* Detach 2kOhm Resistors from MICBIAS to MICGND1/2 */
+       regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+               NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2, 0);
+       /* ground HPL/HPR, MICGRND1/2 */
+       regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0xf, 0xf);
+
+       snd_soc_dapm_sync(dapm);
+}
+
+static int nau8825_button_decode(int value)
+{
+       int buttons = 0;
+
+       /* The chip supports up to 8 buttons, but ALSA defines only 6 buttons */
+       if (value & BIT(0))
+               buttons |= SND_JACK_BTN_0;
+       if (value & BIT(1))
+               buttons |= SND_JACK_BTN_1;
+       if (value & BIT(2))
+               buttons |= SND_JACK_BTN_2;
+       if (value & BIT(3))
+               buttons |= SND_JACK_BTN_3;
+       if (value & BIT(4))
+               buttons |= SND_JACK_BTN_4;
+       if (value & BIT(5))
+               buttons |= SND_JACK_BTN_5;
+
+       return buttons;
+}
+
+static int nau8825_jack_insert(struct nau8825 *nau8825)
+{
+       struct regmap *regmap = nau8825->regmap;
+       struct snd_soc_dapm_context *dapm = nau8825->dapm;
+       int jack_status_reg, mic_detected;
+       int type = 0;
+
+       regmap_read(regmap, NAU8825_REG_GENERAL_STATUS, &jack_status_reg);
+       mic_detected = (jack_status_reg >> 10) & 3;
+
+       switch (mic_detected) {
+       case 0:
+               /* no mic */
+               type = SND_JACK_HEADPHONE;
+               break;
+       case 1:
+               dev_dbg(nau8825->dev, "OMTP (micgnd1) mic connected\n");
+               type = SND_JACK_HEADSET;
+
+               /* Unground MICGND1 */
+               regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2,
+                       1 << 2);
+               /* Attach 2kOhm Resistor from MICBIAS to MICGND1 */
+               regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+                       NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+                       NAU8825_MICBIAS_JKR2);
+               /* Attach SARADC to MICGND1 */
+               regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+                       NAU8825_SAR_INPUT_MASK,
+                       NAU8825_SAR_INPUT_JKR2);
+
+               snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+               snd_soc_dapm_force_enable_pin(dapm, "SAR");
+               snd_soc_dapm_sync(dapm);
+               break;
+       case 2:
+       case 3:
+               dev_dbg(nau8825->dev, "CTIA (micgnd2) mic connected\n");
+               type = SND_JACK_HEADSET;
+
+               /* Unground MICGND2 */
+               regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 3 << 2,
+                       2 << 2);
+               /* Attach 2kOhm Resistor from MICBIAS to MICGND2 */
+               regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+                       NAU8825_MICBIAS_JKSLV | NAU8825_MICBIAS_JKR2,
+                       NAU8825_MICBIAS_JKSLV);
+               /* Attach SARADC to MICGND2 */
+               regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+                       NAU8825_SAR_INPUT_MASK,
+                       NAU8825_SAR_INPUT_JKSLV);
+
+               snd_soc_dapm_force_enable_pin(dapm, "MICBIAS");
+               snd_soc_dapm_force_enable_pin(dapm, "SAR");
+               snd_soc_dapm_sync(dapm);
+               break;
+       }
+
+       if (type & SND_JACK_HEADPHONE) {
+               /* Unground HPL/R */
+               regmap_update_bits(regmap, NAU8825_REG_HSD_CTRL, 0x3, 0);
+       }
+
+       return type;
+}
+
+#define NAU8825_BUTTONS (SND_JACK_BTN_0 | SND_JACK_BTN_1 | \
+               SND_JACK_BTN_2 | SND_JACK_BTN_3)
+
+static irqreturn_t nau8825_interrupt(int irq, void *data)
+{
+       struct nau8825 *nau8825 = (struct nau8825 *)data;
+       struct regmap *regmap = nau8825->regmap;
+       int active_irq, clear_irq = 0, event = 0, event_mask = 0;
+
+       regmap_read(regmap, NAU8825_REG_IRQ_STATUS, &active_irq);
+
+       if ((active_irq & NAU8825_JACK_EJECTION_IRQ_MASK) ==
+               NAU8825_JACK_EJECTION_DETECTED) {
+
+               nau8825_eject_jack(nau8825);
+               event_mask |= SND_JACK_HEADSET;
+               clear_irq = NAU8825_JACK_EJECTION_IRQ_MASK;
+       } else if (active_irq & NAU8825_KEY_SHORT_PRESS_IRQ) {
+               int key_status;
+
+               regmap_read(regmap, NAU8825_REG_INT_CLR_KEY_STATUS,
+                       &key_status);
+
+               /* upper 8 bits of the register are for short pressed keys,
+                * lower 8 bits - for long pressed buttons
+                */
+               nau8825->button_pressed = nau8825_button_decode(
+                       key_status >> 8);
+
+               event |= nau8825->button_pressed;
+               event_mask |= NAU8825_BUTTONS;
+               clear_irq = NAU8825_KEY_SHORT_PRESS_IRQ;
+       } else if (active_irq & NAU8825_KEY_RELEASE_IRQ) {
+               event_mask = NAU8825_BUTTONS;
+               clear_irq = NAU8825_KEY_RELEASE_IRQ;
+       } else if (active_irq & NAU8825_HEADSET_COMPLETION_IRQ) {
+               if (nau8825_is_jack_inserted(regmap)) {
+                       event |= nau8825_jack_insert(nau8825);
+               } else {
+                       dev_warn(nau8825->dev, "Headset completion IRQ fired but no headset connected\n");
+                       nau8825_eject_jack(nau8825);
+               }
+
+               event_mask |= SND_JACK_HEADSET;
+               clear_irq = NAU8825_HEADSET_COMPLETION_IRQ;
+       }
+
+       if (!clear_irq)
+               clear_irq = active_irq;
+       /* clears the rightmost interruption */
+       regmap_write(regmap, NAU8825_REG_INT_CLR_KEY_STATUS, clear_irq);
+
+       if (event_mask)
+               snd_soc_jack_report(nau8825->jack, event, event_mask);
+
+       return IRQ_HANDLED;
+}
+
+static void nau8825_setup_buttons(struct nau8825 *nau8825)
+{
+       struct regmap *regmap = nau8825->regmap;
+
+       regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+               NAU8825_SAR_TRACKING_GAIN_MASK,
+               nau8825->sar_voltage << NAU8825_SAR_TRACKING_GAIN_SFT);
+       regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+               NAU8825_SAR_COMPARE_TIME_MASK,
+               nau8825->sar_compare_time << NAU8825_SAR_COMPARE_TIME_SFT);
+       regmap_update_bits(regmap, NAU8825_REG_SAR_CTRL,
+               NAU8825_SAR_SAMPLING_TIME_MASK,
+               nau8825->sar_sampling_time << NAU8825_SAR_SAMPLING_TIME_SFT);
+
+       regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+               NAU8825_KEYDET_LEVELS_NR_MASK,
+               (nau8825->sar_threshold_num - 1) << NAU8825_KEYDET_LEVELS_NR_SFT);
+       regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+               NAU8825_KEYDET_HYSTERESIS_MASK,
+               nau8825->sar_hysteresis << NAU8825_KEYDET_HYSTERESIS_SFT);
+       regmap_update_bits(regmap, NAU8825_REG_KEYDET_CTRL,
+               NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK,
+               nau8825->key_debounce << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT);
+
+       regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_1,
+               (nau8825->sar_threshold[0] << 8) | nau8825->sar_threshold[1]);
+       regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_2,
+               (nau8825->sar_threshold[2] << 8) | nau8825->sar_threshold[3]);
+       regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_3,
+               (nau8825->sar_threshold[4] << 8) | nau8825->sar_threshold[5]);
+       regmap_write(regmap, NAU8825_REG_VDET_THRESHOLD_4,
+               (nau8825->sar_threshold[6] << 8) | nau8825->sar_threshold[7]);
+
+       /* Enable short press and release interruptions */
+       regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+               NAU8825_IRQ_KEY_SHORT_PRESS_EN | NAU8825_IRQ_KEY_RELEASE_EN,
+               0);
+}
+
+static void nau8825_init_regs(struct nau8825 *nau8825)
+{
+       struct regmap *regmap = nau8825->regmap;
+
+       /* Enable Bias/Vmid */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_BIAS_ADJ,
+               NAU8825_BIAS_VMID, NAU8825_BIAS_VMID);
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_BOOST,
+               NAU8825_GLOBAL_BIAS_EN, NAU8825_GLOBAL_BIAS_EN);
+
+       /* VMID Tieoff */
+       regmap_update_bits(regmap, NAU8825_REG_BIAS_ADJ,
+               NAU8825_BIAS_VMID_SEL_MASK,
+               nau8825->vref_impedance << NAU8825_BIAS_VMID_SEL_SFT);
+       /* Disable Boost Driver, Automatic Short circuit protection enable */
+       regmap_update_bits(regmap, NAU8825_REG_BOOST,
+               NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
+               NAU8825_SHORT_SHUTDOWN_EN,
+               NAU8825_PRECHARGE_DIS | NAU8825_HP_BOOST_G_DIS |
+               NAU8825_SHORT_SHUTDOWN_EN);
+
+       regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+               NAU8825_JKDET_OUTPUT_EN,
+               nau8825->jkdet_enable ? 0 : NAU8825_JKDET_OUTPUT_EN);
+       regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+               NAU8825_JKDET_PULL_EN,
+               nau8825->jkdet_pull_enable ? 0 : NAU8825_JKDET_PULL_EN);
+       regmap_update_bits(regmap, NAU8825_REG_GPIO12_CTRL,
+               NAU8825_JKDET_PULL_UP,
+               nau8825->jkdet_pull_up ? NAU8825_JKDET_PULL_UP : 0);
+       regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+               NAU8825_JACK_POLARITY,
+               /* jkdet_polarity - 1  is for active-low */
+               nau8825->jkdet_polarity ? 0 : NAU8825_JACK_POLARITY);
+
+       regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+               NAU8825_JACK_INSERT_DEBOUNCE_MASK,
+               nau8825->jack_insert_debounce << NAU8825_JACK_INSERT_DEBOUNCE_SFT);
+       regmap_update_bits(regmap, NAU8825_REG_JACK_DET_CTRL,
+               NAU8825_JACK_EJECT_DEBOUNCE_MASK,
+               nau8825->jack_eject_debounce << NAU8825_JACK_EJECT_DEBOUNCE_SFT);
+
+       /* Mask unneeded IRQs: 1 - disable, 0 - enable */
+       regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK, 0x7ff, 0x7ff);
+
+       regmap_update_bits(regmap, NAU8825_REG_MIC_BIAS,
+               NAU8825_MICBIAS_VOLTAGE_MASK, nau8825->micbias_voltage);
+
+       if (nau8825->sar_threshold_num)
+               nau8825_setup_buttons(nau8825);
+
+       /* Default oversampling/decimations settings are unusable
+        * (audible hiss). Set it to something better.
+        */
+       regmap_update_bits(regmap, NAU8825_REG_ADC_RATE,
+               NAU8825_ADC_SYNC_DOWN_MASK, NAU8825_ADC_SYNC_DOWN_128);
+       regmap_update_bits(regmap, NAU8825_REG_DAC_CTRL1,
+               NAU8825_DAC_OVERSAMPLE_MASK, NAU8825_DAC_OVERSAMPLE_128);
+}
+
+static const struct regmap_config nau8825_regmap_config = {
+       .val_bits = 16,
+       .reg_bits = 16,
+
+       .max_register = NAU8825_REG_MAX,
+       .readable_reg = nau8825_readable_reg,
+       .writeable_reg = nau8825_writeable_reg,
+       .volatile_reg = nau8825_volatile_reg,
+
+       .cache_type = REGCACHE_RBTREE,
+       .reg_defaults = nau8825_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(nau8825_reg_defaults),
+};
+
+static int nau8825_codec_probe(struct snd_soc_codec *codec)
+{
+       struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+       nau8825->dapm = dapm;
+
+       /* The interrupt clock is gated by x1[10:8],
+        * one of them needs to be enabled all the time for
+        * interrupts to happen.
+        */
+       snd_soc_dapm_force_enable_pin(dapm, "DDACR");
+       snd_soc_dapm_sync(dapm);
+
+       /* Unmask interruptions. Handler uses dapm object so we can enable
+        * interruptions only after dapm is fully initialized.
+        */
+       regmap_write(nau8825->regmap, NAU8825_REG_INTERRUPT_DIS_CTRL, 0);
+       nau8825_restart_jack_detection(nau8825->regmap);
+
+       return 0;
+}
+
+/**
+ * nau8825_calc_fll_param - Calculate FLL parameters.
+ * @fll_in: external clock provided to codec.
+ * @fs: sampling rate.
+ * @fll_param: Pointer to structure of FLL parameters.
+ *
+ * Calculate FLL parameters to configure codec.
+ *
+ * Returns 0 for success or negative error code.
+ */
+static int nau8825_calc_fll_param(unsigned int fll_in, unsigned int fs,
+               struct nau8825_fll *fll_param)
+{
+       u64 fvco;
+       unsigned int fref, i;
+
+       /* Ensure the reference clock frequency (FREF) is <= 13.5MHz by dividing
+        * freq_in by 1, 2, 4, or 8 using FLL pre-scalar.
+        * FREF = freq_in / NAU8825_FLL_REF_DIV_MASK
+        */
+       for (i = 0; i < ARRAY_SIZE(fll_pre_scalar); i++) {
+               fref = fll_in / fll_pre_scalar[i].param;
+               if (fref <= NAU_FREF_MAX)
+                       break;
+       }
+       if (i == ARRAY_SIZE(fll_pre_scalar))
+               return -EINVAL;
+       fll_param->clk_ref_div = fll_pre_scalar[i].val;
+
+       /* Choose the FLL ratio based on FREF */
+       for (i = 0; i < ARRAY_SIZE(fll_ratio); i++) {
+               if (fref >= fll_ratio[i].param)
+                       break;
+       }
+       if (i == ARRAY_SIZE(fll_ratio))
+               return -EINVAL;
+       fll_param->ratio = fll_ratio[i].val;
+
+       /* Calculate the frequency of DCO (FDCO) given freq_out = 256 * Fs.
+        * FDCO must be within the 90MHz - 100MHz or the FFL cannot be
+        * guaranteed across the full range of operation.
+        * FDCO = freq_out * 2 * mclk_src_scaling
+        */
+       for (i = 0; i < ARRAY_SIZE(mclk_src_scaling); i++) {
+               fvco = 256 * fs * 2 * mclk_src_scaling[i].param;
+               if (NAU_FVCO_MIN < fvco && fvco < NAU_FVCO_MAX)
+                       break;
+       }
+       if (i == ARRAY_SIZE(mclk_src_scaling))
+               return -EINVAL;
+       fll_param->mclk_src = mclk_src_scaling[i].val;
+
+       /* Calculate the FLL 10-bit integer input and the FLL 16-bit fractional
+        * input based on FDCO, FREF and FLL ratio.
+        */
+       fvco = div_u64(fvco << 16, fref * fll_param->ratio);
+       fll_param->fll_int = (fvco >> 16) & 0x3FF;
+       fll_param->fll_frac = fvco & 0xFFFF;
+       return 0;
+}
+
+static void nau8825_fll_apply(struct nau8825 *nau8825,
+               struct nau8825_fll *fll_param)
+{
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+               NAU8825_CLK_MCLK_SRC_MASK, fll_param->mclk_src);
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL1,
+                       NAU8825_FLL_RATIO_MASK, fll_param->ratio);
+       /* FLL 16-bit fractional input */
+       regmap_write(nau8825->regmap, NAU8825_REG_FLL2, fll_param->fll_frac);
+       /* FLL 10-bit integer input */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL3,
+                       NAU8825_FLL_INTEGER_MASK, fll_param->fll_int);
+       /* FLL pre-scaler */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL4,
+                       NAU8825_FLL_REF_DIV_MASK, fll_param->clk_ref_div);
+       /* select divided VCO input */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL5,
+                       NAU8825_FLL_FILTER_SW_MASK, 0x0000);
+       /* FLL sigma delta modulator enable */
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_FLL6,
+                       NAU8825_SDM_EN_MASK, NAU8825_SDM_EN);
+}
+
+/* freq_out must be 256*Fs in order to achieve the best performance */
+static int nau8825_set_pll(struct snd_soc_codec *codec, int pll_id, int source,
+               unsigned int freq_in, unsigned int freq_out)
+{
+       struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+       struct nau8825_fll fll_param;
+       int ret, fs;
+
+       fs = freq_out / 256;
+       ret = nau8825_calc_fll_param(freq_in, fs, &fll_param);
+       if (ret < 0) {
+               dev_err(codec->dev, "Unsupported input clock %d\n", freq_in);
+               return ret;
+       }
+       dev_dbg(codec->dev, "mclk_src=%x ratio=%x fll_frac=%x fll_int=%x clk_ref_div=%x\n",
+               fll_param.mclk_src, fll_param.ratio, fll_param.fll_frac,
+               fll_param.fll_int, fll_param.clk_ref_div);
+
+       nau8825_fll_apply(nau8825, &fll_param);
+       mdelay(2);
+       regmap_update_bits(nau8825->regmap, NAU8825_REG_CLK_DIVIDER,
+                       NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+       return 0;
+}
+
+static int nau8825_configure_sysclk(struct nau8825 *nau8825, int clk_id,
+       unsigned int freq)
+{
+       struct regmap *regmap = nau8825->regmap;
+       int ret;
+
+       switch (clk_id) {
+       case NAU8825_CLK_MCLK:
+               regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+                       NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_MCLK);
+               regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN, 0);
+
+               /* We selected MCLK source but the clock itself managed externally */
+               if (!nau8825->mclk)
+                       break;
+
+               if (!nau8825->mclk_freq) {
+                       ret = clk_prepare_enable(nau8825->mclk);
+                       if (ret) {
+                               dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
+                               return ret;
+                       }
+               }
+
+               if (nau8825->mclk_freq != freq) {
+                       nau8825->mclk_freq = freq;
+
+                       freq = clk_round_rate(nau8825->mclk, freq);
+                       ret = clk_set_rate(nau8825->mclk, freq);
+                       if (ret) {
+                               dev_err(nau8825->dev, "Unable to set mclk rate\n");
+                               return ret;
+                       }
+               }
+
+               break;
+       case NAU8825_CLK_INTERNAL:
+               regmap_update_bits(regmap, NAU8825_REG_FLL6, NAU8825_DCO_EN,
+                       NAU8825_DCO_EN);
+               regmap_update_bits(regmap, NAU8825_REG_CLK_DIVIDER,
+                       NAU8825_CLK_SRC_MASK, NAU8825_CLK_SRC_VCO);
+
+               if (nau8825->mclk_freq) {
+                       clk_disable_unprepare(nau8825->mclk);
+                       nau8825->mclk_freq = 0;
+               }
+
+               break;
+       default:
+               dev_err(nau8825->dev, "Invalid clock id (%d)\n", clk_id);
+               return -EINVAL;
+       }
+
+       dev_dbg(nau8825->dev, "Sysclk is %dHz and clock id is %d\n", freq,
+               clk_id);
+       return 0;
+}
+
+static int nau8825_set_sysclk(struct snd_soc_codec *codec, int clk_id,
+       int source, unsigned int freq, int dir)
+{
+       struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+
+       return nau8825_configure_sysclk(nau8825, clk_id, freq);
+}
+
+static int nau8825_set_bias_level(struct snd_soc_codec *codec,
+                                  enum snd_soc_bias_level level)
+{
+       struct nau8825 *nau8825 = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       switch (level) {
+       case SND_SOC_BIAS_ON:
+               break;
+
+       case SND_SOC_BIAS_PREPARE:
+               break;
+
+       case SND_SOC_BIAS_STANDBY:
+               if (snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF) {
+                       if (nau8825->mclk_freq) {
+                               ret = clk_prepare_enable(nau8825->mclk);
+                               if (ret) {
+                                       dev_err(nau8825->dev, "Unable to prepare codec mclk\n");
+                                       return ret;
+                               }
+                       }
+
+                       ret = regcache_sync(nau8825->regmap);
+                       if (ret) {
+                               dev_err(codec->dev,
+                                       "Failed to sync cache: %d\n", ret);
+                               return ret;
+                       }
+               }
+
+               break;
+
+       case SND_SOC_BIAS_OFF:
+               if (nau8825->mclk_freq)
+                       clk_disable_unprepare(nau8825->mclk);
+
+               regcache_mark_dirty(nau8825->regmap);
+               break;
+       }
+       return 0;
+}
+
+static struct snd_soc_codec_driver nau8825_codec_driver = {
+       .probe = nau8825_codec_probe,
+       .set_sysclk = nau8825_set_sysclk,
+       .set_pll = nau8825_set_pll,
+       .set_bias_level = nau8825_set_bias_level,
+       .suspend_bias_off = true,
+
+       .controls = nau8825_controls,
+       .num_controls = ARRAY_SIZE(nau8825_controls),
+       .dapm_widgets = nau8825_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(nau8825_dapm_widgets),
+       .dapm_routes = nau8825_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(nau8825_dapm_routes),
+};
+
+static void nau8825_reset_chip(struct regmap *regmap)
+{
+       regmap_write(regmap, NAU8825_REG_RESET, 0x00);
+       regmap_write(regmap, NAU8825_REG_RESET, 0x00);
+}
+
+static void nau8825_print_device_properties(struct nau8825 *nau8825)
+{
+       int i;
+       struct device *dev = nau8825->dev;
+
+       dev_dbg(dev, "jkdet-enable:         %d\n", nau8825->jkdet_enable);
+       dev_dbg(dev, "jkdet-pull-enable:    %d\n", nau8825->jkdet_pull_enable);
+       dev_dbg(dev, "jkdet-pull-up:        %d\n", nau8825->jkdet_pull_up);
+       dev_dbg(dev, "jkdet-polarity:       %d\n", nau8825->jkdet_polarity);
+       dev_dbg(dev, "micbias-voltage:      %d\n", nau8825->micbias_voltage);
+       dev_dbg(dev, "vref-impedance:       %d\n", nau8825->vref_impedance);
+
+       dev_dbg(dev, "sar-threshold-num:    %d\n", nau8825->sar_threshold_num);
+       for (i = 0; i < nau8825->sar_threshold_num; i++)
+               dev_dbg(dev, "sar-threshold[%d]=%d\n", i,
+                               nau8825->sar_threshold[i]);
+
+       dev_dbg(dev, "sar-hysteresis:       %d\n", nau8825->sar_hysteresis);
+       dev_dbg(dev, "sar-voltage:          %d\n", nau8825->sar_voltage);
+       dev_dbg(dev, "sar-compare-time:     %d\n", nau8825->sar_compare_time);
+       dev_dbg(dev, "sar-sampling-time:    %d\n", nau8825->sar_sampling_time);
+       dev_dbg(dev, "short-key-debounce:   %d\n", nau8825->key_debounce);
+       dev_dbg(dev, "jack-insert-debounce: %d\n",
+                       nau8825->jack_insert_debounce);
+       dev_dbg(dev, "jack-eject-debounce:  %d\n",
+                       nau8825->jack_eject_debounce);
+}
+
+static int nau8825_read_device_properties(struct device *dev,
+       struct nau8825 *nau8825) {
+
+       nau8825->jkdet_enable = device_property_read_bool(dev,
+               "nuvoton,jkdet-enable");
+       nau8825->jkdet_pull_enable = device_property_read_bool(dev,
+               "nuvoton,jkdet-pull-enable");
+       nau8825->jkdet_pull_up = device_property_read_bool(dev,
+               "nuvoton,jkdet-pull-up");
+       device_property_read_u32(dev, "nuvoton,jkdet-polarity",
+               &nau8825->jkdet_polarity);
+       device_property_read_u32(dev, "nuvoton,micbias-voltage",
+               &nau8825->micbias_voltage);
+       device_property_read_u32(dev, "nuvoton,vref-impedance",
+               &nau8825->vref_impedance);
+       device_property_read_u32(dev, "nuvoton,sar-threshold-num",
+               &nau8825->sar_threshold_num);
+       device_property_read_u32_array(dev, "nuvoton,sar-threshold",
+               nau8825->sar_threshold, nau8825->sar_threshold_num);
+       device_property_read_u32(dev, "nuvoton,sar-hysteresis",
+               &nau8825->sar_hysteresis);
+       device_property_read_u32(dev, "nuvoton,sar-voltage",
+               &nau8825->sar_voltage);
+       device_property_read_u32(dev, "nuvoton,sar-compare-time",
+               &nau8825->sar_compare_time);
+       device_property_read_u32(dev, "nuvoton,sar-sampling-time",
+               &nau8825->sar_sampling_time);
+       device_property_read_u32(dev, "nuvoton,short-key-debounce",
+               &nau8825->key_debounce);
+       device_property_read_u32(dev, "nuvoton,jack-insert-debounce",
+               &nau8825->jack_insert_debounce);
+       device_property_read_u32(dev, "nuvoton,jack-eject-debounce",
+               &nau8825->jack_eject_debounce);
+
+       nau8825->mclk = devm_clk_get(dev, "mclk");
+       if (PTR_ERR(nau8825->mclk) == -EPROBE_DEFER) {
+               return -EPROBE_DEFER;
+       } else if (PTR_ERR(nau8825->mclk) == -ENOENT) {
+               /* The MCLK is managed externally or not used at all */
+               nau8825->mclk = NULL;
+               dev_info(dev, "No 'mclk' clock found, assume MCLK is managed externally");
+       } else if (IS_ERR(nau8825->mclk)) {
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int nau8825_setup_irq(struct nau8825 *nau8825)
+{
+       struct regmap *regmap = nau8825->regmap;
+       int ret;
+
+       /* IRQ Output Enable */
+       regmap_update_bits(regmap, NAU8825_REG_INTERRUPT_MASK,
+               NAU8825_IRQ_OUTPUT_EN, NAU8825_IRQ_OUTPUT_EN);
+
+       /* Enable internal VCO needed for interruptions */
+       nau8825_configure_sysclk(nau8825, NAU8825_CLK_INTERNAL, 0);
+
+       /* Enable DDACR needed for interrupts
+        * It is the same as force_enable_pin("DDACR") we do later
+        */
+       regmap_update_bits(regmap, NAU8825_REG_ENA_CTRL,
+               NAU8825_ENABLE_DACR, NAU8825_ENABLE_DACR);
+
+       /* Chip needs one FSCLK cycle in order to generate interrupts,
+        * as we cannot guarantee one will be provided by the system. Turning
+        * master mode on then off enables us to generate that FSCLK cycle
+        * with a minimum of contention on the clock bus.
+        */
+       regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+               NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_MASTER);
+       regmap_update_bits(regmap, NAU8825_REG_I2S_PCM_CTRL2,
+               NAU8825_I2S_MS_MASK, NAU8825_I2S_MS_SLAVE);
+
+       ret = devm_request_threaded_irq(nau8825->dev, nau8825->irq, NULL,
+               nau8825_interrupt, IRQF_TRIGGER_LOW | IRQF_ONESHOT,
+               "nau8825", nau8825);
+
+       if (ret) {
+               dev_err(nau8825->dev, "Cannot request irq %d (%d)\n",
+                       nau8825->irq, ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int nau8825_i2c_probe(struct i2c_client *i2c,
+       const struct i2c_device_id *id)
+{
+       struct device *dev = &i2c->dev;
+       struct nau8825 *nau8825 = dev_get_platdata(&i2c->dev);
+       int ret, value;
+
+       if (!nau8825) {
+               nau8825 = devm_kzalloc(dev, sizeof(*nau8825), GFP_KERNEL);
+               if (!nau8825)
+                       return -ENOMEM;
+               ret = nau8825_read_device_properties(dev, nau8825);
+               if (ret)
+                       return ret;
+       }
+
+       i2c_set_clientdata(i2c, nau8825);
+
+       nau8825->regmap = devm_regmap_init_i2c(i2c, &nau8825_regmap_config);
+       if (IS_ERR(nau8825->regmap))
+               return PTR_ERR(nau8825->regmap);
+       nau8825->dev = dev;
+       nau8825->irq = i2c->irq;
+
+       nau8825_print_device_properties(nau8825);
+
+       nau8825_reset_chip(nau8825->regmap);
+       ret = regmap_read(nau8825->regmap, NAU8825_REG_I2C_DEVICE_ID, &value);
+       if (ret < 0) {
+               dev_err(dev, "Failed to read device id from the NAU8825: %d\n",
+                       ret);
+               return ret;
+       }
+       if ((value & NAU8825_SOFTWARE_ID_MASK) !=
+                       NAU8825_SOFTWARE_ID_NAU8825) {
+               dev_err(dev, "Not a NAU8825 chip\n");
+               return -ENODEV;
+       }
+
+       nau8825_init_regs(nau8825);
+
+       if (i2c->irq)
+               nau8825_setup_irq(nau8825);
+
+       return snd_soc_register_codec(&i2c->dev, &nau8825_codec_driver,
+               &nau8825_dai, 1);
+}
+
+static int nau8825_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static const struct i2c_device_id nau8825_i2c_ids[] = {
+       { "nau8825", 0 },
+       { }
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id nau8825_of_ids[] = {
+       { .compatible = "nuvoton,nau8825", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, nau8825_of_ids);
+#endif
+
+#ifdef CONFIG_ACPI
+static const struct acpi_device_id nau8825_acpi_match[] = {
+       { "10508825", 0 },
+       {},
+};
+MODULE_DEVICE_TABLE(acpi, nau8825_acpi_match);
+#endif
+
+static struct i2c_driver nau8825_driver = {
+       .driver = {
+               .name = "nau8825",
+               .of_match_table = of_match_ptr(nau8825_of_ids),
+               .acpi_match_table = ACPI_PTR(nau8825_acpi_match),
+       },
+       .probe = nau8825_i2c_probe,
+       .remove = nau8825_i2c_remove,
+       .id_table = nau8825_i2c_ids,
+};
+module_i2c_driver(nau8825_driver);
+
+MODULE_DESCRIPTION("ASoC nau8825 driver");
+MODULE_AUTHOR("Anatol Pomozov <anatol@chromium.org>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/nau8825.h b/sound/soc/codecs/nau8825.h
new file mode 100644 (file)
index 0000000..dff8edb
--- /dev/null
@@ -0,0 +1,341 @@
+/*
+ * NAU8825 ALSA SoC audio driver
+ *
+ * Copyright 2015 Google Inc.
+ * Author: Anatol Pomozov <anatol.pomozov@chrominium.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.
+ */
+
+#ifndef __NAU8825_H__
+#define __NAU8825_H__
+
+#define NAU8825_REG_RESET              0x00
+#define NAU8825_REG_ENA_CTRL           0x01
+#define NAU8825_REG_CLK_DIVIDER                0x03
+#define NAU8825_REG_FLL1               0x04
+#define NAU8825_REG_FLL2               0x05
+#define NAU8825_REG_FLL3               0x06
+#define NAU8825_REG_FLL4               0x07
+#define NAU8825_REG_FLL5               0x08
+#define NAU8825_REG_FLL6               0x09
+#define NAU8825_REG_FLL_VCO_RSV                0x0a
+#define NAU8825_REG_HSD_CTRL           0x0c
+#define NAU8825_REG_JACK_DET_CTRL              0x0d
+#define NAU8825_REG_INTERRUPT_MASK             0x0f
+#define NAU8825_REG_IRQ_STATUS         0x10
+#define NAU8825_REG_INT_CLR_KEY_STATUS         0x11
+#define NAU8825_REG_INTERRUPT_DIS_CTRL         0x12
+#define NAU8825_REG_SAR_CTRL           0x13
+#define NAU8825_REG_KEYDET_CTRL                0x14
+#define NAU8825_REG_VDET_THRESHOLD_1           0x15
+#define NAU8825_REG_VDET_THRESHOLD_2           0x16
+#define NAU8825_REG_VDET_THRESHOLD_3           0x17
+#define NAU8825_REG_VDET_THRESHOLD_4           0x18
+#define NAU8825_REG_GPIO34_CTRL                0x19
+#define NAU8825_REG_GPIO12_CTRL                0x1a
+#define NAU8825_REG_TDM_CTRL           0x1b
+#define NAU8825_REG_I2S_PCM_CTRL1              0x1c
+#define NAU8825_REG_I2S_PCM_CTRL2              0x1d
+#define NAU8825_REG_LEFT_TIME_SLOT             0x1e
+#define NAU8825_REG_RIGHT_TIME_SLOT            0x1f
+#define NAU8825_REG_BIQ_CTRL           0x20
+#define NAU8825_REG_BIQ_COF1           0x21
+#define NAU8825_REG_BIQ_COF2           0x22
+#define NAU8825_REG_BIQ_COF3           0x23
+#define NAU8825_REG_BIQ_COF4           0x24
+#define NAU8825_REG_BIQ_COF5           0x25
+#define NAU8825_REG_BIQ_COF6           0x26
+#define NAU8825_REG_BIQ_COF7           0x27
+#define NAU8825_REG_BIQ_COF8           0x28
+#define NAU8825_REG_BIQ_COF9           0x29
+#define NAU8825_REG_BIQ_COF10          0x2a
+#define NAU8825_REG_ADC_RATE           0x2b
+#define NAU8825_REG_DAC_CTRL1          0x2c
+#define NAU8825_REG_DAC_CTRL2          0x2d
+#define NAU8825_REG_DAC_DGAIN_CTRL             0x2f
+#define NAU8825_REG_ADC_DGAIN_CTRL             0x30
+#define NAU8825_REG_MUTE_CTRL          0x31
+#define NAU8825_REG_HSVOL_CTRL         0x32
+#define NAU8825_REG_DACL_CTRL          0x33
+#define NAU8825_REG_DACR_CTRL          0x34
+#define NAU8825_REG_ADC_DRC_KNEE_IP12          0x38
+#define NAU8825_REG_ADC_DRC_KNEE_IP34          0x39
+#define NAU8825_REG_ADC_DRC_SLOPES             0x3a
+#define NAU8825_REG_ADC_DRC_ATKDCY             0x3b
+#define NAU8825_REG_DAC_DRC_KNEE_IP12          0x45
+#define NAU8825_REG_DAC_DRC_KNEE_IP34          0x46
+#define NAU8825_REG_DAC_DRC_SLOPES             0x47
+#define NAU8825_REG_DAC_DRC_ATKDCY             0x48
+#define NAU8825_REG_IMM_MODE_CTRL              0x4c
+#define NAU8825_REG_IMM_RMS_L          0x4d
+#define NAU8825_REG_IMM_RMS_R          0x4e
+#define NAU8825_REG_CLASSG_CTRL                0x50
+#define NAU8825_REG_OPT_EFUSE_CTRL             0x51
+#define NAU8825_REG_MISC_CTRL          0x55
+#define NAU8825_REG_I2C_DEVICE_ID              0x58
+#define NAU8825_REG_SARDOUT_RAM_STATUS         0x59
+#define NAU8825_REG_BIAS_ADJ           0x66
+#define NAU8825_REG_TRIM_SETTINGS              0x68
+#define NAU8825_REG_ANALOG_CONTROL_1           0x69
+#define NAU8825_REG_ANALOG_CONTROL_2           0x6a
+#define NAU8825_REG_ANALOG_ADC_1               0x71
+#define NAU8825_REG_ANALOG_ADC_2               0x72
+#define NAU8825_REG_RDAC               0x73
+#define NAU8825_REG_MIC_BIAS           0x74
+#define NAU8825_REG_BOOST              0x76
+#define NAU8825_REG_FEPGA              0x77
+#define NAU8825_REG_POWER_UP_CONTROL           0x7f
+#define NAU8825_REG_CHARGE_PUMP                0x80
+#define NAU8825_REG_CHARGE_PUMP_INPUT_READ             0x81
+#define NAU8825_REG_GENERAL_STATUS             0x82
+#define NAU8825_REG_MAX                NAU8825_REG_GENERAL_STATUS
+
+/* ENA_CTRL (0x1) */
+#define NAU8825_ENABLE_DACR_SFT        10
+#define NAU8825_ENABLE_DACR    (1 << NAU8825_ENABLE_DACR_SFT)
+#define NAU8825_ENABLE_DACL_SFT        9
+#define NAU8825_ENABLE_ADC_SFT 8
+#define NAU8825_ENABLE_SAR_SFT 1
+
+/* CLK_DIVIDER (0x3) */
+#define NAU8825_CLK_SRC_SFT                    15
+#define NAU8825_CLK_SRC_MASK                   (1 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_SRC_VCO                    (1 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_SRC_MCLK                   (0 << NAU8825_CLK_SRC_SFT)
+#define NAU8825_CLK_MCLK_SRC_MASK              (0xf << 0)
+
+/* FLL1 (0x04) */
+#define NAU8825_FLL_RATIO_MASK                 (0x7f << 0)
+
+/* FLL3 (0x06) */
+#define NAU8825_FLL_INTEGER_MASK               (0x3ff << 0)
+
+/* FLL4 (0x07) */
+#define NAU8825_FLL_REF_DIV_MASK               (0x3 << 10)
+
+/* FLL5 (0x08) */
+#define NAU8825_FLL_FILTER_SW_MASK             (0x1 << 14)
+
+/* FLL6 (0x9) */
+#define NAU8825_DCO_EN_MASK                    (0x1 << 15)
+#define NAU8825_DCO_EN                         (0x1 << 15)
+#define NAU8825_DCO_DIS                                (0x0 << 15)
+#define NAU8825_SDM_EN_MASK                    (0x1 << 14)
+#define NAU8825_SDM_EN                         (0x1 << 14)
+#define NAU8825_SDM_DIS                                (0x0 << 14)
+
+/* HSD_CTRL (0xc) */
+#define NAU8825_HSD_AUTO_MODE  (1 << 6)
+/* 0 - short to GND, 1 - open */
+#define NAU8825_SPKR_DWN1R     (1 << 1)
+#define NAU8825_SPKR_DWN1L     (1 << 0)
+
+/* JACK_DET_CTRL (0xd) */
+#define NAU8825_JACK_DET_RESTART       (1 << 9)
+#define NAU8825_JACK_INSERT_DEBOUNCE_SFT       5
+#define NAU8825_JACK_INSERT_DEBOUNCE_MASK      (0x7 << NAU8825_JACK_INSERT_DEBOUNCE_SFT)
+#define NAU8825_JACK_EJECT_DEBOUNCE_SFT                2
+#define NAU8825_JACK_EJECT_DEBOUNCE_MASK       (0x7 << NAU8825_JACK_EJECT_DEBOUNCE_SFT)
+#define NAU8825_JACK_POLARITY  (1 << 1) /* 0 - active low, 1 - active high */
+
+/* INTERRUPT_MASK (0xf) */
+#define NAU8825_IRQ_OUTPUT_EN (1 << 11)
+#define NAU8825_IRQ_HEADSET_COMPLETE_EN (1 << 10)
+#define NAU8825_IRQ_KEY_RELEASE_EN (1 << 7)
+#define NAU8825_IRQ_KEY_SHORT_PRESS_EN (1 << 5)
+#define NAU8825_IRQ_EJECT_EN (1 << 2)
+
+/* IRQ_STATUS (0x10) */
+#define NAU8825_HEADSET_COMPLETION_IRQ (1 << 10)
+#define NAU8825_SHORT_CIRCUIT_IRQ      (1 << 9)
+#define NAU8825_IMPEDANCE_MEAS_IRQ     (1 << 8)
+#define NAU8825_KEY_IRQ_MASK   (0x7 << 5)
+#define NAU8825_KEY_RELEASE_IRQ        (1 << 7)
+#define NAU8825_KEY_LONG_PRESS_IRQ     (1 << 6)
+#define NAU8825_KEY_SHORT_PRESS_IRQ    (1 << 5)
+#define NAU8825_MIC_DETECTION_IRQ      (1 << 4)
+#define NAU8825_JACK_EJECTION_IRQ_MASK (3 << 2)
+#define NAU8825_JACK_EJECTION_DETECTED (1 << 2)
+#define NAU8825_JACK_INSERTION_IRQ_MASK        (3 << 0)
+#define NAU8825_JACK_INSERTION_DETECTED        (1 << 0)
+
+/* INTERRUPT_DIS_CTRL (0x12) */
+#define NAU8825_IRQ_HEADSET_COMPLETE_DIS (1 << 10)
+#define NAU8825_IRQ_KEY_RELEASE_DIS (1 << 7)
+#define NAU8825_IRQ_KEY_SHORT_PRESS_DIS (1 << 5)
+#define NAU8825_IRQ_EJECT_DIS (1 << 2)
+
+/* SAR_CTRL (0x13) */
+#define NAU8825_SAR_ADC_EN_SFT 12
+#define NAU8825_SAR_ADC_EN     (1 << NAU8825_SAR_ADC_EN_SFT)
+#define NAU8825_SAR_INPUT_MASK (1 << 11)
+#define NAU8825_SAR_INPUT_JKSLV        (1 << 11)
+#define NAU8825_SAR_INPUT_JKR2 (0 << 11)
+#define NAU8825_SAR_TRACKING_GAIN_SFT  8
+#define NAU8825_SAR_TRACKING_GAIN_MASK (0x7 << NAU8825_SAR_TRACKING_GAIN_SFT)
+#define NAU8825_SAR_COMPARE_TIME_SFT   2
+#define NAU8825_SAR_COMPARE_TIME_MASK  (3 << 2)
+#define NAU8825_SAR_SAMPLING_TIME_SFT  0
+#define NAU8825_SAR_SAMPLING_TIME_MASK (3 << 0)
+
+/* KEYDET_CTRL (0x14) */
+#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT   12
+#define NAU8825_KEYDET_SHORTKEY_DEBOUNCE_MASK  (0x3 << NAU8825_KEYDET_SHORTKEY_DEBOUNCE_SFT)
+#define NAU8825_KEYDET_LEVELS_NR_SFT   8
+#define NAU8825_KEYDET_LEVELS_NR_MASK  (0x7 << 8)
+#define NAU8825_KEYDET_HYSTERESIS_SFT  0
+#define NAU8825_KEYDET_HYSTERESIS_MASK 0xf
+
+/* GPIO12_CTRL (0x1a) */
+#define NAU8825_JKDET_PULL_UP  (1 << 11) /* 0 - pull down, 1 - pull up */
+#define NAU8825_JKDET_PULL_EN  (1 << 9) /* 0 - enable pull, 1 - disable */
+#define NAU8825_JKDET_OUTPUT_EN        (1 << 8) /* 0 - enable input, 1 - enable output */
+
+/* I2S_PCM_CTRL1 (0x1c) */
+#define NAU8825_I2S_BP_SFT     7
+#define NAU8825_I2S_BP_MASK    (1 << NAU8825_I2S_BP_SFT)
+#define NAU8825_I2S_BP_INV     (1 << NAU8825_I2S_BP_SFT)
+#define NAU8825_I2S_PCMB_SFT   6
+#define NAU8825_I2S_PCMB_MASK  (1 << NAU8825_I2S_PCMB_SFT)
+#define NAU8825_I2S_PCMB_EN    (1 << NAU8825_I2S_PCMB_SFT)
+#define NAU8825_I2S_DL_SFT     2
+#define NAU8825_I2S_DL_MASK    (0x3 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_16      (0 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_20      (1 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_24      (2 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DL_32      (3 << NAU8825_I2S_DL_SFT)
+#define NAU8825_I2S_DF_SFT     0
+#define NAU8825_I2S_DF_MASK    (0x3 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_RIGTH   (0 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_LEFT    (1 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_I2S     (2 << NAU8825_I2S_DF_SFT)
+#define NAU8825_I2S_DF_PCM_AB  (3 << NAU8825_I2S_DF_SFT)
+
+/* I2S_PCM_CTRL2 (0x1d) */
+#define NAU8825_I2S_TRISTATE   (1 << 15) /* 0 - normal mode, 1 - Hi-Z output */
+#define NAU8825_I2S_MS_SFT     3
+#define NAU8825_I2S_MS_MASK    (1 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_MS_MASTER  (1 << NAU8825_I2S_MS_SFT)
+#define NAU8825_I2S_MS_SLAVE   (0 << NAU8825_I2S_MS_SFT)
+
+/* ADC_RATE (0x2b) */
+#define NAU8825_ADC_SYNC_DOWN_SFT      0
+#define NAU8825_ADC_SYNC_DOWN_MASK     0x3
+#define NAU8825_ADC_SYNC_DOWN_32       0
+#define NAU8825_ADC_SYNC_DOWN_64       1
+#define NAU8825_ADC_SYNC_DOWN_128      2
+#define NAU8825_ADC_SYNC_DOWN_256      3
+
+/* DAC_CTRL1 (0x2c) */
+#define NAU8825_DAC_CLIP_OFF   (1 << 7)
+#define NAU8825_DAC_OVERSAMPLE_SFT     0
+#define NAU8825_DAC_OVERSAMPLE_MASK    0x7
+#define NAU8825_DAC_OVERSAMPLE_64      0
+#define NAU8825_DAC_OVERSAMPLE_256     1
+#define NAU8825_DAC_OVERSAMPLE_128     2
+#define NAU8825_DAC_OVERSAMPLE_32      4
+
+/* MUTE_CTRL (0x31) */
+#define NAU8825_DAC_ZERO_CROSSING_EN   (1 << 9)
+#define NAU8825_DAC_SOFT_MUTE  (1 << 9)
+
+/* HSVOL_CTRL (0x32) */
+#define NAU8825_HP_MUTE        (1 << 15)
+
+/* DACL_CTRL (0x33) */
+#define NAU8825_DACL_CH_SEL_SFT        9
+
+/* DACR_CTRL (0x34) */
+#define NAU8825_DACR_CH_SEL_SFT        9
+
+/* I2C_DEVICE_ID (0x58) */
+#define NAU8825_GPIO2JD1       (1 << 7)
+#define NAU8825_SOFTWARE_ID_MASK       0x3
+#define NAU8825_SOFTWARE_ID_NAU8825    0x0
+
+/* BIAS_ADJ (0x66) */
+#define NAU8825_BIAS_VMID      (1 << 6)
+#define NAU8825_BIAS_VMID_SEL_SFT      4
+#define NAU8825_BIAS_VMID_SEL_MASK     (3 << NAU8825_BIAS_VMID_SEL_SFT)
+
+/* ANALOG_CONTROL_2 (0x6a) */
+#define NAU8825_HP_NON_CLASSG_CURRENT_2xADJ (1 << 12)
+#define NAU8825_DAC_CAPACITOR_MSB (1 << 1)
+#define NAU8825_DAC_CAPACITOR_LSB (1 << 0)
+
+/* ANALOG_ADC_2 (0x72) */
+#define NAU8825_ADC_VREFSEL_MASK       (0x3 << 8)
+#define NAU8825_ADC_VREFSEL_ANALOG     (0 << 8)
+#define NAU8825_ADC_VREFSEL_VMID       (1 << 8)
+#define NAU8825_ADC_VREFSEL_VMID_PLUS_0_5DB    (2 << 8)
+#define NAU8825_ADC_VREFSEL_VMID_PLUS_1DB      (3 << 8)
+#define NAU8825_POWERUP_ADCL   (1 << 6)
+
+/* MIC_BIAS (0x74) */
+#define NAU8825_MICBIAS_JKSLV  (1 << 14)
+#define NAU8825_MICBIAS_JKR2   (1 << 12)
+#define NAU8825_MICBIAS_POWERUP_SFT    8
+#define NAU8825_MICBIAS_VOLTAGE_SFT    0
+#define NAU8825_MICBIAS_VOLTAGE_MASK   0x7
+
+/* BOOST (0x76) */
+#define NAU8825_PRECHARGE_DIS  (1 << 13)
+#define NAU8825_GLOBAL_BIAS_EN (1 << 12)
+#define NAU8825_HP_BOOST_G_DIS (1 << 8)
+#define NAU8825_SHORT_SHUTDOWN_EN      (1 << 6)
+
+/* POWER_UP_CONTROL (0x7f) */
+#define NAU8825_POWERUP_INTEGR_R       (1 << 5)
+#define NAU8825_POWERUP_INTEGR_L       (1 << 4)
+#define NAU8825_POWERUP_DRV_IN_R       (1 << 3)
+#define NAU8825_POWERUP_DRV_IN_L       (1 << 2)
+#define NAU8825_POWERUP_HP_DRV_R       (1 << 1)
+#define NAU8825_POWERUP_HP_DRV_L       (1 << 0)
+
+/* CHARGE_PUMP (0x80) */
+#define NAU8825_JAMNODCLOW     (1 << 10)
+#define NAU8825_POWER_DOWN_DACR        (1 << 9)
+#define NAU8825_POWER_DOWN_DACL        (1 << 8)
+#define NAU8825_CHANRGE_PUMP_EN        (1 << 5)
+
+
+/* System Clock Source */
+enum {
+       NAU8825_CLK_MCLK = 0,
+       NAU8825_CLK_INTERNAL,
+};
+
+struct nau8825 {
+       struct device *dev;
+       struct regmap *regmap;
+       struct snd_soc_dapm_context *dapm;
+       struct snd_soc_jack *jack;
+       struct clk *mclk;
+       int irq;
+       int mclk_freq; /* 0 - mclk is disabled */
+       int button_pressed;
+       int micbias_voltage;
+       int vref_impedance;
+       bool jkdet_enable;
+       bool jkdet_pull_enable;
+       bool jkdet_pull_up;
+       int jkdet_polarity;
+       int sar_threshold_num;
+       int sar_threshold[8];
+       int sar_hysteresis;
+       int sar_voltage;
+       int sar_compare_time;
+       int sar_sampling_time;
+       int key_debounce;
+       int jack_insert_debounce;
+       int jack_eject_debounce;
+};
+
+int nau8825_enable_jack_detect(struct snd_soc_codec *codec,
+                               struct snd_soc_jack *jack);
+
+
+#endif  /* __NAU8825_H__ */
index 91d5166bd3a1f883e8d3a7f5bd60511b7f50a781..a4b910efbd455eba52f086053ea9a6ec4789ef03 100644 (file)
  */
 
 #include <linux/module.h>
-#include <linux/moduleparam.h>
-#include <linux/init.h>
-#include <linux/delay.h>
-#include <linux/pm.h>
 #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>
-#include <sound/pcm_params.h>
-#include <sound/soc.h>
-#include <sound/soc-dapm.h>
-#include <sound/initval.h>
-#include <sound/tlv.h>
-#include <sound/jack.h>
-#include <linux/workqueue.h>
-#include <sound/hda_verbs.h>
+#include <linux/regmap.h>
 
 #include "rl6347a.h"
 
index 1cb56e50b7f339f70eb23874943be5c05fc10ad0..e127919cb36b3334aac843abb7cd4391b092ed74 100644 (file)
@@ -12,6 +12,8 @@
 #ifndef __RL6347A_H__
 #define __RL6347A_H__
 
+#include <sound/hda_verbs.h>
+
 #define VERB_CMD(V, N, D) ((N << 20) | (V << 8) | D)
 
 #define RL6347A_VENDOR_REGISTERS       0x20
index bd9365885f73f508fa88de0908215473efaaddb3..af2ed774b5529919486587e732ba6924dd39bb4a 100644 (file)
@@ -29,7 +29,6 @@
 #include <sound/jack.h>
 #include <linux/workqueue.h>
 #include <sound/rt286.h>
-#include <sound/hda_verbs.h>
 
 #include "rl6347a.h"
 #include "rt286.h"
@@ -38,7 +37,7 @@
 #define RT288_VENDOR_ID 0x10ec0288
 
 struct rt286_priv {
-       const struct reg_default *index_cache;
+       struct reg_default *index_cache;
        int index_cache_size;
        struct regmap *regmap;
        struct snd_soc_codec *codec;
@@ -1161,7 +1160,11 @@ static int rt286_i2c_probe(struct i2c_client *i2c,
                return -ENODEV;
        }
 
-       rt286->index_cache = rt286_index_def;
+       rt286->index_cache = devm_kmemdup(&i2c->dev, rt286_index_def,
+                                         sizeof(rt286_index_def), GFP_KERNEL);
+       if (!rt286->index_cache)
+               return -ENOMEM;
+
        rt286->index_cache_size = INDEX_CACHE_SIZE;
        rt286->i2c = i2c;
        i2c_set_clientdata(i2c, rt286);
index f823eb502367dccad4b93e71ac7e06d30c0bccdf..b3f795c60749b8f8473184f0872329d4578b8e48 100644 (file)
@@ -28,7 +28,6 @@
 #include <sound/jack.h>
 #include <linux/workqueue.h>
 #include <sound/rt298.h>
-#include <sound/hda_verbs.h>
 
 #include "rl6347a.h"
 #include "rt298.h"
@@ -49,7 +48,7 @@ struct rt298_priv {
        int is_hp_in;
 };
 
-static struct reg_default rt298_index_def[] = {
+static const struct reg_default rt298_index_def[] = {
        { 0x01, 0xa5a8 },
        { 0x02, 0x8e95 },
        { 0x03, 0x0002 },
@@ -129,7 +128,7 @@ static bool rt298_volatile_register(struct device *dev, unsigned int reg)
        case VERB_CMD(AC_VERB_GET_EAPD_BTLENABLE, RT298_HP_OUT, 0):
                return true;
        default:
-               return true;
+               return false;
        }
 
 
@@ -1165,7 +1164,11 @@ static int rt298_i2c_probe(struct i2c_client *i2c,
                return -ENODEV;
        }
 
-       rt298->index_cache = rt298_index_def;
+       rt298->index_cache = devm_kmemdup(&i2c->dev, rt298_index_def,
+                                         sizeof(rt298_index_def), GFP_KERNEL);
+       if (!rt298->index_cache)
+               return -ENOMEM;
+
        rt298->index_cache_size = INDEX_CACHE_SIZE;
        rt298->i2c = i2c;
        i2c_set_clientdata(i2c, rt298);
index e1ceeb885f7d64858248b07206db23796a08f084..f2beb1aa5763dfe99dc6de821f4c9367446773a7 100644 (file)
@@ -405,11 +405,14 @@ static const struct snd_kcontrol_new rt5640_snd_controls[] = {
        SOC_DOUBLE_TLV("DAC1 Playback Volume", RT5640_DAC1_DIG_VOL,
                        RT5640_L_VOL_SFT, RT5640_R_VOL_SFT,
                        175, 0, dac_vol_tlv),
-       /* IN1/IN2 Control */
+       /* IN1/IN2/IN3 Control */
        SOC_SINGLE_TLV("IN1 Boost", RT5640_IN1_IN2,
                RT5640_BST_SFT1, 8, 0, bst_tlv),
        SOC_SINGLE_TLV("IN2 Boost", RT5640_IN3_IN4,
                RT5640_BST_SFT2, 8, 0, bst_tlv),
+       SOC_SINGLE_TLV("IN3 Boost", RT5640_IN1_IN2,
+               RT5640_BST_SFT2, 8, 0, bst_tlv),
+
        /* INL/INR Volume Control */
        SOC_DOUBLE_TLV("IN Capture Volume", RT5640_INL_INR_VOL,
                        RT5640_INL_VOL_SFT, RT5640_INR_VOL_SFT,
@@ -598,6 +601,8 @@ static const struct snd_kcontrol_new rt5640_rec_l_mix[] = {
                        RT5640_M_HP_L_RM_L_SFT, 1, 1),
        SOC_DAPM_SINGLE("INL Switch", RT5640_REC_L2_MIXER,
                        RT5640_M_IN_L_RM_L_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_L2_MIXER,
+                       RT5640_M_BST2_RM_L_SFT, 1, 1),
        SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_L2_MIXER,
                        RT5640_M_BST4_RM_L_SFT, 1, 1),
        SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_L2_MIXER,
@@ -611,6 +616,8 @@ static const struct snd_kcontrol_new rt5640_rec_r_mix[] = {
                        RT5640_M_HP_R_RM_R_SFT, 1, 1),
        SOC_DAPM_SINGLE("INR Switch", RT5640_REC_R2_MIXER,
                        RT5640_M_IN_R_RM_R_SFT, 1, 1),
+       SOC_DAPM_SINGLE("BST3 Switch", RT5640_REC_R2_MIXER,
+                       RT5640_M_BST2_RM_R_SFT, 1, 1),
        SOC_DAPM_SINGLE("BST2 Switch", RT5640_REC_R2_MIXER,
                        RT5640_M_BST4_RM_R_SFT, 1, 1),
        SOC_DAPM_SINGLE("BST1 Switch", RT5640_REC_R2_MIXER,
@@ -1065,6 +1072,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
        SND_SOC_DAPM_INPUT("IN1N"),
        SND_SOC_DAPM_INPUT("IN2P"),
        SND_SOC_DAPM_INPUT("IN2N"),
+       SND_SOC_DAPM_INPUT("IN3P"),
+       SND_SOC_DAPM_INPUT("IN3N"),
        SND_SOC_DAPM_PGA("DMIC L1", SND_SOC_NOPM, 0, 0, NULL, 0),
        SND_SOC_DAPM_PGA("DMIC R1", SND_SOC_NOPM, 0, 0, NULL, 0),
        SND_SOC_DAPM_PGA("DMIC L2", SND_SOC_NOPM, 0, 0, NULL, 0),
@@ -1081,6 +1090,8 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
                RT5640_PWR_BST1_BIT, 0, NULL, 0),
        SND_SOC_DAPM_PGA("BST2", RT5640_PWR_ANLG2,
                RT5640_PWR_BST4_BIT, 0, NULL, 0),
+       SND_SOC_DAPM_PGA("BST3", RT5640_PWR_ANLG2,
+               RT5640_PWR_BST2_BIT, 0, NULL, 0),
        /* Input Volume */
        SND_SOC_DAPM_PGA("INL VOL", RT5640_PWR_VOL,
                RT5640_PWR_IN_L_BIT, 0, NULL, 0),
@@ -1310,6 +1321,7 @@ static const struct snd_soc_dapm_widget rt5639_specific_dapm_widgets[] = {
 static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
        {"IN1P", NULL, "LDO2"},
        {"IN2P", NULL, "LDO2"},
+       {"IN3P", NULL, "LDO2"},
 
        {"DMIC L1", NULL, "DMIC1"},
        {"DMIC R1", NULL, "DMIC1"},
@@ -1320,18 +1332,22 @@ static const struct snd_soc_dapm_route rt5640_dapm_routes[] = {
        {"BST1", NULL, "IN1N"},
        {"BST2", NULL, "IN2P"},
        {"BST2", NULL, "IN2N"},
+       {"BST3", NULL, "IN3P"},
+       {"BST3", NULL, "IN3N"},
 
        {"INL VOL", NULL, "IN2P"},
        {"INR VOL", NULL, "IN2N"},
 
        {"RECMIXL", "HPOL Switch", "HPOL"},
        {"RECMIXL", "INL Switch", "INL VOL"},
+       {"RECMIXL", "BST3 Switch", "BST3"},
        {"RECMIXL", "BST2 Switch", "BST2"},
        {"RECMIXL", "BST1 Switch", "BST1"},
        {"RECMIXL", "OUT MIXL Switch", "OUT MIXL"},
 
        {"RECMIXR", "HPOR Switch", "HPOR"},
        {"RECMIXR", "INR Switch", "INR VOL"},
+       {"RECMIXR", "BST3 Switch", "BST3"},
        {"RECMIXR", "BST2 Switch", "BST2"},
        {"RECMIXR", "BST1 Switch", "BST1"},
        {"RECMIXR", "OUT MIXR Switch", "OUT MIXR"},
@@ -2260,6 +2276,10 @@ static int rt5640_i2c_probe(struct i2c_client *i2c,
                regmap_update_bits(rt5640->regmap, RT5640_IN3_IN4,
                                        RT5640_IN_DF2, RT5640_IN_DF2);
 
+       if (rt5640->pdata.in3_diff)
+               regmap_update_bits(rt5640->regmap, RT5640_IN1_IN2,
+                                       RT5640_IN_DF2, RT5640_IN_DF2);
+
        rt5640->hp_mute = 1;
 
        return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_rt5640,
index 5c101af0ac630dddf1cf12085846d5cfd9c08911..28132375e4274a6e94e830c2fe1e8fcddd255747 100644 (file)
@@ -42,6 +42,8 @@
 
 #define RT5645_PR_BASE (RT5645_PR_RANGE_BASE + (0 * RT5645_PR_SPACING))
 
+#define RT5645_HWEQ_NUM 57
+
 static const struct regmap_range_cfg rt5645_ranges[] = {
        {
                .name = "PR",
@@ -224,6 +226,11 @@ static const struct reg_default rt5645_reg[] = {
        { 0xff, 0x6308 },
 };
 
+struct rt5645_eq_param_s {
+       unsigned short reg;
+       unsigned short val;
+};
+
 static const char *const rt5645_supply_names[] = {
        "avdd",
        "cpvdd",
@@ -240,6 +247,7 @@ struct rt5645_priv {
        struct snd_soc_jack *btn_jack;
        struct delayed_work jack_detect_work;
        struct regulator_bulk_data supplies[ARRAY_SIZE(rt5645_supply_names)];
+       struct rt5645_eq_param_s *eq_param;
 
        int codec_type;
        int sysclk;
@@ -469,6 +477,94 @@ static const DECLARE_TLV_DB_RANGE(bst_tlv,
        8, 8, TLV_DB_SCALE_ITEM(5200, 0, 0)
 );
 
+/* {-6, -4.5, -3, -1.5, 0, 0.82, 1.58, 2.28} dB */
+static const DECLARE_TLV_DB_RANGE(spk_clsd_tlv,
+       0, 4, TLV_DB_SCALE_ITEM(-600, 150, 0),
+       5, 5, TLV_DB_SCALE_ITEM(82, 0, 0),
+       6, 6, TLV_DB_SCALE_ITEM(158, 0, 0),
+       7, 7, TLV_DB_SCALE_ITEM(228, 0, 0)
+);
+
+static int rt5645_hweq_info(struct snd_kcontrol *kcontrol,
+                        struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       uinfo->count = RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s);
+
+       return 0;
+}
+
+static int rt5645_hweq_get(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+       struct rt5645_eq_param_s *eq_param =
+               (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+       int i;
+
+       for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+               eq_param[i].reg = cpu_to_be16(rt5645->eq_param[i].reg);
+               eq_param[i].val = cpu_to_be16(rt5645->eq_param[i].val);
+       }
+
+       return 0;
+}
+
+static bool rt5645_validate_hweq(unsigned short reg)
+{
+       if ((reg >= 0x1a4 && reg <= 0x1cd) | (reg >= 0x1e5 && reg <= 0x1f8) |
+               (reg == RT5645_EQ_CTRL2))
+               return true;
+
+       return false;
+}
+
+static int rt5645_hweq_put(struct snd_kcontrol *kcontrol,
+                       struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_component *component = snd_kcontrol_chip(kcontrol);
+       struct rt5645_priv *rt5645 = snd_soc_component_get_drvdata(component);
+       struct rt5645_eq_param_s *eq_param =
+               (struct rt5645_eq_param_s *)ucontrol->value.bytes.data;
+       int i;
+
+       for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+               eq_param[i].reg = be16_to_cpu(eq_param[i].reg);
+               eq_param[i].val = be16_to_cpu(eq_param[i].val);
+       }
+
+       /* The final setting of the table should be RT5645_EQ_CTRL2 */
+       for (i = RT5645_HWEQ_NUM - 1; i >= 0; i--) {
+               if (eq_param[i].reg == 0)
+                       continue;
+               else if (eq_param[i].reg != RT5645_EQ_CTRL2)
+                       return 0;
+               else
+                       break;
+       }
+
+       for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+               if (!rt5645_validate_hweq(eq_param[i].reg) &&
+                       eq_param[i].reg != 0)
+                       return 0;
+               else if (eq_param[i].reg == 0)
+                       break;
+       }
+
+       memcpy(rt5645->eq_param, eq_param,
+               RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s));
+
+       return 0;
+}
+
+#define RT5645_HWEQ(xname) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = rt5645_hweq_info, \
+       .get = rt5645_hweq_get, \
+       .put = rt5645_hweq_put \
+}
+
 static const struct snd_kcontrol_new rt5645_snd_controls[] = {
        /* Speaker Output Volume */
        SOC_DOUBLE("Speaker Channel Switch", RT5645_SPK_VOL,
@@ -476,6 +572,10 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
        SOC_DOUBLE_TLV("Speaker Playback Volume", RT5645_SPK_VOL,
                RT5645_L_VOL_SFT, RT5645_R_VOL_SFT, 39, 1, out_vol_tlv),
 
+       /* ClassD modulator Speaker Gain Ratio */
+       SOC_SINGLE_TLV("Speaker ClassD Playback Volume", RT5645_SPO_CLSD_RATIO,
+               RT5645_SPK_G_CLSD_SFT, 7, 0, spk_clsd_tlv),
+
        /* Headphone Output Volume */
        SOC_DOUBLE("Headphone Channel Switch", RT5645_HP_VOL,
                RT5645_VOL_L_SFT, RT5645_VOL_R_SFT, 1, 1),
@@ -529,6 +629,7 @@ static const struct snd_kcontrol_new rt5645_snd_controls[] = {
        /* I2S2 function select */
        SOC_SINGLE("I2S2 Func Switch", RT5645_GPIO_CTRL1, RT5645_I2S2_SEL_SFT,
                1, 1),
+       RT5645_HWEQ("Speaker HWEQ"),
 };
 
 /**
@@ -619,6 +720,22 @@ static int is_using_asrc(struct snd_soc_dapm_widget *source,
 
 }
 
+static int rt5645_enable_hweq(struct snd_soc_codec *codec)
+{
+       struct rt5645_priv *rt5645 = snd_soc_codec_get_drvdata(codec);
+       int i;
+
+       for (i = 0; i < RT5645_HWEQ_NUM; i++) {
+               if (rt5645_validate_hweq(rt5645->eq_param[i].reg))
+                       regmap_write(rt5645->regmap, rt5645->eq_param[i].reg,
+                                       rt5645->eq_param[i].val);
+               else
+                       break;
+       }
+
+       return 0;
+}
+
 /**
  * rt5645_sel_asrc_clk_src - select ASRC clock source for a set of filters
  * @codec: SoC audio codec device.
@@ -1523,6 +1640,7 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
 
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
+               rt5645_enable_hweq(codec);
                snd_soc_update_bits(codec, RT5645_PWR_DIG1,
                        RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
                        RT5645_PWR_CLS_D_L,
@@ -1531,6 +1649,7 @@ static int rt5645_spk_event(struct snd_soc_dapm_widget *w,
                break;
 
        case SND_SOC_DAPM_PRE_PMD:
+               snd_soc_write(codec, RT5645_EQ_CTRL2, 0);
                snd_soc_update_bits(codec, RT5645_PWR_DIG1,
                        RT5645_PWR_CLS_D | RT5645_PWR_CLS_D_R |
                        RT5645_PWR_CLS_D_L, 0);
@@ -2733,6 +2852,10 @@ static int rt5645_set_bias_level(struct snd_soc_codec *codec,
                snd_soc_update_bits(codec, RT5645_PWR_ANLG1,
                        RT5645_PWR_FV1 | RT5645_PWR_FV2,
                        RT5645_PWR_FV1 | RT5645_PWR_FV2);
+               if (rt5645->en_button_func &&
+                       snd_soc_codec_get_bias_level(codec) == SND_SOC_BIAS_OFF)
+                       queue_delayed_work(system_power_efficient_wq,
+                               &rt5645->jack_detect_work, msecs_to_jiffies(0));
                break;
 
        case SND_SOC_BIAS_OFF:
@@ -2829,6 +2952,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
                        snd_soc_dapm_sync(dapm);
                        rt5645->jack_type = SND_JACK_HEADPHONE;
                }
+               if (rt5645->pdata.jd_invert)
+                       regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+                               RT5645_JD_1_1_MASK, RT5645_JD_1_1_INV);
        } else { /* jack out */
                rt5645->jack_type = 0;
 
@@ -2847,6 +2973,9 @@ static int rt5645_jack_detect(struct snd_soc_codec *codec, int jack_insert)
                        snd_soc_dapm_disable_pin(dapm, "LDO2");
                snd_soc_dapm_disable_pin(dapm, "Mic Det Power");
                snd_soc_dapm_sync(dapm);
+               if (rt5645->pdata.jd_invert)
+                       regmap_update_bits(rt5645->regmap, RT5645_IRQ_CTRL2,
+                               RT5645_JD_1_1_MASK, RT5645_JD_1_1_NOR);
        }
 
        return rt5645->jack_type;
@@ -3038,6 +3167,9 @@ static int rt5645_probe(struct snd_soc_codec *codec)
                snd_soc_dapm_sync(dapm);
        }
 
+       rt5645->eq_param = devm_kzalloc(codec->dev,
+               RT5645_HWEQ_NUM * sizeof(struct rt5645_eq_param_s), GFP_KERNEL);
+
        return 0;
 }
 
@@ -3098,7 +3230,7 @@ static struct snd_soc_dai_driver rt5645_dai[] = {
                .capture = {
                        .stream_name = "AIF1 Capture",
                        .channels_min = 1,
-                       .channels_max = 2,
+                       .channels_max = 4,
                        .rates = RT5645_STEREO_RATES,
                        .formats = RT5645_FORMATS,
                },
@@ -3209,9 +3341,42 @@ static const struct dmi_system_id dmi_platform_intel_braswell[] = {
                        DMI_MATCH(DMI_PRODUCT_NAME, "Ultima"),
                },
        },
+       {
+               .ident = "Google Reks",
+               .callback = strago_quirk_cb,
+               .matches = {
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Reks"),
+               },
+       },
        { }
 };
 
+static struct rt5645_platform_data buddy_platform_data = {
+       .dmic1_data_pin = RT5645_DMIC_DATA_GPIO5,
+       .dmic2_data_pin = RT5645_DMIC_DATA_IN2P,
+       .jd_mode = 3,
+       .jd_invert = true,
+};
+
+static int buddy_quirk_cb(const struct dmi_system_id *id)
+{
+       rt5645_pdata = &buddy_platform_data;
+
+       return 1;
+}
+
+static struct dmi_system_id dmi_platform_intel_broadwell[] = {
+       {
+               .ident = "Chrome Buddy",
+               .callback = buddy_quirk_cb,
+               .matches = {
+                       DMI_MATCH(DMI_PRODUCT_NAME, "Buddy"),
+               },
+       },
+       { }
+};
+
+
 static int rt5645_parse_dt(struct rt5645_priv *rt5645, struct device *dev)
 {
        rt5645->pdata.in2_diff = device_property_read_bool(dev,
@@ -3244,7 +3409,8 @@ static int rt5645_i2c_probe(struct i2c_client *i2c,
 
        if (pdata)
                rt5645->pdata = *pdata;
-       else if (dmi_check_system(dmi_platform_intel_braswell))
+       else if (dmi_check_system(dmi_platform_intel_braswell) ||
+                       dmi_check_system(dmi_platform_intel_broadwell))
                rt5645->pdata = *rt5645_pdata;
        else
                rt5645_parse_dt(rt5645, &i2c->dev);
@@ -3472,6 +3638,8 @@ static void rt5645_i2c_shutdown(struct i2c_client *i2c)
                RT5645_CBJ_MN_JD);
        regmap_update_bits(rt5645->regmap, RT5645_IN1_CTRL1, RT5645_CBJ_BST1_EN,
                0);
+       msleep(20);
+       regmap_write(rt5645->regmap, RT5645_RESET, 0);
 }
 
 static struct i2c_driver rt5645_i2c_driver = {
index 8c964cfb120ddc4130e5d39e9ab96ae34623eb34..093e46d559fbab24aa05efc5c74a2e8488fa49d0 100644 (file)
 #define RT5645_G_OM_L_SM_L_SFT                 6
 #define RT5645_M_BST1_L_SM_L                   (0x1 << 5)
 #define RT5645_M_BST1_L_SM_L_SFT               5
+#define RT5645_M_BST3_L_SM_L                   (0x1 << 4)
+#define RT5645_M_BST3_L_SM_L_SFT               4
 #define RT5645_M_IN_L_SM_L                     (0x1 << 3)
 #define RT5645_M_IN_L_SM_L_SFT                 3
-#define RT5645_M_DAC_L1_SM_L                   (0x1 << 1)
-#define RT5645_M_DAC_L1_SM_L_SFT               1
 #define RT5645_M_DAC_L2_SM_L                   (0x1 << 2)
 #define RT5645_M_DAC_L2_SM_L_SFT               2
-#define RT5645_M_BST3_L_SM_L                   (0x1 << 4)
-#define RT5645_M_BST3_L_SM_L_SFT               4
+#define RT5645_M_DAC_L1_SM_L                   (0x1 << 1)
+#define RT5645_M_DAC_L1_SM_L_SFT               1
 
 /* SPK Right Mixer Control (0x47) */
 #define RT5645_G_RM_R_SM_R_MASK                        (0x3 << 14)
 #define RT5645_G_OM_R_SM_R_SFT                 6
 #define RT5645_M_BST2_R_SM_R                   (0x1 << 5)
 #define RT5645_M_BST2_R_SM_R_SFT               5
+#define RT5645_M_BST3_R_SM_R                   (0x1 << 4)
+#define RT5645_M_BST3_R_SM_R_SFT               4
 #define RT5645_M_IN_R_SM_R                     (0x1 << 3)
 #define RT5645_M_IN_R_SM_R_SFT                 3
-#define RT5645_M_DAC_R1_SM_R                   (0x1 << 1)
-#define RT5645_M_DAC_R1_SM_R_SFT               1
 #define RT5645_M_DAC_R2_SM_R                   (0x1 << 2)
 #define RT5645_M_DAC_R2_SM_R_SFT               2
-#define RT5645_M_BST3_R_SM_R                   (0x1 << 4)
-#define RT5645_M_BST3_R_SM_R_SFT               4
+#define RT5645_M_DAC_R1_SM_R                   (0x1 << 1)
+#define RT5645_M_DAC_R1_SM_R_SFT               1
 
 /* SPOLMIX Control (0x48) */
 #define RT5645_M_DAC_L1_SPM_L                  (0x1 << 15)
 #define RT5645_M_SV_R_SPM_R                    (0x1 << 0)
 #define RT5645_M_SV_R_SPM_R_SFT                        0
 
+/* SPOMIX Ratio Control (0x4a) */
+#define RT5645_SPK_G_CLSD_MASK                 (0x7 << 0)
+#define RT5645_SPK_G_CLSD_SFT                  0
+
 /* Mono Output Mixer Control (0x4c) */
+#define RT5645_G_MONOMIX_MASK                  (0x1 << 10)
+#define RT5645_G_MONOMIX_SFT                   10
 #define RT5645_M_OV_L_MM                       (0x1 << 9)
 #define RT5645_M_OV_L_MM_SFT                   9
 #define RT5645_M_DAC_L2_MA                     (0x1 << 8)
 #define RT5645_M_DAC_L2_MA_SFT                 8
-#define RT5645_G_MONOMIX_MASK                  (0x1 << 10)
-#define RT5645_G_MONOMIX_SFT                   10
 #define RT5645_M_BST2_MM                       (0x1 << 4)
 #define RT5645_M_BST2_MM_SFT                   4
 #define RT5645_M_DAC_R1_MM                     (0x1 << 3)
 #define RT5645_PWR_CLS_D_R_BIT                 9
 #define RT5645_PWR_CLS_D_L                     (0x1 << 8)
 #define RT5645_PWR_CLS_D_L_BIT                 8
-#define RT5645_PWR_ADC_R                       (0x1 << 1)
-#define RT5645_PWR_ADC_R_BIT                   1
 #define RT5645_PWR_DAC_L2                      (0x1 << 7)
 #define RT5645_PWR_DAC_L2_BIT                  7
 #define RT5645_PWR_DAC_R2                      (0x1 << 6)
 #define RT5645_OT_P_NOR                                (0x0 << 10)
 #define RT5645_OT_P_INV                                (0x1 << 10)
 #define RT5645_IRQ_JD_1_1_EN                   (0x1 << 9)
+#define RT5645_JD_1_1_MASK                     (0x1 << 7)
+#define RT5645_JD_1_1_SFT                      7
+#define RT5645_JD_1_1_NOR                      (0x0 << 7)
+#define RT5645_JD_1_1_INV                      (0x1 << 7)
 
 /* IRQ Control 2 (0xbe) */
 #define RT5645_IRQ_MB1_OC_MASK                 (0x1 << 15)
index ddb0203fc6495b520f4695be9e94e62a7ea56026..86b81a60ac52bb4750da5981d62a210815d515c4 100644 (file)
@@ -723,17 +723,11 @@ static struct snd_soc_codec_driver ssm2518_codec_driver = {
        .num_dapm_routes = ARRAY_SIZE(ssm2518_routes),
 };
 
-static bool ssm2518_register_volatile(struct device *dev, unsigned int reg)
-{
-       return false;
-}
-
 static const struct regmap_config ssm2518_regmap_config = {
        .val_bits = 8,
        .reg_bits = 8,
 
        .max_register = SSM2518_REG_DRC_9,
-       .volatile_reg = ssm2518_register_volatile,
 
        .cache_type = REGCACHE_RBTREE,
        .reg_defaults = ssm2518_reg_defaults,
index 8739126a1f6f60d4c9e3eac08aaf337c2f7b3a2b..a564759845f980119a29c49633cb537ef685bcd9 100644 (file)
@@ -80,6 +80,7 @@ struct aic3x_priv {
        unsigned int sysclk;
        unsigned int dai_fmt;
        unsigned int tdm_delay;
+       unsigned int slot_width;
        struct list_head list;
        int master;
        int gpio_reset;
@@ -1025,10 +1026,14 @@ static int aic3x_hw_params(struct snd_pcm_substream *substream,
        u8 data, j, r, p, pll_q, pll_p = 1, pll_r = 1, pll_j = 1;
        u16 d, pll_d = 1;
        int clk;
+       int width = aic3x->slot_width;
+
+       if (!width)
+               width = params_width(params);
 
        /* select data word length */
        data = snd_soc_read(codec, AIC3X_ASD_INTF_CTRLB) & (~(0x3 << 4));
-       switch (params_width(params)) {
+       switch (width) {
        case 16:
                break;
        case 20:
@@ -1170,12 +1175,16 @@ static int aic3x_prepare(struct snd_pcm_substream *substream,
        struct snd_soc_codec *codec = dai->codec;
        struct aic3x_priv *aic3x = snd_soc_codec_get_drvdata(codec);
        int delay = 0;
+       int width = aic3x->slot_width;
+
+       if (!width)
+               width = substream->runtime->sample_bits;
 
        /* TDM slot selection only valid in DSP_A/_B mode */
        if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_A)
-               delay += (aic3x->tdm_delay + 1);
+               delay += (aic3x->tdm_delay*width + 1);
        else if (aic3x->dai_fmt == SND_SOC_DAIFMT_DSP_B)
-               delay += aic3x->tdm_delay;
+               delay += aic3x->tdm_delay*width;
 
        /* Configure data delay */
        snd_soc_write(codec, AIC3X_ASD_INTF_CTRLC, delay);
@@ -1296,7 +1305,20 @@ static int aic3x_set_dai_tdm_slot(struct snd_soc_dai *codec_dai,
                return -EINVAL;
        }
 
-       aic3x->tdm_delay = lsb * slot_width;
+       switch (slot_width) {
+       case 16:
+       case 20:
+       case 24:
+       case 32:
+               break;
+       default:
+               dev_err(codec->dev, "Unsupported slot width %d\n", slot_width);
+               return -EINVAL;
+       }
+
+
+       aic3x->tdm_delay = lsb;
+       aic3x->slot_width = slot_width;
 
        /* DOUT in high-impedance on inactive bit clocks */
        snd_soc_update_bits(codec, AIC3X_ASD_INTF_CTRLA,
index 2713e1845cbcd93f71232fbc2141784a7c540223..a5a4e9f75c57f89e88f3047d980cf286f36569af 100644 (file)
@@ -1612,19 +1612,16 @@ static void twl4030_constraints(struct twl4030_priv *twl4030,
                return;
 
        /* Set the constraints according to the already configured stream */
-       snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+       snd_pcm_hw_constraint_single(slv_substream->runtime,
                                SNDRV_PCM_HW_PARAM_RATE,
-                               twl4030->rate,
                                twl4030->rate);
 
-       snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+       snd_pcm_hw_constraint_single(slv_substream->runtime,
                                SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
-                               twl4030->sample_bits,
                                twl4030->sample_bits);
 
-       snd_pcm_hw_constraint_minmax(slv_substream->runtime,
+       snd_pcm_hw_constraint_single(slv_substream->runtime,
                                SNDRV_PCM_HW_PARAM_CHANNELS,
-                               twl4030->channels,
                                twl4030->channels);
 }
 
@@ -1669,9 +1666,9 @@ static int twl4030_startup(struct snd_pcm_substream *substream,
                        /* In option2 4 channel is not supported, set the
                         * constraint for the first stream for channels, the
                         * second stream will 'inherit' this cosntraint */
-                       snd_pcm_hw_constraint_minmax(substream->runtime,
+                       snd_pcm_hw_constraint_single(substream->runtime,
                                                     SNDRV_PCM_HW_PARAM_CHANNELS,
-                                                    2, 2);
+                                                    2);
                }
                twl4030->master_substream = substream;
        }
index e1902638053490dda1ab695cf989c91239769ea7..e4c694c758b810e4ae041436fdb364baf4684fd6 100644 (file)
@@ -150,14 +150,12 @@ static int uda134x_startup(struct snd_pcm_substream *substream,
                         master_runtime->sample_bits,
                         master_runtime->rate);
 
-               snd_pcm_hw_constraint_minmax(substream->runtime,
+               snd_pcm_hw_constraint_single(substream->runtime,
                                             SNDRV_PCM_HW_PARAM_RATE,
-                                            master_runtime->rate,
                                             master_runtime->rate);
 
-               snd_pcm_hw_constraint_minmax(substream->runtime,
+               snd_pcm_hw_constraint_single(substream->runtime,
                                             SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
-                                            master_runtime->sample_bits,
                                             master_runtime->sample_bits);
 
                uda134x->slave_substream = substream;
index 80fb1dc81f6cfb89544da1ee393ce1fb3794f253..7693c1129babf0e42c58b5ca93832052374cb426 100644 (file)
@@ -307,11 +307,10 @@ static int wl1273_startup(struct snd_pcm_substream *substream,
 
        switch (wl1273->mode) {
        case WL1273_MODE_BT:
-               snd_pcm_hw_constraint_minmax(substream->runtime,
-                                            SNDRV_PCM_HW_PARAM_RATE,
-                                            8000, 8000);
-               snd_pcm_hw_constraint_minmax(substream->runtime,
-                                            SNDRV_PCM_HW_PARAM_CHANNELS, 1, 1);
+               snd_pcm_hw_constraint_single(substream->runtime,
+                                            SNDRV_PCM_HW_PARAM_RATE, 8000);
+               snd_pcm_hw_constraint_single(substream->runtime,
+                                            SNDRV_PCM_HW_PARAM_CHANNELS, 1);
                break;
        case WL1273_MODE_FM_RX:
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
index 786abd02b1406af3a6b653d22b70742686f2650d..a67ea10f41a1adce51e3625986e1170904429837 100644 (file)
@@ -620,7 +620,7 @@ static int wm2000_anc_mode_put(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
-       int anc_active = ucontrol->value.integer.value[0];
+       unsigned int anc_active = ucontrol->value.integer.value[0];
        int ret;
 
        if (anc_active > 1)
@@ -653,7 +653,7 @@ static int wm2000_speaker_put(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm2000_priv *wm2000 = dev_get_drvdata(codec->dev);
-       int val = ucontrol->value.integer.value[0];
+       unsigned int val = ucontrol->value.integer.value[0];
        int ret;
 
        if (val > 1)
index 9756578fc7526a3182039bad491d3388e297de57..c04c0bc6f58ad434de2bbe5825be1b41182b1c24 100644 (file)
 struct wm5110_priv {
        struct arizona_priv core;
        struct arizona_fll fll[2];
+
+       unsigned int in_value;
+       int in_pre_pending;
+       int in_post_pending;
+
+       unsigned int in_pga_cache[6];
 };
 
 static const struct wm_adsp_region wm5110_dsp1_regions[] = {
@@ -428,6 +434,127 @@ err:
        return ret;
 }
 
+static int wm5110_in_pga_get(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct snd_soc_card *card = dapm->card;
+       int ret;
+
+       /*
+        * PGA Volume is also used as part of the enable sequence, so
+        * usage of it should be avoided whilst that is running.
+        */
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+       ret = snd_soc_get_volsw_range(kcontrol, ucontrol);
+
+       mutex_unlock(&card->dapm_mutex);
+
+       return ret;
+}
+
+static int wm5110_in_pga_put(struct snd_kcontrol *kcontrol,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct snd_soc_card *card = dapm->card;
+       int ret;
+
+       /*
+        * PGA Volume is also used as part of the enable sequence, so
+        * usage of it should be avoided whilst that is running.
+        */
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+
+       ret = snd_soc_put_volsw_range(kcontrol, ucontrol);
+
+       mutex_unlock(&card->dapm_mutex);
+
+       return ret;
+}
+
+static int wm5110_in_analog_ev(struct snd_soc_dapm_widget *w,
+                              struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct wm5110_priv *wm5110 = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+       unsigned int reg, mask;
+       struct reg_sequence analog_seq[] = {
+               { 0x80, 0x3 },
+               { 0x35d, 0 },
+               { 0x80, 0x0 },
+       };
+
+       reg = ARIZONA_IN1L_CONTROL + ((w->shift ^ 0x1) * 4);
+       mask = ARIZONA_IN1L_PGA_VOL_MASK;
+
+       switch (event) {
+       case SND_SOC_DAPM_WILL_PMU:
+               wm5110->in_value |= 0x3 << ((w->shift ^ 0x1) * 2);
+               wm5110->in_pre_pending++;
+               wm5110->in_post_pending++;
+               return 0;
+       case SND_SOC_DAPM_PRE_PMU:
+               wm5110->in_pga_cache[w->shift] = snd_soc_read(codec, reg);
+
+               snd_soc_update_bits(codec, reg, mask,
+                                   0x40 << ARIZONA_IN1L_PGA_VOL_SHIFT);
+
+               wm5110->in_pre_pending--;
+               if (wm5110->in_pre_pending == 0) {
+                       analog_seq[1].def = wm5110->in_value;
+                       regmap_multi_reg_write_bypassed(arizona->regmap,
+                                                       analog_seq,
+                                                       ARRAY_SIZE(analog_seq));
+
+                       msleep(55);
+
+                       wm5110->in_value = 0;
+               }
+
+               break;
+       case SND_SOC_DAPM_POST_PMU:
+               snd_soc_update_bits(codec, reg, mask,
+                                   wm5110->in_pga_cache[w->shift]);
+
+               wm5110->in_post_pending--;
+               if (wm5110->in_post_pending == 0)
+                       regmap_multi_reg_write_bypassed(arizona->regmap,
+                                                       analog_seq,
+                                                       ARRAY_SIZE(analog_seq));
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static int wm5110_in_ev(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+
+       switch (arizona->rev) {
+       case 0 ... 4:
+               if (arizona_input_analog(codec, w->shift))
+                       wm5110_in_analog_ev(w, kcontrol, event);
+
+               break;
+       default:
+               break;
+       }
+
+       return arizona_in_ev(w, kcontrol, event);
+}
+
 static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
 static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
 static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
@@ -454,18 +581,24 @@ SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
 SOC_ENUM("IN3 OSR", arizona_in_dmic_osr[2]),
 SOC_ENUM("IN4 OSR", arizona_in_dmic_osr[3]),
 
-SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
-                    ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
-                    ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
-                    ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
-                    ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
-                    ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
-SOC_SINGLE_RANGE_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
-                    ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+                        ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+                        ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2L Volume", ARIZONA_IN2L_CONTROL,
+                        ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN2R Volume", ARIZONA_IN2R_CONTROL,
+                        ARIZONA_IN2R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3L Volume", ARIZONA_IN3L_CONTROL,
+                        ARIZONA_IN3L_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
+SOC_SINGLE_RANGE_EXT_TLV("IN3R Volume", ARIZONA_IN3R_CONTROL,
+                        ARIZONA_IN3R_PGA_VOL_SHIFT, 0x40, 0x5f, 0,
+                        wm5110_in_pga_get, wm5110_in_pga_put, ana_tlv),
 
 SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum),
 
@@ -896,29 +1029,35 @@ SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
 SND_SOC_DAPM_OUTPUT("DRC2 Signal Activity"),
 
 SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN2L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN3L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3L_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN3R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN3R_ENA_SHIFT,
-                  0, NULL, 0, arizona_in_ev,
+                  0, NULL, 0, wm5110_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
-                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU |
+                  SND_SOC_DAPM_WILL_PMU),
 SND_SOC_DAPM_PGA_E("IN4L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN4L_ENA_SHIFT,
                   0, NULL, 0, arizona_in_ev,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
index ace8645245a0c1765bde0d516a4a1067dec2a755..07cf1bd7913a8dfe4667e310fcb44536f8e4004c 100644 (file)
@@ -132,7 +132,7 @@ static int wm8731_put_deemph(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(codec);
-       int deemph = ucontrol->value.integer.value[0];
+       unsigned int deemph = ucontrol->value.integer.value[0];
        int ret = 0;
 
        if (deemph > 1)
index b011253459af98f57fd87b574fa02164cf38830a..e4cc41e6c23e333cbb771d6f1da91c19c5ff6079 100644 (file)
@@ -452,7 +452,7 @@ static int wm8903_put_deemph(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
-       int deemph = ucontrol->value.integer.value[0];
+       unsigned int deemph = ucontrol->value.integer.value[0];
        int ret = 0;
 
        if (deemph > 1)
index b783743dc97e5a7e82699490aab28ca2e8380950..2aa23f1b9e3ca132c12e826d980b5154ffbe1478 100644 (file)
@@ -534,7 +534,7 @@ static int wm8904_put_deemph(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8904_priv *wm8904 = snd_soc_codec_get_drvdata(codec);
-       int deemph = ucontrol->value.integer.value[0];
+       unsigned int deemph = ucontrol->value.integer.value[0];
 
        if (deemph > 1)
                return -EINVAL;
index 12e4435f00f85507340305442747119f8feb5208..9db00d53abe76f58be4e74a4b4e5342c3a19147d 100644 (file)
@@ -402,7 +402,7 @@ static int wm8955_put_deemph(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8955_priv *wm8955 = snd_soc_codec_get_drvdata(codec);
-       int deemph = ucontrol->value.integer.value[0];
+       unsigned int deemph = ucontrol->value.integer.value[0];
 
        if (deemph > 1)
                return -EINVAL;
index dbd88408861a21af9f3d68ecaf72294b3d47734e..056375339ea32948207723b1affd84ebdd0b1730 100644 (file)
@@ -201,7 +201,7 @@ static int wm8960_put_deemph(struct snd_kcontrol *kcontrol,
 {
        struct snd_soc_codec *codec = snd_soc_kcontrol_codec(kcontrol);
        struct wm8960_priv *wm8960 = snd_soc_codec_get_drvdata(codec);
-       int deemph = ucontrol->value.integer.value[0];
+       unsigned int deemph = ucontrol->value.integer.value[0];
 
        if (deemph > 1)
                return -EINVAL;
diff --git a/sound/soc/codecs/wm8998.c b/sound/soc/codecs/wm8998.c
new file mode 100644 (file)
index 0000000..8782dfb
--- /dev/null
@@ -0,0 +1,1430 @@
+/*
+ * wm8998.c -- ALSA SoC Audio driver for WM8998 codecs
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.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/moduleparam.h>
+#include <linux/init.h>
+#include <linux/delay.h>
+#include <linux/pm.h>
+#include <linux/pm_runtime.h>
+#include <linux/regmap.h>
+#include <linux/slab.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/initval.h>
+#include <sound/tlv.h>
+
+#include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/registers.h>
+
+#include "arizona.h"
+#include "wm8998.h"
+
+struct wm8998_priv {
+       struct arizona_priv core;
+       struct arizona_fll fll[2];
+};
+
+static int wm8998_asrc_ev(struct snd_soc_dapm_widget *w,
+                         struct snd_kcontrol *kcontrol,
+                         int event)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_to_codec(w->dapm);
+       unsigned int val;
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               val = snd_soc_read(codec, ARIZONA_ASRC_RATE1);
+               val &= ARIZONA_ASRC_RATE1_MASK;
+               val >>= ARIZONA_ASRC_RATE1_SHIFT;
+
+               switch (val) {
+               case 0:
+               case 1:
+               case 2:
+                       val = snd_soc_read(codec,
+                                          ARIZONA_SAMPLE_RATE_1 + val);
+                       if (val >= 0x11) {
+                               dev_warn(codec->dev,
+                                        "Unsupported ASRC rate1 (%s)\n",
+                                        arizona_sample_rate_val_to_name(val));
+                       return -EINVAL;
+                       }
+                       break;
+               default:
+                       dev_err(codec->dev,
+                               "Illegal ASRC rate1 selector (0x%x)\n",
+                               val);
+                       return -EINVAL;
+               }
+
+               val = snd_soc_read(codec, ARIZONA_ASRC_RATE2);
+               val &= ARIZONA_ASRC_RATE2_MASK;
+               val >>= ARIZONA_ASRC_RATE2_SHIFT;
+
+               switch (val) {
+               case 8:
+               case 9:
+                       val -= 0x8;
+                       val = snd_soc_read(codec,
+                                          ARIZONA_ASYNC_SAMPLE_RATE_1 + val);
+                       if (val >= 0x11) {
+                               dev_warn(codec->dev,
+                                        "Unsupported ASRC rate2 (%s)\n",
+                                        arizona_sample_rate_val_to_name(val));
+                               return -EINVAL;
+                       }
+                       break;
+               default:
+                       dev_err(codec->dev,
+                               "Illegal ASRC rate2 selector (0x%x)\n",
+                               val);
+                       return -EINVAL;
+               }
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int wm8998_in1mux_put(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = wm8998->core.arizona;
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int mux, inmode;
+       unsigned int mode_val, src_val;
+
+       mux = ucontrol->value.enumerated.item[0];
+       if (mux > 1)
+               return -EINVAL;
+
+       /* L and R registers have same shift and mask */
+       inmode = arizona->pdata.inmode[2 * mux];
+       src_val = mux << ARIZONA_IN1L_SRC_SHIFT;
+       if (inmode & ARIZONA_INMODE_SE)
+               src_val |= 1 << ARIZONA_IN1L_SRC_SE_SHIFT;
+
+       switch (arizona->pdata.inmode[0]) {
+       case ARIZONA_INMODE_DMIC:
+               if (mux)
+                       mode_val = 0;   /* B always analogue */
+               else
+                       mode_val = 1 << ARIZONA_IN1_MODE_SHIFT;
+
+               snd_soc_update_bits(codec, ARIZONA_IN1L_CONTROL,
+                                   ARIZONA_IN1_MODE_MASK, mode_val);
+
+               /* IN1A is digital so L and R must change together */
+               /* src_val setting same for both registers */
+               snd_soc_update_bits(codec,
+                                   ARIZONA_ADC_DIGITAL_VOLUME_1L,
+                                   ARIZONA_IN1L_SRC_MASK |
+                                   ARIZONA_IN1L_SRC_SE_MASK, src_val);
+               snd_soc_update_bits(codec,
+                                   ARIZONA_ADC_DIGITAL_VOLUME_1R,
+                                   ARIZONA_IN1R_SRC_MASK |
+                                   ARIZONA_IN1R_SRC_SE_MASK, src_val);
+               break;
+       default:
+               /* both analogue */
+               snd_soc_update_bits(codec,
+                                   e->reg,
+                                   ARIZONA_IN1L_SRC_MASK |
+                                   ARIZONA_IN1L_SRC_SE_MASK,
+                                   src_val);
+               break;
+       }
+
+       return snd_soc_dapm_mux_update_power(dapm, kcontrol,
+                                            ucontrol->value.enumerated.item[0],
+                                            e, NULL);
+}
+
+static int wm8998_in2mux_put(struct snd_kcontrol *kcontrol,
+                           struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+       struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = wm8998->core.arizona;
+       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
+       unsigned int mux, inmode, src_val, mode_val;
+
+       mux = ucontrol->value.enumerated.item[0];
+       if (mux > 1)
+               return -EINVAL;
+
+       inmode = arizona->pdata.inmode[1 + (2 * mux)];
+       if (inmode & ARIZONA_INMODE_DMIC)
+               mode_val = 1 << ARIZONA_IN2_MODE_SHIFT;
+       else
+               mode_val = 0;
+
+       src_val = mux << ARIZONA_IN2L_SRC_SHIFT;
+       if (inmode & ARIZONA_INMODE_SE)
+               src_val |= 1 << ARIZONA_IN2L_SRC_SE_SHIFT;
+
+       snd_soc_update_bits(codec, ARIZONA_IN2L_CONTROL,
+                           ARIZONA_IN2_MODE_MASK, mode_val);
+
+       snd_soc_update_bits(codec, ARIZONA_ADC_DIGITAL_VOLUME_2L,
+                           ARIZONA_IN2L_SRC_MASK | ARIZONA_IN2L_SRC_SE_MASK,
+                           src_val);
+
+       return snd_soc_dapm_mux_update_power(dapm, kcontrol,
+                                            ucontrol->value.enumerated.item[0],
+                                            e, NULL);
+}
+
+static const char * const wm8998_inmux_texts[] = {
+       "A",
+       "B",
+};
+
+static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxl_enum,
+                                 ARIZONA_ADC_DIGITAL_VOLUME_1L,
+                                 ARIZONA_IN1L_SRC_SHIFT,
+                                 wm8998_inmux_texts);
+
+static const SOC_ENUM_SINGLE_DECL(wm8998_in1muxr_enum,
+                                 ARIZONA_ADC_DIGITAL_VOLUME_1R,
+                                 ARIZONA_IN1R_SRC_SHIFT,
+                                 wm8998_inmux_texts);
+
+static const SOC_ENUM_SINGLE_DECL(wm8998_in2mux_enum,
+                                 ARIZONA_ADC_DIGITAL_VOLUME_2L,
+                                 ARIZONA_IN2L_SRC_SHIFT,
+                                 wm8998_inmux_texts);
+
+static const struct snd_kcontrol_new wm8998_in1mux[2] = {
+       SOC_DAPM_ENUM_EXT("IN1L Mux", wm8998_in1muxl_enum,
+                         snd_soc_dapm_get_enum_double, wm8998_in1mux_put),
+       SOC_DAPM_ENUM_EXT("IN1R Mux", wm8998_in1muxr_enum,
+                         snd_soc_dapm_get_enum_double, wm8998_in1mux_put),
+};
+
+static const struct snd_kcontrol_new wm8998_in2mux =
+       SOC_DAPM_ENUM_EXT("IN2 Mux", wm8998_in2mux_enum,
+                         snd_soc_dapm_get_enum_double, wm8998_in2mux_put);
+
+static DECLARE_TLV_DB_SCALE(ana_tlv, 0, 100, 0);
+static DECLARE_TLV_DB_SCALE(eq_tlv, -1200, 100, 0);
+static DECLARE_TLV_DB_SCALE(digital_tlv, -6400, 50, 0);
+static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+
+#define WM8998_NG_SRC(name, base) \
+       SOC_SINGLE(name " NG HPOUTL Switch",  base,  0, 1, 0), \
+       SOC_SINGLE(name " NG HPOUTR Switch",  base,  1, 1, 0), \
+       SOC_SINGLE(name " NG LINEOUTL Switch",  base,  2, 1, 0), \
+       SOC_SINGLE(name " NG LINEOUTR Switch",  base,  3, 1, 0), \
+       SOC_SINGLE(name " NG EPOUT Switch",   base,  4, 1, 0), \
+       SOC_SINGLE(name " NG SPKOUTL Switch",  base,  6, 1, 0), \
+       SOC_SINGLE(name " NG SPKOUTR Switch",  base,  7, 1, 0)
+
+static const struct snd_kcontrol_new wm8998_snd_controls[] = {
+SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
+SOC_ENUM("IN2 OSR", arizona_in_dmic_osr[1]),
+
+SOC_SINGLE_RANGE_TLV("IN1L Volume", ARIZONA_IN1L_CONTROL,
+                    ARIZONA_IN1L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN1R Volume", ARIZONA_IN1R_CONTROL,
+                    ARIZONA_IN1R_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+SOC_SINGLE_RANGE_TLV("IN2 Volume", ARIZONA_IN2L_CONTROL,
+                    ARIZONA_IN2L_PGA_VOL_SHIFT, 0x40, 0x5f, 0, ana_tlv),
+
+SOC_ENUM("IN HPF Cutoff Frequency", arizona_in_hpf_cut_enum),
+
+SOC_SINGLE("IN1L HPF Switch", ARIZONA_IN1L_CONTROL,
+          ARIZONA_IN1L_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN1R HPF Switch", ARIZONA_IN1R_CONTROL,
+          ARIZONA_IN1R_HPF_SHIFT, 1, 0),
+SOC_SINGLE("IN2 HPF Switch", ARIZONA_IN2L_CONTROL,
+          ARIZONA_IN2L_HPF_SHIFT, 1, 0),
+
+SOC_SINGLE_TLV("IN1L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1L,
+              ARIZONA_IN1L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN1R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_1R,
+              ARIZONA_IN1R_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2 Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+              ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+
+SOC_ENUM("Input Ramp Up", arizona_in_vi_ramp),
+SOC_ENUM("Input Ramp Down", arizona_in_vd_ramp),
+
+ARIZONA_GAINMUX_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES("EQ1 Coefficients", ARIZONA_EQ1_3, 19),
+SOC_SINGLE("EQ1 Mode Switch", ARIZONA_EQ1_2, ARIZONA_EQ1_B1_MODE_SHIFT, 1, 0),
+SOC_SINGLE_TLV("EQ1 B1 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B1_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B2 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B2_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B3 Volume", ARIZONA_EQ1_1, ARIZONA_EQ1_B3_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B4 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B4_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ1 B5 Volume", ARIZONA_EQ1_2, ARIZONA_EQ1_B5_GAIN_SHIFT,
+              24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ2 Coefficients", ARIZONA_EQ2_3, 19),
+SOC_SINGLE("EQ2 Mode Switch", ARIZONA_EQ2_2, ARIZONA_EQ2_B1_MODE_SHIFT, 1, 0),
+SOC_SINGLE_TLV("EQ2 B1 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B1_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B2 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B2_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B3 Volume", ARIZONA_EQ2_1, ARIZONA_EQ2_B3_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B4 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B4_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ2 B5 Volume", ARIZONA_EQ2_2, ARIZONA_EQ2_B5_GAIN_SHIFT,
+              24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ3 Coefficients", ARIZONA_EQ3_3, 19),
+SOC_SINGLE("EQ3 Mode Switch", ARIZONA_EQ3_2, ARIZONA_EQ3_B1_MODE_SHIFT, 1, 0),
+SOC_SINGLE_TLV("EQ3 B1 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B1_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B2 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B2_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B3 Volume", ARIZONA_EQ3_1, ARIZONA_EQ3_B3_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B4 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B4_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ3 B5 Volume", ARIZONA_EQ3_2, ARIZONA_EQ3_B5_GAIN_SHIFT,
+              24, 0, eq_tlv),
+
+SND_SOC_BYTES("EQ4 Coefficients", ARIZONA_EQ4_3, 19),
+SOC_SINGLE("EQ4 Mode Switch", ARIZONA_EQ4_2, ARIZONA_EQ4_B1_MODE_SHIFT, 1, 0),
+SOC_SINGLE_TLV("EQ4 B1 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B1_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B2 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B2_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B3 Volume", ARIZONA_EQ4_1, ARIZONA_EQ4_B3_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B4 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B4_GAIN_SHIFT,
+              24, 0, eq_tlv),
+SOC_SINGLE_TLV("EQ4 B5 Volume", ARIZONA_EQ4_2, ARIZONA_EQ4_B5_GAIN_SHIFT,
+              24, 0, eq_tlv),
+
+ARIZONA_GAINMUX_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("DRC1R", ARIZONA_DRC1RMIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("DRC1", ARIZONA_DRC1_CTRL1, 5,
+                  ARIZONA_DRC1R_ENA | ARIZONA_DRC1L_ENA),
+
+ARIZONA_MIXER_CONTROLS("LHPF1", ARIZONA_HPLP1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF2", ARIZONA_HPLP2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF3", ARIZONA_HPLP3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LHPF4", ARIZONA_HPLP4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES("LHPF1 Coefficients", ARIZONA_HPLPF1_2, 1),
+SND_SOC_BYTES("LHPF2 Coefficients", ARIZONA_HPLPF2_2, 1),
+SND_SOC_BYTES("LHPF3 Coefficients", ARIZONA_HPLPF3_2, 1),
+SND_SOC_BYTES("LHPF4 Coefficients", ARIZONA_HPLPF4_2, 1),
+
+SOC_ENUM("LHPF1 Mode", arizona_lhpf1_mode),
+SOC_ENUM("LHPF2 Mode", arizona_lhpf2_mode),
+SOC_ENUM("LHPF3 Mode", arizona_lhpf3_mode),
+SOC_ENUM("LHPF4 Mode", arizona_lhpf4_mode),
+
+SOC_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+SOC_ENUM("ISRC1 FSH", arizona_isrc_fsh[0]),
+SOC_ENUM("ISRC2 FSH", arizona_isrc_fsh[1]),
+SOC_ENUM("ASRC RATE 1", arizona_asrc_rate1),
+
+ARIZONA_MIXER_CONTROLS("HPOUTL", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUTR", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LINEOUTL", ARIZONA_OUT2LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("LINEOUTR", ARIZONA_OUT2RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTL", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUTR", ARIZONA_OUT4RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDATL", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDATR", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_DOUBLE_R("HPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+            ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("LINEOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+            ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+            ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+            ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+            ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+                ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_VOL_SHIFT,
+                0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("LINEOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_2L,
+                ARIZONA_DAC_DIGITAL_VOLUME_2R, ARIZONA_OUT2L_VOL_SHIFT,
+                0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("EPOUT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+                ARIZONA_OUT3L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+                ARIZONA_DAC_DIGITAL_VOLUME_4R, ARIZONA_OUT4L_VOL_SHIFT,
+                0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+                ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
+                0xbf, 0, digital_tlv),
+
+SOC_DOUBLE("SPKDAT Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
+          ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
+
+SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
+
+SOC_SINGLE("Noise Gate Switch", ARIZONA_NOISE_GATE_CONTROL,
+          ARIZONA_NGATE_ENA_SHIFT, 1, 0),
+SOC_SINGLE_TLV("Noise Gate Threshold Volume", ARIZONA_NOISE_GATE_CONTROL,
+              ARIZONA_NGATE_THR_SHIFT, 7, 1, ng_tlv),
+SOC_ENUM("Noise Gate Hold", arizona_ng_hold),
+
+WM8998_NG_SRC("HPOUTL", ARIZONA_NOISE_GATE_SELECT_1L),
+WM8998_NG_SRC("HPOUTR", ARIZONA_NOISE_GATE_SELECT_1R),
+WM8998_NG_SRC("LINEOUTL", ARIZONA_NOISE_GATE_SELECT_2L),
+WM8998_NG_SRC("LINEOUTR", ARIZONA_NOISE_GATE_SELECT_2R),
+WM8998_NG_SRC("EPOUT",  ARIZONA_NOISE_GATE_SELECT_3L),
+WM8998_NG_SRC("SPKOUTL", ARIZONA_NOISE_GATE_SELECT_4L),
+WM8998_NG_SRC("SPKOUTR", ARIZONA_NOISE_GATE_SELECT_4R),
+WM8998_NG_SRC("SPKDATL", ARIZONA_NOISE_GATE_SELECT_5L),
+WM8998_NG_SRC("SPKDATR", ARIZONA_NOISE_GATE_SELECT_5R),
+
+ARIZONA_MIXER_CONTROLS("AIF1TX1", ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX2", ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX3", ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX4", ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX5", ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX6", ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX3", ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX4", ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX5", ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX6", ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF3TX1", ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF3TX2", ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_GAINMUX_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+
+ARIZONA_GAINMUX_CONTROLS("SPDIFTX1", ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE),
+ARIZONA_GAINMUX_CONTROLS("SPDIFTX2", ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE),
+};
+
+ARIZONA_MUX_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(DRC1R, ARIZONA_DRC1RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(LHPF1, ARIZONA_HPLP1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF2, ARIZONA_HPLP2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF3, ARIZONA_HPLP3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(LHPF4, ARIZONA_HPLP4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(PWM1, ARIZONA_PWM1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(PWM2, ARIZONA_PWM2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(OUT1L, ARIZONA_OUT1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT1R, ARIZONA_OUT1RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2L, ARIZONA_OUT2LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT2R, ARIZONA_OUT2RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(OUT3,  ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTL, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUTR, ARIZONA_OUT4RMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDATL, ARIZONA_OUT5LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDATR, ARIZONA_OUT5RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF1TX1, ARIZONA_AIF1TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX2, ARIZONA_AIF1TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX3, ARIZONA_AIF1TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX4, ARIZONA_AIF1TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX5, ARIZONA_AIF1TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX6, ARIZONA_AIF1TX6MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX3, ARIZONA_AIF2TX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX4, ARIZONA_AIF2TX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX5, ARIZONA_AIF2TX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX6, ARIZONA_AIF2TX6MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF3TX1, ARIZONA_AIF3TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF3TX2, ARIZONA_AIF3TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(SPD1TX1, ARIZONA_SPDIFTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(SPD1TX2, ARIZONA_SPDIFTX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ASRC1L, ARIZONA_ASRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC1R, ARIZONA_ASRC1RMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2L, ARIZONA_ASRC2LMIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ASRC2R, ARIZONA_ASRC2RMIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT3, ARIZONA_ISRC1INT3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT4, ARIZONA_ISRC1INT4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC3, ARIZONA_ISRC1DEC3MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC4, ARIZONA_ISRC1DEC4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2INT1, ARIZONA_ISRC2INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2INT2, ARIZONA_ISRC2INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC2DEC1, ARIZONA_ISRC2DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC2DEC2, ARIZONA_ISRC2DEC2MIX_INPUT_1_SOURCE);
+
+static const char * const wm8998_aec_loopback_texts[] = {
+       "HPOUTL", "HPOUTR", "LINEOUTL", "LINEOUTR", "EPOUT",
+       "SPKOUTL", "SPKOUTR", "SPKDATL", "SPKDATR",
+};
+
+static const unsigned int wm8998_aec_loopback_values[] = {
+       0, 1, 2, 3, 4, 6, 7, 8, 9,
+};
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec1_loopback,
+                                       ARIZONA_DAC_AEC_CONTROL_1,
+                                       ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+                                       wm8998_aec_loopback_texts,
+                                       wm8998_aec_loopback_values);
+
+static const SOC_VALUE_ENUM_SINGLE_DECL(wm8998_aec2_loopback,
+                                       ARIZONA_DAC_AEC_CONTROL_2,
+                                       ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+                                       wm8998_aec_loopback_texts,
+                                       wm8998_aec_loopback_values);
+
+static const struct snd_kcontrol_new wm8998_aec_loopback_mux[] = {
+       SOC_DAPM_ENUM("AEC1 Loopback", wm8998_aec1_loopback),
+       SOC_DAPM_ENUM("AEC2 Loopback", wm8998_aec2_loopback),
+};
+
+static const struct snd_soc_dapm_widget wm8998_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1,
+                   ARIZONA_SYSCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCCLK", ARIZONA_ASYNC_CLOCK_1,
+                   ARIZONA_ASYNC_CLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("OPCLK", ARIZONA_OUTPUT_SYSTEM_CLOCK,
+                   ARIZONA_OPCLK_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("ASYNCOPCLK", ARIZONA_OUTPUT_ASYNC_CLOCK,
+                   ARIZONA_OPCLK_ASYNC_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD2", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("DBVDD3", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDL", 0, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDDR", 0, 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_INPUT("IN1AL"),
+SND_SOC_DAPM_INPUT("IN1AR"),
+SND_SOC_DAPM_INPUT("IN1BL"),
+SND_SOC_DAPM_INPUT("IN1BR"),
+SND_SOC_DAPM_INPUT("IN2A"),
+SND_SOC_DAPM_INPUT("IN2B"),
+
+SND_SOC_DAPM_MUX("IN1L Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[0]),
+SND_SOC_DAPM_MUX("IN1R Mux", SND_SOC_NOPM, 0, 0, &wm8998_in1mux[1]),
+SND_SOC_DAPM_MUX("IN2 Mux", SND_SOC_NOPM, 0, 0, &wm8998_in2mux),
+
+SND_SOC_DAPM_OUTPUT("DRC1 Signal Activity"),
+
+SND_SOC_DAPM_PGA_E("IN1L PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1L_ENA_SHIFT,
+                  0, NULL, 0, arizona_in_ev,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN1R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN1R_ENA_SHIFT,
+                  0, NULL, 0, arizona_in_ev,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("IN2 PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2L_ENA_SHIFT,
+                  0, NULL, 0, arizona_in_ev,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_SUPPLY("MICBIAS1", ARIZONA_MIC_BIAS_CTRL_1,
+                   ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS2", ARIZONA_MIC_BIAS_CTRL_2,
+                   ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
+                   ARIZONA_MICB1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Tone Generator 1", ARIZONA_TONE_GENERATOR_1,
+                ARIZONA_TONE1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("Tone Generator 2", ARIZONA_TONE_GENERATOR_1,
+                ARIZONA_TONE2_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("EQ1", ARIZONA_EQ1_1, ARIZONA_EQ1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ2", ARIZONA_EQ2_1, ARIZONA_EQ2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ3", ARIZONA_EQ3_1, ARIZONA_EQ3_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("EQ4", ARIZONA_EQ4_1, ARIZONA_EQ4_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("DRC1L", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1L_ENA_SHIFT, 0,
+                NULL, 0),
+SND_SOC_DAPM_PGA("DRC1R", ARIZONA_DRC1_CTRL1, ARIZONA_DRC1R_ENA_SHIFT, 0,
+                NULL, 0),
+
+SND_SOC_DAPM_PGA("LHPF1", ARIZONA_HPLPF1_1, ARIZONA_LHPF1_ENA_SHIFT, 0,
+                NULL, 0),
+SND_SOC_DAPM_PGA("LHPF2", ARIZONA_HPLPF2_1, ARIZONA_LHPF2_ENA_SHIFT, 0,
+                NULL, 0),
+SND_SOC_DAPM_PGA("LHPF3", ARIZONA_HPLPF3_1, ARIZONA_LHPF3_ENA_SHIFT, 0,
+                NULL, 0),
+SND_SOC_DAPM_PGA("LHPF4", ARIZONA_HPLPF4_1, ARIZONA_LHPF4_ENA_SHIFT, 0,
+                NULL, 0),
+
+SND_SOC_DAPM_PGA("PWM1 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM1_ENA_SHIFT,
+                0, NULL, 0),
+SND_SOC_DAPM_PGA("PWM2 Driver", ARIZONA_PWM_DRIVE_1, ARIZONA_PWM2_ENA_SHIFT,
+                0, NULL, 0),
+
+SND_SOC_DAPM_PGA_E("ASRC1L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1L_ENA_SHIFT, 0,
+                  NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("ASRC1R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC1R_ENA_SHIFT, 0,
+                  NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("ASRC2L", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2L_ENA_SHIFT, 0,
+                  NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+SND_SOC_DAPM_PGA_E("ASRC2R", ARIZONA_ASRC_ENABLE, ARIZONA_ASRC2R_ENA_SHIFT, 0,
+                  NULL, 0, wm8998_asrc_ev, SND_SOC_DAPM_PRE_PMU),
+
+SND_SOC_DAPM_PGA("ISRC1INT1", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT2", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_INT1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT3", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_INT2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1INT4", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_INT3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC1DEC1", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC2", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_DEC1_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC3", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_DEC2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC1DEC4", ARIZONA_ISRC_1_CTRL_3,
+                ARIZONA_ISRC1_DEC3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2INT1", ARIZONA_ISRC_2_CTRL_3,
+                ARIZONA_ISRC2_INT0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2INT2", ARIZONA_ISRC_2_CTRL_3,
+                ARIZONA_ISRC2_INT1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("ISRC2DEC1", ARIZONA_ISRC_2_CTRL_3,
+                ARIZONA_ISRC2_DEC0_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("ISRC2DEC2", ARIZONA_ISRC_2_CTRL_3,
+                ARIZONA_ISRC2_DEC1_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_MUX("AEC1 Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+                      ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+                      &wm8998_aec_loopback_mux[0]),
+
+SND_SOC_DAPM_MUX("AEC2 Loopback", ARIZONA_DAC_AEC_CONTROL_2,
+                      ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+                      &wm8998_aec_loopback_mux[1]),
+
+SND_SOC_DAPM_AIF_OUT("AIF1TX1", NULL, 0,
+                    ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX2", NULL, 0,
+                    ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX3", NULL, 0,
+                    ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX4", NULL, 0,
+                    ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX5", NULL, 0,
+                    ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX6", NULL, 0,
+                    ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+                   ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0,
+                   ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX3", NULL, 0,
+                   ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX4", NULL, 0,
+                   ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX5", NULL, 0,
+                   ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX6", NULL, 0,
+                   ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+                    ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0,
+                    ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX3", NULL, 0,
+                    ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX4", NULL, 0,
+                    ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX5", NULL, 0,
+                    ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX6", NULL, 0,
+                    ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF2RX1", NULL, 0,
+                   ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX2", NULL, 0,
+                   ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX3", NULL, 0,
+                   ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX4", NULL, 0,
+                   ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX5", NULL, 0,
+                   ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF2RX6", NULL, 0,
+                   ARIZONA_AIF2_RX_ENABLES, ARIZONA_AIF2RX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("SLIMRX1", NULL, 0,
+                   ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+                   ARIZONA_SLIMRX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX2", NULL, 0,
+                   ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+                   ARIZONA_SLIMRX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX3", NULL, 0,
+                   ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+                   ARIZONA_SLIMRX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX4", NULL, 0,
+                   ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+                   ARIZONA_SLIMRX4_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("SLIMTX1", NULL, 0,
+                    ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+                    ARIZONA_SLIMTX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX2", NULL, 0,
+                    ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+                    ARIZONA_SLIMTX2_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX3", NULL, 0,
+                    ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+                    ARIZONA_SLIMTX3_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX4", NULL, 0,
+                    ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+                    ARIZONA_SLIMTX4_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX5", NULL, 0,
+                    ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+                    ARIZONA_SLIMTX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX6", NULL, 0,
+                    ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+                    ARIZONA_SLIMTX6_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF3TX1", NULL, 0,
+                    ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF3TX2", NULL, 0,
+                    ARIZONA_AIF3_TX_ENABLES, ARIZONA_AIF3TX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF3RX1", NULL, 0,
+                   ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF3RX2", NULL, 0,
+                   ARIZONA_AIF3_RX_ENABLES, ARIZONA_AIF3RX2_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_PGA_E("OUT1L", SND_SOC_NOPM,
+                  ARIZONA_OUT1L_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT1R", SND_SOC_NOPM,
+                  ARIZONA_OUT1R_ENA_SHIFT, 0, NULL, 0, arizona_hp_ev,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2L", ARIZONA_OUTPUT_ENABLES_1,
+                  ARIZONA_OUT2L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT2R", ARIZONA_OUTPUT_ENABLES_1,
+                  ARIZONA_OUT2R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT3", ARIZONA_OUTPUT_ENABLES_1,
+                  ARIZONA_OUT3L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5L", ARIZONA_OUTPUT_ENABLES_1,
+                  ARIZONA_OUT5L_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+SND_SOC_DAPM_PGA_E("OUT5R", ARIZONA_OUTPUT_ENABLES_1,
+                  ARIZONA_OUT5R_ENA_SHIFT, 0, NULL, 0, arizona_out_ev,
+                  SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMU),
+
+SND_SOC_DAPM_PGA("SPD1TX1", ARIZONA_SPD1_TX_CONTROL,
+                  ARIZONA_SPD1_VAL1_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_PGA("SPD1TX2", ARIZONA_SPD1_TX_CONTROL,
+                  ARIZONA_SPD1_VAL2_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_OUT_DRV("SPD1", ARIZONA_SPD1_TX_CONTROL,
+                    ARIZONA_SPD1_ENA_SHIFT, 0, NULL, 0),
+
+ARIZONA_MUX_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MUX_WIDGETS(EQ2, "EQ2"),
+ARIZONA_MUX_WIDGETS(EQ3, "EQ3"),
+ARIZONA_MUX_WIDGETS(EQ4, "EQ4"),
+
+ARIZONA_MUX_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MUX_WIDGETS(DRC1R, "DRC1R"),
+
+ARIZONA_MIXER_WIDGETS(LHPF1, "LHPF1"),
+ARIZONA_MIXER_WIDGETS(LHPF2, "LHPF2"),
+ARIZONA_MIXER_WIDGETS(LHPF3, "LHPF3"),
+ARIZONA_MIXER_WIDGETS(LHPF4, "LHPF4"),
+
+ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"),
+ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUTL"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUTR"),
+ARIZONA_MIXER_WIDGETS(OUT2L, "LINEOUTL"),
+ARIZONA_MIXER_WIDGETS(OUT2R, "LINEOUTR"),
+ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"),
+ARIZONA_MIXER_WIDGETS(SPKOUTL, "SPKOUTL"),
+ARIZONA_MIXER_WIDGETS(SPKOUTR, "SPKOUTR"),
+ARIZONA_MIXER_WIDGETS(SPKDATL, "SPKDATL"),
+ARIZONA_MIXER_WIDGETS(SPKDATR, "SPKDATR"),
+
+ARIZONA_MIXER_WIDGETS(AIF1TX1, "AIF1TX1"),
+ARIZONA_MIXER_WIDGETS(AIF1TX2, "AIF1TX2"),
+ARIZONA_MIXER_WIDGETS(AIF1TX3, "AIF1TX3"),
+ARIZONA_MIXER_WIDGETS(AIF1TX4, "AIF1TX4"),
+ARIZONA_MIXER_WIDGETS(AIF1TX5, "AIF1TX5"),
+ARIZONA_MIXER_WIDGETS(AIF1TX6, "AIF1TX6"),
+
+ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+ARIZONA_MIXER_WIDGETS(AIF2TX3, "AIF2TX3"),
+ARIZONA_MIXER_WIDGETS(AIF2TX4, "AIF2TX4"),
+ARIZONA_MIXER_WIDGETS(AIF2TX5, "AIF2TX5"),
+ARIZONA_MIXER_WIDGETS(AIF2TX6, "AIF2TX6"),
+
+ARIZONA_MIXER_WIDGETS(AIF3TX1, "AIF3TX1"),
+ARIZONA_MIXER_WIDGETS(AIF3TX2, "AIF3TX2"),
+
+ARIZONA_MUX_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MUX_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MUX_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MUX_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MUX_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MUX_WIDGETS(SLIMTX6, "SLIMTX6"),
+
+ARIZONA_MUX_WIDGETS(SPD1TX1, "SPDIFTX1"),
+ARIZONA_MUX_WIDGETS(SPD1TX2, "SPDIFTX2"),
+
+ARIZONA_MUX_WIDGETS(ASRC1L, "ASRC1L"),
+ARIZONA_MUX_WIDGETS(ASRC1R, "ASRC1R"),
+ARIZONA_MUX_WIDGETS(ASRC2L, "ASRC2L"),
+ARIZONA_MUX_WIDGETS(ASRC2R, "ASRC2R"),
+
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC3, "ISRC1DEC3"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC4, "ISRC1DEC4"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+ARIZONA_MUX_WIDGETS(ISRC1INT3, "ISRC1INT3"),
+ARIZONA_MUX_WIDGETS(ISRC1INT4, "ISRC1INT4"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+SND_SOC_DAPM_OUTPUT("HPOUTL"),
+SND_SOC_DAPM_OUTPUT("HPOUTR"),
+SND_SOC_DAPM_OUTPUT("LINEOUTL"),
+SND_SOC_DAPM_OUTPUT("LINEOUTR"),
+SND_SOC_DAPM_OUTPUT("EPOUT"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTLP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTRP"),
+SND_SOC_DAPM_OUTPUT("SPKDATL"),
+SND_SOC_DAPM_OUTPUT("SPKDATR"),
+SND_SOC_DAPM_OUTPUT("SPDIF"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name)       \
+       { name, "Tone Generator 1", "Tone Generator 1" }, \
+       { name, "Tone Generator 2", "Tone Generator 2" }, \
+       { name, "Haptics", "HAPTICS" }, \
+       { name, "AEC", "AEC1 Loopback" }, \
+       { name, "AEC2", "AEC2 Loopback" }, \
+       { name, "IN1L", "IN1L PGA" }, \
+       { name, "IN1R", "IN1R PGA" }, \
+       { name, "IN2L", "IN2 PGA" }, \
+       { name, "AIF1RX1", "AIF1RX1" }, \
+       { name, "AIF1RX2", "AIF1RX2" }, \
+       { name, "AIF1RX3", "AIF1RX3" }, \
+       { name, "AIF1RX4", "AIF1RX4" }, \
+       { name, "AIF1RX5", "AIF1RX5" }, \
+       { name, "AIF1RX6", "AIF1RX6" }, \
+       { name, "AIF2RX1", "AIF2RX1" }, \
+       { name, "AIF2RX2", "AIF2RX2" }, \
+       { name, "AIF2RX3", "AIF2RX3" }, \
+       { name, "AIF2RX4", "AIF2RX4" }, \
+       { name, "AIF2RX5", "AIF2RX5" }, \
+       { name, "AIF2RX6", "AIF2RX6" }, \
+       { name, "AIF3RX1", "AIF3RX1" }, \
+       { name, "AIF3RX2", "AIF3RX2" }, \
+       { name, "SLIMRX1", "SLIMRX1" }, \
+       { name, "SLIMRX2", "SLIMRX2" }, \
+       { name, "SLIMRX3", "SLIMRX3" }, \
+       { name, "SLIMRX4", "SLIMRX4" }, \
+       { name, "EQ1", "EQ1" }, \
+       { name, "EQ2", "EQ2" }, \
+       { name, "EQ3", "EQ3" }, \
+       { name, "EQ4", "EQ4" }, \
+       { name, "DRC1L", "DRC1L" }, \
+       { name, "DRC1R", "DRC1R" }, \
+       { name, "LHPF1", "LHPF1" }, \
+       { name, "LHPF2", "LHPF2" }, \
+       { name, "LHPF3", "LHPF3" }, \
+       { name, "LHPF4", "LHPF4" }, \
+       { name, "ASRC1L", "ASRC1L" }, \
+       { name, "ASRC1R", "ASRC1R" }, \
+       { name, "ASRC2L", "ASRC2L" }, \
+       { name, "ASRC2R", "ASRC2R" }, \
+       { name, "ISRC1DEC1", "ISRC1DEC1" }, \
+       { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+       { name, "ISRC1DEC3", "ISRC1DEC3" }, \
+       { name, "ISRC1DEC4", "ISRC1DEC4" }, \
+       { name, "ISRC1INT1", "ISRC1INT1" }, \
+       { name, "ISRC1INT2", "ISRC1INT2" }, \
+       { name, "ISRC1INT3", "ISRC1INT3" }, \
+       { name, "ISRC1INT4", "ISRC1INT4" }, \
+       { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+       { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+       { name, "ISRC2INT1", "ISRC2INT1" }, \
+       { name, "ISRC2INT2", "ISRC2INT2" }
+
+static const struct snd_soc_dapm_route wm8998_dapm_routes[] = {
+       { "AIF2 Capture", NULL, "DBVDD2" },
+       { "AIF2 Playback", NULL, "DBVDD2" },
+
+       { "AIF3 Capture", NULL, "DBVDD3" },
+       { "AIF3 Playback", NULL, "DBVDD3" },
+
+       { "OUT1L", NULL, "CPVDD" },
+       { "OUT1R", NULL, "CPVDD" },
+       { "OUT2L", NULL, "CPVDD" },
+       { "OUT2R", NULL, "CPVDD" },
+       { "OUT3",  NULL, "CPVDD" },
+
+       { "OUT4L", NULL, "SPKVDDL" },
+       { "OUT4R", NULL, "SPKVDDR" },
+
+       { "OUT1L", NULL, "SYSCLK" },
+       { "OUT1R", NULL, "SYSCLK" },
+       { "OUT2L", NULL, "SYSCLK" },
+       { "OUT2R", NULL, "SYSCLK" },
+       { "OUT3",  NULL, "SYSCLK" },
+       { "OUT4L", NULL, "SYSCLK" },
+       { "OUT4R", NULL, "SYSCLK" },
+       { "OUT5L", NULL, "SYSCLK" },
+       { "OUT5R", NULL, "SYSCLK" },
+
+       { "IN1AL", NULL, "SYSCLK" },
+       { "IN1AR", NULL, "SYSCLK" },
+       { "IN1BL", NULL, "SYSCLK" },
+       { "IN1BR", NULL, "SYSCLK" },
+       { "IN2A", NULL, "SYSCLK" },
+       { "IN2B", NULL, "SYSCLK" },
+
+       { "SPD1", NULL, "SYSCLK" },
+       { "SPD1", NULL, "SPD1TX1" },
+       { "SPD1", NULL, "SPD1TX2" },
+
+       { "MICBIAS1", NULL, "MICVDD" },
+       { "MICBIAS2", NULL, "MICVDD" },
+       { "MICBIAS3", NULL, "MICVDD" },
+
+       { "Tone Generator 1", NULL, "SYSCLK" },
+       { "Tone Generator 2", NULL, "SYSCLK" },
+
+       { "Tone Generator 1", NULL, "TONE" },
+       { "Tone Generator 2", NULL, "TONE" },
+
+       { "AIF1 Capture", NULL, "AIF1TX1" },
+       { "AIF1 Capture", NULL, "AIF1TX2" },
+       { "AIF1 Capture", NULL, "AIF1TX3" },
+       { "AIF1 Capture", NULL, "AIF1TX4" },
+       { "AIF1 Capture", NULL, "AIF1TX5" },
+       { "AIF1 Capture", NULL, "AIF1TX6" },
+
+       { "AIF1RX1", NULL, "AIF1 Playback" },
+       { "AIF1RX2", NULL, "AIF1 Playback" },
+       { "AIF1RX3", NULL, "AIF1 Playback" },
+       { "AIF1RX4", NULL, "AIF1 Playback" },
+       { "AIF1RX5", NULL, "AIF1 Playback" },
+       { "AIF1RX6", NULL, "AIF1 Playback" },
+
+       { "AIF2 Capture", NULL, "AIF2TX1" },
+       { "AIF2 Capture", NULL, "AIF2TX2" },
+       { "AIF2 Capture", NULL, "AIF2TX3" },
+       { "AIF2 Capture", NULL, "AIF2TX4" },
+       { "AIF2 Capture", NULL, "AIF2TX5" },
+       { "AIF2 Capture", NULL, "AIF2TX6" },
+
+       { "AIF2RX1", NULL, "AIF2 Playback" },
+       { "AIF2RX2", NULL, "AIF2 Playback" },
+       { "AIF2RX3", NULL, "AIF2 Playback" },
+       { "AIF2RX4", NULL, "AIF2 Playback" },
+       { "AIF2RX5", NULL, "AIF2 Playback" },
+       { "AIF2RX6", NULL, "AIF2 Playback" },
+
+       { "AIF3 Capture", NULL, "AIF3TX1" },
+       { "AIF3 Capture", NULL, "AIF3TX2" },
+
+       { "AIF3RX1", NULL, "AIF3 Playback" },
+       { "AIF3RX2", NULL, "AIF3 Playback" },
+
+       { "Slim1 Capture", NULL, "SLIMTX1" },
+       { "Slim1 Capture", NULL, "SLIMTX2" },
+       { "Slim1 Capture", NULL, "SLIMTX3" },
+       { "Slim1 Capture", NULL, "SLIMTX4" },
+
+       { "Slim2 Capture", NULL, "SLIMTX5" },
+       { "Slim2 Capture", NULL, "SLIMTX6" },
+
+       { "SLIMRX1", NULL, "Slim1 Playback" },
+       { "SLIMRX2", NULL, "Slim1 Playback" },
+
+       { "SLIMRX3", NULL, "Slim2 Playback" },
+       { "SLIMRX4", NULL, "Slim2 Playback" },
+
+       { "AIF1 Playback", NULL, "SYSCLK" },
+       { "AIF2 Playback", NULL, "SYSCLK" },
+       { "AIF3 Playback", NULL, "SYSCLK" },
+       { "Slim1 Playback", NULL, "SYSCLK" },
+       { "Slim2 Playback", NULL, "SYSCLK" },
+
+       { "AIF1 Capture", NULL, "SYSCLK" },
+       { "AIF2 Capture", NULL, "SYSCLK" },
+       { "AIF3 Capture", NULL, "SYSCLK" },
+       { "Slim1 Capture", NULL, "SYSCLK" },
+       { "Slim2 Capture", NULL, "SYSCLK" },
+
+       { "IN1L Mux", "A", "IN1AL" },
+       { "IN1R Mux", "A", "IN1AR" },
+       { "IN1L Mux", "B", "IN1BL" },
+       { "IN1R Mux", "B", "IN1BR" },
+
+       { "IN2 Mux", "A", "IN2A" },
+       { "IN2 Mux", "B", "IN2B" },
+
+       { "IN1L PGA", NULL, "IN1L Mux" },
+       { "IN1R PGA", NULL, "IN1R Mux" },
+       { "IN2 PGA",  NULL, "IN2 Mux" },
+
+       ARIZONA_MIXER_ROUTES("OUT1L", "HPOUTL"),
+       ARIZONA_MIXER_ROUTES("OUT1R", "HPOUTR"),
+       ARIZONA_MIXER_ROUTES("OUT2L", "LINEOUTL"),
+       ARIZONA_MIXER_ROUTES("OUT2R", "LINEOUTR"),
+       ARIZONA_MIXER_ROUTES("OUT3",  "EPOUT"),
+
+       ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUTL"),
+       ARIZONA_MIXER_ROUTES("OUT4R", "SPKOUTR"),
+       ARIZONA_MIXER_ROUTES("OUT5L", "SPKDATL"),
+       ARIZONA_MIXER_ROUTES("OUT5R", "SPKDATR"),
+
+       ARIZONA_MIXER_ROUTES("PWM1 Driver", "PWM1"),
+       ARIZONA_MIXER_ROUTES("PWM2 Driver", "PWM2"),
+
+       ARIZONA_MIXER_ROUTES("AIF1TX1", "AIF1TX1"),
+       ARIZONA_MIXER_ROUTES("AIF1TX2", "AIF1TX2"),
+       ARIZONA_MIXER_ROUTES("AIF1TX3", "AIF1TX3"),
+       ARIZONA_MIXER_ROUTES("AIF1TX4", "AIF1TX4"),
+       ARIZONA_MIXER_ROUTES("AIF1TX5", "AIF1TX5"),
+       ARIZONA_MIXER_ROUTES("AIF1TX6", "AIF1TX6"),
+
+       ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+       ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+       ARIZONA_MIXER_ROUTES("AIF2TX3", "AIF2TX3"),
+       ARIZONA_MIXER_ROUTES("AIF2TX4", "AIF2TX4"),
+       ARIZONA_MIXER_ROUTES("AIF2TX5", "AIF2TX5"),
+       ARIZONA_MIXER_ROUTES("AIF2TX6", "AIF2TX6"),
+
+       ARIZONA_MIXER_ROUTES("AIF3TX1", "AIF3TX1"),
+       ARIZONA_MIXER_ROUTES("AIF3TX2", "AIF3TX2"),
+
+       ARIZONA_MUX_ROUTES("SLIMTX1", "SLIMTX1"),
+       ARIZONA_MUX_ROUTES("SLIMTX2", "SLIMTX2"),
+       ARIZONA_MUX_ROUTES("SLIMTX3", "SLIMTX3"),
+       ARIZONA_MUX_ROUTES("SLIMTX4", "SLIMTX4"),
+       ARIZONA_MUX_ROUTES("SLIMTX5", "SLIMTX5"),
+       ARIZONA_MUX_ROUTES("SLIMTX6", "SLIMTX6"),
+
+       ARIZONA_MUX_ROUTES("SPD1TX1", "SPDIFTX1"),
+       ARIZONA_MUX_ROUTES("SPD1TX2", "SPDIFTX2"),
+
+       ARIZONA_MUX_ROUTES("EQ1", "EQ1"),
+       ARIZONA_MUX_ROUTES("EQ2", "EQ2"),
+       ARIZONA_MUX_ROUTES("EQ3", "EQ3"),
+       ARIZONA_MUX_ROUTES("EQ4", "EQ4"),
+
+       ARIZONA_MUX_ROUTES("DRC1L", "DRC1L"),
+       ARIZONA_MUX_ROUTES("DRC1R", "DRC1R"),
+
+       ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
+       ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
+       ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
+       ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+       ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
+       ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
+       ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
+       ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
+
+       ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+       ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
+       ARIZONA_MUX_ROUTES("ISRC1INT3", "ISRC1INT3"),
+       ARIZONA_MUX_ROUTES("ISRC1INT4", "ISRC1INT4"),
+
+       ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+       ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+       ARIZONA_MUX_ROUTES("ISRC1DEC3", "ISRC1DEC3"),
+       ARIZONA_MUX_ROUTES("ISRC1DEC4", "ISRC1DEC4"),
+
+       ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+       ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+       ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+       ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+       { "AEC1 Loopback", "HPOUTL", "OUT1L" },
+       { "AEC1 Loopback", "HPOUTR", "OUT1R" },
+       { "AEC2 Loopback", "HPOUTL", "OUT1L" },
+       { "AEC2 Loopback", "HPOUTR", "OUT1R" },
+       { "HPOUTL", NULL, "OUT1L" },
+       { "HPOUTR", NULL, "OUT1R" },
+
+       { "AEC1 Loopback", "LINEOUTL", "OUT2L" },
+       { "AEC1 Loopback", "LINEOUTR", "OUT2R" },
+       { "AEC2 Loopback", "LINEOUTL", "OUT2L" },
+       { "AEC2 Loopback", "LINEOUTR", "OUT2R" },
+       { "LINEOUTL", NULL, "OUT2L" },
+       { "LINEOUTR", NULL, "OUT2R" },
+
+       { "AEC1 Loopback", "EPOUT", "OUT3" },
+       { "AEC2 Loopback", "EPOUT", "OUT3" },
+       { "EPOUT", NULL, "OUT3" },
+
+       { "AEC1 Loopback", "SPKOUTL", "OUT4L" },
+       { "AEC2 Loopback", "SPKOUTL", "OUT4L" },
+       { "SPKOUTLN", NULL, "OUT4L" },
+       { "SPKOUTLP", NULL, "OUT4L" },
+
+       { "AEC1 Loopback", "SPKOUTR", "OUT4R" },
+       { "AEC2 Loopback", "SPKOUTR", "OUT4R" },
+       { "SPKOUTRN", NULL, "OUT4R" },
+       { "SPKOUTRP", NULL, "OUT4R" },
+
+       { "SPDIF", NULL, "SPD1" },
+
+       { "AEC1 Loopback", "SPKDATL", "OUT5L" },
+       { "AEC1 Loopback", "SPKDATR", "OUT5R" },
+       { "AEC2 Loopback", "SPKDATL", "OUT5L" },
+       { "AEC2 Loopback", "SPKDATR", "OUT5R" },
+       { "SPKDATL", NULL, "OUT5L" },
+       { "SPKDATR", NULL, "OUT5R" },
+
+       { "MICSUPP", NULL, "SYSCLK" },
+
+       { "DRC1 Signal Activity", NULL, "DRC1L" },
+       { "DRC1 Signal Activity", NULL, "DRC1R" },
+};
+
+#define WM8998_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM8998_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
+                       SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S32_LE)
+
+static struct snd_soc_dai_driver wm8998_dai[] = {
+       {
+               .name = "wm8998-aif1",
+               .id = 1,
+               .base = ARIZONA_AIF1_BCLK_CTRL,
+               .playback = {
+                       .stream_name = "AIF1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 6,
+                       .rates = WM8998_RATES,
+                       .formats = WM8998_FORMATS,
+               },
+               .capture = {
+                        .stream_name = "AIF1 Capture",
+                        .channels_min = 1,
+                        .channels_max = 6,
+                        .rates = WM8998_RATES,
+                        .formats = WM8998_FORMATS,
+                },
+               .ops = &arizona_dai_ops,
+               .symmetric_rates = 1,
+               .symmetric_samplebits = 1,
+       },
+       {
+               .name = "wm8998-aif2",
+               .id = 2,
+               .base = ARIZONA_AIF2_BCLK_CTRL,
+               .playback = {
+                       .stream_name = "AIF2 Playback",
+                       .channels_min = 1,
+                       .channels_max = 6,
+                       .rates = WM8998_RATES,
+                       .formats = WM8998_FORMATS,
+               },
+               .capture = {
+                        .stream_name = "AIF2 Capture",
+                        .channels_min = 1,
+                        .channels_max = 6,
+                        .rates = WM8998_RATES,
+                        .formats = WM8998_FORMATS,
+                },
+               .ops = &arizona_dai_ops,
+               .symmetric_rates = 1,
+               .symmetric_samplebits = 1,
+       },
+       {
+               .name = "wm8998-aif3",
+               .id = 3,
+               .base = ARIZONA_AIF3_BCLK_CTRL,
+               .playback = {
+                       .stream_name = "AIF3 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = WM8998_RATES,
+                       .formats = WM8998_FORMATS,
+               },
+               .capture = {
+                        .stream_name = "AIF3 Capture",
+                        .channels_min = 1,
+                        .channels_max = 2,
+                        .rates = WM8998_RATES,
+                        .formats = WM8998_FORMATS,
+                },
+               .ops = &arizona_dai_ops,
+               .symmetric_rates = 1,
+               .symmetric_samplebits = 1,
+       },
+       {
+               .name = "wm8998-slim1",
+               .id = 4,
+               .playback = {
+                       .stream_name = "Slim1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = WM8998_RATES,
+                       .formats = WM8998_FORMATS,
+               },
+               .capture = {
+                        .stream_name = "Slim1 Capture",
+                        .channels_min = 1,
+                        .channels_max = 4,
+                        .rates = WM8998_RATES,
+                        .formats = WM8998_FORMATS,
+                },
+               .ops = &arizona_simple_dai_ops,
+       },
+       {
+               .name = "wm8998-slim2",
+               .id = 5,
+               .playback = {
+                       .stream_name = "Slim2 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = WM8998_RATES,
+                       .formats = WM8998_FORMATS,
+               },
+               .capture = {
+                        .stream_name = "Slim2 Capture",
+                        .channels_min = 1,
+                        .channels_max = 2,
+                        .rates = WM8998_RATES,
+                        .formats = WM8998_FORMATS,
+                },
+               .ops = &arizona_simple_dai_ops,
+       },
+};
+
+static int wm8998_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+                         unsigned int Fref, unsigned int Fout)
+{
+       struct wm8998_priv *wm8998 = snd_soc_codec_get_drvdata(codec);
+
+       switch (fll_id) {
+       case WM8998_FLL1:
+               return arizona_set_fll(&wm8998->fll[0], source, Fref, Fout);
+       case WM8998_FLL2:
+               return arizona_set_fll(&wm8998->fll[1], source, Fref, Fout);
+       case WM8998_FLL1_REFCLK:
+               return arizona_set_fll_refclk(&wm8998->fll[0], source, Fref,
+                                             Fout);
+       case WM8998_FLL2_REFCLK:
+               return arizona_set_fll_refclk(&wm8998->fll[1], source, Fref,
+                                             Fout);
+       default:
+               return -EINVAL;
+       }
+}
+
+static int wm8998_codec_probe(struct snd_soc_codec *codec)
+{
+       struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct snd_soc_dapm_context *dapm = snd_soc_codec_get_dapm(codec);
+
+       priv->core.arizona->dapm = dapm;
+
+       arizona_init_spk(codec);
+       arizona_init_gpio(codec);
+
+       snd_soc_dapm_disable_pin(dapm, "HAPTICS");
+
+       return 0;
+}
+
+static int wm8998_codec_remove(struct snd_soc_codec *codec)
+{
+       struct wm8998_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       priv->core.arizona->dapm = NULL;
+
+       return 0;
+}
+
+#define WM8998_DIG_VU 0x0200
+
+static unsigned int wm8998_digital_vu[] = {
+       ARIZONA_DAC_DIGITAL_VOLUME_1L,
+       ARIZONA_DAC_DIGITAL_VOLUME_1R,
+       ARIZONA_DAC_DIGITAL_VOLUME_2L,
+       ARIZONA_DAC_DIGITAL_VOLUME_2R,
+       ARIZONA_DAC_DIGITAL_VOLUME_3L,
+       ARIZONA_DAC_DIGITAL_VOLUME_4L,
+       ARIZONA_DAC_DIGITAL_VOLUME_4R,
+       ARIZONA_DAC_DIGITAL_VOLUME_5L,
+       ARIZONA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static struct regmap *wm8998_get_regmap(struct device *dev)
+{
+       struct wm8998_priv *priv = dev_get_drvdata(dev);
+
+       return priv->core.arizona->regmap;
+}
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8998 = {
+       .probe = wm8998_codec_probe,
+       .remove = wm8998_codec_remove,
+       .get_regmap = wm8998_get_regmap,
+
+       .idle_bias_off = true,
+
+       .set_sysclk = arizona_set_sysclk,
+       .set_pll = wm8998_set_fll,
+
+       .controls = wm8998_snd_controls,
+       .num_controls = ARRAY_SIZE(wm8998_snd_controls),
+       .dapm_widgets = wm8998_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(wm8998_dapm_widgets),
+       .dapm_routes = wm8998_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(wm8998_dapm_routes),
+};
+
+static int wm8998_probe(struct platform_device *pdev)
+{
+       struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+       struct wm8998_priv *wm8998;
+       int i;
+
+       wm8998 = devm_kzalloc(&pdev->dev, sizeof(struct wm8998_priv),
+                             GFP_KERNEL);
+       if (!wm8998)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, wm8998);
+
+       wm8998->core.arizona = arizona;
+       wm8998->core.num_inputs = 3;    /* IN1L, IN1R, IN2 */
+
+       for (i = 0; i < ARRAY_SIZE(wm8998->fll); i++)
+               wm8998->fll[i].vco_mult = 1;
+
+       arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+                        ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+                        &wm8998->fll[0]);
+       arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+                        ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+                        &wm8998->fll[1]);
+
+       for (i = 0; i < ARRAY_SIZE(wm8998_dai); i++)
+               arizona_init_dai(&wm8998->core, i);
+
+       /* Latch volume update bits */
+       for (i = 0; i < ARRAY_SIZE(wm8998_digital_vu); i++)
+               regmap_update_bits(arizona->regmap, wm8998_digital_vu[i],
+                                  WM8998_DIG_VU, WM8998_DIG_VU);
+
+       pm_runtime_enable(&pdev->dev);
+       pm_runtime_idle(&pdev->dev);
+
+       return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8998,
+                                     wm8998_dai, ARRAY_SIZE(wm8998_dai));
+}
+
+static int wm8998_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static struct platform_driver wm8998_codec_driver = {
+       .driver = {
+               .name = "wm8998-codec",
+       },
+       .probe = wm8998_probe,
+       .remove = wm8998_remove,
+};
+
+module_platform_driver(wm8998_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM8998 driver");
+MODULE_AUTHOR("Richard Fitzgerald <rf@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:wm8998-codec");
diff --git a/sound/soc/codecs/wm8998.h b/sound/soc/codecs/wm8998.h
new file mode 100644 (file)
index 0000000..1e86472
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * wm8998.h -- ALSA SoC Audio driver for WM8998 codecs
+ *
+ * Copyright 2015 Cirrus Logic, Inc.
+ *
+ * Author: Richard Fitzgerald <rf@opensource.wolfsonmicro.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 _WM8998_H
+#define _WM8998_H
+
+#include "arizona.h"
+
+#define WM8998_FLL1        1
+#define WM8998_FLL2        2
+#define WM8998_FLL1_REFCLK 3
+#define WM8998_FLL2_REFCLK 4
+
+#endif
index 7d45d98a861fccb65f32987e3760c2b74e487f9a..4495a40a94680600ff324bd035098ff3d73f594b 100644 (file)
@@ -80,12 +80,13 @@ struct davinci_mcasp {
 
        /* McASP specific data */
        int     tdm_slots;
+       u32     tdm_mask[2];
+       int     slot_width;
        u8      op_mode;
        u8      num_serializer;
        u8      *serial_dir;
        u8      version;
        u8      bclk_div;
-       u16     bclk_lrclk_ratio;
        int     streams;
        u32     irq_request[2];
        int     dma_request[2];
@@ -556,8 +557,21 @@ static int __davinci_mcasp_set_clkdiv(struct snd_soc_dai *dai, int div_id,
                        mcasp->bclk_div = div;
                break;
 
-       case 2:         /* BCLK/LRCLK ratio */
-               mcasp->bclk_lrclk_ratio = div;
+       case 2: /*
+                * BCLK/LRCLK ratio descries how many bit-clock cycles
+                * fit into one frame. The clock ratio is given for a
+                * full period of data (for I2S format both left and
+                * right channels), so it has to be divided by number
+                * of tdm-slots (for I2S - divided by 2).
+                * Instead of storing this ratio, we calculate a new
+                * tdm_slot width by dividing the the ratio by the
+                * number of configured tdm slots.
+                */
+               mcasp->slot_width = div / mcasp->tdm_slots;
+               if (div % mcasp->tdm_slots)
+                       dev_warn(mcasp->dev,
+                                "%s(): BCLK/LRCLK %d is not divisible by %d tdm slots",
+                                __func__, div, mcasp->tdm_slots);
                break;
 
        default:
@@ -596,12 +610,92 @@ static int davinci_mcasp_set_sysclk(struct snd_soc_dai *dai, int clk_id,
        return 0;
 }
 
+/* All serializers must have equal number of channels */
+static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp, int stream,
+                                      int serializers)
+{
+       struct snd_pcm_hw_constraint_list *cl = &mcasp->chconstr[stream];
+       unsigned int *list = (unsigned int *) cl->list;
+       int slots = mcasp->tdm_slots;
+       int i, count = 0;
+
+       if (mcasp->tdm_mask[stream])
+               slots = hweight32(mcasp->tdm_mask[stream]);
+
+       for (i = 2; i <= slots; i++)
+               list[count++] = i;
+
+       for (i = 2; i <= serializers; i++)
+               list[count++] = i*slots;
+
+       cl->count = count;
+
+       return 0;
+}
+
+static int davinci_mcasp_set_ch_constraints(struct davinci_mcasp *mcasp)
+{
+       int rx_serializers = 0, tx_serializers = 0, ret, i;
+
+       for (i = 0; i < mcasp->num_serializer; i++)
+               if (mcasp->serial_dir[i] == TX_MODE)
+                       tx_serializers++;
+               else if (mcasp->serial_dir[i] == RX_MODE)
+                       rx_serializers++;
+
+       ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_PLAYBACK,
+                                         tx_serializers);
+       if (ret)
+               return ret;
+
+       ret = davinci_mcasp_ch_constraint(mcasp, SNDRV_PCM_STREAM_CAPTURE,
+                                         rx_serializers);
+
+       return ret;
+}
+
+
+static int davinci_mcasp_set_tdm_slot(struct snd_soc_dai *dai,
+                                     unsigned int tx_mask,
+                                     unsigned int rx_mask,
+                                     int slots, int slot_width)
+{
+       struct davinci_mcasp *mcasp = snd_soc_dai_get_drvdata(dai);
+
+       dev_dbg(mcasp->dev,
+                "%s() tx_mask 0x%08x rx_mask 0x%08x slots %d width %d\n",
+                __func__, tx_mask, rx_mask, slots, slot_width);
+
+       if (tx_mask >= (1<<slots) || rx_mask >= (1<<slots)) {
+               dev_err(mcasp->dev,
+                       "Bad tdm mask tx: 0x%08x rx: 0x%08x slots %d\n",
+                       tx_mask, rx_mask, slots);
+               return -EINVAL;
+       }
+
+       if (slot_width &&
+           (slot_width < 8 || slot_width > 32 || slot_width % 4 != 0)) {
+               dev_err(mcasp->dev, "%s: Unsupported slot_width %d\n",
+                       __func__, slot_width);
+               return -EINVAL;
+       }
+
+       mcasp->tdm_slots = slots;
+       mcasp->tdm_mask[SNDRV_PCM_STREAM_PLAYBACK] = rx_mask;
+       mcasp->tdm_mask[SNDRV_PCM_STREAM_CAPTURE] = tx_mask;
+       mcasp->slot_width = slot_width;
+
+       return davinci_mcasp_set_ch_constraints(mcasp);
+}
+
 static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
-                                      int word_length)
+                                      int sample_width)
 {
        u32 fmt;
-       u32 tx_rotate = (word_length / 4) & 0x7;
-       u32 mask = (1ULL << word_length) - 1;
+       u32 tx_rotate = (sample_width / 4) & 0x7;
+       u32 mask = (1ULL << sample_width) - 1;
+       u32 slot_width = sample_width;
+
        /*
         * For captured data we should not rotate, inversion and masking is
         * enoguh to get the data to the right position:
@@ -614,28 +708,23 @@ static int davinci_config_channel_size(struct davinci_mcasp *mcasp,
        u32 rx_rotate = 0;
 
        /*
-        * if s BCLK-to-LRCLK ratio has been configured via the set_clkdiv()
-        * callback, take it into account here. That allows us to for example
-        * send 32 bits per channel to the codec, while only 16 of them carry
-        * audio payload.
-        * The clock ratio is given for a full period of data (for I2S format
-        * both left and right channels), so it has to be divided by number of
-        * tdm-slots (for I2S - divided by 2).
+        * Setting the tdm slot width either with set_clkdiv() or
+        * set_tdm_slot() allows us to for example send 32 bits per
+        * channel to the codec, while only 16 of them carry audio
+        * payload.
         */
-       if (mcasp->bclk_lrclk_ratio) {
-               u32 slot_length = mcasp->bclk_lrclk_ratio / mcasp->tdm_slots;
-
+       if (mcasp->slot_width) {
                /*
-                * 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.
+                * 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;
+               slot_width = mcasp->slot_width;
+               rx_rotate = (slot_width - sample_width) / 4;
        }
 
        /* mapping of the XSSZ bit-field as described in the datasheet */
-       fmt = (word_length >> 1) - 1;
+       fmt = (slot_width >> 1) - 1;
 
        if (mcasp->op_mode != DAVINCI_MCASP_DIT_MODE) {
                mcasp_mod_bits(mcasp, DAVINCI_MCASP_RXFMT_REG, RXSSZ(fmt),
@@ -776,33 +865,50 @@ static int mcasp_i2s_hw_param(struct davinci_mcasp *mcasp, int stream,
 
        /*
         * 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
+        * all the specified tdm_slots. Otherwise, one serializer can
+        * cope with the transaction using just as many slots as there
+        * are channels in the stream.
         */
-       active_serializers = (channels + total_slots - 1) / total_slots;
-       if (active_serializers == 1)
-               active_slots = channels;
-       else
-               active_slots = total_slots;
-
-       for (i = 0; i < active_slots; i++)
-               mask |= (1 << i);
+       if (mcasp->tdm_mask[stream]) {
+               active_slots = hweight32(mcasp->tdm_mask[stream]);
+               active_serializers = (channels + active_slots - 1) /
+                       active_slots;
+               if (active_serializers == 1) {
+                       active_slots = channels;
+                       for (i = 0; i < total_slots; i++) {
+                               if ((1 << i) & mcasp->tdm_mask[stream]) {
+                                       mask |= (1 << i);
+                                       if (--active_slots <= 0)
+                                               break;
+                               }
+                       }
+               }
+       } else {
+               active_serializers = (channels + total_slots - 1) / total_slots;
+               if (active_serializers == 1)
+                       active_slots = channels;
+               else
+                       active_slots = total_slots;
 
+               for (i = 0; i < active_slots; i++)
+                       mask |= (1 << i);
+       }
        mcasp_clr_bits(mcasp, DAVINCI_MCASP_ACLKXCTL_REG, TX_ASYNC);
 
        if (!mcasp->dat_port)
                busel = TXSEL;
 
-       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(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(total_slots), FSRMOD(0x1FF));
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               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(total_slots), FSXMOD(0x1FF));
+       } else if (stream == SNDRV_PCM_STREAM_CAPTURE) {
+               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(total_slots), FSRMOD(0x1FF));
+       }
 
        return 0;
 }
@@ -922,6 +1028,9 @@ static int davinci_mcasp_hw_params(struct snd_pcm_substream *substream,
                int sbits = params_width(params);
                int ppm, div;
 
+               if (mcasp->slot_width)
+                       sbits = mcasp->slot_width;
+
                div = davinci_mcasp_calc_clk_div(mcasp, rate*sbits*slots,
                                                 &ppm);
                if (ppm)
@@ -1027,6 +1136,9 @@ static int davinci_mcasp_hw_rule_rate(struct snd_pcm_hw_params *params,
        struct snd_interval range;
        int i;
 
+       if (rd->mcasp->slot_width)
+               sbits = rd->mcasp->slot_width;
+
        snd_interval_any(&range);
        range.empty = 1;
 
@@ -1069,10 +1181,14 @@ static int davinci_mcasp_hw_rule_format(struct snd_pcm_hw_params *params,
 
        for (i = 0; i < SNDRV_PCM_FORMAT_LAST; i++) {
                if (snd_mask_test(fmt, i)) {
-                       uint bclk_freq = snd_pcm_format_width(i)*slots*rate;
+                       uint sbits = snd_pcm_format_width(i);
                        int ppm;
 
-                       davinci_mcasp_calc_clk_div(rd->mcasp, bclk_freq, &ppm);
+                       if (rd->mcasp->slot_width)
+                               sbits = rd->mcasp->slot_width;
+
+                       davinci_mcasp_calc_clk_div(rd->mcasp, sbits*slots*rate,
+                                                  &ppm);
                        if (abs(ppm) < DAVINCI_MAX_RATE_ERROR_PPM) {
                                snd_mask_set(&nfmt, i);
                                count++;
@@ -1094,6 +1210,10 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
                                        &mcasp->ruledata[substream->stream];
        u32 max_channels = 0;
        int i, dir;
+       int tdm_slots = mcasp->tdm_slots;
+
+       if (mcasp->tdm_mask[substream->stream])
+               tdm_slots = hweight32(mcasp->tdm_mask[substream->stream]);
 
        mcasp->substreams[substream->stream] = substream;
 
@@ -1114,7 +1234,7 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
                        max_channels++;
        }
        ruledata->serializers = max_channels;
-       max_channels *= mcasp->tdm_slots;
+       max_channels *= 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
@@ -1124,15 +1244,25 @@ static int davinci_mcasp_startup(struct snd_pcm_substream *substream,
         */
        if (mcasp->channels && mcasp->channels < max_channels)
                max_channels = mcasp->channels;
+       /*
+        * But we can always allow channels upto the amount of
+        * the available tdm_slots.
+        */
+       if (max_channels < tdm_slots)
+               max_channels = tdm_slots;
 
        snd_pcm_hw_constraint_minmax(substream->runtime,
                                     SNDRV_PCM_HW_PARAM_CHANNELS,
                                     2, max_channels);
 
-       if (mcasp->chconstr[substream->stream].count)
-               snd_pcm_hw_constraint_list(substream->runtime,
-                                          0, SNDRV_PCM_HW_PARAM_CHANNELS,
-                                          &mcasp->chconstr[substream->stream]);
+       snd_pcm_hw_constraint_list(substream->runtime,
+                                  0, SNDRV_PCM_HW_PARAM_CHANNELS,
+                                  &mcasp->chconstr[substream->stream]);
+
+       if (mcasp->slot_width)
+               snd_pcm_hw_constraint_minmax(substream->runtime,
+                                            SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
+                                            8, mcasp->slot_width);
 
        /*
         * If we rely on implicit BCLK divider setting we should
@@ -1184,6 +1314,7 @@ static const struct snd_soc_dai_ops davinci_mcasp_dai_ops = {
        .set_fmt        = davinci_mcasp_set_dai_fmt,
        .set_clkdiv     = davinci_mcasp_set_clkdiv,
        .set_sysclk     = davinci_mcasp_set_sysclk,
+       .set_tdm_slot   = davinci_mcasp_set_tdm_slot,
 };
 
 static int davinci_mcasp_dai_probe(struct snd_soc_dai *dai)
@@ -1514,59 +1645,6 @@ nodata:
        return  pdata;
 }
 
-/* All serializers must have equal number of channels */
-static int davinci_mcasp_ch_constraint(struct davinci_mcasp *mcasp,
-                                      struct snd_pcm_hw_constraint_list *cl,
-                                      int serializers)
-{
-       unsigned int *list;
-       int i, count = 0;
-
-       if (serializers <= 1)
-               return 0;
-
-       list = devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
-                           (mcasp->tdm_slots + serializers - 2),
-                           GFP_KERNEL);
-       if (!list)
-               return -ENOMEM;
-
-       for (i = 2; i <= mcasp->tdm_slots; i++)
-               list[count++] = i;
-
-       for (i = 2; i <= serializers; i++)
-               list[count++] = i*mcasp->tdm_slots;
-
-       cl->count = count;
-       cl->list = list;
-
-       return 0;
-}
-
-
-static int davinci_mcasp_init_ch_constraints(struct davinci_mcasp *mcasp)
-{
-       int rx_serializers = 0, tx_serializers = 0, ret, i;
-
-       for (i = 0; i < mcasp->num_serializer; i++)
-               if (mcasp->serial_dir[i] == TX_MODE)
-                       tx_serializers++;
-               else if (mcasp->serial_dir[i] == RX_MODE)
-                       rx_serializers++;
-
-       ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
-                                                 SNDRV_PCM_STREAM_PLAYBACK],
-                                         tx_serializers);
-       if (ret)
-               return ret;
-
-       ret = davinci_mcasp_ch_constraint(mcasp, &mcasp->chconstr[
-                                                 SNDRV_PCM_STREAM_CAPTURE],
-                                         rx_serializers);
-
-       return ret;
-}
-
 enum {
        PCM_EDMA,
        PCM_SDMA,
@@ -1783,7 +1861,28 @@ static int davinci_mcasp_probe(struct platform_device *pdev)
                mcasp->fifo_base = DAVINCI_MCASP_V3_AFIFO_BASE;
        }
 
-       ret = davinci_mcasp_init_ch_constraints(mcasp);
+       /* Allocate memory for long enough list for all possible
+        * scenarios. Maximum number tdm slots is 32 and there cannot
+        * be more serializers than given in the configuration.  The
+        * serializer directions could be taken into account, but it
+        * would make code much more complex and save only couple of
+        * bytes.
+        */
+       mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list =
+               devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
+                            (32 + mcasp->num_serializer - 2),
+                            GFP_KERNEL);
+
+       mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list =
+               devm_kzalloc(mcasp->dev, sizeof(unsigned int) *
+                            (32 + mcasp->num_serializer - 2),
+                            GFP_KERNEL);
+
+       if (!mcasp->chconstr[SNDRV_PCM_STREAM_PLAYBACK].list ||
+           !mcasp->chconstr[SNDRV_PCM_STREAM_CAPTURE].list)
+               return -ENOMEM;
+
+       ret = davinci_mcasp_set_ch_constraints(mcasp);
        if (ret)
                goto err;
 
index ba34252b7bba4fdd8d1b7c58a313b490c14aa329..6e6a70c5c2bdba8349e4652e53a672c7fb40d74e 100644 (file)
@@ -282,23 +282,25 @@ static int dw_i2s_hw_params(struct snd_pcm_substream *substream,
 
        config->sample_rate = params_rate(params);
 
-       if (dev->i2s_clk_cfg) {
-               ret = dev->i2s_clk_cfg(config);
-               if (ret < 0) {
-                       dev_err(dev->dev, "runtime audio clk config fail\n");
-                       return ret;
-               }
-       } else {
-               u32 bitclk = config->sample_rate * config->data_width * 2;
-
-               ret = clk_set_rate(dev->clk, bitclk);
-               if (ret) {
-                       dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
-                               ret);
-                       return ret;
+       if (dev->capability & DW_I2S_MASTER) {
+               if (dev->i2s_clk_cfg) {
+                       ret = dev->i2s_clk_cfg(config);
+                       if (ret < 0) {
+                               dev_err(dev->dev, "runtime audio clk config fail\n");
+                               return ret;
+                       }
+               } else {
+                       u32 bitclk = config->sample_rate *
+                                       config->data_width * 2;
+
+                       ret = clk_set_rate(dev->clk, bitclk);
+                       if (ret) {
+                               dev_err(dev->dev, "Can't set I2S clock rate: %d\n",
+                                       ret);
+                               return ret;
+                       }
                }
        }
-
        return 0;
 }
 
@@ -348,12 +350,43 @@ static int dw_i2s_trigger(struct snd_pcm_substream *substream,
        return ret;
 }
 
+static int dw_i2s_set_fmt(struct snd_soc_dai *cpu_dai, unsigned int fmt)
+{
+       struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(cpu_dai);
+       int ret = 0;
+
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               if (dev->capability & DW_I2S_SLAVE)
+                       ret = 0;
+               else
+                       ret = -EINVAL;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               if (dev->capability & DW_I2S_MASTER)
+                       ret = 0;
+               else
+                       ret = -EINVAL;
+               break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+       case SND_SOC_DAIFMT_CBS_CFM:
+               ret = -EINVAL;
+               break;
+       default:
+               dev_dbg(dev->dev, "dwc : Invalid master/slave format\n");
+               ret = -EINVAL;
+               break;
+       }
+       return ret;
+}
+
 static struct snd_soc_dai_ops dw_i2s_dai_ops = {
        .startup        = dw_i2s_startup,
        .shutdown       = dw_i2s_shutdown,
        .hw_params      = dw_i2s_hw_params,
        .prepare        = dw_i2s_prepare,
        .trigger        = dw_i2s_trigger,
+       .set_fmt        = dw_i2s_set_fmt,
 };
 
 static const struct snd_soc_component_driver dw_i2s_component = {
@@ -366,7 +399,8 @@ static int dw_i2s_suspend(struct snd_soc_dai *dai)
 {
        struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
 
-       clk_disable(dev->clk);
+       if (dev->capability & DW_I2S_MASTER)
+               clk_disable(dev->clk);
        return 0;
 }
 
@@ -374,7 +408,8 @@ static int dw_i2s_resume(struct snd_soc_dai *dai)
 {
        struct dw_i2s_dev *dev = snd_soc_dai_get_drvdata(dai);
 
-       clk_enable(dev->clk);
+       if (dev->capability & DW_I2S_MASTER)
+               clk_enable(dev->clk);
        return 0;
 }
 
@@ -452,6 +487,14 @@ static int dw_configure_dai(struct dw_i2s_dev *dev,
                dw_i2s_dai->capture.rates = rates;
        }
 
+       if (COMP1_MODE_EN(comp1)) {
+               dev_dbg(dev->dev, "designware: i2s master mode supported\n");
+               dev->capability |= DW_I2S_MASTER;
+       } else {
+               dev_dbg(dev->dev, "designware: i2s slave mode supported\n");
+               dev->capability |= DW_I2S_SLAVE;
+       }
+
        return 0;
 }
 
@@ -538,6 +581,7 @@ static int dw_i2s_probe(struct platform_device *pdev)
        struct resource *res;
        int ret;
        struct snd_soc_dai_driver *dw_i2s_dai;
+       const char *clk_id;
 
        dev = devm_kzalloc(&pdev->dev, sizeof(*dev), GFP_KERNEL);
        if (!dev) {
@@ -559,32 +603,35 @@ static int dw_i2s_probe(struct platform_device *pdev)
                return PTR_ERR(dev->i2s_base);
 
        dev->dev = &pdev->dev;
+
        if (pdata) {
+               dev->capability = pdata->cap;
+               clk_id = NULL;
                ret = dw_configure_dai_by_pd(dev, dw_i2s_dai, res, pdata);
-               if (ret < 0)
-                       return ret;
+       } else {
+               clk_id = "i2sclk";
+               ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
+       }
+       if (ret < 0)
+               return ret;
 
-               dev->capability = pdata->cap;
-               dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
-               if (!dev->i2s_clk_cfg) {
-                       dev_err(&pdev->dev, "no clock configure method\n");
-                       return -ENODEV;
+       if (dev->capability & DW_I2S_MASTER) {
+               if (pdata) {
+                       dev->i2s_clk_cfg = pdata->i2s_clk_cfg;
+                       if (!dev->i2s_clk_cfg) {
+                               dev_err(&pdev->dev, "no clock configure method\n");
+                               return -ENODEV;
+                       }
                }
+               dev->clk = devm_clk_get(&pdev->dev, clk_id);
 
-               dev->clk = devm_clk_get(&pdev->dev, NULL);
-       } else {
-               ret = dw_configure_dai_by_dt(dev, dw_i2s_dai, res);
+               if (IS_ERR(dev->clk))
+                       return PTR_ERR(dev->clk);
+
+               ret = clk_prepare_enable(dev->clk);
                if (ret < 0)
                        return ret;
-
-               dev->clk = devm_clk_get(&pdev->dev, "i2sclk");
        }
-       if (IS_ERR(dev->clk))
-               return PTR_ERR(dev->clk);
-
-       ret = clk_prepare_enable(dev->clk);
-       if (ret < 0)
-               return ret;
 
        dev_set_drvdata(&pdev->dev, dev);
        ret = devm_snd_soc_register_component(&pdev->dev, &dw_i2s_component,
@@ -606,7 +653,8 @@ static int dw_i2s_probe(struct platform_device *pdev)
        return 0;
 
 err_clk_disable:
-       clk_disable_unprepare(dev->clk);
+       if (dev->capability & DW_I2S_MASTER)
+               clk_disable_unprepare(dev->clk);
        return ret;
 }
 
@@ -614,7 +662,8 @@ static int dw_i2s_remove(struct platform_device *pdev)
 {
        struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
 
-       clk_disable_unprepare(dev->clk);
+       if (dev->capability & DW_I2S_MASTER)
+               clk_disable_unprepare(dev->clk);
 
        return 0;
 }
index 96f55ae75c719c87d1dcf9084ccc4e6a679a187f..1b05d1c5d9fd50fe32eb9b65f4da1340db9170e5 100644 (file)
@@ -14,6 +14,9 @@
 #include <linux/i2c.h>
 #include <linux/module.h>
 #include <linux/of_platform.h>
+#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
+#include <sound/ac97_codec.h>
+#endif
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 
@@ -115,6 +118,11 @@ static const struct snd_soc_dapm_widget fsl_asoc_card_dapm_widgets[] = {
        SND_SOC_DAPM_MIC("DMIC", NULL),
 };
 
+static bool fsl_asoc_card_is_ac97(struct fsl_asoc_card_priv *priv)
+{
+       return priv->dai_fmt == SND_SOC_DAIFMT_AC97;
+}
+
 static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
                                   struct snd_pcm_hw_params *params)
 {
@@ -133,7 +141,9 @@ static int fsl_asoc_card_hw_params(struct snd_pcm_substream *substream,
         * 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)
+       if ((priv->card.set_bias_level &&
+            priv->dai_fmt & SND_SOC_DAIFMT_CBM_CFM) ||
+           fsl_asoc_card_is_ac97(priv))
                return 0;
 
        /* Specific configurations of DAIs starts from here */
@@ -300,7 +310,7 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
        ext_port--;
 
        /*
-        * Use asynchronous mode (6 wires) for all cases.
+        * Use asynchronous mode (6 wires) for all cases except AC97.
         * If only 4 wires are needed, just set SSI into
         * synchronous mode and enable 4 PADs in IOMUX.
         */
@@ -346,15 +356,30 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
                           IMX_AUDMUX_V2_PTCR_TCLKDIR;
                break;
        default:
-               return -EINVAL;
+               if (!fsl_asoc_card_is_ac97(priv))
+                       return -EINVAL;
+       }
+
+       if (fsl_asoc_card_is_ac97(priv)) {
+               int_ptcr = IMX_AUDMUX_V2_PTCR_SYN |
+                          IMX_AUDMUX_V2_PTCR_TCSEL(ext_port) |
+                          IMX_AUDMUX_V2_PTCR_TCLKDIR;
+               ext_ptcr = IMX_AUDMUX_V2_PTCR_SYN |
+                          IMX_AUDMUX_V2_PTCR_TFSEL(int_port) |
+                          IMX_AUDMUX_V2_PTCR_TFSDIR;
        }
 
        /* Asynchronous mode can not be set along with RCLKDIR */
-       ret = imx_audmux_v2_configure_port(int_port, 0,
-                                          IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port));
-       if (ret) {
-               dev_err(dev, "audmux internal port setup failed\n");
-               return ret;
+       if (!fsl_asoc_card_is_ac97(priv)) {
+               unsigned int pdcr =
+                               IMX_AUDMUX_V2_PDCR_RXDSEL(ext_port);
+
+               ret = imx_audmux_v2_configure_port(int_port, 0,
+                                                  pdcr);
+               if (ret) {
+                       dev_err(dev, "audmux internal port setup failed\n");
+                       return ret;
+               }
        }
 
        ret = imx_audmux_v2_configure_port(int_port, int_ptcr,
@@ -364,11 +389,16 @@ static int fsl_asoc_card_audmux_init(struct device_node *np,
                return ret;
        }
 
-       ret = imx_audmux_v2_configure_port(ext_port, 0,
-                                          IMX_AUDMUX_V2_PDCR_RXDSEL(int_port));
-       if (ret) {
-               dev_err(dev, "audmux external port setup failed\n");
-               return ret;
+       if (!fsl_asoc_card_is_ac97(priv)) {
+               unsigned int pdcr =
+                               IMX_AUDMUX_V2_PDCR_RXDSEL(int_port);
+
+               ret = imx_audmux_v2_configure_port(ext_port, 0,
+                                                  pdcr);
+               if (ret) {
+                       dev_err(dev, "audmux external port setup failed\n");
+                       return ret;
+               }
        }
 
        ret = imx_audmux_v2_configure_port(ext_port, ext_ptcr,
@@ -389,6 +419,23 @@ static int fsl_asoc_card_late_probe(struct snd_soc_card *card)
        struct device *dev = card->dev;
        int ret;
 
+       if (fsl_asoc_card_is_ac97(priv)) {
+#if IS_ENABLED(CONFIG_SND_AC97_CODEC)
+               struct snd_soc_codec *codec = card->rtd[0].codec;
+               struct snd_ac97 *ac97 = snd_soc_codec_get_drvdata(codec);
+
+               /*
+                * Use slots 3/4 for S/PDIF so SSI won't try to enable
+                * other slots and send some samples there
+                * due to SLOTREQ bits for S/PDIF received from codec
+                */
+               snd_ac97_update_bits(ac97, AC97_EXTENDED_STATUS,
+                                    AC97_EA_SPSA_SLOT_MASK, AC97_EA_SPSA_3_4);
+#endif
+
+               return 0;
+       }
+
        ret = snd_soc_dai_set_sysclk(codec_dai, codec_priv->mclk_id,
                                     codec_priv->mclk_freq, SND_SOC_CLOCK_IN);
        if (ret) {
@@ -407,7 +454,6 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
        struct platform_device *cpu_pdev;
        struct fsl_asoc_card_priv *priv;
        struct i2c_client *codec_dev;
-       struct clk *codec_clk;
        const char *codec_dai_name;
        u32 width;
        int ret;
@@ -420,9 +466,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
        /* Give a chance to old DT binding */
        if (!cpu_np)
                cpu_np = of_parse_phandle(np, "ssi-controller", 0);
-       codec_np = of_parse_phandle(np, "audio-codec", 0);
-       if (!cpu_np || !codec_np) {
-               dev_err(&pdev->dev, "phandle missing or invalid\n");
+       if (!cpu_np) {
+               dev_err(&pdev->dev, "CPU phandle missing or invalid\n");
                ret = -EINVAL;
                goto fail;
        }
@@ -434,22 +479,24 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
                goto fail;
        }
 
-       codec_dev = of_find_i2c_device_by_node(codec_np);
-       if (!codec_dev) {
-               dev_err(&pdev->dev, "failed to find codec platform device\n");
-               ret = -EINVAL;
-               goto fail;
-       }
+       codec_np = of_parse_phandle(np, "audio-codec", 0);
+       if (codec_np)
+               codec_dev = of_find_i2c_device_by_node(codec_np);
+       else
+               codec_dev = NULL;
 
        asrc_np = of_parse_phandle(np, "audio-asrc", 0);
        if (asrc_np)
                asrc_pdev = of_find_device_by_node(asrc_np);
 
        /* Get the MCLK rate only, and leave it controlled by CODEC drivers */
-       codec_clk = clk_get(&codec_dev->dev, NULL);
-       if (!IS_ERR(codec_clk)) {
-               priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
-               clk_put(codec_clk);
+       if (codec_dev) {
+               struct clk *codec_clk = clk_get(&codec_dev->dev, NULL);
+
+               if (!IS_ERR(codec_clk)) {
+                       priv->codec_priv.mclk_freq = clk_get_rate(codec_clk);
+                       clk_put(codec_clk);
+               }
        }
 
        /* Default sample rate and format, will be updated in hw_params() */
@@ -486,12 +533,22 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
                priv->codec_priv.fll_id = WM8960_SYSCLK_AUTO;
                priv->codec_priv.pll_id = WM8960_SYSCLK_AUTO;
                priv->dai_fmt |= SND_SOC_DAIFMT_CBM_CFM;
+       } else if (of_device_is_compatible(np, "fsl,imx-audio-ac97")) {
+               codec_dai_name = "ac97-hifi";
+               priv->card.set_bias_level = NULL;
+               priv->dai_fmt = SND_SOC_DAIFMT_AC97;
        } else {
                dev_err(&pdev->dev, "unknown Device Tree compatible\n");
                ret = -EINVAL;
                goto asrc_fail;
        }
 
+       if (!fsl_asoc_card_is_ac97(priv) && !codec_dev) {
+               dev_err(&pdev->dev, "failed to find codec device\n");
+               ret = -EINVAL;
+               goto asrc_fail;
+       }
+
        /* Common settings for corresponding Freescale CPU DAI driver */
        if (strstr(cpu_np->name, "ssi")) {
                /* Only SSI needs to configure AUDMUX */
@@ -508,7 +565,9 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
                priv->cpu_priv.sysclk_id[0] = FSL_SAI_CLK_MAST1;
        }
 
-       sprintf(priv->name, "%s-audio", codec_dev->name);
+       snprintf(priv->name, sizeof(priv->name), "%s-audio",
+                fsl_asoc_card_is_ac97(priv) ? "ac97" :
+                codec_dev->name);
 
        /* Initialize sound card */
        priv->pdev = pdev;
@@ -532,8 +591,26 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
 
        /* Normal DAI Link */
        priv->dai_link[0].cpu_of_node = cpu_np;
-       priv->dai_link[0].codec_of_node = codec_np;
        priv->dai_link[0].codec_dai_name = codec_dai_name;
+
+       if (!fsl_asoc_card_is_ac97(priv))
+               priv->dai_link[0].codec_of_node = codec_np;
+       else {
+               u32 idx;
+
+               ret = of_property_read_u32(cpu_np, "cell-index", &idx);
+               if (ret) {
+                       dev_err(&pdev->dev,
+                               "cannot get CPU index property\n");
+                       goto asrc_fail;
+               }
+
+               priv->dai_link[0].codec_name =
+                               devm_kasprintf(&pdev->dev, GFP_KERNEL,
+                                              "ac97-codec.%u",
+                                              (unsigned int)idx);
+       }
+
        priv->dai_link[0].platform_of_node = cpu_np;
        priv->dai_link[0].dai_fmt = priv->dai_fmt;
        priv->card.num_links = 1;
@@ -544,6 +621,8 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
                priv->dai_link[1].platform_of_node = asrc_np;
                priv->dai_link[2].codec_dai_name = codec_dai_name;
                priv->dai_link[2].codec_of_node = codec_np;
+               priv->dai_link[2].codec_name =
+                               priv->dai_link[0].codec_name;
                priv->dai_link[2].cpu_of_node = cpu_np;
                priv->dai_link[2].dai_fmt = priv->dai_fmt;
                priv->card.num_links = 3;
@@ -579,20 +658,22 @@ static int fsl_asoc_card_probe(struct platform_device *pdev)
 
 asrc_fail:
        of_node_put(asrc_np);
-fail:
        of_node_put(codec_np);
+fail:
        of_node_put(cpu_np);
 
        return ret;
 }
 
 static const struct of_device_id fsl_asoc_card_dt_ids[] = {
+       { .compatible = "fsl,imx-audio-ac97", },
        { .compatible = "fsl,imx-audio-cs42888", },
        { .compatible = "fsl,imx-audio-sgtl5000", },
        { .compatible = "fsl,imx-audio-wm8962", },
        { .compatible = "fsl,imx-audio-wm8960", },
        {}
 };
+MODULE_DEVICE_TABLE(of, fsl_asoc_card_dt_ids);
 
 static struct platform_driver fsl_asoc_card_driver = {
        .probe = fsl_asoc_card_probe,
index 837979ea5c922e58cfcf1044c9217cfdc2d01745..59f234e51971e2abfe7597dc7ad783a5972fbc76 100644 (file)
@@ -652,6 +652,24 @@ static const struct snd_soc_component_driver fsl_esai_component = {
        .name           = "fsl-esai",
 };
 
+static const struct reg_default fsl_esai_reg_defaults[] = {
+       {0x8,  0x00000000},
+       {0x10, 0x00000000},
+       {0x18, 0x00000000},
+       {0x98, 0x00000000},
+       {0xd0, 0x00000000},
+       {0xd4, 0x00000000},
+       {0xd8, 0x00000000},
+       {0xdc, 0x00000000},
+       {0xe0, 0x00000000},
+       {0xe4, 0x0000ffff},
+       {0xe8, 0x0000ffff},
+       {0xec, 0x0000ffff},
+       {0xf0, 0x0000ffff},
+       {0xf8, 0x00000000},
+       {0xfc, 0x00000000},
+};
+
 static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
@@ -684,6 +702,31 @@ static bool fsl_esai_readable_reg(struct device *dev, unsigned int reg)
        }
 }
 
+static bool fsl_esai_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case REG_ESAI_ETDR:
+       case REG_ESAI_ERDR:
+       case REG_ESAI_ESR:
+       case REG_ESAI_TFSR:
+       case REG_ESAI_RFSR:
+       case REG_ESAI_TX0:
+       case REG_ESAI_TX1:
+       case REG_ESAI_TX2:
+       case REG_ESAI_TX3:
+       case REG_ESAI_TX4:
+       case REG_ESAI_TX5:
+       case REG_ESAI_RX0:
+       case REG_ESAI_RX1:
+       case REG_ESAI_RX2:
+       case REG_ESAI_RX3:
+       case REG_ESAI_SAISR:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static bool fsl_esai_writeable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
@@ -721,8 +764,12 @@ static const struct regmap_config fsl_esai_regmap_config = {
        .val_bits = 32,
 
        .max_register = REG_ESAI_PCRC,
+       .reg_defaults = fsl_esai_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(fsl_esai_reg_defaults),
        .readable_reg = fsl_esai_readable_reg,
+       .volatile_reg = fsl_esai_volatile_reg,
        .writeable_reg = fsl_esai_writeable_reg,
+       .cache_type = REGCACHE_RBTREE,
 };
 
 static int fsl_esai_probe(struct platform_device *pdev)
@@ -853,10 +900,51 @@ static const struct of_device_id fsl_esai_dt_ids[] = {
 };
 MODULE_DEVICE_TABLE(of, fsl_esai_dt_ids);
 
+#ifdef CONFIG_PM_SLEEP
+static int fsl_esai_suspend(struct device *dev)
+{
+       struct fsl_esai *esai = dev_get_drvdata(dev);
+
+       regcache_cache_only(esai->regmap, true);
+       regcache_mark_dirty(esai->regmap);
+
+       return 0;
+}
+
+static int fsl_esai_resume(struct device *dev)
+{
+       struct fsl_esai *esai = dev_get_drvdata(dev);
+       int ret;
+
+       regcache_cache_only(esai->regmap, false);
+
+       /* FIFO reset for safety */
+       regmap_update_bits(esai->regmap, REG_ESAI_TFCR,
+                          ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+       regmap_update_bits(esai->regmap, REG_ESAI_RFCR,
+                          ESAI_xFCR_xFR, ESAI_xFCR_xFR);
+
+       ret = regcache_sync(esai->regmap);
+       if (ret)
+               return ret;
+
+       /* FIFO reset done */
+       regmap_update_bits(esai->regmap, REG_ESAI_TFCR, ESAI_xFCR_xFR, 0);
+       regmap_update_bits(esai->regmap, REG_ESAI_RFCR, ESAI_xFCR_xFR, 0);
+
+       return 0;
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_esai_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(fsl_esai_suspend, fsl_esai_resume)
+};
+
 static struct platform_driver fsl_esai_driver = {
        .probe = fsl_esai_probe,
        .driver = {
                .name = "fsl-esai-dai",
+               .pm = &fsl_esai_pm_ops,
                .of_match_table = fsl_esai_dt_ids,
        },
 };
index a18fd92c4a85c3d67306c544c1af6652789d1467..a4435f5e3be910447f9168b4708d19140f3c1f4f 100644 (file)
 #define FSL_SAI_FLAGS (FSL_SAI_CSR_SEIE |\
                       FSL_SAI_CSR_FEIE)
 
-static u32 fsl_sai_rates[] = {
+static const unsigned int fsl_sai_rates[] = {
        8000, 11025, 12000, 16000, 22050,
        24000, 32000, 44100, 48000, 64000,
        88200, 96000, 176400, 192000
 };
 
-static struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
+static const struct snd_pcm_hw_constraint_list fsl_sai_rate_constraints = {
        .count = ARRAY_SIZE(fsl_sai_rates),
        .list = fsl_sai_rates,
 };
@@ -637,6 +637,8 @@ static bool fsl_sai_readable_reg(struct device *dev, unsigned int reg)
 static bool fsl_sai_volatile_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
+       case FSL_SAI_TCSR:
+       case FSL_SAI_RCSR:
        case FSL_SAI_TFR:
        case FSL_SAI_RFR:
        case FSL_SAI_TDR:
@@ -681,6 +683,7 @@ static const struct regmap_config fsl_sai_regmap_config = {
        .readable_reg = fsl_sai_readable_reg,
        .volatile_reg = fsl_sai_volatile_reg,
        .writeable_reg = fsl_sai_writeable_reg,
+       .cache_type = REGCACHE_FLAT,
 };
 
 static int fsl_sai_probe(struct platform_device *pdev)
@@ -801,11 +804,42 @@ static const struct of_device_id fsl_sai_ids[] = {
        { .compatible = "fsl,imx6sx-sai", },
        { /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, fsl_sai_ids);
+
+#ifdef CONFIG_PM_SLEEP
+static int fsl_sai_suspend(struct device *dev)
+{
+       struct fsl_sai *sai = dev_get_drvdata(dev);
+
+       regcache_cache_only(sai->regmap, true);
+       regcache_mark_dirty(sai->regmap);
+
+       return 0;
+}
+
+static int fsl_sai_resume(struct device *dev)
+{
+       struct fsl_sai *sai = dev_get_drvdata(dev);
+
+       regcache_cache_only(sai->regmap, false);
+       regmap_write(sai->regmap, FSL_SAI_TCSR, FSL_SAI_CSR_SR);
+       regmap_write(sai->regmap, FSL_SAI_RCSR, FSL_SAI_CSR_SR);
+       msleep(1);
+       regmap_write(sai->regmap, FSL_SAI_TCSR, 0);
+       regmap_write(sai->regmap, FSL_SAI_RCSR, 0);
+       return regcache_sync(sai->regmap);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_sai_pm_ops = {
+       SET_SYSTEM_SLEEP_PM_OPS(fsl_sai_suspend, fsl_sai_resume)
+};
 
 static struct platform_driver fsl_sai_driver = {
        .probe = fsl_sai_probe,
        .driver = {
                .name = "fsl-sai",
+               .pm = &fsl_sai_pm_ops,
                .of_match_table = fsl_sai_ids,
        },
 };
index ab729f2426fe3ce18ca19b3adedf0e3fe946b3b3..3d59bb6719f2b6fea0d7ed7137940bac88d99d6b 100644 (file)
@@ -108,6 +108,8 @@ struct fsl_spdif_priv {
        struct clk *sysclk;
        struct snd_dmaengine_dai_dma_data dma_params_tx;
        struct snd_dmaengine_dai_dma_data dma_params_rx;
+       /* regcache for SRPC */
+       u32 regcache_srpc;
 };
 
 /* DPLL locked and lock loss interrupt handler */
@@ -300,6 +302,8 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
        struct regmap *regmap = spdif_priv->regmap;
        u32 val, cycle = 1000;
 
+       regcache_cache_bypass(regmap, true);
+
        regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET);
 
        /*
@@ -310,6 +314,10 @@ static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
                regmap_read(regmap, REG_SPDIF_SCR, &val);
        } while ((val & SCR_SOFT_RESET) && cycle--);
 
+       regcache_cache_bypass(regmap, false);
+       regcache_mark_dirty(regmap);
+       regcache_sync(regmap);
+
        if (cycle)
                return 0;
        else
@@ -997,6 +1005,14 @@ static const struct snd_soc_component_driver fsl_spdif_component = {
 };
 
 /* FSL SPDIF REGMAP */
+static const struct reg_default fsl_spdif_reg_defaults[] = {
+       {0x0,  0x00000400},
+       {0x4,  0x00000000},
+       {0xc,  0x00000000},
+       {0x34, 0x00000000},
+       {0x38, 0x00000000},
+       {0x50, 0x00020f00},
+};
 
 static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
 {
@@ -1022,6 +1038,26 @@ static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
        }
 }
 
+static bool fsl_spdif_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case REG_SPDIF_SRPC:
+       case REG_SPDIF_SIS:
+       case REG_SPDIF_SRL:
+       case REG_SPDIF_SRR:
+       case REG_SPDIF_SRCSH:
+       case REG_SPDIF_SRCSL:
+       case REG_SPDIF_SRU:
+       case REG_SPDIF_SRQ:
+       case REG_SPDIF_STL:
+       case REG_SPDIF_STR:
+       case REG_SPDIF_SRFM:
+               return true;
+       default:
+               return false;
+       }
+}
+
 static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
 {
        switch (reg) {
@@ -1047,8 +1083,12 @@ static const struct regmap_config fsl_spdif_regmap_config = {
        .val_bits = 32,
 
        .max_register = REG_SPDIF_STC,
+       .reg_defaults = fsl_spdif_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(fsl_spdif_reg_defaults),
        .readable_reg = fsl_spdif_readable_reg,
+       .volatile_reg = fsl_spdif_volatile_reg,
        .writeable_reg = fsl_spdif_writeable_reg,
+       .cache_type = REGCACHE_RBTREE,
 };
 
 static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
@@ -1271,6 +1311,38 @@ static int fsl_spdif_probe(struct platform_device *pdev)
        return ret;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int fsl_spdif_suspend(struct device *dev)
+{
+       struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
+
+       regmap_read(spdif_priv->regmap, REG_SPDIF_SRPC,
+                       &spdif_priv->regcache_srpc);
+
+       regcache_cache_only(spdif_priv->regmap, true);
+       regcache_mark_dirty(spdif_priv->regmap);
+
+       return 0;
+}
+
+static int fsl_spdif_resume(struct device *dev)
+{
+       struct fsl_spdif_priv *spdif_priv = dev_get_drvdata(dev);
+
+       regcache_cache_only(spdif_priv->regmap, false);
+
+       regmap_update_bits(spdif_priv->regmap, REG_SPDIF_SRPC,
+                       SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
+                       spdif_priv->regcache_srpc);
+
+       return regcache_sync(spdif_priv->regmap);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_spdif_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(fsl_spdif_suspend, fsl_spdif_resume)
+};
+
 static const struct of_device_id fsl_spdif_dt_ids[] = {
        { .compatible = "fsl,imx35-spdif", },
        { .compatible = "fsl,vf610-spdif", },
@@ -1282,6 +1354,7 @@ static struct platform_driver fsl_spdif_driver = {
        .driver = {
                .name = "fsl-spdif-dai",
                .of_match_table = fsl_spdif_dt_ids,
+               .pm = &fsl_spdif_pm,
        },
        .probe = fsl_spdif_probe,
 };
index 37c5cd4d0e59038ca72c8520bb350de60e42a278..95d2392303eb449bdcf5375872c9edd21aae36c7 100644 (file)
@@ -111,12 +111,75 @@ struct fsl_ssi_rxtx_reg_val {
        struct fsl_ssi_reg_val rx;
        struct fsl_ssi_reg_val tx;
 };
+
+static const struct reg_default fsl_ssi_reg_defaults[] = {
+       {0x10, 0x00000000},
+       {0x18, 0x00003003},
+       {0x1c, 0x00000200},
+       {0x20, 0x00000200},
+       {0x24, 0x00040000},
+       {0x28, 0x00040000},
+       {0x38, 0x00000000},
+       {0x48, 0x00000000},
+       {0x4c, 0x00000000},
+       {0x54, 0x00000000},
+       {0x58, 0x00000000},
+};
+
+static bool fsl_ssi_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CCSR_SSI_SACCEN:
+       case CCSR_SSI_SACCDIS:
+               return false;
+       default:
+               return true;
+       }
+}
+
+static bool fsl_ssi_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CCSR_SSI_STX0:
+       case CCSR_SSI_STX1:
+       case CCSR_SSI_SRX0:
+       case CCSR_SSI_SRX1:
+       case CCSR_SSI_SISR:
+       case CCSR_SSI_SFCSR:
+       case CCSR_SSI_SACADD:
+       case CCSR_SSI_SACDAT:
+       case CCSR_SSI_SATAG:
+       case CCSR_SSI_SACCST:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool fsl_ssi_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case CCSR_SSI_SRX0:
+       case CCSR_SSI_SRX1:
+       case CCSR_SSI_SACCST:
+               return false;
+       default:
+               return true;
+       }
+}
+
 static const struct regmap_config fsl_ssi_regconfig = {
        .max_register = CCSR_SSI_SACCDIS,
        .reg_bits = 32,
        .val_bits = 32,
        .reg_stride = 4,
        .val_format_endian = REGMAP_ENDIAN_NATIVE,
+       .reg_defaults = fsl_ssi_reg_defaults,
+       .num_reg_defaults = ARRAY_SIZE(fsl_ssi_reg_defaults),
+       .readable_reg = fsl_ssi_readable_reg,
+       .volatile_reg = fsl_ssi_volatile_reg,
+       .writeable_reg = fsl_ssi_writeable_reg,
+       .cache_type = REGCACHE_RBTREE,
 };
 
 struct fsl_ssi_soc_data {
@@ -176,6 +239,9 @@ struct fsl_ssi_private {
        unsigned int baudclk_streams;
        unsigned int bitclk_freq;
 
+       /*regcache for SFCSR*/
+       u32 regcache_sfcsr;
+
        /* DMA params */
        struct snd_dmaengine_dai_dma_data dma_params_tx;
        struct snd_dmaengine_dai_dma_data dma_params_rx;
@@ -1514,10 +1580,46 @@ static int fsl_ssi_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_PM_SLEEP
+static int fsl_ssi_suspend(struct device *dev)
+{
+       struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev);
+       struct regmap *regs = ssi_private->regs;
+
+       regmap_read(regs, CCSR_SSI_SFCSR,
+                       &ssi_private->regcache_sfcsr);
+
+       regcache_cache_only(regs, true);
+       regcache_mark_dirty(regs);
+
+       return 0;
+}
+
+static int fsl_ssi_resume(struct device *dev)
+{
+       struct fsl_ssi_private *ssi_private = dev_get_drvdata(dev);
+       struct regmap *regs = ssi_private->regs;
+
+       regcache_cache_only(regs, false);
+
+       regmap_update_bits(regs, CCSR_SSI_SFCSR,
+                       CCSR_SSI_SFCSR_RFWM1_MASK | CCSR_SSI_SFCSR_TFWM1_MASK |
+                       CCSR_SSI_SFCSR_RFWM0_MASK | CCSR_SSI_SFCSR_TFWM0_MASK,
+                       ssi_private->regcache_sfcsr);
+
+       return regcache_sync(regs);
+}
+#endif /* CONFIG_PM_SLEEP */
+
+static const struct dev_pm_ops fsl_ssi_pm = {
+       SET_SYSTEM_SLEEP_PM_OPS(fsl_ssi_suspend, fsl_ssi_resume)
+};
+
 static struct platform_driver fsl_ssi_driver = {
        .driver = {
                .name = "fsl-ssi-dai",
                .of_match_table = fsl_ssi_ids,
+               .pm = &fsl_ssi_pm,
        },
        .probe = fsl_ssi_probe,
        .remove = fsl_ssi_remove,
index 33da26a1245718f4b6e814bb22e04218ecc340fc..a407e833c612523e90c07a732e9cc472b64252ee 100644 (file)
@@ -89,6 +89,7 @@ MODULE_DEVICE_TABLE(of, imx_spdif_dt_ids);
 static struct platform_driver imx_spdif_driver = {
        .driver = {
                .name = "imx-spdif",
+               .pm = &snd_soc_pm_ops,
                .of_match_table = imx_spdif_dt_ids,
        },
        .probe = imx_spdif_audio_probe,
index 3ff76d419436ebbac7ac194e42510495a715f183..54c33204541fdb23dc78c8e2e801f3075fc1fd11 100644 (file)
@@ -151,7 +151,9 @@ static int __asoc_simple_card_dai_init(struct snd_soc_dai *dai,
        }
 
        if (set->slots) {
-               ret = snd_soc_dai_set_tdm_slot(dai, 0, 0,
+               ret = snd_soc_dai_set_tdm_slot(dai,
+                                              set->tx_slot_mask,
+                                              set->rx_slot_mask,
                                                set->slots,
                                                set->slot_width);
                if (ret && ret != -ENOTSUPP) {
@@ -243,7 +245,9 @@ asoc_simple_card_sub_parse_of(struct device_node *np,
                return ret;
 
        /* Parse TDM slot */
-       ret = snd_soc_of_parse_tdm_slot(np, &dai->slots, &dai->slot_width);
+       ret = snd_soc_of_parse_tdm_slot(np, &dai->tx_slot_mask,
+                                       &dai->rx_slot_mask,
+                                       &dai->slots, &dai->slot_width);
        if (ret)
                return ret;
 
index 05fde5e6e2578396aa4e4be20d84d3b35d3bc6ff..7b778ab85f8b41234487d0810f4c350f6dc09775 100644 (file)
@@ -12,6 +12,7 @@ config SND_MFLD_MACHINE
 
 config SND_SST_MFLD_PLATFORM
        tristate
+       select SND_SOC_COMPRESS
 
 config SND_SST_IPC
        tristate
@@ -138,4 +139,18 @@ config SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH
 config SND_SOC_INTEL_SKYLAKE
        tristate
        select SND_HDA_EXT_CORE
+       select SND_SOC_TOPOLOGY
        select SND_SOC_INTEL_SST
+
+config SND_SOC_INTEL_SKL_RT286_MACH
+       tristate "ASoC Audio driver for SKL with RT286 I2S mode"
+       depends on X86 && ACPI
+       select SND_SOC_INTEL_SST
+       select SND_SOC_INTEL_SKYLAKE
+       select SND_SOC_RT286
+       select SND_SOC_DMIC
+       help
+          This adds support for ASoC machine driver for Skylake platforms
+          with RT286 I2S audio codec.
+          Say Y if you have such a device
+          If unsure select "N".
index 683e5011615241b026d91c4056ce3cb28e97c537..0487cfaac5385a5c8201fbb80cf1b28040e18c74 100644 (file)
@@ -368,23 +368,6 @@ static void sst_media_close(struct snd_pcm_substream *substream,
        kfree(stream);
 }
 
-static inline unsigned int get_current_pipe_id(struct snd_soc_dai *dai,
-                                              struct snd_pcm_substream *substream)
-{
-       struct sst_data *sst = snd_soc_dai_get_drvdata(dai);
-       struct sst_dev_stream_map *map = sst->pdata->pdev_strm_map;
-       struct sst_runtime_stream *stream =
-                       substream->runtime->private_data;
-       u32 str_id = stream->stream_info.str_id;
-       unsigned int pipe_id;
-
-       pipe_id = map[str_id].device_id;
-
-       dev_dbg(dai->dev, "got pipe_id = %#x for str_id = %d\n",
-                       pipe_id, str_id);
-       return pipe_id;
-}
-
 static int sst_media_prepare(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
@@ -529,7 +512,7 @@ static struct snd_soc_dai_driver sst_platform_dai[] = {
 },
 {
        .name = "compress-cpu-dai",
-       .compress_dai = 1,
+       .compress_new = snd_soc_new_compress,
        .ops = &sst_compr_dai_ops,
        .playback = {
                .stream_name = "Compress Playback",
index cb94895c9edb040e0e0bcdf33c9f80c1fb4215c9..371c4565cad8ae9be0574be0f4d60bd54d3d34bf 100644 (file)
@@ -6,6 +6,7 @@ snd-soc-sst-bytcr-rt5640-objs := bytcr_rt5640.o
 snd-soc-sst-cht-bsw-rt5672-objs := cht_bsw_rt5672.o
 snd-soc-sst-cht-bsw-rt5645-objs := cht_bsw_rt5645.o
 snd-soc-sst-cht-bsw-max98090_ti-objs := cht_bsw_max98090_ti.o
+snd-soc-skl_rt286-objs := skl_rt286.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
@@ -15,3 +16,4 @@ obj-$(CONFIG_SND_SOC_INTEL_BYTCR_RT5640_MACH) += snd-soc-sst-bytcr-rt5640.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5672_MACH) += snd-soc-sst-cht-bsw-rt5672.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_RT5645_MACH) += snd-soc-sst-cht-bsw-rt5645.o
 obj-$(CONFIG_SND_SOC_INTEL_CHT_BSW_MAX98090_TI_MACH) += snd-soc-sst-cht-bsw-max98090_ti.o
+obj-$(CONFIG_SND_SOC_INTEL_SKL_RT286_MACH) += snd-soc-skl_rt286.o
index 8bafaf6ceab1266f6b85a0d6ca1e4ee04bc5046f..3f8a1e10bed02841f6b437d3d4a24615879ca35e 100644 (file)
@@ -266,18 +266,11 @@ static int broadwell_audio_probe(struct platform_device *pdev)
 {
        broadwell_rt286.dev = &pdev->dev;
 
-       return snd_soc_register_card(&broadwell_rt286);
-}
-
-static int broadwell_audio_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_card(&broadwell_rt286);
-       return 0;
+       return devm_snd_soc_register_card(&pdev->dev, &broadwell_rt286);
 }
 
 static struct platform_driver broadwell_audio = {
        .probe = broadwell_audio_probe,
-       .remove = broadwell_audio_remove,
        .driver = {
                .name = "broadwell-audio",
        },
index c4453120b11a33c60dc08422b49096f2700ce872..7a5c9a36c1db670081c70480e71e6d6b2a3ef538 100644 (file)
@@ -117,20 +117,10 @@ static int byt_codec_fixup(struct snd_soc_pcm_runtime *rtd,
        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);
+       return snd_pcm_hw_constraint_single(substream->runtime,
+                       SNDRV_PCM_HW_PARAM_RATE, 48000);
 }
 
 static struct snd_soc_ops byt_aif1_ops = {
index 49f4869cec48a273595f145cae120fa1485e88e6..4e2fcf188dd1e0be1dce9a618929235f35bcb5a0 100644 (file)
@@ -193,20 +193,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
        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);
+       return snd_pcm_hw_constraint_single(substream->runtime,
+                       SNDRV_PCM_HW_PARAM_RATE, 48000);
 }
 
 static int cht_max98090_headset_init(struct snd_soc_component *component)
index 7be8461e4d3bd45c32b921b96acc1817bcb3584f..38d65a3529c4fc1fda0464d05057e0055b6afe35 100644 (file)
@@ -235,20 +235,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
        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);
+       return snd_pcm_hw_constraint_single(substream->runtime,
+                       SNDRV_PCM_HW_PARAM_RATE, 48000);
 }
 
 static struct snd_soc_ops cht_aif1_ops = {
index 23fe0407514209d2ddfd67b7e076f099c5e73f5d..5621ccd92992d9f198bd5d0fd6149f06d0a3631a 100644 (file)
@@ -222,20 +222,10 @@ static int cht_codec_fixup(struct snd_soc_pcm_runtime *rtd,
        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);
+       return snd_pcm_hw_constraint_single(substream->runtime,
+                       SNDRV_PCM_HW_PARAM_RATE, 48000);
 }
 
 static struct snd_soc_ops cht_aif1_ops = {
diff --git a/sound/soc/intel/boards/skl_rt286.c b/sound/soc/intel/boards/skl_rt286.c
new file mode 100644 (file)
index 0000000..a73a431
--- /dev/null
@@ -0,0 +1,259 @@
+/*
+ * Intel Skylake I2S Machine Driver
+ *
+ * Copyright (C) 2014-2015, Intel Corporation. All rights reserved.
+ *
+ * Modified from:
+ *   Intel Broadwell Wildcatpoint SST Audio
+ *
+ *   Copyright (C) 2013, Intel Corporation. All rights reserved.
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/soc.h>
+#include <sound/jack.h>
+#include <sound/pcm_params.h>
+#include "../../codecs/rt286.h"
+
+static struct snd_soc_jack skylake_headset;
+/* Headset jack detection DAPM pins */
+static struct snd_soc_jack_pin skylake_headset_pins[] = {
+       {
+               .pin = "Mic Jack",
+               .mask = SND_JACK_MICROPHONE,
+       },
+       {
+               .pin = "Headphone Jack",
+               .mask = SND_JACK_HEADPHONE,
+       },
+};
+
+static const struct snd_kcontrol_new skylake_controls[] = {
+       SOC_DAPM_PIN_SWITCH("Speaker"),
+       SOC_DAPM_PIN_SWITCH("Headphone Jack"),
+       SOC_DAPM_PIN_SWITCH("Mic Jack"),
+};
+
+static const struct snd_soc_dapm_widget skylake_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_SPK("Speaker", NULL),
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
+       SND_SOC_DAPM_MIC("DMIC2", NULL),
+       SND_SOC_DAPM_MIC("SoC DMIC", NULL),
+};
+
+static const struct snd_soc_dapm_route skylake_rt286_map[] = {
+       /* speaker */
+       {"Speaker", NULL, "SPOR"},
+       {"Speaker", NULL, "SPOL"},
+
+       /* HP jack connectors - unknown if we have jack deteck */
+       {"Headphone Jack", NULL, "HPO Pin"},
+
+       /* other jacks */
+       {"MIC1", NULL, "Mic Jack"},
+
+       /* digital mics */
+       {"DMIC1 Pin", NULL, "DMIC2"},
+       {"DMIC AIF", NULL, "SoC DMIC"},
+
+       /* CODEC BE connections */
+       { "AIF1 Playback", NULL, "ssp0 Tx"},
+       { "ssp0 Tx", NULL, "codec0_out"},
+       { "ssp0 Tx", NULL, "codec1_out"},
+
+       { "codec0_in", NULL, "ssp0 Rx" },
+       { "codec1_in", NULL, "ssp0 Rx" },
+       { "ssp0 Rx", NULL, "AIF1 Capture" },
+
+       { "dmic01_hifi", NULL, "DMIC01 Rx" },
+       { "DMIC01 Rx", NULL, "Capture" },
+
+       { "hif1", NULL, "iDisp Tx"},
+       { "iDisp Tx", NULL, "iDisp_out"},
+
+};
+
+static int skylake_rt286_codec_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_codec *codec = rtd->codec;
+       int ret;
+
+       ret = snd_soc_card_jack_new(rtd->card, "Headset",
+               SND_JACK_HEADSET | SND_JACK_BTN_0,
+               &skylake_headset,
+               skylake_headset_pins, ARRAY_SIZE(skylake_headset_pins));
+
+       if (ret)
+               return ret;
+
+       rt286_mic_detect(codec, &skylake_headset);
+
+       return 0;
+}
+
+
+static int skylake_ssp0_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 output is 48KHz, stereo, 16bits */
+       rate->min = rate->max = 48000;
+       channels->min = channels->max = 2;
+       params_set_format(params, SNDRV_PCM_FORMAT_S16_LE);
+
+       return 0;
+}
+
+static int skylake_rt286_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;
+
+       ret = snd_soc_dai_set_sysclk(codec_dai, RT286_SCLK_S_PLL, 24000000,
+               SND_SOC_CLOCK_IN);
+       if (ret < 0)
+               dev_err(rtd->dev, "set codec sysclk failed: %d\n", ret);
+
+       return ret;
+}
+
+static struct snd_soc_ops skylake_rt286_ops = {
+       .hw_params = skylake_rt286_hw_params,
+};
+
+/* skylake digital audio interface glue - connects codec <--> CPU */
+static struct snd_soc_dai_link skylake_rt286_dais[] = {
+       /* Front End DAI links */
+       {
+               .name = "Skl Audio Port",
+               .stream_name = "Audio",
+               .cpu_dai_name = "System Pin",
+               .platform_name = "0000:00:1f.3",
+               .nonatomic = 1,
+               .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_playback = 1,
+       },
+       {
+               .name = "Skl Audio Capture Port",
+               .stream_name = "Audio Record",
+               .cpu_dai_name = "System Pin",
+               .platform_name = "0000:00:1f.3",
+               .nonatomic = 1,
+               .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,
+       },
+       {
+               .name = "Skl Audio Reference cap",
+               .stream_name = "refcap",
+               .cpu_dai_name = "Reference Pin",
+               .codec_name = "snd-soc-dummy",
+               .codec_dai_name = "snd-soc-dummy-dai",
+               .platform_name = "0000:00:1f.3",
+               .init = NULL,
+               .dpcm_capture = 1,
+               .ignore_suspend = 1,
+               .nonatomic = 1,
+               .dynamic = 1,
+       },
+
+       /* Back End DAI links */
+       {
+               /* SSP0 - Codec */
+               .name = "SSP0-Codec",
+               .be_id = 0,
+               .cpu_dai_name = "SSP0 Pin",
+               .platform_name = "0000:00:1f.3",
+               .no_pcm = 1,
+               .codec_name = "i2c-INT343A:00",
+               .codec_dai_name = "rt286-aif1",
+               .init = skylake_rt286_codec_init,
+               .dai_fmt = SND_SOC_DAIFMT_I2S |
+                       SND_SOC_DAIFMT_NB_NF |
+                       SND_SOC_DAIFMT_CBS_CFS,
+               .ignore_suspend = 1,
+               .ignore_pmdown_time = 1,
+               .be_hw_params_fixup = skylake_ssp0_fixup,
+               .ops = &skylake_rt286_ops,
+               .dpcm_playback = 1,
+               .dpcm_capture = 1,
+       },
+       {
+               .name = "dmic01",
+               .be_id = 1,
+               .cpu_dai_name = "DMIC01 Pin",
+               .codec_name = "dmic-codec",
+               .codec_dai_name = "dmic-hifi",
+               .platform_name = "0000:00:1f.3",
+               .ignore_suspend = 1,
+               .dpcm_capture = 1,
+               .no_pcm = 1,
+       },
+};
+
+/* skylake audio machine driver for SPT + RT286S */
+static struct snd_soc_card skylake_rt286 = {
+       .name = "skylake-rt286",
+       .owner = THIS_MODULE,
+       .dai_link = skylake_rt286_dais,
+       .num_links = ARRAY_SIZE(skylake_rt286_dais),
+       .controls = skylake_controls,
+       .num_controls = ARRAY_SIZE(skylake_controls),
+       .dapm_widgets = skylake_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(skylake_widgets),
+       .dapm_routes = skylake_rt286_map,
+       .num_dapm_routes = ARRAY_SIZE(skylake_rt286_map),
+       .fully_routed = true,
+};
+
+static int skylake_audio_probe(struct platform_device *pdev)
+{
+       skylake_rt286.dev = &pdev->dev;
+
+       return devm_snd_soc_register_card(&pdev->dev, &skylake_rt286);
+}
+
+static struct platform_driver skylake_audio = {
+       .probe = skylake_audio_probe,
+       .driver = {
+               .name = "skl_alc286s_i2s",
+       },
+};
+
+module_platform_driver(skylake_audio)
+
+/* Module information */
+MODULE_AUTHOR("Omair Mohammed Abdullah <omair.m.abdullah@intel.com>");
+MODULE_DESCRIPTION("Intel SST Audio for Skylake");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:skl_alc286s_i2s");
index f24154ca4e983e615a40f6abc3f6734ecce5039a..d9105584c51f5838a4ebbc360eb35ce91c56b1be 100644 (file)
@@ -1,7 +1,11 @@
-snd-soc-sst-dsp-objs := sst-dsp.o sst-firmware.o
+snd-soc-sst-dsp-objs := sst-dsp.o
 snd-soc-sst-acpi-objs := sst-acpi.o
 snd-soc-sst-ipc-objs := sst-ipc.o
 
+ifneq ($(CONFIG_DW_DMAC_CORE),)
+snd-soc-sst-dsp-objs += sst-firmware.o
+endif
+
 obj-$(CONFIG_SND_SOC_INTEL_SST) += snd-soc-sst-dsp.o snd-soc-sst-ipc.o
 obj-$(CONFIG_SND_SOC_INTEL_SST_ACPI) += snd-soc-sst-acpi.o
 
index cbd568eac033ebe3aa1af77a2b51ec92e3ccae50..2151652d37b72e33c556b11ea46af565747fd965 100644 (file)
@@ -314,6 +314,7 @@ struct sst_dsp {
        int sst_state;
        struct skl_cl_dev cl_dev;
        u32 intr_status;
+       const struct firmware *fw;
 };
 
 /* Size optimised DRAM/IRAM memcpy */
index a627236dd1f5b47e47e132c6feae996bdfcb9dc1..c9452e02e0dda6735238736a9e8d05267615abb0 100644 (file)
@@ -420,6 +420,7 @@ void sst_dsp_inbox_read(struct sst_dsp *sst, void *message, size_t bytes)
 }
 EXPORT_SYMBOL_GPL(sst_dsp_inbox_read);
 
+#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
 struct sst_dsp *sst_dsp_new(struct device *dev,
        struct sst_dsp_device *sst_dev, struct sst_pdata *pdata)
 {
@@ -484,6 +485,7 @@ void sst_dsp_free(struct sst_dsp *sst)
        sst_dma_free(sst->dma);
 }
 EXPORT_SYMBOL_GPL(sst_dsp_free);
+#endif
 
 /* Module information */
 MODULE_AUTHOR("Liam Girdwood");
index 1f45f18715c09ef44ccb0b31930f27ae875673b6..859f0de003391489b2da65c0195f62caa2e0b9ef 100644 (file)
@@ -216,10 +216,12 @@ struct sst_pdata {
        void *dsp;
 };
 
+#if IS_ENABLED(CONFIG_DW_DMAC_CORE)
 /* Initialization */
 struct sst_dsp *sst_dsp_new(struct device *dev,
        struct sst_dsp_device *sst_dev, struct sst_pdata *pdata);
 void sst_dsp_free(struct sst_dsp *sst);
+#endif
 
 /* SHIM Read / Write */
 void sst_dsp_shim_write(struct sst_dsp *sst, u32 offset, u32 value);
index ebcca6dc48d189eb16b8903683db5460a3955357..1636a1eeb0021efa076674e3375db682c1cbb45b 100644 (file)
@@ -26,7 +26,6 @@
 #include <linux/acpi.h>
 
 /* supported DMA engine drivers */
-#include <linux/platform_data/dma-dw.h>
 #include <linux/dma/dw.h>
 
 #include <asm/page.h>
@@ -169,12 +168,6 @@ err:
        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)
 {
@@ -195,7 +188,8 @@ static struct dw_dma_chip *dw_probe(struct device *dev, struct resource *mem,
                return ERR_PTR(err);
 
        chip->dev = dev;
-       err = dw_dma_probe(chip, &dw_pdata);
+
+       err = dw_dma_probe(chip, NULL);
        if (err)
                return ERR_PTR(err);
 
index 27db22178204b1bdb5d65502d1b905f7714fd1df..914b6dab9beae20fa0421f06d9c21f0f58d3dd64 100644 (file)
@@ -1,4 +1,5 @@
-snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o
+snd-soc-skl-objs := skl.o skl-pcm.o skl-nhlt.o skl-messages.o \
+skl-topology.o
 
 obj-$(CONFIG_SND_SOC_INTEL_SKYLAKE) += snd-soc-skl.o
 
index 826d4fd8930a5ca802f07bef5542bc26c199ffdf..50a109503a3fefbe41b1c153b8c96f975a4a6d1f 100644 (file)
@@ -54,6 +54,24 @@ static int skl_free_dma_buf(struct device *dev, struct snd_dma_buffer *dmab)
        return 0;
 }
 
+#define NOTIFICATION_PARAM_ID 3
+#define NOTIFICATION_MASK 0xf
+
+/* disable notfication for underruns/overruns from firmware module */
+static void skl_dsp_enable_notification(struct skl_sst *ctx, bool enable)
+{
+       struct notification_mask mask;
+       struct skl_ipc_large_config_msg msg = {0};
+
+       mask.notify = NOTIFICATION_MASK;
+       mask.enable = enable;
+
+       msg.large_param_id = NOTIFICATION_PARAM_ID;
+       msg.param_data_size = sizeof(mask);
+
+       skl_ipc_set_large_config(&ctx->ipc, &msg, (u32 *)&mask);
+}
+
 int skl_init_dsp(struct skl *skl)
 {
        void __iomem *mmio_base;
@@ -79,7 +97,10 @@ int skl_init_dsp(struct skl *skl)
 
        ret = skl_sst_dsp_init(bus->dev, mmio_base, irq,
                        loader_ops, &skl->skl_sst);
+       if (ret < 0)
+               return ret;
 
+       skl_dsp_enable_notification(skl->skl_sst, false);
        dev_dbg(bus->dev, "dsp registration status=%d\n", ret);
 
        return ret;
@@ -122,6 +143,7 @@ int skl_suspend_dsp(struct skl *skl)
 int skl_resume_dsp(struct skl *skl)
 {
        struct skl_sst *ctx = skl->skl_sst;
+       int ret;
 
        /* if ppcap is not supported return 0 */
        if (!skl->ebus.ppcap)
@@ -131,7 +153,12 @@ int skl_resume_dsp(struct skl *skl)
        snd_hdac_ext_bus_ppcap_enable(&skl->ebus, true);
        snd_hdac_ext_bus_ppcap_int_enable(&skl->ebus, true);
 
-       return skl_dsp_wake(ctx->dsp);
+       ret = skl_dsp_wake(ctx->dsp);
+       if (ret < 0)
+               return ret;
+
+       skl_dsp_enable_notification(skl->skl_sst, false);
+       return ret;
 }
 
 enum skl_bitdepth skl_get_bit_depth(int params)
@@ -294,6 +321,7 @@ static void skl_copy_copier_caps(struct skl_module_cfg *mconfig,
                        (mconfig->formats_config.caps_size) / 4;
 }
 
+#define SKL_NON_GATEWAY_CPR_NODE_ID 0xFFFFFFFF
 /*
  * Calculate the gatewat settings required for copier module, type of
  * gateway and index of gateway to use
@@ -303,6 +331,7 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
                        struct skl_cpr_cfg *cpr_mconfig)
 {
        union skl_connector_node_id node_id = {0};
+       union skl_ssp_dma_node ssp_node  = {0};
        struct skl_pipe_params *params = mconfig->pipe->p_params;
 
        switch (mconfig->dev_type) {
@@ -320,9 +349,9 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
                        (SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
                        SKL_DMA_I2S_LINK_OUTPUT_CLASS :
                        SKL_DMA_I2S_LINK_INPUT_CLASS;
-               node_id.node.vindex = params->host_dma_id +
-                                        (mconfig->time_slot << 1) +
-                                        (mconfig->vbus_id << 3);
+               ssp_node.dma_node.time_slot_index = mconfig->time_slot;
+               ssp_node.dma_node.i2s_instance = mconfig->vbus_id;
+               node_id.node.vindex = ssp_node.val;
                break;
 
        case SKL_DEVICE_DMIC:
@@ -339,13 +368,18 @@ static void skl_setup_cpr_gateway_cfg(struct skl_sst *ctx,
                node_id.node.vindex = params->link_dma_id;
                break;
 
-       default:
+       case SKL_DEVICE_HDAHOST:
                node_id.node.dma_type =
                        (SKL_CONN_SOURCE == mconfig->hw_conn_type) ?
                        SKL_DMA_HDA_HOST_OUTPUT_CLASS :
                        SKL_DMA_HDA_HOST_INPUT_CLASS;
                node_id.node.vindex = params->host_dma_id;
                break;
+
+       default:
+               cpr_mconfig->gtw_cfg.node_id = SKL_NON_GATEWAY_CPR_NODE_ID;
+               cpr_mconfig->cpr_feature_mask = 0;
+               return;
        }
 
        cpr_mconfig->gtw_cfg.node_id = node_id.val;
index 13036b19d7e55f81aa77d511be5fc5f8ca87ebdf..b0c7bd113aacfd59db229320f5227985010bb8f4 100644 (file)
@@ -25,7 +25,7 @@ static u8 OSC_UUID[16] = {0x6E, 0x88, 0x9F, 0xA6, 0xEB, 0x6C, 0x94, 0x45,
 
 #define DSDT_NHLT_PATH "\\_SB.PCI0.HDAS"
 
-void __iomem *skl_nhlt_init(struct device *dev)
+void *skl_nhlt_init(struct device *dev)
 {
        acpi_handle handle;
        union acpi_object *obj;
@@ -40,17 +40,17 @@ void __iomem *skl_nhlt_init(struct device *dev)
        if (obj && obj->type == ACPI_TYPE_BUFFER) {
                nhlt_ptr = (struct nhlt_resource_desc  *)obj->buffer.pointer;
 
-               return ioremap_cache(nhlt_ptr->min_addr, nhlt_ptr->length);
+               return memremap(nhlt_ptr->min_addr, nhlt_ptr->length,
+                               MEMREMAP_WB);
        }
 
        dev_err(dev, "device specific method to extract NHLT blob failed\n");
        return NULL;
 }
 
-void skl_nhlt_free(void __iomem *addr)
+void skl_nhlt_free(void *addr)
 {
-       iounmap(addr);
-       addr = NULL;
+       memunmap(addr);
 }
 
 static struct nhlt_specific_cfg *skl_get_specific_cfg(
index 7d617bf493bc7a1295560d034d76ba161e2eb431..a2f94ce1679d0db2c23b3ccb0d0339afb46473cf 100644 (file)
@@ -24,6 +24,7 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include "skl.h"
+#include "skl-topology.h"
 
 #define HDA_MONO 1
 #define HDA_STEREO 2
@@ -115,7 +116,7 @@ static int skl_pcm_open(struct snd_pcm_substream *substream,
 
        dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
        ret = pm_runtime_get_sync(dai->dev);
-       if (ret)
+       if (ret < 0)
                return ret;
 
        stream = snd_hdac_ext_stream_assign(ebus, substream,
@@ -214,6 +215,8 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
        struct hdac_ext_bus *ebus = dev_get_drvdata(dai->dev);
        struct hdac_ext_stream *stream = get_hdac_ext_stream(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
+       struct skl_pipe_params p_params = {0};
+       struct skl_module_cfg *m_cfg;
        int ret, dma_id;
 
        dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
@@ -228,6 +231,16 @@ static int skl_pcm_hw_params(struct snd_pcm_substream *substream,
        dma_id = hdac_stream(stream)->stream_tag - 1;
        dev_dbg(dai->dev, "dma_id=%d\n", dma_id);
 
+       p_params.s_fmt = snd_pcm_format_width(params_format(params));
+       p_params.ch = params_channels(params);
+       p_params.s_freq = params_rate(params);
+       p_params.host_dma_id = dma_id;
+       p_params.stream = substream->stream;
+
+       m_cfg = skl_tplg_fe_get_cpr_module(dai, p_params.stream);
+       if (m_cfg)
+               skl_tplg_update_pipe_params(dai->dev, m_cfg, &p_params);
+
        return 0;
 }
 
@@ -268,6 +281,46 @@ static int skl_pcm_hw_free(struct snd_pcm_substream *substream,
        return skl_substream_free_pages(ebus_to_hbus(ebus), substream);
 }
 
+static int skl_be_hw_params(struct snd_pcm_substream *substream,
+                               struct snd_pcm_hw_params *params,
+                               struct snd_soc_dai *dai)
+{
+       struct skl_pipe_params p_params = {0};
+
+       p_params.s_fmt = snd_pcm_format_width(params_format(params));
+       p_params.ch = params_channels(params);
+       p_params.s_freq = params_rate(params);
+       p_params.stream = substream->stream;
+       skl_tplg_be_update_params(dai, &p_params);
+
+       return 0;
+}
+
+static int skl_pcm_trigger(struct snd_pcm_substream *substream, int cmd,
+               struct snd_soc_dai *dai)
+{
+       struct skl *skl = get_skl_ctx(dai->dev);
+       struct skl_sst *ctx = skl->skl_sst;
+       struct skl_module_cfg *mconfig;
+
+       mconfig = skl_tplg_fe_get_cpr_module(dai, substream->stream);
+       if (!mconfig)
+               return -EIO;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+       case SNDRV_PCM_TRIGGER_RESUME:
+               return skl_run_pipe(ctx, mconfig->pipe);
+
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+               return skl_stop_pipe(ctx, mconfig->pipe);
+
+       default:
+               return 0;
+       }
+}
+
 static int skl_link_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_soc_dai *dai)
@@ -277,9 +330,8 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
        struct skl_dma_params *dma_params;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       int dma_id;
+       struct skl_pipe_params p_params = {0};
 
-       pr_debug("%s\n", __func__);
        link_dev = snd_hdac_ext_stream_assign(ebus, substream,
                                        HDAC_EXT_STREAM_TYPE_LINK);
        if (!link_dev)
@@ -293,7 +345,14 @@ static int skl_link_hw_params(struct snd_pcm_substream *substream,
        if (dma_params)
                dma_params->stream_tag =  hdac_stream(link_dev)->stream_tag;
        snd_soc_dai_set_dma_data(codec_dai, substream, (void *)dma_params);
-       dma_id = hdac_stream(link_dev)->stream_tag - 1;
+
+       p_params.s_fmt = snd_pcm_format_width(params_format(params));
+       p_params.ch = params_channels(params);
+       p_params.s_freq = params_rate(params);
+       p_params.stream = substream->stream;
+       p_params.link_dma_id = hdac_stream(link_dev)->stream_tag - 1;
+
+       skl_tplg_be_update_params(dai, &p_params);
 
        return 0;
 }
@@ -308,27 +367,12 @@ static int skl_link_pcm_prepare(struct snd_pcm_substream *substream,
        unsigned int format_val = 0;
        struct skl_dma_params *dma_params;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
-       struct snd_pcm_hw_params *params;
-       struct snd_interval *channels, *rate;
        struct hdac_ext_link *link;
 
-       dev_dbg(dai->dev, "%s: %s\n", __func__, dai->name);
        if (link_dev->link_prepared) {
                dev_dbg(dai->dev, "already stream is prepared - returning\n");
                return 0;
        }
-       params  = devm_kzalloc(dai->dev, sizeof(*params), GFP_KERNEL);
-       if (params == NULL)
-               return -ENOMEM;
-
-       channels = hw_param_interval(params, SNDRV_PCM_HW_PARAM_CHANNELS);
-       channels->min = channels->max = substream->runtime->channels;
-       rate = hw_param_interval(params, SNDRV_PCM_HW_PARAM_RATE);
-       rate->min = rate->max = substream->runtime->rate;
-       snd_mask_set(&params->masks[SNDRV_PCM_HW_PARAM_FORMAT -
-                                       SNDRV_PCM_HW_PARAM_FIRST_MASK],
-                                       substream->runtime->format);
-
 
        dma_params  = (struct skl_dma_params *)
                        snd_soc_dai_get_dma_data(codec_dai, substream);
@@ -399,13 +443,13 @@ static int skl_link_hw_free(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int skl_hda_be_startup(struct snd_pcm_substream *substream,
+static int skl_be_startup(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
        return pm_runtime_get_sync(dai->dev);
 }
 
-static void skl_hda_be_shutdown(struct snd_pcm_substream *substream,
+static void skl_be_shutdown(struct snd_pcm_substream *substream,
                struct snd_soc_dai *dai)
 {
        pm_runtime_mark_last_busy(dai->dev);
@@ -418,20 +462,28 @@ static struct snd_soc_dai_ops skl_pcm_dai_ops = {
        .prepare = skl_pcm_prepare,
        .hw_params = skl_pcm_hw_params,
        .hw_free = skl_pcm_hw_free,
+       .trigger = skl_pcm_trigger,
 };
 
 static struct snd_soc_dai_ops skl_dmic_dai_ops = {
-       .startup = skl_hda_be_startup,
-       .shutdown = skl_hda_be_shutdown,
+       .startup = skl_be_startup,
+       .hw_params = skl_be_hw_params,
+       .shutdown = skl_be_shutdown,
+};
+
+static struct snd_soc_dai_ops skl_be_ssp_dai_ops = {
+       .startup = skl_be_startup,
+       .hw_params = skl_be_hw_params,
+       .shutdown = skl_be_shutdown,
 };
 
 static struct snd_soc_dai_ops skl_link_dai_ops = {
-       .startup = skl_hda_be_startup,
+       .startup = skl_be_startup,
        .prepare = skl_link_pcm_prepare,
        .hw_params = skl_link_hw_params,
        .hw_free = skl_link_hw_free,
        .trigger = skl_link_pcm_trigger,
-       .shutdown = skl_hda_be_shutdown,
+       .shutdown = skl_be_shutdown,
 };
 
 static struct snd_soc_dai_driver skl_platform_dai[] = {
@@ -487,6 +539,24 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
        },
 },
 /* BE CPU  Dais */
+{
+       .name = "SSP0 Pin",
+       .ops = &skl_be_ssp_dai_ops,
+       .playback = {
+               .stream_name = "ssp0 Tx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "ssp0 Rx",
+               .channels_min = HDA_STEREO,
+               .channels_max = HDA_STEREO,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+},
 {
        .name = "iDisp Pin",
        .ops = &skl_link_dai_ops,
@@ -509,17 +579,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
                .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
        },
 },
-{
-       .name = "DMIC23 Pin",
-       .ops = &skl_dmic_dai_ops,
-       .capture = {
-               .stream_name = "DMIC23 Rx",
-               .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
-               .rates = SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_16000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S24_LE,
-       },
-},
 {
        .name = "HD-Codec Pin",
        .ops = &skl_link_dai_ops,
@@ -538,28 +597,6 @@ static struct snd_soc_dai_driver skl_platform_dai[] = {
                .formats = SNDRV_PCM_FMTBIT_S16_LE,
        },
 },
-{
-       .name = "HD-Codec-SPK Pin",
-       .ops = &skl_link_dai_ops,
-       .playback = {
-               .stream_name = "HD-Codec-SPK Tx",
-               .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,
-       },
-},
-{
-       .name = "HD-Codec-AMIC Pin",
-       .ops = &skl_link_dai_ops,
-       .capture = {
-               .stream_name = "HD-Codec-AMIC Rx",
-               .channels_min = HDA_STEREO,
-               .channels_max = HDA_STEREO,
-               .rates = SNDRV_PCM_RATE_48000,
-               .formats = SNDRV_PCM_FMTBIT_S16_LE,
-       },
-},
 };
 
 static int skl_platform_open(struct snd_pcm_substream *substream)
@@ -577,7 +614,7 @@ static int skl_platform_open(struct snd_pcm_substream *substream)
        return 0;
 }
 
-static int skl_pcm_trigger(struct snd_pcm_substream *substream,
+static int skl_coupled_trigger(struct snd_pcm_substream *substream,
                                        int cmd)
 {
        struct hdac_ext_bus *ebus = get_bus_ctx(substream);
@@ -651,7 +688,7 @@ static int skl_pcm_trigger(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int skl_dsp_trigger(struct snd_pcm_substream *substream,
+static int skl_decoupled_trigger(struct snd_pcm_substream *substream,
                int cmd)
 {
        struct hdac_ext_bus *ebus = get_bus_ctx(substream);
@@ -708,9 +745,9 @@ static int skl_platform_pcm_trigger(struct snd_pcm_substream *substream,
        struct hdac_ext_bus *ebus = get_bus_ctx(substream);
 
        if (ebus->ppcap)
-               return skl_dsp_trigger(substream, cmd);
+               return skl_decoupled_trigger(substream, cmd);
        else
-               return skl_pcm_trigger(substream, cmd);
+               return skl_coupled_trigger(substream, cmd);
 }
 
 /* calculate runtime delay from LPIB */
@@ -877,7 +914,17 @@ static int skl_pcm_new(struct snd_soc_pcm_runtime *rtd)
        return retval;
 }
 
+static int skl_platform_soc_probe(struct snd_soc_platform *platform)
+{
+       struct hdac_ext_bus *ebus = dev_get_drvdata(platform->dev);
+
+       if (ebus->ppcap)
+               return skl_tplg_init(platform, ebus);
+
+       return 0;
+}
 static struct snd_soc_platform_driver skl_platform_drv  = {
+       .probe          = skl_platform_soc_probe,
        .ops            = &skl_platform_ops,
        .pcm_new        = skl_pcm_new,
        .pcm_free       = skl_pcm_free,
@@ -890,6 +937,11 @@ static const struct snd_soc_component_driver skl_component = {
 int skl_platform_register(struct device *dev)
 {
        int ret;
+       struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+       struct skl *skl = ebus_to_skl(ebus);
+
+       INIT_LIST_HEAD(&skl->ppl_list);
+       INIT_LIST_HEAD(&skl->dapm_path_list);
 
        ret = snd_soc_register_platform(dev, &skl_platform_drv);
        if (ret) {
index 94875b008b0b83a221789f281852d195305faf9c..1bfb7f63b5724d98e8acf1446364fafec01e115f 100644 (file)
@@ -175,7 +175,7 @@ static int skl_dsp_core_power_down(struct sst_dsp  *ctx)
        /* poll with timeout to check if operation successful */
        return sst_dsp_register_poll(ctx,
                        SKL_ADSP_REG_ADSPCS,
-                       SKL_ADSPCS_SPA_MASK,
+                       SKL_ADSPCS_CPA_MASK,
                        0,
                        SKL_DSP_PD_TO,
                        "Power down");
@@ -262,6 +262,11 @@ irqreturn_t skl_dsp_sst_interrupt(int irq, void *dev_id)
        val = sst_dsp_shim_read_unlocked(ctx, SKL_ADSP_REG_ADSPIS);
        ctx->intr_status = val;
 
+       if (val == 0xffffffff) {
+               spin_unlock(&ctx->spinlock);
+               return IRQ_NONE;
+       }
+
        if (val & SKL_ADSPIS_IPC) {
                skl_ipc_int_disable(ctx);
                result = IRQ_WAKE_THREAD;
index 937a0a3a63a0f0241cb19c89f79a2410665d898a..3345ea0d44147e9fdb62275f73d1d0d6d64e0c5f 100644 (file)
@@ -464,6 +464,18 @@ void skl_ipc_op_int_enable(struct sst_dsp *ctx)
                SKL_ADSP_REG_HIPCCTL_BUSY, SKL_ADSP_REG_HIPCCTL_BUSY);
 }
 
+void skl_ipc_op_int_disable(struct sst_dsp *ctx)
+{
+       /* disable IPC DONE interrupt */
+       sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL,
+                                       SKL_ADSP_REG_HIPCCTL_DONE, 0);
+
+       /* Disable IPC BUSY interrupt */
+       sst_dsp_shim_update_bits_unlocked(ctx, SKL_ADSP_REG_HIPCCTL,
+                                       SKL_ADSP_REG_HIPCCTL_BUSY, 0);
+
+}
+
 bool skl_ipc_int_status(struct sst_dsp *ctx)
 {
        return sst_dsp_shim_read_unlocked(ctx,
index 9f5f67202858fdbbcfe665c9a9275bfae1f073e7..f1a154e45dc343209b7a1adb8010f62313785bf8 100644 (file)
@@ -116,6 +116,7 @@ int skl_ipc_set_large_config(struct sst_generic_ipc *ipc,
 
 void skl_ipc_int_enable(struct sst_dsp *dsp);
 void skl_ipc_op_int_enable(struct sst_dsp *ctx);
+void skl_ipc_op_int_disable(struct sst_dsp *ctx);
 void skl_ipc_int_disable(struct sst_dsp *dsp);
 
 bool skl_ipc_int_status(struct sst_dsp *dsp);
index c18ea51b7484d13e0ed79d3d56f774d6c55b653f..3b83dc99f1d405ffd5ea65e1376cae6920a99e63 100644 (file)
@@ -70,15 +70,31 @@ static int skl_transfer_firmware(struct sst_dsp *ctx,
 static int skl_load_base_firmware(struct sst_dsp *ctx)
 {
        int ret = 0, i;
-       const struct firmware *fw = NULL;
        struct skl_sst *skl = ctx->thread_context;
        u32 reg;
 
-       ret = request_firmware(&fw, "dsp_fw_release.bin", ctx->dev);
+       skl->boot_complete = false;
+       init_waitqueue_head(&skl->boot_wait);
+
+       if (ctx->fw == NULL) {
+               ret = request_firmware(&ctx->fw, "dsp_fw_release.bin", ctx->dev);
+               if (ret < 0) {
+                       dev_err(ctx->dev, "Request firmware failed %d\n", ret);
+                       skl_dsp_disable_core(ctx);
+                       return -EIO;
+               }
+       }
+
+       ret = skl_dsp_boot(ctx);
        if (ret < 0) {
-               dev_err(ctx->dev, "Request firmware failed %d\n", ret);
-               skl_dsp_disable_core(ctx);
-               return -EIO;
+               dev_err(ctx->dev, "Boot dsp core failed ret: %d", ret);
+               goto skl_load_base_firmware_failed;
+       }
+
+       ret = skl_cldma_prepare(ctx);
+       if (ret < 0) {
+               dev_err(ctx->dev, "CL dma prepare failed : %d", ret);
+               goto skl_load_base_firmware_failed;
        }
 
        /* enable Interrupt */
@@ -102,7 +118,7 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
                goto skl_load_base_firmware_failed;
        }
 
-       ret = skl_transfer_firmware(ctx, fw->data, fw->size);
+       ret = skl_transfer_firmware(ctx, ctx->fw->data, ctx->fw->size);
        if (ret < 0) {
                dev_err(ctx->dev, "Transfer firmware failed%d\n", ret);
                goto skl_load_base_firmware_failed;
@@ -118,13 +134,12 @@ static int skl_load_base_firmware(struct sst_dsp *ctx)
                dev_dbg(ctx->dev, "Download firmware successful%d\n", ret);
                skl_dsp_set_state_locked(ctx, SKL_DSP_RUNNING);
        }
-       release_firmware(fw);
-
        return 0;
 
 skl_load_base_firmware_failed:
        skl_dsp_disable_core(ctx);
-       release_firmware(fw);
+       release_firmware(ctx->fw);
+       ctx->fw = NULL;
        return ret;
 }
 
@@ -172,6 +187,12 @@ static int skl_set_dsp_D3(struct sst_dsp *ctx)
        }
        skl_dsp_set_state_locked(ctx, SKL_DSP_RESET);
 
+       /* disable Interrupt */
+       ctx->cl_dev.ops.cl_cleanup_controller(ctx);
+       skl_cldma_int_disable(ctx);
+       skl_ipc_op_int_disable(ctx);
+       skl_ipc_int_disable(ctx);
+
        return ret;
 }
 
@@ -235,22 +256,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
        if (ret)
                return ret;
 
-       skl->boot_complete = false;
-       init_waitqueue_head(&skl->boot_wait);
-
-       ret = skl_dsp_boot(sst);
-       if (ret < 0) {
-               dev_err(skl->dev, "Boot dsp core failed ret: %d", ret);
-               goto free_ipc;
-       }
-
-       ret = skl_cldma_prepare(sst);
-       if (ret < 0) {
-               dev_err(dev, "CL dma prepare failed : %d", ret);
-               goto free_ipc;
-       }
-
-
        ret = sst->fw_ops.load_fw(sst);
        if (ret < 0) {
                dev_err(dev, "Load base fw failed : %d", ret);
@@ -262,7 +267,6 @@ int skl_sst_dsp_init(struct device *dev, void __iomem *mmio_base, int irq,
 
        return 0;
 
-free_ipc:
        skl_ipc_free(&skl->ipc);
        return ret;
 }
diff --git a/sound/soc/intel/skylake/skl-topology.c b/sound/soc/intel/skylake/skl-topology.c
new file mode 100644 (file)
index 0000000..a7854c8
--- /dev/null
@@ -0,0 +1,1252 @@
+/*
+ *  skl-topology.c - Implements Platform component ALSA controls/widget
+ *  handlers.
+ *
+ *  Copyright (C) 2014-2015 Intel Corp
+ *  Author: Jeeja KP <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 version 2, as
+ * published by the Free Software Foundation.
+ *
+ * This program is distributed in the hope that it will be useful, but
+ * WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the GNU
+ * General Public License for more details.
+ */
+
+#include <linux/slab.h>
+#include <linux/types.h>
+#include <linux/firmware.h>
+#include <sound/soc.h>
+#include <sound/soc-topology.h>
+#include "skl-sst-dsp.h"
+#include "skl-sst-ipc.h"
+#include "skl-topology.h"
+#include "skl.h"
+#include "skl-tplg-interface.h"
+
+#define SKL_CH_FIXUP_MASK              (1 << 0)
+#define SKL_RATE_FIXUP_MASK            (1 << 1)
+#define SKL_FMT_FIXUP_MASK             (1 << 2)
+
+/*
+ * SKL DSP driver modelling uses only few DAPM widgets so for rest we will
+ * ignore. This helpers checks if the SKL driver handles this widget type
+ */
+static int is_skl_dsp_widget_type(struct snd_soc_dapm_widget *w)
+{
+       switch (w->id) {
+       case snd_soc_dapm_dai_link:
+       case snd_soc_dapm_dai_in:
+       case snd_soc_dapm_aif_in:
+       case snd_soc_dapm_aif_out:
+       case snd_soc_dapm_dai_out:
+       case snd_soc_dapm_switch:
+               return false;
+       default:
+               return true;
+       }
+}
+
+/*
+ * Each pipelines needs memory to be allocated. Check if we have free memory
+ * from available pool. Then only add this to pool
+ * This is freed when pipe is deleted
+ * Note: DSP does actual memory management we only keep track for complete
+ * pool
+ */
+static bool skl_tplg_alloc_pipe_mem(struct skl *skl,
+                               struct skl_module_cfg *mconfig)
+{
+       struct skl_sst *ctx = skl->skl_sst;
+
+       if (skl->resource.mem + mconfig->pipe->memory_pages >
+                               skl->resource.max_mem) {
+               dev_err(ctx->dev,
+                               "%s: module_id %d instance %d\n", __func__,
+                               mconfig->id.module_id,
+                               mconfig->id.instance_id);
+               dev_err(ctx->dev,
+                               "exceeds ppl memory available %d mem %d\n",
+                               skl->resource.max_mem, skl->resource.mem);
+               return false;
+       }
+
+       skl->resource.mem += mconfig->pipe->memory_pages;
+       return true;
+}
+
+/*
+ * Pipeline needs needs DSP CPU resources for computation, this is
+ * quantified in MCPS (Million Clocks Per Second) required for module/pipe
+ *
+ * Each pipelines needs mcps to be allocated. Check if we have mcps for this
+ * pipe. This adds the mcps to driver counter
+ * This is removed on pipeline delete
+ */
+static bool skl_tplg_alloc_pipe_mcps(struct skl *skl,
+                               struct skl_module_cfg *mconfig)
+{
+       struct skl_sst *ctx = skl->skl_sst;
+
+       if (skl->resource.mcps + mconfig->mcps > skl->resource.max_mcps) {
+               dev_err(ctx->dev,
+                       "%s: module_id %d instance %d\n", __func__,
+                       mconfig->id.module_id, mconfig->id.instance_id);
+               dev_err(ctx->dev,
+                       "exceeds ppl memory available %d > mem %d\n",
+                       skl->resource.max_mcps, skl->resource.mcps);
+               return false;
+       }
+
+       skl->resource.mcps += mconfig->mcps;
+       return true;
+}
+
+/*
+ * Free the mcps when tearing down
+ */
+static void
+skl_tplg_free_pipe_mcps(struct skl *skl, struct skl_module_cfg *mconfig)
+{
+       skl->resource.mcps -= mconfig->mcps;
+}
+
+/*
+ * Free the memory when tearing down
+ */
+static void
+skl_tplg_free_pipe_mem(struct skl *skl, struct skl_module_cfg *mconfig)
+{
+       skl->resource.mem -= mconfig->pipe->memory_pages;
+}
+
+
+static void skl_dump_mconfig(struct skl_sst *ctx,
+                                       struct skl_module_cfg *mcfg)
+{
+       dev_dbg(ctx->dev, "Dumping config\n");
+       dev_dbg(ctx->dev, "Input Format:\n");
+       dev_dbg(ctx->dev, "channels = %d\n", mcfg->in_fmt.channels);
+       dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->in_fmt.s_freq);
+       dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->in_fmt.ch_cfg);
+       dev_dbg(ctx->dev, "valid bit depth = %d\n",
+                       mcfg->in_fmt.valid_bit_depth);
+       dev_dbg(ctx->dev, "Output Format:\n");
+       dev_dbg(ctx->dev, "channels = %d\n", mcfg->out_fmt.channels);
+       dev_dbg(ctx->dev, "s_freq = %d\n", mcfg->out_fmt.s_freq);
+       dev_dbg(ctx->dev, "valid bit depth = %d\n",
+                       mcfg->out_fmt.valid_bit_depth);
+       dev_dbg(ctx->dev, "ch_cfg = %d\n", mcfg->out_fmt.ch_cfg);
+}
+
+static void skl_tplg_update_params(struct skl_module_fmt *fmt,
+                       struct skl_pipe_params *params, int fixup)
+{
+       if (fixup & SKL_RATE_FIXUP_MASK)
+               fmt->s_freq = params->s_freq;
+       if (fixup & SKL_CH_FIXUP_MASK)
+               fmt->channels = params->ch;
+       if (fixup & SKL_FMT_FIXUP_MASK)
+               fmt->valid_bit_depth = params->s_fmt;
+}
+
+/*
+ * A pipeline may have modules which impact the pcm parameters, like SRC,
+ * channel converter, format converter.
+ * We need to calculate the output params by applying the 'fixup'
+ * Topology will tell driver which type of fixup is to be applied by
+ * supplying the fixup mask, so based on that we calculate the output
+ *
+ * Now In FE the pcm hw_params is source/target format. Same is applicable
+ * for BE with its hw_params invoked.
+ * here based on FE, BE pipeline and direction we calculate the input and
+ * outfix and then apply that for a module
+ */
+static void skl_tplg_update_params_fixup(struct skl_module_cfg *m_cfg,
+               struct skl_pipe_params *params, bool is_fe)
+{
+       int in_fixup, out_fixup;
+       struct skl_module_fmt *in_fmt, *out_fmt;
+
+       in_fmt = &m_cfg->in_fmt;
+       out_fmt = &m_cfg->out_fmt;
+
+       if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (is_fe) {
+                       in_fixup = m_cfg->params_fixup;
+                       out_fixup = (~m_cfg->converter) &
+                                       m_cfg->params_fixup;
+               } else {
+                       out_fixup = m_cfg->params_fixup;
+                       in_fixup = (~m_cfg->converter) &
+                                       m_cfg->params_fixup;
+               }
+       } else {
+               if (is_fe) {
+                       out_fixup = m_cfg->params_fixup;
+                       in_fixup = (~m_cfg->converter) &
+                                       m_cfg->params_fixup;
+               } else {
+                       in_fixup = m_cfg->params_fixup;
+                       out_fixup = (~m_cfg->converter) &
+                                       m_cfg->params_fixup;
+               }
+       }
+
+       skl_tplg_update_params(in_fmt, params, in_fixup);
+       skl_tplg_update_params(out_fmt, params, out_fixup);
+}
+
+/*
+ * A module needs input and output buffers, which are dependent upon pcm
+ * params, so once we have calculate params, we need buffer calculation as
+ * well.
+ */
+static void skl_tplg_update_buffer_size(struct skl_sst *ctx,
+                               struct skl_module_cfg *mcfg)
+{
+       int multiplier = 1;
+
+       if (mcfg->m_type == SKL_MODULE_TYPE_SRCINT)
+               multiplier = 5;
+
+       mcfg->ibs = (mcfg->in_fmt.s_freq / 1000) *
+                               (mcfg->in_fmt.channels) *
+                               (mcfg->in_fmt.bit_depth >> 3) *
+                               multiplier;
+
+       mcfg->obs = (mcfg->out_fmt.s_freq / 1000) *
+                               (mcfg->out_fmt.channels) *
+                               (mcfg->out_fmt.bit_depth >> 3) *
+                               multiplier;
+}
+
+static void skl_tplg_update_module_params(struct snd_soc_dapm_widget *w,
+                                                       struct skl_sst *ctx)
+{
+       struct skl_module_cfg *m_cfg = w->priv;
+       struct skl_pipe_params *params = m_cfg->pipe->p_params;
+       int p_conn_type = m_cfg->pipe->conn_type;
+       bool is_fe;
+
+       if (!m_cfg->params_fixup)
+               return;
+
+       dev_dbg(ctx->dev, "Mconfig for widget=%s BEFORE updation\n",
+                               w->name);
+
+       skl_dump_mconfig(ctx, m_cfg);
+
+       if (p_conn_type == SKL_PIPE_CONN_TYPE_FE)
+               is_fe = true;
+       else
+               is_fe = false;
+
+       skl_tplg_update_params_fixup(m_cfg, params, is_fe);
+       skl_tplg_update_buffer_size(ctx, m_cfg);
+
+       dev_dbg(ctx->dev, "Mconfig for widget=%s AFTER updation\n",
+                               w->name);
+
+       skl_dump_mconfig(ctx, m_cfg);
+}
+
+/*
+ * A pipe can have multiple modules, each of them will be a DAPM widget as
+ * well. While managing a pipeline we need to get the list of all the
+ * widgets in a pipelines, so this helper - skl_tplg_get_pipe_widget() helps
+ * to get the SKL type widgets in that pipeline
+ */
+static int skl_tplg_alloc_pipe_widget(struct device *dev,
+       struct snd_soc_dapm_widget *w, struct skl_pipe *pipe)
+{
+       struct skl_module_cfg *src_module = NULL;
+       struct snd_soc_dapm_path *p = NULL;
+       struct skl_pipe_module *p_module = NULL;
+
+       p_module = devm_kzalloc(dev, sizeof(*p_module), GFP_KERNEL);
+       if (!p_module)
+               return -ENOMEM;
+
+       p_module->w = w;
+       list_add_tail(&p_module->node, &pipe->w_list);
+
+       snd_soc_dapm_widget_for_each_sink_path(w, p) {
+               if ((p->sink->priv == NULL)
+                               && (!is_skl_dsp_widget_type(w)))
+                       continue;
+
+               if ((p->sink->priv != NULL) && p->connect
+                               && is_skl_dsp_widget_type(p->sink)) {
+
+                       src_module = p->sink->priv;
+                       if (pipe->ppl_id == src_module->pipe->ppl_id)
+                               skl_tplg_alloc_pipe_widget(dev,
+                                                       p->sink, pipe);
+               }
+       }
+       return 0;
+}
+
+/*
+ * Inside a pipe instance, we can have various modules. These modules need
+ * to instantiated in DSP by invoking INIT_MODULE IPC, which is achieved by
+ * skl_init_module() routine, so invoke that for all modules in a pipeline
+ */
+static int
+skl_tplg_init_pipe_modules(struct skl *skl, struct skl_pipe *pipe)
+{
+       struct skl_pipe_module *w_module;
+       struct snd_soc_dapm_widget *w;
+       struct skl_module_cfg *mconfig;
+       struct skl_sst *ctx = skl->skl_sst;
+       int ret = 0;
+
+       list_for_each_entry(w_module, &pipe->w_list, node) {
+               w = w_module->w;
+               mconfig = w->priv;
+
+               /* check resource available */
+               if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+                       return -ENOMEM;
+
+               /*
+                * apply fix/conversion to module params based on
+                * FE/BE params
+                */
+               skl_tplg_update_module_params(w, ctx);
+               ret = skl_init_module(ctx, mconfig, NULL);
+               if (ret < 0)
+                       return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Mixer module represents a pipeline. So in the Pre-PMU event of mixer we
+ * need create the pipeline. So we do following:
+ *   - check the resources
+ *   - Create the pipeline
+ *   - Initialize the modules in pipeline
+ *   - finally bind all modules together
+ */
+static int skl_tplg_mixer_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
+                                                       struct skl *skl)
+{
+       int ret;
+       struct skl_module_cfg *mconfig = w->priv;
+       struct skl_pipe_module *w_module;
+       struct skl_pipe *s_pipe = mconfig->pipe;
+       struct skl_module_cfg *src_module = NULL, *dst_module;
+       struct skl_sst *ctx = skl->skl_sst;
+
+       /* check resource available */
+       if (!skl_tplg_alloc_pipe_mcps(skl, mconfig))
+               return -EBUSY;
+
+       if (!skl_tplg_alloc_pipe_mem(skl, mconfig))
+               return -ENOMEM;
+
+       /*
+        * Create a list of modules for pipe.
+        * This list contains modules from source to sink
+        */
+       ret = skl_create_pipeline(ctx, mconfig->pipe);
+       if (ret < 0)
+               return ret;
+
+       /*
+        * we create a w_list of all widgets in that pipe. This list is not
+        * freed on PMD event as widgets within a pipe are static. This
+        * saves us cycles to get widgets in pipe every time.
+        *
+        * So if we have already initialized all the widgets of a pipeline
+        * we skip, so check for list_empty and create the list if empty
+        */
+       if (list_empty(&s_pipe->w_list)) {
+               ret = skl_tplg_alloc_pipe_widget(ctx->dev, w, s_pipe);
+               if (ret < 0)
+                       return ret;
+       }
+
+       /* Init all pipe modules from source to sink */
+       ret = skl_tplg_init_pipe_modules(skl, s_pipe);
+       if (ret < 0)
+               return ret;
+
+       /* Bind modules from source to sink */
+       list_for_each_entry(w_module, &s_pipe->w_list, node) {
+               dst_module = w_module->w->priv;
+
+               if (src_module == NULL) {
+                       src_module = dst_module;
+                       continue;
+               }
+
+               ret = skl_bind_modules(ctx, src_module, dst_module);
+               if (ret < 0)
+                       return ret;
+
+               src_module = dst_module;
+       }
+
+       return 0;
+}
+
+/*
+ * A PGA represents a module in a pipeline. So in the Pre-PMU event of PGA
+ * we need to do following:
+ *   - Bind to sink pipeline
+ *      Since the sink pipes can be running and we don't get mixer event on
+ *      connect for already running mixer, we need to find the sink pipes
+ *      here and bind to them. This way dynamic connect works.
+ *   - Start sink pipeline, if not running
+ *   - Then run current pipe
+ */
+static int skl_tplg_pga_dapm_pre_pmu_event(struct snd_soc_dapm_widget *w,
+                                                       struct skl *skl)
+{
+       struct snd_soc_dapm_path *p;
+       struct skl_dapm_path_list *path_list;
+       struct snd_soc_dapm_widget *source, *sink;
+       struct skl_module_cfg *src_mconfig, *sink_mconfig;
+       struct skl_sst *ctx = skl->skl_sst;
+       int ret = 0;
+
+       source = w;
+       src_mconfig = source->priv;
+
+       /*
+        * find which sink it is connected to, bind with the sink,
+        * if sink is not started, start sink pipe first, then start
+        * this pipe
+        */
+       snd_soc_dapm_widget_for_each_source_path(w, p) {
+               if (!p->connect)
+                       continue;
+
+               dev_dbg(ctx->dev, "%s: src widget=%s\n", __func__, w->name);
+               dev_dbg(ctx->dev, "%s: sink widget=%s\n", __func__, p->sink->name);
+
+               /*
+                * here we will check widgets in sink pipelines, so that
+                * can be any widgets type and we are only interested if
+                * they are ones used for SKL so check that first
+                */
+               if ((p->sink->priv != NULL) &&
+                                       is_skl_dsp_widget_type(p->sink)) {
+
+                       sink = p->sink;
+                       src_mconfig = source->priv;
+                       sink_mconfig = sink->priv;
+
+                       /* Bind source to sink, mixin is always source */
+                       ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
+                       if (ret)
+                               return ret;
+
+                       /* Start sinks pipe first */
+                       if (sink_mconfig->pipe->state != SKL_PIPE_STARTED) {
+                               ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+                               if (ret)
+                                       return ret;
+                       }
+
+                       path_list = kzalloc(
+                                       sizeof(struct skl_dapm_path_list),
+                                       GFP_KERNEL);
+                       if (path_list == NULL)
+                               return -ENOMEM;
+
+                       /* Add connected path to one global list */
+                       path_list->dapm_path = p;
+                       list_add_tail(&path_list->node, &skl->dapm_path_list);
+                       break;
+               }
+       }
+
+       /* Start source pipe last after starting all sinks */
+       ret = skl_run_pipe(ctx, src_mconfig->pipe);
+       if (ret)
+               return ret;
+
+       return 0;
+}
+
+/*
+ * in the Post-PMU event of mixer we need to do following:
+ *   - Check if this pipe is running
+ *   - if not, then
+ *     - bind this pipeline to its source pipeline
+ *       if source pipe is already running, this means it is a dynamic
+ *       connection and we need to bind only to that pipe
+ *     - start this pipeline
+ */
+static int skl_tplg_mixer_dapm_post_pmu_event(struct snd_soc_dapm_widget *w,
+                                                       struct skl *skl)
+{
+       int ret = 0;
+       struct snd_soc_dapm_path *p;
+       struct snd_soc_dapm_widget *source, *sink;
+       struct skl_module_cfg *src_mconfig, *sink_mconfig;
+       struct skl_sst *ctx = skl->skl_sst;
+       int src_pipe_started = 0;
+
+       sink = w;
+       sink_mconfig = sink->priv;
+
+       /*
+        * If source pipe is already started, that means source is driving
+        * one more sink before this sink got connected, Since source is
+        * started, bind this sink to source and start this pipe.
+        */
+       snd_soc_dapm_widget_for_each_sink_path(w, p) {
+               if (!p->connect)
+                       continue;
+
+               dev_dbg(ctx->dev, "sink widget=%s\n", w->name);
+               dev_dbg(ctx->dev, "src widget=%s\n", p->source->name);
+
+               /*
+                * here we will check widgets in sink pipelines, so that
+                * can be any widgets type and we are only interested if
+                * they are ones used for SKL so check that first
+                */
+               if ((p->source->priv != NULL) &&
+                                       is_skl_dsp_widget_type(p->source)) {
+                       source = p->source;
+                       src_mconfig = source->priv;
+                       sink_mconfig = sink->priv;
+                       src_pipe_started = 1;
+
+                       /*
+                        * check pipe state, then no need to bind or start
+                        * the pipe
+                        */
+                       if (src_mconfig->pipe->state != SKL_PIPE_STARTED)
+                               src_pipe_started = 0;
+               }
+       }
+
+       if (src_pipe_started) {
+               ret = skl_bind_modules(ctx, src_mconfig, sink_mconfig);
+               if (ret)
+                       return ret;
+
+               ret = skl_run_pipe(ctx, sink_mconfig->pipe);
+       }
+
+       return ret;
+}
+
+/*
+ * in the Pre-PMD event of mixer we need to do following:
+ *   - Stop the pipe
+ *   - find the source connections and remove that from dapm_path_list
+ *   - unbind with source pipelines if still connected
+ */
+static int skl_tplg_mixer_dapm_pre_pmd_event(struct snd_soc_dapm_widget *w,
+                                                       struct skl *skl)
+{
+       struct snd_soc_dapm_widget *source, *sink;
+       struct skl_module_cfg *src_mconfig, *sink_mconfig;
+       int ret = 0, path_found = 0;
+       struct skl_dapm_path_list *path_list, *tmp_list;
+       struct skl_sst *ctx = skl->skl_sst;
+
+       sink = w;
+       sink_mconfig = sink->priv;
+
+       /* Stop the pipe */
+       ret = skl_stop_pipe(ctx, sink_mconfig->pipe);
+       if (ret)
+               return ret;
+
+       /*
+        * This list, dapm_path_list handling here does not need any locks
+        * as we are under dapm lock while handling widget events.
+        * List can be manipulated safely only under dapm widgets handler
+        * routines
+        */
+       list_for_each_entry_safe(path_list, tmp_list,
+                               &skl->dapm_path_list, node) {
+               if (path_list->dapm_path->sink == sink) {
+                       dev_dbg(ctx->dev, "Path found = %s\n",
+                                       path_list->dapm_path->name);
+                       source = path_list->dapm_path->source;
+                       src_mconfig = source->priv;
+                       path_found = 1;
+
+                       list_del(&path_list->node);
+                       kfree(path_list);
+                       break;
+               }
+       }
+
+       /*
+        * If path_found == 1, that means pmd for source pipe has
+        * not occurred, source is connected to some other sink.
+        * so its responsibility of sink to unbind itself from source.
+        */
+       if (path_found) {
+               ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+               if (ret < 0)
+                       return ret;
+
+               ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
+       }
+
+       return ret;
+}
+
+/*
+ * in the Post-PMD event of mixer we need to do following:
+ *   - Free the mcps used
+ *   - Free the mem used
+ *   - Unbind the modules within the pipeline
+ *   - Delete the pipeline (modules are not required to be explicitly
+ *     deleted, pipeline delete is enough here
+ */
+static int skl_tplg_mixer_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
+                                                       struct skl *skl)
+{
+       struct skl_module_cfg *mconfig = w->priv;
+       struct skl_pipe_module *w_module;
+       struct skl_module_cfg *src_module = NULL, *dst_module;
+       struct skl_sst *ctx = skl->skl_sst;
+       struct skl_pipe *s_pipe = mconfig->pipe;
+       int ret = 0;
+
+       skl_tplg_free_pipe_mcps(skl, mconfig);
+
+       list_for_each_entry(w_module, &s_pipe->w_list, node) {
+               dst_module = w_module->w->priv;
+
+               if (src_module == NULL) {
+                       src_module = dst_module;
+                       continue;
+               }
+
+               ret = skl_unbind_modules(ctx, src_module, dst_module);
+               if (ret < 0)
+                       return ret;
+
+               src_module = dst_module;
+       }
+
+       ret = skl_delete_pipe(ctx, mconfig->pipe);
+       skl_tplg_free_pipe_mem(skl, mconfig);
+
+       return ret;
+}
+
+/*
+ * in the Post-PMD event of PGA we need to do following:
+ *   - Free the mcps used
+ *   - Stop the pipeline
+ *   - In source pipe is connected, unbind with source pipelines
+ */
+static int skl_tplg_pga_dapm_post_pmd_event(struct snd_soc_dapm_widget *w,
+                                                               struct skl *skl)
+{
+       struct snd_soc_dapm_widget *source, *sink;
+       struct skl_module_cfg *src_mconfig, *sink_mconfig;
+       int ret = 0, path_found = 0;
+       struct skl_dapm_path_list *path_list, *tmp_path_list;
+       struct skl_sst *ctx = skl->skl_sst;
+
+       source = w;
+       src_mconfig = source->priv;
+
+       skl_tplg_free_pipe_mcps(skl, src_mconfig);
+       /* Stop the pipe since this is a mixin module */
+       ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+       if (ret)
+               return ret;
+
+       list_for_each_entry_safe(path_list, tmp_path_list, &skl->dapm_path_list, node) {
+               if (path_list->dapm_path->source == source) {
+                       dev_dbg(ctx->dev, "Path found = %s\n",
+                                       path_list->dapm_path->name);
+                       sink = path_list->dapm_path->sink;
+                       sink_mconfig = sink->priv;
+                       path_found = 1;
+
+                       list_del(&path_list->node);
+                       kfree(path_list);
+                       break;
+               }
+       }
+
+       /*
+        * This is a connector and if path is found that means
+        * unbind between source and sink has not happened yet
+        */
+       if (path_found) {
+               ret = skl_stop_pipe(ctx, src_mconfig->pipe);
+               if (ret < 0)
+                       return ret;
+
+               ret = skl_unbind_modules(ctx, src_mconfig, sink_mconfig);
+       }
+
+       return ret;
+}
+
+/*
+ * In modelling, we assume there will be ONLY one mixer in a pipeline.  If
+ * mixer is not required then it is treated as static mixer aka vmixer with
+ * a hard path to source module
+ * So we don't need to check if source is started or not as hard path puts
+ * dependency on each other
+ */
+static int skl_tplg_vmixer_event(struct snd_soc_dapm_widget *w,
+                               struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct skl *skl = get_skl_ctx(dapm->dev);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
+
+       case SND_SOC_DAPM_POST_PMD:
+               return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
+       }
+
+       return 0;
+}
+
+/*
+ * In modelling, we assume there will be ONLY one mixer in a pipeline. If a
+ * second one is required that is created as another pipe entity.
+ * The mixer is responsible for pipe management and represent a pipeline
+ * instance
+ */
+static int skl_tplg_mixer_event(struct snd_soc_dapm_widget *w,
+                               struct snd_kcontrol *k, int event)
+{
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct skl *skl = get_skl_ctx(dapm->dev);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               return skl_tplg_mixer_dapm_pre_pmu_event(w, skl);
+
+       case SND_SOC_DAPM_POST_PMU:
+               return skl_tplg_mixer_dapm_post_pmu_event(w, skl);
+
+       case SND_SOC_DAPM_PRE_PMD:
+               return skl_tplg_mixer_dapm_pre_pmd_event(w, skl);
+
+       case SND_SOC_DAPM_POST_PMD:
+               return skl_tplg_mixer_dapm_post_pmd_event(w, skl);
+       }
+
+       return 0;
+}
+
+/*
+ * In modelling, we assumed rest of the modules in pipeline are PGA. But we
+ * are interested in last PGA (leaf PGA) in a pipeline to disconnect with
+ * the sink when it is running (two FE to one BE or one FE to two BE)
+ * scenarios
+ */
+static int skl_tplg_pga_event(struct snd_soc_dapm_widget *w,
+                       struct snd_kcontrol *k, int event)
+
+{
+       struct snd_soc_dapm_context *dapm = w->dapm;
+       struct skl *skl = get_skl_ctx(dapm->dev);
+
+       switch (event) {
+       case SND_SOC_DAPM_PRE_PMU:
+               return skl_tplg_pga_dapm_pre_pmu_event(w, skl);
+
+       case SND_SOC_DAPM_POST_PMD:
+               return skl_tplg_pga_dapm_post_pmd_event(w, skl);
+       }
+
+       return 0;
+}
+
+/*
+ * The FE params are passed by hw_params of the DAI.
+ * On hw_params, the params are stored in Gateway module of the FE and we
+ * need to calculate the format in DSP module configuration, that
+ * conversion is done here
+ */
+int skl_tplg_update_pipe_params(struct device *dev,
+                       struct skl_module_cfg *mconfig,
+                       struct skl_pipe_params *params)
+{
+       struct skl_pipe *pipe = mconfig->pipe;
+       struct skl_module_fmt *format = NULL;
+
+       memcpy(pipe->p_params, params, sizeof(*params));
+
+       if (params->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               format = &mconfig->in_fmt;
+       else
+               format = &mconfig->out_fmt;
+
+       /* set the hw_params */
+       format->s_freq = params->s_freq;
+       format->channels = params->ch;
+       format->valid_bit_depth = skl_get_bit_depth(params->s_fmt);
+
+       /*
+        * 16 bit is 16 bit container whereas 24 bit is in 32 bit
+        * container so update bit depth accordingly
+        */
+       switch (format->valid_bit_depth) {
+       case SKL_DEPTH_16BIT:
+               format->bit_depth = format->valid_bit_depth;
+               break;
+
+       case SKL_DEPTH_24BIT:
+               format->bit_depth = SKL_DEPTH_32BIT;
+               break;
+
+       default:
+               dev_err(dev, "Invalid bit depth %x for pipe\n",
+                               format->valid_bit_depth);
+               return -EINVAL;
+       }
+
+       if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               mconfig->ibs = (format->s_freq / 1000) *
+                               (format->channels) *
+                               (format->bit_depth >> 3);
+       } else {
+               mconfig->obs = (format->s_freq / 1000) *
+                               (format->channels) *
+                               (format->bit_depth >> 3);
+       }
+
+       return 0;
+}
+
+/*
+ * Query the module config for the FE DAI
+ * This is used to find the hw_params set for that DAI and apply to FE
+ * pipeline
+ */
+struct skl_module_cfg *
+skl_tplg_fe_get_cpr_module(struct snd_soc_dai *dai, int stream)
+{
+       struct snd_soc_dapm_widget *w;
+       struct snd_soc_dapm_path *p = NULL;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               w = dai->playback_widget;
+               snd_soc_dapm_widget_for_each_sink_path(w, p) {
+                       if (p->connect && p->sink->power &&
+                                       is_skl_dsp_widget_type(p->sink))
+                               continue;
+
+                       if (p->sink->priv) {
+                               dev_dbg(dai->dev, "set params for %s\n",
+                                               p->sink->name);
+                               return p->sink->priv;
+                       }
+               }
+       } else {
+               w = dai->capture_widget;
+               snd_soc_dapm_widget_for_each_source_path(w, p) {
+                       if (p->connect && p->source->power &&
+                                       is_skl_dsp_widget_type(p->source))
+                               continue;
+
+                       if (p->source->priv) {
+                               dev_dbg(dai->dev, "set params for %s\n",
+                                               p->source->name);
+                               return p->source->priv;
+                       }
+               }
+       }
+
+       return NULL;
+}
+
+static u8 skl_tplg_be_link_type(int dev_type)
+{
+       int ret;
+
+       switch (dev_type) {
+       case SKL_DEVICE_BT:
+               ret = NHLT_LINK_SSP;
+               break;
+
+       case SKL_DEVICE_DMIC:
+               ret = NHLT_LINK_DMIC;
+               break;
+
+       case SKL_DEVICE_I2S:
+               ret = NHLT_LINK_SSP;
+               break;
+
+       case SKL_DEVICE_HDALINK:
+               ret = NHLT_LINK_HDA;
+               break;
+
+       default:
+               ret = NHLT_LINK_INVALID;
+               break;
+       }
+
+       return ret;
+}
+
+/*
+ * Fill the BE gateway parameters
+ * The BE gateway expects a blob of parameters which are kept in the ACPI
+ * NHLT blob, so query the blob for interface type (i2s/pdm) and instance.
+ * The port can have multiple settings so pick based on the PCM
+ * parameters
+ */
+static int skl_tplg_be_fill_pipe_params(struct snd_soc_dai *dai,
+                               struct skl_module_cfg *mconfig,
+                               struct skl_pipe_params *params)
+{
+       struct skl_pipe *pipe = mconfig->pipe;
+       struct nhlt_specific_cfg *cfg;
+       struct skl *skl = get_skl_ctx(dai->dev);
+       int link_type = skl_tplg_be_link_type(mconfig->dev_type);
+
+       memcpy(pipe->p_params, params, sizeof(*params));
+
+       /* update the blob based on virtual bus_id*/
+       cfg = skl_get_ep_blob(skl, mconfig->vbus_id, link_type,
+                                       params->s_fmt, params->ch,
+                                       params->s_freq, params->stream);
+       if (cfg) {
+               mconfig->formats_config.caps_size = cfg->size;
+               mconfig->formats_config.caps = (u32 *) &cfg->caps;
+       } else {
+               dev_err(dai->dev, "Blob NULL for id %x type %d dirn %d\n",
+                                       mconfig->vbus_id, link_type,
+                                       params->stream);
+               dev_err(dai->dev, "PCM: ch %d, freq %d, fmt %d\n",
+                                params->ch, params->s_freq, params->s_fmt);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int skl_tplg_be_set_src_pipe_params(struct snd_soc_dai *dai,
+                               struct snd_soc_dapm_widget *w,
+                               struct skl_pipe_params *params)
+{
+       struct snd_soc_dapm_path *p;
+       int ret = -EIO;
+
+       snd_soc_dapm_widget_for_each_source_path(w, p) {
+               if (p->connect && is_skl_dsp_widget_type(p->source) &&
+                                               p->source->priv) {
+
+                       if (!p->source->power) {
+                               ret = skl_tplg_be_fill_pipe_params(
+                                               dai, p->source->priv,
+                                               params);
+                               if (ret < 0)
+                                       return ret;
+                       } else {
+                               return -EBUSY;
+                       }
+               } else {
+                       ret = skl_tplg_be_set_src_pipe_params(
+                                               dai, p->source, params);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       return ret;
+}
+
+static int skl_tplg_be_set_sink_pipe_params(struct snd_soc_dai *dai,
+       struct snd_soc_dapm_widget *w, struct skl_pipe_params *params)
+{
+       struct snd_soc_dapm_path *p = NULL;
+       int ret = -EIO;
+
+       snd_soc_dapm_widget_for_each_sink_path(w, p) {
+               if (p->connect && is_skl_dsp_widget_type(p->sink) &&
+                                               p->sink->priv) {
+
+                       if (!p->sink->power) {
+                               ret = skl_tplg_be_fill_pipe_params(
+                                               dai, p->sink->priv, params);
+                               if (ret < 0)
+                                       return ret;
+                       } else {
+                               return -EBUSY;
+                       }
+
+               } else {
+                       ret = skl_tplg_be_set_sink_pipe_params(
+                                               dai, p->sink, params);
+                       if (ret < 0)
+                               return ret;
+               }
+       }
+
+       return ret;
+}
+
+/*
+ * BE hw_params can be a source parameters (capture) or sink parameters
+ * (playback). Based on sink and source we need to either find the source
+ * list or the sink list and set the pipeline parameters
+ */
+int skl_tplg_be_update_params(struct snd_soc_dai *dai,
+                               struct skl_pipe_params *params)
+{
+       struct snd_soc_dapm_widget *w;
+
+       if (params->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               w = dai->playback_widget;
+
+               return skl_tplg_be_set_src_pipe_params(dai, w, params);
+
+       } else {
+               w = dai->capture_widget;
+
+               return skl_tplg_be_set_sink_pipe_params(dai, w, params);
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_tplg_widget_events skl_tplg_widget_ops[] = {
+       {SKL_MIXER_EVENT, skl_tplg_mixer_event},
+       {SKL_VMIXER_EVENT, skl_tplg_vmixer_event},
+       {SKL_PGA_EVENT, skl_tplg_pga_event},
+};
+
+/*
+ * The topology binary passes the pin info for a module so initialize the pin
+ * info passed into module instance
+ */
+static void skl_fill_module_pin_info(struct skl_dfw_module_pin *dfw_pin,
+                                               struct skl_module_pin *m_pin,
+                                               bool is_dynamic, int max_pin)
+{
+       int i;
+
+       for (i = 0; i < max_pin; i++) {
+               m_pin[i].id.module_id = dfw_pin[i].module_id;
+               m_pin[i].id.instance_id = dfw_pin[i].instance_id;
+               m_pin[i].in_use = false;
+               m_pin[i].is_dynamic = is_dynamic;
+       }
+}
+
+/*
+ * Add pipeline from topology binary into driver pipeline list
+ *
+ * If already added we return that instance
+ * Otherwise we create a new instance and add into driver list
+ */
+static struct skl_pipe *skl_tplg_add_pipe(struct device *dev,
+                       struct skl *skl, struct skl_dfw_pipe *dfw_pipe)
+{
+       struct skl_pipeline *ppl;
+       struct skl_pipe *pipe;
+       struct skl_pipe_params *params;
+
+       list_for_each_entry(ppl, &skl->ppl_list, node) {
+               if (ppl->pipe->ppl_id == dfw_pipe->pipe_id)
+                       return ppl->pipe;
+       }
+
+       ppl = devm_kzalloc(dev, sizeof(*ppl), GFP_KERNEL);
+       if (!ppl)
+               return NULL;
+
+       pipe = devm_kzalloc(dev, sizeof(*pipe), GFP_KERNEL);
+       if (!pipe)
+               return NULL;
+
+       params = devm_kzalloc(dev, sizeof(*params), GFP_KERNEL);
+       if (!params)
+               return NULL;
+
+       pipe->ppl_id = dfw_pipe->pipe_id;
+       pipe->memory_pages = dfw_pipe->memory_pages;
+       pipe->pipe_priority = dfw_pipe->pipe_priority;
+       pipe->conn_type = dfw_pipe->conn_type;
+       pipe->state = SKL_PIPE_INVALID;
+       pipe->p_params = params;
+       INIT_LIST_HEAD(&pipe->w_list);
+
+       ppl->pipe = pipe;
+       list_add(&ppl->node, &skl->ppl_list);
+
+       return ppl->pipe;
+}
+
+/*
+ * Topology core widget load callback
+ *
+ * This is used to save the private data for each widget which gives
+ * information to the driver about module and pipeline parameters which DSP
+ * FW expects like ids, resource values, formats etc
+ */
+static int skl_tplg_widget_load(struct snd_soc_component *cmpnt,
+                               struct snd_soc_dapm_widget *w,
+                               struct snd_soc_tplg_dapm_widget *tplg_w)
+{
+       int ret;
+       struct hdac_ext_bus *ebus = snd_soc_component_get_drvdata(cmpnt);
+       struct skl *skl = ebus_to_skl(ebus);
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+       struct skl_module_cfg *mconfig;
+       struct skl_pipe *pipe;
+       struct skl_dfw_module *dfw_config =
+                               (struct skl_dfw_module *)tplg_w->priv.data;
+
+       if (!tplg_w->priv.size)
+               goto bind_event;
+
+       mconfig = devm_kzalloc(bus->dev, sizeof(*mconfig), GFP_KERNEL);
+
+       if (!mconfig)
+               return -ENOMEM;
+
+       w->priv = mconfig;
+       mconfig->id.module_id = dfw_config->module_id;
+       mconfig->id.instance_id = dfw_config->instance_id;
+       mconfig->mcps = dfw_config->max_mcps;
+       mconfig->ibs = dfw_config->ibs;
+       mconfig->obs = dfw_config->obs;
+       mconfig->core_id = dfw_config->core_id;
+       mconfig->max_in_queue = dfw_config->max_in_queue;
+       mconfig->max_out_queue = dfw_config->max_out_queue;
+       mconfig->is_loadable = dfw_config->is_loadable;
+       mconfig->in_fmt.channels = dfw_config->in_fmt.channels;
+       mconfig->in_fmt.s_freq = dfw_config->in_fmt.freq;
+       mconfig->in_fmt.bit_depth = dfw_config->in_fmt.bit_depth;
+       mconfig->in_fmt.valid_bit_depth =
+                               dfw_config->in_fmt.valid_bit_depth;
+       mconfig->in_fmt.ch_cfg = dfw_config->in_fmt.ch_cfg;
+       mconfig->out_fmt.channels = dfw_config->out_fmt.channels;
+       mconfig->out_fmt.s_freq = dfw_config->out_fmt.freq;
+       mconfig->out_fmt.bit_depth = dfw_config->out_fmt.bit_depth;
+       mconfig->out_fmt.valid_bit_depth =
+                               dfw_config->out_fmt.valid_bit_depth;
+       mconfig->out_fmt.ch_cfg = dfw_config->out_fmt.ch_cfg;
+       mconfig->params_fixup = dfw_config->params_fixup;
+       mconfig->converter = dfw_config->converter;
+       mconfig->m_type = dfw_config->module_type;
+       mconfig->vbus_id = dfw_config->vbus_id;
+
+       pipe = skl_tplg_add_pipe(bus->dev, skl, &dfw_config->pipe);
+       if (pipe)
+               mconfig->pipe = pipe;
+
+       mconfig->dev_type = dfw_config->dev_type;
+       mconfig->hw_conn_type = dfw_config->hw_conn_type;
+       mconfig->time_slot = dfw_config->time_slot;
+       mconfig->formats_config.caps_size = dfw_config->caps.caps_size;
+
+       mconfig->m_in_pin = devm_kzalloc(bus->dev,
+                               (mconfig->max_in_queue) *
+                                       sizeof(*mconfig->m_in_pin),
+                               GFP_KERNEL);
+       if (!mconfig->m_in_pin)
+               return -ENOMEM;
+
+       mconfig->m_out_pin = devm_kzalloc(bus->dev, (mconfig->max_out_queue) *
+                                               sizeof(*mconfig->m_out_pin),
+                                               GFP_KERNEL);
+       if (!mconfig->m_out_pin)
+               return -ENOMEM;
+
+       skl_fill_module_pin_info(dfw_config->in_pin, mconfig->m_in_pin,
+                                               dfw_config->is_dynamic_in_pin,
+                                               mconfig->max_in_queue);
+
+       skl_fill_module_pin_info(dfw_config->out_pin, mconfig->m_out_pin,
+                                                dfw_config->is_dynamic_out_pin,
+                                                       mconfig->max_out_queue);
+
+
+       if (mconfig->formats_config.caps_size == 0)
+               goto bind_event;
+
+       mconfig->formats_config.caps = (u32 *)devm_kzalloc(bus->dev,
+                       mconfig->formats_config.caps_size, GFP_KERNEL);
+
+       if (mconfig->formats_config.caps == NULL)
+               return -ENOMEM;
+
+       memcpy(mconfig->formats_config.caps, dfw_config->caps.caps,
+                                        dfw_config->caps.caps_size);
+
+bind_event:
+       if (tplg_w->event_type == 0) {
+               dev_dbg(bus->dev, "ASoC: No event handler required\n");
+               return 0;
+       }
+
+       ret = snd_soc_tplg_widget_bind_event(w, skl_tplg_widget_ops,
+                                       ARRAY_SIZE(skl_tplg_widget_ops),
+                                       tplg_w->event_type);
+
+       if (ret) {
+               dev_err(bus->dev, "%s: No matching event handlers found for %d\n",
+                                       __func__, tplg_w->event_type);
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_tplg_ops skl_tplg_ops  = {
+       .widget_load = skl_tplg_widget_load,
+};
+
+/* This will be read from topology manifest, currently defined here */
+#define SKL_MAX_MCPS 30000000
+#define SKL_FW_MAX_MEM 1000000
+
+/*
+ * SKL topology init routine
+ */
+int skl_tplg_init(struct snd_soc_platform *platform, struct hdac_ext_bus *ebus)
+{
+       int ret;
+       const struct firmware *fw;
+       struct hdac_bus *bus = ebus_to_hbus(ebus);
+       struct skl *skl = ebus_to_skl(ebus);
+
+       ret = request_firmware(&fw, "dfw_sst.bin", bus->dev);
+       if (ret < 0) {
+               dev_err(bus->dev, "tplg fw %s load failed with %d\n",
+                               "dfw_sst.bin", ret);
+               return ret;
+       }
+
+       /*
+        * The complete tplg for SKL is loaded as index 0, we don't use
+        * any other index
+        */
+       ret = snd_soc_tplg_component_load(&platform->component,
+                                       &skl_tplg_ops, fw, 0);
+       if (ret < 0) {
+               dev_err(bus->dev, "tplg component load failed%d\n", ret);
+               return -EINVAL;
+       }
+
+       skl->resource.max_mcps = SKL_MAX_MCPS;
+       skl->resource.max_mem = SKL_FW_MAX_MEM;
+
+       return 0;
+}
index 8c7767baa94fbe44239f425b48dbd6c6ecf643a8..76053a8de41cf255bfe77d24673c320cf5d2fd0a 100644 (file)
@@ -129,6 +129,11 @@ struct skl_src_module_cfg {
        enum skl_s_freq src_cfg;
 } __packed;
 
+struct notification_mask {
+       u32 notify;
+       u32 enable;
+} __packed;
+
 struct skl_up_down_mixer_cfg {
        struct skl_base_cfg base_cfg;
        enum skl_ch_cfg out_ch_cfg;
@@ -153,8 +158,7 @@ enum skl_dma_type {
 union skl_ssp_dma_node {
        u8 val;
        struct {
-               u8 dual_mono:1;
-               u8 time_slot:3;
+               u8 time_slot_index:4;
                u8 i2s_instance:4;
        } dma_node;
 };
@@ -263,6 +267,34 @@ struct skl_module_cfg {
        struct skl_specific_cfg formats_config;
 };
 
+struct skl_pipeline {
+       struct skl_pipe *pipe;
+       struct list_head node;
+};
+
+struct skl_dapm_path_list {
+       struct snd_soc_dapm_path *dapm_path;
+       struct list_head node;
+};
+
+static inline struct skl *get_skl_ctx(struct device *dev)
+{
+       struct hdac_ext_bus *ebus = dev_get_drvdata(dev);
+
+       return ebus_to_skl(ebus);
+}
+
+int skl_tplg_be_update_params(struct snd_soc_dai *dai,
+       struct skl_pipe_params *params);
+void skl_tplg_set_be_dmic_config(struct snd_soc_dai *dai,
+       struct skl_pipe_params *params, int stream);
+int skl_tplg_init(struct snd_soc_platform *platform,
+                               struct hdac_ext_bus *ebus);
+struct skl_module_cfg *skl_tplg_fe_get_cpr_module(
+               struct snd_soc_dai *dai, int stream);
+int skl_tplg_update_pipe_params(struct device *dev,
+               struct skl_module_cfg *mconfig, struct skl_pipe_params *params);
+
 int skl_create_pipeline(struct skl_sst *ctx, struct skl_pipe *pipe);
 
 int skl_run_pipe(struct skl_sst *ctx, struct skl_pipe *pipe);
index a50689825bcace5836d41f931155cf0fdacdc454..2bc396d54cbed52fa257c1c499749eaed8ebc111 100644 (file)
 #ifndef __HDA_TPLG_INTERFACE_H__
 #define __HDA_TPLG_INTERFACE_H__
 
+/*
+ * Default types range from 0~12. type can range from 0 to 0xff
+ * SST types start at higher to avoid any overlapping in future
+ */
+#define SOC_CONTROL_TYPE_HDA_SST_ALGO_PARAMS   0x100
+#define SOC_CONTROL_TYPE_HDA_SST_MUX           0x101
+#define SOC_CONTROL_TYPE_HDA_SST_MIX           0x101
+#define SOC_CONTROL_TYPE_HDA_SST_BYTE          0x103
+
+#define HDA_SST_CFG_MAX        900 /* size of copier cfg*/
+#define MAX_IN_QUEUE 8
+#define MAX_OUT_QUEUE 8
+
+/* Event types goes here */
+/* Reserve event type 0 for no event handlers */
+enum skl_event_types {
+       SKL_EVENT_NONE = 0,
+       SKL_MIXER_EVENT,
+       SKL_MUX_EVENT,
+       SKL_VMIXER_EVENT,
+       SKL_PGA_EVENT
+};
+
 /**
  * enum skl_ch_cfg - channel configuration
  *
@@ -83,6 +106,66 @@ enum skl_dev_type {
        SKL_DEVICE_I2S = 0x2,
        SKL_DEVICE_SLIMBUS = 0x3,
        SKL_DEVICE_HDALINK = 0x4,
+       SKL_DEVICE_HDAHOST = 0x5,
        SKL_DEVICE_NONE
 };
+
+struct skl_dfw_module_pin {
+       u16 module_id;
+       u16 instance_id;
+} __packed;
+
+struct skl_dfw_module_fmt {
+       u32 channels;
+       u32 freq;
+       u32 bit_depth;
+       u32 valid_bit_depth;
+       u32 ch_cfg;
+} __packed;
+
+struct skl_dfw_module_caps {
+       u32 caps_size;
+       u32 caps[HDA_SST_CFG_MAX];
+};
+
+struct skl_dfw_pipe {
+       u8 pipe_id;
+       u8 pipe_priority;
+       u16 conn_type;
+       u32 memory_pages;
+} __packed;
+
+struct skl_dfw_module {
+       u16 module_id;
+       u16 instance_id;
+       u32 max_mcps;
+       u8 core_id;
+       u8 max_in_queue;
+       u8 max_out_queue;
+       u8 is_loadable;
+       u8 conn_type;
+       u8 dev_type;
+       u8 hw_conn_type;
+       u8 time_slot;
+       u32 obs;
+       u32 ibs;
+       u32 params_fixup;
+       u32 converter;
+       u32 module_type;
+       u32 vbus_id;
+       u8 is_dynamic_in_pin;
+       u8 is_dynamic_out_pin;
+       struct skl_dfw_pipe pipe;
+       struct skl_dfw_module_fmt in_fmt;
+       struct skl_dfw_module_fmt out_fmt;
+       struct skl_dfw_module_pin in_pin[MAX_IN_QUEUE];
+       struct skl_dfw_module_pin out_pin[MAX_OUT_QUEUE];
+       struct skl_dfw_module_caps caps;
+} __packed;
+
+struct skl_dfw_algo_data {
+       u32 max;
+       char *params;
+} __packed;
+
 #endif
index 348d094e81d6609ee95505ce91e9d41671032bb6..5319529aedf7ae5030dbf47ce9b6056512f05c2d 100644 (file)
@@ -166,12 +166,20 @@ static int skl_runtime_suspend(struct device *dev)
        struct pci_dev *pci = to_pci_dev(dev);
        struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
        struct hdac_bus *bus = ebus_to_hbus(ebus);
+       struct skl *skl = ebus_to_skl(ebus);
+       int ret;
 
        dev_dbg(bus->dev, "in %s\n", __func__);
 
        /* enable controller wake up event */
        snd_hdac_chip_updatew(bus, WAKEEN, 0, STATESTS_INT_MASK);
 
+       snd_hdac_ext_bus_link_power_down_all(ebus);
+
+       ret = skl_suspend_dsp(skl);
+       if (ret < 0)
+               return ret;
+
        snd_hdac_bus_stop_chip(bus);
        snd_hdac_bus_enter_link_reset(bus);
 
@@ -183,7 +191,7 @@ static int skl_runtime_resume(struct device *dev)
        struct pci_dev *pci = to_pci_dev(dev);
        struct hdac_ext_bus *ebus = pci_get_drvdata(pci);
        struct hdac_bus *bus = ebus_to_hbus(ebus);
-       struct skl *hda = ebus_to_skl(ebus);
+       struct skl *skl = ebus_to_skl(ebus);
        int status;
 
        dev_dbg(bus->dev, "in %s\n", __func__);
@@ -191,12 +199,12 @@ static int skl_runtime_resume(struct device *dev)
        /* Read STATESTS before controller reset */
        status = snd_hdac_chip_readw(bus, STATESTS);
 
-       skl_init_pci(hda);
+       skl_init_pci(skl);
        snd_hdac_bus_init_chip(bus, true);
        /* disable controller Wake Up event */
        snd_hdac_chip_updatew(bus, WAKEEN, STATESTS_INT_MASK, 0);
 
-       return 0;
+       return skl_resume_dsp(skl);
 }
 #endif /* CONFIG_PM */
 
@@ -453,21 +461,28 @@ static int skl_probe(struct pci_dev *pci,
        if (err < 0)
                goto out_free;
 
+       skl->nhlt = skl_nhlt_init(bus->dev);
+
+       if (skl->nhlt == NULL)
+               goto out_free;
+
        pci_set_drvdata(skl->pci, ebus);
 
        /* check if dsp is there */
        if (ebus->ppcap) {
-               /* TODO register with dsp IPC */
-               dev_dbg(bus->dev, "Register dsp\n");
+               err = skl_init_dsp(skl);
+               if (err < 0) {
+                       dev_dbg(bus->dev, "error failed to register dsp\n");
+                       goto out_free;
+               }
        }
-
        if (ebus->mlcap)
                snd_hdac_ext_bus_get_ml_capabilities(ebus);
 
        /* create device for soc dmic */
        err = skl_dmic_device_register(skl);
        if (err < 0)
-               goto out_free;
+               goto out_dsp_free;
 
        /* register platform dai and controls */
        err = skl_platform_register(bus->dev);
@@ -491,6 +506,8 @@ out_unregister:
        skl_platform_unregister(bus->dev);
 out_dmic_free:
        skl_dmic_device_unregister(skl);
+out_dsp_free:
+       skl_free_dsp(skl);
 out_free:
        skl->init_failed = 1;
        skl_free(ebus);
@@ -507,6 +524,7 @@ static void skl_remove(struct pci_dev *pci)
                pm_runtime_get_noresume(&pci->dev);
        pci_dev_put(pci);
        skl_platform_unregister(&pci->dev);
+       skl_free_dsp(skl);
        skl_dmic_device_unregister(skl);
        skl_free(ebus);
        dev_set_drvdata(&pci->dev, NULL);
index f7fdbb02947f79952bcb9e16ac41be3342a2bd86..dd2e79ae45a8e6294c2766ef5eff7f4fac37befe 100644 (file)
 #define AZX_REG_VS_SDXEFIFOS_XBASE     0x1094
 #define AZX_REG_VS_SDXEFIFOS_XINTERVAL 0x20
 
+struct skl_dsp_resource {
+       u32 max_mcps;
+       u32 max_mem;
+       u32 mcps;
+       u32 mem;
+};
+
 struct skl {
        struct hdac_ext_bus ebus;
        struct pci_dev *pci;
@@ -55,8 +62,12 @@ struct skl {
        unsigned int init_failed:1; /* delayed init failed */
        struct platform_device *dmic_dev;
 
-       void __iomem *nhlt; /* nhlt ptr */
+       void *nhlt; /* nhlt ptr */
        struct skl_sst *skl_sst; /* sst skl ctx */
+
+       struct skl_dsp_resource resource;
+       struct list_head ppl_list;
+       struct list_head dapm_path_list;
 };
 
 #define skl_to_ebus(s) (&(s)->ebus)
@@ -72,8 +83,8 @@ struct skl_dma_params {
 int skl_platform_unregister(struct device *dev);
 int skl_platform_register(struct device *dev);
 
-void __iomem *skl_nhlt_init(struct device *dev);
-void skl_nhlt_free(void __iomem *addr);
+void *skl_nhlt_init(struct device *dev);
+void skl_nhlt_free(void *addr);
 struct nhlt_specific_cfg *skl_get_ep_blob(struct skl *skl, u32 instance,
                        u8 link_type, u8 s_fmt, u8 no_ch, u32 s_rate, u8 dirn);
 
index b05fb1c1a8483e18ea7e5226325563ea82d044c0..794a3499e567c3a907e70b8c1c57aafce6aab207 100644 (file)
@@ -485,6 +485,7 @@ static const struct of_device_id jz4740_of_matches[] = {
        { .compatible = "ingenic,jz4780-i2s", .data = (void *)JZ_I2S_JZ4780 },
        { /* sentinel */ }
 };
+MODULE_DEVICE_TABLE(of, jz4740_of_matches);
 #endif
 
 static int jz4740_i2s_dev_probe(struct platform_device *pdev)
index de7563bdc5c28e2c7954a00ca9929e508176d718..e0304d544f26ba45660e94fa98942d2416ddb94a 100644 (file)
@@ -130,6 +130,7 @@ static const struct of_device_id a370db_dt_ids[] = {
        { .compatible = "marvell,a370db-audio" },
        { },
 };
+MODULE_DEVICE_TABLE(of, a370db_dt_ids);
 
 static struct platform_driver a370db_driver = {
        .driver         = {
index 684e8a78bed0640310e0b54e292df725b409fe22..71a1a35047bafd085881efd5369f5275bf9a050f 100644 (file)
@@ -179,21 +179,13 @@ static int mt8173_max98090_dev_probe(struct platform_device *pdev)
        }
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
                        __func__, ret);
        return ret;
 }
 
-static int mt8173_max98090_dev_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 mt8173_max98090_dt_match[] = {
        { .compatible = "mediatek,mt8173-max98090", },
        { }
@@ -209,7 +201,6 @@ static struct platform_driver mt8173_max98090_driver = {
 #endif
        },
        .probe = mt8173_max98090_dev_probe,
-       .remove = mt8173_max98090_dev_remove,
 };
 
 module_platform_driver(mt8173_max98090_driver);
index 86cf9752f18a343791eaeeff8ca8ab9c2c4a83e8..50ba538eccb3f771da9c08685d4e33aa0cbfa80b 100644 (file)
@@ -246,21 +246,13 @@ static int mt8173_rt5650_rt5676_dev_probe(struct platform_device *pdev)
        card->dev = &pdev->dev;
        platform_set_drvdata(pdev, card);
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "%s snd_soc_register_card fail %d\n",
                        __func__, ret);
        return ret;
 }
 
-static int mt8173_rt5650_rt5676_dev_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 mt8173_rt5650_rt5676_dt_match[] = {
        { .compatible = "mediatek,mt8173-rt5650-rt5676", },
        { }
@@ -276,7 +268,6 @@ static struct platform_driver mt8173_rt5650_rt5676_driver = {
 #endif
        },
        .probe = mt8173_rt5650_rt5676_dev_probe,
-       .remove = mt8173_rt5650_rt5676_dev_remove,
 };
 
 module_platform_driver(mt8173_rt5650_rt5676_driver);
index 6e6fce6a14ba6dc1c2ab14d054df8f5b07b2caea..2b23ffbac6b126cdff2674f68133dea77c40f546 100644 (file)
@@ -142,7 +142,7 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
        card->dev = &pdev->dev;
        platform_set_drvdata(pdev, card);
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card failed (%d)\n",
                        ret);
@@ -154,12 +154,8 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
 
 static int mxs_sgtl5000_remove(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
        mxs_saif_put_mclk(0);
 
-       snd_soc_unregister_card(card);
-
        return 0;
 }
 
index dcb5336b569815b8958e2ecb572710930ca425cb..190f868e78b24af37d8442d378c06aeb8ec6aaf2 100644 (file)
@@ -99,8 +99,7 @@ static int n810_startup(struct snd_pcm_substream *substream)
        struct snd_pcm_runtime *runtime = substream->runtime;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
 
-       snd_pcm_hw_constraint_minmax(runtime,
-                                    SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+       snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
 
        n810_ext_control(&rtd->card->dapm);
        return clk_prepare_enable(sys_clkout2);
index 3bebfb1d3a6f9b7ad9a8d20641018d16bf97f886..5e21f08579d804c5f7895a22e87de6cdff8b79ef 100644 (file)
@@ -107,8 +107,7 @@ static int rx51_startup(struct snd_pcm_substream *substream)
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_soc_card *card = rtd->card;
 
-       snd_pcm_hw_constraint_minmax(runtime,
-                                    SNDRV_PCM_HW_PARAM_CHANNELS, 2, 2);
+       snd_pcm_hw_constraint_single(runtime, SNDRV_PCM_HW_PARAM_CHANNELS, 2);
        rx51_ext_control(&card->dapm);
 
        return 0;
@@ -297,7 +296,7 @@ static int rx51_aic34_init(struct snd_soc_pcm_runtime *rtd)
                dev_err(card->dev, "Failed to add TPA6130A2 controls\n");
                return err;
        }
-       snd_soc_limit_volume(codec, "TPA6130A2 Headphone Playback Volume", 42);
+       snd_soc_limit_volume(card, "TPA6130A2 Headphone Playback Volume", 42);
 
        err = omap_mcbsp_st_add_controls(rtd, 2);
        if (err < 0) {
index 2b26318bc20097b0c5e369b50f151b7bde511e59..6147e86e9b0fc2f0addc82a2374c7aa3f095bbb8 100644 (file)
@@ -116,26 +116,19 @@ static int brownstone_probe(struct platform_device *pdev)
        int ret;
 
        brownstone.dev = &pdev->dev;
-       ret = snd_soc_register_card(&brownstone);
+       ret = devm_snd_soc_register_card(&pdev->dev, &brownstone);
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                                ret);
        return ret;
 }
 
-static int brownstone_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_card(&brownstone);
-       return 0;
-}
-
 static struct platform_driver mmp_driver = {
        .driver         = {
                .name   = "brownstone-audio",
                .pm     = &snd_soc_pm_ops,
        },
        .probe          = brownstone_probe,
-       .remove         = brownstone_remove,
 };
 
 module_platform_driver(mmp_driver);
index 3580d10c9f282312056d6ad3952b6adbcf632ffe..c97dc13d36087628433de516a1b2217df02b492b 100644 (file)
@@ -295,28 +295,19 @@ static int corgi_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
        return ret;
 }
 
-static int corgi_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
 static struct platform_driver corgi_driver = {
        .driver         = {
                .name   = "corgi-audio",
                .pm     = &snd_soc_pm_ops,
        },
        .probe          = corgi_probe,
-       .remove         = corgi_remove,
 };
 
 module_platform_driver(corgi_driver);
index d72e124a3676bf545be9d0afa49e6180973a00b2..1de876529aa128c39f9fc5000dcb371a778849a3 100644 (file)
@@ -138,7 +138,7 @@ static int e740_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
@@ -149,10 +149,7 @@ static int e740_probe(struct platform_device *pdev)
 
 static int e740_remove(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
        gpio_free_array(e740_audio_gpios, ARRAY_SIZE(e740_audio_gpios));
-       snd_soc_unregister_card(card);
        return 0;
 }
 
index 48f2d7c2e68c2621f7cce3c6225a166ae8f0920f..b7eb7cd5df7d0814e4c4abe172215d38d2dd5ab1 100644 (file)
@@ -120,7 +120,7 @@ static int e750_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
@@ -131,10 +131,7 @@ static int e750_probe(struct platform_device *pdev)
 
 static int e750_remove(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
        gpio_free_array(e750_audio_gpios, ARRAY_SIZE(e750_audio_gpios));
-       snd_soc_unregister_card(card);
        return 0;
 }
 
index 45d4bd46fff60d08dbda013b5e7b8f8cefd1851e..41bf71466a7bb619c06de89e81d4e1b6b731c8fb 100644 (file)
@@ -119,7 +119,7 @@ static int e800_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
@@ -130,10 +130,7 @@ static int e800_probe(struct platform_device *pdev)
 
 static int e800_remove(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
        gpio_free_array(e800_audio_gpios, ARRAY_SIZE(e800_audio_gpios));
-       snd_soc_unregister_card(card);
        return 0;
 }
 
index 9f8be7cd567e48c83218624ddfe2cad538ef28d7..ecbf2873b7ffd8e205ce86fdd7dfa743ebe9435c 100644 (file)
@@ -193,7 +193,7 @@ static int hx4700_audio_probe(struct platform_device *pdev)
                return ret;
 
        snd_soc_card_hx4700.dev = &pdev->dev;
-       ret = snd_soc_register_card(&snd_soc_card_hx4700);
+       ret = devm_snd_soc_register_card(&pdev->dev, &snd_soc_card_hx4700);
        if (ret)
                gpio_free_array(hx4700_audio_gpios,
                                ARRAY_SIZE(hx4700_audio_gpios));
@@ -203,8 +203,6 @@ static int hx4700_audio_probe(struct platform_device *pdev)
 
 static int hx4700_audio_remove(struct platform_device *pdev)
 {
-       snd_soc_unregister_card(&snd_soc_card_hx4700);
-
        gpio_set_value(GPIO92_HX4700_HP_DRIVER, 0);
        gpio_set_value(GPIO107_HX4700_SPK_nSD, 0);
 
index 29fabbfd21f1796cd91142c0e99d7addfcc5f924..9d0e40771ef5b01de482aeb9daef67c15f30297f 100644 (file)
@@ -72,28 +72,19 @@ static int imote2_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
        return ret;
 }
 
-static int imote2_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
 static struct platform_driver imote2_driver = {
        .driver         = {
                .name   = "imote2-audio",
                .pm     = &snd_soc_pm_ops,
        },
        .probe          = imote2_probe,
-       .remove         = imote2_remove,
 };
 
 module_platform_driver(imote2_driver);
index a9615a574546988f6fc8e83fe5a018dc05528cba..29bc60e85e92898e585a57202ad2540f4be61cae 100644 (file)
@@ -181,7 +181,7 @@ static int mioa701_wm9713_probe(struct platform_device *pdev)
                return -ENODEV;
 
        mioa701.dev = &pdev->dev;
-       rc =  snd_soc_register_card(&mioa701);
+       rc = devm_snd_soc_register_card(&pdev->dev, &mioa701);
        if (!rc)
                dev_warn(&pdev->dev, "Be warned that incorrect mixers/muxes setup will"
                         "lead to overheating and possible destruction of your device."
@@ -189,17 +189,8 @@ static int mioa701_wm9713_probe(struct platform_device *pdev)
        return rc;
 }
 
-static int mioa701_wm9713_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
 static struct platform_driver mioa701_wm9713_driver = {
        .probe          = mioa701_wm9713_probe,
-       .remove         = mioa701_wm9713_remove,
        .driver         = {
                .name           = "mioa701-wm9713",
                .pm     = &snd_soc_pm_ops,
index c20bbc042425dacb2a478d247f9087b87ba8469c..4e74d9573f032eb77cb79a05f16cfd3da95e4f5b 100644 (file)
@@ -140,22 +140,15 @@ static int palm27x_asoc_probe(struct platform_device *pdev)
 
        palm27x_asoc.dev = &pdev->dev;
 
-       ret = snd_soc_register_card(&palm27x_asoc);
+       ret = devm_snd_soc_register_card(&pdev->dev, &palm27x_asoc);
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
        return ret;
 }
 
-static int palm27x_asoc_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_card(&palm27x_asoc);
-       return 0;
-}
-
 static struct platform_driver palm27x_wm9712_driver = {
        .probe          = palm27x_asoc_probe,
-       .remove         = palm27x_asoc_remove,
        .driver         = {
                .name           = "palm27x-asoc",
                .pm     = &snd_soc_pm_ops,
index 80b457ac522acb2c9bdbf1318dfc59bbd36d3789..84d0e2e508088a260b54911bca03236a50046203 100644 (file)
@@ -267,28 +267,19 @@ static int poodle_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
        return ret;
 }
 
-static int poodle_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-       return 0;
-}
-
 static struct platform_driver poodle_driver = {
        .driver         = {
                .name   = "poodle-audio",
                .pm     = &snd_soc_pm_ops,
        },
        .probe          = poodle_probe,
-       .remove         = poodle_remove,
 };
 
 module_platform_driver(poodle_driver);
index 3da485ec1de7fa9d968f1cfd819252258a35cc46..da03fad1b9cda1ad8fb31f4724db0d149948a27e 100644 (file)
@@ -809,6 +809,7 @@ static const struct of_device_id pxa_ssp_of_ids[] = {
        { .compatible = "mrvl,pxa-ssp-dai" },
        {}
 };
+MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids);
 #endif
 
 static int asoc_ssp_probe(struct platform_device *pdev)
index 9e4b04e0fbd12b452e270214ed2ee7ce09a31b90..f3de615aacd77fcbbc20cc5bf5718b135f931ac8 100644 (file)
@@ -15,6 +15,7 @@
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/dmaengine.h>
+#include <linux/dma/pxa-dma.h>
 
 #include <sound/core.h>
 #include <sound/ac97_codec.h>
@@ -49,7 +50,11 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
        .reset  = pxa2xx_ac97_cold_reset,
 };
 
-static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 11;
+static struct pxad_param pxa2xx_ac97_pcm_stereo_in_req = {
+       .prio = PXAD_PRIO_LOWEST,
+       .drcmr = 11,
+};
+
 static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
        .addr           = __PREG(PCDR),
        .addr_width     = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -57,7 +62,11 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
        .filter_data    = &pxa2xx_ac97_pcm_stereo_in_req,
 };
 
-static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 12;
+static struct pxad_param pxa2xx_ac97_pcm_stereo_out_req = {
+       .prio = PXAD_PRIO_LOWEST,
+       .drcmr = 12,
+};
+
 static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
        .addr           = __PREG(PCDR),
        .addr_width     = DMA_SLAVE_BUSWIDTH_4_BYTES,
@@ -65,7 +74,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
        .filter_data    = &pxa2xx_ac97_pcm_stereo_out_req,
 };
 
-static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10;
+static struct pxad_param pxa2xx_ac97_pcm_aux_mono_out_req = {
+       .prio = PXAD_PRIO_LOWEST,
+       .drcmr = 10,
+};
 static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
        .addr           = __PREG(MODR),
        .addr_width     = DMA_SLAVE_BUSWIDTH_2_BYTES,
@@ -73,7 +85,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
        .filter_data    = &pxa2xx_ac97_pcm_aux_mono_out_req,
 };
 
-static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9;
+static struct pxad_param pxa2xx_ac97_pcm_aux_mono_in_req = {
+       .prio = PXAD_PRIO_LOWEST,
+       .drcmr = 9,
+};
 static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
        .addr           = __PREG(MODR),
        .addr_width     = DMA_SLAVE_BUSWIDTH_2_BYTES,
@@ -81,7 +96,10 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
        .filter_data    = &pxa2xx_ac97_pcm_aux_mono_in_req,
 };
 
-static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8;
+static struct pxad_param pxa2xx_ac97_pcm_aux_mic_mono_req = {
+       .prio = PXAD_PRIO_LOWEST,
+       .drcmr = 8,
+};
 static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
        .addr           = __PREG(MCDR),
        .addr_width     = DMA_SLAVE_BUSWIDTH_2_BYTES,
@@ -89,9 +107,8 @@ static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
        .filter_data    = &pxa2xx_ac97_pcm_aux_mic_mono_req,
 };
 
-static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
-                                struct snd_pcm_hw_params *params,
-                                struct snd_soc_dai *cpu_dai)
+static int pxa2xx_ac97_hifi_startup(struct snd_pcm_substream *substream,
+                                   struct snd_soc_dai *cpu_dai)
 {
        struct snd_dmaengine_dai_dma_data *dma_data;
 
@@ -105,9 +122,8 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
-                                    struct snd_pcm_hw_params *params,
-                                    struct snd_soc_dai *cpu_dai)
+static int pxa2xx_ac97_aux_startup(struct snd_pcm_substream *substream,
+                                  struct snd_soc_dai *cpu_dai)
 {
        struct snd_dmaengine_dai_dma_data *dma_data;
 
@@ -121,9 +137,8 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
        return 0;
 }
 
-static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
-                                    struct snd_pcm_hw_params *params,
-                                    struct snd_soc_dai *cpu_dai)
+static int pxa2xx_ac97_mic_startup(struct snd_pcm_substream *substream,
+                                  struct snd_soc_dai *cpu_dai)
 {
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                return -ENODEV;
@@ -139,15 +154,15 @@ static int pxa2xx_ac97_hw_mic_params(struct snd_pcm_substream *substream,
                SNDRV_PCM_RATE_48000)
 
 static const struct snd_soc_dai_ops pxa_ac97_hifi_dai_ops = {
-       .hw_params      = pxa2xx_ac97_hw_params,
+       .startup        = pxa2xx_ac97_hifi_startup,
 };
 
 static const struct snd_soc_dai_ops pxa_ac97_aux_dai_ops = {
-       .hw_params      = pxa2xx_ac97_hw_aux_params,
+       .startup        = pxa2xx_ac97_aux_startup,
 };
 
 static const struct snd_soc_dai_ops pxa_ac97_mic_dai_ops = {
-       .hw_params      = pxa2xx_ac97_hw_mic_params,
+       .startup        = pxa2xx_ac97_mic_startup,
 };
 
 /*
index 6b4e400369107ccfac6513d28953b38e7805accd..0389cf7b4b1ea57169bb33ab8208937c63ef3cc2 100644 (file)
@@ -319,6 +319,9 @@ static int pxa2xx_i2s_probe(struct snd_soc_dai *dai)
        /* Along with FIFO servicing */
        SAIMR &= ~(SAIMR_RFS | SAIMR_TFS);
 
+       snd_soc_dai_init_dma_data(dai, &pxa2xx_i2s_pcm_stereo_out,
+               &pxa2xx_i2s_pcm_stereo_in);
+
        return 0;
 }
 
index 831ee37d2e3e714941855ebdef31bb3eaf15d26a..9f390398d518046ebd336a5834ef8207bd283e36 100644 (file)
@@ -15,8 +15,6 @@
 #include <linux/dmaengine.h>
 #include <linux/of.h>
 
-#include <mach/dma.h>
-
 #include <sound/core.h>
 #include <sound/soc.h>
 #include <sound/pxa2xx-lib.h>
 static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params)
 {
-       struct snd_pcm_runtime *runtime = substream->runtime;
-       struct pxa2xx_runtime_data *prtd = runtime->private_data;
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct snd_dmaengine_dai_dma_data *dma;
-       int ret;
 
        dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
 
@@ -40,40 +35,13 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
        if (!dma)
                return 0;
 
-       /* this may get called several times by oss emulation
-        * with different params */
-       if (prtd->params == NULL) {
-               prtd->params = dma;
-               ret = pxa_request_dma("name", DMA_PRIO_LOW,
-                             pxa2xx_pcm_dma_irq, substream);
-               if (ret < 0)
-                       return ret;
-               prtd->dma_ch = ret;
-       } else if (prtd->params != dma) {
-               pxa_free_dma(prtd->dma_ch);
-               prtd->params = dma;
-               ret = pxa_request_dma("name", DMA_PRIO_LOW,
-                             pxa2xx_pcm_dma_irq, substream);
-               if (ret < 0)
-                       return ret;
-               prtd->dma_ch = ret;
-       }
-
        return __pxa2xx_pcm_hw_params(substream, params);
 }
 
 static int pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream)
 {
-       struct pxa2xx_runtime_data *prtd = substream->runtime->private_data;
-
        __pxa2xx_pcm_hw_free(substream);
 
-       if (prtd->dma_ch >= 0) {
-               pxa_free_dma(prtd->dma_ch);
-               prtd->dma_ch = -1;
-               prtd->params = NULL;
-       }
-
        return 0;
 }
 
@@ -132,6 +100,7 @@ static const struct of_device_id snd_soc_pxa_audio_match[] = {
        { .compatible   = "mrvl,pxa-pcm-audio" },
        { }
 };
+MODULE_DEVICE_TABLE(of, snd_soc_pxa_audio_match);
 #endif
 
 static struct platform_driver pxa_pcm_driver = {
index 461123ad5ff2d4c56e21a97a14e27f8ce7d841c4..b00222620fd01be5cf00d4118d44f18832ad3bdc 100644 (file)
@@ -305,7 +305,7 @@ static int spitz_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
@@ -322,9 +322,6 @@ err1:
 
 static int spitz_remove(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
        gpio_free(spitz_mic_gpio);
        return 0;
 }
index f59f566551ef72ad4e0b5c4bb369d1c8b8de7ec0..49518dd642aa18ffd3f0004fe2f425210e820b54 100644 (file)
@@ -233,7 +233,7 @@ static int tosa_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret) {
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
@@ -244,10 +244,7 @@ static int tosa_probe(struct platform_device *pdev)
 
 static int tosa_remove(struct platform_device *pdev)
 {
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
        gpio_free(TOSA_GPIO_L_MUTE);
-       snd_soc_unregister_card(card);
        return 0;
 }
 
index 1753c7d9e760e4bfa14e5b5ed8844b530e7413cf..65c20f779177589bcb7eb0ce8d61a2196806eb69 100644 (file)
@@ -128,7 +128,7 @@ static int ttc_dkb_probe(struct platform_device *pdev)
 
        card->dev = &pdev->dev;
 
-       ret = snd_soc_register_card(card);
+       ret = devm_snd_soc_register_card(&pdev->dev, card);
        if (ret)
                dev_err(&pdev->dev, "snd_soc_register_card() failed: %d\n",
                        ret);
@@ -136,22 +136,12 @@ static int ttc_dkb_probe(struct platform_device *pdev)
        return ret;
 }
 
-static int ttc_dkb_remove(struct platform_device *pdev)
-{
-       struct snd_soc_card *card = platform_get_drvdata(pdev);
-
-       snd_soc_unregister_card(card);
-
-       return 0;
-}
-
 static struct platform_driver ttc_dkb_driver = {
        .driver         = {
                .name   = "ttc-dkb-audio",
                .pm     = &snd_soc_pm_ops,
        },
        .probe          = ttc_dkb_probe,
-       .remove         = ttc_dkb_remove,
 };
 
 module_platform_driver(ttc_dkb_driver);
index 97bc2023f08aa81fff0b1524b6fb0f34e992c23b..e5101e0d2d372262f8ecf7d0498240cba971b86f 100644 (file)
@@ -438,7 +438,8 @@ int asoc_qcom_lpass_cpu_platform_probe(struct platform_device *pdev)
                if (IS_ERR(drvdata->mi2s_bit_clk[dai_id])) {
                        dev_err(&pdev->dev,
                                "%s() error getting mi2s-bit-clk: %ld\n",
-                               __func__, PTR_ERR(drvdata->mi2s_bit_clk[i]));
+                               __func__,
+                               PTR_ERR(drvdata->mi2s_bit_clk[dai_id]));
                        return PTR_ERR(drvdata->mi2s_bit_clk[dai_id]);
                }
        }
index 58bae8e2cf5ff7b9d1922feceaa0ac7347483561..f1e0c703e0d2f105226c2becffafc852f1927879 100644 (file)
@@ -15,9 +15,17 @@ config SND_SOC_ROCKCHIP_I2S
          Rockchip I2S device. The device supports upto maximum of
          8 channels each for play and record.
 
+config SND_SOC_ROCKCHIP_SPDIF
+       tristate "Rockchip SPDIF 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 SPDIF driver for
+         Rockchip SPDIF transceiver device.
+
 config SND_SOC_ROCKCHIP_MAX98090
        tristate "ASoC support for Rockchip boards using a MAX98090 codec"
-       depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB
+       depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
        select SND_SOC_ROCKCHIP_I2S
        select SND_SOC_MAX98090
        select SND_SOC_TS3A227E
@@ -27,7 +35,7 @@ config SND_SOC_ROCKCHIP_MAX98090
 
 config SND_SOC_ROCKCHIP_RT5645
        tristate "ASoC support for Rockchip boards using a RT5645/RT5650 codec"
-       depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB
+       depends on SND_SOC_ROCKCHIP && I2C && GPIOLIB && CLKDEV_LOOKUP
        select SND_SOC_ROCKCHIP_I2S
        select SND_SOC_RT5645
        help
index 1bc1dc3c729a40ce715903346a8c409c319b4fb7..c0bf560125f3d8d4fc4131fd65cbb277be8bb8ac 100644 (file)
@@ -1,7 +1,9 @@
 # ROCKCHIP Platform Support
-snd-soc-i2s-objs := rockchip_i2s.o
+snd-soc-rockchip-i2s-objs := rockchip_i2s.o
+snd-soc-rockchip-spdif-objs := rockchip_spdif.o
 
-obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-i2s.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_I2S) += snd-soc-rockchip-i2s.o
+obj-$(CONFIG_SND_SOC_ROCKCHIP_SPDIF) += snd-soc-rockchip-spdif.o
 
 snd-soc-rockchip-max98090-objs := rockchip_max98090.o
 snd-soc-rockchip-rt5645-objs := rockchip_rt5645.o
index b93610212e3df75354e34d89dcb1999930a1705a..58ee64594f075efb7327d1480113f1401e97f248 100644 (file)
@@ -226,6 +226,7 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
                                  struct snd_soc_dai *dai)
 {
        struct rk_i2s_dev *i2s = to_info(dai);
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
        unsigned int val = 0;
 
        switch (params_format(params)) {
@@ -245,13 +246,46 @@ static int rockchip_i2s_hw_params(struct snd_pcm_substream *substream,
                return -EINVAL;
        }
 
-       regmap_update_bits(i2s->regmap, I2S_TXCR, I2S_TXCR_VDW_MASK, val);
-       regmap_update_bits(i2s->regmap, I2S_RXCR, I2S_RXCR_VDW_MASK, val);
+       switch (params_channels(params)) {
+       case 8:
+               val |= I2S_CHN_8;
+               break;
+       case 6:
+               val |= I2S_CHN_6;
+               break;
+       case 4:
+               val |= I2S_CHN_4;
+               break;
+       case 2:
+               val |= I2S_CHN_2;
+               break;
+       default:
+               dev_err(i2s->dev, "invalid channel: %d\n",
+                       params_channels(params));
+               return -EINVAL;
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_CAPTURE)
+               regmap_update_bits(i2s->regmap, I2S_RXCR,
+                                  I2S_RXCR_VDW_MASK | I2S_RXCR_CSR_MASK,
+                                  val);
+       else
+               regmap_update_bits(i2s->regmap, I2S_TXCR,
+                                  I2S_TXCR_VDW_MASK | I2S_TXCR_CSR_MASK,
+                                  val);
+
        regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_TDL_MASK,
                           I2S_DMACR_TDL(16));
        regmap_update_bits(i2s->regmap, I2S_DMACR, I2S_DMACR_RDL_MASK,
                           I2S_DMACR_RDL(16));
 
+       val = I2S_CKR_TRCM_TXRX;
+       if (dai->driver->symmetric_rates || rtd->dai_link->symmetric_rates)
+               val = I2S_CKR_TRCM_TXSHARE;
+
+       regmap_update_bits(i2s->regmap, I2S_CKR,
+                          I2S_CKR_TRCM_MASK,
+                          val);
        return 0;
 }
 
@@ -415,10 +449,12 @@ static const struct regmap_config rockchip_i2s_regmap_config = {
 
 static int rockchip_i2s_probe(struct platform_device *pdev)
 {
+       struct device_node *node = pdev->dev.of_node;
        struct rk_i2s_dev *i2s;
        struct resource *res;
        void __iomem *regs;
        int ret;
+       int val;
 
        i2s = devm_kzalloc(&pdev->dev, sizeof(*i2s), GFP_KERNEL);
        if (!i2s) {
@@ -475,6 +511,14 @@ static int rockchip_i2s_probe(struct platform_device *pdev)
                        goto err_pm_disable;
        }
 
+       /* refine capture channels */
+       if (!of_property_read_u32(node, "rockchip,capture-channels", &val)) {
+               if (val >= 2 && val <= 8)
+                       rockchip_i2s_dai.capture.channels_max = val;
+               else
+                       rockchip_i2s_dai.capture.channels_max = 2;
+       }
+
        ret = devm_snd_soc_register_component(&pdev->dev,
                                              &rockchip_i2s_component,
                                              &rockchip_i2s_dai, 1);
index 93f456f518a97dc9a048350093acae715852e10a..dc6e2c74d08818bf68d333f2ca4d811eb914b7ed 100644 (file)
@@ -49,6 +49,9 @@
  * RXCR
  * receive operation control register
 */
+#define I2S_RXCR_CSR_SHIFT     15
+#define I2S_RXCR_CSR(x)                (x << I2S_RXCR_CSR_SHIFT)
+#define I2S_RXCR_CSR_MASK      (3 << I2S_RXCR_CSR_SHIFT)
 #define I2S_RXCR_HWT           BIT(14)
 #define I2S_RXCR_SJM_SHIFT     12
 #define I2S_RXCR_SJM_R         (0 << I2S_RXCR_SJM_SHIFT)
  * CKR
  * clock generation register
 */
+#define I2S_CKR_TRCM_SHIFT     28
+#define I2S_CKR_TRCM(x)        (x << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXRX      (0 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_TXSHARE   (1 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_RXSHARE   (2 << I2S_CKR_TRCM_SHIFT)
+#define I2S_CKR_TRCM_MASK      (3 << I2S_CKR_TRCM_SHIFT)
 #define I2S_CKR_MSS_SHIFT      27
 #define I2S_CKR_MSS_MASTER     (0 << I2S_CKR_MSS_SHIFT)
 #define I2S_CKR_MSS_SLAVE      (1 << I2S_CKR_MSS_SHIFT)
@@ -207,6 +216,13 @@ enum {
        ROCKCHIP_DIV_BCLK,
 };
 
+/* channel select */
+#define I2S_CSR_SHIFT  15
+#define I2S_CHN_2      (0 << I2S_CSR_SHIFT)
+#define I2S_CHN_4      (1 << I2S_CSR_SHIFT)
+#define I2S_CHN_6      (2 << I2S_CSR_SHIFT)
+#define I2S_CHN_8      (3 << I2S_CSR_SHIFT)
+
 /* I2S REGS */
 #define I2S_TXCR       (0x0000)
 #define I2S_RXCR       (0x0004)
diff --git a/sound/soc/rockchip/rockchip_spdif.c b/sound/soc/rockchip/rockchip_spdif.c
new file mode 100644 (file)
index 0000000..a38a302
--- /dev/null
@@ -0,0 +1,405 @@
+/* sound/soc/rockchip/rk_spdif.c
+ *
+ * ALSA SoC Audio Layer - Rockchip I2S Controller driver
+ *
+ * Copyright (c) 2014 Rockchip Electronics Co. Ltd.
+ * Author: Jianqun <jay.xu@rock-chips.com>
+ * Copyright (c) 2015 Collabora Ltd.
+ * Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ *
+ * 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/delay.h>
+#include <linux/of_gpio.h>
+#include <linux/clk.h>
+#include <linux/pm_runtime.h>
+#include <linux/mfd/syscon.h>
+#include <linux/regmap.h>
+#include <sound/pcm_params.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "rockchip_spdif.h"
+
+enum rk_spdif_type {
+       RK_SPDIF_RK3066,
+       RK_SPDIF_RK3188,
+       RK_SPDIF_RK3288,
+};
+
+#define RK3288_GRF_SOC_CON2 0x24c
+
+struct rk_spdif_dev {
+       struct device *dev;
+
+       struct clk *mclk;
+       struct clk *hclk;
+
+       struct snd_dmaengine_dai_dma_data playback_dma_data;
+
+       struct regmap *regmap;
+};
+
+static const struct of_device_id rk_spdif_match[] = {
+       { .compatible = "rockchip,rk3066-spdif",
+         .data = (void *) RK_SPDIF_RK3066 },
+       { .compatible = "rockchip,rk3188-spdif",
+         .data = (void *) RK_SPDIF_RK3188 },
+       { .compatible = "rockchip,rk3288-spdif",
+         .data = (void *) RK_SPDIF_RK3288 },
+       {},
+};
+MODULE_DEVICE_TABLE(of, rk_spdif_match);
+
+static int rk_spdif_runtime_suspend(struct device *dev)
+{
+       struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
+
+       clk_disable_unprepare(spdif->mclk);
+       clk_disable_unprepare(spdif->hclk);
+
+       return 0;
+}
+
+static int rk_spdif_runtime_resume(struct device *dev)
+{
+       struct rk_spdif_dev *spdif = dev_get_drvdata(dev);
+       int ret;
+
+       ret = clk_prepare_enable(spdif->mclk);
+       if (ret) {
+               dev_err(spdif->dev, "mclk clock enable failed %d\n", ret);
+               return ret;
+       }
+
+       ret = clk_prepare_enable(spdif->hclk);
+       if (ret) {
+               dev_err(spdif->dev, "hclk clock enable failed %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+static int rk_spdif_hw_params(struct snd_pcm_substream *substream,
+                                 struct snd_pcm_hw_params *params,
+                                 struct snd_soc_dai *dai)
+{
+       struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+       unsigned int val = SPDIF_CFGR_HALFWORD_ENABLE;
+       int srate, mclk;
+       int ret;
+
+       srate = params_rate(params);
+       switch (srate) {
+       case 32000:
+       case 48000:
+       case 96000:
+               mclk = 96000 * 128; /* 12288000 hz */
+               break;
+       case 44100:
+               mclk = 44100 * 256; /* 11289600 hz */
+               break;
+       case 192000:
+               mclk = 192000 * 128; /* 24576000 hz */
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       switch (params_format(params)) {
+       case SNDRV_PCM_FORMAT_S16_LE:
+               val |= SPDIF_CFGR_VDW_16;
+               break;
+       case SNDRV_PCM_FORMAT_S20_3LE:
+               val |= SPDIF_CFGR_VDW_20;
+               break;
+       case SNDRV_PCM_FORMAT_S24_LE:
+               val |= SPDIF_CFGR_VDW_24;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* Set clock and calculate divider */
+       ret = clk_set_rate(spdif->mclk, mclk);
+       if (ret != 0) {
+               dev_err(spdif->dev, "Failed to set module clock rate: %d\n",
+                       ret);
+               return ret;
+       }
+
+       val |= SPDIF_CFGR_CLK_DIV(mclk/(srate * 256));
+       ret = regmap_update_bits(spdif->regmap, SPDIF_CFGR,
+               SPDIF_CFGR_CLK_DIV_MASK | SPDIF_CFGR_HALFWORD_ENABLE |
+               SDPIF_CFGR_VDW_MASK,
+               val);
+
+       return ret;
+}
+
+static int rk_spdif_trigger(struct snd_pcm_substream *substream,
+                               int cmd, struct snd_soc_dai *dai)
+{
+       struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+       int ret;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
+                                  SPDIF_DMACR_TDE_ENABLE,
+                                  SPDIF_DMACR_TDE_ENABLE);
+
+               if (ret != 0)
+                       return ret;
+
+               ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
+                                  SPDIF_XFER_TXS_START,
+                                  SPDIF_XFER_TXS_START);
+               break;
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               ret = regmap_update_bits(spdif->regmap, SPDIF_DMACR,
+                                  SPDIF_DMACR_TDE_ENABLE,
+                                  SPDIF_DMACR_TDE_DISABLE);
+
+               if (ret != 0)
+                       return ret;
+
+               ret = regmap_update_bits(spdif->regmap, SPDIF_XFER,
+                                  SPDIF_XFER_TXS_START,
+                                  SPDIF_XFER_TXS_STOP);
+               break;
+       default:
+               ret = -EINVAL;
+               break;
+       }
+
+       return ret;
+}
+
+static int rk_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+       struct rk_spdif_dev *spdif = snd_soc_dai_get_drvdata(dai);
+
+       dai->playback_dma_data = &spdif->playback_dma_data;
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops rk_spdif_dai_ops = {
+       .hw_params = rk_spdif_hw_params,
+       .trigger = rk_spdif_trigger,
+};
+
+static struct snd_soc_dai_driver rk_spdif_dai = {
+       .probe = rk_spdif_dai_probe,
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = (SNDRV_PCM_RATE_32000 |
+                         SNDRV_PCM_RATE_44100 |
+                         SNDRV_PCM_RATE_48000 |
+                         SNDRV_PCM_RATE_96000 |
+                         SNDRV_PCM_RATE_192000),
+               .formats = (SNDRV_PCM_FMTBIT_S16_LE |
+                           SNDRV_PCM_FMTBIT_S20_3LE |
+                           SNDRV_PCM_FMTBIT_S24_LE),
+       },
+       .ops = &rk_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver rk_spdif_component = {
+       .name = "rockchip-spdif",
+};
+
+static bool rk_spdif_wr_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case SPDIF_CFGR:
+       case SPDIF_DMACR:
+       case SPDIF_INTCR:
+       case SPDIF_XFER:
+       case SPDIF_SMPDR:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool rk_spdif_rd_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case SPDIF_CFGR:
+       case SPDIF_SDBLR:
+       case SPDIF_INTCR:
+       case SPDIF_INTSR:
+       case SPDIF_XFER:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static bool rk_spdif_volatile_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case SPDIF_INTSR:
+       case SPDIF_SDBLR:
+               return true;
+       default:
+               return false;
+       }
+}
+
+static const struct regmap_config rk_spdif_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+       .max_register = SPDIF_SMPDR,
+       .writeable_reg = rk_spdif_wr_reg,
+       .readable_reg = rk_spdif_rd_reg,
+       .volatile_reg = rk_spdif_volatile_reg,
+       .cache_type = REGCACHE_FLAT,
+};
+
+static int rk_spdif_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct rk_spdif_dev *spdif;
+       const struct of_device_id *match;
+       struct resource *res;
+       void __iomem *regs;
+       int ret;
+
+       match = of_match_node(rk_spdif_match, np);
+       if ((int) match->data == RK_SPDIF_RK3288) {
+               struct regmap *grf;
+
+               grf = syscon_regmap_lookup_by_phandle(np, "rockchip,grf");
+               if (IS_ERR(grf)) {
+                       dev_err(&pdev->dev,
+                               "rockchip_spdif missing 'rockchip,grf' \n");
+                       return PTR_ERR(grf);
+               }
+
+               /* Select the 8 channel SPDIF solution on RK3288 as
+                * the 2 channel one does not appear to work
+                */
+               regmap_write(grf, RK3288_GRF_SOC_CON2, BIT(1) << 16);
+       }
+
+       spdif = devm_kzalloc(&pdev->dev, sizeof(*spdif), GFP_KERNEL);
+       if (!spdif)
+               return -ENOMEM;
+
+       spdif->hclk = devm_clk_get(&pdev->dev, "hclk");
+       if (IS_ERR(spdif->hclk)) {
+               dev_err(&pdev->dev, "Can't retrieve rk_spdif bus clock\n");
+               return PTR_ERR(spdif->hclk);
+       }
+       ret = clk_prepare_enable(spdif->hclk);
+       if (ret) {
+               dev_err(spdif->dev, "hclock enable failed %d\n", ret);
+               return ret;
+       }
+
+       spdif->mclk = devm_clk_get(&pdev->dev, "mclk");
+       if (IS_ERR(spdif->mclk)) {
+               dev_err(&pdev->dev, "Can't retrieve rk_spdif master clock\n");
+               return PTR_ERR(spdif->mclk);
+       }
+
+       ret = clk_prepare_enable(spdif->mclk);
+       if (ret) {
+               dev_err(spdif->dev, "clock enable failed %d\n", ret);
+               return ret;
+       }
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(regs))
+               return PTR_ERR(regs);
+
+       spdif->regmap = devm_regmap_init_mmio_clk(&pdev->dev, "hclk", regs,
+                                                 &rk_spdif_regmap_config);
+       if (IS_ERR(spdif->regmap)) {
+               dev_err(&pdev->dev,
+                       "Failed to initialise managed register map\n");
+               return PTR_ERR(spdif->regmap);
+       }
+
+       spdif->playback_dma_data.addr = res->start + SPDIF_SMPDR;
+       spdif->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       spdif->playback_dma_data.maxburst = 4;
+
+       spdif->dev = &pdev->dev;
+       dev_set_drvdata(&pdev->dev, spdif);
+
+       pm_runtime_set_active(&pdev->dev);
+       pm_runtime_enable(&pdev->dev);
+       pm_request_idle(&pdev->dev);
+
+       ret = devm_snd_soc_register_component(&pdev->dev,
+                                             &rk_spdif_component,
+                                             &rk_spdif_dai, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register DAI\n");
+               goto err_pm_runtime;
+       }
+
+       ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+       if (ret) {
+               dev_err(&pdev->dev, "Could not register PCM\n");
+               goto err_pm_runtime;
+       }
+
+       return 0;
+
+err_pm_runtime:
+       pm_runtime_disable(&pdev->dev);
+
+       return ret;
+}
+
+static int rk_spdif_remove(struct platform_device *pdev)
+{
+       struct rk_spdif_dev *spdif = dev_get_drvdata(&pdev->dev);
+
+       pm_runtime_disable(&pdev->dev);
+       if (!pm_runtime_status_suspended(&pdev->dev))
+               rk_spdif_runtime_suspend(&pdev->dev);
+
+       clk_disable_unprepare(spdif->mclk);
+       clk_disable_unprepare(spdif->hclk);
+
+       return 0;
+}
+
+static const struct dev_pm_ops rk_spdif_pm_ops = {
+       SET_RUNTIME_PM_OPS(rk_spdif_runtime_suspend, rk_spdif_runtime_resume,
+                          NULL)
+};
+
+static struct platform_driver rk_spdif_driver = {
+       .probe = rk_spdif_probe,
+       .remove = rk_spdif_remove,
+       .driver = {
+               .name = "rockchip-spdif",
+               .of_match_table = of_match_ptr(rk_spdif_match),
+               .pm = &rk_spdif_pm_ops,
+       },
+};
+module_platform_driver(rk_spdif_driver);
+
+MODULE_ALIAS("platform:rockchip-spdif");
+MODULE_DESCRIPTION("ROCKCHIP SPDIF transceiver Interface");
+MODULE_AUTHOR("Sjoerd Simons <sjoerd.simons@collabora.co.uk>");
+MODULE_LICENSE("GPL v2");
diff --git a/sound/soc/rockchip/rockchip_spdif.h b/sound/soc/rockchip/rockchip_spdif.h
new file mode 100644 (file)
index 0000000..07f86a2
--- /dev/null
@@ -0,0 +1,63 @@
+/*
+ * ALSA SoC Audio Layer - Rockchip SPDIF transceiver driver
+ *
+ * Copyright (c) 2015 Collabora Ltd.
+ * Author: Sjoerd Simons <sjoerd.simons@collabora.co.uk>
+ *
+ * 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 _ROCKCHIP_SPDIF_H
+#define _ROCKCHIP_SPDIF_H
+
+/*
+ * CFGR
+ * transfer configuration register
+*/
+#define SPDIF_CFGR_CLK_DIV_SHIFT       (16)
+#define SPDIF_CFGR_CLK_DIV_MASK                (0xff << SPDIF_CFGR_CLK_DIV_SHIFT)
+#define SPDIF_CFGR_CLK_DIV(x)          (x << SPDIF_CFGR_CLK_DIV_SHIFT)
+
+#define SPDIF_CFGR_HALFWORD_SHIFT      2
+#define SPDIF_CFGR_HALFWORD_DISABLE    (0 << SPDIF_CFGR_HALFWORD_SHIFT)
+#define SPDIF_CFGR_HALFWORD_ENABLE     (1 << SPDIF_CFGR_HALFWORD_SHIFT)
+
+#define SPDIF_CFGR_VDW_SHIFT   0
+#define SPDIF_CFGR_VDW(x)      (x << SPDIF_CFGR_VDW_SHIFT)
+#define SDPIF_CFGR_VDW_MASK    (0xf << SPDIF_CFGR_VDW_SHIFT)
+
+#define SPDIF_CFGR_VDW_16      SPDIF_CFGR_VDW(0x00)
+#define SPDIF_CFGR_VDW_20      SPDIF_CFGR_VDW(0x01)
+#define SPDIF_CFGR_VDW_24      SPDIF_CFGR_VDW(0x10)
+
+/*
+ * DMACR
+ * DMA control register
+*/
+#define SPDIF_DMACR_TDE_SHIFT  5
+#define SPDIF_DMACR_TDE_DISABLE        (0 << SPDIF_DMACR_TDE_SHIFT)
+#define SPDIF_DMACR_TDE_ENABLE (1 << SPDIF_DMACR_TDE_SHIFT)
+
+#define SPDIF_DMACR_TDL_SHIFT  0
+#define SPDIF_DMACR_TDL(x)     ((x) << SPDIF_DMACR_TDL_SHIFT)
+#define SPDIF_DMACR_TDL_MASK   (0x1f << SDPIF_DMACR_TDL_SHIFT)
+
+/*
+ * XFER
+ * Transfer control register
+*/
+#define SPDIF_XFER_TXS_SHIFT   0
+#define SPDIF_XFER_TXS_STOP    (0 << SPDIF_XFER_TXS_SHIFT)
+#define SPDIF_XFER_TXS_START   (1 << SPDIF_XFER_TXS_SHIFT)
+
+#define SPDIF_CFGR     (0x0000)
+#define SPDIF_SDBLR    (0x0004)
+#define SPDIF_DMACR    (0x0008)
+#define SPDIF_INTCR    (0x000c)
+#define SPDIF_INTSR    (0x0010)
+#define SPDIF_XFER     (0x0018)
+#define SPDIF_SMPDR    (0x0020)
+
+#endif /* _ROCKCHIP_SPDIF_H */
index c72e9fb26658ce17036761164e6fd9e083618841..5f5825faeb2a4331177595eaabcd0600b9d2476e 100644 (file)
 #include <mach/gpio-samsung.h>
 #include "s3c24xx-i2s.h"
 
-static unsigned int rates[] = {
+static const unsigned int rates[] = {
        11025,
        22050,
        44100,
 };
 
-static struct snd_pcm_hw_constraint_list hw_rates = {
+static const struct snd_pcm_hw_constraint_list hw_rates = {
        .count = ARRAY_SIZE(rates),
        .list = rates,
-       .mask = 0,
 };
 
 static struct snd_soc_jack hp_jack;
index 35e37c457f1fd311ee982169010df6507939924d..fa096abe9e75689b58e46bb818810838a78c6080 100644 (file)
@@ -38,16 +38,15 @@ static int rx1950_hw_params(struct snd_pcm_substream *substream,
 static int rx1950_spk_power(struct snd_soc_dapm_widget *w,
                                struct snd_kcontrol *kcontrol, int event);
 
-static unsigned int rates[] = {
+static const unsigned int rates[] = {
        16000,
        44100,
        48000,
 };
 
-static struct snd_pcm_hw_constraint_list hw_rates = {
+static const struct snd_pcm_hw_constraint_list hw_rates = {
        .count = ARRAY_SIZE(rates),
        .list = rates,
-       .mask = 0,
 };
 
 static struct snd_soc_jack hp_jack;
index 07114b0b0dc12bcf23e0a25fb342ad3df23b2464..206d1edab07c1f8a8c91c1b69363b3bc057d38ec 100644 (file)
@@ -37,10 +37,11 @@ config SND_SOC_SH4_SIU
 config SND_SOC_RCAR
        tristate "R-Car series SRU/SCU/SSIU/SSI support"
        depends on DMA_OF
+       depends on COMMON_CLK
        select SND_SIMPLE_CARD
        select REGMAP_MMIO
        help
-         This option enables R-Car SUR/SCU/SSIU/SSI sound support
+         This option enables R-Car SRU/SCU/SSIU/SSI sound support
 
 config SND_SOC_RSRC_CARD
        tristate "Renesas Sampling Rate Convert Sound Card"
index fefc881dbac24acf8e190703973712d8ccc8f22c..2a5b3a293cd243db3d4325b5f6a8e2b4e7041611 100644 (file)
@@ -7,7 +7,7 @@
  * License.  See the file "COPYING" in the main directory of this archive
  * for more details.
  */
-#include <linux/sh_clk.h>
+#include <linux/clk-provider.h>
 #include "rsnd.h"
 
 #define CLKA   0
 #define CLKI   3
 #define CLKMAX 4
 
+#define CLKOUT 0
+#define CLKOUT1        1
+#define CLKOUT2        2
+#define CLKOUT3        3
+#define CLKOUTMAX 4
+
+#define BRRx_MASK(x) (0x3FF & x)
+
+static struct rsnd_mod_ops adg_ops = {
+       .name = "adg",
+};
+
 struct rsnd_adg {
        struct clk *clk[CLKMAX];
+       struct clk *clkout[CLKOUTMAX];
+       struct clk_onecell_data onecell;
+       struct rsnd_mod mod;
 
-       int rbga_rate_for_441khz_div_6; /* RBGA */
-       int rbgb_rate_for_48khz_div_6;  /* RBGB */
-       u32 ckr;
+       int rbga_rate_for_441khz; /* RBGA */
+       int rbgb_rate_for_48khz;  /* RBGB */
 };
 
 #define for_each_rsnd_clk(pos, adg, i)         \
@@ -29,17 +43,36 @@ struct rsnd_adg {
             (i < CLKMAX) &&                    \
             ((pos) = adg->clk[i]);             \
             i++)
+#define for_each_rsnd_clkout(pos, adg, i)      \
+       for (i = 0;                             \
+            (i < CLKOUTMAX) &&                 \
+            ((pos) = adg->clkout[i]);  \
+            i++)
 #define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
 
+static u32 rsnd_adg_calculate_rbgx(unsigned long div)
+{
+       int i, ratio;
+
+       if (!div)
+               return 0;
+
+       for (i = 3; i >= 0; i--) {
+               ratio = 2 << (i * 2);
+               if (0 == (div % ratio))
+                       return (u32)((i << 8) | ((div / ratio) - 1));
+       }
+
+       return ~0;
+}
 
 static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
 {
        struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        int id = rsnd_mod_id(mod);
        int ws = id;
 
-       if (rsnd_ssi_is_pin_sharing(rsnd_ssi_mod_get(priv, id))) {
+       if (rsnd_ssi_is_pin_sharing(io)) {
                switch (id) {
                case 1:
                case 2:
@@ -60,6 +93,9 @@ static u32 rsnd_adg_ssi_ws_timing_gen2(struct rsnd_dai_stream *io)
 int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
                                 struct rsnd_dai_stream *io)
 {
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
        int id = rsnd_mod_id(mod);
        int shift = (id % 2) ? 16 : 0;
        u32 mask, val;
@@ -69,21 +105,26 @@ int rsnd_adg_set_cmd_timsel_gen2(struct rsnd_mod *mod,
        val  = val      << shift;
        mask = 0xffff   << shift;
 
-       rsnd_mod_bset(mod, CMDOUT_TIMSEL, mask, val);
+       rsnd_mod_bset(adg_mod, CMDOUT_TIMSEL, mask, val);
 
        return 0;
 }
 
-static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod,
+static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *src_mod,
                                        struct rsnd_dai_stream *io,
                                        u32 timsel)
 {
+       struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
+       struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
        int is_play = rsnd_io_is_play(io);
-       int id = rsnd_mod_id(mod);
+       int id = rsnd_mod_id(src_mod);
        int shift = (id % 2) ? 16 : 0;
        u32 mask, ws;
        u32 in, out;
 
+       rsnd_mod_confirm_src(src_mod);
+
        ws = rsnd_adg_ssi_ws_timing_gen2(io);
 
        in  = (is_play) ? timsel : ws;
@@ -95,37 +136,38 @@ static int rsnd_adg_set_src_timsel_gen2(struct rsnd_mod *mod,
 
        switch (id / 2) {
        case 0:
-               rsnd_mod_bset(mod, SRCIN_TIMSEL0,  mask, in);
-               rsnd_mod_bset(mod, SRCOUT_TIMSEL0, mask, out);
+               rsnd_mod_bset(adg_mod, SRCIN_TIMSEL0,  mask, in);
+               rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL0, mask, out);
                break;
        case 1:
-               rsnd_mod_bset(mod, SRCIN_TIMSEL1,  mask, in);
-               rsnd_mod_bset(mod, SRCOUT_TIMSEL1, mask, out);
+               rsnd_mod_bset(adg_mod, SRCIN_TIMSEL1,  mask, in);
+               rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL1, mask, out);
                break;
        case 2:
-               rsnd_mod_bset(mod, SRCIN_TIMSEL2,  mask, in);
-               rsnd_mod_bset(mod, SRCOUT_TIMSEL2, mask, out);
+               rsnd_mod_bset(adg_mod, SRCIN_TIMSEL2,  mask, in);
+               rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL2, mask, out);
                break;
        case 3:
-               rsnd_mod_bset(mod, SRCIN_TIMSEL3,  mask, in);
-               rsnd_mod_bset(mod, SRCOUT_TIMSEL3, mask, out);
+               rsnd_mod_bset(adg_mod, SRCIN_TIMSEL3,  mask, in);
+               rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL3, mask, out);
                break;
        case 4:
-               rsnd_mod_bset(mod, SRCIN_TIMSEL4,  mask, in);
-               rsnd_mod_bset(mod, SRCOUT_TIMSEL4, mask, out);
+               rsnd_mod_bset(adg_mod, SRCIN_TIMSEL4,  mask, in);
+               rsnd_mod_bset(adg_mod, SRCOUT_TIMSEL4, mask, out);
                break;
        }
 
        return 0;
 }
 
-int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *src_mod,
                                  struct rsnd_dai_stream *io,
                                  unsigned int src_rate,
                                  unsigned int dst_rate)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(src_mod);
        struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
        struct device *dev = rsnd_priv_to_dev(priv);
        int idx, sel, div, step, ret;
        u32 val, en;
@@ -134,10 +176,12 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
                clk_get_rate(adg->clk[CLKA]),   /* 0000: CLKA */
                clk_get_rate(adg->clk[CLKB]),   /* 0001: CLKB */
                clk_get_rate(adg->clk[CLKC]),   /* 0010: CLKC */
-               adg->rbga_rate_for_441khz_div_6,/* 0011: RBGA */
-               adg->rbgb_rate_for_48khz_div_6, /* 0100: RBGB */
+               adg->rbga_rate_for_441khz,      /* 0011: RBGA */
+               adg->rbgb_rate_for_48khz,       /* 0100: RBGB */
        };
 
+       rsnd_mod_confirm_src(src_mod);
+
        min = ~0;
        val = 0;
        en = 0;
@@ -175,25 +219,27 @@ int rsnd_adg_set_convert_clk_gen2(struct rsnd_mod *mod,
                return -EIO;
        }
 
-       ret = rsnd_adg_set_src_timsel_gen2(mod, io, val);
+       ret = rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
        if (ret < 0) {
                dev_err(dev, "timsel error\n");
                return ret;
        }
 
-       rsnd_mod_bset(mod, DIV_EN, en, en);
+       rsnd_mod_bset(adg_mod, DIV_EN, en, en);
 
        dev_dbg(dev, "convert rate %d <-> %d\n", src_rate, dst_rate);
 
        return 0;
 }
 
-int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *mod,
+int rsnd_adg_set_convert_timing_gen2(struct rsnd_mod *src_mod,
                                     struct rsnd_dai_stream *io)
 {
        u32 val = rsnd_adg_ssi_ws_timing_gen2(io);
 
-       return rsnd_adg_set_src_timsel_gen2(mod, io, val);
+       rsnd_mod_confirm_src(src_mod);
+
+       return rsnd_adg_set_src_timsel_gen2(src_mod, io, val);
 }
 
 int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
@@ -202,6 +248,7 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
                                  unsigned int dst_rate)
 {
        struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
        struct device *dev = rsnd_priv_to_dev(priv);
        int idx, sel, div, shift;
        u32 mask, val;
@@ -211,8 +258,8 @@ int rsnd_adg_set_convert_clk_gen1(struct rsnd_priv *priv,
                clk_get_rate(adg->clk[CLKB]),   /* 001: CLKB */
                clk_get_rate(adg->clk[CLKC]),   /* 010: CLKC */
                0,                              /* 011: MLBCLK (not used) */
-               adg->rbga_rate_for_441khz_div_6,/* 100: RBGA */
-               adg->rbgb_rate_for_48khz_div_6, /* 101: RBGB */
+               adg->rbga_rate_for_441khz,      /* 100: RBGA */
+               adg->rbgb_rate_for_48khz,       /* 101: RBGB */
        };
 
        /* find div (= 1/128, 1/256, 1/512, 1/1024, 1/2048 */
@@ -238,13 +285,13 @@ find_rate:
 
        switch (id / 4) {
        case 0:
-               rsnd_mod_bset(mod, AUDIO_CLK_SEL3, mask, val);
+               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL3, mask, val);
                break;
        case 1:
-               rsnd_mod_bset(mod, AUDIO_CLK_SEL4, mask, val);
+               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL4, mask, val);
                break;
        case 2:
-               rsnd_mod_bset(mod, AUDIO_CLK_SEL5, mask, val);
+               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL5, mask, val);
                break;
        }
 
@@ -257,12 +304,17 @@ find_rate:
        return 0;
 }
 
-static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
+static void rsnd_adg_set_ssi_clk(struct rsnd_mod *ssi_mod, u32 val)
 {
-       int id = rsnd_mod_id(mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(ssi_mod);
+       struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+       int id = rsnd_mod_id(ssi_mod);
        int shift = (id % 4) * 8;
        u32 mask = 0xFF << shift;
 
+       rsnd_mod_confirm_ssi(ssi_mod);
+
        val = val << shift;
 
        /*
@@ -274,13 +326,13 @@ static void rsnd_adg_set_ssi_clk(struct rsnd_mod *mod, u32 val)
 
        switch (id / 4) {
        case 0:
-               rsnd_mod_bset(mod, AUDIO_CLK_SEL0, mask, val);
+               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL0, mask, val);
                break;
        case 1:
-               rsnd_mod_bset(mod, AUDIO_CLK_SEL1, mask, val);
+               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL1, mask, val);
                break;
        case 2:
-               rsnd_mod_bset(mod, AUDIO_CLK_SEL2, mask, val);
+               rsnd_mod_bset(adg_mod, AUDIO_CLK_SEL2, mask, val);
                break;
        }
 }
@@ -326,14 +378,14 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
        }
 
        /*
-        * find 1/6 clock from BRGA/BRGB
+        * find divided clock from BRGA/BRGB
         */
-       if (rate == adg->rbga_rate_for_441khz_div_6) {
+       if (rate  == adg->rbga_rate_for_441khz) {
                data = 0x10;
                goto found_clock;
        }
 
-       if (rate == adg->rbgb_rate_for_48khz_div_6) {
+       if (rate == adg->rbgb_rate_for_48khz) {
                data = 0x20;
                goto found_clock;
        }
@@ -342,29 +394,60 @@ int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
 
 found_clock:
 
-       /* see rsnd_adg_ssi_clk_init() */
-       rsnd_mod_bset(mod, SSICKR, 0x00FF0000, adg->ckr);
-       rsnd_mod_write(mod, BRRA,  0x00000002); /* 1/6 */
-       rsnd_mod_write(mod, BRRB,  0x00000002); /* 1/6 */
-
        /*
         * This "mod" = "ssi" here.
         * we can get "ssi id" from mod
         */
        rsnd_adg_set_ssi_clk(mod, data);
 
-       dev_dbg(dev, "ADG: ssi%d selects clk%d = %d",
-               rsnd_mod_id(mod), i, rate);
+       dev_dbg(dev, "ADG: %s[%d] selects 0x%x for %d\n",
+               rsnd_mod_name(mod), rsnd_mod_id(mod),
+               data, rate);
 
        return 0;
 }
 
-static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
+static void rsnd_adg_get_clkin(struct rsnd_priv *priv,
+                              struct rsnd_adg *adg)
+{
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct clk *clk;
+       static const char * const clk_name[] = {
+               [CLKA]  = "clk_a",
+               [CLKB]  = "clk_b",
+               [CLKC]  = "clk_c",
+               [CLKI]  = "clk_i",
+       };
+       int i;
+
+       for (i = 0; i < CLKMAX; i++) {
+               clk = devm_clk_get(dev, clk_name[i]);
+               adg->clk[i] = IS_ERR(clk) ? NULL : clk;
+       }
+
+       for_each_rsnd_clk(clk, adg, i)
+               dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+}
+
+static void rsnd_adg_get_clkout(struct rsnd_priv *priv,
+                               struct rsnd_adg *adg)
 {
        struct clk *clk;
-       unsigned long rate;
-       u32 ckr;
+       struct rsnd_mod *adg_mod = rsnd_mod_get(adg);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct device_node *np = dev->of_node;
+       u32 ckr, rbgx, rbga, rbgb;
+       u32 rate, req_rate, div;
+       uint32_t count = 0;
+       unsigned long req_48kHz_rate, req_441kHz_rate;
        int i;
+       const char *parent_clk_name = NULL;
+       static const char * const clkout_name[] = {
+               [CLKOUT]  = "audio_clkout",
+               [CLKOUT1] = "audio_clkout1",
+               [CLKOUT2] = "audio_clkout2",
+               [CLKOUT3] = "audio_clkout3",
+       };
        int brg_table[] = {
                [CLKA] = 0x0,
                [CLKB] = 0x1,
@@ -372,19 +455,34 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
                [CLKI] = 0x2,
        };
 
+       of_property_read_u32(np, "#clock-cells", &count);
+
+       /*
+        * ADG supports BRRA/BRRB output only
+        * this means all clkout0/1/2/3 will be same rate
+        */
+       of_property_read_u32(np, "clock-frequency", &req_rate);
+       req_48kHz_rate = 0;
+       req_441kHz_rate = 0;
+       if (0 == (req_rate % 44100))
+               req_441kHz_rate = req_rate;
+       if (0 == (req_rate % 48000))
+               req_48kHz_rate = req_rate;
+
        /*
         * This driver is assuming that AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC
         * have 44.1kHz or 48kHz base clocks for now.
         *
         * SSI itself can divide parent clock by 1/1 - 1/16
-        * So,  BRGA outputs 44.1kHz base parent clock 1/32,
-        * and, BRGB outputs 48.0kHz base parent clock 1/32 here.
         * see
         *      rsnd_adg_ssi_clk_try_start()
+        *      rsnd_ssi_master_clk_start()
         */
        ckr = 0;
-       adg->rbga_rate_for_441khz_div_6 = 0;
-       adg->rbgb_rate_for_48khz_div_6  = 0;
+       rbga = 2; /* default 1/6 */
+       rbgb = 2; /* default 1/6 */
+       adg->rbga_rate_for_441khz       = 0;
+       adg->rbgb_rate_for_48khz        = 0;
        for_each_rsnd_clk(clk, adg, i) {
                rate = clk_get_rate(clk);
 
@@ -392,19 +490,86 @@ static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
                        continue;
 
                /* RBGA */
-               if (!adg->rbga_rate_for_441khz_div_6 && (0 == rate % 44100)) {
-                       adg->rbga_rate_for_441khz_div_6 = rate / 6;
-                       ckr |= brg_table[i] << 20;
+               if (!adg->rbga_rate_for_441khz && (0 == rate % 44100)) {
+                       div = 6;
+                       if (req_441kHz_rate)
+                               div = rate / req_441kHz_rate;
+                       rbgx = rsnd_adg_calculate_rbgx(div);
+                       if (BRRx_MASK(rbgx) == rbgx) {
+                               rbga = rbgx;
+                               adg->rbga_rate_for_441khz = rate / div;
+                               ckr |= brg_table[i] << 20;
+                               if (req_441kHz_rate)
+                                       parent_clk_name = __clk_get_name(clk);
+                       }
                }
 
                /* RBGB */
-               if (!adg->rbgb_rate_for_48khz_div_6 && (0 == rate % 48000)) {
-                       adg->rbgb_rate_for_48khz_div_6 = rate / 6;
-                       ckr |= brg_table[i] << 16;
+               if (!adg->rbgb_rate_for_48khz && (0 == rate % 48000)) {
+                       div = 6;
+                       if (req_48kHz_rate)
+                               div = rate / req_48kHz_rate;
+                       rbgx = rsnd_adg_calculate_rbgx(div);
+                       if (BRRx_MASK(rbgx) == rbgx) {
+                               rbgb = rbgx;
+                               adg->rbgb_rate_for_48khz = rate / div;
+                               ckr |= brg_table[i] << 16;
+                               if (req_48kHz_rate) {
+                                       parent_clk_name = __clk_get_name(clk);
+                                       ckr |= 0x80000000;
+                               }
+                       }
                }
        }
 
-       adg->ckr = ckr;
+       /*
+        * ADG supports BRRA/BRRB output only.
+        * this means all clkout0/1/2/3 will be * same rate
+        */
+
+       /*
+        * for clkout
+        */
+       if (!count) {
+               clk = clk_register_fixed_rate(dev, clkout_name[CLKOUT],
+                                             parent_clk_name,
+                                             (parent_clk_name) ?
+                                             0 : CLK_IS_ROOT, req_rate);
+               if (!IS_ERR(clk)) {
+                       adg->clkout[CLKOUT] = clk;
+                       of_clk_add_provider(np, of_clk_src_simple_get, clk);
+               }
+       }
+       /*
+        * for clkout0/1/2/3
+        */
+       else {
+               for (i = 0; i < CLKOUTMAX; i++) {
+                       clk = clk_register_fixed_rate(dev, clkout_name[i],
+                                                     parent_clk_name,
+                                                     (parent_clk_name) ?
+                                                     0 : CLK_IS_ROOT,
+                                                     req_rate);
+                       if (!IS_ERR(clk)) {
+                               adg->onecell.clks       = adg->clkout;
+                               adg->onecell.clk_num    = CLKOUTMAX;
+
+                               adg->clkout[i] = clk;
+
+                               of_clk_add_provider(np, of_clk_src_onecell_get,
+                                                   &adg->onecell);
+                       }
+               }
+       }
+
+       rsnd_mod_bset(adg_mod, SSICKR, 0x00FF0000, ckr);
+       rsnd_mod_write(adg_mod, BRRA,  rbga);
+       rsnd_mod_write(adg_mod, BRRB,  rbgb);
+
+       for_each_rsnd_clkout(clk, adg, i)
+               dev_dbg(dev, "clkout %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+       dev_dbg(dev, "SSICKR = 0x%08x, BRRA/BRRB = 0x%x/0x%x\n",
+               ckr, rbga, rbgb);
 }
 
 int rsnd_adg_probe(struct platform_device *pdev,
@@ -413,8 +578,6 @@ int rsnd_adg_probe(struct platform_device *pdev,
 {
        struct rsnd_adg *adg;
        struct device *dev = rsnd_priv_to_dev(priv);
-       struct clk *clk;
-       int i;
 
        adg = devm_kzalloc(dev, sizeof(*adg), GFP_KERNEL);
        if (!adg) {
@@ -422,15 +585,16 @@ int rsnd_adg_probe(struct platform_device *pdev,
                return -ENOMEM;
        }
 
-       adg->clk[CLKA]  = devm_clk_get(dev, "clk_a");
-       adg->clk[CLKB]  = devm_clk_get(dev, "clk_b");
-       adg->clk[CLKC]  = devm_clk_get(dev, "clk_c");
-       adg->clk[CLKI]  = devm_clk_get(dev, "clk_i");
-
-       for_each_rsnd_clk(clk, adg, i)
-               dev_dbg(dev, "clk %d : %p : %ld\n", i, clk, clk_get_rate(clk));
+       /*
+        * ADG is special module.
+        * Use ADG mod without rsnd_mod_init() to make debug easy
+        * for rsnd_write/rsnd_read
+        */
+       adg->mod.ops = &adg_ops;
+       adg->mod.priv = priv;
 
-       rsnd_adg_ssi_clk_init(priv, adg);
+       rsnd_adg_get_clkin(priv, adg);
+       rsnd_adg_get_clkout(priv, adg);
 
        priv->adg = adg;
 
index f3feed5ce9b65ba67d936a399a4a06f91cf77db7..deed48ef28b832821010f7192961aaa9af4ba84b 100644 (file)
@@ -110,6 +110,7 @@ static const struct rsnd_of_data rsnd_of_data_gen2 = {
 static const struct of_device_id rsnd_of_match[] = {
        { .compatible = "renesas,rcar_sound-gen1", .data = &rsnd_of_data_gen1 },
        { .compatible = "renesas,rcar_sound-gen2", .data = &rsnd_of_data_gen2 },
+       { .compatible = "renesas,rcar_sound-gen3", .data = &rsnd_of_data_gen2 }, /* gen2 compatible */
        {},
 };
 MODULE_DEVICE_TABLE(of, rsnd_of_match);
@@ -126,6 +127,17 @@ MODULE_DEVICE_TABLE(of, rsnd_of_match);
 #define rsnd_info_id(priv, io, name) \
        ((io)->info->name - priv->info->name##_info)
 
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type)
+{
+       if (mod->type != type) {
+               struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+               struct device *dev = rsnd_priv_to_dev(priv);
+
+               dev_warn(dev, "%s[%d] is not your expected module\n",
+                        rsnd_mod_name(mod), rsnd_mod_id(mod));
+       }
+}
+
 /*
  *     rsnd_mod functions
  */
@@ -288,7 +300,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
 /*
  *     rsnd_dai functions
  */
-#define __rsnd_mod_call(mod, io, func, param...)               \
+#define rsnd_mod_call(mod, io, func, param...)                 \
 ({                                                             \
        struct rsnd_priv *priv = rsnd_mod_to_priv(mod);         \
        struct device *dev = rsnd_priv_to_dev(priv);            \
@@ -296,24 +308,17 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
        u8 val  = (mod->status >> __rsnd_mod_shift_##func) & 0xF;       \
        u8 add  = ((val + __rsnd_mod_add_##func) & 0xF);                \
        int ret = 0;                                                    \
-       int called = 0;                                                 \
-       if (val == __rsnd_mod_call_##func) {                            \
-               called = 1;                                             \
-               ret = (mod)->ops->func(mod, io, param);                 \
-       }                                                               \
+       int call = (val == __rsnd_mod_call_##func) && (mod)->ops->func; \
        mod->status = (mod->status & ~mask) +                           \
                (add << __rsnd_mod_shift_##func);                       \
-       dev_dbg(dev, "%s[%d] 0x%08x %s\n",                              \
-               rsnd_mod_name(mod), rsnd_mod_id(mod), mod->status,      \
-               called ? #func : "");                                   \
+       dev_dbg(dev, "%s[%d]\t0x%08x %s\n",                             \
+               rsnd_mod_name(mod), rsnd_mod_id(mod),                   \
+               mod->status, call ? #func : "");                        \
+       if (call)                                                       \
+               ret = (mod)->ops->func(mod, io, param);                 \
        ret;                                                            \
 })
 
-#define rsnd_mod_call(mod, io, func, param...) \
-       (!(mod) ? -ENODEV :                     \
-        !((mod)->ops->func) ? 0 :              \
-        __rsnd_mod_call(mod, io, func, param))
-
 #define rsnd_dai_call(fn, io, param...)                                \
 ({                                                             \
        struct rsnd_mod *mod;                                   \
@@ -322,9 +327,7 @@ u32 rsnd_get_dalign(struct rsnd_mod *mod, struct rsnd_dai_stream *io)
                mod = (io)->mod[i];                             \
                if (!mod)                                       \
                        continue;                               \
-               ret = rsnd_mod_call(mod, io, fn, param);        \
-               if (ret < 0)                                    \
-                       break;                                  \
+               ret |= rsnd_mod_call(mod, io, fn, param);       \
        }                                                       \
        ret;                                                    \
 })
@@ -490,16 +493,10 @@ static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
                break;
        case SNDRV_PCM_TRIGGER_STOP:
                ret = rsnd_dai_call(stop, io, priv);
-               if (ret < 0)
-                       goto dai_trigger_end;
 
-               ret = rsnd_dai_call(quit, io, priv);
-               if (ret < 0)
-                       goto dai_trigger_end;
+               ret |= rsnd_dai_call(quit, io, priv);
 
-               ret = rsnd_platform_call(priv, dai, stop, ssi_id);
-               if (ret < 0)
-                       goto dai_trigger_end;
+               ret |= rsnd_platform_call(priv, dai, stop, ssi_id);
 
                rsnd_dai_stream_quit(io);
                break;
@@ -1224,20 +1221,11 @@ static int rsnd_probe(struct platform_device *pdev)
        };
        int ret, i;
 
-       info = NULL;
-       of_data = NULL;
-       if (of_id) {
-               info = devm_kzalloc(&pdev->dev,
-                                   sizeof(struct rcar_snd_info), GFP_KERNEL);
-               of_data = of_id->data;
-       } else {
-               info = pdev->dev.platform_data;
-       }
-
-       if (!info) {
-               dev_err(dev, "driver needs R-Car sound information\n");
-               return -ENODEV;
-       }
+       info = devm_kzalloc(&pdev->dev, sizeof(struct rcar_snd_info),
+                           GFP_KERNEL);
+       if (!info)
+               return -ENOMEM;
+       of_data = of_id->data;
 
        /*
         *      init priv data
index 05498bba5874dbb0fe9fffdb46e26bad0866f045..3cb214ab848be05ca345e0a976e2d1a2d6bdc65b 100644 (file)
@@ -35,7 +35,7 @@ static int rsnd_ctu_init(struct rsnd_mod *mod,
                         struct rsnd_dai_stream *io,
                         struct rsnd_priv *priv)
 {
-       rsnd_mod_hw_start(mod);
+       rsnd_mod_power_on(mod);
 
        rsnd_ctu_initialize_lock(mod);
 
@@ -50,7 +50,7 @@ static int rsnd_ctu_quit(struct rsnd_mod *mod,
                         struct rsnd_dai_stream *io,
                         struct rsnd_priv *priv)
 {
-       rsnd_mod_hw_stop(mod);
+       rsnd_mod_power_off(mod);
 
        return 0;
 }
@@ -66,7 +66,7 @@ struct rsnd_mod *rsnd_ctu_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_ctu_nr(priv)))
                id = 0;
 
-       return &((struct rsnd_ctu *)(priv->ctu) + id)->mod;
+       return rsnd_mod_get((struct rsnd_ctu *)(priv->ctu) + id);
 }
 
 static void rsnd_of_parse_ctu(struct platform_device *pdev,
@@ -118,10 +118,8 @@ int rsnd_ctu_probe(struct platform_device *pdev,
        int i, nr, ret;
 
        /* This driver doesn't support Gen1 at this point */
-       if (rsnd_is_gen1(priv)) {
-               dev_warn(dev, "CTU is not supported on Gen1\n");
-               return -EINVAL;
-       }
+       if (rsnd_is_gen1(priv))
+               return 0;
 
        rsnd_of_parse_ctu(pdev, of_data, priv);
 
@@ -150,7 +148,7 @@ int rsnd_ctu_probe(struct platform_device *pdev,
 
                ctu->info = &info->ctu_info[i];
 
-               ret = rsnd_mod_init(priv, &ctu->mod, &rsnd_ctu_ops,
+               ret = rsnd_mod_init(priv, rsnd_mod_get(ctu), &rsnd_ctu_ops,
                                    clk, RSND_MOD_CTU, i);
                if (ret)
                        return ret;
@@ -166,6 +164,6 @@ void rsnd_ctu_remove(struct platform_device *pdev,
        int i;
 
        for_each_rsnd_ctu(ctu, priv, i) {
-               rsnd_mod_quit(&ctu->mod);
+               rsnd_mod_quit(rsnd_mod_get(ctu));
        }
 }
index bfbb8a5e93bdce6c5c70092565869bf446233416..5d084d0409611dd67cf35bc00c2fed53b517862b 100644 (file)
@@ -470,7 +470,7 @@ rsnd_gen2_dma_addr(struct rsnd_dai_stream *io,
                dev_err(dev, "DVC is selected without SRC\n");
 
        /* use SSIU or SSI ? */
-       if (is_ssi && rsnd_ssi_use_busif(io, mod))
+       if (is_ssi && rsnd_ssi_use_busif(io))
                is_ssi++;
 
        return (is_from) ?
index 57796387d482303bc84c29212dfed167685ae6fe..58f690900e6d36a704be976b5a440550df76726a 100644 (file)
@@ -153,7 +153,7 @@ static int rsnd_dvc_init(struct rsnd_mod *mod,
                         struct rsnd_dai_stream *io,
                         struct rsnd_priv *priv)
 {
-       rsnd_mod_hw_start(mod);
+       rsnd_mod_power_on(mod);
 
        rsnd_dvc_soft_reset(mod);
 
@@ -175,7 +175,7 @@ static int rsnd_dvc_quit(struct rsnd_mod *mod,
                         struct rsnd_dai_stream *io,
                         struct rsnd_priv *priv)
 {
-       rsnd_mod_hw_stop(mod);
+       rsnd_mod_power_off(mod);
 
        return 0;
 }
@@ -282,7 +282,7 @@ struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_dvc_nr(priv)))
                id = 0;
 
-       return &((struct rsnd_dvc *)(priv->dvc) + id)->mod;
+       return rsnd_mod_get((struct rsnd_dvc *)(priv->dvc) + id);
 }
 
 static void rsnd_of_parse_dvc(struct platform_device *pdev,
@@ -333,10 +333,8 @@ int rsnd_dvc_probe(struct platform_device *pdev,
        int i, nr, ret;
 
        /* This driver doesn't support Gen1 at this point */
-       if (rsnd_is_gen1(priv)) {
-               dev_warn(dev, "CMD is not supported on Gen1\n");
-               return -EINVAL;
-       }
+       if (rsnd_is_gen1(priv))
+               return 0;
 
        rsnd_of_parse_dvc(pdev, of_data, priv);
 
@@ -361,7 +359,7 @@ int rsnd_dvc_probe(struct platform_device *pdev,
 
                dvc->info = &info->dvc_info[i];
 
-               ret = rsnd_mod_init(priv, &dvc->mod, &rsnd_dvc_ops,
+               ret = rsnd_mod_init(priv, rsnd_mod_get(dvc), &rsnd_dvc_ops,
                              clk, RSND_MOD_DVC, i);
                if (ret)
                        return ret;
@@ -377,6 +375,6 @@ void rsnd_dvc_remove(struct platform_device *pdev,
        int i;
 
        for_each_rsnd_dvc(dvc, priv, i) {
-               rsnd_mod_quit(&dvc->mod);
+               rsnd_mod_quit(rsnd_mod_get(dvc));
        }
 }
index f04d17bc6e3debba80a7c69f2d6c51e3daa72ec8..76da7620904c982ee1946f7db9369c7065028133 100644 (file)
 #include "rsnd.h"
 
 struct rsnd_gen {
-       void __iomem *base[RSND_BASE_MAX];
-
        struct rsnd_gen_ops *ops;
 
+       /* RSND_BASE_MAX base */
+       void __iomem *base[RSND_BASE_MAX];
+       phys_addr_t res[RSND_BASE_MAX];
        struct regmap *regmap[RSND_BASE_MAX];
+
+       /* RSND_REG_MAX base */
        struct regmap_field *regs[RSND_REG_MAX];
-       phys_addr_t res[RSND_REG_MAX];
 };
 
 #define rsnd_priv_to_gen(p)    ((struct rsnd_gen *)(p)->gen)
@@ -79,11 +81,11 @@ 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);
 
-       regmap_fields_read(gen->regs[reg], rsnd_mod_id(mod), &val);
-
        return val;
 }
 
@@ -182,6 +184,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
        if (IS_ERR(regmap))
                return PTR_ERR(regmap);
 
+       /* RSND_BASE_MAX base */
        gen->base[reg_id] = base;
        gen->regmap[reg_id] = regmap;
        gen->res[reg_id] = res->start;
@@ -198,6 +201,7 @@ static int _rsnd_gen_regmap_init(struct rsnd_priv *priv,
                if (IS_ERR(regs))
                        return PTR_ERR(regs);
 
+               /* RSND_REG_MAX base */
                gen->regs[conf[i].idx] = regs;
        }
 
index 0d5c102db6f5fb6ed54b05e42f97888bdd0c6c20..953dd0be9b6026d4b21e952c371c5b9ee3d65a08 100644 (file)
@@ -58,7 +58,7 @@ static int rsnd_mix_init(struct rsnd_mod *mod,
                         struct rsnd_dai_stream *io,
                         struct rsnd_priv *priv)
 {
-       rsnd_mod_hw_start(mod);
+       rsnd_mod_power_on(mod);
 
        rsnd_mix_soft_reset(mod);
 
@@ -83,7 +83,7 @@ static int rsnd_mix_quit(struct rsnd_mod *mod,
                         struct rsnd_dai_stream *io,
                         struct rsnd_priv *priv)
 {
-       rsnd_mod_hw_stop(mod);
+       rsnd_mod_power_off(mod);
 
        return 0;
 }
@@ -99,7 +99,7 @@ struct rsnd_mod *rsnd_mix_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_mix_nr(priv)))
                id = 0;
 
-       return &((struct rsnd_mix *)(priv->mix) + id)->mod;
+       return rsnd_mod_get((struct rsnd_mix *)(priv->mix) + id);
 }
 
 static void rsnd_of_parse_mix(struct platform_device *pdev,
@@ -151,10 +151,8 @@ int rsnd_mix_probe(struct platform_device *pdev,
        int i, nr, ret;
 
        /* This driver doesn't support Gen1 at this point */
-       if (rsnd_is_gen1(priv)) {
-               dev_warn(dev, "MIX is not supported on Gen1\n");
-               return -EINVAL;
-       }
+       if (rsnd_is_gen1(priv))
+               return 0;
 
        rsnd_of_parse_mix(pdev, of_data, priv);
 
@@ -179,7 +177,7 @@ int rsnd_mix_probe(struct platform_device *pdev,
 
                mix->info = &info->mix_info[i];
 
-               ret = rsnd_mod_init(priv, &mix->mod, &rsnd_mix_ops,
+               ret = rsnd_mod_init(priv, rsnd_mod_get(mix), &rsnd_mix_ops,
                                    clk, RSND_MOD_MIX, i);
                if (ret)
                        return ret;
@@ -195,6 +193,6 @@ void rsnd_mix_remove(struct platform_device *pdev,
        int i;
 
        for_each_rsnd_mix(mix, priv, i) {
-               rsnd_mod_quit(&mix->mod);
+               rsnd_mod_quit(rsnd_mod_get(mix));
        }
 }
diff --git a/sound/soc/sh/rcar/rcar_snd.h b/sound/soc/sh/rcar/rcar_snd.h
new file mode 100644 (file)
index 0000000..d8e33d3
--- /dev/null
@@ -0,0 +1,117 @@
+/*
+ * Renesas R-Car SRU/SCU/SSIU/SSI support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.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 RCAR_SND_H
+#define RCAR_SND_H
+
+
+#define RSND_GEN1_SRU  0
+#define RSND_GEN1_ADG  1
+#define RSND_GEN1_SSI  2
+
+#define RSND_GEN2_SCU  0
+#define RSND_GEN2_ADG  1
+#define RSND_GEN2_SSIU 2
+#define RSND_GEN2_SSI  3
+
+#define RSND_BASE_MAX  4
+
+/*
+ * flags
+ *
+ * 0xAB000000
+ *
+ * A : clock sharing settings
+ * B : SSI direction
+ */
+#define RSND_SSI_CLK_PIN_SHARE         (1 << 31)
+#define RSND_SSI_NO_BUSIF              (1 << 30) /* SSI+DMA without BUSIF */
+
+#define RSND_SSI(_dma_id, _irq, _flags)                \
+{ .dma_id = _dma_id, .irq = _irq, .flags = _flags }
+#define RSND_SSI_UNUSED \
+{ .dma_id = -1, .irq = -1, .flags = 0 }
+
+struct rsnd_ssi_platform_info {
+       int dma_id;
+       int irq;
+       u32 flags;
+};
+
+#define RSND_SRC(rate, _dma_id)                                                \
+{ .convert_rate = rate, .dma_id = _dma_id, }
+#define RSND_SRC_UNUSED                                \
+{ .convert_rate = 0, .dma_id = -1, }
+
+struct rsnd_src_platform_info {
+       u32 convert_rate; /* sampling rate convert */
+       int dma_id; /* for Gen2 SCU */
+       int irq;
+};
+
+/*
+ * flags
+ */
+struct rsnd_ctu_platform_info {
+       u32 flags;
+};
+
+struct rsnd_mix_platform_info {
+       u32 flags;
+};
+
+struct rsnd_dvc_platform_info {
+       u32 flags;
+};
+
+struct rsnd_dai_path_info {
+       struct rsnd_ssi_platform_info *ssi;
+       struct rsnd_src_platform_info *src;
+       struct rsnd_ctu_platform_info *ctu;
+       struct rsnd_mix_platform_info *mix;
+       struct rsnd_dvc_platform_info *dvc;
+};
+
+struct rsnd_dai_platform_info {
+       struct rsnd_dai_path_info playback;
+       struct rsnd_dai_path_info capture;
+};
+
+/*
+ * flags
+ *
+ * 0x0000000A
+ *
+ * A : generation
+ */
+#define RSND_GEN_MASK  (0xF << 0)
+#define RSND_GEN1      (1 << 0) /* fixme */
+#define RSND_GEN2      (2 << 0) /* fixme */
+
+struct rcar_snd_info {
+       u32 flags;
+       struct rsnd_ssi_platform_info *ssi_info;
+       int ssi_info_nr;
+       struct rsnd_src_platform_info *src_info;
+       int src_info_nr;
+       struct rsnd_ctu_platform_info *ctu_info;
+       int ctu_info_nr;
+       struct rsnd_mix_platform_info *mix_info;
+       int mix_info_nr;
+       struct rsnd_dvc_platform_info *dvc_info;
+       int dvc_info_nr;
+       struct rsnd_dai_platform_info *dai_info;
+       int dai_info_nr;
+       int (*start)(int id);
+       int (*stop)(int id);
+};
+
+#endif
index 7a0e52b4640a5a39a0bfb3f1d496d71919e0806c..08532987852516cea79ed04051bf9b7ba62f3e51 100644 (file)
 #include <linux/of_irq.h>
 #include <linux/sh_dma.h>
 #include <linux/workqueue.h>
-#include <sound/rcar_snd.h>
 #include <sound/soc.h>
 #include <sound/pcm_params.h>
 
+#include "rcar_snd.h"
+
 /*
  *     pseudo register
  *
@@ -214,6 +215,7 @@ struct rsnd_dma {
 };
 #define rsnd_dma_to_dmaen(dma) (&(dma)->dma.en)
 #define rsnd_dma_to_dmapp(dma) (&(dma)->dma.pp)
+#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
 
 void rsnd_dma_start(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
 void rsnd_dma_stop(struct rsnd_dai_stream *io, struct rsnd_dma *dma);
@@ -225,8 +227,6 @@ int rsnd_dma_probe(struct platform_device *pdev,
 struct dma_chan *rsnd_dma_request_channel(struct device_node *of_node,
                                          struct rsnd_mod *mod, char *name);
 
-#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
-
 /*
  *     R-Car sound mod
  */
@@ -330,8 +330,9 @@ struct rsnd_mod {
 #define rsnd_mod_to_priv(mod) ((mod)->priv)
 #define rsnd_mod_to_dma(mod) (&(mod)->dma)
 #define rsnd_mod_id(mod) ((mod) ? (mod)->id : -1)
-#define rsnd_mod_hw_start(mod) clk_enable((mod)->clk)
-#define rsnd_mod_hw_stop(mod)  clk_disable((mod)->clk)
+#define rsnd_mod_power_on(mod) clk_enable((mod)->clk)
+#define rsnd_mod_power_off(mod)        clk_disable((mod)->clk)
+#define rsnd_mod_get(ip)       (&(ip)->mod)
 
 int rsnd_mod_init(struct rsnd_priv *priv,
                  struct rsnd_mod *mod,
@@ -571,9 +572,12 @@ int rsnd_ssi_probe(struct platform_device *pdev,
 void rsnd_ssi_remove(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);
-int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod);
+int rsnd_ssi_use_busif(struct rsnd_dai_stream *io);
+
+#define rsnd_ssi_is_pin_sharing(io)    \
+       __rsnd_ssi_is_pin_sharing(rsnd_io_to_mod_ssi(io))
+int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod);
 
 /*
  *     R-Car SRC
@@ -627,4 +631,15 @@ void rsnd_dvc_remove(struct platform_device *pdev,
                     struct rsnd_priv *priv);
 struct rsnd_mod *rsnd_dvc_mod_get(struct rsnd_priv *priv, int id);
 
+#ifdef DEBUG
+void rsnd_mod_make_sure(struct rsnd_mod *mod, enum rsnd_mod_type type);
+#define rsnd_mod_confirm_ssi(mssi)     rsnd_mod_make_sure(mssi, RSND_MOD_SSI)
+#define rsnd_mod_confirm_src(msrc)     rsnd_mod_make_sure(msrc, RSND_MOD_SRC)
+#define rsnd_mod_confirm_dvc(mdvc)     rsnd_mod_make_sure(mdvc, RSND_MOD_DVC)
+#else
+#define rsnd_mod_confirm_ssi(mssi)
+#define rsnd_mod_confirm_src(msrc)
+#define rsnd_mod_confirm_dvc(mdvc)
+#endif
+
 #endif
index 89a18e102feb100b57350024a3d548261e91243e..261b50217c48d94f0a66f44c513f685755ddae07 100644 (file)
@@ -159,7 +159,7 @@ int rsnd_src_ssiu_start(struct rsnd_mod *ssi_mod,
        /*
         * SSI_MODE1
         */
-       if (rsnd_ssi_is_pin_sharing(ssi_mod)) {
+       if (rsnd_ssi_is_pin_sharing(io)) {
                int shift = -1;
                switch (ssi_id) {
                case 1:
@@ -352,7 +352,7 @@ static int rsnd_src_init(struct rsnd_mod *mod,
 {
        struct rsnd_src *src = rsnd_mod_to_src(mod);
 
-       rsnd_mod_hw_start(mod);
+       rsnd_mod_power_on(mod);
 
        rsnd_src_soft_reset(mod);
 
@@ -373,7 +373,7 @@ static int rsnd_src_quit(struct rsnd_mod *mod,
        struct rsnd_src *src = rsnd_mod_to_src(mod);
        struct device *dev = rsnd_priv_to_dev(priv);
 
-       rsnd_mod_hw_stop(mod);
+       rsnd_mod_power_off(mod);
 
        if (src->err)
                dev_warn(dev, "%s[%d] under/over flow err = %d\n",
@@ -918,11 +918,10 @@ static void rsnd_src_reconvert_update(struct rsnd_dai_stream *io,
        rsnd_mod_write(mod, SRC_IFSVR, fsrate);
 }
 
-static int rsnd_src_pcm_new(struct rsnd_mod *mod,
+static int rsnd_src_pcm_new_gen2(struct rsnd_mod *mod,
                            struct rsnd_dai_stream *io,
                            struct snd_soc_pcm_runtime *rtd)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
        struct rsnd_src *src = rsnd_mod_to_src(mod);
        int ret;
@@ -931,12 +930,6 @@ static int rsnd_src_pcm_new(struct rsnd_mod *mod,
         * enable SRC sync convert if possible
         */
 
-       /*
-        * Gen1 is not supported
-        */
-       if (rsnd_is_gen1(priv))
-               return 0;
-
        /*
         * SRC sync convert needs clock master
         */
@@ -975,7 +968,7 @@ static struct rsnd_mod_ops rsnd_src_gen2_ops = {
        .start  = rsnd_src_start_gen2,
        .stop   = rsnd_src_stop_gen2,
        .hw_params = rsnd_src_hw_params,
-       .pcm_new = rsnd_src_pcm_new,
+       .pcm_new = rsnd_src_pcm_new_gen2,
 };
 
 struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
@@ -983,7 +976,7 @@ struct rsnd_mod *rsnd_src_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_src_nr(priv)))
                id = 0;
 
-       return &((struct rsnd_src *)(priv->src) + id)->mod;
+       return rsnd_mod_get((struct rsnd_src *)(priv->src) + id);
 }
 
 static void rsnd_of_parse_src(struct platform_device *pdev,
@@ -1043,8 +1036,10 @@ int rsnd_src_probe(struct platform_device *pdev,
        int i, nr, ret;
 
        ops = NULL;
-       if (rsnd_is_gen1(priv))
+       if (rsnd_is_gen1(priv)) {
                ops = &rsnd_src_gen1_ops;
+               dev_warn(dev, "Gen1 support will be removed soon\n");
+       }
        if (rsnd_is_gen2(priv))
                ops = &rsnd_src_gen2_ops;
        if (!ops) {
@@ -1078,7 +1073,7 @@ int rsnd_src_probe(struct platform_device *pdev,
 
                src->info = &info->src_info[i];
 
-               ret = rsnd_mod_init(priv, &src->mod, ops, clk, RSND_MOD_SRC, i);
+               ret = rsnd_mod_init(priv, rsnd_mod_get(src), ops, clk, RSND_MOD_SRC, i);
                if (ret)
                        return ret;
        }
@@ -1093,6 +1088,6 @@ void rsnd_src_remove(struct platform_device *pdev,
        int i;
 
        for_each_rsnd_src(src, priv, i) {
-               rsnd_mod_quit(&src->mod);
+               rsnd_mod_quit(rsnd_mod_get(src));
        }
 }
index d45b9a7e324efb49a5159417232c9f43815ebfed..1427ec21bd7ee1f581ef3ef4109716e3175ee066 100644 (file)
@@ -79,7 +79,6 @@ 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->irq > 0)
 #define rsnd_ssi_parent(ssi) ((ssi)->parent)
 #define rsnd_ssi_mode_flags(p) ((p)->info->flags)
@@ -87,8 +86,9 @@ struct rsnd_ssi {
 #define rsnd_ssi_of_node(priv) \
        of_get_child_by_name(rsnd_priv_to_dev(priv)->of_node, "rcar_sound,ssi")
 
-int rsnd_ssi_use_busif(struct rsnd_dai_stream *io, struct rsnd_mod *mod)
+int rsnd_ssi_use_busif(struct rsnd_dai_stream *io)
 {
+       struct rsnd_mod *mod = rsnd_io_to_mod_ssi(io);
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
        int use_busif = 0;
 
@@ -128,10 +128,8 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
        struct rsnd_priv *priv = rsnd_io_to_priv(io);
        struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
        struct device *dev = rsnd_priv_to_dev(priv);
-       int i, j, ret;
-       int adg_clk_div_table[] = {
-               1, 6, /* see adg.c */
-       };
+       struct rsnd_mod *mod = rsnd_mod_get(ssi);
+       int j, ret;
        int ssi_clk_mul_table[] = {
                1, 2, 4, 8, 16, 6, 12,
        };
@@ -141,28 +139,25 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
        /*
         * Find best clock, and try to start ADG
         */
-       for (i = 0; i < ARRAY_SIZE(adg_clk_div_table); i++) {
-               for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
-
-                       /*
-                        * this driver is assuming that
-                        * system word is 64fs (= 2 x 32bit)
-                        * see rsnd_ssi_init()
-                        */
-                       main_rate = rate / adg_clk_div_table[i]
-                               * 32 * 2 * ssi_clk_mul_table[j];
-
-                       ret = rsnd_adg_ssi_clk_try_start(&ssi->mod, main_rate);
-                       if (0 == ret) {
-                               ssi->cr_clk     = FORCE | SWL_32 |
-                                                 SCKD | SWSD | CKDV(j);
-
-                               dev_dbg(dev, "%s[%d] outputs %u Hz\n",
-                                       rsnd_mod_name(&ssi->mod),
-                                       rsnd_mod_id(&ssi->mod), rate);
-
-                               return 0;
-                       }
+       for (j = 0; j < ARRAY_SIZE(ssi_clk_mul_table); j++) {
+
+               /*
+                * this driver is assuming that
+                * system word is 64fs (= 2 x 32bit)
+                * see rsnd_ssi_init()
+                */
+               main_rate = rate * 32 * 2 * ssi_clk_mul_table[j];
+
+               ret = rsnd_adg_ssi_clk_try_start(mod, main_rate);
+               if (0 == ret) {
+                       ssi->cr_clk     = FORCE | SWL_32 |
+                               SCKD | SWSD | CKDV(j);
+
+                       dev_dbg(dev, "%s[%d] outputs %u Hz\n",
+                               rsnd_mod_name(mod),
+                               rsnd_mod_id(mod), rate);
+
+                       return 0;
                }
        }
 
@@ -172,8 +167,10 @@ static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
 
 static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi)
 {
+       struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
        ssi->cr_clk = 0;
-       rsnd_adg_ssi_clk_stop(&ssi->mod);
+       rsnd_adg_ssi_clk_stop(mod);
 }
 
 static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
@@ -182,11 +179,12 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
        struct rsnd_priv *priv = rsnd_io_to_priv(io);
        struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
        struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_mod *mod = rsnd_mod_get(ssi);
        u32 cr_mode;
        u32 cr;
 
        if (0 == ssi->usrcnt) {
-               rsnd_mod_hw_start(&ssi->mod);
+               rsnd_mod_power_on(mod);
 
                if (rsnd_rdai_is_clk_master(rdai)) {
                        struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
@@ -198,7 +196,7 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
                }
        }
 
-       if (rsnd_ssi_is_dma_mode(&ssi->mod)) {
+       if (rsnd_ssi_is_dma_mode(mod)) {
                cr_mode = UIEN | OIEN | /* over/under run */
                          DMEN;         /* DMA : enable DMA */
        } else {
@@ -210,24 +208,25 @@ static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
                cr_mode         |
                EN;
 
-       rsnd_mod_write(&ssi->mod, SSICR, cr);
+       rsnd_mod_write(mod, SSICR, cr);
 
        /* enable WS continue */
        if (rsnd_rdai_is_clk_master(rdai))
-               rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+               rsnd_mod_write(mod, SSIWSR, CONT);
 
        /* clear error status */
-       rsnd_mod_write(&ssi->mod, SSISR, 0);
+       rsnd_mod_write(mod, SSISR, 0);
 
        ssi->usrcnt++;
 
        dev_dbg(dev, "%s[%d] hw started\n",
-               rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 }
 
 static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
 {
-       struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+       struct rsnd_mod *mod = rsnd_mod_get(ssi);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
        struct rsnd_dai *rdai = rsnd_io_to_rdai(io);
        struct device *dev = rsnd_priv_to_dev(priv);
        u32 cr;
@@ -247,15 +246,15 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
                cr  =   ssi->cr_own     |
                        ssi->cr_clk;
 
-               rsnd_mod_write(&ssi->mod, SSICR, cr | EN);
-               rsnd_ssi_status_check(&ssi->mod, DIRQ);
+               rsnd_mod_write(mod, SSICR, cr | EN);
+               rsnd_ssi_status_check(mod, DIRQ);
 
                /*
                 * disable SSI,
                 * and, wait idle state
                 */
-               rsnd_mod_write(&ssi->mod, SSICR, cr);   /* disabled all */
-               rsnd_ssi_status_check(&ssi->mod, IIRQ);
+               rsnd_mod_write(mod, SSICR, cr); /* disabled all */
+               rsnd_ssi_status_check(mod, IIRQ);
 
                if (rsnd_rdai_is_clk_master(rdai)) {
                        struct rsnd_ssi *ssi_parent = rsnd_ssi_parent(ssi);
@@ -266,13 +265,13 @@ static void rsnd_ssi_hw_stop(struct rsnd_dai_stream *io, struct rsnd_ssi *ssi)
                                rsnd_ssi_master_clk_stop(ssi);
                }
 
-               rsnd_mod_hw_stop(&ssi->mod);
+               rsnd_mod_power_off(mod);
 
                ssi->chan = 0;
        }
 
        dev_dbg(dev, "%s[%d] hw stopped\n",
-               rsnd_mod_name(&ssi->mod), rsnd_mod_id(&ssi->mod));
+               rsnd_mod_name(mod), rsnd_mod_id(mod));
 }
 
 /*
@@ -371,7 +370,7 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
        /* It will be removed on rsnd_ssi_hw_stop */
        ssi->chan = chan;
        if (ssi_parent)
-               return rsnd_ssi_hw_params(&ssi_parent->mod, io,
+               return rsnd_ssi_hw_params(rsnd_mod_get(ssi_parent), io,
                                          substream, params);
 
        return 0;
@@ -379,12 +378,14 @@ static int rsnd_ssi_hw_params(struct rsnd_mod *mod,
 
 static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
 {
+       struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
        /* under/over flow error */
        if (status & (UIRQ | OIRQ)) {
                ssi->err++;
 
                /* clear error status */
-               rsnd_mod_write(&ssi->mod, SSISR, 0);
+               rsnd_mod_write(mod, SSISR, 0);
        }
 }
 
@@ -394,7 +395,7 @@ static int rsnd_ssi_start(struct rsnd_mod *mod,
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 
-       rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io, mod));
+       rsnd_src_ssiu_start(mod, io, rsnd_ssi_use_busif(io));
 
        rsnd_ssi_hw_start(ssi, io);
 
@@ -554,7 +555,7 @@ static int rsnd_ssi_dma_remove(struct rsnd_mod *mod,
        rsnd_dma_quit(io, rsnd_mod_to_dma(mod));
 
        /* PIO will request IRQ again */
-       devm_free_irq(dev, irq, ssi);
+       devm_free_irq(dev, irq, mod);
 
        return 0;
 }
@@ -613,7 +614,7 @@ static struct dma_chan *rsnd_ssi_dma_req(struct rsnd_dai_stream *io,
        int is_play = rsnd_io_is_play(io);
        char *name;
 
-       if (rsnd_ssi_use_busif(io, mod))
+       if (rsnd_ssi_use_busif(io))
                name = is_play ? "rxu" : "txu";
        else
                name = is_play ? "rx" : "tx";
@@ -656,10 +657,10 @@ struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
        if (WARN_ON(id < 0 || id >= rsnd_ssi_nr(priv)))
                id = 0;
 
-       return &((struct rsnd_ssi *)(priv->ssi) + id)->mod;
+       return rsnd_mod_get((struct rsnd_ssi *)(priv->ssi) + id);
 }
 
-int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
+int __rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
 {
        struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
 
@@ -668,10 +669,12 @@ int rsnd_ssi_is_pin_sharing(struct rsnd_mod *mod)
 
 static void rsnd_ssi_parent_setup(struct rsnd_priv *priv, struct rsnd_ssi *ssi)
 {
-       if (!rsnd_ssi_is_pin_sharing(&ssi->mod))
+       struct rsnd_mod *mod = rsnd_mod_get(ssi);
+
+       if (!__rsnd_ssi_is_pin_sharing(mod))
                return;
 
-       switch (rsnd_mod_id(&ssi->mod)) {
+       switch (rsnd_mod_id(mod)) {
        case 1:
        case 2:
                ssi->parent = rsnd_mod_to_ssi(rsnd_ssi_mod_get(priv, 0));
@@ -697,9 +700,6 @@ static void rsnd_of_parse_ssi(struct platform_device *pdev,
        struct device *dev = &pdev->dev;
        int nr, i;
 
-       if (!of_data)
-               return;
-
        node = rsnd_ssi_of_node(priv);
        if (!node)
                return;
@@ -794,7 +794,8 @@ int rsnd_ssi_probe(struct platform_device *pdev,
                else if (rsnd_ssi_pio_available(ssi))
                        ops = &rsnd_ssi_pio_ops;
 
-               ret = rsnd_mod_init(priv, &ssi->mod, ops, clk, RSND_MOD_SSI, i);
+               ret = rsnd_mod_init(priv, rsnd_mod_get(ssi), ops, clk,
+                                   RSND_MOD_SSI, i);
                if (ret)
                        return ret;
 
@@ -811,6 +812,6 @@ void rsnd_ssi_remove(struct platform_device *pdev,
        int i;
 
        for_each_rsnd_ssi(ssi, priv, i) {
-               rsnd_mod_quit(&ssi->mod);
+               rsnd_mod_quit(rsnd_mod_get(ssi));
        }
 }
index abb0d956231ca1235333ffd516595ca8fc5b083d..76b2ab8c2b4a4f964307b9832677ebb8a4961464 100644 (file)
@@ -738,7 +738,7 @@ static int siu_probe(struct platform_device *pdev)
        struct siu_info *info;
        int ret;
 
-       info = kmalloc(sizeof(*info), GFP_KERNEL);
+       info = devm_kmalloc(&pdev->dev, sizeof(*info), GFP_KERNEL);
        if (!info)
                return -ENOMEM;
        siu_i2s_data = info;
@@ -746,7 +746,7 @@ static int siu_probe(struct platform_device *pdev)
 
        ret = request_firmware(&fw_entry, "siu_spb.bin", &pdev->dev);
        if (ret)
-               goto ereqfw;
+               return ret;
 
        /*
         * Loaded firmware is "const" - read only, but we have to modify it in
@@ -757,89 +757,52 @@ static int siu_probe(struct platform_device *pdev)
        release_firmware(fw_entry);
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res) {
-               ret = -ENODEV;
-               goto egetres;
-       }
+       if (!res)
+               return -ENODEV;
 
-       region = request_mem_region(res->start, resource_size(res),
-                                   pdev->name);
+       region = devm_request_mem_region(&pdev->dev, res->start,
+                                        resource_size(res), pdev->name);
        if (!region) {
                dev_err(&pdev->dev, "SIU region already claimed\n");
-               ret = -EBUSY;
-               goto ereqmemreg;
+               return -EBUSY;
        }
 
-       ret = -ENOMEM;
-       info->pram = ioremap(res->start, PRAM_SIZE);
+       info->pram = devm_ioremap(&pdev->dev, res->start, PRAM_SIZE);
        if (!info->pram)
-               goto emappram;
-       info->xram = ioremap(res->start + XRAM_OFFSET, XRAM_SIZE);
+               return -ENOMEM;
+       info->xram = devm_ioremap(&pdev->dev, res->start + XRAM_OFFSET,
+                                 XRAM_SIZE);
        if (!info->xram)
-               goto emapxram;
-       info->yram = ioremap(res->start + YRAM_OFFSET, YRAM_SIZE);
+               return -ENOMEM;
+       info->yram = devm_ioremap(&pdev->dev, res->start + YRAM_OFFSET,
+                                 YRAM_SIZE);
        if (!info->yram)
-               goto emapyram;
-       info->reg = ioremap(res->start + REG_OFFSET, resource_size(res) -
-                           REG_OFFSET);
+               return -ENOMEM;
+       info->reg = devm_ioremap(&pdev->dev, res->start + REG_OFFSET,
+                           resource_size(res) - REG_OFFSET);
        if (!info->reg)
-               goto emapreg;
+               return -ENOMEM;
 
        dev_set_drvdata(&pdev->dev, info);
 
        /* register using ARRAY version so we can keep dai name */
-       ret = snd_soc_register_component(&pdev->dev, &siu_i2s_component,
-                                        &siu_i2s_dai, 1);
+       ret = devm_snd_soc_register_component(&pdev->dev, &siu_i2s_component,
+                                             &siu_i2s_dai, 1);
        if (ret < 0)
-               goto edaiinit;
+               return ret;
 
-       ret = snd_soc_register_platform(&pdev->dev, &siu_platform);
+       ret = devm_snd_soc_register_platform(&pdev->dev, &siu_platform);
        if (ret < 0)
-               goto esocregp;
+               return ret;
 
        pm_runtime_enable(&pdev->dev);
 
-       return ret;
-
-esocregp:
-       snd_soc_unregister_component(&pdev->dev);
-edaiinit:
-       iounmap(info->reg);
-emapreg:
-       iounmap(info->yram);
-emapyram:
-       iounmap(info->xram);
-emapxram:
-       iounmap(info->pram);
-emappram:
-       release_mem_region(res->start, resource_size(res));
-ereqmemreg:
-egetres:
-ereqfw:
-       kfree(info);
-
-       return ret;
+       return 0;
 }
 
 static int siu_remove(struct platform_device *pdev)
 {
-       struct siu_info *info = dev_get_drvdata(&pdev->dev);
-       struct resource *res;
-
        pm_runtime_disable(&pdev->dev);
-
-       snd_soc_unregister_platform(&pdev->dev);
-       snd_soc_unregister_component(&pdev->dev);
-
-       iounmap(info->reg);
-       iounmap(info->yram);
-       iounmap(info->xram);
-       iounmap(info->pram);
-       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res)
-               release_mem_region(res->start, resource_size(res));
-       kfree(info);
-
        return 0;
 }
 
index 025c38fbe3c03fea08db0b365e472902c5f110fc..12a9820feac1548d5c3d3cbc50e0a8cc2bfefc25 100644 (file)
@@ -612,8 +612,15 @@ static struct snd_compr_ops soc_compr_dyn_ops = {
        .get_codec_caps = soc_compr_get_codec_caps
 };
 
-/* create a new compress */
-int soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
+/**
+ * snd_soc_new_compress - create a new compress.
+ *
+ * @rtd: The runtime for which we will create compress
+ * @num: the device index number (zero based - shared with normal PCMs)
+ *
+ * Return: 0 for success, else error.
+ */
+int snd_soc_new_compress(struct snd_soc_pcm_runtime *rtd, int num)
 {
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_platform *platform = rtd->platform;
@@ -703,3 +710,4 @@ compr_err:
        kfree(compr);
        return ret;
 }
+EXPORT_SYMBOL_GPL(snd_soc_new_compress);
index 6173d15236c3c0c2ce53c3c31b721310f9576e9e..24b096066a07205c88e377e28f94eacff1261f3e 100644 (file)
@@ -1370,9 +1370,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
                soc_dpcm_debugfs_add(rtd);
 #endif
 
-       if (cpu_dai->driver->compress_dai) {
+       if (cpu_dai->driver->compress_new) {
                /*create compress_device"*/
-               ret = soc_new_compress(rtd, num);
+               ret = cpu_dai->driver->compress_new(rtd, num);
                if (ret < 0) {
                        dev_err(card->dev, "ASoC: can't create compress %s\n",
                                         dai_link->stream_name);
@@ -3291,13 +3291,38 @@ int snd_soc_of_parse_audio_simple_widgets(struct snd_soc_card *card,
 }
 EXPORT_SYMBOL_GPL(snd_soc_of_parse_audio_simple_widgets);
 
+static int snd_soc_of_get_slot_mask(struct device_node *np,
+                                   const char *prop_name,
+                                   unsigned int *mask)
+{
+       u32 val;
+       const __be32 *of_slot_mask = of_get_property(np, prop_name, &val);
+       int i;
+
+       if (!of_slot_mask)
+               return 0;
+       val /= sizeof(u32);
+       for (i = 0; i < val; i++)
+               if (be32_to_cpup(&of_slot_mask[i]))
+                       *mask |= (1 << i);
+
+       return val;
+}
+
 int snd_soc_of_parse_tdm_slot(struct device_node *np,
+                             unsigned int *tx_mask,
+                             unsigned int *rx_mask,
                              unsigned int *slots,
                              unsigned int *slot_width)
 {
        u32 val;
        int ret;
 
+       if (tx_mask)
+               snd_soc_of_get_slot_mask(np, "dai-tdm-slot-tx-mask", tx_mask);
+       if (rx_mask)
+               snd_soc_of_get_slot_mask(np, "dai-tdm-slot-rx-mask", rx_mask);
+
        if (of_property_read_bool(np, "dai-tdm-slot-num")) {
                ret = of_property_read_u32(np, "dai-tdm-slot-num", &val);
                if (ret)
index ff8bda471b2531fede57ea0dd225bc4081d5ba16..016eba10b1ec2744a8b9b1b3492d6c0fa1c7a561 100644 (file)
@@ -508,6 +508,18 @@ static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
        return true;
 }
 
+/**
+ * snd_soc_dapm_kcontrol_widget() - Returns the widget associated to a
+ *   kcontrol
+ * @kcontrol: The kcontrol
+ */
+struct snd_soc_dapm_widget *snd_soc_dapm_kcontrol_widget(
+                               struct snd_kcontrol *kcontrol)
+{
+       return dapm_kcontrol_get_wlist(kcontrol)->widgets[0];
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_widget);
+
 /**
  * snd_soc_dapm_kcontrol_dapm() - Returns the dapm context associated to a
  *  kcontrol
@@ -779,7 +791,7 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
  * Determine if a kcontrol is shared. If it is, look it up. If it isn't,
  * create it. Either way, add the widget into the control's widget list
  */
-static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
+static int dapm_create_or_share_kcontrol(struct snd_soc_dapm_widget *w,
        int kci)
 {
        struct snd_soc_dapm_context *dapm = w->dapm;
@@ -810,6 +822,7 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
                        switch (w->id) {
                        case snd_soc_dapm_switch:
                        case snd_soc_dapm_mixer:
+                       case snd_soc_dapm_pga:
                                wname_in_long_name = true;
                                kcname_in_long_name = true;
                                break;
@@ -899,7 +912,7 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
                                continue;
 
                        if (!w->kcontrols[i]) {
-                               ret = dapm_create_or_share_mixmux_kcontrol(w, i);
+                               ret = dapm_create_or_share_kcontrol(w, i);
                                if (ret < 0)
                                        return ret;
                        }
@@ -952,7 +965,7 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
                return -EINVAL;
        }
 
-       ret = dapm_create_or_share_mixmux_kcontrol(w, 0);
+       ret = dapm_create_or_share_kcontrol(w, 0);
        if (ret < 0)
                return ret;
 
@@ -967,9 +980,13 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
 /* create new dapm volume control */
 static int dapm_new_pga(struct snd_soc_dapm_widget *w)
 {
-       if (w->num_kcontrols)
-               dev_err(w->dapm->dev,
-                       "ASoC: PGA controls not supported: '%s'\n", w->name);
+       int i, ret;
+
+       for (i = 0; i < w->num_kcontrols; i++) {
+               ret = dapm_create_or_share_kcontrol(w, i);
+               if (ret < 0)
+                       return ret;
+       }
 
        return 0;
 }
@@ -3473,11 +3490,29 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
        switch (event) {
        case SND_SOC_DAPM_PRE_PMU:
                substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+               if (source->driver->ops && source->driver->ops->startup) {
+                       ret = source->driver->ops->startup(&substream, source);
+                       if (ret < 0) {
+                               dev_err(source->dev,
+                                       "ASoC: startup() failed: %d\n", ret);
+                               goto out;
+                       }
+                       source->active++;
+               }
                ret = soc_dai_hw_params(&substream, params, source);
                if (ret < 0)
                        goto out;
 
                substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+               if (sink->driver->ops && sink->driver->ops->startup) {
+                       ret = sink->driver->ops->startup(&substream, sink);
+                       if (ret < 0) {
+                               dev_err(sink->dev,
+                                       "ASoC: startup() failed: %d\n", ret);
+                               goto out;
+                       }
+                       sink->active++;
+               }
                ret = soc_dai_hw_params(&substream, params, sink);
                if (ret < 0)
                        goto out;
@@ -3497,6 +3532,18 @@ static int snd_soc_dai_link_event(struct snd_soc_dapm_widget *w,
                if (ret != 0 && ret != -ENOTSUPP)
                        dev_warn(sink->dev, "ASoC: Failed to mute: %d\n", ret);
                ret = 0;
+
+               source->active--;
+               if (source->driver->ops && source->driver->ops->shutdown) {
+                       substream.stream = SNDRV_PCM_STREAM_CAPTURE;
+                       source->driver->ops->shutdown(&substream, source);
+               }
+
+               sink->active--;
+               if (sink->driver->ops && sink->driver->ops->shutdown) {
+                       substream.stream = SNDRV_PCM_STREAM_PLAYBACK;
+                       sink->driver->ops->shutdown(&substream, sink);
+               }
                break;
 
        default:
index 05977ae1ff2a3250c3fe2043b8124f9370dd1542..ecd38e52285a964d53fce4c5c0d4ae71fd4bc15d 100644 (file)
@@ -588,16 +588,16 @@ 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
+ * @card: 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,
+int snd_soc_limit_volume(struct snd_soc_card *card,
        const char *name, int max)
 {
-       struct snd_card *card = codec->component.card->snd_card;
+       struct snd_card *snd_card = card->snd_card;
        struct snd_kcontrol *kctl;
        struct soc_mixer_control *mc;
        int found = 0;
@@ -607,7 +607,7 @@ int snd_soc_limit_volume(struct snd_soc_codec *codec,
        if (unlikely(!name || max <= 0))
                return -EINVAL;
 
-       list_for_each_entry(kctl, &card->controls, list) {
+       list_for_each_entry(kctl, &snd_card->controls, list) {
                if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name))) {
                        found = 1;
                        break;
index 70e4b9d8bdcdfd3fb83f37615a29fae64036d4c0..c86dc96e8986f39cd08bcf7e92c63f49459d50f2 100644 (file)
 
 #define DPCM_MAX_BE_USERS      8
 
+/*
+ * snd_soc_dai_stream_valid() - check if a DAI supports the given stream
+ *
+ * Returns true if the DAI supports the indicated stream type.
+ */
+static bool snd_soc_dai_stream_valid(struct snd_soc_dai *dai, int stream)
+{
+       struct snd_soc_pcm_stream *codec_stream;
+
+       if (stream == SNDRV_PCM_STREAM_PLAYBACK)
+               codec_stream = &dai->driver->playback;
+       else
+               codec_stream = &dai->driver->capture;
+
+       /* If the codec specifies any rate at all, it supports the stream. */
+       return codec_stream->rates;
+}
+
 /**
  * snd_soc_runtime_activate() - Increment active count for PCM runtime components
  * @rtd: ASoC PCM runtime that is activated
@@ -182,9 +200,9 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
                dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %dHz rate\n",
                                soc_dai->rate);
 
-               ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+               ret = snd_pcm_hw_constraint_single(substream->runtime,
                                                SNDRV_PCM_HW_PARAM_RATE,
-                                               soc_dai->rate, soc_dai->rate);
+                                               soc_dai->rate);
                if (ret < 0) {
                        dev_err(soc_dai->dev,
                                "ASoC: Unable to apply rate constraint: %d\n",
@@ -198,9 +216,8 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
                dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d channel(s)\n",
                                soc_dai->channels);
 
-               ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+               ret = snd_pcm_hw_constraint_single(substream->runtime,
                                                SNDRV_PCM_HW_PARAM_CHANNELS,
-                                               soc_dai->channels,
                                                soc_dai->channels);
                if (ret < 0) {
                        dev_err(soc_dai->dev,
@@ -215,9 +232,8 @@ static int soc_pcm_apply_symmetry(struct snd_pcm_substream *substream,
                dev_dbg(soc_dai->dev, "ASoC: Symmetry forces %d sample bits\n",
                                soc_dai->sample_bits);
 
-               ret = snd_pcm_hw_constraint_minmax(substream->runtime,
+               ret = snd_pcm_hw_constraint_single(substream->runtime,
                                                SNDRV_PCM_HW_PARAM_SAMPLE_BITS,
-                                               soc_dai->sample_bits,
                                                soc_dai->sample_bits);
                if (ret < 0) {
                        dev_err(soc_dai->dev,
@@ -371,6 +387,20 @@ static void soc_pcm_init_runtime_hw(struct snd_pcm_substream *substream)
 
        /* first calculate min/max only for CODECs in the DAI link */
        for (i = 0; i < rtd->num_codecs; i++) {
+
+               /*
+                * Skip CODECs which don't support the current stream type.
+                * Otherwise, since the rate, channel, and format values will
+                * zero in that case, we would have no usable settings left,
+                * causing the resulting setup to fail.
+                * At least one CODEC should match, otherwise we should have
+                * bailed out on a higher level, since there would be no
+                * CODEC to support the transfer direction in that case.
+                */
+               if (!snd_soc_dai_stream_valid(rtd->codec_dais[i],
+                                             substream->stream))
+                       continue;
+
                codec_dai_drv = rtd->codec_dais[i]->driver;
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                        codec_stream = &codec_dai_drv->playback;
@@ -827,6 +857,23 @@ static int soc_pcm_hw_params(struct snd_pcm_substream *substream,
                struct snd_soc_dai *codec_dai = rtd->codec_dais[i];
                struct snd_pcm_hw_params codec_params;
 
+               /*
+                * Skip CODECs which don't support the current stream type,
+                * the idea being that if a CODEC is not used for the currently
+                * set up transfer direction, it should not need to be
+                * configured, especially since the configuration used might
+                * not even be supported by that CODEC. There may be cases
+                * however where a CODEC needs to be set up although it is
+                * actually not being used for the transfer, e.g. if a
+                * capture-only CODEC is acting as an LRCLK and/or BCLK master
+                * for the DAI link including a playback-only CODEC.
+                * If this becomes necessary, we will have to augment the
+                * machine driver setup with information on how to act, so
+                * we can do the right thing here.
+                */
+               if (!snd_soc_dai_stream_valid(codec_dai, substream->stream))
+                       continue;
+
                /* copy params for each codec */
                codec_params = *params;
 
index 69d01cd925ce28fb5fc5e798880f391ec207fff0..8d7ec80af51b499738dc2032356896a08144078a 100644 (file)
@@ -1558,7 +1558,7 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg,
        pcm_dai = (struct snd_soc_tplg_pcm_dai *)tplg->pos;
 
        if (soc_tplg_check_elem_count(tplg,
-               sizeof(struct snd_soc_tplg_pcm_dai), count,
+               sizeof(struct snd_soc_tplg_pcm), count,
                hdr->payload_size, "PCM DAI")) {
                dev_err(tplg->dev, "ASoC: invalid count %d for PCM DAI elems\n",
                        count);
@@ -1566,7 +1566,7 @@ static int soc_tplg_pcm_dai_elems_load(struct soc_tplg *tplg,
        }
 
        dev_dbg(tplg->dev, "ASoC: adding %d PCM DAIs\n", count);
-       tplg->pos += sizeof(struct snd_soc_tplg_pcm_dai) * count;
+       tplg->pos += sizeof(struct snd_soc_tplg_pcm) * count;
 
        dobj = kzalloc(sizeof(struct snd_soc_dobj), GFP_KERNEL);
        if (dobj == NULL)
diff --git a/sound/soc/sunxi/Kconfig b/sound/soc/sunxi/Kconfig
new file mode 100644 (file)
index 0000000..84c72ec
--- /dev/null
@@ -0,0 +1,11 @@
+menu "Allwinner SoC Audio support"
+
+config SND_SUN4I_CODEC
+       tristate "Allwinner A10 Codec Support"
+       select SND_SOC_GENERIC_DMAENGINE_PCM
+       select REGMAP_MMIO
+       help
+         Select Y or M to add support for the Codec embedded in the Allwinner
+         A10 and affiliated SoCs.
+
+endmenu
diff --git a/sound/soc/sunxi/Makefile b/sound/soc/sunxi/Makefile
new file mode 100644 (file)
index 0000000..ea8a08c
--- /dev/null
@@ -0,0 +1,2 @@
+obj-$(CONFIG_SND_SUN4I_CODEC) += sun4i-codec.o
+
diff --git a/sound/soc/sunxi/sun4i-codec.c b/sound/soc/sunxi/sun4i-codec.c
new file mode 100644 (file)
index 0000000..bcbf4da
--- /dev/null
@@ -0,0 +1,712 @@
+/*
+ * Copyright 2014 Emilio López <emilio@elopez.com.ar>
+ * Copyright 2014 Jon Smirl <jonsmirl@gmail.com>
+ * Copyright 2015 Maxime Ripard <maxime.ripard@free-electrons.com>
+ *
+ * Based on the Allwinner SDK driver, released under the GPL.
+ *
+ * This program is free software; you can redistribute it and/or modify
+ * it under the terms of the GNU General Public License as published by
+ * the Free Software Foundation; either version 2 of the License, or
+ * (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/init.h>
+#include <linux/kernel.h>
+#include <linux/module.h>
+#include <linux/platform_device.h>
+#include <linux/delay.h>
+#include <linux/slab.h>
+#include <linux/of.h>
+#include <linux/of_platform.h>
+#include <linux/of_address.h>
+#include <linux/clk.h>
+#include <linux/regmap.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <sound/initval.h>
+#include <sound/dmaengine_pcm.h>
+
+/* Codec DAC register offsets and bit fields */
+#define SUN4I_CODEC_DAC_DPC                    (0x00)
+#define SUN4I_CODEC_DAC_DPC_EN_DA                      (31)
+#define SUN4I_CODEC_DAC_DPC_DVOL                       (12)
+#define SUN4I_CODEC_DAC_FIFOC                  (0x04)
+#define SUN4I_CODEC_DAC_FIFOC_DAC_FS                   (29)
+#define SUN4I_CODEC_DAC_FIFOC_FIR_VERSION              (28)
+#define SUN4I_CODEC_DAC_FIFOC_SEND_LASAT               (26)
+#define SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE             (24)
+#define SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT              (21)
+#define SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL            (8)
+#define SUN4I_CODEC_DAC_FIFOC_MONO_EN                  (6)
+#define SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS           (5)
+#define SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN               (4)
+#define SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH               (0)
+#define SUN4I_CODEC_DAC_FIFOS                  (0x08)
+#define SUN4I_CODEC_DAC_TXDATA                 (0x0c)
+#define SUN4I_CODEC_DAC_ACTL                   (0x10)
+#define SUN4I_CODEC_DAC_ACTL_DACAENR                   (31)
+#define SUN4I_CODEC_DAC_ACTL_DACAENL                   (30)
+#define SUN4I_CODEC_DAC_ACTL_MIXEN                     (29)
+#define SUN4I_CODEC_DAC_ACTL_LDACLMIXS                 (15)
+#define SUN4I_CODEC_DAC_ACTL_RDACRMIXS                 (14)
+#define SUN4I_CODEC_DAC_ACTL_LDACRMIXS                 (13)
+#define SUN4I_CODEC_DAC_ACTL_DACPAS                    (8)
+#define SUN4I_CODEC_DAC_ACTL_MIXPAS                    (7)
+#define SUN4I_CODEC_DAC_ACTL_PA_MUTE                   (6)
+#define SUN4I_CODEC_DAC_ACTL_PA_VOL                    (0)
+#define SUN4I_CODEC_DAC_TUNE                   (0x14)
+#define SUN4I_CODEC_DAC_DEBUG                  (0x18)
+
+/* Codec ADC register offsets and bit fields */
+#define SUN4I_CODEC_ADC_FIFOC                  (0x1c)
+#define SUN4I_CODEC_ADC_FIFOC_EN_AD                    (28)
+#define SUN4I_CODEC_ADC_FIFOC_RX_FIFO_MODE             (24)
+#define SUN4I_CODEC_ADC_FIFOC_RX_TRIG_LEVEL            (8)
+#define SUN4I_CODEC_ADC_FIFOC_MONO_EN                  (7)
+#define SUN4I_CODEC_ADC_FIFOC_RX_SAMPLE_BITS           (6)
+#define SUN4I_CODEC_ADC_FIFOC_ADC_DRQ_EN               (4)
+#define SUN4I_CODEC_ADC_FIFOC_FIFO_FLUSH               (0)
+#define SUN4I_CODEC_ADC_FIFOS                  (0x20)
+#define SUN4I_CODEC_ADC_RXDATA                 (0x24)
+#define SUN4I_CODEC_ADC_ACTL                   (0x28)
+#define SUN4I_CODEC_ADC_ACTL_ADC_R_EN                  (31)
+#define SUN4I_CODEC_ADC_ACTL_ADC_L_EN                  (30)
+#define SUN4I_CODEC_ADC_ACTL_PREG1EN                   (29)
+#define SUN4I_CODEC_ADC_ACTL_PREG2EN                   (28)
+#define SUN4I_CODEC_ADC_ACTL_VMICEN                    (27)
+#define SUN4I_CODEC_ADC_ACTL_VADCG                     (20)
+#define SUN4I_CODEC_ADC_ACTL_ADCIS                     (17)
+#define SUN4I_CODEC_ADC_ACTL_PA_EN                     (4)
+#define SUN4I_CODEC_ADC_ACTL_DDE                       (3)
+#define SUN4I_CODEC_ADC_DEBUG                  (0x2c)
+
+/* Other various ADC registers */
+#define SUN4I_CODEC_DAC_TXCNT                  (0x30)
+#define SUN4I_CODEC_ADC_RXCNT                  (0x34)
+#define SUN4I_CODEC_AC_SYS_VERI                        (0x38)
+#define SUN4I_CODEC_AC_MIC_PHONE_CAL           (0x3c)
+
+struct sun4i_codec {
+       struct device   *dev;
+       struct regmap   *regmap;
+       struct clk      *clk_apb;
+       struct clk      *clk_module;
+
+       struct snd_dmaengine_dai_dma_data       playback_dma_data;
+};
+
+static void sun4i_codec_start_playback(struct sun4i_codec *scodec)
+{
+       /*
+        * FIXME: according to the BSP, we might need to drive a PA
+        *        GPIO high here on some boards
+        */
+
+       /* Flush TX FIFO */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
+                          BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
+
+       /* Enable DAC DRQ */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
+                          BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN));
+}
+
+static void sun4i_codec_stop_playback(struct sun4i_codec *scodec)
+{
+       /*
+        * FIXME: according to the BSP, we might need to drive a PA
+        *        GPIO low here on some boards
+        */
+
+       /* Disable DAC DRQ */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_DAC_DRQ_EN),
+                          0);
+}
+
+static int sun4i_codec_trigger(struct snd_pcm_substream *substream, int cmd,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+               return -ENOTSUPP;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               sun4i_codec_start_playback(scodec);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               sun4i_codec_stop_playback(scodec);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+static int sun4i_codec_prepare(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+       u32 val;
+
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+               return -ENOTSUPP;
+
+       /* Flush the TX FIFO */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH),
+                          BIT(SUN4I_CODEC_DAC_FIFOC_FIFO_FLUSH));
+
+       /* Set TX FIFO Empty Trigger Level */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          0x3f << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL,
+                          0xf << SUN4I_CODEC_DAC_FIFOC_TX_TRIG_LEVEL);
+
+       if (substream->runtime->rate > 32000)
+               /* Use 64 bits FIR filter */
+               val = 0;
+       else
+               /* Use 32 bits FIR filter */
+               val = BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION);
+
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_FIR_VERSION),
+                          val);
+
+       /* Send zeros when we have an underrun */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_SEND_LASAT),
+                          0);
+
+       return 0;
+}
+
+static unsigned long sun4i_codec_get_mod_freq(struct snd_pcm_hw_params *params)
+{
+       unsigned int rate = params_rate(params);
+
+       switch (rate) {
+       case 176400:
+       case 88200:
+       case 44100:
+       case 33075:
+       case 22050:
+       case 14700:
+       case 11025:
+       case 7350:
+               return 22579200;
+
+       case 192000:
+       case 96000:
+       case 48000:
+       case 32000:
+       case 24000:
+       case 16000:
+       case 12000:
+       case 8000:
+               return 24576000;
+
+       default:
+               return 0;
+       }
+}
+
+static int sun4i_codec_get_hw_rate(struct snd_pcm_hw_params *params)
+{
+       unsigned int rate = params_rate(params);
+
+       switch (rate) {
+       case 192000:
+       case 176400:
+               return 6;
+
+       case 96000:
+       case 88200:
+               return 7;
+
+       case 48000:
+       case 44100:
+               return 0;
+
+       case 32000:
+       case 33075:
+               return 1;
+
+       case 24000:
+       case 22050:
+               return 2;
+
+       case 16000:
+       case 14700:
+               return 3;
+
+       case 12000:
+       case 11025:
+               return 4;
+
+       case 8000:
+       case 7350:
+               return 5;
+
+       default:
+               return -EINVAL;
+       }
+}
+
+static int sun4i_codec_hw_params(struct snd_pcm_substream *substream,
+                                struct snd_pcm_hw_params *params,
+                                struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+       unsigned long clk_freq;
+       int ret, hwrate;
+       u32 val;
+
+       if (substream->stream != SNDRV_PCM_STREAM_PLAYBACK)
+               return -ENOTSUPP;
+
+       clk_freq = sun4i_codec_get_mod_freq(params);
+       if (!clk_freq)
+               return -EINVAL;
+
+       ret = clk_set_rate(scodec->clk_module, clk_freq);
+       if (ret)
+               return ret;
+
+       hwrate = sun4i_codec_get_hw_rate(params);
+       if (hwrate < 0)
+               return hwrate;
+
+       /* Set DAC sample rate */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          7 << SUN4I_CODEC_DAC_FIFOC_DAC_FS,
+                          hwrate << SUN4I_CODEC_DAC_FIFOC_DAC_FS);
+
+       /* Set the number of channels we want to use */
+       if (params_channels(params) == 1)
+               val = BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN);
+       else
+               val = 0;
+
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          BIT(SUN4I_CODEC_DAC_FIFOC_MONO_EN),
+                          val);
+
+       /* Set the number of sample bits to either 16 or 24 bits */
+       if (hw_param_interval(params, SNDRV_PCM_HW_PARAM_SAMPLE_BITS)->min == 32) {
+               regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
+                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS));
+
+               /* Set TX FIFO mode to padding the LSBs with 0 */
+               regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
+                                  0);
+
+               scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_4_BYTES;
+       } else {
+               regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_SAMPLE_BITS),
+                                  0);
+
+               /* Set TX FIFO mode to repeat the MSB */
+               regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE),
+                                  BIT(SUN4I_CODEC_DAC_FIFOC_TX_FIFO_MODE));
+
+               scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+       }
+
+       return 0;
+}
+
+static int sun4i_codec_startup(struct snd_pcm_substream *substream,
+                              struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+       /*
+        * Stop issuing DRQ when we have room for less than 16 samples
+        * in our TX FIFO
+        */
+       regmap_update_bits(scodec->regmap, SUN4I_CODEC_DAC_FIFOC,
+                          3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT,
+                          3 << SUN4I_CODEC_DAC_FIFOC_DRQ_CLR_CNT);
+
+       return clk_prepare_enable(scodec->clk_module);
+}
+
+static void sun4i_codec_shutdown(struct snd_pcm_substream *substream,
+                                struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(rtd->card);
+
+       clk_disable_unprepare(scodec->clk_module);
+}
+
+static const struct snd_soc_dai_ops sun4i_codec_dai_ops = {
+       .startup        = sun4i_codec_startup,
+       .shutdown       = sun4i_codec_shutdown,
+       .trigger        = sun4i_codec_trigger,
+       .hw_params      = sun4i_codec_hw_params,
+       .prepare        = sun4i_codec_prepare,
+};
+
+static struct snd_soc_dai_driver sun4i_codec_dai = {
+       .name   = "Codec",
+       .ops    = &sun4i_codec_dai_ops,
+       .playback = {
+               .stream_name    = "Codec Playback",
+               .channels_min   = 1,
+               .channels_max   = 2,
+               .rate_min       = 8000,
+               .rate_max       = 192000,
+               .rates          = SNDRV_PCM_RATE_8000_48000 |
+                                 SNDRV_PCM_RATE_96000 |
+                                 SNDRV_PCM_RATE_192000,
+               .formats        = SNDRV_PCM_FMTBIT_S16_LE |
+                                 SNDRV_PCM_FMTBIT_S32_LE,
+               .sig_bits       = 24,
+       },
+};
+
+/*** Codec ***/
+static const struct snd_kcontrol_new sun4i_codec_pa_mute =
+       SOC_DAPM_SINGLE("Switch", SUN4I_CODEC_DAC_ACTL,
+                       SUN4I_CODEC_DAC_ACTL_PA_MUTE, 1, 0);
+
+static DECLARE_TLV_DB_SCALE(sun4i_codec_pa_volume_scale, -6300, 100, 1);
+
+static const struct snd_kcontrol_new sun4i_codec_widgets[] = {
+       SOC_SINGLE_TLV("PA Volume", SUN4I_CODEC_DAC_ACTL,
+                      SUN4I_CODEC_DAC_ACTL_PA_VOL, 0x3F, 0,
+                      sun4i_codec_pa_volume_scale),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_left_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+                       SUN4I_CODEC_DAC_ACTL_LDACLMIXS, 1, 0),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_right_mixer_controls[] = {
+       SOC_DAPM_SINGLE("Right DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+                       SUN4I_CODEC_DAC_ACTL_RDACRMIXS, 1, 0),
+       SOC_DAPM_SINGLE("Left DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+                       SUN4I_CODEC_DAC_ACTL_LDACRMIXS, 1, 0),
+};
+
+static const struct snd_kcontrol_new sun4i_codec_pa_mixer_controls[] = {
+       SOC_DAPM_SINGLE("DAC Playback Switch", SUN4I_CODEC_DAC_ACTL,
+                       SUN4I_CODEC_DAC_ACTL_DACPAS, 1, 0),
+       SOC_DAPM_SINGLE("Mixer Playback Switch", SUN4I_CODEC_DAC_ACTL,
+                       SUN4I_CODEC_DAC_ACTL_MIXPAS, 1, 0),
+};
+
+static const struct snd_soc_dapm_widget sun4i_codec_dapm_widgets[] = {
+       /* Digital parts of the DACs */
+       SND_SOC_DAPM_SUPPLY("DAC", SUN4I_CODEC_DAC_DPC,
+                           SUN4I_CODEC_DAC_DPC_EN_DA, 0,
+                           NULL, 0),
+
+       /* Analog parts of the DACs */
+       SND_SOC_DAPM_DAC("Left DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
+                        SUN4I_CODEC_DAC_ACTL_DACAENL, 0),
+       SND_SOC_DAPM_DAC("Right DAC", "Codec Playback", SUN4I_CODEC_DAC_ACTL,
+                        SUN4I_CODEC_DAC_ACTL_DACAENR, 0),
+
+       /* Mixers */
+       SND_SOC_DAPM_MIXER("Left Mixer", SND_SOC_NOPM, 0, 0,
+                          sun4i_codec_left_mixer_controls,
+                          ARRAY_SIZE(sun4i_codec_left_mixer_controls)),
+       SND_SOC_DAPM_MIXER("Right Mixer", SND_SOC_NOPM, 0, 0,
+                          sun4i_codec_right_mixer_controls,
+                          ARRAY_SIZE(sun4i_codec_right_mixer_controls)),
+
+       /* Global Mixer Enable */
+       SND_SOC_DAPM_SUPPLY("Mixer Enable", SUN4I_CODEC_DAC_ACTL,
+                           SUN4I_CODEC_DAC_ACTL_MIXEN, 0, NULL, 0),
+
+       /* Pre-Amplifier */
+       SND_SOC_DAPM_MIXER("Pre-Amplifier", SUN4I_CODEC_ADC_ACTL,
+                          SUN4I_CODEC_ADC_ACTL_PA_EN, 0,
+                          sun4i_codec_pa_mixer_controls,
+                          ARRAY_SIZE(sun4i_codec_pa_mixer_controls)),
+       SND_SOC_DAPM_SWITCH("Pre-Amplifier Mute", SND_SOC_NOPM, 0, 0,
+                           &sun4i_codec_pa_mute),
+
+       SND_SOC_DAPM_OUTPUT("HP Right"),
+       SND_SOC_DAPM_OUTPUT("HP Left"),
+};
+
+static const struct snd_soc_dapm_route sun4i_codec_dapm_routes[] = {
+       /* Left DAC Routes */
+       { "Left DAC", NULL, "DAC" },
+
+       /* Right DAC Routes */
+       { "Right DAC", NULL, "DAC" },
+
+       /* Right Mixer Routes */
+       { "Right Mixer", NULL, "Mixer Enable" },
+       { "Right Mixer", "Left DAC Playback Switch", "Left DAC" },
+       { "Right Mixer", "Right DAC Playback Switch", "Right DAC" },
+
+       /* Left Mixer Routes */
+       { "Left Mixer", NULL, "Mixer Enable" },
+       { "Left Mixer", "Left DAC Playback Switch", "Left DAC" },
+
+       /* Pre-Amplifier Mixer Routes */
+       { "Pre-Amplifier", "Mixer Playback Switch", "Left Mixer" },
+       { "Pre-Amplifier", "Mixer Playback Switch", "Right Mixer" },
+       { "Pre-Amplifier", "DAC Playback Switch", "Left DAC" },
+       { "Pre-Amplifier", "DAC Playback Switch", "Right DAC" },
+
+       /* PA -> HP path */
+       { "Pre-Amplifier Mute", "Switch", "Pre-Amplifier" },
+       { "HP Right", NULL, "Pre-Amplifier Mute" },
+       { "HP Left", NULL, "Pre-Amplifier Mute" },
+};
+
+static struct snd_soc_codec_driver sun4i_codec_codec = {
+       .controls               = sun4i_codec_widgets,
+       .num_controls           = ARRAY_SIZE(sun4i_codec_widgets),
+       .dapm_widgets           = sun4i_codec_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(sun4i_codec_dapm_widgets),
+       .dapm_routes            = sun4i_codec_dapm_routes,
+       .num_dapm_routes        = ARRAY_SIZE(sun4i_codec_dapm_routes),
+};
+
+static const struct snd_soc_component_driver sun4i_codec_component = {
+       .name = "sun4i-codec",
+};
+
+#define SUN4I_CODEC_RATES      SNDRV_PCM_RATE_8000_192000
+#define SUN4I_CODEC_FORMATS    (SNDRV_PCM_FMTBIT_S16_LE | \
+                                SNDRV_PCM_FMTBIT_S32_LE)
+
+static int sun4i_codec_dai_probe(struct snd_soc_dai *dai)
+{
+       struct snd_soc_card *card = snd_soc_dai_get_drvdata(dai);
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
+
+       snd_soc_dai_init_dma_data(dai, &scodec->playback_dma_data,
+                                 NULL);
+
+       return 0;
+}
+
+static struct snd_soc_dai_driver dummy_cpu_dai = {
+       .name   = "sun4i-codec-cpu-dai",
+       .probe  = sun4i_codec_dai_probe,
+       .playback = {
+               .stream_name    = "Playback",
+               .channels_min   = 1,
+               .channels_max   = 2,
+               .rates          = SUN4I_CODEC_RATES,
+               .formats        = SUN4I_CODEC_FORMATS,
+               .sig_bits       = 24,
+       },
+};
+
+static const struct regmap_config sun4i_codec_regmap_config = {
+       .reg_bits       = 32,
+       .reg_stride     = 4,
+       .val_bits       = 32,
+       .max_register   = SUN4I_CODEC_AC_MIC_PHONE_CAL,
+};
+
+static const struct of_device_id sun4i_codec_of_match[] = {
+       { .compatible = "allwinner,sun4i-a10-codec" },
+       { .compatible = "allwinner,sun7i-a20-codec" },
+       {}
+};
+MODULE_DEVICE_TABLE(of, sun4i_codec_of_match);
+
+static struct snd_soc_dai_link *sun4i_codec_create_link(struct device *dev,
+                                                       int *num_links)
+{
+       struct snd_soc_dai_link *link = devm_kzalloc(dev, sizeof(*link),
+                                                    GFP_KERNEL);
+       if (!link)
+               return NULL;
+
+       link->name              = "cdc";
+       link->stream_name       = "CDC PCM";
+       link->codec_dai_name    = "Codec";
+       link->cpu_dai_name      = dev_name(dev);
+       link->codec_name        = dev_name(dev);
+       link->platform_name     = dev_name(dev);
+       link->dai_fmt           = SND_SOC_DAIFMT_I2S;
+
+       *num_links = 1;
+
+       return link;
+};
+
+static struct snd_soc_card *sun4i_codec_create_card(struct device *dev)
+{
+       struct snd_soc_card *card;
+
+       card = devm_kzalloc(dev, sizeof(*card), GFP_KERNEL);
+       if (!card)
+               return NULL;
+
+       card->dai_link = sun4i_codec_create_link(dev, &card->num_links);
+       if (!card->dai_link)
+               return NULL;
+
+       card->dev               = dev;
+       card->name              = "sun4i-codec";
+
+       return card;
+};
+
+static int sun4i_codec_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card;
+       struct sun4i_codec *scodec;
+       struct resource *res;
+       void __iomem *base;
+       int ret;
+
+       scodec = devm_kzalloc(&pdev->dev, sizeof(*scodec), GFP_KERNEL);
+       if (!scodec)
+               return -ENOMEM;
+
+       scodec->dev = &pdev->dev;
+
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(base)) {
+               dev_err(&pdev->dev, "Failed to map the registers\n");
+               return PTR_ERR(base);
+       }
+
+       scodec->regmap = devm_regmap_init_mmio(&pdev->dev, base,
+                                            &sun4i_codec_regmap_config);
+       if (IS_ERR(scodec->regmap)) {
+               dev_err(&pdev->dev, "Failed to create our regmap\n");
+               return PTR_ERR(scodec->regmap);
+       }
+
+       /* Get the clocks from the DT */
+       scodec->clk_apb = devm_clk_get(&pdev->dev, "apb");
+       if (IS_ERR(scodec->clk_apb)) {
+               dev_err(&pdev->dev, "Failed to get the APB clock\n");
+               return PTR_ERR(scodec->clk_apb);
+       }
+
+       scodec->clk_module = devm_clk_get(&pdev->dev, "codec");
+       if (IS_ERR(scodec->clk_module)) {
+               dev_err(&pdev->dev, "Failed to get the module clock\n");
+               return PTR_ERR(scodec->clk_module);
+       }
+
+       /* Enable the bus clock */
+       if (clk_prepare_enable(scodec->clk_apb)) {
+               dev_err(&pdev->dev, "Failed to enable the APB clock\n");
+               return -EINVAL;
+       }
+
+       /* DMA configuration for TX FIFO */
+       scodec->playback_dma_data.addr = res->start + SUN4I_CODEC_DAC_TXDATA;
+       scodec->playback_dma_data.maxburst = 4;
+       scodec->playback_dma_data.addr_width = DMA_SLAVE_BUSWIDTH_2_BYTES;
+
+       ret = snd_soc_register_codec(&pdev->dev, &sun4i_codec_codec,
+                                    &sun4i_codec_dai, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register our codec\n");
+               goto err_clk_disable;
+       }
+
+       ret = devm_snd_soc_register_component(&pdev->dev,
+                                             &sun4i_codec_component,
+                                             &dummy_cpu_dai, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register our DAI\n");
+               goto err_unregister_codec;
+       }
+
+       ret = devm_snd_dmaengine_pcm_register(&pdev->dev, NULL, 0);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register against DMAEngine\n");
+               goto err_unregister_codec;
+       }
+
+       card = sun4i_codec_create_card(&pdev->dev);
+       if (!card) {
+               dev_err(&pdev->dev, "Failed to create our card\n");
+               goto err_unregister_codec;
+       }
+
+       platform_set_drvdata(pdev, card);
+       snd_soc_card_set_drvdata(card, scodec);
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "Failed to register our card\n");
+               goto err_unregister_codec;
+       }
+
+       return 0;
+
+err_unregister_codec:
+       snd_soc_unregister_codec(&pdev->dev);
+err_clk_disable:
+       clk_disable_unprepare(scodec->clk_apb);
+       return ret;
+}
+
+static int sun4i_codec_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct sun4i_codec *scodec = snd_soc_card_get_drvdata(card);
+
+       snd_soc_unregister_card(card);
+       snd_soc_unregister_codec(&pdev->dev);
+       clk_disable_unprepare(scodec->clk_apb);
+
+       return 0;
+}
+
+static struct platform_driver sun4i_codec_driver = {
+       .driver = {
+               .name = "sun4i-codec",
+               .of_match_table = sun4i_codec_of_match,
+       },
+       .probe = sun4i_codec_probe,
+       .remove = sun4i_codec_remove,
+};
+module_platform_driver(sun4i_codec_driver);
+
+MODULE_DESCRIPTION("Allwinner A10 codec driver");
+MODULE_AUTHOR("Emilio López <emilio@elopez.com.ar>");
+MODULE_AUTHOR("Jon Smirl <jonsmirl@gmail.com>");
+MODULE_AUTHOR("Maxime Ripard <maxime.ripard@free-electrons.com>");
+MODULE_LICENSE("GPL");
index 4e0c0e502ade2c228a00db3a44c44ecfa4c9978d..ba9fc099cf67b22997aad328f256c2b1150c86a1 100644 (file)
@@ -152,6 +152,7 @@ static const struct of_device_id snd_soc_mop500_match[] = {
        { .compatible = "stericsson,snd-soc-mop500", },
        {},
 };
+MODULE_DEVICE_TABLE(of, snd_soc_mop500_match);
 
 static struct platform_driver snd_soc_mop500_driver = {
        .driver = {
index f5df08ded770ef9cbafd3ec88903893bc4657344..6d5698b25bd4b25c92c128860527245167d665d0 100644 (file)
@@ -522,9 +522,9 @@ static int ux500_msp_dai_hw_params(struct snd_pcm_substream *substream,
                slots_active = hweight32(mask);
                dev_dbg(dai->dev, "TDM-slots active: %d", slots_active);
 
-               snd_pcm_hw_constraint_minmax(runtime,
+               snd_pcm_hw_constraint_single(runtime,
                                SNDRV_PCM_HW_PARAM_CHANNELS,
-                               slots_active, slots_active);
+                               slots_active);
                break;
 
        default:
@@ -843,6 +843,7 @@ static const struct of_device_id ux500_msp_i2s_match[] = {
        { .compatible = "stericsson,ux500-msp-i2s", },
        {},
 };
+MODULE_DEVICE_TABLE(of, ux500_msp_i2s_match);
 
 static struct platform_driver msp_i2s_driver = {
        .driver = {
index ef580b43f1e3b1e71dcc4ed0f6a16f11928e071e..71778ca4b26aafcb3dacedcc660947a5df61f7e2 100644 (file)
@@ -122,6 +122,7 @@ struct snd_usb_substream {
        unsigned int buffer_periods;    /* current periods per buffer */
        unsigned int altset_idx;     /* USB data format: index of alternate setting */
        unsigned int txfr_quirk:1;      /* allow sub-frame alignment */
+       unsigned int tx_length_quirk:1; /* add length specifier to transfers */
        unsigned int fmt_type;          /* USB audio format type (1-3) */
        unsigned int pkt_offset_adj;    /* Bytes to drop from beginning of packets (for non-compliant devices) */
 
index e6f71894ecdc950117776d2ef9e2cc4df4661904..7b1cb365ffab74d6028206adb8012226da963bac 100644 (file)
@@ -183,13 +183,53 @@ static void retire_inbound_urb(struct snd_usb_endpoint *ep,
                ep->retire_data_urb(ep->data_subs, urb);
 }
 
+static void prepare_silent_urb(struct snd_usb_endpoint *ep,
+                              struct snd_urb_ctx *ctx)
+{
+       struct urb *urb = ctx->urb;
+       unsigned int offs = 0;
+       unsigned int extra = 0;
+       __le32 packet_length;
+       int i;
+
+       /* For tx_length_quirk, put packet length at start of packet */
+       if (ep->chip->tx_length_quirk)
+               extra = sizeof(packet_length);
+
+       for (i = 0; i < ctx->packets; ++i) {
+               unsigned int offset;
+               unsigned int length;
+               int counts;
+
+               if (ctx->packet_size[i])
+                       counts = ctx->packet_size[i];
+               else
+                       counts = snd_usb_endpoint_next_packet_size(ep);
+
+               length = counts * ep->stride; /* number of silent bytes */
+               offset = offs * ep->stride + extra * i;
+               urb->iso_frame_desc[i].offset = offset;
+               urb->iso_frame_desc[i].length = length + extra;
+               if (extra) {
+                       packet_length = cpu_to_le32(length);
+                       memcpy(urb->transfer_buffer + offset,
+                              &packet_length, sizeof(packet_length));
+               }
+               memset(urb->transfer_buffer + offset + extra,
+                      ep->silence_value, length);
+               offs += counts;
+       }
+
+       urb->number_of_packets = ctx->packets;
+       urb->transfer_buffer_length = offs * ep->stride + ctx->packets * extra;
+}
+
 /*
  * Prepare a PLAYBACK urb for submission to the bus.
  */
 static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
                                 struct snd_urb_ctx *ctx)
 {
-       int i;
        struct urb *urb = ctx->urb;
        unsigned char *cp = urb->transfer_buffer;
 
@@ -201,24 +241,7 @@ static void prepare_outbound_urb(struct snd_usb_endpoint *ep,
                        ep->prepare_data_urb(ep->data_subs, urb);
                } else {
                        /* no data provider, so send silence */
-                       unsigned int offs = 0;
-                       for (i = 0; i < ctx->packets; ++i) {
-                               int counts;
-
-                               if (ctx->packet_size[i])
-                                       counts = ctx->packet_size[i];
-                               else
-                                       counts = snd_usb_endpoint_next_packet_size(ep);
-
-                               urb->iso_frame_desc[i].offset = offs * ep->stride;
-                               urb->iso_frame_desc[i].length = counts * ep->stride;
-                               offs += counts;
-                       }
-
-                       urb->number_of_packets = ctx->packets;
-                       urb->transfer_buffer_length = offs * ep->stride;
-                       memset(urb->transfer_buffer, ep->silence_value,
-                              offs * ep->stride);
+                       prepare_silent_urb(ep, ctx);
                }
                break;
 
@@ -594,6 +617,8 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
        unsigned int max_packs_per_period, urbs_per_period, urb_packs;
        unsigned int max_urbs, i;
        int frame_bits = snd_pcm_format_physical_width(pcm_format) * channels;
+       int tx_length_quirk = (ep->chip->tx_length_quirk &&
+                              usb_pipeout(ep->pipe));
 
        if (pcm_format == SNDRV_PCM_FORMAT_DSD_U16_LE && fmt->dsd_dop) {
                /*
@@ -610,13 +635,34 @@ static int data_ep_set_params(struct snd_usb_endpoint *ep,
 
        /* assume max. frequency is 25% higher than nominal */
        ep->freqmax = ep->freqn + (ep->freqn >> 2);
-       maxsize = ((ep->freqmax + 0xffff) * (frame_bits >> 3))
-                               >> (16 - ep->datainterval);
+       /* Round up freqmax to nearest integer in order to calculate maximum
+        * packet size, which must represent a whole number of frames.
+        * This is accomplished by adding 0x0.ffff before converting the
+        * Q16.16 format into integer.
+        * In order to accurately calculate the maximum packet size when
+        * the data interval is more than 1 (i.e. ep->datainterval > 0),
+        * multiply by the data interval prior to rounding. For instance,
+        * a freqmax of 41 kHz will result in a max packet size of 6 (5.125)
+        * frames with a data interval of 1, but 11 (10.25) frames with a
+        * data interval of 2.
+        * (ep->freqmax << ep->datainterval overflows at 8.192 MHz for the
+        * maximum datainterval value of 3, at USB full speed, higher for
+        * USB high speed, noting that ep->freqmax is in units of
+        * frames per packet in Q16.16 format.)
+        */
+       maxsize = (((ep->freqmax << ep->datainterval) + 0xffff) >> 16) *
+                        (frame_bits >> 3);
+       if (tx_length_quirk)
+               maxsize += sizeof(__le32); /* Space for length descriptor */
        /* but wMaxPacketSize might reduce this */
        if (ep->maxpacksize && ep->maxpacksize < maxsize) {
                /* whatever fits into a max. size packet */
-               maxsize = ep->maxpacksize;
-               ep->freqmax = (maxsize / (frame_bits >> 3))
+               unsigned int data_maxsize = maxsize = ep->maxpacksize;
+
+               if (tx_length_quirk)
+                       /* Need to remove the length descriptor to calc freq */
+                       data_maxsize -= sizeof(__le32);
+               ep->freqmax = (data_maxsize / (frame_bits >> 3))
                                << (16 - ep->datainterval);
        }
 
index 417ebb11cf4896b8009c0aa86c7dab71c34d61a4..7661616f36361d142144842bd6595994829edec4 100644 (file)
@@ -1903,11 +1903,14 @@ static void snd_usbmidi_switch_roland_altsetting(struct snd_usb_midi *umidi)
 
        hostif = &intf->altsetting[1];
        intfd = get_iface_desc(hostif);
+       /* If either or both of the endpoints support interrupt transfer,
+        * then use the alternate setting
+        */
        if (intfd->bNumEndpoints != 2 ||
-           (get_endpoint(hostif, 0)->bmAttributes &
-            USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_BULK ||
-           (get_endpoint(hostif, 1)->bmAttributes &
-            USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_INT)
+           !((get_endpoint(hostif, 0)->bmAttributes &
+              USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT ||
+             (get_endpoint(hostif, 1)->bmAttributes &
+              USB_ENDPOINT_XFERTYPE_MASK) == USB_ENDPOINT_XFER_INT))
                return;
 
        dev_dbg(&umidi->dev->dev, "switching to altsetting %d with int ep\n",
index d3608c0a29f343f32179b33b74f537172484fb4b..fe91184ce83247d8b0360bab09e94c8f7cb720d8 100644 (file)
@@ -338,7 +338,7 @@ static int snd_audigy2nx_led_put(struct snd_kcontrol *kcontrol,
        struct usb_mixer_elem_list *list = snd_kcontrol_chip(kcontrol);
        struct usb_mixer_interface *mixer = list->mixer;
        int index = kcontrol->private_value & 0xff;
-       int value = ucontrol->value.integer.value[0];
+       unsigned int value = ucontrol->value.integer.value[0];
        int old_value = kcontrol->private_value >> 8;
        int err;
 
index cdac5179db3f349f8ac61e010a8a9ff05c2e93ae..9245f52d43bdecfeb710b6cfc6ae99b3202aae90 100644 (file)
@@ -1383,6 +1383,56 @@ static inline void fill_playback_urb_dsd_dop(struct snd_usb_substream *subs,
                        subs->hwptr_done++;
                }
        }
+       if (subs->hwptr_done >= runtime->buffer_size * stride)
+               subs->hwptr_done -= runtime->buffer_size * stride;
+}
+
+static void copy_to_urb(struct snd_usb_substream *subs, struct urb *urb,
+                       int offset, int stride, unsigned int bytes)
+{
+       struct snd_pcm_runtime *runtime = subs->pcm_substream->runtime;
+
+       if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
+               /* err, the transferred area goes over buffer boundary. */
+               unsigned int bytes1 =
+                       runtime->buffer_size * stride - subs->hwptr_done;
+               memcpy(urb->transfer_buffer + offset,
+                      runtime->dma_area + subs->hwptr_done, bytes1);
+               memcpy(urb->transfer_buffer + offset + bytes1,
+                      runtime->dma_area, bytes - bytes1);
+       } else {
+               memcpy(urb->transfer_buffer + offset,
+                      runtime->dma_area + subs->hwptr_done, bytes);
+       }
+       subs->hwptr_done += bytes;
+       if (subs->hwptr_done >= runtime->buffer_size * stride)
+               subs->hwptr_done -= runtime->buffer_size * stride;
+}
+
+static unsigned int copy_to_urb_quirk(struct snd_usb_substream *subs,
+                                     struct urb *urb, int stride,
+                                     unsigned int bytes)
+{
+       __le32 packet_length;
+       int i;
+
+       /* Put __le32 length descriptor at start of each packet. */
+       for (i = 0; i < urb->number_of_packets; i++) {
+               unsigned int length = urb->iso_frame_desc[i].length;
+               unsigned int offset = urb->iso_frame_desc[i].offset;
+
+               packet_length = cpu_to_le32(length);
+               offset += i * sizeof(packet_length);
+               urb->iso_frame_desc[i].offset = offset;
+               urb->iso_frame_desc[i].length += sizeof(packet_length);
+               memcpy(urb->transfer_buffer + offset,
+                      &packet_length, sizeof(packet_length));
+               copy_to_urb(subs, urb, offset + sizeof(packet_length),
+                           stride, length);
+       }
+       /* Adjust transfer size accordingly. */
+       bytes += urb->number_of_packets * sizeof(packet_length);
+       return bytes;
 }
 
 static void prepare_playback_urb(struct snd_usb_substream *subs,
@@ -1460,27 +1510,17 @@ static void prepare_playback_urb(struct snd_usb_substream *subs,
                }
 
                subs->hwptr_done += bytes;
+               if (subs->hwptr_done >= runtime->buffer_size * stride)
+                       subs->hwptr_done -= runtime->buffer_size * stride;
        } else {
                /* usual PCM */
-               if (subs->hwptr_done + bytes > runtime->buffer_size * stride) {
-                       /* err, the transferred area goes over buffer boundary. */
-                       unsigned int bytes1 =
-                               runtime->buffer_size * stride - subs->hwptr_done;
-                       memcpy(urb->transfer_buffer,
-                              runtime->dma_area + subs->hwptr_done, bytes1);
-                       memcpy(urb->transfer_buffer + bytes1,
-                              runtime->dma_area, bytes - bytes1);
-               } else {
-                       memcpy(urb->transfer_buffer,
-                              runtime->dma_area + subs->hwptr_done, bytes);
-               }
-
-               subs->hwptr_done += bytes;
+               if (!subs->tx_length_quirk)
+                       copy_to_urb(subs, urb, 0, stride, bytes);
+               else
+                       bytes = copy_to_urb_quirk(subs, urb, stride, bytes);
+                       /* bytes is now amount of outgoing data */
        }
 
-       if (subs->hwptr_done >= runtime->buffer_size * stride)
-               subs->hwptr_done -= runtime->buffer_size * stride;
-
        /* update delay with exact number of samples queued */
        runtime->delay = subs->last_delay;
        runtime->delay += frames;
index e4756651a52c8873457ae340450934dcfbd5dfad..1a1e2e4df35e5809e7f7d81b405ca6dddf2f4311 100644 (file)
@@ -2663,6 +2663,15 @@ YAMAHA_DEVICE(0x7010, "UB99"),
                .type = QUIRK_MIDI_NOVATION
        }
 },
+{
+       USB_DEVICE(0x1235, 0x000a),
+       .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
+               /* .vendor_name = "Novation", */
+               /* .product_name = "Nocturn", */
+               .ifnum = 0,
+               .type = QUIRK_MIDI_RAW_BYTES
+       }
+},
 {
        USB_DEVICE(0x1235, 0x000e),
        .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
@@ -3182,25 +3191,19 @@ AU0828_DEVICE(0x2040, 0x7270, "Hauppauge", "HVR-950Q"),
 {
        /*
         * ZOOM R16/24 in audio interface mode.
-        * Mixer descriptors are garbage, further quirks will be needed
-        * to make any of it functional, thus disabled for now.
-        * Playback stream appears to start and run fine but no sound
-        * is produced, so also disabled for now.
+        * Playback requires an extra four byte LE length indicator
+        * at the start of each isochronous packet. This quirk is
+        * enabled in create_standard_audio_quirk().
         */
        USB_DEVICE(0x1686, 0x00dd),
        .driver_info = (unsigned long) & (const struct snd_usb_audio_quirk) {
                .ifnum = QUIRK_ANY_INTERFACE,
                .type = QUIRK_COMPOSITE,
                .data = (const struct snd_usb_audio_quirk[]) {
-                       {
-                               /* Mixer */
-                               .ifnum = 0,
-                               .type = QUIRK_IGNORE_INTERFACE,
-                       },
                        {
                                /* Playback  */
                                .ifnum = 1,
-                               .type = QUIRK_IGNORE_INTERFACE,
+                               .type = QUIRK_AUDIO_STANDARD_INTERFACE,
                        },
                        {
                                /* Capture */
index 00ebc0ca008e08b98b2f5fb6aed67dfb0d642ec1..4897ea171194f474c352c45579f62aa036d219ea 100644 (file)
@@ -115,6 +115,9 @@ static int create_standard_audio_quirk(struct snd_usb_audio *chip,
        struct usb_interface_descriptor *altsd;
        int err;
 
+       if (chip->usb_id == USB_ID(0x1686, 0x00dd)) /* Zoom R16/24 */
+               chip->tx_length_quirk = 1;
+
        alts = &iface->altsetting[0];
        altsd = get_iface_desc(alts);
        err = snd_usb_parse_audio_interface(chip, altsd->bInterfaceNumber);
index 970086015cded9f0a75f4d66a1471b1e99d9bfd4..8ee14f2365e749d964c369acef5ac45f95a060ca 100644 (file)
@@ -92,6 +92,7 @@ static void snd_usb_init_substream(struct snd_usb_stream *as,
        subs->direction = stream;
        subs->dev = as->chip->dev;
        subs->txfr_quirk = as->chip->txfr_quirk;
+       subs->tx_length_quirk = as->chip->tx_length_quirk;
        subs->speed = snd_usb_get_speed(subs->dev);
        subs->pkt_offset_adj = 0;
 
index 33a176437e2e4fc34f5064fc23984bab60b22795..15a12715bd05154bd9c0b4bbe7084f3df15022b2 100644 (file)
@@ -43,6 +43,7 @@ struct snd_usb_audio {
        atomic_t usage_count;
        wait_queue_head_t shutdown_wait;
        unsigned int txfr_quirk:1; /* Subframe boundaries on transfers */
+       unsigned int tx_length_quirk:1; /* Put length specifier in transfers */
        
        int num_interfaces;
        int num_suspended_intf;