]> git.karo-electronics.de Git - karo-tx-linux.git/commitdiff
Merge remote-tracking branch 'asoc/topic/fsi' into tmp
authorMark Brown <broonie@linaro.org>
Fri, 30 Aug 2013 10:04:22 +0000 (11:04 +0100)
committerMark Brown <broonie@linaro.org>
Fri, 30 Aug 2013 10:04:22 +0000 (11:04 +0100)
378 files changed:
Documentation/devicetree/bindings/misc/atmel-ssc.txt
Documentation/devicetree/bindings/serial/mrvl,pxa-ssp.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/ak4554.c [new file with mode: 0644]
Documentation/devicetree/bindings/sound/alc5632.txt
Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/atmel-wm8904.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/fsl,spdif.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/fsl,ssi.txt [moved from Documentation/devicetree/bindings/powerpc/fsl/ssi.txt with 87% similarity]
Documentation/devicetree/bindings/sound/imx-audmux.txt
Documentation/devicetree/bindings/sound/mrvl,pxa-ssp.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/mrvl,pxa2xx-pcm.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-alc5632.txt
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-rt5640.txt
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8753.txt
Documentation/devicetree/bindings/sound/nvidia,tegra-audio-wm8903.txt
Documentation/devicetree/bindings/sound/pcm1792a.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/rt5640.txt
Documentation/devicetree/bindings/sound/samsung-i2s.txt
Documentation/devicetree/bindings/sound/soc-ac97link.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/ti,pcm1681.txt [new file with mode: 0644]
Documentation/devicetree/bindings/sound/tlv320aic3x.txt
Documentation/devicetree/bindings/sound/wm8731.txt
Documentation/devicetree/bindings/sound/wm8753.txt
Documentation/devicetree/bindings/sound/wm8903.txt
Documentation/devicetree/bindings/sound/wm8994.txt
Documentation/kernel-parameters.txt
Documentation/sound/alsa/HD-Audio-Models.txt
Documentation/sound/alsa/HD-Audio.txt
MAINTAINERS
Makefile
arch/arc/lib/strchr-700.S
arch/arm/boot/dts/at91sam9n12ek.dts
arch/arm/boot/dts/at91sam9x5ek.dtsi
arch/arm/boot/dts/exynos5250.dtsi
arch/arm/boot/dts/tegra20-seaboard.dts
arch/arm/boot/dts/tegra20-trimslice.dts
arch/arm/boot/dts/tegra20-whistler.dts
arch/arm/kernel/fiq.c
arch/arm/kernel/machine_kexec.c
arch/arm/kvm/coproc.c
arch/arm/kvm/coproc.h
arch/arm/kvm/coproc_a15.c
arch/arm/kvm/mmio.c
arch/arm/kvm/mmu.c
arch/arm/mach-at91/at91sam9x5.c
arch/arm/mach-davinci/board-dm355-leopard.c
arch/arm/mach-davinci/board-dm644x-evm.c
arch/arm/mach-davinci/board-dm646x-evm.c
arch/arm/mach-davinci/board-neuros-osd2.c
arch/arm/mach-dove/common.c
arch/arm/mach-kirkwood/common.c
arch/arm/mach-omap2/board-n8x0.c
arch/arm/mach-omap2/board-rx51.c
arch/arm/mach-omap2/usb-musb.c
arch/arm/mm/Kconfig
arch/arm/plat-pxa/ssp.c
arch/arm/plat-samsung/init.c
arch/arm/xen/enlighten.c
arch/arm64/include/asm/kvm_asm.h
arch/arm64/include/asm/kvm_host.h
arch/arm64/kernel/perf_event.c
arch/arm64/kvm/hyp.S
arch/arm64/kvm/sys_regs.c
arch/mips/math-emu/cp1emu.c
arch/x86/include/asm/bootparam_utils.h
arch/x86/include/asm/microcode_amd.h
arch/x86/kernel/cpu/amd.c
arch/x86/kernel/microcode_amd.c
arch/x86/kernel/microcode_amd_early.c
arch/x86/kernel/sys_x86_64.c
arch/x86/mm/mmap.c
arch/x86/xen/setup.c
arch/x86/xen/smp.c
drivers/acpi/video.c
drivers/ata/libata-pmp.c
drivers/ata/sata_fsl.c
drivers/ata/sata_highbank.c
drivers/gpu/drm/gma500/psb_intel_sdvo.c
drivers/gpu/drm/i915/i915_gem_dmabuf.c
drivers/gpu/drm/i915/i915_reg.h
drivers/gpu/drm/i915/intel_display.c
drivers/gpu/drm/i915/intel_ringbuffer.c
drivers/gpu/drm/nouveau/core/core/mm.c
drivers/gpu/drm/nouveau/core/include/subdev/mc.h
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv49.c
drivers/gpu/drm/nouveau/core/subdev/fb/ramnv4e.c
drivers/gpu/drm/nouveau/core/subdev/ltcg/nvc0.c
drivers/gpu/drm/nouveau/core/subdev/mc/base.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv04.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv44.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv50.c
drivers/gpu/drm/nouveau/core/subdev/mc/nv98.c
drivers/gpu/drm/nouveau/core/subdev/mc/nvc0.c
drivers/gpu/drm/nouveau/dispnv04/crtc.c
drivers/gpu/drm/nouveau/dispnv04/disp.h
drivers/gpu/drm/nouveau/nouveau_display.c
drivers/gpu/drm/nouveau/nv40_pm.c
drivers/gpu/drm/radeon/radeon.h
drivers/gpu/drm/radeon/radeon_uvd.c
drivers/gpu/drm/radeon/rv770.c
drivers/iio/light/adjd_s311.c
drivers/md/dm-cache-policy-mq.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_dcb.h
drivers/net/ethernet/broadcom/bnx2x/bnx2x_main.c
drivers/net/ethernet/broadcom/bnx2x/bnx2x_sriov.c
drivers/net/ethernet/emulex/benet/be_main.c
drivers/net/ethernet/realtek/r8169.c
drivers/net/ethernet/sfc/filter.c
drivers/net/irda/via-ircc.c
drivers/net/macvtap.c
drivers/net/phy/realtek.c
drivers/net/usb/hso.c
drivers/net/wireless/hostap/hostap_ioctl.c
drivers/net/wireless/iwlwifi/dvm/mac80211.c
drivers/net/wireless/iwlwifi/iwl-prph.h
drivers/net/wireless/iwlwifi/mvm/time-event.c
drivers/net/wireless/iwlwifi/pcie/rx.c
drivers/net/wireless/iwlwifi/pcie/trans.c
drivers/net/wireless/zd1201.c
drivers/of/fdt.c
drivers/pinctrl/pinctrl-sunxi.c
drivers/pinctrl/pinctrl-sunxi.h
drivers/platform/olpc/olpc-ec.c
drivers/platform/x86/hp-wmi.c
drivers/platform/x86/sony-laptop.c
drivers/s390/scsi/zfcp_erp.c
drivers/s390/scsi/zfcp_qdio.c
drivers/s390/scsi/zfcp_sysfs.c
drivers/scsi/Kconfig
drivers/staging/comedi/drivers.c
drivers/usb/host/ohci-pci.c
drivers/usb/phy/phy-fsl-usb.h
drivers/usb/phy/phy-fsm-usb.c
drivers/xen/events.c
fs/bfs/inode.c
fs/bio.c
fs/dcache.c
fs/efs/inode.c
fs/gfs2/glock.c
fs/gfs2/glops.c
fs/gfs2/inode.c
fs/gfs2/main.c
fs/hugetlbfs/inode.c
fs/namespace.c
fs/nilfs2/segbuf.c
fs/proc/fd.c
fs/proc/generic.c
fs/proc/root.c
include/dt-bindings/sound/fsl-imx-audmux.h [new file with mode: 0644]
include/linux/atmel-ssc.h
include/linux/dcache.h
include/linux/inetdevice.h
include/linux/ipv6.h
include/linux/mfd/arizona/gpio.h [new file with mode: 0644]
include/linux/mm_types.h
include/linux/platform_data/asoc-s3c.h
include/linux/platform_data/omap-abe-twl6040.h [deleted file]
include/linux/pxa2xx_ssp.h
include/linux/sched.h
include/linux/wait.h
include/net/ip6_route.h
include/sound/core.h
include/sound/pxa2xx-lib.h
include/sound/rcar_snd.h [new file with mode: 0644]
include/sound/soc-dapm.h
include/sound/soc-dpcm.h
include/sound/soc.h
include/uapi/linux/ip.h
include/uapi/sound/hdspm.h
init/Kconfig
kernel/cpuset.c
kernel/time/sched_clock.c
kernel/time/tick-sched.c
kernel/wait.c
lib/lz4/lz4_compress.c
lib/lz4/lz4_decompress.c
lib/lz4/lz4hc_compress.c
mm/memcontrol.c
mm/shmem.c
net/batman-adv/unicast.c
net/bridge/br_fdb.c
net/bridge/br_netlink.c
net/bridge/br_vlan.c
net/ipv4/tcp.c
net/ipv6/addrconf.c
net/ipv6/ndisc.c
net/ipv6/reassembly.c
net/ipv6/route.c
net/netlink/genetlink.c
net/packet/af_packet.c
net/wireless/nl80211.c
net/wireless/sme.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/pcm_dmaengine.c [moved from sound/soc/soc-dmaengine-pcm.c with 100% similarity]
sound/core/pcm_lib.c
sound/drivers/dummy.c
sound/firewire/speakers.c
sound/isa/gus/interwave.c
sound/oss/dmabuf.c
sound/pci/hda/Kconfig
sound/pci/hda/hda_codec.c
sound/pci/hda/hda_codec.h
sound/pci/hda/hda_generic.c
sound/pci/hda/hda_generic.h
sound/pci/hda/hda_hwdep.c
sound/pci/hda/hda_intel.c
sound/pci/hda/hda_jack.c
sound/pci/hda/hda_jack.h
sound/pci/hda/hda_proc.c
sound/pci/hda/patch_analog.c
sound/pci/hda/patch_conexant.c
sound/pci/hda/patch_hdmi.c
sound/pci/hda/patch_realtek.c
sound/pci/hda/patch_sigmatel.c
sound/pci/hda/patch_via.c
sound/pci/rme96.c
sound/pci/rme9652/hdspm.c
sound/soc/Kconfig
sound/soc/Makefile
sound/soc/atmel/Kconfig
sound/soc/atmel/Makefile
sound/soc/atmel/atmel-pcm-dma.c
sound/soc/atmel/atmel_ssc_dai.c
sound/soc/atmel/atmel_wm8904.c [new file with mode: 0644]
sound/soc/atmel/sam9x5_wm8731.c [new file with mode: 0644]
sound/soc/au1x/db1200.c
sound/soc/au1x/psc-ac97.c
sound/soc/blackfin/bf5xx-ac97.h
sound/soc/cirrus/ep93xx-ac97.c
sound/soc/cirrus/ep93xx-i2s.c
sound/soc/codecs/Kconfig
sound/soc/codecs/Makefile
sound/soc/codecs/ac97.c
sound/soc/codecs/ad1980.c
sound/soc/codecs/ad73311.c
sound/soc/codecs/adau1701.c
sound/soc/codecs/adav80x.c
sound/soc/codecs/ads117x.c
sound/soc/codecs/ak4104.c
sound/soc/codecs/ak4554.c [new file with mode: 0644]
sound/soc/codecs/ak5386.c
sound/soc/codecs/arizona.c
sound/soc/codecs/arizona.h
sound/soc/codecs/bt-sco.c
sound/soc/codecs/cs4270.c
sound/soc/codecs/cs4271.c
sound/soc/codecs/dmic.c
sound/soc/codecs/hdmi.c
sound/soc/codecs/lm4857.c
sound/soc/codecs/max9768.c
sound/soc/codecs/max98090.c
sound/soc/codecs/max9877.c
sound/soc/codecs/mc13783.c
sound/soc/codecs/pcm1681.c [new file with mode: 0644]
sound/soc/codecs/pcm1792a.c [new file with mode: 0644]
sound/soc/codecs/pcm1792a.h [new file with mode: 0644]
sound/soc/codecs/pcm3008.c
sound/soc/codecs/rt5640.c
sound/soc/codecs/sgtl5000.c
sound/soc/codecs/si476x.c
sound/soc/codecs/spdif_receiver.c
sound/soc/codecs/spdif_transmitter.c
sound/soc/codecs/sta32x.c
sound/soc/codecs/tlv320aic26.c
sound/soc/codecs/tlv320aic32x4.c
sound/soc/codecs/tlv320aic3x.c
sound/soc/codecs/twl4030.c
sound/soc/codecs/twl6040.c
sound/soc/codecs/uda134x.c
sound/soc/codecs/wl1273.c
sound/soc/codecs/wm0010.c
sound/soc/codecs/wm5102.c
sound/soc/codecs/wm5110.c
sound/soc/codecs/wm8350.c
sound/soc/codecs/wm8727.c
sound/soc/codecs/wm8731.c
sound/soc/codecs/wm8753.c
sound/soc/codecs/wm8782.c
sound/soc/codecs/wm8903.c
sound/soc/codecs/wm8904.c
sound/soc/codecs/wm8960.c
sound/soc/codecs/wm8962.c
sound/soc/codecs/wm8994.c
sound/soc/codecs/wm8995.c
sound/soc/codecs/wm8997.c [new file with mode: 0644]
sound/soc/codecs/wm8997.h [new file with mode: 0644]
sound/soc/codecs/wm_adsp.c
sound/soc/codecs/wm_adsp.h
sound/soc/codecs/wm_hubs.c
sound/soc/dwc/designware_i2s.c
sound/soc/fsl/Kconfig
sound/soc/fsl/Makefile
sound/soc/fsl/fsl_spdif.c [new file with mode: 0644]
sound/soc/fsl/fsl_spdif.h [new file with mode: 0644]
sound/soc/fsl/fsl_ssi.c
sound/soc/fsl/imx-audmux.c
sound/soc/fsl/imx-audmux.h
sound/soc/fsl/imx-mc13783.c
sound/soc/fsl/imx-pcm-dma.c
sound/soc/fsl/imx-pcm-fiq.c
sound/soc/fsl/imx-pcm.h
sound/soc/fsl/imx-sgtl5000.c
sound/soc/fsl/imx-ssi.c
sound/soc/fsl/imx-ssi.h
sound/soc/fsl/imx-wm8962.c
sound/soc/generic/simple-card.c
sound/soc/kirkwood/Kconfig
sound/soc/kirkwood/Makefile
sound/soc/kirkwood/kirkwood-dma.c
sound/soc/kirkwood/kirkwood-i2s.c
sound/soc/kirkwood/kirkwood-openrd.c
sound/soc/kirkwood/kirkwood-t5325.c
sound/soc/kirkwood/kirkwood.h
sound/soc/mxs/Kconfig
sound/soc/mxs/mxs-saif.c
sound/soc/mxs/mxs-sgtl5000.c
sound/soc/nuc900/nuc900-ac97.c
sound/soc/omap/Kconfig
sound/soc/omap/mcbsp.c
sound/soc/omap/omap-abe-twl6040.c
sound/soc/omap/omap-dmic.c
sound/soc/omap/omap-mcbsp.c
sound/soc/omap/omap-mcpdm.c
sound/soc/pxa/Kconfig
sound/soc/pxa/brownstone.c
sound/soc/pxa/mioa701_wm9713.c
sound/soc/pxa/mmp-pcm.c
sound/soc/pxa/mmp-sspa.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/ttc-dkb.c
sound/soc/s6000/s6105-ipcam.c
sound/soc/samsung/ac97.c
sound/soc/samsung/dma.c
sound/soc/samsung/dma.h
sound/soc/samsung/i2s-regs.h
sound/soc/samsung/i2s.c
sound/soc/samsung/pcm.c
sound/soc/samsung/s3c2412-i2s.c
sound/soc/samsung/s3c24xx-i2s.c
sound/soc/samsung/smdk_wm8994.c
sound/soc/samsung/spdif.c
sound/soc/sh/Kconfig
sound/soc/sh/Makefile
sound/soc/sh/rcar/Makefile [new file with mode: 0644]
sound/soc/sh/rcar/adg.c [new file with mode: 0644]
sound/soc/sh/rcar/core.c [new file with mode: 0644]
sound/soc/sh/rcar/gen.c [new file with mode: 0644]
sound/soc/sh/rcar/rsnd.h [new file with mode: 0644]
sound/soc/sh/rcar/scu.c [new file with mode: 0644]
sound/soc/sh/rcar/ssi.c [new file with mode: 0644]
sound/soc/soc-compress.c
sound/soc/soc-core.c
sound/soc/soc-dapm.c
sound/soc/soc-jack.c
sound/soc/soc-pcm.c
sound/soc/spear/Kconfig
sound/soc/tegra/Kconfig
sound/soc/tegra/tegra20_ac97.c
sound/soc/tegra/tegra_alc5632.c
sound/soc/tegra/tegra_rt5640.c
sound/soc/tegra/tegra_wm8753.c
sound/soc/tegra/trimslice.c
sound/soc/txx9/txx9aclc-ac97.c
sound/soc/ux500/mop500.c
sound/usb/6fire/firmware.c
sound/usb/endpoint.c
sound/usb/pcm.c
sound/usb/usx2y/usbusx2y.c

index 38e51ad2e07e6b5665d6b35850734e5073d62bc0..a45ae08c8ed1d3fef886c34c2c07a501a0719520 100644 (file)
@@ -7,9 +7,30 @@ Required properties:
 - reg: Should contain SSC registers location and length
 - interrupts: Should contain SSC interrupt
 
-Example:
+
+Required properties for devices compatible with "atmel,at91sam9g45-ssc":
+- dmas: DMA specifier, consisting of a phandle to DMA controller node,
+  the memory interface and SSC DMA channel ID (for tx and rx).
+  See Documentation/devicetree/bindings/dma/atmel-dma.txt for details.
+- dma-names: Must be "tx", "rx".
+
+Examples:
+- PDC transfer:
 ssc0: ssc@fffbc000 {
        compatible = "atmel,at91rm9200-ssc";
        reg = <0xfffbc000 0x4000>;
        interrupts = <14 4 5>;
 };
+
+- DMA transfer:
+ssc0: ssc@f0010000 {
+      compatible = "atmel,at91sam9g45-ssc";
+      reg = <0xf0010000 0x4000>;
+      interrupts = <28 4 5>;
+      dmas = <&dma0 1 13>,
+            <&dma0 1 14>;
+      dma-names = "tx", "rx";
+      pinctrl-names = "default";
+      pinctrl-0 = <&pinctrl_ssc0_tx &pinctrl_ssc0_rx>;
+      status = "disabled";
+};
diff --git a/Documentation/devicetree/bindings/serial/mrvl,pxa-ssp.txt b/Documentation/devicetree/bindings/serial/mrvl,pxa-ssp.txt
new file mode 100644 (file)
index 0000000..669b814
--- /dev/null
@@ -0,0 +1,65 @@
+Device tree bindings for Marvell PXA SSP ports
+
+Required properties:
+
+       - compatible:   Must be one of
+                               mrvl,pxa25x-ssp
+                               mvrl,pxa25x-nssp
+                               mrvl,pxa27x-ssp
+                               mrvl,pxa3xx-ssp
+                               mvrl,pxa168-ssp
+                               mrvl,pxa910-ssp
+                               mrvl,ce4100-ssp
+                               mrvl,lpss-ssp
+
+       - reg:          The memory base
+       - dmas:         Two dma phandles, one for rx, one for tx
+       - dma-names:    Must be "rx", "tx"
+
+
+Example for PXA3xx:
+
+       ssp0: ssp@41000000 {
+               compatible = "mrvl,pxa3xx-ssp";
+               reg = <0x41000000 0x40>;
+               ssp-id = <1>;
+               interrupts = <24>;
+               clock-names = "pxa27x-ssp.0";
+               dmas = <&dma 13
+                       &dma 14>;
+               dma-names = "rx", "tx";
+       };
+
+       ssp1: ssp@41700000 {
+               compatible = "mrvl,pxa3xx-ssp";
+               reg = <0x41700000 0x40>;
+               ssp-id = <2>;
+               interrupts = <16>;
+               clock-names = "pxa27x-ssp.1";
+               dmas = <&dma 15
+                       &dma 16>;
+               dma-names = "rx", "tx";
+       };
+
+       ssp2: ssp@41900000 {
+               compatibl3 = "mrvl,pxa3xx-ssp";
+               reg = <0x41900000 0x40>;
+               ssp-id = <3>;
+               interrupts = <0>;
+               clock-names = "pxa27x-ssp.2";
+               dmas = <&dma 66
+                       &dma 67>;
+               dma-names = "rx", "tx";
+       };
+
+       ssp3: ssp@41a00000 {
+               compatible = "mrvl,pxa3xx-ssp";
+               reg = <0x41a00000 0x40>;
+               ssp-id = <4>;
+               interrupts = <13>;
+               clock-names = "pxa27x-ssp.3";
+               dmas = <&dma 2
+                       &dma 3>;
+               dma-names = "rx", "tx";
+       };
+
diff --git a/Documentation/devicetree/bindings/sound/ak4554.c b/Documentation/devicetree/bindings/sound/ak4554.c
new file mode 100644 (file)
index 0000000..934fa02
--- /dev/null
@@ -0,0 +1,11 @@
+AK4554 ADC/DAC
+
+Required properties:
+
+  - compatible : "asahi-kasei,ak4554"
+
+Example:
+
+ak4554-adc-dac {
+       compatible = "asahi-kasei,ak4554";
+};
index 8608f747dcfe1869ae5061007a2fd583000ef545..ffd886d110bdcbc6aa79d2372149ae14867b2449 100644 (file)
@@ -13,6 +13,25 @@ Required properties:
   - #gpio-cells : Should be two. The first cell is the pin number and the
     second cell is used to specify optional parameters (currently unused).
 
+Pins on the device (for linking into audio routes):
+
+  * SPK_OUTP
+  * SPK_OUTN
+  * HP_OUT_L
+  * HP_OUT_R
+  * AUX_OUT_P
+  * AUX_OUT_N
+  * LINE_IN_L
+  * LINE_IN_R
+  * PHONE_P
+  * PHONE_N
+  * MIC1_P
+  * MIC1_N
+  * MIC2_P
+  * MIC2_N
+  * MICBIAS1
+  * DMICDAT
+
 Example:
 
 alc5632: alc5632@1e {
diff --git a/Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt b/Documentation/devicetree/bindings/sound/atmel-sam9x5-wm8731-audio.txt
new file mode 100644 (file)
index 0000000..0720857
--- /dev/null
@@ -0,0 +1,35 @@
+* Atmel at91sam9x5ek wm8731 audio complex
+
+Required properties:
+  - compatible: "atmel,sam9x5-wm8731-audio"
+  - atmel,model: The user-visible name of this sound complex.
+  - atmel,ssc-controller: The phandle of the SSC controller
+  - atmel,audio-codec: The phandle of the WM8731 audio codec
+  - atmel,audio-routing: A list of the connections between audio components.
+    Each entry is a pair of strings, the first being the connection's sink,
+    the second being the connection's source.
+
+Available audio endpoints for the audio-routing table:
+
+Board connectors:
+ * Headphone Jack
+ * Line In Jack
+
+wm8731 pins:
+cf Documentation/devicetree/bindings/sound/wm8731.txt
+
+Example:
+sound {
+       compatible = "atmel,sam9x5-wm8731-audio";
+
+       atmel,model = "wm8731 @ AT91SAM9X5EK";
+
+       atmel,audio-routing =
+               "Headphone Jack", "RHPOUT",
+               "Headphone Jack", "LHPOUT",
+               "LLINEIN", "Line In Jack",
+               "RLINEIN", "Line In Jack";
+
+       atmel,ssc-controller = <&ssc0>;
+       atmel,audio-codec = <&wm8731>;
+};
diff --git a/Documentation/devicetree/bindings/sound/atmel-wm8904.txt b/Documentation/devicetree/bindings/sound/atmel-wm8904.txt
new file mode 100644 (file)
index 0000000..8bbe50c
--- /dev/null
@@ -0,0 +1,55 @@
+Atmel ASoC driver with wm8904 audio codec complex
+
+Required properties:
+  - compatible: "atmel,asoc-wm8904"
+  - atmel,model: The user-visible name of this sound complex.
+  - atmel,audio-routing: A list of the connections between audio components.
+    Each entry is a pair of strings, the first being the connection's sink,
+    the second being the connection's source. Valid names for sources and
+    sinks are the WM8904's pins, and the jacks on the board:
+
+    WM8904 pins:
+
+    * IN1L
+    * IN1R
+    * IN2L
+    * IN2R
+    * IN3L
+    * IN3R
+    * HPOUTL
+    * HPOUTR
+    * LINEOUTL
+    * LINEOUTR
+    * MICBIAS
+
+    Board connectors:
+
+    * Headphone Jack
+    * Line In Jack
+    * Mic
+
+  - atmel,ssc-controller: The phandle of the SSC controller
+  - atmel,audio-codec: The phandle of the WM8904 audio codec
+
+Optional properties:
+  - pinctrl-names, pinctrl-0: Please refer to pinctrl-bindings.txt
+
+Example:
+sound {
+       compatible = "atmel,asoc-wm8904";
+       pinctrl-names = "default";
+       pinctrl-0 = <&pinctrl_pck0_as_mck>;
+
+       atmel,model = "wm8904 @ AT91SAM9N12EK";
+
+       atmel,audio-routing =
+               "Headphone Jack", "HPOUTL",
+               "Headphone Jack", "HPOUTR",
+               "IN2L", "Line In Jack",
+               "IN2R", "Line In Jack",
+               "Mic", "MICBIAS",
+               "IN1L", "Mic";
+
+       atmel,ssc-controller = <&ssc0>;
+       atmel,audio-codec = <&wm8904>;
+};
diff --git a/Documentation/devicetree/bindings/sound/fsl,spdif.txt b/Documentation/devicetree/bindings/sound/fsl,spdif.txt
new file mode 100644 (file)
index 0000000..f2ae335
--- /dev/null
@@ -0,0 +1,54 @@
+Freescale Sony/Philips Digital Interface Format (S/PDIF) Controller
+
+The Freescale 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 : Compatible list, must contain "fsl,imx35-spdif".
+
+  - reg : Offset and length of the register set for the device.
+
+  - interrupts : Contains the spdif interrupt.
+
+  - dmas : Generic dma devicetree binding as described in
+  Documentation/devicetree/bindings/dma/dma.txt.
+
+  - dma-names : Two dmas have to be defined, "tx" and "rx".
+
+  - clocks : Contains an entry for each entry in clock-names.
+
+  - clock-names : Includes the following entries:
+       "core"          The core clock of spdif controller
+       "rxtx<0-7>"     Clock source list for tx and rx clock.
+                       This clock list should be identical to
+                       the source list connecting to the spdif
+                       clock mux in "SPDIF Transceiver Clock
+                       Diagram" of SoC reference manual. It
+                       can also be referred to TxClk_Source
+                       bit of register SPDIF_STC.
+
+Example:
+
+spdif: spdif@02004000 {
+       compatible = "fsl,imx35-spdif";
+       reg = <0x02004000 0x4000>;
+       interrupts = <0 52 0x04>;
+       dmas = <&sdma 14 18 0>,
+              <&sdma 15 18 0>;
+       dma-names = "rx", "tx";
+
+       clocks = <&clks 197>, <&clks 3>,
+              <&clks 197>, <&clks 107>,
+              <&clks 0>, <&clks 118>,
+              <&clks 62>, <&clks 139>,
+              <&clks 0>;
+       clock-names = "core", "rxtx0",
+               "rxtx1", "rxtx2",
+               "rxtx3", "rxtx4",
+               "rxtx5", "rxtx6",
+               "rxtx7";
+
+       status = "okay";
+};
similarity index 87%
rename from Documentation/devicetree/bindings/powerpc/fsl/ssi.txt
rename to Documentation/devicetree/bindings/sound/fsl,ssi.txt
index 5ff76c9c57d27212d88723b85442e431cfa1aed4..4303b6ab6208e0856f81bc2fbc0ab8c4e81f519c 100644 (file)
@@ -43,10 +43,22 @@ Required properties:
                     together.  This would still allow different sample sizes,
                     but not different sample rates.
 
+Required are also ac97 link bindings if ac97 is used. See
+Documentation/devicetree/bindings/sound/soc-ac97link.txt for the necessary
+bindings.
+
 Optional properties:
 - codec-handle:     Phandle to a 'codec' node that defines an audio
                     codec connected to this SSI.  This node is typically
                     a child of an I2C or other control node.
+- fsl,fiq-stream-filter: Bool property. Disabled DMA and use FIQ instead to
+                   filter the codec stream. This is necessary for some boards
+                   where an incompatible codec is connected to this SSI, e.g.
+                   on pca100 and pcm043.
+- dmas:                    Generic dma devicetree binding as described in
+                   Documentation/devicetree/bindings/dma/dma.txt.
+- dma-names:       Two dmas have to be defined, "tx" and "rx", if fsl,imx-fiq
+                   is not defined.
 
 Child 'codec' node required properties:
 - compatible:       Compatible list, contains the name of the codec
index 215aa9817213e9ea8e469a94f19fcc251aa87352..f88a00e54c6351cca3972943482d14b620ba3671 100644 (file)
@@ -5,6 +5,15 @@ Required properties:
   or "fsl,imx31-audmux" for the version firstly used on i.MX31.
 - reg : Should contain AUDMUX registers location and length
 
+An initial configuration can be setup using child nodes.
+
+Required properties of optional child nodes:
+- fsl,audmux-port : Integer of the audmux port that is configured by this
+  child node.
+- fsl,port-config : List of configuration options for the specific port. For
+  imx31-audmux and above, it is a list of tuples <ptcr pdcr>. For
+  imx21-audmux it is a list of pcr values.
+
 Example:
 
 audmux@021d8000 {
diff --git a/Documentation/devicetree/bindings/sound/mrvl,pxa-ssp.txt b/Documentation/devicetree/bindings/sound/mrvl,pxa-ssp.txt
new file mode 100644 (file)
index 0000000..74c9ba6
--- /dev/null
@@ -0,0 +1,28 @@
+Marvell PXA SSP CPU DAI bindings
+
+Required properties:
+
+       compatible      Must be "mrvl,pxa-ssp-dai"
+       port            A phandle reference to a PXA ssp upstream device
+
+Example:
+
+       /* upstream device */
+
+       ssp0: ssp@41000000 {
+               compatible = "mrvl,pxa3xx-ssp";
+               reg = <0x41000000 0x40>;
+               interrupts = <24>;
+               clock-names = "pxa27x-ssp.0";
+               dmas = <&dma 13
+                       &dma 14>;
+               dma-names = "rx", "tx";
+       };
+
+       /* DAI as user */
+
+       ssp_dai0: ssp_dai@0 {
+               compatible = "mrvl,pxa-ssp-dai";
+               port = <&ssp0>;
+       };
+
diff --git a/Documentation/devicetree/bindings/sound/mrvl,pxa2xx-pcm.txt b/Documentation/devicetree/bindings/sound/mrvl,pxa2xx-pcm.txt
new file mode 100644 (file)
index 0000000..551fbb8
--- /dev/null
@@ -0,0 +1,15 @@
+DT bindings for ARM PXA2xx PCM platform driver
+
+This is just a dummy driver that registers the PXA ASoC platform driver.
+It does not have any resources assigned.
+
+Required properties:
+
+       - compatible            'mrvl,pxa-pcm-audio'
+
+Example:
+
+       pxa_pcm_audio: snd_soc_pxa_audio {
+               compatible = "mrvl,pxa-pcm-audio";
+       };
+
index 05ffecb571037cb562778cb243a84555384345d1..8b8903ef0800069afdad90f10e5964f3bd2e20c9 100644 (file)
@@ -11,28 +11,8 @@ Required properties:
 - nvidia,audio-routing : A list of the connections between audio components.
   Each entry is a pair of strings, the first being the connection's sink,
   the second being the connection's source. Valid names for sources and
-  sinks are the ALC5632's pins:
-
-  ALC5632 pins:
-
-  * SPK_OUTP
-  * SPK_OUTN
-  * HP_OUT_L
-  * HP_OUT_R
-  * AUX_OUT_P
-  * AUX_OUT_N
-  * LINE_IN_L
-  * LINE_IN_R
-  * PHONE_P
-  * PHONE_N
-  * MIC1_P
-  * MIC1_N
-  * MIC2_P
-  * MIC2_N
-  * MICBIAS1
-  * DMICDAT
-
-  Board connectors:
+  sinks are the ALC5632's pins as documented in the binding for the device
+  and:
 
   * Headset Stereophone
   * Int Spk
index d130818700b228a200e4c81c35e7406f6e792e3a..dc6224994d69d97f31614a308d71ba6cf2c9784c 100644 (file)
@@ -11,32 +11,12 @@ Required properties:
 - nvidia,audio-routing : A list of the connections between audio components.
   Each entry is a pair of strings, the first being the connection's sink,
   the second being the connection's source. Valid names for sources and
-  sinks are the RT5640's pins, and the jacks on the board:
-
-  RT5640 pins:
-
-  * DMIC1
-  * DMIC2
-  * MICBIAS1
-  * IN1P
-  * IN1R
-  * IN2P
-  * IN2R
-  * HPOL
-  * HPOR
-  * LOUTL
-  * LOUTR
-  * MONOP
-  * MONON
-  * SPOLP
-  * SPOLN
-  * SPORP
-  * SPORN
-
-  Board connectors:
+  sinks are the RT5640's pins (as documented in its binding), and the jacks
+  on the board:
 
   * Headphones
   * Speakers
+  * Mic Jack
 
 - nvidia,i2s-controller : The phandle of the Tegra I2S controller that's
   connected to the CODEC.
index d14510613a7fdf1ac076320a04468976aa88811a..aab6ce0ad2fc5930054a1f97c73ea5781aa57e10 100644 (file)
@@ -11,31 +11,8 @@ Required properties:
 - nvidia,audio-routing : A list of the connections between audio components.
   Each entry is a pair of strings, the first being the connection's sink,
   the second being the connection's source. Valid names for sources and
-  sinks are the WM8753's pins, and the jacks on the board:
-
-  WM8753 pins:
-
-  * LOUT1
-  * LOUT2
-  * ROUT1
-  * ROUT2
-  * MONO1
-  * MONO2
-  * OUT3
-  * OUT4
-  * LINE1
-  * LINE2
-  * RXP
-  * RXN
-  * ACIN
-  * ACOP
-  * MIC1N
-  * MIC1
-  * MIC2N
-  * MIC2
-  * Mic Bias
-
-  Board connectors:
+  sinks are the WM8753's pins as documented in the binding for the WM8753,
+  and the jacks on the board:
 
   * Headphone Jack
   * Mic Jack
index 3bf722deb722265422283b4b897c37ffc74d31ae..4b44dfb6ca0dcb08e5ad4468df6aeee853d3d56e 100644 (file)
@@ -11,28 +11,8 @@ Required properties:
 - nvidia,audio-routing : A list of the connections between audio components.
   Each entry is a pair of strings, the first being the connection's sink,
   the second being the connection's source. Valid names for sources and
-  sinks are the WM8903's pins, and the jacks on the board:
-
-  WM8903 pins:
-
-  * IN1L
-  * IN1R
-  * IN2L
-  * IN2R
-  * IN3L
-  * IN3R
-  * DMICDAT
-  * HPOUTL
-  * HPOUTR
-  * LINEOUTL
-  * LINEOUTR
-  * LOP
-  * LON
-  * ROP
-  * RON
-  * MICBIAS
-
-  Board connectors:
+  sinks are the WM8903's pins (documented in the WM8903 binding document),
+  and the jacks on the board:
 
   * Headphone Jack
   * Int Spk
diff --git a/Documentation/devicetree/bindings/sound/pcm1792a.txt b/Documentation/devicetree/bindings/sound/pcm1792a.txt
new file mode 100644 (file)
index 0000000..970ba1e
--- /dev/null
@@ -0,0 +1,18 @@
+Texas Instruments pcm1792a DT bindings
+
+This driver supports the SPI bus.
+
+Required properties:
+
+ - compatible: "ti,pcm1792a"
+
+For required properties on SPI, please consult
+Documentation/devicetree/bindings/spi/spi-bus.txt
+
+Examples:
+
+       codec_spi: 1792a@0 {
+               compatible = "ti,pcm1792a";
+               spi-max-frequency = <600000>;
+       };
+
index 005bcb24d72dc1e5e359aeadf9842eda277d23e1..068a1141b06f19de0f5206e97de40f0af6793812 100644 (file)
@@ -18,6 +18,26 @@ Optional properties:
 
 - realtek,ldo1-en-gpios : The GPIO that controls the CODEC's LDO1_EN pin.
 
+Pins on the device (for linking into audio routes):
+
+  * DMIC1
+  * DMIC2
+  * MICBIAS1
+  * IN1P
+  * IN1R
+  * IN2P
+  * IN2R
+  * HPOL
+  * HPOR
+  * LOUTL
+  * LOUTR
+  * MONOP
+  * MONON
+  * SPOLP
+  * SPOLN
+  * SPORP
+  * SPORN
+
 Example:
 
 rt5640 {
index 025e66b85a43905b3cb64b2e5590872d0a000b3e..7386d444ada1b4a7bcb7f29b29a5ef45b70ace48 100644 (file)
@@ -2,7 +2,15 @@
 
 Required SoC Specific Properties:
 
-- compatible : "samsung,i2s-v5"
+- compatible : should be one of the following.
+   - samsung,s3c6410-i2s: for 8/16/24bit stereo I2S.
+   - samsung,s5pv210-i2s: for 8/16/24bit multichannel(5.1) I2S with
+     secondary fifo, s/w reset control and internal mux for root clk src.
+   - samsung,exynos5420-i2s: for 8/16/24bit multichannel(7.1) I2S with
+     secondary fifo, s/w reset control, internal mux for root clk src and
+     TDM support. TDM (Time division multiplexing) is to allow transfer of
+     multiple channel audio data on single data line.
+
 - reg: physical base address of the controller and length of memory mapped
   region.
 - dmas: list of DMA controller phandle and DMA request line ordered pairs.
@@ -21,13 +29,6 @@ Required SoC Specific Properties:
 
 Optional SoC Specific Properties:
 
-- samsung,supports-6ch: If the I2S Primary sound source has 5.1 Channel
-  support, this flag is enabled.
-- samsung,supports-rstclr: This flag should be set if I2S software reset bit
-  control is required. When this flag is set I2S software reset bit will be
-  enabled or disabled based on need.
-- samsung,supports-secdai:If I2S block has a secondary FIFO and internal DMA,
-  then this flag is enabled.
 - samsung,idma-addr: Internal DMA register base address of the audio
   sub system(used in secondary sound source).
 - pinctrl-0: Should specify pin control groups used for this controller.
@@ -36,7 +37,7 @@ Optional SoC Specific Properties:
 Example:
 
 i2s0: i2s@03830000 {
-       compatible = "samsung,i2s-v5";
+       compatible = "samsung,s5pv210-i2s";
        reg = <0x03830000 0x100>;
        dmas = <&pdma0 10
                &pdma0 9
@@ -46,9 +47,6 @@ i2s0: i2s@03830000 {
                <&clock_audss EXYNOS_I2S_BUS>,
                <&clock_audss EXYNOS_SCLK_I2S>;
        clock-names = "iis", "i2s_opclk0", "i2s_opclk1";
-       samsung,supports-6ch;
-       samsung,supports-rstclr;
-       samsung,supports-secdai;
        samsung,idma-addr = <0x03000000>;
        pinctrl-names = "default";
        pinctrl-0 = <&i2s0_bus>;
diff --git a/Documentation/devicetree/bindings/sound/soc-ac97link.txt b/Documentation/devicetree/bindings/sound/soc-ac97link.txt
new file mode 100644 (file)
index 0000000..80152a8
--- /dev/null
@@ -0,0 +1,28 @@
+AC97 link bindings
+
+These bindings can be included within any other device node.
+
+Required properties:
+ - pinctrl-names: Has to contain following states to setup the correct
+   pinmuxing for the used gpios:
+       "ac97-running": AC97-link is active
+       "ac97-reset": AC97-link reset state
+       "ac97-warm-reset": AC97-link warm reset state
+ - ac97-gpios: List of gpio phandles with args in the order ac97-sync,
+   ac97-sdata, ac97-reset
+
+
+Example:
+
+ssi {
+       ...
+
+       pinctrl-names = "default", "ac97-running", "ac97-reset", "ac97-warm-reset";
+       pinctrl-0 = <&ac97link_running>;
+       pinctrl-1 = <&ac97link_running>;
+       pinctrl-2 = <&ac97link_reset>;
+       pinctrl-3 = <&ac97link_warm_reset>;
+       ac97-gpios = <&gpio3 20 0 &gpio3 22 0 &gpio3 28 0>;
+
+       ...
+};
diff --git a/Documentation/devicetree/bindings/sound/ti,pcm1681.txt b/Documentation/devicetree/bindings/sound/ti,pcm1681.txt
new file mode 100644 (file)
index 0000000..4df1718
--- /dev/null
@@ -0,0 +1,15 @@
+Texas Instruments PCM1681 8-channel PWM Processor
+
+Required properties:
+
+ - compatible:         Should contain "ti,pcm1681".
+ - reg:                        The i2c address. Should contain <0x4c>.
+
+Examples:
+
+       i2c_bus {
+               pcm1681@4c {
+                       compatible = "ti,pcm1681";
+                       reg = <0x4c>;
+               };
+       };
index f47c3f589fd03a8deb2485292116e33d17a0f7da..705a6b156c6c4fc00713443f5fa631996b259401 100644 (file)
@@ -3,7 +3,14 @@ Texas Instruments - tlv320aic3x Codec module
 The tlv320aic3x serial control bus communicates through I2C protocols
 
 Required properties:
-- compatible - "string" -  "ti,tlv320aic3x"
+
+- compatible - "string" - One of:
+    "ti,tlv320aic3x" - Generic TLV320AIC3x device
+    "ti,tlv320aic33" - TLV320AIC33
+    "ti,tlv320aic3007" - TLV320AIC3007
+    "ti,tlv320aic3106" - TLV320AIC3106
+
+
 - reg - <int> -  I2C slave address
 
 
index 15f70048469bdde098a857c6f2566d94ce320d2a..236690e99b87fbc81c0cb5b1823981cf1e87ff4d 100644 (file)
@@ -16,3 +16,12 @@ codec: wm8731@1a {
        compatible = "wlf,wm8731";
        reg = <0x1a>;
 };
+
+Available audio endpoints for an audio-routing table:
+ * LOUT: Left Channel Line Output
+ * ROUT: Right Channel Line Output
+ * LHPOUT: Left Channel Headphone Output
+ * RHPOUT: Right Channel Headphone Output
+ * LLINEIN: Left Channel Line Input
+ * RLINEIN: Right Channel Line Input
+ * MICIN: Microphone Input
index e65277a0fb60dee8b720ef3246cb2ae581f640c7..8eee6128210552e0d8decfedeffbc0b216a9ec21 100644 (file)
@@ -10,9 +10,31 @@ Required properties:
   - reg : the I2C address of the device for I2C, the chip select
           number for SPI.
 
+Pins on the device (for linking into audio routes):
+
+  * LOUT1
+  * LOUT2
+  * ROUT1
+  * ROUT2
+  * MONO1
+  * MONO2
+  * OUT3
+  * OUT4
+  * LINE1
+  * LINE2
+  * RXP
+  * RXN
+  * ACIN
+  * ACOP
+  * MIC1N
+  * MIC1
+  * MIC2N
+  * MIC2
+  * Mic Bias
+
 Example:
 
-codec: wm8737@1a {
+codec: wm8753@1a {
        compatible = "wlf,wm8753";
        reg = <0x1a>;
 };
index f102cbc426943370dad18b96f311827a86e39dad..94ec32c194bb1fc8f3dd4b6bbe10069008e4d673 100644 (file)
@@ -28,6 +28,25 @@ Optional properties:
     performed. If any entry has the value 0xffffffff, that GPIO's
     configuration will not be modified.
 
+Pins on the device (for linking into audio routes):
+
+  * IN1L
+  * IN1R
+  * IN2L
+  * IN2R
+  * IN3L
+  * IN3R
+  * DMICDAT
+  * HPOUTL
+  * HPOUTR
+  * LINEOUTL
+  * LINEOUTR
+  * LOP
+  * LON
+  * ROP
+  * RON
+  * MICBIAS
+
 Example:
 
 codec: wm8903@1a {
index f2f3e80934d227f123e4ecef3f8562c1530ed70a..e045e90a0924bc6e4d8a1861cd1a43b1a7a8d178 100644 (file)
@@ -32,6 +32,10 @@ Optional properties:
     The second cell is the flags, encoded as the trigger masks from
     Documentation/devicetree/bindings/interrupts.txt
 
+  - clocks : A list of up to two phandle and clock specifier pairs
+  - clock-names : A list of clock names sorted in the same order as clocks.
+                  Valid clock names are "MCLK1" and "MCLK2".
+
   - wlf,gpio-cfg : A list of GPIO configuration register values. If absent,
     no configuration of these registers is performed. If any value is
     over 0xffff then the register will be left as default. If present 11
index 15356aca938cd9a7bb2cdef09d8e7a19da36db90..7f9d4f53882c457ab8aa7f3e48c5fbbae7e903c2 100644 (file)
@@ -2953,7 +2953,7 @@ bytes respectively. Such letter suffixes can also be entirely omitted.
                        improve throughput, but will also increase the
                        amount of memory reserved for use by the client.
 
-       swapaccount[=0|1]
+       swapaccount=[0|1]
                        [KNL] Enable accounting of swap in memory resource
                        controller if no parameter or 1 is given or disable
                        it if 0 is given (See Documentation/cgroups/memory.txt)
index 809d72b8eff1a8217dc5e7666f3e895019421f9c..a46ddb85e83a0dcdf0f2a54fd9b745eadb31cb6f 100644 (file)
@@ -244,6 +244,7 @@ STAC9227/9228/9229/927x
   5stack-no-fp D965 5stack without front panel
   dell-3stack  Dell Dimension E520
   dell-bios    Fixes with Dell BIOS setup
+  dell-bios-amic Fixes with Dell BIOS setup including analog mic
   volknob      Fixes with volume-knob widget 0x24
   auto         BIOS setup (default)
 
index c3c912d023cc5c184c6f0496344cb1a1a82265eb..42a0a39b77e6a5da339e0f71717a6d3809986c24 100644 (file)
@@ -454,6 +454,8 @@ The generic parser supports the following hints:
 - need_dac_fix (bool): limits the DACs depending on the channel count
 - primary_hp (bool): probe headphone jacks as the primary outputs;
   default true
+- multi_io (bool): try probing multi-I/O config (e.g. shared
+  line-in/surround, mic/clfe jacks)
 - multi_cap_vol (bool): provide multiple capture volumes
 - inv_dmic_split (bool): provide split internal mic volume/switch for
   phase-inverted digital mics
index 229c66b12cc21b9ebe6479932491ecd5e8784118..ee738a6bfc3212eda86010dc285c5398580a73cd 100644 (file)
@@ -595,6 +595,7 @@ S:  Supported
 F:     sound/soc/codecs/adau*
 F:     sound/soc/codecs/adav*
 F:     sound/soc/codecs/ad1*
+F:     sound/soc/codecs/ad7*
 F:     sound/soc/codecs/ssm*
 F:     sound/soc/codecs/sigmadsp.*
 
@@ -5884,7 +5885,7 @@ F:        drivers/i2c/busses/i2c-omap.c
 F:     include/linux/i2c-omap.h
 
 OMAP DEVICE TREE SUPPORT
-M:     Benoît Cousson <b-cousson@ti.com>
+M:     Benoît Cousson <bcousson@baylibre.com>
 M:     Tony Lindgren <tony@atomide.com>
 L:     linux-omap@vger.kernel.org
 L:     devicetree@vger.kernel.org
@@ -5964,14 +5965,14 @@ S:      Maintained
 F:     drivers/char/hw_random/omap-rng.c
 
 OMAP HWMOD SUPPORT
-M:     Benoît Cousson <b-cousson@ti.com>
+M:     Benoît Cousson <bcousson@baylibre.com>
 M:     Paul Walmsley <paul@pwsan.com>
 L:     linux-omap@vger.kernel.org
 S:     Maintained
 F:     arch/arm/mach-omap2/omap_hwmod.*
 
 OMAP HWMOD DATA FOR OMAP4-BASED DEVICES
-M:     Benoît Cousson <b-cousson@ti.com>
+M:     Benoît Cousson <bcousson@baylibre.com>
 L:     linux-omap@vger.kernel.org
 S:     Maintained
 F:     arch/arm/mach-omap2/omap_hwmod_44xx_data.c
@@ -7675,6 +7676,17 @@ F:       include/sound/
 F:     include/uapi/sound/
 F:     sound/
 
+SOUND - COMPRESSED AUDIO
+M:     Vinod Koul <vinod.koul@intel.com>
+L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
+T:     git git://git.kernel.org/pub/scm/linux/kernel/git/tiwai/sound.git
+S:     Supported
+F:     Documentation/sound/alsa/compress_offload.txt
+F:     include/sound/compress_driver.h
+F:     include/uapi/sound/compress_*
+F:     sound/core/compress_offload.c
+F:     sound/soc/soc-compress.c
+
 SOUND - SOC LAYER / DYNAMIC AUDIO POWER MANAGEMENT (ASoC)
 M:     Liam Girdwood <lgirdwood@gmail.com>
 M:     Mark Brown <broonie@kernel.org>
@@ -7682,6 +7694,7 @@ T:        git git://git.kernel.org/pub/scm/linux/kernel/git/broonie/sound.git
 L:     alsa-devel@alsa-project.org (moderated for non-subscribers)
 W:     http://alsa-project.org/main/index.php/ASoC
 S:     Supported
+F:     Documentation/sound/alsa/soc/
 F:     sound/soc/
 F:     include/sound/soc*
 
index a5a55f4547c6e79fa17a0051dc5cce1daaec2a50..369882e4fc7718dd4e114a24be5514a43d3a1ef1 100644 (file)
--- a/Makefile
+++ b/Makefile
@@ -1,7 +1,7 @@
 VERSION = 3
 PATCHLEVEL = 11
 SUBLEVEL = 0
-EXTRAVERSION = -rc6
+EXTRAVERSION = -rc7
 NAME = Linux for Workgroups
 
 # *DOCUMENTATION*
index 99c10475d477c73957353e5e9df751718289b5ac..9c548c7cf0014e1ce9c0823026039d60365b6192 100644 (file)
@@ -39,9 +39,18 @@ ARC_ENTRY strchr
        ld.a    r2,[r0,4]
        sub     r12,r6,r7
        bic     r12,r12,r6
+#ifdef __LITTLE_ENDIAN__
        and     r7,r12,r4
        breq    r7,0,.Loop ; For speed, we want this branch to be unaligned.
        b       .Lfound_char ; Likewise this one.
+#else
+       and     r12,r12,r4
+       breq    r12,0,.Loop ; For speed, we want this branch to be unaligned.
+       lsr_s   r12,r12,7
+       bic     r2,r7,r6
+       b.d     .Lfound_char_b
+       and_s   r2,r2,r12
+#endif
 ; /* We require this code address to be unaligned for speed...  */
 .Laligned:
        ld_s    r2,[r0]
@@ -95,6 +104,7 @@ ARC_ENTRY strchr
        lsr     r7,r7,7
 
        bic     r2,r7,r6
+.Lfound_char_b:
        norm    r2,r2
        sub_s   r0,r0,4
        asr_s   r2,r2,3
index d59b70c6a6a0dbadafcded2baaba38f12270aff4..3d77dbe406f4736aacb7a1d361f4f02758225aa0 100644 (file)
        compatible = "atmel,at91sam9n12ek", "atmel,at91sam9n12", "atmel,at91sam9";
 
        chosen {
-               bootargs = "mem=128M console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2";
+               bootargs = "console=ttyS0,115200 root=/dev/mtdblock1 rw rootfstype=jffs2";
        };
 
        memory {
-               reg = <0x20000000 0x10000000>;
+               reg = <0x20000000 0x8000000>;
        };
 
        clocks {
index b753855b20584320d00c9b38c0a4c40d972a1b1b..49e3c45818c236caf750fe5f42137b963f0eb7c2 100644 (file)
@@ -94,8 +94,9 @@
 
                usb0: ohci@00600000 {
                        status = "okay";
-                       num-ports = <2>;
-                       atmel,vbus-gpio = <&pioD 19 GPIO_ACTIVE_LOW
+                       num-ports = <3>;
+                       atmel,vbus-gpio = <0 /* &pioD 18 GPIO_ACTIVE_LOW *//* Activate to have access to port A */
+                                          &pioD 19 GPIO_ACTIVE_LOW
                                           &pioD 20 GPIO_ACTIVE_LOW
                                          >;
                };
index ef57277fc38fa4b79262dd056a896fe6c92c77cb..376090f07231919eb1348a5b58ede1ea7f0853cd 100644 (file)
        };
 
        i2s0: i2s@03830000 {
-               compatible = "samsung,i2s-v5";
+               compatible = "samsung,s5pv210-i2s";
                reg = <0x03830000 0x100>;
                dmas = <&pdma0 10
                        &pdma0 9
                        <&clock_audss EXYNOS_I2S_BUS>,
                        <&clock_audss EXYNOS_SCLK_I2S>;
                clock-names = "iis", "i2s_opclk0", "i2s_opclk1";
-               samsung,supports-6ch;
-               samsung,supports-rstclr;
-               samsung,supports-secdai;
                samsung,idma-addr = <0x03000000>;
                pinctrl-names = "default";
                pinctrl-0 = <&i2s0_bus>;
        };
 
        i2s1: i2s@12D60000 {
-               compatible = "samsung,i2s-v5";
+               compatible = "samsung,s3c6410-i2s";
                reg = <0x12D60000 0x100>;
                dmas = <&pdma1 12
                        &pdma1 11>;
        };
 
        i2s2: i2s@12D70000 {
-               compatible = "samsung,i2s-v5";
+               compatible = "samsung,s3c6410-i2s";
                reg = <0x12D70000 0x100>;
                dmas = <&pdma0 12
                        &pdma0 11>;
index 365760b33a26e1ea9ac7bae3a7c32f7f4bde9878..40e6fb280333ec190864a0c3ef55ba6007371ac4 100644 (file)
                        regulator-max-microvolt = <5000000>;
                        enable-active-high;
                        gpio = <&gpio 24 0>; /* PD0 */
+                       regulator-always-on;
+                       regulator-boot-on;
                };
        };
 
index ed4b901b0227405f3cd687f1a832cad4db808f22..37c93d3c4812ec65d9ef7689bb173757d37d9060 100644 (file)
                        regulator-max-microvolt = <5000000>;
                        enable-active-high;
                        gpio = <&gpio 170 0>; /* PV2 */
+                       regulator-always-on;
+                       regulator-boot-on;
                };
        };
 
index ab67c94db280cebb2fc8c5e7126977a3fee8fb40..a3d0ebad78a1137eca61fea4781a178af0d516cc 100644 (file)
                        regulator-max-microvolt = <5000000>;
                        enable-active-high;
                        gpio = <&tca6416 0 0>; /* GPIO_PMU0 */
+                       regulator-always-on;
+                       regulator-boot-on;
                };
 
                vbus3_reg: regulator@3 {
                        regulator-max-microvolt = <5000000>;
                        enable-active-high;
                        gpio = <&tca6416 1 0>; /* GPIO_PMU1 */
+                       regulator-always-on;
+                       regulator-boot-on;
                };
        };
 
index fc7920288a3d90a3f9c3ca38be03ff845f84515a..918875d96d5dc598985c7dce050e0a1785637b49 100644 (file)
@@ -89,7 +89,8 @@ void set_fiq_handler(void *start, unsigned int length)
 
        memcpy(base + offset, start, length);
        if (!cache_is_vipt_nonaliasing())
-               flush_icache_range(base + offset, offset + length);
+               flush_icache_range((unsigned long)base + offset, offset +
+                                  length);
        flush_icache_range(0xffff0000 + offset, 0xffff0000 + offset + length);
 }
 
index d7c82df692436df0248fa1a00502cf74b7fca23c..57221e349a7ce0eec03445cd56de2845e5d23358 100644 (file)
@@ -82,6 +82,7 @@ void machine_crash_nonpanic_core(void *unused)
        crash_save_cpu(&regs, smp_processor_id());
        flush_cache_all();
 
+       set_cpu_online(smp_processor_id(), false);
        atomic_dec(&waiting_for_crash_ipi);
        while (1)
                cpu_relax();
index 4a5199070430672728c91dfe610047e83b78a964..db9cf692d4dded3e2a6cc7e5622ba90ee5bef2e8 100644 (file)
@@ -146,7 +146,11 @@ static bool pm_fake(struct kvm_vcpu *vcpu,
 #define access_pmintenclr pm_fake
 
 /* Architected CP15 registers.
- * Important: Must be sorted ascending by CRn, CRM, Op1, Op2
+ * CRn denotes the primary register number, but is copied to the CRm in the
+ * user space API for 64-bit register access in line with the terminology used
+ * in the ARM ARM.
+ * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 and with 64-bit
+ *            registers preceding 32-bit ones.
  */
 static const struct coproc_reg cp15_regs[] = {
        /* CSSELR: swapped by interrupt.S. */
@@ -154,8 +158,8 @@ static const struct coproc_reg cp15_regs[] = {
                        NULL, reset_unknown, c0_CSSELR },
 
        /* TTBR0/TTBR1: swapped by interrupt.S. */
-       { CRm( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 },
-       { CRm( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 },
+       { CRm64( 2), Op1( 0), is64, NULL, reset_unknown64, c2_TTBR0 },
+       { CRm64( 2), Op1( 1), is64, NULL, reset_unknown64, c2_TTBR1 },
 
        /* TTBCR: swapped by interrupt.S. */
        { CRn( 2), CRm( 0), Op1( 0), Op2( 2), is32,
@@ -182,7 +186,7 @@ static const struct coproc_reg cp15_regs[] = {
                        NULL, reset_unknown, c6_IFAR },
 
        /* PAR swapped by interrupt.S */
-       { CRn( 7), Op1( 0), is64, NULL, reset_unknown64, c7_PAR },
+       { CRm64( 7), Op1( 0), is64, NULL, reset_unknown64, c7_PAR },
 
        /*
         * DC{C,I,CI}SW operations:
@@ -399,12 +403,13 @@ static bool index_to_params(u64 id, struct coproc_params *params)
                              | KVM_REG_ARM_OPC1_MASK))
                        return false;
                params->is_64bit = true;
-               params->CRm = ((id & KVM_REG_ARM_CRM_MASK)
+               /* CRm to CRn: see cp15_to_index for details */
+               params->CRn = ((id & KVM_REG_ARM_CRM_MASK)
                               >> KVM_REG_ARM_CRM_SHIFT);
                params->Op1 = ((id & KVM_REG_ARM_OPC1_MASK)
                               >> KVM_REG_ARM_OPC1_SHIFT);
                params->Op2 = 0;
-               params->CRn = 0;
+               params->CRm = 0;
                return true;
        default:
                return false;
@@ -898,7 +903,14 @@ static u64 cp15_to_index(const struct coproc_reg *reg)
        if (reg->is_64) {
                val |= KVM_REG_SIZE_U64;
                val |= (reg->Op1 << KVM_REG_ARM_OPC1_SHIFT);
-               val |= (reg->CRm << KVM_REG_ARM_CRM_SHIFT);
+               /*
+                * CRn always denotes the primary coproc. reg. nr. for the
+                * in-kernel representation, but the user space API uses the
+                * CRm for the encoding, because it is modelled after the
+                * MRRC/MCRR instructions: see the ARM ARM rev. c page
+                * B3-1445
+                */
+               val |= (reg->CRn << KVM_REG_ARM_CRM_SHIFT);
        } else {
                val |= KVM_REG_SIZE_U32;
                val |= (reg->Op1 << KVM_REG_ARM_OPC1_SHIFT);
index b7301d3e479921f4d8983a172c88ec6edababd81..0461d5c8d3de4f99c3ecfef669340ec6fa8e0411 100644 (file)
@@ -135,6 +135,8 @@ static inline int cmp_reg(const struct coproc_reg *i1,
                return -1;
        if (i1->CRn != i2->CRn)
                return i1->CRn - i2->CRn;
+       if (i1->is_64 != i2->is_64)
+               return i2->is_64 - i1->is_64;
        if (i1->CRm != i2->CRm)
                return i1->CRm - i2->CRm;
        if (i1->Op1 != i2->Op1)
@@ -145,6 +147,7 @@ static inline int cmp_reg(const struct coproc_reg *i1,
 
 #define CRn(_x)                .CRn = _x
 #define CRm(_x)        .CRm = _x
+#define CRm64(_x)       .CRn = _x, .CRm = 0
 #define Op1(_x)        .Op1 = _x
 #define Op2(_x)        .Op2 = _x
 #define is64           .is_64 = true
index 685063a6d0cf655296aaec9713d08f19b53fa260..cf93472b9dd60daf3da620cf3a44a9ff65a6eac6 100644 (file)
@@ -114,7 +114,11 @@ static bool access_l2ectlr(struct kvm_vcpu *vcpu,
 
 /*
  * A15-specific CP15 registers.
- * Important: Must be sorted ascending by CRn, CRM, Op1, Op2
+ * CRn denotes the primary register number, but is copied to the CRm in the
+ * user space API for 64-bit register access in line with the terminology used
+ * in the ARM ARM.
+ * Important: Must be sorted ascending by CRn, CRM, Op1, Op2 and with 64-bit
+ *            registers preceding 32-bit ones.
  */
 static const struct coproc_reg a15_regs[] = {
        /* MPIDR: we use VMPIDR for guest access. */
index b8e06b7a28331ede0a01ce6aefcb60d7be343f6e..0c25d9487d5382d2a19a1b3399398244f3718866 100644 (file)
@@ -63,7 +63,8 @@ int kvm_handle_mmio_return(struct kvm_vcpu *vcpu, struct kvm_run *run)
 static int decode_hsr(struct kvm_vcpu *vcpu, phys_addr_t fault_ipa,
                      struct kvm_exit_mmio *mmio)
 {
-       unsigned long rt, len;
+       unsigned long rt;
+       int len;
        bool is_write, sign_extend;
 
        if (kvm_vcpu_dabt_isextabt(vcpu)) {
index ca6bea4859b48c35e9c34d970fb02580840e23b4..0988d9e04dd4c21dab8eae53205e92fafe809dfd 100644 (file)
@@ -85,6 +85,12 @@ static void *mmu_memory_cache_alloc(struct kvm_mmu_memory_cache *mc)
        return p;
 }
 
+static bool page_empty(void *ptr)
+{
+       struct page *ptr_page = virt_to_page(ptr);
+       return page_count(ptr_page) == 1;
+}
+
 static void clear_pud_entry(struct kvm *kvm, pud_t *pud, phys_addr_t addr)
 {
        pmd_t *pmd_table = pmd_offset(pud, 0);
@@ -103,12 +109,6 @@ static void clear_pmd_entry(struct kvm *kvm, pmd_t *pmd, phys_addr_t addr)
        put_page(virt_to_page(pmd));
 }
 
-static bool pmd_empty(pmd_t *pmd)
-{
-       struct page *pmd_page = virt_to_page(pmd);
-       return page_count(pmd_page) == 1;
-}
-
 static void clear_pte_entry(struct kvm *kvm, pte_t *pte, phys_addr_t addr)
 {
        if (pte_present(*pte)) {
@@ -118,12 +118,6 @@ static void clear_pte_entry(struct kvm *kvm, pte_t *pte, phys_addr_t addr)
        }
 }
 
-static bool pte_empty(pte_t *pte)
-{
-       struct page *pte_page = virt_to_page(pte);
-       return page_count(pte_page) == 1;
-}
-
 static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
                        unsigned long long start, u64 size)
 {
@@ -132,37 +126,37 @@ static void unmap_range(struct kvm *kvm, pgd_t *pgdp,
        pmd_t *pmd;
        pte_t *pte;
        unsigned long long addr = start, end = start + size;
-       u64 range;
+       u64 next;
 
        while (addr < end) {
                pgd = pgdp + pgd_index(addr);
                pud = pud_offset(pgd, addr);
                if (pud_none(*pud)) {
-                       addr += PUD_SIZE;
+                       addr = pud_addr_end(addr, end);
                        continue;
                }
 
                pmd = pmd_offset(pud, addr);
                if (pmd_none(*pmd)) {
-                       addr += PMD_SIZE;
+                       addr = pmd_addr_end(addr, end);
                        continue;
                }
 
                pte = pte_offset_kernel(pmd, addr);
                clear_pte_entry(kvm, pte, addr);
-               range = PAGE_SIZE;
+               next = addr + PAGE_SIZE;
 
                /* If we emptied the pte, walk back up the ladder */
-               if (pte_empty(pte)) {
+               if (page_empty(pte)) {
                        clear_pmd_entry(kvm, pmd, addr);
-                       range = PMD_SIZE;
-                       if (pmd_empty(pmd)) {
+                       next = pmd_addr_end(addr, end);
+                       if (page_empty(pmd) && !page_empty(pud)) {
                                clear_pud_entry(kvm, pud, addr);
-                               range = PUD_SIZE;
+                               next = pud_addr_end(addr, end);
                        }
                }
 
-               addr += range;
+               addr = next;
        }
 }
 
index 2abee6626aace2cff322f2c22a3bab79786d5473..916e5a1429171bd39835da54b02fa444b1941905 100644 (file)
@@ -227,6 +227,8 @@ static struct clk_lookup periph_clocks_lookups[] = {
        CLKDEV_CON_DEV_ID("usart", "f8020000.serial", &usart1_clk),
        CLKDEV_CON_DEV_ID("usart", "f8024000.serial", &usart2_clk),
        CLKDEV_CON_DEV_ID("usart", "f8028000.serial", &usart3_clk),
+       CLKDEV_CON_DEV_ID("usart", "f8040000.serial", &uart0_clk),
+       CLKDEV_CON_DEV_ID("usart", "f8044000.serial", &uart1_clk),
        CLKDEV_CON_DEV_ID("t0_clk", "f8008000.timer", &tcb0_clk),
        CLKDEV_CON_DEV_ID("t0_clk", "f800c000.timer", &tcb0_clk),
        CLKDEV_CON_DEV_ID("mci_clk", "f0008000.mmc", &mmc0_clk),
index dff4ddc5ef81312590cd3a2cdb1ad4b40e3741ab..139e42da25f061baa0128c7615723da54e068592 100644 (file)
@@ -75,6 +75,7 @@ static struct davinci_nand_pdata davinci_nand_data = {
        .parts                  = davinci_nand_partitions,
        .nr_parts               = ARRAY_SIZE(davinci_nand_partitions),
        .ecc_mode               = NAND_ECC_HW_SYNDROME,
+       .ecc_bits               = 4,
        .bbt_options            = NAND_BBT_USE_FLASH,
 };
 
index a33686a6fbb226f9b880c2268a87beeb6b6f98e9..fa4bfaf952d886abcc94fd20bbb46285bd4cada6 100644 (file)
@@ -153,6 +153,7 @@ static struct davinci_nand_pdata davinci_evm_nandflash_data = {
        .parts          = davinci_evm_nandflash_partition,
        .nr_parts       = ARRAY_SIZE(davinci_evm_nandflash_partition),
        .ecc_mode       = NAND_ECC_HW,
+       .ecc_bits       = 1,
        .bbt_options    = NAND_BBT_USE_FLASH,
        .timing         = &davinci_evm_nandflash_timing,
 };
index fbb8e5ab1dc19bbd56e3508a5505929bb6c71406..0c005e876cac6fbd226c1700cfe47818b80dc6ee 100644 (file)
@@ -90,6 +90,7 @@ static struct davinci_nand_pdata davinci_nand_data = {
        .parts                  = davinci_nand_partitions,
        .nr_parts               = ARRAY_SIZE(davinci_nand_partitions),
        .ecc_mode               = NAND_ECC_HW,
+       .ecc_bits               = 1,
        .options                = 0,
 };
 
index 2bc112adf565495aed9505bfc23a401e53341d52..808233b60e3d0047e257227d50d49955c816228d 100644 (file)
@@ -88,6 +88,7 @@ static struct davinci_nand_pdata davinci_ntosd2_nandflash_data = {
        .parts          = davinci_ntosd2_nandflash_partition,
        .nr_parts       = ARRAY_SIZE(davinci_ntosd2_nandflash_partition),
        .ecc_mode       = NAND_ECC_HW,
+       .ecc_bits       = 1,
        .bbt_options    = NAND_BBT_USE_FLASH,
 };
 
index 00247c7713135a1f403eeaa9a0ea196895efaea3..304f069ebf5001dd31a791a6a7377fb437c97cd7 100644 (file)
@@ -108,8 +108,8 @@ static void __init dove_clk_init(void)
        orion_clkdev_add(NULL, "sdhci-dove.1", sdio1);
        orion_clkdev_add(NULL, "orion_nand", nand);
        orion_clkdev_add(NULL, "cafe1000-ccic.0", camera);
-       orion_clkdev_add(NULL, "kirkwood-i2s.0", i2s0);
-       orion_clkdev_add(NULL, "kirkwood-i2s.1", i2s1);
+       orion_clkdev_add(NULL, "mvebu-audio.0", i2s0);
+       orion_clkdev_add(NULL, "mvebu-audio.1", i2s1);
        orion_clkdev_add(NULL, "mv_crypto", crypto);
        orion_clkdev_add(NULL, "dove-ac97", ac97);
        orion_clkdev_add(NULL, "dove-pdma", pdma);
index e9238b5567eeba8e1ac2df9402d53859b27b7879..1663de090984535dfac2f6dcae4647644b563704 100644 (file)
@@ -264,7 +264,7 @@ void __init kirkwood_clk_init(void)
        orion_clkdev_add(NULL, MV_XOR_NAME ".1", xor1);
        orion_clkdev_add("0", "pcie", pex0);
        orion_clkdev_add("1", "pcie", pex1);
-       orion_clkdev_add(NULL, "kirkwood-i2s", audio);
+       orion_clkdev_add(NULL, "mvebu-audio", audio);
        orion_clkdev_add(NULL, MV64XXX_I2C_CTLR_NAME ".0", runit);
        orion_clkdev_add(NULL, MV64XXX_I2C_CTLR_NAME ".1", runit);
 
@@ -560,7 +560,7 @@ void __init kirkwood_timer_init(void)
 /*****************************************************************************
  * Audio
  ****************************************************************************/
-static struct resource kirkwood_i2s_resources[] = {
+static struct resource kirkwood_audio_resources[] = {
        [0] = {
                .start  = AUDIO_PHYS_BASE,
                .end    = AUDIO_PHYS_BASE + SZ_16K - 1,
@@ -573,29 +573,23 @@ static struct resource kirkwood_i2s_resources[] = {
        },
 };
 
-static struct kirkwood_asoc_platform_data kirkwood_i2s_data = {
+static struct kirkwood_asoc_platform_data kirkwood_audio_data = {
        .burst       = 128,
 };
 
-static struct platform_device kirkwood_i2s_device = {
-       .name           = "kirkwood-i2s",
+static struct platform_device kirkwood_audio_device = {
+       .name           = "mvebu-audio",
        .id             = -1,
-       .num_resources  = ARRAY_SIZE(kirkwood_i2s_resources),
-       .resource       = kirkwood_i2s_resources,
+       .num_resources  = ARRAY_SIZE(kirkwood_audio_resources),
+       .resource       = kirkwood_audio_resources,
        .dev            = {
-               .platform_data  = &kirkwood_i2s_data,
+               .platform_data  = &kirkwood_audio_data,
        },
 };
 
-static struct platform_device kirkwood_pcm_device = {
-       .name           = "kirkwood-pcm-audio",
-       .id             = -1,
-};
-
 void __init kirkwood_audio_init(void)
 {
-       platform_device_register(&kirkwood_i2s_device);
-       platform_device_register(&kirkwood_pcm_device);
+       platform_device_register(&kirkwood_audio_device);
 }
 
 /*****************************************************************************
index f6eeb87e4e955e425903475b733328656ffcaeda..827d15009a86c980a9577ad8951c2a94e0cbd17d 100644 (file)
@@ -122,11 +122,7 @@ static struct musb_hdrc_config musb_config = {
 };
 
 static struct musb_hdrc_platform_data tusb_data = {
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
        .mode           = MUSB_OTG,
-#else
-       .mode           = MUSB_HOST,
-#endif
        .set_power      = tusb_set_power,
        .min_power      = 25,   /* x2 = 50 mA drawn from VBUS as peripheral */
        .power          = 100,  /* Max 100 mA VBUS for host mode */
index d2ea68ea678af901715aa609b4c5f41175641ddf..7735105561d87dd218c436b357ade5211e5a6d2e 100644 (file)
@@ -85,7 +85,7 @@ static struct omap_board_mux board_mux[] __initdata = {
 
 static struct omap_musb_board_data musb_board_data = {
        .interface_type         = MUSB_INTERFACE_ULPI,
-       .mode                   = MUSB_PERIPHERAL,
+       .mode                   = MUSB_OTG,
        .power                  = 0,
 };
 
index 8c4de2708cf28e6bf5f0011392c5fb15bbcb60fe..bc897231bd1098714ca602e7763442671e9f2fe5 100644 (file)
@@ -38,11 +38,8 @@ static struct musb_hdrc_config musb_config = {
 };
 
 static struct musb_hdrc_platform_data musb_plat = {
-#ifdef CONFIG_USB_GADGET_MUSB_HDRC
        .mode           = MUSB_OTG,
-#else
-       .mode           = MUSB_HOST,
-#endif
+
        /* .clock is set dynamically */
        .config         = &musb_config,
 
index db5c2cab8fda4251bb636aa5567afafe5cfdb406..cd2c88e7a8f7557bfe299a7a6b364c395a969428 100644 (file)
@@ -809,15 +809,18 @@ config KUSER_HELPERS
          the CPU type fitted to the system.  This permits binaries to be
          run on ARMv4 through to ARMv7 without modification.
 
+         See Documentation/arm/kernel_user_helpers.txt for details.
+
          However, the fixed address nature of these helpers can be used
          by ROP (return orientated programming) authors when creating
          exploits.
 
          If all of the binaries and libraries which run on your platform
          are built specifically for your platform, and make no use of
-         these helpers, then you can turn this option off.  However,
-         when such an binary or library is run, it will receive a SIGILL
-         signal, which will terminate the program.
+         these helpers, then you can turn this option off to hinder
+         such exploits. However, in that case, if a binary or library
+         relying on those helpers is run, it will receive a SIGILL signal,
+         which will terminate the program.
 
          Say N here only if you are absolutely certain that you do not
          need these helpers; otherwise, the safe option is to say Y.
index 8e11e96eab5ef6dbb07f79c3dd747252401d5a52..c83f27b6bdda0eac2c566d40b4d99e5428be650b 100644 (file)
@@ -30,6 +30,8 @@
 #include <linux/platform_device.h>
 #include <linux/spi/pxa2xx_spi.h>
 #include <linux/io.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
 
 #include <asm/irq.h>
 #include <mach/hardware.h>
@@ -60,6 +62,30 @@ struct ssp_device *pxa_ssp_request(int port, const char *label)
 }
 EXPORT_SYMBOL(pxa_ssp_request);
 
+struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node,
+                                     const char *label)
+{
+       struct ssp_device *ssp = NULL;
+
+       mutex_lock(&ssp_lock);
+
+       list_for_each_entry(ssp, &ssp_list, node) {
+               if (ssp->of_node == of_node && ssp->use_count == 0) {
+                       ssp->use_count++;
+                       ssp->label = label;
+                       break;
+               }
+       }
+
+       mutex_unlock(&ssp_lock);
+
+       if (&ssp->node == &ssp_list)
+               return NULL;
+
+       return ssp;
+}
+EXPORT_SYMBOL(pxa_ssp_request_of);
+
 void pxa_ssp_free(struct ssp_device *ssp)
 {
        mutex_lock(&ssp_lock);
@@ -72,96 +98,126 @@ void pxa_ssp_free(struct ssp_device *ssp)
 }
 EXPORT_SYMBOL(pxa_ssp_free);
 
+#ifdef CONFIG_OF
+static const struct of_device_id pxa_ssp_of_ids[] = {
+       { .compatible = "mrvl,pxa25x-ssp",      .data = (void *) PXA25x_SSP },
+       { .compatible = "mvrl,pxa25x-nssp",     .data = (void *) PXA25x_NSSP },
+       { .compatible = "mrvl,pxa27x-ssp",      .data = (void *) PXA27x_SSP },
+       { .compatible = "mrvl,pxa3xx-ssp",      .data = (void *) PXA3xx_SSP },
+       { .compatible = "mvrl,pxa168-ssp",      .data = (void *) PXA168_SSP },
+       { .compatible = "mrvl,pxa910-ssp",      .data = (void *) PXA910_SSP },
+       { .compatible = "mrvl,ce4100-ssp",      .data = (void *) CE4100_SSP },
+       { .compatible = "mrvl,lpss-ssp",        .data = (void *) LPSS_SSP },
+       { },
+};
+MODULE_DEVICE_TABLE(of, pxa_ssp_of_ids);
+#endif
+
 static int pxa_ssp_probe(struct platform_device *pdev)
 {
-       const struct platform_device_id *id = platform_get_device_id(pdev);
        struct resource *res;
        struct ssp_device *ssp;
-       int ret = 0;
+       struct device *dev = &pdev->dev;
 
-       ssp = kzalloc(sizeof(struct ssp_device), GFP_KERNEL);
-       if (ssp == NULL) {
-               dev_err(&pdev->dev, "failed to allocate memory");
+       ssp = devm_kzalloc(dev, sizeof(struct ssp_device), GFP_KERNEL);
+       if (ssp == NULL)
                return -ENOMEM;
-       }
-       ssp->pdev = pdev;
 
-       ssp->clk = clk_get(&pdev->dev, NULL);
-       if (IS_ERR(ssp->clk)) {
-               ret = PTR_ERR(ssp->clk);
-               goto err_free;
-       }
+       ssp->pdev = pdev;
 
-       res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
-       if (res == NULL) {
-               dev_err(&pdev->dev, "no SSP RX DRCMR defined\n");
-               ret = -ENODEV;
-               goto err_free_clk;
-       }
-       ssp->drcmr_rx = res->start;
+       ssp->clk = devm_clk_get(dev, NULL);
+       if (IS_ERR(ssp->clk))
+               return PTR_ERR(ssp->clk);
+
+       if (dev->of_node) {
+               struct of_phandle_args dma_spec;
+               struct device_node *np = dev->of_node;
+
+               /*
+                * FIXME: we should allocate the DMA channel from this
+                * context and pass the channel down to the ssp users.
+                * For now, we lookup the rx and tx indices manually
+                */
+
+               /* rx */
+               of_parse_phandle_with_args(np, "dmas", "#dma-cells",
+                                          0, &dma_spec);
+               ssp->drcmr_rx = dma_spec.args[0];
+               of_node_put(dma_spec.np);
+
+               /* tx */
+               of_parse_phandle_with_args(np, "dmas", "#dma-cells",
+                                          1, &dma_spec);
+               ssp->drcmr_tx = dma_spec.args[0];
+               of_node_put(dma_spec.np);
+       } else {
+               res = platform_get_resource(pdev, IORESOURCE_DMA, 0);
+               if (res == NULL) {
+                       dev_err(dev, "no SSP RX DRCMR defined\n");
+                       return -ENODEV;
+               }
+               ssp->drcmr_rx = res->start;
 
-       res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
-       if (res == NULL) {
-               dev_err(&pdev->dev, "no SSP TX DRCMR defined\n");
-               ret = -ENODEV;
-               goto err_free_clk;
+               res = platform_get_resource(pdev, IORESOURCE_DMA, 1);
+               if (res == NULL) {
+                       dev_err(dev, "no SSP TX DRCMR defined\n");
+                       return -ENODEV;
+               }
+               ssp->drcmr_tx = res->start;
        }
-       ssp->drcmr_tx = res->start;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        if (res == NULL) {
-               dev_err(&pdev->dev, "no memory resource defined\n");
-               ret = -ENODEV;
-               goto err_free_clk;
+               dev_err(dev, "no memory resource defined\n");
+               return -ENODEV;
        }
 
-       res = request_mem_region(res->start, resource_size(res),
-                       pdev->name);
+       res = devm_request_mem_region(dev, res->start, resource_size(res),
+                                     pdev->name);
        if (res == NULL) {
-               dev_err(&pdev->dev, "failed to request memory resource\n");
-               ret = -EBUSY;
-               goto err_free_clk;
+               dev_err(dev, "failed to request memory resource\n");
+               return -EBUSY;
        }
 
        ssp->phys_base = res->start;
 
-       ssp->mmio_base = ioremap(res->start, resource_size(res));
+       ssp->mmio_base = devm_ioremap(dev, res->start, resource_size(res));
        if (ssp->mmio_base == NULL) {
-               dev_err(&pdev->dev, "failed to ioremap() registers\n");
-               ret = -ENODEV;
-               goto err_free_mem;
+               dev_err(dev, "failed to ioremap() registers\n");
+               return -ENODEV;
        }
 
        ssp->irq = platform_get_irq(pdev, 0);
        if (ssp->irq < 0) {
-               dev_err(&pdev->dev, "no IRQ resource defined\n");
-               ret = -ENODEV;
-               goto err_free_io;
+               dev_err(dev, "no IRQ resource defined\n");
+               return -ENODEV;
+       }
+
+       if (dev->of_node) {
+               const struct of_device_id *id =
+                       of_match_device(of_match_ptr(pxa_ssp_of_ids), dev);
+               ssp->type = (int) id->data;
+       } else {
+               const struct platform_device_id *id =
+                       platform_get_device_id(pdev);
+               ssp->type = (int) id->driver_data;
+
+               /* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id
+                * starts from 0, do a translation here
+                */
+               ssp->port_id = pdev->id + 1;
        }
 
-       /* PXA2xx/3xx SSP ports starts from 1 and the internal pdev->id
-        * starts from 0, do a translation here
-        */
-       ssp->port_id = pdev->id + 1;
        ssp->use_count = 0;
-       ssp->type = (int)id->driver_data;
+       ssp->of_node = dev->of_node;
 
        mutex_lock(&ssp_lock);
        list_add(&ssp->node, &ssp_list);
        mutex_unlock(&ssp_lock);
 
        platform_set_drvdata(pdev, ssp);
-       return 0;
 
-err_free_io:
-       iounmap(ssp->mmio_base);
-err_free_mem:
-       release_mem_region(res->start, resource_size(res));
-err_free_clk:
-       clk_put(ssp->clk);
-err_free:
-       kfree(ssp);
-       return ret;
+       return 0;
 }
 
 static int pxa_ssp_remove(struct platform_device *pdev)
@@ -201,8 +257,9 @@ static struct platform_driver pxa_ssp_driver = {
        .probe          = pxa_ssp_probe,
        .remove         = pxa_ssp_remove,
        .driver         = {
-               .owner  = THIS_MODULE,
-               .name   = "pxa2xx-ssp",
+               .owner          = THIS_MODULE,
+               .name           = "pxa2xx-ssp",
+               .of_match_table = of_match_ptr(pxa_ssp_of_ids),
        },
        .id_table       = ssp_id_table,
 };
index 3e5c4619caa5ef26cc9fdea0940856c09f639390..50a3ea0037db10d2032e2ce020688b6fa74614b0 100644 (file)
@@ -55,12 +55,13 @@ void __init s3c_init_cpu(unsigned long idcode,
 
        printk("CPU %s (id 0x%08lx)\n", cpu->name, idcode);
 
-       if (cpu->map_io == NULL || cpu->init == NULL) {
+       if (cpu->init == NULL) {
                printk(KERN_ERR "CPU %s support not enabled\n", cpu->name);
                panic("Unsupported Samsung CPU");
        }
 
-       cpu->map_io();
+       if (cpu->map_io)
+               cpu->map_io();
 }
 
 /* s3c24xx_init_clocks
index c9770ba5c7df5c3b68c909c32db7fa2fb7be39f1..8a6295c86209cd982076a8f79662bd20c2c0f02b 100644 (file)
@@ -170,6 +170,7 @@ static void __init xen_percpu_init(void *unused)
        per_cpu(xen_vcpu, cpu) = vcpup;
 
        enable_percpu_irq(xen_events_irq, 0);
+       put_cpu();
 }
 
 static void xen_restart(enum reboot_mode reboot_mode, const char *cmd)
index c92de4163eba519802dfaa4b63450259bb6d0395..b25763bc0ec4977a4eca6139ef7d84b3a15eeea0 100644 (file)
 #define        TPIDR_EL1       18      /* Thread ID, Privileged */
 #define        AMAIR_EL1       19      /* Aux Memory Attribute Indirection Register */
 #define        CNTKCTL_EL1     20      /* Timer Control Register (EL1) */
+#define        PAR_EL1         21      /* Physical Address Register */
 /* 32bit specific registers. Keep them at the end of the range */
-#define        DACR32_EL2      21      /* Domain Access Control Register */
-#define        IFSR32_EL2      22      /* Instruction Fault Status Register */
-#define        FPEXC32_EL2     23      /* Floating-Point Exception Control Register */
-#define        DBGVCR32_EL2    24      /* Debug Vector Catch Register */
-#define        TEECR32_EL1     25      /* ThumbEE Configuration Register */
-#define        TEEHBR32_EL1    26      /* ThumbEE Handler Base Register */
-#define        NR_SYS_REGS     27
+#define        DACR32_EL2      22      /* Domain Access Control Register */
+#define        IFSR32_EL2      23      /* Instruction Fault Status Register */
+#define        FPEXC32_EL2     24      /* Floating-Point Exception Control Register */
+#define        DBGVCR32_EL2    25      /* Debug Vector Catch Register */
+#define        TEECR32_EL1     26      /* ThumbEE Configuration Register */
+#define        TEEHBR32_EL1    27      /* ThumbEE Handler Base Register */
+#define        NR_SYS_REGS     28
 
 /* 32bit mapping */
 #define c0_MPIDR       (MPIDR_EL1 * 2) /* MultiProcessor ID Register */
@@ -69,6 +70,8 @@
 #define c5_AIFSR       (AFSR1_EL1 * 2) /* Auxiliary Instr Fault Status R */
 #define c6_DFAR                (FAR_EL1 * 2)   /* Data Fault Address Register */
 #define c6_IFAR                (c6_DFAR + 1)   /* Instruction Fault Address Register */
+#define c7_PAR         (PAR_EL1 * 2)   /* Physical Address Register */
+#define c7_PAR_high    (c7_PAR + 1)    /* PAR top 32 bits */
 #define c10_PRRR       (MAIR_EL1 * 2)  /* Primary Region Remap Register */
 #define c10_NMRR       (c10_PRRR + 1)  /* Normal Memory Remap Register */
 #define c12_VBAR       (VBAR_EL1 * 2)  /* Vector Base Address Register */
index 644d7395686493e371d01266c98597a00a104da4..0859a4ddd1e7d0e8b1792416b19a8f9908457af7 100644 (file)
@@ -129,7 +129,7 @@ struct kvm_vcpu_arch {
        struct kvm_mmu_memory_cache mmu_page_cache;
 
        /* Target CPU and feature flags */
-       u32 target;
+       int target;
        DECLARE_BITMAP(features, KVM_VCPU_MAX_FEATURES);
 
        /* Detect first run of a vcpu */
index 9ba33c40cdf8f841e974f68e599f0f97e87138ff..12e6ccb88691c65e6a20d761275babb1af369182 100644 (file)
@@ -107,7 +107,12 @@ armpmu_map_cache_event(const unsigned (*cache_map)
 static int
 armpmu_map_event(const unsigned (*event_map)[PERF_COUNT_HW_MAX], u64 config)
 {
-       int mapping = (*event_map)[config];
+       int mapping;
+
+       if (config >= PERF_COUNT_HW_MAX)
+               return -EINVAL;
+
+       mapping = (*event_map)[config];
        return mapping == HW_OP_UNSUPPORTED ? -ENOENT : mapping;
 }
 
@@ -317,6 +322,9 @@ validate_event(struct pmu_hw_events *hw_events,
        struct hw_perf_event fake_event = event->hw;
        struct pmu *leader_pmu = event->group_leader->pmu;
 
+       if (is_software_event(event))
+               return 1;
+
        if (event->pmu != leader_pmu || event->state <= PERF_EVENT_STATE_OFF)
                return 1;
 
index ff985e3d8b72db7861b1559cd42a59a59d957fad..1ac0bbbdddb27976ada4376fe4d28538fee7ccc4 100644 (file)
@@ -214,6 +214,7 @@ __kvm_hyp_code_start:
        mrs     x21,    tpidr_el1
        mrs     x22,    amair_el1
        mrs     x23,    cntkctl_el1
+       mrs     x24,    par_el1
 
        stp     x4, x5, [x3]
        stp     x6, x7, [x3, #16]
@@ -225,6 +226,7 @@ __kvm_hyp_code_start:
        stp     x18, x19, [x3, #112]
        stp     x20, x21, [x3, #128]
        stp     x22, x23, [x3, #144]
+       str     x24, [x3, #160]
 .endm
 
 .macro restore_sysregs
@@ -243,6 +245,7 @@ __kvm_hyp_code_start:
        ldp     x18, x19, [x3, #112]
        ldp     x20, x21, [x3, #128]
        ldp     x22, x23, [x3, #144]
+       ldr     x24, [x3, #160]
 
        msr     vmpidr_el2,     x4
        msr     csselr_el1,     x5
@@ -264,6 +267,7 @@ __kvm_hyp_code_start:
        msr     tpidr_el1,      x21
        msr     amair_el1,      x22
        msr     cntkctl_el1,    x23
+       msr     par_el1,        x24
 .endm
 
 .macro skip_32bit_state tmp, target
@@ -600,6 +604,8 @@ END(__kvm_vcpu_run)
 
 // void __kvm_tlb_flush_vmid_ipa(struct kvm *kvm, phys_addr_t ipa);
 ENTRY(__kvm_tlb_flush_vmid_ipa)
+       dsb     ishst
+
        kern_hyp_va     x0
        ldr     x2, [x0, #KVM_VTTBR]
        msr     vttbr_el2, x2
@@ -621,6 +627,7 @@ ENTRY(__kvm_tlb_flush_vmid_ipa)
 ENDPROC(__kvm_tlb_flush_vmid_ipa)
 
 ENTRY(__kvm_flush_vm_context)
+       dsb     ishst
        tlbi    alle1is
        ic      ialluis
        dsb     sy
@@ -753,6 +760,10 @@ el1_trap:
         */
        tbnz    x1, #7, 1f      // S1PTW is set
 
+       /* Preserve PAR_EL1 */
+       mrs     x3, par_el1
+       push    x3, xzr
+
        /*
         * Permission fault, HPFAR_EL2 is invalid.
         * Resolve the IPA the hard way using the guest VA.
@@ -766,6 +777,8 @@ el1_trap:
 
        /* Read result */
        mrs     x3, par_el1
+       pop     x0, xzr                 // Restore PAR_EL1 from the stack
+       msr     par_el1, x0
        tbnz    x3, #0, 3f              // Bail out if we failed the translation
        ubfx    x3, x3, #12, #36        // Extract IPA
        lsl     x3, x3, #4              // and present it like HPFAR
index 94923609753b2ae91715080fff2cd544281c8621..02e9d09e1d804b4e9344427037dd5a2b88d378ba 100644 (file)
@@ -211,6 +211,9 @@ static const struct sys_reg_desc sys_reg_descs[] = {
        /* FAR_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b0110), CRm(0b0000), Op2(0b000),
          NULL, reset_unknown, FAR_EL1 },
+       /* PAR_EL1 */
+       { Op0(0b11), Op1(0b000), CRn(0b0111), CRm(0b0100), Op2(0b000),
+         NULL, reset_unknown, PAR_EL1 },
 
        /* PMINTENSET_EL1 */
        { Op0(0b11), Op1(0b000), CRn(0b1001), CRm(0b1110), Op2(0b001),
index e773659ccf9f8f607db709109e39b0cacb6f7989..46048d24328c759b0bf4189c612929015f139f69 100644 (file)
@@ -803,6 +803,32 @@ static int isBranchInstr(struct pt_regs *regs, struct mm_decoded_insn dec_insn,
                                dec_insn.next_pc_inc;
                return 1;
                break;
+#ifdef CONFIG_CPU_CAVIUM_OCTEON
+       case lwc2_op: /* This is bbit0 on Octeon */
+               if ((regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt)) == 0)
+                       *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2);
+               else
+                       *contpc = regs->cp0_epc + 8;
+               return 1;
+       case ldc2_op: /* This is bbit032 on Octeon */
+               if ((regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32))) == 0)
+                       *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2);
+               else
+                       *contpc = regs->cp0_epc + 8;
+               return 1;
+       case swc2_op: /* This is bbit1 on Octeon */
+               if (regs->regs[insn.i_format.rs] & (1ull<<insn.i_format.rt))
+                       *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2);
+               else
+                       *contpc = regs->cp0_epc + 8;
+               return 1;
+       case sdc2_op: /* This is bbit132 on Octeon */
+               if (regs->regs[insn.i_format.rs] & (1ull<<(insn.i_format.rt + 32)))
+                       *contpc = regs->cp0_epc + 4 + (insn.i_format.simmediate << 2);
+               else
+                       *contpc = regs->cp0_epc + 8;
+               return 1;
+#endif
        case cop0_op:
        case cop1_op:
        case cop2_op:
index 653668d140f994e543ad52e46d0c8402d5fe9259..4a8cb8d7cbd5d2b0febd4333931b459e75f1ea1d 100644 (file)
@@ -35,9 +35,9 @@ static void sanitize_boot_params(struct boot_params *boot_params)
         */
        if (boot_params->sentinel) {
                /* fields in boot_params are left uninitialized, clear them */
-               memset(&boot_params->olpc_ofw_header, 0,
+               memset(&boot_params->ext_ramdisk_image, 0,
                       (char *)&boot_params->efi_info -
-                       (char *)&boot_params->olpc_ofw_header);
+                       (char *)&boot_params->ext_ramdisk_image);
                memset(&boot_params->kbd_status, 0,
                       (char *)&boot_params->hdr -
                       (char *)&boot_params->kbd_status);
index 50e5c58ced23b2ec8537569a71ae4ac41566281f..4c019179a57dd97d6b48ae064ef1faea0dc2e7f7 100644 (file)
@@ -59,7 +59,7 @@ static inline u16 find_equiv_id(struct equiv_cpu_entry *equiv_cpu_table,
 
 extern int __apply_microcode_amd(struct microcode_amd *mc_amd);
 extern int apply_microcode_amd(int cpu);
-extern enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size);
+extern enum ucode_state load_microcode_amd(u8 family, const u8 *data, size_t size);
 
 #ifdef CONFIG_MICROCODE_AMD_EARLY
 #ifdef CONFIG_X86_32
index f654ecefea5b6d5348df41195a529a4dce303261..08a089043ccfbb669c889ac034091a55aaa92b75 100644 (file)
@@ -512,7 +512,7 @@ static void early_init_amd(struct cpuinfo_x86 *c)
 
 static const int amd_erratum_383[];
 static const int amd_erratum_400[];
-static bool cpu_has_amd_erratum(const int *erratum);
+static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum);
 
 static void init_amd(struct cpuinfo_x86 *c)
 {
@@ -729,11 +729,11 @@ static void init_amd(struct cpuinfo_x86 *c)
                value &= ~(1ULL << 24);
                wrmsrl_safe(MSR_AMD64_BU_CFG2, value);
 
-               if (cpu_has_amd_erratum(amd_erratum_383))
+               if (cpu_has_amd_erratum(c, amd_erratum_383))
                        set_cpu_bug(c, X86_BUG_AMD_TLB_MMATCH);
        }
 
-       if (cpu_has_amd_erratum(amd_erratum_400))
+       if (cpu_has_amd_erratum(c, amd_erratum_400))
                set_cpu_bug(c, X86_BUG_AMD_APIC_C1E);
 
        rdmsr_safe(MSR_AMD64_PATCH_LEVEL, &c->microcode, &dummy);
@@ -878,23 +878,13 @@ static const int amd_erratum_400[] =
 static const int amd_erratum_383[] =
        AMD_OSVW_ERRATUM(3, AMD_MODEL_RANGE(0x10, 0, 0, 0xff, 0xf));
 
-static bool cpu_has_amd_erratum(const int *erratum)
+
+static bool cpu_has_amd_erratum(struct cpuinfo_x86 *cpu, const int *erratum)
 {
-       struct cpuinfo_x86 *cpu = __this_cpu_ptr(&cpu_info);
        int osvw_id = *erratum++;
        u32 range;
        u32 ms;
 
-       /*
-        * If called early enough that current_cpu_data hasn't been initialized
-        * yet, fall back to boot_cpu_data.
-        */
-       if (cpu->x86 == 0)
-               cpu = &boot_cpu_data;
-
-       if (cpu->x86_vendor != X86_VENDOR_AMD)
-               return false;
-
        if (osvw_id >= 0 && osvw_id < 65536 &&
            cpu_has(cpu, X86_FEATURE_OSVW)) {
                u64 osvw_len;
index 7a0adb7ee43397aa9a9fcbf733c3b1c9308f2b71..7123b5df479d872def8ff437fcd407c5c4d5ca50 100644 (file)
@@ -145,10 +145,9 @@ static int collect_cpu_info_amd(int cpu, struct cpu_signature *csig)
        return 0;
 }
 
-static unsigned int verify_patch_size(int cpu, u32 patch_size,
+static unsigned int verify_patch_size(u8 family, u32 patch_size,
                                      unsigned int size)
 {
-       struct cpuinfo_x86 *c = &cpu_data(cpu);
        u32 max_size;
 
 #define F1XH_MPB_MAX_SIZE 2048
@@ -156,7 +155,7 @@ static unsigned int verify_patch_size(int cpu, u32 patch_size,
 #define F15H_MPB_MAX_SIZE 4096
 #define F16H_MPB_MAX_SIZE 3458
 
-       switch (c->x86) {
+       switch (family) {
        case 0x14:
                max_size = F14H_MPB_MAX_SIZE;
                break;
@@ -277,9 +276,8 @@ static void cleanup(void)
  * driver cannot continue functioning normally. In such cases, we tear
  * down everything we've used up so far and exit.
  */
-static int verify_and_add_patch(unsigned int cpu, u8 *fw, unsigned int leftover)
+static int verify_and_add_patch(u8 family, u8 *fw, unsigned int leftover)
 {
-       struct cpuinfo_x86 *c = &cpu_data(cpu);
        struct microcode_header_amd *mc_hdr;
        struct ucode_patch *patch;
        unsigned int patch_size, crnt_size, ret;
@@ -299,7 +297,7 @@ static int verify_and_add_patch(unsigned int cpu, u8 *fw, unsigned int leftover)
 
        /* check if patch is for the current family */
        proc_fam = ((proc_fam >> 8) & 0xf) + ((proc_fam >> 20) & 0xff);
-       if (proc_fam != c->x86)
+       if (proc_fam != family)
                return crnt_size;
 
        if (mc_hdr->nb_dev_id || mc_hdr->sb_dev_id) {
@@ -308,7 +306,7 @@ static int verify_and_add_patch(unsigned int cpu, u8 *fw, unsigned int leftover)
                return crnt_size;
        }
 
-       ret = verify_patch_size(cpu, patch_size, leftover);
+       ret = verify_patch_size(family, patch_size, leftover);
        if (!ret) {
                pr_err("Patch-ID 0x%08x: size mismatch.\n", mc_hdr->patch_id);
                return crnt_size;
@@ -339,7 +337,8 @@ static int verify_and_add_patch(unsigned int cpu, u8 *fw, unsigned int leftover)
        return crnt_size;
 }
 
-static enum ucode_state __load_microcode_amd(int cpu, const u8 *data, size_t size)
+static enum ucode_state __load_microcode_amd(u8 family, const u8 *data,
+                                            size_t size)
 {
        enum ucode_state ret = UCODE_ERROR;
        unsigned int leftover;
@@ -362,7 +361,7 @@ static enum ucode_state __load_microcode_amd(int cpu, const u8 *data, size_t siz
        }
 
        while (leftover) {
-               crnt_size = verify_and_add_patch(cpu, fw, leftover);
+               crnt_size = verify_and_add_patch(family, fw, leftover);
                if (crnt_size < 0)
                        return ret;
 
@@ -373,22 +372,22 @@ static enum ucode_state __load_microcode_amd(int cpu, const u8 *data, size_t siz
        return UCODE_OK;
 }
 
-enum ucode_state load_microcode_amd(int cpu, const u8 *data, size_t size)
+enum ucode_state load_microcode_amd(u8 family, const u8 *data, size_t size)
 {
        enum ucode_state ret;
 
        /* free old equiv table */
        free_equiv_cpu_table();
 
-       ret = __load_microcode_amd(cpu, data, size);
+       ret = __load_microcode_amd(family, data, size);
 
        if (ret != UCODE_OK)
                cleanup();
 
 #if defined(CONFIG_MICROCODE_AMD_EARLY) && defined(CONFIG_X86_32)
        /* save BSP's matching patch for early load */
-       if (cpu_data(cpu).cpu_index == boot_cpu_data.cpu_index) {
-               struct ucode_patch *p = find_patch(cpu);
+       if (cpu_data(smp_processor_id()).cpu_index == boot_cpu_data.cpu_index) {
+               struct ucode_patch *p = find_patch(smp_processor_id());
                if (p) {
                        memset(amd_bsp_mpb, 0, MPB_MAX_SIZE);
                        memcpy(amd_bsp_mpb, p->data, min_t(u32, ksize(p->data),
@@ -441,7 +440,7 @@ static enum ucode_state request_microcode_amd(int cpu, struct device *device,
                goto fw_release;
        }
 
-       ret = load_microcode_amd(cpu, fw->data, fw->size);
+       ret = load_microcode_amd(c->x86, fw->data, fw->size);
 
  fw_release:
        release_firmware(fw);
index 1d14ffee57495a9793d8f9f5f01073958da6ee3e..6073104ccaa36bca776290155e42a30bdd444a8d 100644 (file)
@@ -238,25 +238,17 @@ static void __init collect_cpu_sig_on_bsp(void *arg)
        uci->cpu_sig.sig = cpuid_eax(0x00000001);
 }
 #else
-static void collect_cpu_info_amd_early(struct cpuinfo_x86 *c,
-                                                struct ucode_cpu_info *uci)
+void load_ucode_amd_ap(void)
 {
+       unsigned int cpu = smp_processor_id();
+       struct ucode_cpu_info *uci = ucode_cpu_info + cpu;
        u32 rev, eax;
 
        rdmsr(MSR_AMD64_PATCH_LEVEL, rev, eax);
        eax = cpuid_eax(0x00000001);
 
-       uci->cpu_sig.sig = eax;
        uci->cpu_sig.rev = rev;
-       c->microcode = rev;
-       c->x86 = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);
-}
-
-void load_ucode_amd_ap(void)
-{
-       unsigned int cpu = smp_processor_id();
-
-       collect_cpu_info_amd_early(&cpu_data(cpu), ucode_cpu_info + cpu);
+       uci->cpu_sig.sig = eax;
 
        if (cpu && !ucode_loaded) {
                void *ucode;
@@ -265,8 +257,10 @@ void load_ucode_amd_ap(void)
                        return;
 
                ucode = (void *)(initrd_start + ucode_offset);
-               if (load_microcode_amd(0, ucode, ucode_size) != UCODE_OK)
+               eax   = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);
+               if (load_microcode_amd(eax, ucode, ucode_size) != UCODE_OK)
                        return;
+
                ucode_loaded = true;
        }
 
@@ -278,6 +272,8 @@ int __init save_microcode_in_initrd_amd(void)
 {
        enum ucode_state ret;
        void *ucode;
+       u32 eax;
+
 #ifdef CONFIG_X86_32
        unsigned int bsp = boot_cpu_data.cpu_index;
        struct ucode_cpu_info *uci = ucode_cpu_info + bsp;
@@ -293,7 +289,10 @@ int __init save_microcode_in_initrd_amd(void)
                return 0;
 
        ucode = (void *)(initrd_start + ucode_offset);
-       ret = load_microcode_amd(0, ucode, ucode_size);
+       eax   = cpuid_eax(0x00000001);
+       eax   = ((eax >> 8) & 0xf) + ((eax >> 20) & 0xff);
+
+       ret = load_microcode_amd(eax, ucode, ucode_size);
        if (ret != UCODE_OK)
                return -EINVAL;
 
index 48f8375e4c6b07edfbcefd819a8210f6e0839dfe..30277e27431acde9a9320e0b1be4470bddb40e3a 100644 (file)
@@ -101,7 +101,7 @@ static void find_start_end(unsigned long flags, unsigned long *begin,
                                *begin = new_begin;
                }
        } else {
-               *begin = mmap_legacy_base();
+               *begin = current->mm->mmap_legacy_base;
                *end = TASK_SIZE;
        }
 }
index f63778cb2363981ad8f98d0e068e4d789c2136b0..25e7e1372bb26e961b580c753407edf28a320aa3 100644 (file)
@@ -98,7 +98,7 @@ static unsigned long mmap_base(void)
  * Bottom-up (legacy) layout on X86_32 did not support randomization, X86_64
  * does, but not when emulating X86_32
  */
-unsigned long mmap_legacy_base(void)
+static unsigned long mmap_legacy_base(void)
 {
        if (mmap_is_ia32())
                return TASK_UNMAPPED_BASE;
@@ -112,11 +112,13 @@ unsigned long mmap_legacy_base(void)
  */
 void arch_pick_mmap_layout(struct mm_struct *mm)
 {
+       mm->mmap_legacy_base = mmap_legacy_base();
+       mm->mmap_base = mmap_base();
+
        if (mmap_is_legacy()) {
-               mm->mmap_base = mmap_legacy_base();
+               mm->mmap_base = mm->mmap_legacy_base;
                mm->get_unmapped_area = arch_get_unmapped_area;
        } else {
-               mm->mmap_base = mmap_base();
                mm->get_unmapped_area = arch_get_unmapped_area_topdown;
        }
 }
index 056d11faef21e96e5adf56a455a2827d2f97fbf1..8f3eea6b80c527bd65fbbe80c6c8c8b7513805c6 100644 (file)
@@ -313,6 +313,17 @@ static void xen_align_and_add_e820_region(u64 start, u64 size, int type)
        e820_add_region(start, end - start, type);
 }
 
+void xen_ignore_unusable(struct e820entry *list, size_t map_size)
+{
+       struct e820entry *entry;
+       unsigned int i;
+
+       for (i = 0, entry = list; i < map_size; i++, entry++) {
+               if (entry->type == E820_UNUSABLE)
+                       entry->type = E820_RAM;
+       }
+}
+
 /**
  * machine_specific_memory_setup - Hook for machine specific memory setup.
  **/
@@ -353,6 +364,17 @@ char * __init xen_memory_setup(void)
        }
        BUG_ON(rc);
 
+       /*
+        * Xen won't allow a 1:1 mapping to be created to UNUSABLE
+        * regions, so if we're using the machine memory map leave the
+        * region as RAM as it is in the pseudo-physical map.
+        *
+        * UNUSABLE regions in domUs are not handled and will need
+        * a patch in the future.
+        */
+       if (xen_initial_domain())
+               xen_ignore_unusable(map, memmap.nr_entries);
+
        /* Make sure the Xen-supplied memory map is well-ordered. */
        sanitize_e820_map(map, memmap.nr_entries, &memmap.nr_entries);
 
index ca92754eb846b6d7f8293a4f6f75dedae7bf13a9..b81c88e51daa3d412a147f2088c1f51be1649d26 100644 (file)
@@ -694,8 +694,15 @@ static void __init xen_hvm_smp_prepare_cpus(unsigned int max_cpus)
 static int xen_hvm_cpu_up(unsigned int cpu, struct task_struct *tidle)
 {
        int rc;
-       rc = native_cpu_up(cpu, tidle);
-       WARN_ON (xen_smp_intr_init(cpu));
+       /*
+        * xen_smp_intr_init() needs to run before native_cpu_up()
+        * so that IPI vectors are set up on the booting CPU before
+        * it is marked online in native_cpu_up().
+       */
+       rc = xen_smp_intr_init(cpu);
+       WARN_ON(rc);
+       if (!rc)
+               rc =  native_cpu_up(cpu, tidle);
        return rc;
 }
 
index e1284b8dc6eef9b800f997ecbf6bc5e60214eacc..3270d3c8ba4ed239d25b0507882fa50df3abb5e0 100644 (file)
@@ -908,9 +908,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
                device->cap._DDC = 1;
        }
 
-       if (acpi_video_init_brightness(device))
-               return;
-
        if (acpi_video_backlight_support()) {
                struct backlight_properties props;
                struct pci_dev *pdev;
@@ -920,6 +917,9 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
                static int count = 0;
                char *name;
 
+               result = acpi_video_init_brightness(device);
+               if (result)
+                       return;
                name = kasprintf(GFP_KERNEL, "acpi_video%d", count);
                if (!name)
                        return;
@@ -979,11 +979,6 @@ static void acpi_video_device_find_cap(struct acpi_video_device *device)
                if (result)
                        printk(KERN_ERR PREFIX "Create sysfs link\n");
 
-       } else {
-               /* Remove the brightness object. */
-               kfree(device->brightness->levels);
-               kfree(device->brightness);
-               device->brightness = NULL;
        }
 }
 
index 1c41722bb7e2d39016dd25f023dc37d6860b7584..20fd337a57314a2928c9208bf706ffabed0c4dd5 100644 (file)
@@ -289,24 +289,24 @@ static int sata_pmp_configure(struct ata_device *dev, int print_info)
 
        /* Disable sending Early R_OK.
         * With "cached read" HDD testing and multiple ports busy on a SATA
-        * host controller, 3726 PMP will very rarely drop a deferred
+        * host controller, 3x26 PMP will very rarely drop a deferred
         * R_OK that was intended for the host. Symptom will be all
         * 5 drives under test will timeout, get reset, and recover.
         */
-       if (vendor == 0x1095 && devid == 0x3726) {
+       if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) {
                u32 reg;
 
                err_mask = sata_pmp_read(&ap->link, PMP_GSCR_SII_POL, &reg);
                if (err_mask) {
                        rc = -EIO;
-                       reason = "failed to read Sil3726 Private Register";
+                       reason = "failed to read Sil3x26 Private Register";
                        goto fail;
                }
                reg &= ~0x1;
                err_mask = sata_pmp_write(&ap->link, PMP_GSCR_SII_POL, reg);
                if (err_mask) {
                        rc = -EIO;
-                       reason = "failed to write Sil3726 Private Register";
+                       reason = "failed to write Sil3x26 Private Register";
                        goto fail;
                }
        }
@@ -383,8 +383,8 @@ static void sata_pmp_quirks(struct ata_port *ap)
        u16 devid = sata_pmp_gscr_devid(gscr);
        struct ata_link *link;
 
-       if (vendor == 0x1095 && devid == 0x3726) {
-               /* sil3726 quirks */
+       if (vendor == 0x1095 && (devid == 0x3726 || devid == 0x3826)) {
+               /* sil3x26 quirks */
                ata_for_each_link(link, ap, EDGE) {
                        /* link reports offline after LPM */
                        link->flags |= ATA_LFLAG_NO_LPM;
index 19720a0a4a65ff5c6198ab4e37df7e756ce85717..851bd3f43ac63fc0f3631193f58aa3d94c0af39a 100644 (file)
@@ -293,6 +293,7 @@ static void fsl_sata_set_irq_coalescing(struct ata_host *host,
 {
        struct sata_fsl_host_priv *host_priv = host->private_data;
        void __iomem *hcr_base = host_priv->hcr_base;
+       unsigned long flags;
 
        if (count > ICC_MAX_INT_COUNT_THRESHOLD)
                count = ICC_MAX_INT_COUNT_THRESHOLD;
@@ -305,12 +306,12 @@ static void fsl_sata_set_irq_coalescing(struct ata_host *host,
                        (count > ICC_MIN_INT_COUNT_THRESHOLD))
                ticks = ICC_SAFE_INT_TICKS;
 
-       spin_lock(&host->lock);
+       spin_lock_irqsave(&host->lock, flags);
        iowrite32((count << 24 | ticks), hcr_base + ICC);
 
        intr_coalescing_count = count;
        intr_coalescing_ticks = ticks;
-       spin_unlock(&host->lock);
+       spin_unlock_irqrestore(&host->lock, flags);
 
        DPRINTK("interrupt coalescing, count = 0x%x, ticks = %x\n",
                        intr_coalescing_count, intr_coalescing_ticks);
index d047d92a456fbab39c56046d74feb24c9163668f..e9a4f46d962e817ab21aa44e155d399f07e76132 100644 (file)
@@ -86,11 +86,11 @@ struct ecx_plat_data {
 
 #define SGPIO_SIGNALS                  3
 #define ECX_ACTIVITY_BITS              0x300000
-#define ECX_ACTIVITY_SHIFT             2
+#define ECX_ACTIVITY_SHIFT             0
 #define ECX_LOCATE_BITS                        0x80000
 #define ECX_LOCATE_SHIFT               1
 #define ECX_FAULT_BITS                 0x400000
-#define ECX_FAULT_SHIFT                        0
+#define ECX_FAULT_SHIFT                        2
 static inline int sgpio_bit_shift(struct ecx_plat_data *pdata, u32 port,
                                u32 shift)
 {
index 19e36603b23b64e2c500cb425d3bf0246b2fdd8f..3bc8414533c9bfa235024bef3458bbcf3e119605 100644 (file)
@@ -500,7 +500,8 @@ static bool psb_intel_sdvo_read_response(struct psb_intel_sdvo *psb_intel_sdvo,
                                  &status))
                goto log_fail;
 
-       while (status == SDVO_CMD_STATUS_PENDING && retry--) {
+       while ((status == SDVO_CMD_STATUS_PENDING ||
+               status == SDVO_CMD_STATUS_TARGET_NOT_SPECIFIED) && retry--) {
                udelay(15);
                if (!psb_intel_sdvo_read_byte(psb_intel_sdvo,
                                          SDVO_I2C_CMD_STATUS,
index dc53a527126b0569800ff2df3a8a36ebbf904855..9e6578330801638caeb91e7f92e8e0139660eb6f 100644 (file)
@@ -85,9 +85,17 @@ static void i915_gem_unmap_dma_buf(struct dma_buf_attachment *attachment,
                                   struct sg_table *sg,
                                   enum dma_data_direction dir)
 {
+       struct drm_i915_gem_object *obj = attachment->dmabuf->priv;
+
+       mutex_lock(&obj->base.dev->struct_mutex);
+
        dma_unmap_sg(attachment->dev, sg->sgl, sg->nents, dir);
        sg_free_table(sg);
        kfree(sg);
+
+       i915_gem_object_unpin_pages(obj);
+
+       mutex_unlock(&obj->base.dev->struct_mutex);
 }
 
 static void i915_gem_dmabuf_release(struct dma_buf *dma_buf)
index 6f514297c4837882a64f9480b9f944c3913f7b0c..53cddd98540616084a7bcc05b718bb048879c730 100644 (file)
                                        will not assert AGPBUSY# and will only
                                        be delivered when out of C3. */
 #define   INSTPM_FORCE_ORDERING                                (1<<7) /* GEN6+ */
+#define   INSTPM_TLB_INVALIDATE        (1<<9)
+#define   INSTPM_SYNC_FLUSH    (1<<5)
 #define ACTHD          0x020c8
 #define FW_BLC         0x020d8
 #define FW_BLC2                0x020dc
index e38b457866535925acaf054b549f5bb07ce180f7..be79f477a38f9e48de386332e4062f09484a3453 100644 (file)
@@ -10042,6 +10042,8 @@ struct intel_display_error_state {
 
        u32 power_well_driver;
 
+       int num_transcoders;
+
        struct intel_cursor_error_state {
                u32 control;
                u32 position;
@@ -10050,16 +10052,7 @@ struct intel_display_error_state {
        } cursor[I915_MAX_PIPES];
 
        struct intel_pipe_error_state {
-               enum transcoder cpu_transcoder;
-               u32 conf;
                u32 source;
-
-               u32 htotal;
-               u32 hblank;
-               u32 hsync;
-               u32 vtotal;
-               u32 vblank;
-               u32 vsync;
        } pipe[I915_MAX_PIPES];
 
        struct intel_plane_error_state {
@@ -10071,6 +10064,19 @@ struct intel_display_error_state {
                u32 surface;
                u32 tile_offset;
        } plane[I915_MAX_PIPES];
+
+       struct intel_transcoder_error_state {
+               enum transcoder cpu_transcoder;
+
+               u32 conf;
+
+               u32 htotal;
+               u32 hblank;
+               u32 hsync;
+               u32 vtotal;
+               u32 vblank;
+               u32 vsync;
+       } transcoder[4];
 };
 
 struct intel_display_error_state *
@@ -10078,9 +10084,17 @@ intel_display_capture_error_state(struct drm_device *dev)
 {
        drm_i915_private_t *dev_priv = dev->dev_private;
        struct intel_display_error_state *error;
-       enum transcoder cpu_transcoder;
+       int transcoders[] = {
+               TRANSCODER_A,
+               TRANSCODER_B,
+               TRANSCODER_C,
+               TRANSCODER_EDP,
+       };
        int i;
 
+       if (INTEL_INFO(dev)->num_pipes == 0)
+               return NULL;
+
        error = kmalloc(sizeof(*error), GFP_ATOMIC);
        if (error == NULL)
                return NULL;
@@ -10089,9 +10103,6 @@ intel_display_capture_error_state(struct drm_device *dev)
                error->power_well_driver = I915_READ(HSW_PWR_WELL_DRIVER);
 
        for_each_pipe(i) {
-               cpu_transcoder = intel_pipe_to_cpu_transcoder(dev_priv, i);
-               error->pipe[i].cpu_transcoder = cpu_transcoder;
-
                if (INTEL_INFO(dev)->gen <= 6 || IS_VALLEYVIEW(dev)) {
                        error->cursor[i].control = I915_READ(CURCNTR(i));
                        error->cursor[i].position = I915_READ(CURPOS(i));
@@ -10115,14 +10126,25 @@ intel_display_capture_error_state(struct drm_device *dev)
                        error->plane[i].tile_offset = I915_READ(DSPTILEOFF(i));
                }
 
-               error->pipe[i].conf = I915_READ(PIPECONF(cpu_transcoder));
                error->pipe[i].source = I915_READ(PIPESRC(i));
-               error->pipe[i].htotal = I915_READ(HTOTAL(cpu_transcoder));
-               error->pipe[i].hblank = I915_READ(HBLANK(cpu_transcoder));
-               error->pipe[i].hsync = I915_READ(HSYNC(cpu_transcoder));
-               error->pipe[i].vtotal = I915_READ(VTOTAL(cpu_transcoder));
-               error->pipe[i].vblank = I915_READ(VBLANK(cpu_transcoder));
-               error->pipe[i].vsync = I915_READ(VSYNC(cpu_transcoder));
+       }
+
+       error->num_transcoders = INTEL_INFO(dev)->num_pipes;
+       if (HAS_DDI(dev_priv->dev))
+               error->num_transcoders++; /* Account for eDP. */
+
+       for (i = 0; i < error->num_transcoders; i++) {
+               enum transcoder cpu_transcoder = transcoders[i];
+
+               error->transcoder[i].cpu_transcoder = cpu_transcoder;
+
+               error->transcoder[i].conf = I915_READ(PIPECONF(cpu_transcoder));
+               error->transcoder[i].htotal = I915_READ(HTOTAL(cpu_transcoder));
+               error->transcoder[i].hblank = I915_READ(HBLANK(cpu_transcoder));
+               error->transcoder[i].hsync = I915_READ(HSYNC(cpu_transcoder));
+               error->transcoder[i].vtotal = I915_READ(VTOTAL(cpu_transcoder));
+               error->transcoder[i].vblank = I915_READ(VBLANK(cpu_transcoder));
+               error->transcoder[i].vsync = I915_READ(VSYNC(cpu_transcoder));
        }
 
        /* In the code above we read the registers without checking if the power
@@ -10144,22 +10166,16 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m,
 {
        int i;
 
+       if (!error)
+               return;
+
        err_printf(m, "Num Pipes: %d\n", INTEL_INFO(dev)->num_pipes);
        if (HAS_POWER_WELL(dev))
                err_printf(m, "PWR_WELL_CTL2: %08x\n",
                           error->power_well_driver);
        for_each_pipe(i) {
                err_printf(m, "Pipe [%d]:\n", i);
-               err_printf(m, "  CPU transcoder: %c\n",
-                          transcoder_name(error->pipe[i].cpu_transcoder));
-               err_printf(m, "  CONF: %08x\n", error->pipe[i].conf);
                err_printf(m, "  SRC: %08x\n", error->pipe[i].source);
-               err_printf(m, "  HTOTAL: %08x\n", error->pipe[i].htotal);
-               err_printf(m, "  HBLANK: %08x\n", error->pipe[i].hblank);
-               err_printf(m, "  HSYNC: %08x\n", error->pipe[i].hsync);
-               err_printf(m, "  VTOTAL: %08x\n", error->pipe[i].vtotal);
-               err_printf(m, "  VBLANK: %08x\n", error->pipe[i].vblank);
-               err_printf(m, "  VSYNC: %08x\n", error->pipe[i].vsync);
 
                err_printf(m, "Plane [%d]:\n", i);
                err_printf(m, "  CNTR: %08x\n", error->plane[i].control);
@@ -10180,5 +10196,17 @@ intel_display_print_error_state(struct drm_i915_error_state_buf *m,
                err_printf(m, "  POS: %08x\n", error->cursor[i].position);
                err_printf(m, "  BASE: %08x\n", error->cursor[i].base);
        }
+
+       for (i = 0; i < error->num_transcoders; i++) {
+               err_printf(m, "  CPU transcoder: %c\n",
+                          transcoder_name(error->transcoder[i].cpu_transcoder));
+               err_printf(m, "  CONF: %08x\n", error->transcoder[i].conf);
+               err_printf(m, "  HTOTAL: %08x\n", error->transcoder[i].htotal);
+               err_printf(m, "  HBLANK: %08x\n", error->transcoder[i].hblank);
+               err_printf(m, "  HSYNC: %08x\n", error->transcoder[i].hsync);
+               err_printf(m, "  VTOTAL: %08x\n", error->transcoder[i].vtotal);
+               err_printf(m, "  VBLANK: %08x\n", error->transcoder[i].vblank);
+               err_printf(m, "  VSYNC: %08x\n", error->transcoder[i].vsync);
+       }
 }
 #endif
index 664118d8c1d6426353ed97bb61b1113369a7678a..079ef0129e7416aabcf46e3c96ee409b65702808 100644 (file)
@@ -968,6 +968,18 @@ void intel_ring_setup_status_page(struct intel_ring_buffer *ring)
 
        I915_WRITE(mmio, (u32)ring->status_page.gfx_addr);
        POSTING_READ(mmio);
+
+       /* Flush the TLB for this page */
+       if (INTEL_INFO(dev)->gen >= 6) {
+               u32 reg = RING_INSTPM(ring->mmio_base);
+               I915_WRITE(reg,
+                          _MASKED_BIT_ENABLE(INSTPM_TLB_INVALIDATE |
+                                             INSTPM_SYNC_FLUSH));
+               if (wait_for((I915_READ(reg) & INSTPM_SYNC_FLUSH) == 0,
+                            1000))
+                       DRM_ERROR("%s: wait for SyncFlush to complete for TLB invalidation timed out\n",
+                                 ring->name);
+       }
 }
 
 static int
index d8291724dbd47d53ff6edc48a12ad669e5c68d8c..7a4e0891c5f872e0dbda7dc5164b348aa59058c7 100644 (file)
@@ -98,6 +98,8 @@ nouveau_mm_head(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min,
        u32 splitoff;
        u32 s, e;
 
+       BUG_ON(!type);
+
        list_for_each_entry(this, &mm->free, fl_entry) {
                e = this->offset + this->length;
                s = this->offset;
@@ -162,6 +164,8 @@ nouveau_mm_tail(struct nouveau_mm *mm, u8 type, u32 size_max, u32 size_min,
        struct nouveau_mm_node *prev, *this, *next;
        u32 mask = align - 1;
 
+       BUG_ON(!type);
+
        list_for_each_entry_reverse(this, &mm->free, fl_entry) {
                u32 e = this->offset + this->length;
                u32 s = this->offset;
index d5502267c30f71162ad901fdbedfa6a375cb54eb..9d2cd200625084608b9a6ecb8e866a9dd7cecf2f 100644 (file)
@@ -20,8 +20,8 @@ nouveau_mc(void *obj)
        return (void *)nv_device(obj)->subdev[NVDEV_SUBDEV_MC];
 }
 
-#define nouveau_mc_create(p,e,o,d)                                             \
-       nouveau_mc_create_((p), (e), (o), sizeof(**d), (void **)d)
+#define nouveau_mc_create(p,e,o,m,d)                                           \
+       nouveau_mc_create_((p), (e), (o), (m), sizeof(**d), (void **)d)
 #define nouveau_mc_destroy(p) ({                                               \
        struct nouveau_mc *pmc = (p); _nouveau_mc_dtor(nv_object(pmc));        \
 })
@@ -33,7 +33,8 @@ nouveau_mc(void *obj)
 })
 
 int  nouveau_mc_create_(struct nouveau_object *, struct nouveau_object *,
-                       struct nouveau_oclass *, int, void **);
+                       struct nouveau_oclass *, const struct nouveau_mc_intr *,
+                       int, void **);
 void _nouveau_mc_dtor(struct nouveau_object *);
 int  _nouveau_mc_init(struct nouveau_object *);
 int  _nouveau_mc_fini(struct nouveau_object *, bool);
index 19e3a9a63a02a79ae582e65fae75c617890afc5a..ab7ef0ac9e34c121e863c8110d243557773c681f 100644 (file)
@@ -40,15 +40,15 @@ nv49_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
                return ret;
 
        switch (pfb914 & 0x00000003) {
-       case 0x00000000: pfb->ram->type = NV_MEM_TYPE_DDR1; break;
-       case 0x00000001: pfb->ram->type = NV_MEM_TYPE_DDR2; break;
-       case 0x00000002: pfb->ram->type = NV_MEM_TYPE_GDDR3; break;
+       case 0x00000000: ram->type = NV_MEM_TYPE_DDR1; break;
+       case 0x00000001: ram->type = NV_MEM_TYPE_DDR2; break;
+       case 0x00000002: ram->type = NV_MEM_TYPE_GDDR3; break;
        case 0x00000003: break;
        }
 
-       pfb->ram->size  =  nv_rd32(pfb, 0x10020c) & 0xff000000;
-       pfb->ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
-       pfb->ram->tags  =  nv_rd32(pfb, 0x100320);
+       ram->size  =  nv_rd32(pfb, 0x10020c) & 0xff000000;
+       ram->parts = (nv_rd32(pfb, 0x100200) & 0x00000003) + 1;
+       ram->tags  =  nv_rd32(pfb, 0x100320);
        return 0;
 }
 
index 7192aa6e5577b6b2e1b460dc8b75a2b145eaff6e..63a6aab860282272caac175ff77ab89eb0fc91db 100644 (file)
@@ -38,8 +38,8 @@ nv4e_ram_create(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
-       pfb->ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000;
-       pfb->ram->type = NV_MEM_TYPE_STOLEN;
+       ram->size = nv_rd32(pfb, 0x10020c) & 0xff000000;
+       ram->type = NV_MEM_TYPE_STOLEN;
        return 0;
 }
 
index bcca883018f4cb030dd20642da03379c9064b06e..cce65cc565145cb74fdfd513967cb5723c1afc8d 100644 (file)
@@ -30,8 +30,9 @@ struct nvc0_ltcg_priv {
        struct nouveau_ltcg base;
        u32 part_nr;
        u32 subp_nr;
-       struct nouveau_mm tags;
        u32 num_tags;
+       u32 tag_base;
+       struct nouveau_mm tags;
        struct nouveau_mm_node *tag_ram;
 };
 
@@ -117,10 +118,6 @@ nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv)
        u32 tag_size, tag_margin, tag_align;
        int ret;
 
-       nv_wr32(priv, 0x17e8d8, priv->part_nr);
-       if (nv_device(pfb)->card_type >= NV_E0)
-               nv_wr32(priv, 0x17e000, priv->part_nr);
-
        /* tags for 1/4 of VRAM should be enough (8192/4 per GiB of VRAM) */
        priv->num_tags = (pfb->ram->size >> 17) / 4;
        if (priv->num_tags > (1 << 17))
@@ -142,7 +139,7 @@ nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv)
        tag_size += tag_align;
        tag_size  = (tag_size + 0xfff) >> 12; /* round up */
 
-       ret = nouveau_mm_tail(&pfb->vram, 0, tag_size, tag_size, 1,
+       ret = nouveau_mm_tail(&pfb->vram, 1, tag_size, tag_size, 1,
                              &priv->tag_ram);
        if (ret) {
                priv->num_tags = 0;
@@ -152,7 +149,7 @@ nvc0_ltcg_init_tag_ram(struct nouveau_fb *pfb, struct nvc0_ltcg_priv *priv)
                tag_base += tag_align - 1;
                ret = do_div(tag_base, tag_align);
 
-               nv_wr32(priv, 0x17e8d4, tag_base);
+               priv->tag_base = tag_base;
        }
        ret = nouveau_mm_init(&priv->tags, 0, priv->num_tags, 1);
 
@@ -182,8 +179,6 @@ nvc0_ltcg_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        }
        priv->subp_nr = nv_rd32(priv, 0x17e8dc) >> 28;
 
-       nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */
-
        ret = nvc0_ltcg_init_tag_ram(pfb, priv);
        if (ret)
                return ret;
@@ -209,13 +204,32 @@ nvc0_ltcg_dtor(struct nouveau_object *object)
        nouveau_ltcg_destroy(ltcg);
 }
 
+static int
+nvc0_ltcg_init(struct nouveau_object *object)
+{
+       struct nouveau_ltcg *ltcg = (struct nouveau_ltcg *)object;
+       struct nvc0_ltcg_priv *priv = (struct nvc0_ltcg_priv *)ltcg;
+       int ret;
+
+       ret = nouveau_ltcg_init(ltcg);
+       if (ret)
+               return ret;
+
+       nv_mask(priv, 0x17e820, 0x00100000, 0x00000000); /* INTR_EN &= ~0x10 */
+       nv_wr32(priv, 0x17e8d8, priv->part_nr);
+       if (nv_device(ltcg)->card_type >= NV_E0)
+               nv_wr32(priv, 0x17e000, priv->part_nr);
+       nv_wr32(priv, 0x17e8d4, priv->tag_base);
+       return 0;
+}
+
 struct nouveau_oclass
 nvc0_ltcg_oclass = {
        .handle = NV_SUBDEV(LTCG, 0xc0),
        .ofuncs = &(struct nouveau_ofuncs) {
                .ctor = nvc0_ltcg_ctor,
                .dtor = nvc0_ltcg_dtor,
-               .init = _nouveau_ltcg_init,
+               .init = nvc0_ltcg_init,
                .fini = _nouveau_ltcg_fini,
        },
 };
index 1c0330b8c9a43919861a42781f6691bec3cbf51f..ec9cd6f10f910aac9f56c4acbc4d9e09d393c85a 100644 (file)
@@ -80,7 +80,9 @@ _nouveau_mc_dtor(struct nouveau_object *object)
 
 int
 nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine,
-                  struct nouveau_oclass *oclass, int length, void **pobject)
+                  struct nouveau_oclass *oclass,
+                  const struct nouveau_mc_intr *intr_map,
+                  int length, void **pobject)
 {
        struct nouveau_device *device = nv_device(parent);
        struct nouveau_mc *pmc;
@@ -92,6 +94,8 @@ nouveau_mc_create_(struct nouveau_object *parent, struct nouveau_object *engine,
        if (ret)
                return ret;
 
+       pmc->intr_map = intr_map;
+
        ret = request_irq(device->pdev->irq, nouveau_mc_intr,
                          IRQF_SHARED, "nouveau", pmc);
        if (ret < 0)
index 8c769715227bd65a6b814b0585e047b169ad1bb8..64aa4edb0d9d958d60daf543fcd7d98209da10fb 100644 (file)
@@ -50,12 +50,11 @@ nv04_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv04_mc_priv *priv;
        int ret;
 
-       ret = nouveau_mc_create(parent, engine, oclass, &priv);
+       ret = nouveau_mc_create(parent, engine, oclass, nv04_mc_intr, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
-       priv->base.intr_map = nv04_mc_intr;
        return 0;
 }
 
index 51919371810fdf25c924f6e765ede4c0c682e829..d9891782bf28ea69a906a50714110914af9cb8ba 100644 (file)
@@ -36,12 +36,11 @@ nv44_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv44_mc_priv *priv;
        int ret;
 
-       ret = nouveau_mc_create(parent, engine, oclass, &priv);
+       ret = nouveau_mc_create(parent, engine, oclass, nv04_mc_intr, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
-       priv->base.intr_map = nv04_mc_intr;
        return 0;
 }
 
index f25fc5fc7dd11a773e6743a3df5d3ae5e1235532..2b1afe225db84b6a715e3891a96e649f65a7c380 100644 (file)
@@ -53,12 +53,11 @@ nv50_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv50_mc_priv *priv;
        int ret;
 
-       ret = nouveau_mc_create(parent, engine, oclass, &priv);
+       ret = nouveau_mc_create(parent, engine, oclass, nv50_mc_intr, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
-       priv->base.intr_map = nv50_mc_intr;
        return 0;
 }
 
index e82fd21b504154e8b28ed3dcca7fb7c1647a4394..0d57b4d3e001a9f2d141f745bed505ae7670badd 100644 (file)
@@ -54,12 +54,11 @@ nv98_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nv98_mc_priv *priv;
        int ret;
 
-       ret = nouveau_mc_create(parent, engine, oclass, &priv);
+       ret = nouveau_mc_create(parent, engine, oclass, nv98_mc_intr, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
-       priv->base.intr_map = nv98_mc_intr;
        return 0;
 }
 
index c5da3babbc621edccdb1bed8a55552885ec8d98f..104175c5a2ddf0a08ca82a01246b551605397246 100644 (file)
@@ -57,12 +57,11 @@ nvc0_mc_ctor(struct nouveau_object *parent, struct nouveau_object *engine,
        struct nvc0_mc_priv *priv;
        int ret;
 
-       ret = nouveau_mc_create(parent, engine, oclass, &priv);
+       ret = nouveau_mc_create(parent, engine, oclass, nvc0_mc_intr, &priv);
        *pobject = nv_object(priv);
        if (ret)
                return ret;
 
-       priv->base.intr_map = nvc0_mc_intr;
        return 0;
 }
 
index 0782bd2f1e04c4076c715c3bac9af271fa76348a..6a13ffb53bdb642e989cfdc75a7bc5f8341d08d3 100644 (file)
@@ -606,6 +606,24 @@ nv_crtc_mode_set_regs(struct drm_crtc *crtc, struct drm_display_mode * mode)
        regp->ramdac_a34 = 0x1;
 }
 
+static int
+nv_crtc_swap_fbs(struct drm_crtc *crtc, struct drm_framebuffer *old_fb)
+{
+       struct nv04_display *disp = nv04_display(crtc->dev);
+       struct nouveau_framebuffer *nvfb = nouveau_framebuffer(crtc->fb);
+       struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+       int ret;
+
+       ret = nouveau_bo_pin(nvfb->nvbo, TTM_PL_FLAG_VRAM);
+       if (ret == 0) {
+               if (disp->image[nv_crtc->index])
+                       nouveau_bo_unpin(disp->image[nv_crtc->index]);
+               nouveau_bo_ref(nvfb->nvbo, &disp->image[nv_crtc->index]);
+       }
+
+       return ret;
+}
+
 /**
  * Sets up registers for the given mode/adjusted_mode pair.
  *
@@ -622,10 +640,15 @@ nv_crtc_mode_set(struct drm_crtc *crtc, struct drm_display_mode *mode,
        struct drm_device *dev = crtc->dev;
        struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
        struct nouveau_drm *drm = nouveau_drm(dev);
+       int ret;
 
        NV_DEBUG(drm, "CTRC mode on CRTC %d:\n", nv_crtc->index);
        drm_mode_debug_printmodeline(adjusted_mode);
 
+       ret = nv_crtc_swap_fbs(crtc, old_fb);
+       if (ret)
+               return ret;
+
        /* unlock must come after turning off FP_TG_CONTROL in output_prepare */
        nv_lock_vga_crtc_shadow(dev, nv_crtc->index, -1);
 
@@ -722,6 +745,7 @@ static void nv_crtc_commit(struct drm_crtc *crtc)
 
 static void nv_crtc_destroy(struct drm_crtc *crtc)
 {
+       struct nv04_display *disp = nv04_display(crtc->dev);
        struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
 
        if (!nv_crtc)
@@ -729,6 +753,10 @@ static void nv_crtc_destroy(struct drm_crtc *crtc)
 
        drm_crtc_cleanup(crtc);
 
+       if (disp->image[nv_crtc->index])
+               nouveau_bo_unpin(disp->image[nv_crtc->index]);
+       nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]);
+
        nouveau_bo_unmap(nv_crtc->cursor.nvbo);
        nouveau_bo_unpin(nv_crtc->cursor.nvbo);
        nouveau_bo_ref(NULL, &nv_crtc->cursor.nvbo);
@@ -753,6 +781,16 @@ nv_crtc_gamma_load(struct drm_crtc *crtc)
        nouveau_hw_load_state_palette(dev, nv_crtc->index, &nv04_display(dev)->mode_reg);
 }
 
+static void
+nv_crtc_disable(struct drm_crtc *crtc)
+{
+       struct nv04_display *disp = nv04_display(crtc->dev);
+       struct nouveau_crtc *nv_crtc = nouveau_crtc(crtc);
+       if (disp->image[nv_crtc->index])
+               nouveau_bo_unpin(disp->image[nv_crtc->index]);
+       nouveau_bo_ref(NULL, &disp->image[nv_crtc->index]);
+}
+
 static void
 nv_crtc_gamma_set(struct drm_crtc *crtc, u16 *r, u16 *g, u16 *b, uint32_t start,
                  uint32_t size)
@@ -791,7 +829,6 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
        struct drm_framebuffer *drm_fb;
        struct nouveau_framebuffer *fb;
        int arb_burst, arb_lwm;
-       int ret;
 
        NV_DEBUG(drm, "index %d\n", nv_crtc->index);
 
@@ -801,10 +838,8 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
                return 0;
        }
 
-
        /* If atomic, we want to switch to the fb we were passed, so
-        * now we update pointers to do that.  (We don't pin; just
-        * assume we're already pinned and update the base address.)
+        * now we update pointers to do that.
         */
        if (atomic) {
                drm_fb = passed_fb;
@@ -812,17 +847,6 @@ nv04_crtc_do_mode_set_base(struct drm_crtc *crtc,
        } else {
                drm_fb = crtc->fb;
                fb = nouveau_framebuffer(crtc->fb);
-               /* If not atomic, we can go ahead and pin, and unpin the
-                * old fb we were passed.
-                */
-               ret = nouveau_bo_pin(fb->nvbo, TTM_PL_FLAG_VRAM);
-               if (ret)
-                       return ret;
-
-               if (passed_fb) {
-                       struct nouveau_framebuffer *ofb = nouveau_framebuffer(passed_fb);
-                       nouveau_bo_unpin(ofb->nvbo);
-               }
        }
 
        nv_crtc->fb.offset = fb->nvbo->bo.offset;
@@ -877,6 +901,9 @@ static int
 nv04_crtc_mode_set_base(struct drm_crtc *crtc, int x, int y,
                        struct drm_framebuffer *old_fb)
 {
+       int ret = nv_crtc_swap_fbs(crtc, old_fb);
+       if (ret)
+               return ret;
        return nv04_crtc_do_mode_set_base(crtc, old_fb, x, y, false);
 }
 
@@ -1027,6 +1054,7 @@ static const struct drm_crtc_helper_funcs nv04_crtc_helper_funcs = {
        .mode_set_base = nv04_crtc_mode_set_base,
        .mode_set_base_atomic = nv04_crtc_mode_set_base_atomic,
        .load_lut = nv_crtc_gamma_load,
+       .disable = nv_crtc_disable,
 };
 
 int
index a0a031dad13f366a9b69510afaa55200337fba3b..9928187f0a7d0bebdf3c80f8aec00809b294546e 100644 (file)
@@ -81,6 +81,7 @@ struct nv04_display {
        uint32_t saved_vga_font[4][16384];
        uint32_t dac_users[4];
        struct nouveau_object *core;
+       struct nouveau_bo *image[2];
 };
 
 static inline struct nv04_display *
index 907d20ef6d4d119f81c06fe1801edf3867f01673..a03e75deacafc23e05ff2c9982bd9e44e2380d76 100644 (file)
@@ -577,6 +577,9 @@ nouveau_crtc_page_flip(struct drm_crtc *crtc, struct drm_framebuffer *fb,
                ret = nv50_display_flip_next(crtc, fb, chan, 0);
                if (ret)
                        goto fail_unreserve;
+       } else {
+               struct nv04_display *dispnv04 = nv04_display(dev);
+               nouveau_bo_ref(new_bo, &dispnv04->image[nouveau_crtc(crtc)->index]);
        }
 
        ret = nouveau_page_flip_emit(chan, old_bo, new_bo, s, &fence);
index 3af5bcd0b203f904440fe33445d8da4098cf0bb2..625f80d53dc2b9c4c7e886ac5d282355193a643f 100644 (file)
@@ -131,7 +131,7 @@ nv40_calc_pll(struct drm_device *dev, u32 reg, struct nvbios_pll *pll,
        if (clk < pll->vco1.max_freq)
                pll->vco2.max_freq = 0;
 
-       pclk->pll_calc(pclk, pll, clk, &coef);
+       ret = pclk->pll_calc(pclk, pll, clk, &coef);
        if (ret == 0)
                return -ERANGE;
 
index 274b8e1b889fd0fbbe1dde2a71492e975f00a711..9f19259667dfa71e254052735be72ce0f14e75d9 100644 (file)
@@ -2163,7 +2163,7 @@ void cik_mm_wdoorbell(struct radeon_device *rdev, u32 offset, u32 v);
                WREG32(reg, tmp_);                              \
        } while (0)
 #define WREG32_AND(reg, and) WREG32_P(reg, 0, and)
-#define WREG32_OR(reg, or) WREG32_P(reg, or, ~or)
+#define WREG32_OR(reg, or) WREG32_P(reg, or, ~(or))
 #define WREG32_PLL_P(reg, val, mask)                           \
        do {                                                    \
                uint32_t tmp_ = RREG32_PLL(reg);                \
index f1c15754e73ca6d933d6ea1e0877b839cecab4b6..b79f4f5cdd626108c8790394cc6e57ddd9b27bce 100644 (file)
@@ -356,6 +356,14 @@ static int radeon_uvd_cs_msg(struct radeon_cs_parser *p, struct radeon_bo *bo,
                return -EINVAL;
        }
 
+       if (bo->tbo.sync_obj) {
+               r = radeon_fence_wait(bo->tbo.sync_obj, false);
+               if (r) {
+                       DRM_ERROR("Failed waiting for UVD message (%d)!\n", r);
+                       return r;
+               }
+       }
+
        r = radeon_bo_kmap(bo, &ptr);
        if (r) {
                DRM_ERROR("Failed mapping the UVD message (%d)!\n", r);
index bcc68ec204adeb7582a536cd3125ab28d00bce6d..f5e92cfcc140984bd63e1a892fa88277bb7530c3 100644 (file)
@@ -744,10 +744,10 @@ static void rv770_init_golden_registers(struct radeon_device *rdev)
                                                 (const u32)ARRAY_SIZE(r7xx_golden_dyn_gpr_registers));
                radeon_program_register_sequence(rdev,
                                                 rv730_golden_registers,
-                                                (const u32)ARRAY_SIZE(rv770_golden_registers));
+                                                (const u32)ARRAY_SIZE(rv730_golden_registers));
                radeon_program_register_sequence(rdev,
                                                 rv730_mgcg_init,
-                                                (const u32)ARRAY_SIZE(rv770_mgcg_init));
+                                                (const u32)ARRAY_SIZE(rv730_mgcg_init));
                break;
        case CHIP_RV710:
                radeon_program_register_sequence(rdev,
@@ -758,18 +758,18 @@ static void rv770_init_golden_registers(struct radeon_device *rdev)
                                                 (const u32)ARRAY_SIZE(r7xx_golden_dyn_gpr_registers));
                radeon_program_register_sequence(rdev,
                                                 rv710_golden_registers,
-                                                (const u32)ARRAY_SIZE(rv770_golden_registers));
+                                                (const u32)ARRAY_SIZE(rv710_golden_registers));
                radeon_program_register_sequence(rdev,
                                                 rv710_mgcg_init,
-                                                (const u32)ARRAY_SIZE(rv770_mgcg_init));
+                                                (const u32)ARRAY_SIZE(rv710_mgcg_init));
                break;
        case CHIP_RV740:
                radeon_program_register_sequence(rdev,
                                                 rv740_golden_registers,
-                                                (const u32)ARRAY_SIZE(rv770_golden_registers));
+                                                (const u32)ARRAY_SIZE(rv740_golden_registers));
                radeon_program_register_sequence(rdev,
                                                 rv740_mgcg_init,
-                                                (const u32)ARRAY_SIZE(rv770_mgcg_init));
+                                                (const u32)ARRAY_SIZE(rv740_mgcg_init));
                break;
        default:
                break;
index 5f4749e60b0428ae6fb3ffb1be7b4de00913b699..c1cd5698b8aea471802f471626e0b3865b2ca058 100644 (file)
@@ -232,7 +232,8 @@ static int adjd_s311_read_raw(struct iio_dev *indio_dev,
 
        switch (mask) {
        case IIO_CHAN_INFO_RAW:
-               ret = adjd_s311_read_data(indio_dev, chan->address, val);
+               ret = adjd_s311_read_data(indio_dev,
+                       ADJD_S311_DATA_REG(chan->address), val);
                if (ret < 0)
                        return ret;
                return IIO_VAL_INT;
index dc112a7137fe9280fca348908ed99b77f36f9417..4296155090b2b181f5840e21d97402ae0351d739 100644 (file)
@@ -959,23 +959,21 @@ out:
        return r;
 }
 
-static void remove_mapping(struct mq_policy *mq, dm_oblock_t oblock)
+static void mq_remove_mapping(struct dm_cache_policy *p, dm_oblock_t oblock)
 {
-       struct entry *e = hash_lookup(mq, oblock);
+       struct mq_policy *mq = to_mq_policy(p);
+       struct entry *e;
+
+       mutex_lock(&mq->lock);
+
+       e = hash_lookup(mq, oblock);
 
        BUG_ON(!e || !e->in_cache);
 
        del(mq, e);
        e->in_cache = false;
        push(mq, e);
-}
 
-static void mq_remove_mapping(struct dm_cache_policy *p, dm_oblock_t oblock)
-{
-       struct mq_policy *mq = to_mq_policy(p);
-
-       mutex_lock(&mq->lock);
-       remove_mapping(mq, oblock);
        mutex_unlock(&mq->lock);
 }
 
index ce9b387b5a1962949582354f7b0dcfecb621a08d..00b88cbfde25c41bdad32491eb7ab24edfb8c00d 100644 (file)
@@ -1333,6 +1333,8 @@ enum {
        BNX2X_SP_RTNL_VFPF_CHANNEL_DOWN,
        BNX2X_SP_RTNL_VFPF_STORM_RX_MODE,
        BNX2X_SP_RTNL_HYPERVISOR_VLAN,
+       BNX2X_SP_RTNL_TX_STOP,
+       BNX2X_SP_RTNL_TX_RESUME,
 };
 
 struct bnx2x_prev_path_list {
index f9122f2d6b657d0e674c7b036635b60e97586609..fcf2761d8828804d3edf7ca8e2ad245576d23685 100644 (file)
 #include "bnx2x_dcb.h"
 
 /* forward declarations of dcbx related functions */
-static int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp);
 static void bnx2x_pfc_set_pfc(struct bnx2x *bp);
 static void bnx2x_dcbx_update_ets_params(struct bnx2x *bp);
-static int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp);
 static void bnx2x_dcbx_get_ets_pri_pg_tbl(struct bnx2x *bp,
                                          u32 *set_configuration_ets_pg,
                                          u32 *pri_pg_tbl);
@@ -425,30 +423,52 @@ static void bnx2x_pfc_set_pfc(struct bnx2x *bp)
                bnx2x_pfc_clear(bp);
 }
 
-static int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp)
+int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp)
 {
        struct bnx2x_func_state_params func_params = {NULL};
+       int rc;
 
        func_params.f_obj = &bp->func_obj;
        func_params.cmd = BNX2X_F_CMD_TX_STOP;
 
+       __set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags);
+       __set_bit(RAMROD_RETRY, &func_params.ramrod_flags);
+
        DP(BNX2X_MSG_DCB, "STOP TRAFFIC\n");
-       return bnx2x_func_state_change(bp, &func_params);
+
+       rc = bnx2x_func_state_change(bp, &func_params);
+       if (rc) {
+               BNX2X_ERR("Unable to hold traffic for HW configuration\n");
+               bnx2x_panic();
+       }
+
+       return rc;
 }
 
-static int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp)
+int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp)
 {
        struct bnx2x_func_state_params func_params = {NULL};
        struct bnx2x_func_tx_start_params *tx_params =
                &func_params.params.tx_start;
+       int rc;
 
        func_params.f_obj = &bp->func_obj;
        func_params.cmd = BNX2X_F_CMD_TX_START;
 
+       __set_bit(RAMROD_COMP_WAIT, &func_params.ramrod_flags);
+       __set_bit(RAMROD_RETRY, &func_params.ramrod_flags);
+
        bnx2x_dcbx_fw_struct(bp, tx_params);
 
        DP(BNX2X_MSG_DCB, "START TRAFFIC\n");
-       return bnx2x_func_state_change(bp, &func_params);
+
+       rc = bnx2x_func_state_change(bp, &func_params);
+       if (rc) {
+               BNX2X_ERR("Unable to resume traffic after HW configuration\n");
+               bnx2x_panic();
+       }
+
+       return rc;
 }
 
 static void bnx2x_dcbx_2cos_limit_update_ets_config(struct bnx2x *bp)
@@ -744,7 +764,9 @@ void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state)
                        if (IS_MF(bp))
                                bnx2x_link_sync_notify(bp);
 
-                       bnx2x_dcbx_stop_hw_tx(bp);
+                       set_bit(BNX2X_SP_RTNL_TX_STOP, &bp->sp_rtnl_state);
+
+                       schedule_delayed_work(&bp->sp_rtnl_task, 0);
 
                        return;
                }
@@ -757,7 +779,9 @@ void bnx2x_dcbx_set_params(struct bnx2x *bp, u32 state)
                /* ets may affect cmng configuration: reinit it in hw */
                bnx2x_set_local_cmng(bp);
 
-               bnx2x_dcbx_resume_hw_tx(bp);
+               set_bit(BNX2X_SP_RTNL_TX_RESUME, &bp->sp_rtnl_state);
+
+               schedule_delayed_work(&bp->sp_rtnl_task, 0);
 
                return;
        case BNX2X_DCBX_STATE_TX_RELEASED:
@@ -2367,21 +2391,24 @@ static u8 bnx2x_dcbnl_get_featcfg(struct net_device *netdev, int featid,
                case DCB_FEATCFG_ATTR_PG:
                        if (bp->dcbx_local_feat.ets.enabled)
                                *flags |= DCB_FEATCFG_ENABLE;
-                       if (bp->dcbx_error & DCBX_LOCAL_ETS_ERROR)
+                       if (bp->dcbx_error & (DCBX_LOCAL_ETS_ERROR |
+                                             DCBX_REMOTE_MIB_ERROR))
                                *flags |= DCB_FEATCFG_ERROR;
                        break;
                case DCB_FEATCFG_ATTR_PFC:
                        if (bp->dcbx_local_feat.pfc.enabled)
                                *flags |= DCB_FEATCFG_ENABLE;
                        if (bp->dcbx_error & (DCBX_LOCAL_PFC_ERROR |
-                           DCBX_LOCAL_PFC_MISMATCH))
+                                             DCBX_LOCAL_PFC_MISMATCH |
+                                             DCBX_REMOTE_MIB_ERROR))
                                *flags |= DCB_FEATCFG_ERROR;
                        break;
                case DCB_FEATCFG_ATTR_APP:
                        if (bp->dcbx_local_feat.app.enabled)
                                *flags |= DCB_FEATCFG_ENABLE;
                        if (bp->dcbx_error & (DCBX_LOCAL_APP_ERROR |
-                           DCBX_LOCAL_APP_MISMATCH))
+                                             DCBX_LOCAL_APP_MISMATCH |
+                                             DCBX_REMOTE_MIB_ERROR))
                                *flags |= DCB_FEATCFG_ERROR;
                        break;
                default:
index 125bd1b6586ffc1f96b5fc946a4ee5a4613ce5a4..804b8f64463e80a1fcb45f51bda976b4d8544062 100644 (file)
@@ -199,4 +199,7 @@ extern const struct dcbnl_rtnl_ops bnx2x_dcbnl_ops;
 int bnx2x_dcbnl_update_applist(struct bnx2x *bp, bool delall);
 #endif /* BCM_DCBNL */
 
+int bnx2x_dcbx_stop_hw_tx(struct bnx2x *bp);
+int bnx2x_dcbx_resume_hw_tx(struct bnx2x *bp);
+
 #endif /* BNX2X_DCB_H */
index 955d6cfd9cb7c48179b587a7bf5239a572b7e9e8..8bdc8b9730074159f64411b818114c48727a08a5 100644 (file)
@@ -2261,6 +2261,23 @@ static void bnx2x_set_requested_fc(struct bnx2x *bp)
                bp->link_params.req_fc_auto_adv = BNX2X_FLOW_CTRL_BOTH;
 }
 
+static void bnx2x_init_dropless_fc(struct bnx2x *bp)
+{
+       u32 pause_enabled = 0;
+
+       if (!CHIP_IS_E1(bp) && bp->dropless_fc && bp->link_vars.link_up) {
+               if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX)
+                       pause_enabled = 1;
+
+               REG_WR(bp, BAR_USTRORM_INTMEM +
+                          USTORM_ETH_PAUSE_ENABLED_OFFSET(BP_PORT(bp)),
+                      pause_enabled);
+       }
+
+       DP(NETIF_MSG_IFUP | NETIF_MSG_LINK, "dropless_fc is %s\n",
+          pause_enabled ? "enabled" : "disabled");
+}
+
 int bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode)
 {
        int rc, cfx_idx = bnx2x_get_link_cfg_idx(bp);
@@ -2294,6 +2311,8 @@ int bnx2x_initial_phy_init(struct bnx2x *bp, int load_mode)
 
                bnx2x_release_phy_lock(bp);
 
+               bnx2x_init_dropless_fc(bp);
+
                bnx2x_calc_fc_adv(bp);
 
                if (bp->link_vars.link_up) {
@@ -2315,6 +2334,8 @@ void bnx2x_link_set(struct bnx2x *bp)
                bnx2x_phy_init(&bp->link_params, &bp->link_vars);
                bnx2x_release_phy_lock(bp);
 
+               bnx2x_init_dropless_fc(bp);
+
                bnx2x_calc_fc_adv(bp);
        } else
                BNX2X_ERR("Bootcode is missing - can not set link\n");
@@ -2556,20 +2577,9 @@ static void bnx2x_link_attn(struct bnx2x *bp)
 
        bnx2x_link_update(&bp->link_params, &bp->link_vars);
 
-       if (bp->link_vars.link_up) {
+       bnx2x_init_dropless_fc(bp);
 
-               /* dropless flow control */
-               if (!CHIP_IS_E1(bp) && bp->dropless_fc) {
-                       int port = BP_PORT(bp);
-                       u32 pause_enabled = 0;
-
-                       if (bp->link_vars.flow_ctrl & BNX2X_FLOW_CTRL_TX)
-                               pause_enabled = 1;
-
-                       REG_WR(bp, BAR_USTRORM_INTMEM +
-                              USTORM_ETH_PAUSE_ENABLED_OFFSET(port),
-                              pause_enabled);
-               }
+       if (bp->link_vars.link_up) {
 
                if (bp->link_vars.mac_type != MAC_TYPE_EMAC) {
                        struct host_port_stats *pstats;
@@ -9645,6 +9655,12 @@ sp_rtnl_not_reset:
                               &bp->sp_rtnl_state))
                bnx2x_pf_set_vfs_vlan(bp);
 
+       if (test_and_clear_bit(BNX2X_SP_RTNL_TX_STOP, &bp->sp_rtnl_state))
+               bnx2x_dcbx_stop_hw_tx(bp);
+
+       if (test_and_clear_bit(BNX2X_SP_RTNL_TX_RESUME, &bp->sp_rtnl_state))
+               bnx2x_dcbx_resume_hw_tx(bp);
+
        /* work which needs rtnl lock not-taken (as it takes the lock itself and
         * can be called from other contexts as well)
         */
@@ -11147,6 +11163,9 @@ static bool bnx2x_get_dropless_info(struct bnx2x *bp)
        int tmp;
        u32 cfg;
 
+       if (IS_VF(bp))
+               return 0;
+
        if (IS_MF(bp) && !CHIP_IS_E1x(bp)) {
                /* Take function: tmp = func */
                tmp = BP_ABS_FUNC(bp);
index 44104fb27947fba144575a33a0be3cf1343fe39d..ad83f4b4877761f73ed2ff790a9fef26b6bbd9b1 100644 (file)
@@ -1747,11 +1747,8 @@ void bnx2x_iov_init_dq(struct bnx2x *bp)
 
 void bnx2x_iov_init_dmae(struct bnx2x *bp)
 {
-       DP(BNX2X_MSG_IOV, "SRIOV is %s\n", IS_SRIOV(bp) ? "ON" : "OFF");
-       if (!IS_SRIOV(bp))
-               return;
-
-       REG_WR(bp, DMAE_REG_BACKWARD_COMP_EN, 0);
+       if (pci_find_ext_capability(bp->pdev, PCI_EXT_CAP_ID_SRIOV))
+               REG_WR(bp, DMAE_REG_BACKWARD_COMP_EN, 0);
 }
 
 static int bnx2x_vf_bus(struct bnx2x *bp, int vfid)
@@ -3084,8 +3081,9 @@ void bnx2x_disable_sriov(struct bnx2x *bp)
        pci_disable_sriov(bp->pdev);
 }
 
-static int bnx2x_vf_ndo_sanity(struct bnx2x *bp, int vfidx,
-                              struct bnx2x_virtf *vf)
+static int bnx2x_vf_ndo_prep(struct bnx2x *bp, int vfidx,
+                            struct bnx2x_virtf **vf,
+                            struct pf_vf_bulletin_content **bulletin)
 {
        if (bp->state != BNX2X_STATE_OPEN) {
                BNX2X_ERR("vf ndo called though PF is down\n");
@@ -3103,12 +3101,22 @@ static int bnx2x_vf_ndo_sanity(struct bnx2x *bp, int vfidx,
                return -EINVAL;
        }
 
-       if (!vf) {
+       /* init members */
+       *vf = BP_VF(bp, vfidx);
+       *bulletin = BP_VF_BULLETIN(bp, vfidx);
+
+       if (!*vf) {
                BNX2X_ERR("vf ndo called but vf was null. vfidx was %d\n",
                          vfidx);
                return -EINVAL;
        }
 
+       if (!*bulletin) {
+               BNX2X_ERR("vf ndo called but Bulletin Board struct is null. vfidx was %d\n",
+                         vfidx);
+               return -EINVAL;
+       }
+
        return 0;
 }
 
@@ -3116,17 +3124,19 @@ int bnx2x_get_vf_config(struct net_device *dev, int vfidx,
                        struct ifla_vf_info *ivi)
 {
        struct bnx2x *bp = netdev_priv(dev);
-       struct bnx2x_virtf *vf = BP_VF(bp, vfidx);
-       struct bnx2x_vlan_mac_obj *mac_obj = &bnx2x_vfq(vf, 0, mac_obj);
-       struct bnx2x_vlan_mac_obj *vlan_obj = &bnx2x_vfq(vf, 0, vlan_obj);
-       struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx);
+       struct bnx2x_virtf *vf = NULL;
+       struct pf_vf_bulletin_content *bulletin = NULL;
+       struct bnx2x_vlan_mac_obj *mac_obj;
+       struct bnx2x_vlan_mac_obj *vlan_obj;
        int rc;
 
-       /* sanity */
-       rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf);
+       /* sanity and init */
+       rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin);
        if (rc)
                return rc;
-       if (!mac_obj || !vlan_obj || !bulletin) {
+       mac_obj = &bnx2x_vfq(vf, 0, mac_obj);
+       vlan_obj = &bnx2x_vfq(vf, 0, vlan_obj);
+       if (!mac_obj || !vlan_obj) {
                BNX2X_ERR("VF partially initialized\n");
                return -EINVAL;
        }
@@ -3183,11 +3193,11 @@ int bnx2x_set_vf_mac(struct net_device *dev, int vfidx, u8 *mac)
 {
        struct bnx2x *bp = netdev_priv(dev);
        int rc, q_logical_state;
-       struct bnx2x_virtf *vf = BP_VF(bp, vfidx);
-       struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx);
+       struct bnx2x_virtf *vf = NULL;
+       struct pf_vf_bulletin_content *bulletin = NULL;
 
-       /* sanity */
-       rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf);
+       /* sanity and init */
+       rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin);
        if (rc)
                return rc;
        if (!is_valid_ether_addr(mac)) {
@@ -3249,11 +3259,11 @@ int bnx2x_set_vf_vlan(struct net_device *dev, int vfidx, u16 vlan, u8 qos)
 {
        struct bnx2x *bp = netdev_priv(dev);
        int rc, q_logical_state;
-       struct bnx2x_virtf *vf = BP_VF(bp, vfidx);
-       struct pf_vf_bulletin_content *bulletin = BP_VF_BULLETIN(bp, vfidx);
+       struct bnx2x_virtf *vf = NULL;
+       struct pf_vf_bulletin_content *bulletin = NULL;
 
-       /* sanity */
-       rc = bnx2x_vf_ndo_sanity(bp, vfidx, vf);
+       /* sanity and init */
+       rc = bnx2x_vf_ndo_prep(bp, vfidx, &vf, &bulletin);
        if (rc)
                return rc;
 
index 181edb522450ada0c9ba72e9a736d312594cc183..4559c35eea13b5bf5e989066cf062c94ab38710a 100644 (file)
@@ -2563,8 +2563,8 @@ static int be_close(struct net_device *netdev)
        /* Wait for all pending tx completions to arrive so that
         * all tx skbs are freed.
         */
-       be_tx_compl_clean(adapter);
        netif_tx_disable(netdev);
+       be_tx_compl_clean(adapter);
 
        be_rx_qs_destroy(adapter);
 
index b5eb4195fc9927a8b88493c7b1f783926928dc8c..85e5c97191dd0b1a6e75e5c548c9f96cbe19be04 100644 (file)
@@ -7088,7 +7088,7 @@ rtl_init_one(struct pci_dev *pdev, const struct pci_device_id *ent)
 
        RTL_W8(Cfg9346, Cfg9346_Unlock);
        RTL_W8(Config1, RTL_R8(Config1) | PMEnable);
-       RTL_W8(Config5, RTL_R8(Config5) & PMEStatus);
+       RTL_W8(Config5, RTL_R8(Config5) & (BWF | MWF | UWF | LanWake | PMEStatus));
        if ((RTL_R8(Config3) & (LinkUp | MagicPacket)) != 0)
                tp->features |= RTL_FEATURE_WOL;
        if ((RTL_R8(Config5) & (UWF | BWF | MWF)) != 0)
index 2a469b27a5061641a07a8502ecf63736953070a2..30d744235d276bc0bf10c56d7ea821692a5de811 100644 (file)
@@ -675,7 +675,7 @@ s32 efx_filter_insert_filter(struct efx_nic *efx, struct efx_filter_spec *spec,
                BUILD_BUG_ON(EFX_FILTER_INDEX_UC_DEF != 0);
                BUILD_BUG_ON(EFX_FILTER_INDEX_MC_DEF !=
                             EFX_FILTER_MC_DEF - EFX_FILTER_UC_DEF);
-               rep_index = spec->type - EFX_FILTER_INDEX_UC_DEF;
+               rep_index = spec->type - EFX_FILTER_UC_DEF;
                ins_index = rep_index;
 
                spin_lock_bh(&state->lock);
index 51f2bc37610188b44de5b8546062d16142571505..2dcc60fb37f1dee50beea82b878e98248c1688a0 100644 (file)
@@ -210,8 +210,7 @@ static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id)
                        pci_write_config_byte(pcidev,0x42,(bTmp | 0xf0));
                        pci_write_config_byte(pcidev,0x5a,0xc0);
                        WriteLPCReg(0x28, 0x70 );
-                       if (via_ircc_open(pcidev, &info, 0x3076) == 0)
-                               rc=0;
+                       rc = via_ircc_open(pcidev, &info, 0x3076);
                } else
                        rc = -ENODEV; //IR not turn on   
        } else { //Not VT1211
@@ -249,8 +248,7 @@ static int via_init_one(struct pci_dev *pcidev, const struct pci_device_id *id)
                        info.irq=FirIRQ;
                        info.dma=FirDRQ1;
                        info.dma2=FirDRQ0;
-                       if (via_ircc_open(pcidev, &info, 0x3096) == 0)
-                               rc=0;
+                       rc = via_ircc_open(pcidev, &info, 0x3096);
                } else
                        rc = -ENODEV; //IR not turn on !!!!!
        }//Not VT1211
index b51db2abfe442cd95fdbcb97e17aec702ec0d2c5..ea53abb209889ed1032323bfcebd154e68fc1d1d 100644 (file)
@@ -68,6 +68,8 @@ static const struct proto_ops macvtap_socket_ops;
 #define TUN_OFFLOADS (NETIF_F_HW_CSUM | NETIF_F_TSO_ECN | NETIF_F_TSO | \
                      NETIF_F_TSO6 | NETIF_F_UFO)
 #define RX_OFFLOADS (NETIF_F_GRO | NETIF_F_LRO)
+#define TAP_FEATURES (NETIF_F_GSO | NETIF_F_SG)
+
 /*
  * RCU usage:
  * The macvtap_queue and the macvlan_dev are loosely coupled, the
@@ -278,7 +280,8 @@ static int macvtap_forward(struct net_device *dev, struct sk_buff *skb)
 {
        struct macvlan_dev *vlan = netdev_priv(dev);
        struct macvtap_queue *q = macvtap_get_queue(dev, skb);
-       netdev_features_t features;
+       netdev_features_t features = TAP_FEATURES;
+
        if (!q)
                goto drop;
 
@@ -287,9 +290,11 @@ static int macvtap_forward(struct net_device *dev, struct sk_buff *skb)
 
        skb->dev = dev;
        /* Apply the forward feature mask so that we perform segmentation
-        * according to users wishes.
+        * according to users wishes.  This only works if VNET_HDR is
+        * enabled.
         */
-       features = netif_skb_features(skb) & vlan->tap_features;
+       if (q->flags & IFF_VNET_HDR)
+               features |= vlan->tap_features;
        if (netif_needs_gso(skb, features)) {
                struct sk_buff *segs = __skb_gso_segment(skb, features, false);
 
@@ -1064,8 +1069,7 @@ static int set_offload(struct macvtap_queue *q, unsigned long arg)
        /* tap_features are the same as features on tun/tap and
         * reflect user expectations.
         */
-       vlan->tap_features = vlan->dev->features &
-                           (feature_mask | ~TUN_OFFLOADS);
+       vlan->tap_features = feature_mask;
        vlan->set_features = features;
        netdev_update_features(vlan->dev);
 
@@ -1161,10 +1165,6 @@ static long macvtap_ioctl(struct file *file, unsigned int cmd,
                            TUN_F_TSO_ECN | TUN_F_UFO))
                        return -EINVAL;
 
-               /* TODO: only accept frames with the features that
-                        got enabled for forwarded frames */
-               if (!(q->flags & IFF_VNET_HDR))
-                       return  -EINVAL;
                rtnl_lock();
                ret = set_offload(q, arg);
                rtnl_unlock();
index 8e7af8354342c9ce6440e3131aad536fbe385c87..138de837977f1e5762ecb8ae6fecac59ade8c181 100644 (file)
@@ -23,7 +23,7 @@
 #define RTL821x_INER_INIT      0x6400
 #define RTL821x_INSR           0x13
 
-#define        RTL8211E_INER_LINK_STAT 0x10
+#define        RTL8211E_INER_LINK_STATUS       0x400
 
 MODULE_DESCRIPTION("Realtek PHY driver");
 MODULE_AUTHOR("Johnson Leung");
@@ -57,7 +57,7 @@ static int rtl8211e_config_intr(struct phy_device *phydev)
 
        if (phydev->interrupts == PHY_INTERRUPT_ENABLED)
                err = phy_write(phydev, RTL821x_INER,
-                               RTL8211E_INER_LINK_STAT);
+                               RTL8211E_INER_LINK_STATUS);
        else
                err = phy_write(phydev, RTL821x_INER, 0);
 
index cba1d46e672e4cde205fbbd36fb79275cd1ecc53..86292e6aaf4955c4412ead6579f74e6848bcd089 100644 (file)
@@ -2816,13 +2816,16 @@ exit:
 static int hso_get_config_data(struct usb_interface *interface)
 {
        struct usb_device *usbdev = interface_to_usbdev(interface);
-       u8 config_data[17];
+       u8 *config_data = kmalloc(17, GFP_KERNEL);
        u32 if_num = interface->altsetting->desc.bInterfaceNumber;
        s32 result;
 
+       if (!config_data)
+               return -ENOMEM;
        if (usb_control_msg(usbdev, usb_rcvctrlpipe(usbdev, 0),
                            0x86, 0xC0, 0, 0, config_data, 17,
                            USB_CTRL_SET_TIMEOUT) != 0x11) {
+               kfree(config_data);
                return -EIO;
        }
 
@@ -2873,6 +2876,7 @@ static int hso_get_config_data(struct usb_interface *interface)
        if (config_data[16] & 0x1)
                result |= HSO_INFO_CRC_BUG;
 
+       kfree(config_data);
        return result;
 }
 
@@ -2886,6 +2890,11 @@ static int hso_probe(struct usb_interface *interface,
        struct hso_shared_int *shared_int;
        struct hso_device *tmp_dev = NULL;
 
+       if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) {
+               dev_err(&interface->dev, "Not our interface\n");
+               return -ENODEV;
+       }
+
        if_num = interface->altsetting->desc.bInterfaceNumber;
 
        /* Get the interface/port specification from either driver_info or from
@@ -2895,10 +2904,6 @@ static int hso_probe(struct usb_interface *interface,
        else
                port_spec = hso_get_config_data(interface);
 
-       if (interface->cur_altsetting->desc.bInterfaceClass != 0xFF) {
-               dev_err(&interface->dev, "Not our interface\n");
-               return -ENODEV;
-       }
        /* Check if we need to switch to alt interfaces prior to port
         * configuration */
        if (interface->num_altsetting > 1)
index ac074731335a5ed1b7ef393dfd15ae6c87299d03..e5090309824e53c04d1961c0fd6993e80c5aa61e 100644 (file)
@@ -523,9 +523,9 @@ static int prism2_ioctl_giwaplist(struct net_device *dev,
 
        data->length = prism2_ap_get_sta_qual(local, addr, qual, IW_MAX_AP, 1);
 
-       memcpy(extra, &addr, sizeof(struct sockaddr) * data->length);
+       memcpy(extra, addr, sizeof(struct sockaddr) * data->length);
        data->flags = 1; /* has quality information */
-       memcpy(extra + sizeof(struct sockaddr) * data->length, &qual,
+       memcpy(extra + sizeof(struct sockaddr) * data->length, qual,
               sizeof(struct iw_quality) * data->length);
 
        kfree(addr);
index 822f1a00efbb7c5f40ed3edf7ca14f11103d49cc..319387263e1234daec62105051b00aa94994a107 100644 (file)
@@ -1068,7 +1068,10 @@ void iwl_chswitch_done(struct iwl_priv *priv, bool is_success)
        if (test_bit(STATUS_EXIT_PENDING, &priv->status))
                return;
 
-       if (test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
+       if (!test_and_clear_bit(STATUS_CHANNEL_SWITCH_PENDING, &priv->status))
+               return;
+
+       if (ctx->vif)
                ieee80211_chswitch_done(ctx->vif, is_success);
 }
 
index a70c7b9d9bad897345fb1e1e89d5c421e0d8a3da..ff8cc75c189d4d842abf8611fb8c5e7c7a63bf38 100644 (file)
@@ -97,8 +97,6 @@
 
 #define APMG_PCIDEV_STT_VAL_L1_ACT_DIS         (0x00000800)
 
-#define APMG_RTC_INT_STT_RFKILL                (0x10000000)
-
 /* Device system time */
 #define DEVICE_SYSTEM_TIME_REG 0xA0206C
 
index ad9bbca992133cc096ff0b90c5048ae248b635c9..7fd6fbfbc1b387696a7ad05f16e9bd49bf48b350 100644 (file)
@@ -138,6 +138,20 @@ static void iwl_mvm_roc_finished(struct iwl_mvm *mvm)
        schedule_work(&mvm->roc_done_wk);
 }
 
+static bool iwl_mvm_te_check_disconnect(struct iwl_mvm *mvm,
+                                       struct ieee80211_vif *vif,
+                                       const char *errmsg)
+{
+       if (vif->type != NL80211_IFTYPE_STATION)
+               return false;
+       if (vif->bss_conf.assoc && vif->bss_conf.dtim_period)
+               return false;
+       if (errmsg)
+               IWL_ERR(mvm, "%s\n", errmsg);
+       ieee80211_connection_loss(vif);
+       return true;
+}
+
 /*
  * Handles a FW notification for an event that is known to the driver.
  *
@@ -163,8 +177,13 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
         * P2P Device discoveribility, while there are other higher priority
         * events in the system).
         */
-       WARN_ONCE(!le32_to_cpu(notif->status),
-                 "Failed to schedule time event\n");
+       if (WARN_ONCE(!le32_to_cpu(notif->status),
+                     "Failed to schedule time event\n")) {
+               if (iwl_mvm_te_check_disconnect(mvm, te_data->vif, NULL)) {
+                       iwl_mvm_te_clear_data(mvm, te_data);
+                       return;
+               }
+       }
 
        if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_END) {
                IWL_DEBUG_TE(mvm,
@@ -180,14 +199,8 @@ static void iwl_mvm_te_handle_notif(struct iwl_mvm *mvm,
                 * By now, we should have finished association
                 * and know the dtim period.
                 */
-               if (te_data->vif->type == NL80211_IFTYPE_STATION &&
-                   (!te_data->vif->bss_conf.assoc ||
-                    !te_data->vif->bss_conf.dtim_period)) {
-                       IWL_ERR(mvm,
-                               "No assocation and the time event is over already...\n");
-                       ieee80211_connection_loss(te_data->vif);
-               }
-
+               iwl_mvm_te_check_disconnect(mvm, te_data->vif,
+                       "No assocation and the time event is over already...");
                iwl_mvm_te_clear_data(mvm, te_data);
        } else if (le32_to_cpu(notif->action) & TE_NOTIF_HOST_EVENT_START) {
                te_data->running = true;
index f600e68a410a1abe454170bd65d29b5937df41a9..fd848cd1583ebeb3372792911849a0eec25d9667 100644 (file)
@@ -888,14 +888,6 @@ irqreturn_t iwl_pcie_irq_handler(int irq, void *dev_id)
 
                iwl_op_mode_hw_rf_kill(trans->op_mode, hw_rfkill);
                if (hw_rfkill) {
-                       /*
-                        * Clear the interrupt in APMG if the NIC is going down.
-                        * Note that when the NIC exits RFkill (else branch), we
-                        * can't access prph and the NIC will be reset in
-                        * start_hw anyway.
-                        */
-                       iwl_write_prph(trans, APMG_RTC_INT_STT_REG,
-                                      APMG_RTC_INT_STT_RFKILL);
                        set_bit(STATUS_RFKILL, &trans_pcie->status);
                        if (test_and_clear_bit(STATUS_HCMD_ACTIVE,
                                               &trans_pcie->status))
index 96cfcdd390794060ee6c72c3951984f33da5ff38..390e2f058aff12c48e5f349e2c00730848e30b72 100644 (file)
@@ -1502,16 +1502,16 @@ struct iwl_trans *iwl_trans_pcie_alloc(struct pci_dev *pdev,
        spin_lock_init(&trans_pcie->reg_lock);
        init_waitqueue_head(&trans_pcie->ucode_write_waitq);
 
-       /* W/A - seems to solve weird behavior. We need to remove this if we
-        * don't want to stay in L1 all the time. This wastes a lot of power */
-       pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
-                              PCIE_LINK_STATE_CLKPM);
-
        if (pci_enable_device(pdev)) {
                err = -ENODEV;
                goto out_no_pci;
        }
 
+       /* W/A - seems to solve weird behavior. We need to remove this if we
+        * don't want to stay in L1 all the time. This wastes a lot of power */
+       pci_disable_link_state(pdev, PCIE_LINK_STATE_L0S | PCIE_LINK_STATE_L1 |
+                              PCIE_LINK_STATE_CLKPM);
+
        pci_set_master(pdev);
 
        err = pci_set_dma_mask(pdev, DMA_BIT_MASK(36));
index 4941f201d6c8dc5a4b62446a9153a4384a3cb4f4..b8ba1f925e75521a2886b42a8a3d95dac69c0aa9 100644 (file)
@@ -98,10 +98,12 @@ static int zd1201_fw_upload(struct usb_device *dev, int apfw)
                goto exit;
 
        err = usb_control_msg(dev, usb_rcvctrlpipe(dev, 0), 0x4,
-           USB_DIR_IN | 0x40, 0,0, &ret, sizeof(ret), ZD1201_FW_TIMEOUT);
+           USB_DIR_IN | 0x40, 0, 0, buf, sizeof(ret), ZD1201_FW_TIMEOUT);
        if (err < 0)
                goto exit;
 
+       memcpy(&ret, buf, sizeof(ret));
+
        if (ret & 0x80) {
                err = -EIO;
                goto exit;
index 6bb7cf2de556b559d1f54f9d1c7c3ff297138a3a..b10ba00cc3e6d00f476fc99e1f35daa26f5ae2e6 100644 (file)
@@ -392,6 +392,8 @@ static void __unflatten_device_tree(struct boot_param_header *blob,
        mem = (unsigned long)
                dt_alloc(size + 4, __alignof__(struct device_node));
 
+       memset((void *)mem, 0, size);
+
        ((__be32 *)mem)[size / 4] = cpu_to_be32(0xdeadbeef);
 
        pr_debug("  unflattening %lx...\n", mem);
index c47fd1e5450ba6f26ba8e70f2ad207efc1959a82..94716c779800ea099bfd580c627a93345af7d1db 100644 (file)
@@ -278,6 +278,7 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev,
 {
        struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
        struct sunxi_pinctrl_group *g = &pctl->groups[group];
+       unsigned long flags;
        u32 val, mask;
        u16 strength;
        u8 dlevel;
@@ -295,22 +296,35 @@ static int sunxi_pconf_group_set(struct pinctrl_dev *pctldev,
                 *   3: 40mA
                 */
                dlevel = strength / 10 - 1;
+
+               spin_lock_irqsave(&pctl->lock, flags);
+
                val = readl(pctl->membase + sunxi_dlevel_reg(g->pin));
                mask = DLEVEL_PINS_MASK << sunxi_dlevel_offset(g->pin);
                writel((val & ~mask) | dlevel << sunxi_dlevel_offset(g->pin),
                        pctl->membase + sunxi_dlevel_reg(g->pin));
+
+               spin_unlock_irqrestore(&pctl->lock, flags);
                break;
        case PIN_CONFIG_BIAS_PULL_UP:
+               spin_lock_irqsave(&pctl->lock, flags);
+
                val = readl(pctl->membase + sunxi_pull_reg(g->pin));
                mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin);
                writel((val & ~mask) | 1 << sunxi_pull_offset(g->pin),
                        pctl->membase + sunxi_pull_reg(g->pin));
+
+               spin_unlock_irqrestore(&pctl->lock, flags);
                break;
        case PIN_CONFIG_BIAS_PULL_DOWN:
+               spin_lock_irqsave(&pctl->lock, flags);
+
                val = readl(pctl->membase + sunxi_pull_reg(g->pin));
                mask = PULL_PINS_MASK << sunxi_pull_offset(g->pin);
                writel((val & ~mask) | 2 << sunxi_pull_offset(g->pin),
                        pctl->membase + sunxi_pull_reg(g->pin));
+
+               spin_unlock_irqrestore(&pctl->lock, flags);
                break;
        default:
                break;
@@ -360,11 +374,17 @@ static void sunxi_pmx_set(struct pinctrl_dev *pctldev,
                                 u8 config)
 {
        struct sunxi_pinctrl *pctl = pinctrl_dev_get_drvdata(pctldev);
+       unsigned long flags;
+       u32 val, mask;
+
+       spin_lock_irqsave(&pctl->lock, flags);
 
-       u32 val = readl(pctl->membase + sunxi_mux_reg(pin));
-       u32 mask = MUX_PINS_MASK << sunxi_mux_offset(pin);
+       val = readl(pctl->membase + sunxi_mux_reg(pin));
+       mask = MUX_PINS_MASK << sunxi_mux_offset(pin);
        writel((val & ~mask) | config << sunxi_mux_offset(pin),
                pctl->membase + sunxi_mux_reg(pin));
+
+       spin_unlock_irqrestore(&pctl->lock, flags);
 }
 
 static int sunxi_pmx_enable(struct pinctrl_dev *pctldev,
@@ -464,8 +484,21 @@ static void sunxi_pinctrl_gpio_set(struct gpio_chip *chip,
        struct sunxi_pinctrl *pctl = dev_get_drvdata(chip->dev);
        u32 reg = sunxi_data_reg(offset);
        u8 index = sunxi_data_offset(offset);
+       unsigned long flags;
+       u32 regval;
+
+       spin_lock_irqsave(&pctl->lock, flags);
+
+       regval = readl(pctl->membase + reg);
 
-       writel((value & DATA_PINS_MASK) << index, pctl->membase + reg);
+       if (value)
+               regval |= BIT(index);
+       else
+               regval &= ~(BIT(index));
+
+       writel(regval, pctl->membase + reg);
+
+       spin_unlock_irqrestore(&pctl->lock, flags);
 }
 
 static int sunxi_pinctrl_gpio_of_xlate(struct gpio_chip *gc,
@@ -526,6 +559,8 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d,
        struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
        u32 reg = sunxi_irq_cfg_reg(d->hwirq);
        u8 index = sunxi_irq_cfg_offset(d->hwirq);
+       unsigned long flags;
+       u32 regval;
        u8 mode;
 
        switch (type) {
@@ -548,7 +583,13 @@ static int sunxi_pinctrl_irq_set_type(struct irq_data *d,
                return -EINVAL;
        }
 
-       writel((mode & IRQ_CFG_IRQ_MASK) << index, pctl->membase + reg);
+       spin_lock_irqsave(&pctl->lock, flags);
+
+       regval = readl(pctl->membase + reg);
+       regval &= ~IRQ_CFG_IRQ_MASK;
+       writel(regval | (mode << index), pctl->membase + reg);
+
+       spin_unlock_irqrestore(&pctl->lock, flags);
 
        return 0;
 }
@@ -560,14 +601,19 @@ static void sunxi_pinctrl_irq_mask_ack(struct irq_data *d)
        u8 ctrl_idx = sunxi_irq_ctrl_offset(d->hwirq);
        u32 status_reg = sunxi_irq_status_reg(d->hwirq);
        u8 status_idx = sunxi_irq_status_offset(d->hwirq);
+       unsigned long flags;
        u32 val;
 
+       spin_lock_irqsave(&pctl->lock, flags);
+
        /* Mask the IRQ */
        val = readl(pctl->membase + ctrl_reg);
        writel(val & ~(1 << ctrl_idx), pctl->membase + ctrl_reg);
 
        /* Clear the IRQ */
        writel(1 << status_idx, pctl->membase + status_reg);
+
+       spin_unlock_irqrestore(&pctl->lock, flags);
 }
 
 static void sunxi_pinctrl_irq_mask(struct irq_data *d)
@@ -575,11 +621,16 @@ static void sunxi_pinctrl_irq_mask(struct irq_data *d)
        struct sunxi_pinctrl *pctl = irq_data_get_irq_chip_data(d);
        u32 reg = sunxi_irq_ctrl_reg(d->hwirq);
        u8 idx = sunxi_irq_ctrl_offset(d->hwirq);
+       unsigned long flags;
        u32 val;
 
+       spin_lock_irqsave(&pctl->lock, flags);
+
        /* Mask the IRQ */
        val = readl(pctl->membase + reg);
        writel(val & ~(1 << idx), pctl->membase + reg);
+
+       spin_unlock_irqrestore(&pctl->lock, flags);
 }
 
 static void sunxi_pinctrl_irq_unmask(struct irq_data *d)
@@ -588,6 +639,7 @@ static void sunxi_pinctrl_irq_unmask(struct irq_data *d)
        struct sunxi_desc_function *func;
        u32 reg = sunxi_irq_ctrl_reg(d->hwirq);
        u8 idx = sunxi_irq_ctrl_offset(d->hwirq);
+       unsigned long flags;
        u32 val;
 
        func = sunxi_pinctrl_desc_find_function_by_pin(pctl,
@@ -597,9 +649,13 @@ static void sunxi_pinctrl_irq_unmask(struct irq_data *d)
        /* Change muxing to INT mode */
        sunxi_pmx_set(pctl->pctl_dev, pctl->irq_array[d->hwirq], func->muxval);
 
+       spin_lock_irqsave(&pctl->lock, flags);
+
        /* Unmask the IRQ */
        val = readl(pctl->membase + reg);
        writel(val | (1 << idx), pctl->membase + reg);
+
+       spin_unlock_irqrestore(&pctl->lock, flags);
 }
 
 static struct irq_chip sunxi_pinctrl_irq_chip = {
@@ -752,6 +808,8 @@ static int sunxi_pinctrl_probe(struct platform_device *pdev)
                return -ENOMEM;
        platform_set_drvdata(pdev, pctl);
 
+       spin_lock_init(&pctl->lock);
+
        pctl->membase = of_iomap(node, 0);
        if (!pctl->membase)
                return -ENOMEM;
index d68047d8f6992e709c250d39dab4cf1497102eea..01c494f8a14f0119493d783624b86831549ccdb0 100644 (file)
@@ -14,6 +14,7 @@
 #define __PINCTRL_SUNXI_H
 
 #include <linux/kernel.h>
+#include <linux/spinlock.h>
 
 #define PA_BASE        0
 #define PB_BASE        32
@@ -407,6 +408,7 @@ struct sunxi_pinctrl {
        unsigned                        ngroups;
        int                             irq;
        int                             irq_array[SUNXI_IRQ_NUMBER];
+       spinlock_t                      lock;
        struct pinctrl_dev              *pctl_dev;
 };
 
index 0f9f8596b300a0d06d525547159bf4d038ba1505..f9119525f5570c2d041d563c0eefbc9a01e32c20 100644 (file)
@@ -330,7 +330,7 @@ static int __init olpc_ec_init_module(void)
        return platform_driver_register(&olpc_ec_plat_driver);
 }
 
-module_init(olpc_ec_init_module);
+arch_initcall(olpc_ec_init_module);
 
 MODULE_AUTHOR("Andres Salomon <dilinger@queued.net>");
 MODULE_LICENSE("GPL");
index 97bb05edcb5a806d85e9930022a77530735b2336..d6970f47ae72f639d648c924a27bea8b97cbdfb6 100644 (file)
@@ -53,7 +53,6 @@ MODULE_ALIAS("wmi:5FB7F034-2C63-45e9-BE91-3D44E2C707E4");
 #define HPWMI_ALS_QUERY 0x3
 #define HPWMI_HARDWARE_QUERY 0x4
 #define HPWMI_WIRELESS_QUERY 0x5
-#define HPWMI_BIOS_QUERY 0x9
 #define HPWMI_HOTKEY_QUERY 0xc
 #define HPWMI_WIRELESS2_QUERY 0x1b
 #define HPWMI_POSTCODEERROR_QUERY 0x2a
@@ -293,19 +292,6 @@ static int hp_wmi_tablet_state(void)
        return (state & 0x4) ? 1 : 0;
 }
 
-static int hp_wmi_enable_hotkeys(void)
-{
-       int ret;
-       int query = 0x6e;
-
-       ret = hp_wmi_perform_query(HPWMI_BIOS_QUERY, 1, &query, sizeof(query),
-                                  0);
-
-       if (ret)
-               return -EINVAL;
-       return 0;
-}
-
 static int hp_wmi_set_block(void *data, bool blocked)
 {
        enum hp_wmi_radio r = (enum hp_wmi_radio) data;
@@ -1009,8 +995,6 @@ static int __init hp_wmi_init(void)
                err = hp_wmi_input_setup();
                if (err)
                        return err;
-
-               hp_wmi_enable_hotkeys();
        }
 
        if (bios_capable) {
index 2ac045f27f10112aa467b867a9b97a9a1790c189..3a1b6bf326a814d8453a1cdb2e3d699834f7e87a 100644 (file)
@@ -2440,7 +2440,10 @@ static ssize_t sony_nc_gfx_switch_status_show(struct device *dev,
        if (pos < 0)
                return pos;
 
-       return snprintf(buffer, PAGE_SIZE, "%s\n", pos ? "speed" : "stamina");
+       return snprintf(buffer, PAGE_SIZE, "%s\n",
+                                       pos == SPEED ? "speed" :
+                                       pos == STAMINA ? "stamina" :
+                                       pos == AUTO ? "auto" : "unknown");
 }
 
 static int sony_nc_gfx_switch_setup(struct platform_device *pd,
@@ -4320,7 +4323,8 @@ static int sony_pic_add(struct acpi_device *device)
                goto err_free_resources;
        }
 
-       if (sonypi_compat_init())
+       result = sonypi_compat_init();
+       if (result)
                goto err_remove_input;
 
        /* request io port */
index 1d4c8fe72752899acb408119bc435b959277d731..c82fe65c41286ae90af5d9d12452df32202e5504 100644 (file)
@@ -102,10 +102,13 @@ static void zfcp_erp_action_dismiss_port(struct zfcp_port *port)
 
        if (atomic_read(&port->status) & ZFCP_STATUS_COMMON_ERP_INUSE)
                zfcp_erp_action_dismiss(&port->erp_action);
-       else
-               shost_for_each_device(sdev, port->adapter->scsi_host)
+       else {
+               spin_lock(port->adapter->scsi_host->host_lock);
+               __shost_for_each_device(sdev, port->adapter->scsi_host)
                        if (sdev_to_zfcp(sdev)->port == port)
                                zfcp_erp_action_dismiss_lun(sdev);
+               spin_unlock(port->adapter->scsi_host->host_lock);
+       }
 }
 
 static void zfcp_erp_action_dismiss_adapter(struct zfcp_adapter *adapter)
@@ -592,9 +595,11 @@ static void _zfcp_erp_lun_reopen_all(struct zfcp_port *port, int clear,
 {
        struct scsi_device *sdev;
 
-       shost_for_each_device(sdev, port->adapter->scsi_host)
+       spin_lock(port->adapter->scsi_host->host_lock);
+       __shost_for_each_device(sdev, port->adapter->scsi_host)
                if (sdev_to_zfcp(sdev)->port == port)
                        _zfcp_erp_lun_reopen(sdev, clear, id, 0);
+       spin_unlock(port->adapter->scsi_host->host_lock);
 }
 
 static void zfcp_erp_strategy_followup_failed(struct zfcp_erp_action *act)
@@ -1434,8 +1439,10 @@ void zfcp_erp_set_adapter_status(struct zfcp_adapter *adapter, u32 mask)
                atomic_set_mask(common_mask, &port->status);
        read_unlock_irqrestore(&adapter->port_list_lock, flags);
 
-       shost_for_each_device(sdev, adapter->scsi_host)
+       spin_lock_irqsave(adapter->scsi_host->host_lock, flags);
+       __shost_for_each_device(sdev, adapter->scsi_host)
                atomic_set_mask(common_mask, &sdev_to_zfcp(sdev)->status);
+       spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags);
 }
 
 /**
@@ -1469,11 +1476,13 @@ void zfcp_erp_clear_adapter_status(struct zfcp_adapter *adapter, u32 mask)
        }
        read_unlock_irqrestore(&adapter->port_list_lock, flags);
 
-       shost_for_each_device(sdev, adapter->scsi_host) {
+       spin_lock_irqsave(adapter->scsi_host->host_lock, flags);
+       __shost_for_each_device(sdev, adapter->scsi_host) {
                atomic_clear_mask(common_mask, &sdev_to_zfcp(sdev)->status);
                if (clear_counter)
                        atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0);
        }
+       spin_unlock_irqrestore(adapter->scsi_host->host_lock, flags);
 }
 
 /**
@@ -1487,16 +1496,19 @@ void zfcp_erp_set_port_status(struct zfcp_port *port, u32 mask)
 {
        struct scsi_device *sdev;
        u32 common_mask = mask & ZFCP_COMMON_FLAGS;
+       unsigned long flags;
 
        atomic_set_mask(mask, &port->status);
 
        if (!common_mask)
                return;
 
-       shost_for_each_device(sdev, port->adapter->scsi_host)
+       spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags);
+       __shost_for_each_device(sdev, port->adapter->scsi_host)
                if (sdev_to_zfcp(sdev)->port == port)
                        atomic_set_mask(common_mask,
                                        &sdev_to_zfcp(sdev)->status);
+       spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags);
 }
 
 /**
@@ -1511,6 +1523,7 @@ void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask)
        struct scsi_device *sdev;
        u32 common_mask = mask & ZFCP_COMMON_FLAGS;
        u32 clear_counter = mask & ZFCP_STATUS_COMMON_ERP_FAILED;
+       unsigned long flags;
 
        atomic_clear_mask(mask, &port->status);
 
@@ -1520,13 +1533,15 @@ void zfcp_erp_clear_port_status(struct zfcp_port *port, u32 mask)
        if (clear_counter)
                atomic_set(&port->erp_counter, 0);
 
-       shost_for_each_device(sdev, port->adapter->scsi_host)
+       spin_lock_irqsave(port->adapter->scsi_host->host_lock, flags);
+       __shost_for_each_device(sdev, port->adapter->scsi_host)
                if (sdev_to_zfcp(sdev)->port == port) {
                        atomic_clear_mask(common_mask,
                                          &sdev_to_zfcp(sdev)->status);
                        if (clear_counter)
                                atomic_set(&sdev_to_zfcp(sdev)->erp_counter, 0);
                }
+       spin_unlock_irqrestore(port->adapter->scsi_host->host_lock, flags);
 }
 
 /**
index 665e3cfaaf85d5b14859465163a3867073f2c5f5..de0598eaacd226ed2824ff94412069ed855a6420 100644 (file)
@@ -224,11 +224,9 @@ int zfcp_qdio_sbals_from_sg(struct zfcp_qdio *qdio, struct zfcp_qdio_req *q_req,
 
 static int zfcp_qdio_sbal_check(struct zfcp_qdio *qdio)
 {
-       spin_lock_irq(&qdio->req_q_lock);
        if (atomic_read(&qdio->req_q_free) ||
            !(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP))
                return 1;
-       spin_unlock_irq(&qdio->req_q_lock);
        return 0;
 }
 
@@ -246,9 +244,8 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio)
 {
        long ret;
 
-       spin_unlock_irq(&qdio->req_q_lock);
-       ret = wait_event_interruptible_timeout(qdio->req_q_wq,
-                              zfcp_qdio_sbal_check(qdio), 5 * HZ);
+       ret = wait_event_interruptible_lock_irq_timeout(qdio->req_q_wq,
+                      zfcp_qdio_sbal_check(qdio), qdio->req_q_lock, 5 * HZ);
 
        if (!(atomic_read(&qdio->adapter->status) & ZFCP_STATUS_ADAPTER_QDIOUP))
                return -EIO;
@@ -262,7 +259,6 @@ int zfcp_qdio_sbal_get(struct zfcp_qdio *qdio)
                zfcp_erp_adapter_reopen(qdio->adapter, 0, "qdsbg_1");
        }
 
-       spin_lock_irq(&qdio->req_q_lock);
        return -EIO;
 }
 
index 3f01bbf0609f6fb854911da5d9c0261271b184be..890639274bcfe83a1895a27b39a00d7d6ebf1ca0 100644 (file)
@@ -27,6 +27,16 @@ static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev,              \
 static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO,                                   \
                     zfcp_sysfs_##_feat##_##_name##_show, NULL);
 
+#define ZFCP_DEFINE_ATTR_CONST(_feat, _name, _format, _value)                 \
+static ssize_t zfcp_sysfs_##_feat##_##_name##_show(struct device *dev,        \
+                                                  struct device_attribute *at,\
+                                                  char *buf)                  \
+{                                                                             \
+       return sprintf(buf, _format, _value);                                  \
+}                                                                             \
+static ZFCP_DEV_ATTR(_feat, _name, S_IRUGO,                                   \
+                    zfcp_sysfs_##_feat##_##_name##_show, NULL);
+
 #define ZFCP_DEFINE_A_ATTR(_name, _format, _value)                          \
 static ssize_t zfcp_sysfs_adapter_##_name##_show(struct device *dev,        \
                                                 struct device_attribute *at,\
@@ -75,6 +85,8 @@ ZFCP_DEFINE_ATTR(zfcp_unit, unit, in_recovery, "%d\n",
 ZFCP_DEFINE_ATTR(zfcp_unit, unit, access_denied, "%d\n",
                 (zfcp_unit_sdev_status(unit) &
                  ZFCP_STATUS_COMMON_ACCESS_DENIED) != 0);
+ZFCP_DEFINE_ATTR_CONST(unit, access_shared, "%d\n", 0);
+ZFCP_DEFINE_ATTR_CONST(unit, access_readonly, "%d\n", 0);
 
 static ssize_t zfcp_sysfs_port_failed_show(struct device *dev,
                                           struct device_attribute *attr,
@@ -347,6 +359,8 @@ static struct attribute *zfcp_unit_attrs[] = {
        &dev_attr_unit_in_recovery.attr,
        &dev_attr_unit_status.attr,
        &dev_attr_unit_access_denied.attr,
+       &dev_attr_unit_access_shared.attr,
+       &dev_attr_unit_access_readonly.attr,
        NULL
 };
 static struct attribute_group zfcp_unit_attr_group = {
index 48b2918e0d654b7e5978d0da1063d4e25b5e16a0..92ff027746f2a8bfbe6a8d0f3434d807c9e56708 100644 (file)
@@ -1353,7 +1353,6 @@ config SCSI_LPFC
        tristate "Emulex LightPulse Fibre Channel Support"
        depends on PCI && SCSI
        select SCSI_FC_ATTRS
-       select GENERIC_CSUM
        select CRC_T10DIF
        help
           This lpfc driver supports the Emulex LightPulse
index e25eba5713c153a9fd2e829561eb481b26a8dc47..b3b5125faa728988b1d1efe31e4116df70622cf2 100644 (file)
@@ -482,7 +482,7 @@ int comedi_device_attach(struct comedi_device *dev, struct comedi_devconfig *it)
                ret = comedi_device_postconfig(dev);
        if (ret < 0) {
                comedi_device_detach(dev);
-               module_put(dev->driver->module);
+               module_put(driv->module);
        }
        /* On success, the driver module count has been incremented. */
        return ret;
index 08613e24189415641bc033d61af814e77b47143b..0f1d193fef02972163794fc611d8ee2f292cdc3a 100644 (file)
@@ -304,6 +304,11 @@ static int __init ohci_pci_init(void)
        pr_info("%s: " DRIVER_DESC "\n", hcd_name);
 
        ohci_init_driver(&ohci_pci_hc_driver, &pci_overrides);
+
+       /* Entries for the PCI suspend/resume callbacks are special */
+       ohci_pci_hc_driver.pci_suspend = ohci_suspend;
+       ohci_pci_hc_driver.pci_resume = ohci_resume;
+
        return pci_register_driver(&ohci_pci_driver);
 }
 module_init(ohci_pci_init);
index ca266280895dc2f150484f1dbb8873150588175b..e1859b8ef5679dc5c39a367c7e86979a2cfde5ec 100644 (file)
@@ -15,7 +15,7 @@
  * 675 Mass Ave, Cambridge, MA 02139, USA.
  */
 
-#include "otg_fsm.h"
+#include "phy-fsm-usb.h"
 #include <linux/usb/otg.h>
 #include <linux/ioctl.h>
 
index c520b3548e7cf59cd0058eda330cd2d7ac792197..7f4596606e18b41565ef338598298b6f36c64269 100644 (file)
@@ -29,7 +29,7 @@
 #include <linux/usb/gadget.h>
 #include <linux/usb/otg.h>
 
-#include "phy-otg-fsm.h"
+#include "phy-fsm-usb.h"
 
 /* Change USB protocol when there is a protocol change */
 static int otg_set_protocol(struct otg_fsm *fsm, int protocol)
index a58ac435a9a4a03f39274f8b5a41ea659d44eee7..5e8be462aed56da7060792b0abb8d4426e838d06 100644 (file)
@@ -348,7 +348,7 @@ static void init_evtchn_cpu_bindings(void)
 
        for_each_possible_cpu(i)
                memset(per_cpu(cpu_evtchn_mask, i),
-                      (i == 0) ? ~0 : 0, sizeof(*per_cpu(cpu_evtchn_mask, i)));
+                      (i == 0) ? ~0 : 0, NR_EVENT_CHANNELS/8);
 }
 
 static inline void clear_evtchn(int port)
@@ -1493,8 +1493,10 @@ void rebind_evtchn_irq(int evtchn, int irq)
 /* Rebind an evtchn so that it gets delivered to a specific cpu */
 static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu)
 {
+       struct shared_info *s = HYPERVISOR_shared_info;
        struct evtchn_bind_vcpu bind_vcpu;
        int evtchn = evtchn_from_irq(irq);
+       int masked;
 
        if (!VALID_EVTCHN(evtchn))
                return -1;
@@ -1510,6 +1512,12 @@ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu)
        bind_vcpu.port = evtchn;
        bind_vcpu.vcpu = tcpu;
 
+       /*
+        * Mask the event while changing the VCPU binding to prevent
+        * it being delivered on an unexpected VCPU.
+        */
+       masked = sync_test_and_set_bit(evtchn, BM(s->evtchn_mask));
+
        /*
         * If this fails, it usually just indicates that we're dealing with a
         * virq or IPI channel, which don't actually need to be rebound. Ignore
@@ -1518,6 +1526,9 @@ static int rebind_irq_to_cpu(unsigned irq, unsigned tcpu)
        if (HYPERVISOR_event_channel_op(EVTCHNOP_bind_vcpu, &bind_vcpu) >= 0)
                bind_evtchn_to_cpu(evtchn, tcpu);
 
+       if (!masked)
+               unmask_evtchn(evtchn);
+
        return 0;
 }
 
index 5e376bb934196d2b8a0e104473c637439e5e2244..8defc6b3f9a21b6e4971f9576a0fc202f7ba2e85 100644 (file)
@@ -40,7 +40,7 @@ struct inode *bfs_iget(struct super_block *sb, unsigned long ino)
        int block, off;
 
        inode = iget_locked(sb, ino);
-       if (IS_ERR(inode))
+       if (!inode)
                return ERR_PTR(-ENOMEM);
        if (!(inode->i_state & I_NEW))
                return inode;
index 94bbc04dba77053bb47d3d8b793a3a8218f0a0d2..c5eae7251490e9b48553000f8e582cb6c0b9420f 100644 (file)
--- a/fs/bio.c
+++ b/fs/bio.c
@@ -1045,12 +1045,22 @@ static int __bio_copy_iov(struct bio *bio, struct bio_vec *iovecs,
 int bio_uncopy_user(struct bio *bio)
 {
        struct bio_map_data *bmd = bio->bi_private;
-       int ret = 0;
+       struct bio_vec *bvec;
+       int ret = 0, i;
 
-       if (!bio_flagged(bio, BIO_NULL_MAPPED))
-               ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs,
-                                    bmd->nr_sgvecs, bio_data_dir(bio) == READ,
-                                    0, bmd->is_our_pages);
+       if (!bio_flagged(bio, BIO_NULL_MAPPED)) {
+               /*
+                * if we're in a workqueue, the request is orphaned, so
+                * don't copy into a random user address space, just free.
+                */
+               if (current->mm)
+                       ret = __bio_copy_iov(bio, bmd->iovecs, bmd->sgvecs,
+                                            bmd->nr_sgvecs, bio_data_dir(bio) == READ,
+                                            0, bmd->is_our_pages);
+               else if (bmd->is_our_pages)
+                       bio_for_each_segment_all(bvec, bio, i)
+                               __free_page(bvec->bv_page);
+       }
        bio_free_map_data(bmd);
        bio_put(bio);
        return ret;
index 87bdb5329c3c90b99d67f4b5bab3ae0b347d2acf..83cfb834db0364b1ffed30d58815f826aaaf55dd 100644 (file)
@@ -2724,6 +2724,17 @@ char *dynamic_dname(struct dentry *dentry, char *buffer, int buflen,
        return memcpy(buffer, temp, sz);
 }
 
+char *simple_dname(struct dentry *dentry, char *buffer, int buflen)
+{
+       char *end = buffer + buflen;
+       /* these dentries are never renamed, so d_lock is not needed */
+       if (prepend(&end, &buflen, " (deleted)", 11) ||
+           prepend_name(&end, &buflen, &dentry->d_name) ||
+           prepend(&end, &buflen, "/", 1))  
+               end = ERR_PTR(-ENAMETOOLONG);
+       return end;  
+}
+
 /*
  * Write full pathname from the root of the filesystem into the buffer.
  */
index f3913eb2c47482739681f51551cd6a325e6fc40d..d15ccf20f1b37a9734c338941106fdc0982e0ca4 100644 (file)
@@ -57,7 +57,7 @@ struct inode *efs_iget(struct super_block *super, unsigned long ino)
        struct inode *inode;
 
        inode = iget_locked(super, ino);
-       if (IS_ERR(inode))
+       if (!inode)
                return ERR_PTR(-ENOMEM);
        if (!(inode->i_state & I_NEW))
                return inode;
index 9435384562a271cacd5219651269b36d5d2923c4..544a809819c3ee5c16ddaa42a8ce0ce26d2194f4 100644 (file)
@@ -1838,14 +1838,14 @@ int __init gfs2_glock_init(void)
 
        glock_workqueue = alloc_workqueue("glock_workqueue", WQ_MEM_RECLAIM |
                                          WQ_HIGHPRI | WQ_FREEZABLE, 0);
-       if (IS_ERR(glock_workqueue))
-               return PTR_ERR(glock_workqueue);
+       if (!glock_workqueue)
+               return -ENOMEM;
        gfs2_delete_workqueue = alloc_workqueue("delete_workqueue",
                                                WQ_MEM_RECLAIM | WQ_FREEZABLE,
                                                0);
-       if (IS_ERR(gfs2_delete_workqueue)) {
+       if (!gfs2_delete_workqueue) {
                destroy_workqueue(glock_workqueue);
-               return PTR_ERR(gfs2_delete_workqueue);
+               return -ENOMEM;
        }
 
        register_shrinker(&glock_shrinker);
index 5f2e5224c51c9ae79e34a1eb0f405a7b304cd0be..e2e0a90396e7823da9aa47c44a727d5e75751cc6 100644 (file)
@@ -47,7 +47,8 @@ static void gfs2_ail_error(struct gfs2_glock *gl, const struct buffer_head *bh)
  * None of the buffers should be dirty, locked, or pinned.
  */
 
-static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync)
+static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync,
+                            unsigned int nr_revokes)
 {
        struct gfs2_sbd *sdp = gl->gl_sbd;
        struct list_head *head = &gl->gl_ail_list;
@@ -57,7 +58,9 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync)
 
        gfs2_log_lock(sdp);
        spin_lock(&sdp->sd_ail_lock);
-       list_for_each_entry_safe(bd, tmp, head, bd_ail_gl_list) {
+       list_for_each_entry_safe_reverse(bd, tmp, head, bd_ail_gl_list) {
+               if (nr_revokes == 0)
+                       break;
                bh = bd->bd_bh;
                if (bh->b_state & b_state) {
                        if (fsync)
@@ -65,6 +68,7 @@ static void __gfs2_ail_flush(struct gfs2_glock *gl, bool fsync)
                        gfs2_ail_error(gl, bh);
                }
                gfs2_trans_add_revoke(sdp, bd);
+               nr_revokes--;
        }
        GLOCK_BUG_ON(gl, !fsync && atomic_read(&gl->gl_ail_count));
        spin_unlock(&sdp->sd_ail_lock);
@@ -91,7 +95,7 @@ static void gfs2_ail_empty_gl(struct gfs2_glock *gl)
        WARN_ON_ONCE(current->journal_info);
        current->journal_info = &tr;
 
-       __gfs2_ail_flush(gl, 0);
+       __gfs2_ail_flush(gl, 0, tr.tr_revokes);
 
        gfs2_trans_end(sdp);
        gfs2_log_flush(sdp, NULL);
@@ -101,15 +105,19 @@ void gfs2_ail_flush(struct gfs2_glock *gl, bool fsync)
 {
        struct gfs2_sbd *sdp = gl->gl_sbd;
        unsigned int revokes = atomic_read(&gl->gl_ail_count);
+       unsigned int max_revokes = (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_log_descriptor)) / sizeof(u64);
        int ret;
 
        if (!revokes)
                return;
 
-       ret = gfs2_trans_begin(sdp, 0, revokes);
+       while (revokes > max_revokes)
+               max_revokes += (sdp->sd_sb.sb_bsize - sizeof(struct gfs2_meta_header)) / sizeof(u64);
+
+       ret = gfs2_trans_begin(sdp, 0, max_revokes);
        if (ret)
                return;
-       __gfs2_ail_flush(gl, fsync);
+       __gfs2_ail_flush(gl, fsync, max_revokes);
        gfs2_trans_end(sdp);
        gfs2_log_flush(sdp, NULL);
 }
index bbb2715171cd0c983770cf86b4b57ed69e04c98d..64915eeae5a7112f59256185a00a1cc9f3b2a193 100644 (file)
@@ -594,7 +594,7 @@ static int gfs2_create_inode(struct inode *dir, struct dentry *dentry,
                }
                gfs2_glock_dq_uninit(ghs);
                if (IS_ERR(d))
-                       return PTR_RET(d);
+                       return PTR_ERR(d);
                return error;
        } else if (error != -ENOENT) {
                goto fail_gunlock;
@@ -1750,6 +1750,10 @@ static ssize_t gfs2_getxattr(struct dentry *dentry, const char *name,
        struct gfs2_holder gh;
        int ret;
 
+       /* For selinux during lookup */
+       if (gfs2_glock_is_locked_by_me(ip->i_gl))
+               return generic_getxattr(dentry, name, data, size);
+
        gfs2_holder_init(ip->i_gl, LM_ST_SHARED, LM_FLAG_ANY, &gh);
        ret = gfs2_glock_nq(&gh);
        if (ret == 0) {
index e04d0e09ee7b59d5959d69e5df872a5871e9ae64..7b0f5043cf24c253612451787588d638da5483ce 100644 (file)
@@ -155,7 +155,7 @@ static int __init init_gfs2_fs(void)
                goto fail_wq;
 
        gfs2_control_wq = alloc_workqueue("gfs2_control",
-                              WQ_NON_REENTRANT | WQ_UNBOUND | WQ_FREEZABLE, 0);
+                                         WQ_UNBOUND | WQ_FREEZABLE, 0);
        if (!gfs2_control_wq)
                goto fail_recovery;
 
index 34423978b17083d73b038d360d72b47dc08c6693..d19b30ababf10b115175f3b9814e10130c7c52ed 100644 (file)
@@ -926,14 +926,8 @@ static int get_hstate_idx(int page_size_log)
        return h - hstates;
 }
 
-static char *hugetlb_dname(struct dentry *dentry, char *buffer, int buflen)
-{
-       return dynamic_dname(dentry, buffer, buflen, "/%s (deleted)",
-                               dentry->d_name.name);
-}
-
 static struct dentry_operations anon_ops = {
-       .d_dname = hugetlb_dname
+       .d_dname = simple_dname
 };
 
 /*
index 7b1ca9ba0b0a70213915f6d7bce688184ab7a56f..a45ba4f267fe6e834909f122dd166cdd608dcce5 100644 (file)
@@ -1429,7 +1429,7 @@ struct vfsmount *collect_mounts(struct path *path)
                         CL_COPY_ALL | CL_PRIVATE);
        namespace_unlock();
        if (IS_ERR(tree))
-               return NULL;
+               return ERR_CAST(tree);
        return &tree->mnt;
 }
 
index dc9a913784ab94127fba6e4503d12a0708069f41..2d8be51f90dc9257bf74cad77b719d17f781c739 100644 (file)
@@ -345,8 +345,7 @@ static void nilfs_end_bio_write(struct bio *bio, int err)
 
        if (err == -EOPNOTSUPP) {
                set_bit(BIO_EOPNOTSUPP, &bio->bi_flags);
-               bio_put(bio);
-               /* to be detected by submit_seg_bio() */
+               /* to be detected by nilfs_segbuf_submit_bio() */
        }
 
        if (!uptodate)
@@ -377,12 +376,12 @@ static int nilfs_segbuf_submit_bio(struct nilfs_segment_buffer *segbuf,
        bio->bi_private = segbuf;
        bio_get(bio);
        submit_bio(mode, bio);
+       segbuf->sb_nbio++;
        if (bio_flagged(bio, BIO_EOPNOTSUPP)) {
                bio_put(bio);
                err = -EOPNOTSUPP;
                goto failed;
        }
-       segbuf->sb_nbio++;
        bio_put(bio);
 
        wi->bio = NULL;
index 75f2890abbd8ddcc8a674b1c42397cc1456fd33e..0ff80f9b930f7226124e670c2814d7f4618d4532 100644 (file)
@@ -228,8 +228,6 @@ static int proc_readfd_common(struct file *file, struct dir_context *ctx,
        if (!p)
                return -ENOENT;
 
-       if (!dir_emit_dots(file, ctx))
-               goto out;
        if (!dir_emit_dots(file, ctx))
                goto out;
        files = get_files_struct(p);
index 94441a407337bb02fb77e89fe93dfbbed169f827..737e15615b0490c40d002a315217033f5463d5b3 100644 (file)
@@ -271,7 +271,7 @@ int proc_readdir_de(struct proc_dir_entry *de, struct file *file,
                de = next;
        } while (de);
        spin_unlock(&proc_subdir_lock);
-       return 0;
+       return 1;
 }
 
 int proc_readdir(struct file *file, struct dir_context *ctx)
index 229e366598daecd4e905e8f51f13efaf0a44e773..e0a790da726d0f710a7585b0a8b6663e9902a0ac 100644 (file)
@@ -205,7 +205,9 @@ static struct dentry *proc_root_lookup(struct inode * dir, struct dentry * dentr
 static int proc_root_readdir(struct file *file, struct dir_context *ctx)
 {
        if (ctx->pos < FIRST_PROCESS_ENTRY) {
-               proc_readdir(file, ctx);
+               int error = proc_readdir(file, ctx);
+               if (unlikely(error <= 0))
+                       return error;
                ctx->pos = FIRST_PROCESS_ENTRY;
        }
 
diff --git a/include/dt-bindings/sound/fsl-imx-audmux.h b/include/dt-bindings/sound/fsl-imx-audmux.h
new file mode 100644 (file)
index 0000000..50b09e9
--- /dev/null
@@ -0,0 +1,56 @@
+#ifndef __DT_FSL_IMX_AUDMUX_H
+#define __DT_FSL_IMX_AUDMUX_H
+
+#define MX27_AUDMUX_HPCR1_SSI0         0
+#define MX27_AUDMUX_HPCR2_SSI1         1
+#define MX27_AUDMUX_HPCR3_SSI_PINS_4   2
+#define MX27_AUDMUX_PPCR1_SSI_PINS_1   3
+#define MX27_AUDMUX_PPCR2_SSI_PINS_2   4
+#define MX27_AUDMUX_PPCR3_SSI_PINS_3   5
+
+#define MX31_AUDMUX_PORT1_SSI0         0
+#define MX31_AUDMUX_PORT2_SSI1         1
+#define MX31_AUDMUX_PORT3_SSI_PINS_3   2
+#define MX31_AUDMUX_PORT4_SSI_PINS_4   3
+#define MX31_AUDMUX_PORT5_SSI_PINS_5   4
+#define MX31_AUDMUX_PORT6_SSI_PINS_6   5
+#define MX31_AUDMUX_PORT7_SSI_PINS_7   6
+
+#define MX51_AUDMUX_PORT1_SSI0         0
+#define MX51_AUDMUX_PORT2_SSI1         1
+#define MX51_AUDMUX_PORT3              2
+#define MX51_AUDMUX_PORT4              3
+#define MX51_AUDMUX_PORT5              4
+#define MX51_AUDMUX_PORT6              5
+#define MX51_AUDMUX_PORT7              6
+
+/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */
+#define IMX_AUDMUX_V1_PCR_INMMASK(x)   ((x) & 0xff)
+#define IMX_AUDMUX_V1_PCR_INMEN                (1 << 8)
+#define IMX_AUDMUX_V1_PCR_TXRXEN       (1 << 10)
+#define IMX_AUDMUX_V1_PCR_SYN          (1 << 12)
+#define IMX_AUDMUX_V1_PCR_RXDSEL(x)    (((x) & 0x7) << 13)
+#define IMX_AUDMUX_V1_PCR_RFCSEL(x)    (((x) & 0xf) << 20)
+#define IMX_AUDMUX_V1_PCR_RCLKDIR      (1 << 24)
+#define IMX_AUDMUX_V1_PCR_RFSDIR       (1 << 25)
+#define IMX_AUDMUX_V1_PCR_TFCSEL(x)    (((x) & 0xf) << 26)
+#define IMX_AUDMUX_V1_PCR_TCLKDIR      (1 << 30)
+#define IMX_AUDMUX_V1_PCR_TFSDIR       (1 << 31)
+
+/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */
+#define IMX_AUDMUX_V2_PTCR_TFSDIR      (1 << 31)
+#define IMX_AUDMUX_V2_PTCR_TFSEL(x)    (((x) & 0xf) << 27)
+#define IMX_AUDMUX_V2_PTCR_TCLKDIR     (1 << 26)
+#define IMX_AUDMUX_V2_PTCR_TCSEL(x)    (((x) & 0xf) << 22)
+#define IMX_AUDMUX_V2_PTCR_RFSDIR      (1 << 21)
+#define IMX_AUDMUX_V2_PTCR_RFSEL(x)    (((x) & 0xf) << 17)
+#define IMX_AUDMUX_V2_PTCR_RCLKDIR     (1 << 16)
+#define IMX_AUDMUX_V2_PTCR_RCSEL(x)    (((x) & 0xf) << 12)
+#define IMX_AUDMUX_V2_PTCR_SYN         (1 << 11)
+
+#define IMX_AUDMUX_V2_PDCR_RXDSEL(x)   (((x) & 0x7) << 13)
+#define IMX_AUDMUX_V2_PDCR_TXRXEN      (1 << 12)
+#define IMX_AUDMUX_V2_PDCR_MODE(x)     (((x) & 0x3) << 8)
+#define IMX_AUDMUX_V2_PDCR_INMMASK(x)  ((x) & 0xff)
+
+#endif /* __DT_FSL_IMX_AUDMUX_H */
index deb0ae58b99bb3724bee4376b0a376b563c29cff..66a0e5384edd6090ae3793082c8a4f56fb6ea74d 100644 (file)
@@ -11,7 +11,7 @@ struct atmel_ssc_platform_data {
 
 struct ssc_device {
        struct list_head        list;
-       resource_size_t         phybase;
+       dma_addr_t              phybase;
        void __iomem            *regs;
        struct platform_device  *pdev;
        struct atmel_ssc_platform_data *pdata;
index b90337c9d468ead13e94f4c6ed110bdf75723755..4a12532da8c4ab9f7783394043e3be76c99db741 100644 (file)
@@ -336,6 +336,7 @@ extern int d_validate(struct dentry *, struct dentry *);
  * helper function for dentry_operations.d_dname() members
  */
 extern char *dynamic_dname(struct dentry *, char *, int, const char *, ...);
+extern char *simple_dname(struct dentry *, char *, int);
 
 extern char *__d_path(const struct path *, const struct path *, char *, int);
 extern char *d_absolute_path(const struct path *, char *, int);
index b99cd23f347489ea0a3c2a14d58193c6e1117331..79640e015a86c6eb4a8bab2c0d2ee78eaf241558 100644 (file)
@@ -5,45 +5,13 @@
 
 #include <linux/bitmap.h>
 #include <linux/if.h>
+#include <linux/ip.h>
 #include <linux/netdevice.h>
 #include <linux/rcupdate.h>
 #include <linux/timer.h>
 #include <linux/sysctl.h>
 #include <linux/rtnetlink.h>
 
-enum
-{
-       IPV4_DEVCONF_FORWARDING=1,
-       IPV4_DEVCONF_MC_FORWARDING,
-       IPV4_DEVCONF_PROXY_ARP,
-       IPV4_DEVCONF_ACCEPT_REDIRECTS,
-       IPV4_DEVCONF_SECURE_REDIRECTS,
-       IPV4_DEVCONF_SEND_REDIRECTS,
-       IPV4_DEVCONF_SHARED_MEDIA,
-       IPV4_DEVCONF_RP_FILTER,
-       IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE,
-       IPV4_DEVCONF_BOOTP_RELAY,
-       IPV4_DEVCONF_LOG_MARTIANS,
-       IPV4_DEVCONF_TAG,
-       IPV4_DEVCONF_ARPFILTER,
-       IPV4_DEVCONF_MEDIUM_ID,
-       IPV4_DEVCONF_NOXFRM,
-       IPV4_DEVCONF_NOPOLICY,
-       IPV4_DEVCONF_FORCE_IGMP_VERSION,
-       IPV4_DEVCONF_ARP_ANNOUNCE,
-       IPV4_DEVCONF_ARP_IGNORE,
-       IPV4_DEVCONF_PROMOTE_SECONDARIES,
-       IPV4_DEVCONF_ARP_ACCEPT,
-       IPV4_DEVCONF_ARP_NOTIFY,
-       IPV4_DEVCONF_ACCEPT_LOCAL,
-       IPV4_DEVCONF_SRC_VMARK,
-       IPV4_DEVCONF_PROXY_ARP_PVLAN,
-       IPV4_DEVCONF_ROUTE_LOCALNET,
-       __IPV4_DEVCONF_MAX
-};
-
-#define IPV4_DEVCONF_MAX (__IPV4_DEVCONF_MAX - 1)
-
 struct ipv4_devconf {
        void    *sysctl;
        int     data[IPV4_DEVCONF_MAX];
index 850e95bc766c8504d4fbc2c592c1327ef5994431..b8b7dc755752d8cfe6337ca92062b196cd19f073 100644 (file)
@@ -101,6 +101,7 @@ struct inet6_skb_parm {
 #define IP6SKB_FORWARDED       2
 #define IP6SKB_REROUTED                4
 #define IP6SKB_ROUTERALERT     8
+#define IP6SKB_FRAGMENTED      16
 };
 
 #define IP6CB(skb)     ((struct inet6_skb_parm*)((skb)->cb))
diff --git a/include/linux/mfd/arizona/gpio.h b/include/linux/mfd/arizona/gpio.h
new file mode 100644 (file)
index 0000000..d2146bb
--- /dev/null
@@ -0,0 +1,96 @@
+/*
+ * GPIO configuration for Arizona devices
+ *
+ * Copyright 2013 Wolfson Microelectronics. PLC.
+ *
+ * Author: Charles Keepax <ckeepax@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 _ARIZONA_GPIO_H
+#define _ARIZONA_GPIO_H
+
+#define ARIZONA_GP_FN_TXLRCLK                    0x00
+#define ARIZONA_GP_FN_GPIO                       0x01
+#define ARIZONA_GP_FN_IRQ1                       0x02
+#define ARIZONA_GP_FN_IRQ2                       0x03
+#define ARIZONA_GP_FN_OPCLK                      0x04
+#define ARIZONA_GP_FN_FLL1_OUT                   0x05
+#define ARIZONA_GP_FN_FLL2_OUT                   0x06
+#define ARIZONA_GP_FN_PWM1                       0x08
+#define ARIZONA_GP_FN_PWM2                       0x09
+#define ARIZONA_GP_FN_SYSCLK_UNDERCLOCKED        0x0A
+#define ARIZONA_GP_FN_ASYNCCLK_UNDERCLOCKED      0x0B
+#define ARIZONA_GP_FN_FLL1_LOCK                  0x0C
+#define ARIZONA_GP_FN_FLL2_LOCK                  0x0D
+#define ARIZONA_GP_FN_FLL1_CLOCK_OK              0x0F
+#define ARIZONA_GP_FN_FLL2_CLOCK_OK              0x10
+#define ARIZONA_GP_FN_HEADPHONE_DET              0x12
+#define ARIZONA_GP_FN_MIC_DET                    0x13
+#define ARIZONA_GP_FN_WSEQ_STATUS                0x15
+#define ARIZONA_GP_FN_CIF_ADDRESS_ERROR          0x16
+#define ARIZONA_GP_FN_ASRC1_LOCK                 0x1A
+#define ARIZONA_GP_FN_ASRC2_LOCK                 0x1B
+#define ARIZONA_GP_FN_ASRC_CONFIG_ERROR          0x1C
+#define ARIZONA_GP_FN_DRC1_SIGNAL_DETECT         0x1D
+#define ARIZONA_GP_FN_DRC1_ANTICLIP              0x1E
+#define ARIZONA_GP_FN_DRC1_DECAY                 0x1F
+#define ARIZONA_GP_FN_DRC1_NOISE                 0x20
+#define ARIZONA_GP_FN_DRC1_QUICK_RELEASE         0x21
+#define ARIZONA_GP_FN_DRC2_SIGNAL_DETECT         0x22
+#define ARIZONA_GP_FN_DRC2_ANTICLIP              0x23
+#define ARIZONA_GP_FN_DRC2_DECAY                 0x24
+#define ARIZONA_GP_FN_DRC2_NOISE                 0x25
+#define ARIZONA_GP_FN_DRC2_QUICK_RELEASE         0x26
+#define ARIZONA_GP_FN_MIXER_DROPPED_SAMPLE       0x27
+#define ARIZONA_GP_FN_AIF1_CONFIG_ERROR          0x28
+#define ARIZONA_GP_FN_AIF2_CONFIG_ERROR          0x29
+#define ARIZONA_GP_FN_AIF3_CONFIG_ERROR          0x2A
+#define ARIZONA_GP_FN_SPK_TEMP_SHUTDOWN          0x2B
+#define ARIZONA_GP_FN_SPK_TEMP_WARNING           0x2C
+#define ARIZONA_GP_FN_UNDERCLOCKED               0x2D
+#define ARIZONA_GP_FN_OVERCLOCKED                0x2E
+#define ARIZONA_GP_FN_DSP_IRQ1                   0x35
+#define ARIZONA_GP_FN_DSP_IRQ2                   0x36
+#define ARIZONA_GP_FN_ASYNC_OPCLK                0x3D
+#define ARIZONA_GP_FN_BOOT_DONE                  0x44
+#define ARIZONA_GP_FN_DSP1_RAM_READY             0x45
+#define ARIZONA_GP_FN_SYSCLK_ENA_STATUS          0x4B
+#define ARIZONA_GP_FN_ASYNCCLK_ENA_STATUS        0x4C
+
+#define ARIZONA_GPN_DIR                          0x8000  /* GPN_DIR */
+#define ARIZONA_GPN_DIR_MASK                     0x8000  /* GPN_DIR */
+#define ARIZONA_GPN_DIR_SHIFT                        15  /* GPN_DIR */
+#define ARIZONA_GPN_DIR_WIDTH                         1  /* GPN_DIR */
+#define ARIZONA_GPN_PU                           0x4000  /* GPN_PU */
+#define ARIZONA_GPN_PU_MASK                      0x4000  /* GPN_PU */
+#define ARIZONA_GPN_PU_SHIFT                         14  /* GPN_PU */
+#define ARIZONA_GPN_PU_WIDTH                          1  /* GPN_PU */
+#define ARIZONA_GPN_PD                           0x2000  /* GPN_PD */
+#define ARIZONA_GPN_PD_MASK                      0x2000  /* GPN_PD */
+#define ARIZONA_GPN_PD_SHIFT                         13  /* GPN_PD */
+#define ARIZONA_GPN_PD_WIDTH                          1  /* GPN_PD */
+#define ARIZONA_GPN_LVL                          0x0800  /* GPN_LVL */
+#define ARIZONA_GPN_LVL_MASK                     0x0800  /* GPN_LVL */
+#define ARIZONA_GPN_LVL_SHIFT                        11  /* GPN_LVL */
+#define ARIZONA_GPN_LVL_WIDTH                         1  /* GPN_LVL */
+#define ARIZONA_GPN_POL                          0x0400  /* GPN_POL */
+#define ARIZONA_GPN_POL_MASK                     0x0400  /* GPN_POL */
+#define ARIZONA_GPN_POL_SHIFT                        10  /* GPN_POL */
+#define ARIZONA_GPN_POL_WIDTH                         1  /* GPN_POL */
+#define ARIZONA_GPN_OP_CFG                       0x0200  /* GPN_OP_CFG */
+#define ARIZONA_GPN_OP_CFG_MASK                  0x0200  /* GPN_OP_CFG */
+#define ARIZONA_GPN_OP_CFG_SHIFT                      9  /* GPN_OP_CFG */
+#define ARIZONA_GPN_OP_CFG_WIDTH                      1  /* GPN_OP_CFG */
+#define ARIZONA_GPN_DB                           0x0100  /* GPN_DB */
+#define ARIZONA_GPN_DB_MASK                      0x0100  /* GPN_DB */
+#define ARIZONA_GPN_DB_SHIFT                          8  /* GPN_DB */
+#define ARIZONA_GPN_DB_WIDTH                          1  /* GPN_DB */
+#define ARIZONA_GPN_FN_MASK                      0x007F  /* GPN_DB */
+#define ARIZONA_GPN_FN_SHIFT                          0  /* GPN_DB */
+#define ARIZONA_GPN_FN_WIDTH                          7  /* GPN_DB */
+
+#endif
index fb425aa16c0149fdf35b9fe6a1be3a56577d3ff7..faf4b7c1ad12702db919bd03a448741b7e579789 100644 (file)
@@ -332,6 +332,7 @@ struct mm_struct {
                                unsigned long pgoff, unsigned long flags);
 #endif
        unsigned long mmap_base;                /* base of mmap area */
+       unsigned long mmap_legacy_base;         /* base of mmap area in bottom-up allocations */
        unsigned long task_size;                /* size of task vm space */
        unsigned long highest_vm_end;           /* highest vma end address */
        pgd_t * pgd;
index 88272591a8951d09fb18d8b7967ed3e885ca8855..9efc04dd255aa59799c69e4bba791551825023e8 100644 (file)
@@ -36,6 +36,7 @@ struct samsung_i2s {
  */
 #define QUIRK_NO_MUXPSR                (1 << 2)
 #define QUIRK_NEED_RSTCLR      (1 << 3)
+#define QUIRK_SUPPORTS_TDM     (1 << 4)
        /* Quirks of the I2S controller */
        u32 quirks;
        dma_addr_t idma_addr;
diff --git a/include/linux/platform_data/omap-abe-twl6040.h b/include/linux/platform_data/omap-abe-twl6040.h
deleted file mode 100644 (file)
index 5d298ac..0000000
+++ /dev/null
@@ -1,49 +0,0 @@
-/**
- * omap-abe-twl6040.h - ASoC machine driver OMAP4+ devices, header.
- *
- * Copyright (C) 2011 Texas Instruments Incorporated - http://www.ti.com
- * All rights reserved.
- *
- * Author: Peter Ujfalusi <peter.ujfalusi@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
- */
-
-#ifndef _OMAP_ABE_TWL6040_H_
-#define _OMAP_ABE_TWL6040_H_
-
-/* To select if only one channel is connected in a stereo port */
-#define ABE_TWL6040_LEFT       (1 << 0)
-#define ABE_TWL6040_RIGHT      (1 << 1)
-
-struct omap_abe_twl6040_data {
-       char *card_name;
-       /* Feature flags for connected audio pins */
-       u8      has_hs;
-       u8      has_hf;
-       bool    has_ep;
-       u8      has_aux;
-       u8      has_vibra;
-       bool    has_dmic;
-       bool    has_hsmic;
-       bool    has_mainmic;
-       bool    has_submic;
-       u8      has_afm;
-       /* Other features */
-       bool    jack_detection; /* board can detect jack events */
-       int     mclk_freq;      /* MCLK frequency speed for twl6040 */
-};
-
-#endif /* _OMAP_ABE_TWL6040_H_ */
index 467cc6307b621adf71fd94e0dc6f1d3aae409741..49444203328ad8546b4299fb702d0155897bc0d0 100644 (file)
@@ -21,6 +21,8 @@
 
 #include <linux/list.h>
 #include <linux/io.h>
+#include <linux/of.h>
+
 
 /*
  * SSP Serial Port Registers
@@ -190,6 +192,8 @@ struct ssp_device {
        int             irq;
        int             drcmr_rx;
        int             drcmr_tx;
+
+       struct device_node      *of_node;
 };
 
 /**
@@ -218,11 +222,18 @@ static inline u32 pxa_ssp_read_reg(struct ssp_device *dev, u32 reg)
 #ifdef CONFIG_ARCH_PXA
 struct ssp_device *pxa_ssp_request(int port, const char *label);
 void pxa_ssp_free(struct ssp_device *);
+struct ssp_device *pxa_ssp_request_of(const struct device_node *of_node,
+                                     const char *label);
 #else
 static inline struct ssp_device *pxa_ssp_request(int port, const char *label)
 {
        return NULL;
 }
+static inline struct ssp_device *pxa_ssp_request_of(const struct device_node *n,
+                                                   const char *name)
+{
+       return NULL;
+}
 static inline void pxa_ssp_free(struct ssp_device *ssp) {}
 #endif
 
index e9995eb5985cd50b28aaae49f528487acf3f018c..078066daffd486d426027e9c97c3a40b8202e950 100644 (file)
@@ -314,7 +314,6 @@ struct nsproxy;
 struct user_namespace;
 
 #ifdef CONFIG_MMU
-extern unsigned long mmap_legacy_base(void);
 extern void arch_pick_mmap_layout(struct mm_struct *mm);
 extern unsigned long
 arch_get_unmapped_area(struct file *, unsigned long, unsigned long,
index f487a4750b7f36e97acef1542cb6076d76ce7a59..a67fc1635592fd34717ef559049924b1add707ca 100644 (file)
@@ -811,6 +811,63 @@ do {                                                                       \
        __ret;                                                          \
 })
 
+#define __wait_event_interruptible_lock_irq_timeout(wq, condition,     \
+                                                   lock, ret)          \
+do {                                                                   \
+       DEFINE_WAIT(__wait);                                            \
+                                                                       \
+       for (;;) {                                                      \
+               prepare_to_wait(&wq, &__wait, TASK_INTERRUPTIBLE);      \
+               if (condition)                                          \
+                       break;                                          \
+               if (signal_pending(current)) {                          \
+                       ret = -ERESTARTSYS;                             \
+                       break;                                          \
+               }                                                       \
+               spin_unlock_irq(&lock);                                 \
+               ret = schedule_timeout(ret);                            \
+               spin_lock_irq(&lock);                                   \
+               if (!ret)                                               \
+                       break;                                          \
+       }                                                               \
+       finish_wait(&wq, &__wait);                                      \
+} while (0)
+
+/**
+ * wait_event_interruptible_lock_irq_timeout - sleep until a condition gets true or a timeout elapses.
+ *             The condition is checked under the lock. This is expected
+ *             to be called with the lock taken.
+ * @wq: the waitqueue to wait on
+ * @condition: a C expression for the event to wait for
+ * @lock: a locked spinlock_t, which will be released before schedule()
+ *       and reacquired afterwards.
+ * @timeout: timeout, in jiffies
+ *
+ * The process is put to sleep (TASK_INTERRUPTIBLE) until the
+ * @condition evaluates to true or signal is received. The @condition is
+ * checked each time the waitqueue @wq is woken up.
+ *
+ * wake_up() has to be called after changing any variable that could
+ * change the result of the wait condition.
+ *
+ * This is supposed to be called while holding the lock. The lock is
+ * dropped before going to sleep and is reacquired afterwards.
+ *
+ * The function returns 0 if the @timeout elapsed, -ERESTARTSYS if it
+ * was interrupted by a signal, and the remaining jiffies otherwise
+ * if the condition evaluated to true before the timeout elapsed.
+ */
+#define wait_event_interruptible_lock_irq_timeout(wq, condition, lock, \
+                                                 timeout)              \
+({                                                                     \
+       int __ret = timeout;                                            \
+                                                                       \
+       if (!(condition))                                               \
+               __wait_event_interruptible_lock_irq_timeout(            \
+                                       wq, condition, lock, __ret);    \
+       __ret;                                                          \
+})
+
 
 /*
  * These are the old interfaces to sleep waiting for an event.
index 260f83f16bcfb3e79f2572604fc373c1830e2310..f667248202b6be9aacd3dc51f93eb43c9936a217 100644 (file)
@@ -135,6 +135,8 @@ extern void ip6_update_pmtu(struct sk_buff *skb, struct net *net, __be32 mtu,
 extern void ip6_sk_update_pmtu(struct sk_buff *skb, struct sock *sk,
                               __be32 mtu);
 extern void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark);
+extern void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
+                                  u32 mark);
 extern void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk);
 
 struct netlink_callback;
index c586617cfa0dffaa2cdce5e548f78fed28e1f63e..2a14f1f02d4f6766e236b288f6173ee7391e78a2 100644 (file)
@@ -27,6 +27,7 @@
 #include <linux/rwsem.h>               /* struct rw_semaphore */
 #include <linux/pm.h>                  /* pm_message_t */
 #include <linux/stringify.h>
+#include <linux/printk.h>
 
 /* number of supported soundcards */
 #ifdef CONFIG_SND_DYNAMIC_MINORS
@@ -375,6 +376,11 @@ void __snd_printk(unsigned int level, const char *file, int line,
  */
 #define snd_BUG()              WARN(1, "BUG?\n")
 
+/**
+ * Suppress high rates of output when CONFIG_SND_DEBUG is enabled.
+ */
+#define snd_printd_ratelimit() printk_ratelimit()
+
 /**
  * snd_BUG_ON - debugging check macro
  * @cond: condition to evaluate
@@ -398,6 +404,8 @@ static inline void _snd_printd(int level, const char *format, ...) {}
        unlikely(__ret_warn_on); \
 })
 
+static inline bool snd_printd_ratelimit(void) { return false; }
+
 #endif /* CONFIG_SND_DEBUG */
 
 #ifdef CONFIG_SND_DEBUG_VERBOSE
index 2fd3d251d9a54e4896453508fcd3bcd86ebfbf26..56e818e4a1cbf90270d21c3a67e44ff3a0f32d73 100644 (file)
@@ -6,13 +6,6 @@
 
 /* PCM */
 
-struct pxa2xx_pcm_dma_params {
-       char *name;                     /* stream identifier */
-       u32 dcmd;                       /* DMA descriptor dcmd field */
-       volatile u32 *drcmr;            /* the DMA request channel to use */
-       u32 dev_addr;                   /* device physical address for DMA */
-};
-
 extern int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params);
 extern int __pxa2xx_pcm_hw_free(struct snd_pcm_substream *substream);
diff --git a/include/sound/rcar_snd.h b/include/sound/rcar_snd.h
new file mode 100644 (file)
index 0000000..d35412a
--- /dev/null
@@ -0,0 +1,84 @@
+/*
+ * 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_SRU  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_CLK_FROM_ADG          (1 << 30) /* clock parent is master */
+#define RSND_SSI_SYNC                  (1 << 29) /* SSI34_sync etc */
+#define RSND_SSI_DEPENDENT             (1 << 28) /* SSI needs SRU/SCU */
+
+#define RSND_SSI_PLAY                  (1 << 24)
+
+#define RSND_SSI_SET(_dai_id, _dma_id, _pio_irq, _flags)       \
+{ .dai_id = _dai_id, .dma_id = _dma_id, .pio_irq = _pio_irq, .flags = _flags }
+#define RSND_SSI_UNUSED \
+{ .dai_id = -1, .dma_id = -1, .pio_irq = -1, .flags = 0 }
+
+struct rsnd_ssi_platform_info {
+       int dai_id;
+       int dma_id;
+       int pio_irq;
+       u32 flags;
+};
+
+/*
+ * flags
+ */
+#define RSND_SCU_USB_HPBIF             (1 << 31) /* it needs RSND_SSI_DEPENDENT */
+
+struct rsnd_scu_platform_info {
+       u32 flags;
+};
+
+/*
+ * flags
+ *
+ * 0x0000000A
+ *
+ * A : generation
+ */
+#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_scu_platform_info *scu_info;
+       int scu_info_nr;
+       int (*start)(int id);
+       int (*stop)(int id);
+};
+
+#endif
index 3e479f4e15f5e3469ef0c7f72f6e88174f36ceba..27a72d5d4b00ff36133fb19a2a1580044ac27d11 100644 (file)
@@ -70,121 +70,144 @@ struct device;
        .num_kcontrols = 0, .reg = SND_SOC_NOPM, .event = wevent, \
        .event_flags = SND_SOC_DAPM_POST_PMU | SND_SOC_DAPM_PRE_PMD}
 
+#define SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) \
+       .reg = wreg, .mask = 1, .shift = wshift, \
+       .on_val = winvert ? 0 : 1, .off_val = winvert ? 1 : 0
+
 /* path domain */
 #define SND_SOC_DAPM_PGA(wname, wreg, wshift, winvert,\
         wcontrols, wncontrols) \
-{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
+{      .id = snd_soc_dapm_pga, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
 #define SND_SOC_DAPM_OUT_DRV(wname, wreg, wshift, winvert,\
         wcontrols, wncontrols) \
-{      .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
+{      .id = snd_soc_dapm_out_drv, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
 #define SND_SOC_DAPM_MIXER(wname, wreg, wshift, winvert, \
         wcontrols, wncontrols)\
-{      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
+{      .id = snd_soc_dapm_mixer, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
 #define SND_SOC_DAPM_MIXER_NAMED_CTL(wname, wreg, wshift, winvert, \
         wcontrols, wncontrols)\
-{       .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
-       .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
-       .num_kcontrols = wncontrols}
+{       .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = wncontrols}
 #define SND_SOC_DAPM_MICBIAS(wname, wreg, wshift, winvert) \
-{      .id = snd_soc_dapm_micbias, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = NULL, .num_kcontrols = 0}
+{      .id = snd_soc_dapm_micbias, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = NULL, .num_kcontrols = 0}
 #define SND_SOC_DAPM_SWITCH(wname, wreg, wshift, winvert, wcontrols) \
-{      .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
+{      .id = snd_soc_dapm_switch, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = 1}
 #define SND_SOC_DAPM_MUX(wname, wreg, wshift, winvert, wcontrols) \
-{      .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
+{      .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, \
+       .kcontrol_news = wcontrols, .num_kcontrols = 1}
 #define SND_SOC_DAPM_VIRT_MUX(wname, wreg, wshift, winvert, wcontrols) \
-{      .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1}
+{      .id = snd_soc_dapm_virt_mux, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = 1}
 #define SND_SOC_DAPM_VALUE_MUX(wname, wreg, wshift, winvert, wcontrols) \
-{      .id = snd_soc_dapm_value_mux, .name = wname, .reg = wreg, \
-       .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
-       .num_kcontrols = 1}
+{      .id = snd_soc_dapm_value_mux, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = 1}
 
 /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
 #define SOC_PGA_ARRAY(wname, wreg, wshift, winvert,\
         wcontrols) \
-{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
+{      .id = snd_soc_dapm_pga, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
 #define SOC_MIXER_ARRAY(wname, wreg, wshift, winvert, \
         wcontrols)\
-{      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
+{      .id = snd_soc_dapm_mixer, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
 #define SOC_MIXER_NAMED_CTL_ARRAY(wname, wreg, wshift, winvert, \
         wcontrols)\
-{       .id = snd_soc_dapm_mixer_named_ctl, .name = wname, .reg = wreg, \
-       .shift = wshift, .invert = winvert, .kcontrol_news = wcontrols, \
-       .num_kcontrols = ARRAY_SIZE(wcontrols)}
+{       .id = snd_soc_dapm_mixer_named_ctl, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols)}
 
 /* path domain with event - event handler must return 0 for success */
 #define SND_SOC_DAPM_PGA_E(wname, wreg, wshift, winvert, wcontrols, \
        wncontrols, wevent, wflags) \
-{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
+{      .id = snd_soc_dapm_pga, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
        .event = wevent, .event_flags = wflags}
 #define SND_SOC_DAPM_OUT_DRV_E(wname, wreg, wshift, winvert, wcontrols, \
        wncontrols, wevent, wflags) \
-{      .id = snd_soc_dapm_out_drv, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
+{      .id = snd_soc_dapm_out_drv, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
        .event = wevent, .event_flags = wflags}
 #define SND_SOC_DAPM_MIXER_E(wname, wreg, wshift, winvert, wcontrols, \
        wncontrols, wevent, wflags) \
-{      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
+{      .id = snd_soc_dapm_mixer, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = wncontrols, \
        .event = wevent, .event_flags = wflags}
 #define SND_SOC_DAPM_MIXER_NAMED_CTL_E(wname, wreg, wshift, winvert, \
        wcontrols, wncontrols, wevent, wflags) \
-{       .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, \
+{       .id = snd_soc_dapm_mixer, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, \
        .num_kcontrols = wncontrols, .event = wevent, .event_flags = wflags}
 #define SND_SOC_DAPM_SWITCH_E(wname, wreg, wshift, winvert, wcontrols, \
        wevent, wflags) \
-{      .id = snd_soc_dapm_switch, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
+{      .id = snd_soc_dapm_switch, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = 1, \
        .event = wevent, .event_flags = wflags}
 #define SND_SOC_DAPM_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
        wevent, wflags) \
-{      .id = snd_soc_dapm_mux, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
+{      .id = snd_soc_dapm_mux, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = 1, \
        .event = wevent, .event_flags = wflags}
 #define SND_SOC_DAPM_VIRT_MUX_E(wname, wreg, wshift, winvert, wcontrols, \
        wevent, wflags) \
-{      .id = snd_soc_dapm_virt_mux, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = 1, \
+{      .id = snd_soc_dapm_virt_mux, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = 1, \
        .event = wevent, .event_flags = wflags}
 
 /* additional sequencing control within an event type */
 #define SND_SOC_DAPM_PGA_S(wname, wsubseq, wreg, wshift, winvert, \
        wevent, wflags) \
-{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .event = wevent, .event_flags = wflags, \
+{      .id = snd_soc_dapm_pga, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .event = wevent, .event_flags = wflags, \
        .subseq = wsubseq}
 #define SND_SOC_DAPM_SUPPLY_S(wname, wsubseq, wreg, wshift, winvert, wevent, \
        wflags) \
-{      .id = snd_soc_dapm_supply, .name = wname, .reg = wreg,  \
-       .shift = wshift, .invert = winvert, .event = wevent, \
-       .event_flags = wflags, .subseq = wsubseq}
+{      .id = snd_soc_dapm_supply, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .event = wevent, .event_flags = wflags, .subseq = wsubseq}
 
 /* Simplified versions of above macros, assuming wncontrols = ARRAY_SIZE(wcontrols) */
 #define SOC_PGA_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
        wevent, wflags) \
-{      .id = snd_soc_dapm_pga, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
+{      .id = snd_soc_dapm_pga, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
        .event = wevent, .event_flags = wflags}
 #define SOC_MIXER_E_ARRAY(wname, wreg, wshift, winvert, wcontrols, \
        wevent, wflags) \
-{      .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
+{      .id = snd_soc_dapm_mixer, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
        .event = wevent, .event_flags = wflags}
 #define SOC_MIXER_NAMED_CTL_E_ARRAY(wname, wreg, wshift, winvert, \
        wcontrols, wevent, wflags) \
-{       .id = snd_soc_dapm_mixer, .name = wname, .reg = wreg, .shift = wshift, \
-       .invert = winvert, .kcontrol_news = wcontrols, \
-       .num_kcontrols = ARRAY_SIZE(wcontrols), .event = wevent, .event_flags = wflags}
+{       .id = snd_soc_dapm_mixer, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .kcontrol_news = wcontrols, .num_kcontrols = ARRAY_SIZE(wcontrols), \
+       .event = wevent, .event_flags = wflags}
 
 /* events that are pre and post DAPM */
 #define SND_SOC_DAPM_PRE(wname, wevent) \
@@ -199,35 +222,36 @@ struct device;
 /* stream domain */
 #define SND_SOC_DAPM_AIF_IN(wname, stname, wslot, wreg, wshift, winvert) \
 {      .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
-       .reg = wreg, .shift = wshift, .invert = winvert }
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), }
 #define SND_SOC_DAPM_AIF_IN_E(wname, stname, wslot, wreg, wshift, winvert, \
                              wevent, wflags)                           \
 {      .id = snd_soc_dapm_aif_in, .name = wname, .sname = stname, \
-       .reg = wreg, .shift = wshift, .invert = winvert, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
        .event = wevent, .event_flags = wflags }
 #define SND_SOC_DAPM_AIF_OUT(wname, stname, wslot, wreg, wshift, winvert) \
 {      .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
-       .reg = wreg, .shift = wshift, .invert = winvert }
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), }
 #define SND_SOC_DAPM_AIF_OUT_E(wname, stname, wslot, wreg, wshift, winvert, \
                             wevent, wflags)                            \
 {      .id = snd_soc_dapm_aif_out, .name = wname, .sname = stname, \
-       .reg = wreg, .shift = wshift, .invert = winvert, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
        .event = wevent, .event_flags = wflags }
 #define SND_SOC_DAPM_DAC(wname, stname, wreg, wshift, winvert) \
-{      .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
-       .shift = wshift, .invert = winvert}
+{      .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert) }
 #define SND_SOC_DAPM_DAC_E(wname, stname, wreg, wshift, winvert, \
                           wevent, wflags)                              \
-{      .id = snd_soc_dapm_dac, .name = wname, .sname = stname, .reg = wreg, \
-       .shift = wshift, .invert = winvert, \
+{      .id = snd_soc_dapm_dac, .name = wname, .sname = stname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
        .event = wevent, .event_flags = wflags}
+
 #define SND_SOC_DAPM_ADC(wname, stname, wreg, wshift, winvert) \
-{      .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
-       .shift = wshift, .invert = winvert}
+{      .id = snd_soc_dapm_adc, .name = wname, .sname = stname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), }
 #define SND_SOC_DAPM_ADC_E(wname, stname, wreg, wshift, winvert, \
                           wevent, wflags)                              \
-{      .id = snd_soc_dapm_adc, .name = wname, .sname = stname, .reg = wreg, \
-       .shift = wshift, .invert = winvert, \
+{      .id = snd_soc_dapm_adc, .name = wname, .sname = stname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
        .event = wevent, .event_flags = wflags}
 #define SND_SOC_DAPM_CLOCK_SUPPLY(wname) \
 {      .id = snd_soc_dapm_clock_supply, .name = wname, \
@@ -241,14 +265,14 @@ struct device;
        .on_val = won_val, .off_val = woff_val, .event = dapm_reg_event, \
        .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD}
 #define SND_SOC_DAPM_SUPPLY(wname, wreg, wshift, winvert, wevent, wflags) \
-{      .id = snd_soc_dapm_supply, .name = wname, .reg = wreg,  \
-       .shift = wshift, .invert = winvert, .event = wevent, \
-       .event_flags = wflags}
+{      .id = snd_soc_dapm_supply, .name = wname, \
+       SND_SOC_DAPM_INIT_REG_VAL(wreg, wshift, winvert), \
+       .event = wevent, .event_flags = wflags}
 #define SND_SOC_DAPM_REGULATOR_SUPPLY(wname, wdelay, wflags)       \
 {      .id = snd_soc_dapm_regulator_supply, .name = wname, \
        .reg = SND_SOC_NOPM, .shift = wdelay, .event = dapm_regulator_event, \
        .event_flags = SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD, \
-       .invert = wflags}
+       .on_val = wflags}
 
 
 /* dapm kcontrol types */
@@ -256,14 +280,26 @@ struct device;
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_volsw, \
        .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
-       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+       .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }
+#define SOC_DAPM_SINGLE_AUTODISABLE(xname, reg, shift, max, invert) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, \
+       .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+       .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 1) }
 #define SOC_DAPM_SINGLE_TLV(xname, reg, shift, max, invert, tlv_array) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_volsw, \
        .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\
        .tlv.p = (tlv_array), \
        .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
-       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+       .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }
+#define SOC_DAPM_SINGLE_TLV_AUTODISABLE(xname, reg, shift, max, invert, tlv_array) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
+       .info = snd_soc_info_volsw, \
+       .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | SNDRV_CTL_ELEM_ACCESS_READWRITE,\
+       .tlv.p = (tlv_array), \
+       .get = snd_soc_dapm_get_volsw, .put = snd_soc_dapm_put_volsw, \
+       .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }
 #define SOC_DAPM_ENUM(xname, xenum) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_enum_double, \
@@ -333,6 +369,7 @@ struct snd_soc_dapm_route;
 struct snd_soc_dapm_context;
 struct regulator;
 struct snd_soc_dapm_widget_list;
+struct snd_soc_dapm_update;
 
 int dapm_reg_event(struct snd_soc_dapm_widget *w,
                   struct snd_kcontrol *kcontrol, int event);
@@ -376,7 +413,7 @@ int snd_soc_dapm_new_pcm(struct snd_soc_card *card,
                         struct snd_soc_dapm_widget *sink);
 
 /* dapm path setup */
-int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm);
+int snd_soc_dapm_new_widgets(struct snd_soc_card *card);
 void snd_soc_dapm_free(struct snd_soc_dapm_context *dapm);
 int snd_soc_dapm_add_routes(struct snd_soc_dapm_context *dapm,
                            const struct snd_soc_dapm_route *route, int num);
@@ -391,10 +428,12 @@ void snd_soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
 void snd_soc_dapm_shutdown(struct snd_soc_card *card);
 
 /* external DAPM widget events */
-int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
-               struct snd_kcontrol *kcontrol, int connect);
-int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
-                                struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e);
+int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
+               struct snd_kcontrol *kcontrol, int connect,
+               struct snd_soc_dapm_update *update);
+int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm,
+               struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e,
+               struct snd_soc_dapm_update *update);
 
 /* dapm sys fs - used by the core */
 int snd_soc_dapm_sys_add(struct device *dev);
@@ -424,6 +463,8 @@ void dapm_mark_io_dirty(struct snd_soc_dapm_context *dapm);
 int snd_soc_dapm_dai_get_connected_widgets(struct snd_soc_dai *dai, int stream,
        struct snd_soc_dapm_widget_list **list);
 
+struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol);
+
 /* dapm widget types */
 enum snd_soc_dapm_type {
        snd_soc_dapm_input = 0,         /* input pin */
@@ -455,6 +496,7 @@ enum snd_soc_dapm_type {
        snd_soc_dapm_dai_in,            /* link to DAI structure */
        snd_soc_dapm_dai_out,
        snd_soc_dapm_dai_link,          /* link between two DAI structures */
+       snd_soc_dapm_kcontrol,          /* Auto-disabled kcontrol */
 };
 
 enum snd_soc_dapm_subclass {
@@ -485,7 +527,6 @@ struct snd_soc_dapm_path {
        /* source (input) and sink (output) widgets */
        struct snd_soc_dapm_widget *source;
        struct snd_soc_dapm_widget *sink;
-       struct snd_kcontrol *kcontrol;
 
        /* status */
        u32 connect:1;  /* source and sink widgets are connected */
@@ -498,6 +539,7 @@ struct snd_soc_dapm_path {
 
        struct list_head list_source;
        struct list_head list_sink;
+       struct list_head list_kcontrol;
        struct list_head list;
 };
 
@@ -518,12 +560,10 @@ struct snd_soc_dapm_widget {
        /* dapm control */
        int reg;                                /* negative reg = no direct dapm */
        unsigned char shift;                    /* bits to shift */
-       unsigned int value;                             /* widget current value */
        unsigned int mask;                      /* non-shifted mask */
        unsigned int on_val;                    /* on state value */
        unsigned int off_val;                   /* off state value */
        unsigned char power:1;                  /* block power status */
-       unsigned char invert:1;                 /* invert the power bit */
        unsigned char active:1;                 /* active stream on DAC, ADC's */
        unsigned char connected:1;              /* connected codec pin */
        unsigned char new:1;                    /* cnew complete */
@@ -559,7 +599,6 @@ struct snd_soc_dapm_widget {
 };
 
 struct snd_soc_dapm_update {
-       struct snd_soc_dapm_widget *widget;
        struct snd_kcontrol *kcontrol;
        int reg;
        int mask;
@@ -573,8 +612,6 @@ struct snd_soc_dapm_context {
        struct delayed_work delayed_work;
        unsigned int idle_bias_off:1; /* Use BIAS_OFF instead of STANDBY */
 
-       struct snd_soc_dapm_update *update;
-
        void (*seq_notifier)(struct snd_soc_dapm_context *,
                             enum snd_soc_dapm_type, int);
 
index 04598f1efd771f9f05e6f051edf975bd510700ce..047d657c331ce77b6c54f51ca38f18cfdc24a1d1 100644 (file)
@@ -133,6 +133,6 @@ void snd_soc_dpcm_be_set_state(struct snd_soc_pcm_runtime *be, int stream,
 /* internal use only */
 int soc_dpcm_be_digital_mute(struct snd_soc_pcm_runtime *fe, int mute);
 int soc_dpcm_debugfs_add(struct snd_soc_pcm_runtime *rtd);
-int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *);
+int soc_dpcm_runtime_update(struct snd_soc_card *);
 
 #endif
index 6eabee7ec15a90197ff3024f827d47cfaad91095..8e2ad52078b613ec853c2a6637f8a118674e8814 100644 (file)
 /*
  * Convenience kcontrol builders
  */
-#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert) \
+#define SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, xmax, xinvert, xautodisable) \
        ((unsigned long)&(struct soc_mixer_control) \
        {.reg = xreg, .rreg = xreg, .shift = shift_left, \
        .rshift = shift_right, .max = xmax, .platform_max = xmax, \
-       .invert = xinvert})
-#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) \
-       SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert)
+       .invert = xinvert, .autodisable = xautodisable})
+#define SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, xautodisable) \
+       SOC_DOUBLE_VALUE(xreg, xshift, xshift, xmax, xinvert, xautodisable)
 #define SOC_SINGLE_VALUE_EXT(xreg, xmax, xinvert) \
        ((unsigned long)&(struct soc_mixer_control) \
        {.reg = xreg, .max = xmax, .platform_max = xmax, .invert = xinvert})
@@ -52,7 +52,7 @@
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
        .put = snd_soc_put_volsw, \
-       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+       .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }
 #define SOC_SINGLE_RANGE(xname, xreg, xshift, xmin, xmax, xinvert) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
        .info = snd_soc_info_volsw_range, .get = snd_soc_get_volsw_range, \
@@ -68,7 +68,7 @@
        .tlv.p = (tlv_array), \
        .info = snd_soc_info_volsw, .get = snd_soc_get_volsw,\
        .put = snd_soc_put_volsw, \
-       .private_value =  SOC_SINGLE_VALUE(reg, shift, max, invert) }
+       .private_value = SOC_SINGLE_VALUE(reg, shift, max, invert, 0) }
 #define SOC_SINGLE_SX_TLV(xname, xreg, xshift, xmin, xmax, tlv_array) \
 {       .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ | \
@@ -97,7 +97,7 @@
        .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
        .put = snd_soc_put_volsw, \
        .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \
-                                         max, invert) }
+                                         max, invert, 0) }
 #define SOC_DOUBLE_R(xname, reg_left, reg_right, xshift, xmax, xinvert) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
        .info = snd_soc_info_volsw, \
        .info = snd_soc_info_volsw, .get = snd_soc_get_volsw, \
        .put = snd_soc_put_volsw, \
        .private_value = SOC_DOUBLE_VALUE(reg, shift_left, shift_right, \
-                                         max, invert) }
+                                         max, invert, 0) }
 #define SOC_DOUBLE_R_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert, tlv_array) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname),\
        .access = SNDRV_CTL_ELEM_ACCESS_TLV_READ |\
 {      .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) }
+       .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) }
 #define SOC_DOUBLE_EXT(xname, reg, shift_left, shift_right, max, invert,\
         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_VALUE(reg, shift_left, shift_right, max, invert) }
+               SOC_DOUBLE_VALUE(reg, shift_left, shift_right, max, invert, 0) }
 #define SOC_SINGLE_EXT_TLV(xname, xreg, xshift, xmax, xinvert,\
         xhandler_get, xhandler_put, tlv_array) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
        .tlv.p = (tlv_array), \
        .info = snd_soc_info_volsw, \
        .get = xhandler_get, .put = xhandler_put, \
-       .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert) }
+       .private_value = SOC_SINGLE_VALUE(xreg, xshift, xmax, xinvert, 0) }
 #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), \
        .info = snd_soc_info_volsw, \
        .get = xhandler_get, .put = xhandler_put, \
        .private_value = SOC_DOUBLE_VALUE(xreg, shift_left, shift_right, \
-                                         xmax, xinvert) }
+                                         xmax, xinvert, 0) }
 #define SOC_DOUBLE_R_EXT_TLV(xname, reg_left, reg_right, xshift, xmax, xinvert,\
         xhandler_get, xhandler_put, tlv_array) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = (xname), \
        .private_value = xdata }
 #define SOC_ENUM_EXT(xname, xenum, xhandler_get, xhandler_put) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, .name = xname, \
-       .info = snd_soc_info_enum_ext, \
+       .info = snd_soc_info_enum_double, \
        .get = xhandler_get, .put = xhandler_put, \
        .private_value = (unsigned long)&xenum }
 
@@ -468,6 +468,8 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
 void snd_soc_free_ac97_codec(struct snd_soc_codec *codec);
 
 int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops);
+int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
+               struct platform_device *pdev);
 
 /*
  *Controls
@@ -475,6 +477,8 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops);
 struct snd_kcontrol *snd_soc_cnew(const struct snd_kcontrol_new *_template,
                                  void *data, const char *long_name,
                                  const char *prefix);
+struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
+                                              const char *name);
 int snd_soc_add_codec_controls(struct snd_soc_codec *codec,
        const struct snd_kcontrol_new *controls, int num_controls);
 int snd_soc_add_platform_controls(struct snd_soc_platform *platform,
@@ -485,8 +489,6 @@ int snd_soc_add_dai_controls(struct snd_soc_dai *dai,
        const struct snd_kcontrol_new *controls, int num_controls);
 int snd_soc_info_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_info *uinfo);
-int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo);
 int snd_soc_get_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 int snd_soc_put_enum_double(struct snd_kcontrol *kcontrol,
@@ -497,8 +499,6 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
 int snd_soc_info_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_info *uinfo);
-int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo);
 #define snd_soc_info_bool_ext          snd_ctl_boolean_mono_info
 int snd_soc_get_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol);
@@ -1042,6 +1042,7 @@ struct snd_soc_card {
        /* Generic DAPM context for the card */
        struct snd_soc_dapm_context dapm;
        struct snd_soc_dapm_stats dapm_stats;
+       struct snd_soc_dapm_update *update;
 
 #ifdef CONFIG_DEBUG_FS
        struct dentry *debugfs_card_root;
@@ -1087,7 +1088,9 @@ struct snd_soc_pcm_runtime {
 /* mixer control */
 struct soc_mixer_control {
        int min, max, platform_max;
-       unsigned int reg, rreg, shift, rshift, invert;
+       unsigned int reg, rreg, shift, rshift;
+       unsigned int invert:1;
+       unsigned int autodisable:1;
 };
 
 struct soc_bytes {
index 6cf06bfd841bc5e7c77df6cd32456309ff460d46..2fee45bdec0ab5dbaf50503c00851cf798363c7c 100644 (file)
@@ -133,4 +133,38 @@ struct ip_beet_phdr {
        __u8 reserved;
 };
 
+/* index values for the variables in ipv4_devconf */
+enum
+{
+       IPV4_DEVCONF_FORWARDING=1,
+       IPV4_DEVCONF_MC_FORWARDING,
+       IPV4_DEVCONF_PROXY_ARP,
+       IPV4_DEVCONF_ACCEPT_REDIRECTS,
+       IPV4_DEVCONF_SECURE_REDIRECTS,
+       IPV4_DEVCONF_SEND_REDIRECTS,
+       IPV4_DEVCONF_SHARED_MEDIA,
+       IPV4_DEVCONF_RP_FILTER,
+       IPV4_DEVCONF_ACCEPT_SOURCE_ROUTE,
+       IPV4_DEVCONF_BOOTP_RELAY,
+       IPV4_DEVCONF_LOG_MARTIANS,
+       IPV4_DEVCONF_TAG,
+       IPV4_DEVCONF_ARPFILTER,
+       IPV4_DEVCONF_MEDIUM_ID,
+       IPV4_DEVCONF_NOXFRM,
+       IPV4_DEVCONF_NOPOLICY,
+       IPV4_DEVCONF_FORCE_IGMP_VERSION,
+       IPV4_DEVCONF_ARP_ANNOUNCE,
+       IPV4_DEVCONF_ARP_IGNORE,
+       IPV4_DEVCONF_PROMOTE_SECONDARIES,
+       IPV4_DEVCONF_ARP_ACCEPT,
+       IPV4_DEVCONF_ARP_NOTIFY,
+       IPV4_DEVCONF_ACCEPT_LOCAL,
+       IPV4_DEVCONF_SRC_VMARK,
+       IPV4_DEVCONF_PROXY_ARP_PVLAN,
+       IPV4_DEVCONF_ROUTE_LOCALNET,
+       __IPV4_DEVCONF_MAX
+};
+
+#define IPV4_DEVCONF_MAX (__IPV4_DEVCONF_MAX - 1)
+
 #endif /* _UAPI_LINUX_IP_H */
index 1f59ea2a4a76b30214a907b2b7ac3199edb029ab..d956c3593f652e01a7df0ffa72ce7ba0642444ed 100644 (file)
@@ -111,7 +111,7 @@ struct hdspm_ltc {
        enum hdspm_ltc_input_format input_format;
 };
 
-#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_mixer_ioctl)
+#define SNDRV_HDSPM_IOCTL_GET_LTC _IOR('H', 0x46, struct hdspm_ltc)
 
 /**
  * The status data reflects the device's current state
index 247084be059030162838199c953ebac6409f7e01..fed81b576f29ad8003c48ed293bb9cd42bd95922 100644 (file)
@@ -955,7 +955,7 @@ config MEMCG_SWAP_ENABLED
          Memory Resource Controller Swap Extension comes with its price in
          a bigger memory consumption. General purpose distribution kernels
          which want to enable the feature but keep it disabled by default
-         and let the user enable it by swapaccount boot command line
+         and let the user enable it by swapaccount=1 boot command line
          parameter should have this option unselected.
          For those who want to have the feature enabled by default should
          select this option (if, for some reason, they need to disable it
index 010a0083c0ae4cfa222e2d51f2e951893bac28c4..ea1966db34f2842d859045e2b2d8c7f20ebd196e 100644 (file)
@@ -475,13 +475,17 @@ static int validate_change(const struct cpuset *cur, const struct cpuset *trial)
 
        /*
         * Cpusets with tasks - existing or newly being attached - can't
-        * have empty cpus_allowed or mems_allowed.
+        * be changed to have empty cpus_allowed or mems_allowed.
         */
        ret = -ENOSPC;
-       if ((cgroup_task_count(cur->css.cgroup) || cur->attach_in_progress) &&
-           (cpumask_empty(trial->cpus_allowed) &&
-            nodes_empty(trial->mems_allowed)))
-               goto out;
+       if ((cgroup_task_count(cur->css.cgroup) || cur->attach_in_progress)) {
+               if (!cpumask_empty(cur->cpus_allowed) &&
+                   cpumask_empty(trial->cpus_allowed))
+                       goto out;
+               if (!nodes_empty(cur->mems_allowed) &&
+                   nodes_empty(trial->mems_allowed))
+                       goto out;
+       }
 
        ret = 0;
 out:
index a326f27d7f09e0942ae2df9eb010d8caaabdb725..0b479a6a22bb8fe30e2b9d6c76c2ddb1d5646ae1 100644 (file)
@@ -121,7 +121,7 @@ void __init setup_sched_clock(u32 (*read)(void), int bits, unsigned long rate)
        BUG_ON(bits > 32);
        WARN_ON(!irqs_disabled());
        read_sched_clock = read;
-       sched_clock_mask = (1 << bits) - 1;
+       sched_clock_mask = (1ULL << bits) - 1;
        cd.rate = rate;
 
        /* calculate the mult/shift to convert counter ticks to ns. */
index e77edc97e036b4e8216ae8b54fe0e6cafaa2fe71..e8a1516cc0a36d3c247d2bd4a18ef6d69f17b42c 100644 (file)
@@ -182,7 +182,8 @@ static bool can_stop_full_tick(void)
                 * Don't allow the user to think they can get
                 * full NO_HZ with this machine.
                 */
-               WARN_ONCE(1, "NO_HZ FULL will not work with unstable sched clock");
+               WARN_ONCE(have_nohz_full_mask,
+                         "NO_HZ FULL will not work with unstable sched clock");
                return false;
        }
 #endif
@@ -343,8 +344,6 @@ static int tick_nohz_init_all(void)
 
 void __init tick_nohz_init(void)
 {
-       int cpu;
-
        if (!have_nohz_full_mask) {
                if (tick_nohz_init_all() < 0)
                        return;
index dec68bd4e9d8e996404faa065b67354f1defa8eb..d550920e040c4515c7c865bb6ba2ab884f7ef7bd 100644 (file)
@@ -363,8 +363,7 @@ EXPORT_SYMBOL(out_of_line_wait_on_atomic_t);
 
 /**
  * wake_up_atomic_t - Wake up a waiter on a atomic_t
- * @word: The word being waited on, a kernel virtual address
- * @bit: The bit of the word being waited on
+ * @p: The atomic_t being waited on, a kernel virtual address
  *
  * Wake up anyone waiting for the atomic_t to go to zero.
  *
index fd94058bd7f9496fe69699b85177e1c18e4af2e7..28321d8f75eff530ca6832f67f0418cf0abad936 100644 (file)
@@ -437,7 +437,7 @@ int lz4_compress(const unsigned char *src, size_t src_len,
 exit:
        return ret;
 }
-EXPORT_SYMBOL_GPL(lz4_compress);
+EXPORT_SYMBOL(lz4_compress);
 
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("Dual BSD/GPL");
 MODULE_DESCRIPTION("LZ4 compressor");
index d3414eae73a1dfcbbe520e116385ae7c44cb7b44..411be80ddb46942242fb11013844219f8a5bf39c 100644 (file)
@@ -299,7 +299,7 @@ exit_0:
        return ret;
 }
 #ifndef STATIC
-EXPORT_SYMBOL_GPL(lz4_decompress);
+EXPORT_SYMBOL(lz4_decompress);
 #endif
 
 int lz4_decompress_unknownoutputsize(const char *src, size_t src_len,
@@ -319,8 +319,8 @@ exit_0:
        return ret;
 }
 #ifndef STATIC
-EXPORT_SYMBOL_GPL(lz4_decompress_unknownoutputsize);
+EXPORT_SYMBOL(lz4_decompress_unknownoutputsize);
 
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("Dual BSD/GPL");
 MODULE_DESCRIPTION("LZ4 Decompressor");
 #endif
index eb1a74f5e36828ca3617e721daa06ae43ed658c6..f344f76b6559620bf7ae3bdaaeb6ab25265a344e 100644 (file)
@@ -533,7 +533,7 @@ int lz4hc_compress(const unsigned char *src, size_t src_len,
 exit:
        return ret;
 }
-EXPORT_SYMBOL_GPL(lz4hc_compress);
+EXPORT_SYMBOL(lz4hc_compress);
 
-MODULE_LICENSE("GPL");
+MODULE_LICENSE("Dual BSD/GPL");
 MODULE_DESCRIPTION("LZ4HC compressor");
index c5792a5d87cede8cf2c5474156c1596f51f5ba61..0878ff7c26a95bcf40e23e6e257ee8b9e1704dac 100644 (file)
@@ -6969,7 +6969,6 @@ struct cgroup_subsys mem_cgroup_subsys = {
 #ifdef CONFIG_MEMCG_SWAP
 static int __init enable_swap_account(char *s)
 {
-       /* consider enabled if no parameter or 1 is given */
        if (!strcmp(s, "1"))
                really_do_swap_account = 1;
        else if (!strcmp(s, "0"))
index 8335dbd3fc358ed1fe9d6521785ecee1dc031313..e43dc555069dbe609cd1afae76e56fc4b38e4951 100644 (file)
@@ -2909,14 +2909,8 @@ EXPORT_SYMBOL_GPL(shmem_truncate_range);
 
 /* common code */
 
-static char *shmem_dname(struct dentry *dentry, char *buffer, int buflen)
-{
-       return dynamic_dname(dentry, buffer, buflen, "/%s (deleted)",
-                               dentry->d_name.name);
-}
-
 static struct dentry_operations anon_ops = {
-       .d_dname = shmem_dname
+       .d_dname = simple_dname
 };
 
 /**
index 688a0419756bfc6ce2a9f632f6e84a6ed42debcf..857e1b8349ee417e96fd4e6afb840c541245d04c 100644 (file)
@@ -432,12 +432,16 @@ find_router:
 
        switch (packet_type) {
        case BATADV_UNICAST:
-               batadv_unicast_prepare_skb(skb, orig_node);
+               if (!batadv_unicast_prepare_skb(skb, orig_node))
+                       goto out;
+
                header_len = sizeof(struct batadv_unicast_packet);
                break;
        case BATADV_UNICAST_4ADDR:
-               batadv_unicast_4addr_prepare_skb(bat_priv, skb, orig_node,
-                                                packet_subtype);
+               if (!batadv_unicast_4addr_prepare_skb(bat_priv, skb, orig_node,
+                                                     packet_subtype))
+                       goto out;
+
                header_len = sizeof(struct batadv_unicast_4addr_packet);
                break;
        default:
index 60aca9109a508d5a75c5b34befbdca8c7364fe52..ffd5874f25920a94c74f5d97ebf4a0e2aa77f48d 100644 (file)
@@ -161,7 +161,7 @@ void br_fdb_change_mac_address(struct net_bridge *br, const u8 *newaddr)
        if (!pv)
                return;
 
-       for_each_set_bit_from(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) {
+       for_each_set_bit_from(vid, pv->vlan_bitmap, VLAN_N_VID) {
                f = __br_fdb_get(br, br->dev->dev_addr, vid);
                if (f && f->is_local && !f->dst)
                        fdb_delete(br, f);
@@ -730,7 +730,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
                /* VID was specified, so use it. */
                err = __br_fdb_add(ndm, p, addr, nlh_flags, vid);
        } else {
-               if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) {
+               if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID)) {
                        err = __br_fdb_add(ndm, p, addr, nlh_flags, 0);
                        goto out;
                }
@@ -739,7 +739,7 @@ int br_fdb_add(struct ndmsg *ndm, struct nlattr *tb[],
                 * specify a VLAN.  To be nice, add/update entry for every
                 * vlan on this port.
                 */
-               for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) {
+               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
                        err = __br_fdb_add(ndm, p, addr, nlh_flags, vid);
                        if (err)
                                goto out;
@@ -817,7 +817,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
 
                err = __br_fdb_delete(p, addr, vid);
        } else {
-               if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN)) {
+               if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID)) {
                        err = __br_fdb_delete(p, addr, 0);
                        goto out;
                }
@@ -827,7 +827,7 @@ int br_fdb_delete(struct ndmsg *ndm, struct nlattr *tb[],
                 * vlan on this port.
                 */
                err = -ENOENT;
-               for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) {
+               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
                        err &= __br_fdb_delete(p, addr, vid);
                }
        }
index 1fc30abd3a523912376ce01fcae932f3b6b8c746..b9259efa636ef8fe2b79ec25de49a16efa9034db 100644 (file)
@@ -132,7 +132,7 @@ static int br_fill_ifinfo(struct sk_buff *skb,
                else
                        pv = br_get_vlan_info(br);
 
-               if (!pv || bitmap_empty(pv->vlan_bitmap, BR_VLAN_BITMAP_LEN))
+               if (!pv || bitmap_empty(pv->vlan_bitmap, VLAN_N_VID))
                        goto done;
 
                af = nla_nest_start(skb, IFLA_AF_SPEC);
@@ -140,7 +140,7 @@ static int br_fill_ifinfo(struct sk_buff *skb,
                        goto nla_put_failure;
 
                pvid = br_get_pvid(pv);
-               for_each_set_bit(vid, pv->vlan_bitmap, BR_VLAN_BITMAP_LEN) {
+               for_each_set_bit(vid, pv->vlan_bitmap, VLAN_N_VID) {
                        vinfo.vid = vid;
                        vinfo.flags = 0;
                        if (vid == pvid)
index bd58b45f5f901fd4c6a3ad00abe068baeba8d898..9a9ffe7e4019741d75456e3b9afdba21c44785b3 100644 (file)
@@ -108,7 +108,7 @@ static int __vlan_del(struct net_port_vlans *v, u16 vid)
 
        clear_bit(vid, v->vlan_bitmap);
        v->num_vlans--;
-       if (bitmap_empty(v->vlan_bitmap, BR_VLAN_BITMAP_LEN)) {
+       if (bitmap_empty(v->vlan_bitmap, VLAN_N_VID)) {
                if (v->port_idx)
                        rcu_assign_pointer(v->parent.port->vlan_info, NULL);
                else
@@ -122,7 +122,7 @@ static void __vlan_flush(struct net_port_vlans *v)
 {
        smp_wmb();
        v->pvid = 0;
-       bitmap_zero(v->vlan_bitmap, BR_VLAN_BITMAP_LEN);
+       bitmap_zero(v->vlan_bitmap, VLAN_N_VID);
        if (v->port_idx)
                rcu_assign_pointer(v->parent.port->vlan_info, NULL);
        else
index 5423223e93c25074f87c92dc60b338fddcb66f3f..b2f6c74861af6d8e1209c65823bef34f806609c2 100644 (file)
@@ -1120,6 +1120,13 @@ new_segment:
                                if (!skb)
                                        goto wait_for_memory;
 
+                               /*
+                                * All packets are restored as if they have
+                                * already been sent.
+                                */
+                               if (tp->repair)
+                                       TCP_SKB_CB(skb)->when = tcp_time_stamp;
+
                                /*
                                 * Check whether we can use HW checksum.
                                 */
index da4241c8c7dafe0004a53ed85dfc270cd3be16ba..498ea99194af69eeaab39f05134e4eb646e22c8a 100644 (file)
@@ -1126,12 +1126,10 @@ retry:
        if (ifp->flags & IFA_F_OPTIMISTIC)
                addr_flags |= IFA_F_OPTIMISTIC;
 
-       ift = !max_addresses ||
-             ipv6_count_addresses(idev) < max_addresses ?
-               ipv6_add_addr(idev, &addr, NULL, tmp_plen,
-                             ipv6_addr_scope(&addr), addr_flags,
-                             tmp_valid_lft, tmp_prefered_lft) : NULL;
-       if (IS_ERR_OR_NULL(ift)) {
+       ift = ipv6_add_addr(idev, &addr, NULL, tmp_plen,
+                           ipv6_addr_scope(&addr), addr_flags,
+                           tmp_valid_lft, tmp_prefered_lft);
+       if (IS_ERR(ift)) {
                in6_ifa_put(ifp);
                in6_dev_put(idev);
                pr_info("%s: retry temporary address regeneration\n", __func__);
index 79aa9652ed86d9cac754366c075800fbbe4aca1b..04d31c2fbef1ede543bbc4942873722f66a9b524 100644 (file)
@@ -1369,8 +1369,10 @@ static void ndisc_redirect_rcv(struct sk_buff *skb)
        if (!ndisc_parse_options(msg->opt, ndoptlen, &ndopts))
                return;
 
-       if (!ndopts.nd_opts_rh)
+       if (!ndopts.nd_opts_rh) {
+               ip6_redirect_no_header(skb, dev_net(skb->dev), 0, 0);
                return;
+       }
 
        hdr = (u8 *)ndopts.nd_opts_rh;
        hdr += 8;
index 790d9f4b8b0b21c1d4dd4577ee6a472bd96fd729..1aeb473b2cc695d8d2b0a3696972ec9228455d14 100644 (file)
@@ -490,6 +490,7 @@ static int ip6_frag_reasm(struct frag_queue *fq, struct sk_buff *prev,
        ipv6_hdr(head)->payload_len = htons(payload_len);
        ipv6_change_dsfield(ipv6_hdr(head), 0xff, ecn);
        IP6CB(head)->nhoff = nhoff;
+       IP6CB(head)->flags |= IP6SKB_FRAGMENTED;
 
        /* Yes, and fold redundant checksum back. 8) */
        if (head->ip_summed == CHECKSUM_COMPLETE)
@@ -524,6 +525,9 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
        struct net *net = dev_net(skb_dst(skb)->dev);
        int evicted;
 
+       if (IP6CB(skb)->flags & IP6SKB_FRAGMENTED)
+               goto fail_hdr;
+
        IP6_INC_STATS_BH(net, ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMREQDS);
 
        /* Jumbo payload inhibits frag. header */
@@ -544,6 +548,7 @@ static int ipv6_frag_rcv(struct sk_buff *skb)
                                 ip6_dst_idev(skb_dst(skb)), IPSTATS_MIB_REASMOKS);
 
                IP6CB(skb)->nhoff = (u8 *)fhdr - skb_network_header(skb);
+               IP6CB(skb)->flags |= IP6SKB_FRAGMENTED;
                return 1;
        }
 
index b70f8979003b5e23c4b1e78e44713ba3d3520de9..8d9a93ed9c5926924b809b5bb68b267c07f3ca61 100644 (file)
@@ -1178,6 +1178,27 @@ void ip6_redirect(struct sk_buff *skb, struct net *net, int oif, u32 mark)
 }
 EXPORT_SYMBOL_GPL(ip6_redirect);
 
+void ip6_redirect_no_header(struct sk_buff *skb, struct net *net, int oif,
+                           u32 mark)
+{
+       const struct ipv6hdr *iph = ipv6_hdr(skb);
+       const struct rd_msg *msg = (struct rd_msg *)icmp6_hdr(skb);
+       struct dst_entry *dst;
+       struct flowi6 fl6;
+
+       memset(&fl6, 0, sizeof(fl6));
+       fl6.flowi6_oif = oif;
+       fl6.flowi6_mark = mark;
+       fl6.flowi6_flags = 0;
+       fl6.daddr = msg->dest;
+       fl6.saddr = iph->daddr;
+
+       dst = ip6_route_output(net, NULL, &fl6);
+       if (!dst->error)
+               rt6_do_redirect(dst, NULL, skb);
+       dst_release(dst);
+}
+
 void ip6_sk_redirect(struct sk_buff *skb, struct sock *sk)
 {
        ip6_redirect(skb, sock_net(sk), sk->sk_bound_dev_if, sk->sk_mark);
index f85f8a2ad6cf002fa438bef15597496897d00483..512718adb0d59df5120e047c69c973556d1c6fb6 100644 (file)
@@ -789,10 +789,6 @@ static int ctrl_dumpfamily(struct sk_buff *skb, struct netlink_callback *cb)
        struct net *net = sock_net(skb->sk);
        int chains_to_skip = cb->args[0];
        int fams_to_skip = cb->args[1];
-       bool need_locking = chains_to_skip || fams_to_skip;
-
-       if (need_locking)
-               genl_lock();
 
        for (i = chains_to_skip; i < GENL_FAM_TAB_SIZE; i++) {
                n = 0;
@@ -814,9 +810,6 @@ errout:
        cb->args[0] = i;
        cb->args[1] = n;
 
-       if (need_locking)
-               genl_unlock();
-
        return skb->len;
 }
 
index 4b66c752eae5d99b2bfe5fa7832a1301d20519ef..75c8bbf598c86b54b6160d88d1d1177ce04426b5 100644 (file)
@@ -3259,9 +3259,11 @@ static int packet_getsockopt(struct socket *sock, int level, int optname,
 
                if (po->tp_version == TPACKET_V3) {
                        lv = sizeof(struct tpacket_stats_v3);
+                       st.stats3.tp_packets += st.stats3.tp_drops;
                        data = &st.stats3;
                } else {
                        lv = sizeof(struct tpacket_stats);
+                       st.stats1.tp_packets += st.stats1.tp_drops;
                        data = &st.stats1;
                }
 
index 3fcba69817e579244e836b2e2d39a1aab14cc210..5f6e982cdcf4f04fef43872756c44b3c14c4705d 100644 (file)
@@ -2622,8 +2622,8 @@ static int nl80211_get_key(struct sk_buff *skb, struct genl_info *info)
 
        hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
                             NL80211_CMD_NEW_KEY);
-       if (IS_ERR(hdr))
-               return PTR_ERR(hdr);
+       if (!hdr)
+               return -ENOBUFS;
 
        cookie.msg = msg;
        cookie.idx = key_idx;
@@ -6507,6 +6507,9 @@ static int nl80211_testmode_dump(struct sk_buff *skb,
                                           NL80211_CMD_TESTMODE);
                struct nlattr *tmdata;
 
+               if (!hdr)
+                       break;
+
                if (nla_put_u32(skb, NL80211_ATTR_WIPHY, phy_idx)) {
                        genlmsg_cancel(skb, hdr);
                        break;
@@ -6951,9 +6954,8 @@ static int nl80211_remain_on_channel(struct sk_buff *skb,
 
        hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
                             NL80211_CMD_REMAIN_ON_CHANNEL);
-
-       if (IS_ERR(hdr)) {
-               err = PTR_ERR(hdr);
+       if (!hdr) {
+               err = -ENOBUFS;
                goto free_msg;
        }
 
@@ -7251,9 +7253,8 @@ static int nl80211_tx_mgmt(struct sk_buff *skb, struct genl_info *info)
 
                hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
                                     NL80211_CMD_FRAME);
-
-               if (IS_ERR(hdr)) {
-                       err = PTR_ERR(hdr);
+               if (!hdr) {
+                       err = -ENOBUFS;
                        goto free_msg;
                }
        }
@@ -8132,9 +8133,8 @@ static int nl80211_probe_client(struct sk_buff *skb,
 
        hdr = nl80211hdr_put(msg, info->snd_portid, info->snd_seq, 0,
                             NL80211_CMD_PROBE_CLIENT);
-
-       if (IS_ERR(hdr)) {
-               err = PTR_ERR(hdr);
+       if (!hdr) {
+               err = -ENOBUFS;
                goto free_msg;
        }
 
index 81c8a10d743c04fb76981498b42588f9b821f33f..20e86a95dc4e0ed358485f04208c670297ee6517 100644 (file)
@@ -976,21 +976,19 @@ int cfg80211_disconnect(struct cfg80211_registered_device *rdev,
                        struct net_device *dev, u16 reason, bool wextev)
 {
        struct wireless_dev *wdev = dev->ieee80211_ptr;
-       int err;
+       int err = 0;
 
        ASSERT_WDEV_LOCK(wdev);
 
        kfree(wdev->connect_keys);
        wdev->connect_keys = NULL;
 
-       if (wdev->conn) {
+       if (wdev->conn)
                err = cfg80211_sme_disconnect(wdev, reason);
-       } else if (!rdev->ops->disconnect) {
+       else if (!rdev->ops->disconnect)
                cfg80211_mlme_down(rdev, dev);
-               err = 0;
-       } else {
+       else if (wdev->current_bss)
                err = rdev_disconnect(rdev, dev, reason);
-       }
 
        return err;
 }
index ce431e6e07cfc7df27e8320d6da1b13516310df9..5066a3768b2841a4f46d8cbb8116b4d8276334da 100644 (file)
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/dmaengine.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/ac97_codec.h>
 #include <sound/initval.h>
 #include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
 
 #include <mach/regs-ac97.h>
 #include <mach/audio.h>
@@ -41,20 +43,20 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
        .reset  = pxa2xx_ac97_reset,
 };
 
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_out = {
-       .name                   = "AC97 PCM out",
-       .dev_addr               = __PREG(PCDR),
-       .drcmr                  = &DRCMR(12),
-       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-                                 DCMD_BURST32 | DCMD_WIDTH4,
+static unsigned long pxa2xx_ac97_pcm_out_req = 12;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_out = {
+       .addr           = __PREG(PCDR),
+       .addr_width     = DMA_SLAVE_BUSWIDTH_4_BYTES,
+       .maxburst       = 32,
+       .filter_data    = &pxa2xx_ac97_pcm_out_req,
 };
 
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_in = {
-       .name                   = "AC97 PCM in",
-       .dev_addr               = __PREG(PCDR),
-       .drcmr                  = &DRCMR(11),
-       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-                                 DCMD_BURST32 | DCMD_WIDTH4,
+static unsigned long pxa2xx_ac97_pcm_in_req = 11;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_in = {
+       .addr           = __PREG(PCDR),
+       .addr_width     = DMA_SLAVE_BUSWIDTH_4_BYTES,
+       .maxburst       = 32,
+       .filter_data    = &pxa2xx_ac97_pcm_in_req,
 };
 
 static struct snd_pcm *pxa2xx_ac97_pcm;
index 823359ed95e16a0969ee6c28843a4777387d3561..a61d7a9a995e86daaeea8b8243c93f9fbfe911d2 100644 (file)
@@ -7,11 +7,13 @@
 #include <linux/slab.h>
 #include <linux/module.h>
 #include <linux/dma-mapping.h>
+#include <linux/dmaengine.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
 
 #include <mach/dma.h>
 
@@ -43,6 +45,35 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
        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;
+
+       /* 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;
+       }
+
+       switch (rtd->params->maxburst) {
+       case 8:
+               dcmd |= DCMD_BURST8;
+               break;
+       case 16:
+               dcmd |= DCMD_BURST16;
+               break;
+       case 32:
+               dcmd |= DCMD_BURST32;
+               break;
+       }
 
        snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
        runtime->dma_bytes = totsize;
@@ -55,14 +86,14 @@ int __pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
                dma_desc->ddadr = next_desc_phys;
                if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
                        dma_desc->dsadr = dma_buff_phys;
-                       dma_desc->dtadr = rtd->params->dev_addr;
+                       dma_desc->dtadr = rtd->params->addr;
                } else {
-                       dma_desc->dsadr = rtd->params->dev_addr;
+                       dma_desc->dsadr = rtd->params->addr;
                        dma_desc->dtadr = dma_buff_phys;
                }
                if (period > totsize)
                        period = totsize;
-               dma_desc->dcmd = rtd->params->dcmd | period | DCMD_ENDIRQEN;
+               dma_desc->dcmd = dcmd | period | DCMD_ENDIRQEN;
                dma_desc++;
                dma_buff_phys += period;
        } while (totsize -= period);
@@ -76,8 +107,10 @@ 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->drcmr)
-               *rtd->params->drcmr = 0;
+       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;
@@ -136,6 +169,7 @@ 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;
@@ -146,7 +180,8 @@ int __pxa2xx_pcm_prepare(struct snd_pcm_substream *substream)
        DCSR(prtd->dma_ch) &= ~DCSR_RUN;
        DCSR(prtd->dma_ch) = 0;
        DCMD(prtd->dma_ch) = 0;
-       *prtd->params->drcmr = prtd->dma_ch | DRCMR_MAPVLD;
+       req = *(unsigned long *) prtd->params->filter_data;
+       DRCMR(req) = prtd->dma_ch | DRCMR_MAPVLD;
 
        return 0;
 }
@@ -155,7 +190,6 @@ EXPORT_SYMBOL(__pxa2xx_pcm_prepare);
 void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
 {
        struct snd_pcm_substream *substream = dev_id;
-       struct pxa2xx_runtime_data *rtd = substream->runtime->private_data;
        int dcsr;
 
        dcsr = DCSR(dma_ch);
@@ -164,8 +198,8 @@ void pxa2xx_pcm_dma_irq(int dma_ch, void *dev_id)
        if (dcsr & DCSR_ENDINTR) {
                snd_pcm_period_elapsed(substream);
        } else {
-               printk(KERN_ERR "%s: DMA error on channel %d (DCSR=%#x)\n",
-                       rtd->params->name, dma_ch, dcsr);
+               printk(KERN_ERR "DMA error on channel %d (DCSR=%#x)\n",
+                       dma_ch, dcsr);
                snd_pcm_stream_lock(substream);
                snd_pcm_stop(substream, SNDRV_PCM_STATE_XRUN);
                snd_pcm_stream_unlock(substream);
index 26422a3584ea6684e934f66b33a7f9aad8b5b762..69a2455b447210d42c2bf2c37b23fbe9035eada9 100644 (file)
  */
 
 #include <linux/module.h>
+#include <linux/dmaengine.h>
+
 #include <sound/core.h>
 #include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
 
 #include "pxa2xx-pcm.h"
 
@@ -40,7 +43,7 @@ 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(rtd->params->name, DMA_PRIO_LOW,
+       ret = pxa_request_dma("dma", DMA_PRIO_LOW,
                              pxa2xx_pcm_dma_irq, substream);
        if (ret < 0)
                goto err2;
index 65f86b56ba422a2c28c6d0c70c731c6250df7c63..2a8fc08d52a173686fa9e946f2f3932179a64812 100644 (file)
 
 struct pxa2xx_runtime_data {
        int dma_ch;
-       struct pxa2xx_pcm_dma_params *params;
+       struct snd_dmaengine_dai_dma_data *params;
        pxa_dma_desc *dma_desc_array;
        dma_addr_t dma_desc_array_phys;
 };
 
 struct pxa2xx_pcm_client {
-       struct pxa2xx_pcm_dma_params *playback_params;
-       struct pxa2xx_pcm_dma_params *capture_params;
+       struct snd_dmaengine_dai_dma_data *playback_params;
+       struct snd_dmaengine_dai_dma_data *capture_params;
        int (*startup)(struct snd_pcm_substream *);
        void (*shutdown)(struct snd_pcm_substream *);
        int (*prepare)(struct snd_pcm_substream *);
index c0c2f57a0d6f332b6143c3eaa6850921c1dde485..313f22e9d929d8f5dbd039d224a529788b22c635 100644 (file)
@@ -6,6 +6,9 @@ config SND_PCM
        tristate
        select SND_TIMER
 
+config SND_DMAENGINE_PCM
+       tristate
+
 config SND_HWDEP
        tristate
 
index 43d4117428ac9d551391f0f89d1edfd3b2665c6b..5e890cfed42363e1aa1b1ebfbfba76d0d16f0326 100644 (file)
@@ -13,6 +13,8 @@ snd-$(CONFIG_SND_JACK)          += jack.o
 snd-pcm-objs := pcm.o pcm_native.o pcm_lib.o pcm_timer.o pcm_misc.o \
                pcm_memory.o
 
+snd-pcm-dmaengine-objs := pcm_dmaengine.o
+
 snd-page-alloc-y := memalloc.o
 snd-page-alloc-$(CONFIG_SND_DMA_SGBUF) += sgbuf.o
 
@@ -30,6 +32,7 @@ obj-$(CONFIG_SND_TIMER)               += snd-timer.o
 obj-$(CONFIG_SND_HRTIMER)      += snd-hrtimer.o
 obj-$(CONFIG_SND_RTCTIMER)     += snd-rtctimer.o
 obj-$(CONFIG_SND_PCM)          += snd-pcm.o snd-page-alloc.o
+obj-$(CONFIG_SND_DMAENGINE_PCM)        += snd-pcm-dmaengine.o
 obj-$(CONFIG_SND_RAWMIDI)      += snd-rawmidi.o
 
 obj-$(CONFIG_SND_OSSEMUL)      += oss/
index 82bb029d4414155e60ae2a01ddd4707d926b83ae..6e03b465e44e3a05f82e93a2d25a61f030cc3cf6 100644 (file)
@@ -184,7 +184,7 @@ static void xrun(struct snd_pcm_substream *substream)
        do {                                                            \
                if (xrun_debug(substream, XRUN_DEBUG_BASIC)) {          \
                        xrun_log_show(substream);                       \
-                       if (printk_ratelimit()) {                       \
+                       if (snd_printd_ratelimit()) {                   \
                                snd_printd("PCM: " fmt, ##args);        \
                        }                                               \
                        dump_stack_on_xrun(substream);                  \
@@ -342,7 +342,7 @@ static int snd_pcm_update_hw_ptr0(struct snd_pcm_substream *substream,
                return -EPIPE;
        }
        if (pos >= runtime->buffer_size) {
-               if (printk_ratelimit()) {
+               if (snd_printd_ratelimit()) {
                        char name[16];
                        snd_pcm_debug_name(substream, name, sizeof(name));
                        xrun_log_show(substream);
index 11048cc744d0ef01034ce0a9aceb517ff202b5a5..915b4d7fbb23fe8c1309e05c17ff525a6d7e7d78 100644 (file)
@@ -1022,7 +1022,7 @@ static void dummy_proc_write(struct snd_info_entry *entry,
                if (i >= ARRAY_SIZE(fields))
                        continue;
                snd_info_get_str(item, ptr, sizeof(item));
-               if (strict_strtoull(item, 0, &val))
+               if (kstrtoull(item, 0, &val))
                        continue;
                if (fields[i].size == sizeof(int))
                        *get_dummy_int_ptr(dummy, fields[i].offset) = val;
index 2c6386503940bf6db7f996a2dcc6a9a843182013..fe9e6e2f2c5b2a825ae24a432541d45de8f029fb 100644 (file)
@@ -49,7 +49,6 @@ struct fwspk {
        struct snd_card *card;
        struct fw_unit *unit;
        const struct device_info *device_info;
-       struct snd_pcm_substream *pcm;
        struct mutex mutex;
        struct cmp_connection connection;
        struct amdtp_out_stream stream;
@@ -363,8 +362,7 @@ static int fwspk_create_pcm(struct fwspk *fwspk)
                return err;
        pcm->private_data = fwspk;
        strcpy(pcm->name, fwspk->device_info->short_name);
-       fwspk->pcm = pcm->streams[SNDRV_PCM_STREAM_PLAYBACK].substream;
-       fwspk->pcm->ops = &ops;
+       snd_pcm_set_ops(pcm, SNDRV_PCM_STREAM_PLAYBACK, &ops);
        return 0;
 }
 
index 9942691cc0ca9c101bfd830b134bc4e97bddb727..afef0d738078f27935d844f5d002db1b7bbe2609 100644 (file)
@@ -443,8 +443,7 @@ static void snd_interwave_detect_memory(struct snd_gus_card *gus)
                for (i = 0; i < 8; ++i)
                        iwave[i] = snd_gf1_peek(gus, bank_pos + i);
 #ifdef CONFIG_SND_DEBUG_ROM
-               printk(KERN_DEBUG "ROM at 0x%06x = %*phC\n", bank_pos,
-                                 8, iwave);
+               printk(KERN_DEBUG "ROM at 0x%06x = %8phC\n", bank_pos, iwave);
 #endif
                if (strncmp(iwave, "INTRWAVE", 8))
                        continue;       /* first check */
index a59c88818f48a2dbf1f79e9ac4a627ee81f6cb26..461d94cfecbee6bc0ac02d324db0f3f74c9d4eae 100644 (file)
@@ -557,7 +557,6 @@ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
        unsigned long flags;
        int err = 0, n = 0;
        struct dma_buffparms *dmap = adev->dmap_in;
-       int go;
 
        if (!(adev->open_mode & OPEN_READ))
                return -EIO;
@@ -584,7 +583,7 @@ int DMAbuf_getrdbuffer(int dev, char **buf, int *len, int dontblock)
                        spin_unlock_irqrestore(&dmap->lock,flags);
                        return -EAGAIN;
                }
-               if ((go = adev->go))
+               if (adev->go)
                        timeout = dmabuf_timeout(dmap);
 
                spin_unlock_irqrestore(&dmap->lock,flags);
index 59c5e9c03d538ea6c18fe377d5c0152c7b2030ba..8de66ccd7279d682d84f1718d8cd2f5788ac9e06 100644 (file)
@@ -152,14 +152,9 @@ config SND_HDA_CODEC_HDMI
          This module is automatically loaded at probing.
 
 config SND_HDA_I915
-       bool "Build Display HD-audio controller/codec power well support for i915 cards"
+       bool
+       default y
        depends on DRM_I915
-       help
-         Say Y here to include full HDMI and DisplayPort HD-audio controller/codec
-         power-well support for Intel Haswell graphics cards based on the i915 driver.
-
-         Note that this option must be enabled for Intel Haswell C+ stepping machines, otherwise
-         the GPU audio controller/codecs will not be initialized or damaged when exit from S3 mode.
 
 config SND_HDA_CODEC_CIRRUS
        bool "Build Cirrus Logic codec support"
index 8a005f0e5ca484c842e2fe5f684275abafbde248..5b6c4e3c92ca40a7000909c1540d7d6787d36946 100644 (file)
@@ -666,6 +666,64 @@ int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
 }
 EXPORT_SYMBOL_HDA(snd_hda_get_conn_index);
 
+
+/* return DEVLIST_LEN parameter of the given widget */
+static unsigned int get_num_devices(struct hda_codec *codec, hda_nid_t nid)
+{
+       unsigned int wcaps = get_wcaps(codec, nid);
+       unsigned int parm;
+
+       if (!codec->dp_mst || !(wcaps & AC_WCAP_DIGITAL) ||
+           get_wcaps_type(wcaps) != AC_WID_PIN)
+               return 0;
+
+       parm = snd_hda_param_read(codec, nid, AC_PAR_DEVLIST_LEN);
+       if (parm == -1 && codec->bus->rirb_error)
+               parm = 0;
+       return parm & AC_DEV_LIST_LEN_MASK;
+}
+
+/**
+ * snd_hda_get_devices - copy device list without cache
+ * @codec: the HDA codec
+ * @nid: NID of the pin to parse
+ * @dev_list: device list array
+ * @max_devices: max. number of devices to store
+ *
+ * Copy the device list. This info is dynamic and so not cached.
+ * Currently called only from hda_proc.c, so not exported.
+ */
+int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
+                       u8 *dev_list, int max_devices)
+{
+       unsigned int parm;
+       int i, dev_len, devices;
+
+       parm = get_num_devices(codec, nid);
+       if (!parm)      /* not multi-stream capable */
+               return 0;
+
+       dev_len = parm + 1;
+       dev_len = dev_len < max_devices ? dev_len : max_devices;
+
+       devices = 0;
+       while (devices < dev_len) {
+               parm = snd_hda_codec_read(codec, nid, 0,
+                                         AC_VERB_GET_DEVICE_LIST, devices);
+               if (parm == -1 && codec->bus->rirb_error)
+                       break;
+
+               for (i = 0; i < 8; i++) {
+                       dev_list[devices] = (u8)parm;
+                       parm >>= 4;
+                       devices++;
+                       if (devices >= dev_len)
+                               break;
+               }
+       }
+       return devices;
+}
+
 /**
  * snd_hda_queue_unsol_event - add an unsolicited event to queue
  * @bus: the BUS
@@ -1216,11 +1274,13 @@ static void hda_jackpoll_work(struct work_struct *work)
 {
        struct hda_codec *codec =
                container_of(work, struct hda_codec, jackpoll_work.work);
-       if (!codec->jackpoll_interval)
-               return;
 
        snd_hda_jack_set_dirty_all(codec);
        snd_hda_jack_poll_all(codec);
+
+       if (!codec->jackpoll_interval)
+               return;
+
        queue_delayed_work(codec->bus->workq, &codec->jackpoll_work,
                           codec->jackpoll_interval);
 }
index 701c2e069b10ad218886f5853caa1c5c3e31409c..7aa9870040c102df5b02aa0a94778c8e14c36024 100644 (file)
@@ -94,6 +94,8 @@ enum {
 #define AC_VERB_GET_HDMI_DIP_XMIT              0x0f32
 #define AC_VERB_GET_HDMI_CP_CTRL               0x0f33
 #define AC_VERB_GET_HDMI_CHAN_SLOT             0x0f34
+#define AC_VERB_GET_DEVICE_SEL                 0xf35
+#define AC_VERB_GET_DEVICE_LIST                        0xf36
 
 /*
  * SET verbs
@@ -133,6 +135,7 @@ enum {
 #define AC_VERB_SET_HDMI_DIP_XMIT              0x732
 #define AC_VERB_SET_HDMI_CP_CTRL               0x733
 #define AC_VERB_SET_HDMI_CHAN_SLOT             0x734
+#define AC_VERB_SET_DEVICE_SEL                 0x735
 
 /*
  * Parameter IDs
@@ -154,6 +157,7 @@ enum {
 #define AC_PAR_GPIO_CAP                        0x11
 #define AC_PAR_AMP_OUT_CAP             0x12
 #define AC_PAR_VOL_KNB_CAP             0x13
+#define AC_PAR_DEVLIST_LEN             0x15
 #define AC_PAR_HDMI_LPCM_CAP           0x20
 
 /*
@@ -251,6 +255,11 @@ enum {
 #define AC_UNSOL_RES_TAG_SHIFT         26
 #define AC_UNSOL_RES_SUBTAG            (0x1f<<21)
 #define AC_UNSOL_RES_SUBTAG_SHIFT      21
+#define AC_UNSOL_RES_DE                        (0x3f<<15)  /* Device Entry
+                                                    * (for DP1.2 MST)
+                                                    */
+#define AC_UNSOL_RES_DE_SHIFT          15
+#define AC_UNSOL_RES_IA                        (1<<2)  /* Inactive (for DP1.2 MST) */
 #define AC_UNSOL_RES_ELDV              (1<<1)  /* ELD Data valid (for HDMI) */
 #define AC_UNSOL_RES_PD                        (1<<0)  /* pinsense detect */
 #define AC_UNSOL_RES_CP_STATE          (1<<1)  /* content protection */
@@ -352,6 +361,10 @@ enum {
 #define AC_LPCMCAP_44K                 (1<<30) /* 44.1kHz support */
 #define AC_LPCMCAP_44K_MS              (1<<31) /* 44.1kHz-multiplies support */
 
+/* Display pin's device list length */
+#define AC_DEV_LIST_LEN_MASK           0x3f
+#define AC_MAX_DEV_LIST_LEN            64
+
 /*
  * Control Parameters
  */
@@ -460,6 +473,11 @@ enum {
 #define AC_DEFCFG_PORT_CONN            (0x3<<30)
 #define AC_DEFCFG_PORT_CONN_SHIFT      30
 
+/* Display pin's device list entry */
+#define AC_DE_PD                       (1<<0)
+#define AC_DE_ELDV                     (1<<1)
+#define AC_DE_IA                       (1<<2)
+
 /* device device types (0x0-0xf) */
 enum {
        AC_JACK_LINE_OUT,
@@ -885,6 +903,7 @@ struct hda_codec {
        unsigned int pcm_format_first:1; /* PCM format must be set first */
        unsigned int epss:1;            /* supporting EPSS? */
        unsigned int cached_write:1;    /* write only to caches */
+       unsigned int dp_mst:1; /* support DP1.2 Multi-stream transport */
 #ifdef CONFIG_PM
        unsigned int power_on :1;       /* current (global) power-state */
        unsigned int d3_stop_clk:1;     /* support D3 operation without BCLK */
@@ -972,6 +991,8 @@ int snd_hda_override_conn_list(struct hda_codec *codec, hda_nid_t nid, int nums,
                          const hda_nid_t *list);
 int snd_hda_get_conn_index(struct hda_codec *codec, hda_nid_t mux,
                           hda_nid_t nid, int recursive);
+int snd_hda_get_devices(struct hda_codec *codec, hda_nid_t nid,
+                       u8 *dev_list, int max_devices);
 int snd_hda_query_supported_pcm(struct hda_codec *codec, hda_nid_t nid,
                                u32 *ratesp, u64 *formatsp, unsigned int *bpsp);
 
index e3c7ba8d7582643f9d1f52681251e16e3835d042..ac41e9cdc976a1c190c51684519abb9c475c2c59 100644 (file)
@@ -142,6 +142,9 @@ static void parse_user_hints(struct hda_codec *codec)
        val = snd_hda_get_bool_hint(codec, "primary_hp");
        if (val >= 0)
                spec->no_primary_hp = !val;
+       val = snd_hda_get_bool_hint(codec, "multi_io");
+       if (val >= 0)
+               spec->no_multi_io = !val;
        val = snd_hda_get_bool_hint(codec, "multi_cap_vol");
        if (val >= 0)
                spec->multi_cap_vol = !!val;
@@ -813,6 +816,8 @@ static void resume_path_from_idx(struct hda_codec *codec, int path_idx)
 
 static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_value *ucontrol);
+static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol);
 
 enum {
        HDA_CTL_WIDGET_VOL,
@@ -830,7 +835,13 @@ static const struct snd_kcontrol_new control_templates[] = {
                .put = hda_gen_mixer_mute_put, /* replaced */
                .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
        },
-       HDA_BIND_MUTE(NULL, 0, 0, 0),
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .info = snd_hda_mixer_amp_switch_info,
+               .get = snd_hda_mixer_bind_switch_get,
+               .put = hda_gen_bind_mute_put, /* replaced */
+               .private_value = HDA_COMPOSE_AMP_VAL(0, 3, 0, 0),
+       },
 };
 
 /* add dynamic controls from template */
@@ -937,8 +948,8 @@ static int add_stereo_sw(struct hda_codec *codec, const char *pfx,
 }
 
 /* playback mute control with the software mute bit check */
-static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
-                                 struct snd_ctl_elem_value *ucontrol)
+static void sync_auto_mute_bits(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        struct hda_gen_spec *spec = codec->spec;
@@ -949,10 +960,22 @@ static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
                ucontrol->value.integer.value[0] &= enabled;
                ucontrol->value.integer.value[1] &= enabled;
        }
+}
 
+static int hda_gen_mixer_mute_put(struct snd_kcontrol *kcontrol,
+                                 struct snd_ctl_elem_value *ucontrol)
+{
+       sync_auto_mute_bits(kcontrol, ucontrol);
        return snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
 }
 
+static int hda_gen_bind_mute_put(struct snd_kcontrol *kcontrol,
+                                struct snd_ctl_elem_value *ucontrol)
+{
+       sync_auto_mute_bits(kcontrol, ucontrol);
+       return snd_hda_mixer_bind_switch_put(kcontrol, ucontrol);
+}
+
 /* any ctl assigned to the path with the given index? */
 static bool path_has_mixer(struct hda_codec *codec, int path_idx, int ctl_type)
 {
@@ -1541,7 +1564,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
                                              cfg->speaker_pins,
                                              spec->multiout.extra_out_nid,
                                              spec->speaker_paths);
-                       if (fill_mio_first && cfg->line_outs == 1 &&
+                       if (!spec->no_multi_io &&
+                           fill_mio_first && cfg->line_outs == 1 &&
                            cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
                                err = fill_multi_ios(codec, cfg->line_out_pins[0], true);
                                if (!err)
@@ -1554,7 +1578,7 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
                                   spec->private_dac_nids, spec->out_paths,
                                   spec->main_out_badness);
 
-       if (fill_mio_first &&
+       if (!spec->no_multi_io && fill_mio_first &&
            cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
                /* try to fill multi-io first */
                err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
@@ -1582,7 +1606,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
                        return err;
                badness += err;
        }
-       if (cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
+       if (!spec->no_multi_io &&
+           cfg->line_outs == 1 && cfg->line_out_type != AUTO_PIN_SPEAKER_OUT) {
                err = fill_multi_ios(codec, cfg->line_out_pins[0], false);
                if (err < 0)
                        return err;
@@ -1600,7 +1625,8 @@ static int fill_and_eval_dacs(struct hda_codec *codec,
                                check_aamix_out_path(codec, spec->speaker_paths[0]);
        }
 
-       if (cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
+       if (!spec->no_multi_io &&
+           cfg->hp_outs && cfg->line_out_type == AUTO_PIN_SPEAKER_OUT)
                if (count_multiio_pins(codec, cfg->hp_pins[0]) >= 2)
                        spec->multi_ios = 1; /* give badness */
 
@@ -3724,7 +3750,8 @@ static int mux_select(struct hda_codec *codec, unsigned int adc_idx,
 /* check each pin in the given array; returns true if any of them is plugged */
 static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
 {
-       int i, present = 0;
+       int i;
+       bool present = false;
 
        for (i = 0; i < num_pins; i++) {
                hda_nid_t nid = pins[i];
@@ -3733,14 +3760,15 @@ static bool detect_jacks(struct hda_codec *codec, int num_pins, hda_nid_t *pins)
                /* don't detect pins retasked as inputs */
                if (snd_hda_codec_get_pin_target(codec, nid) & AC_PINCTL_IN_EN)
                        continue;
-               present |= snd_hda_jack_detect(codec, nid);
+               if (snd_hda_jack_detect_state(codec, nid) == HDA_JACK_PRESENT)
+                       present = true;
        }
        return present;
 }
 
 /* standard HP/line-out auto-mute helper */
 static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
-                       bool mute)
+                       int *paths, bool mute)
 {
        struct hda_gen_spec *spec = codec->spec;
        int i;
@@ -3752,10 +3780,19 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
                        break;
 
                if (spec->auto_mute_via_amp) {
+                       struct nid_path *path;
+                       hda_nid_t mute_nid;
+
+                       path = snd_hda_get_path_from_idx(codec, paths[i]);
+                       if (!path)
+                               continue;
+                       mute_nid = get_amp_nid_(path->ctls[NID_PATH_MUTE_CTL]);
+                       if (!mute_nid)
+                               continue;
                        if (mute)
-                               spec->mute_bits |= (1ULL << nid);
+                               spec->mute_bits |= (1ULL << mute_nid);
                        else
-                               spec->mute_bits &= ~(1ULL << nid);
+                               spec->mute_bits &= ~(1ULL << mute_nid);
                        set_pin_eapd(codec, nid, !mute);
                        continue;
                }
@@ -3786,14 +3823,19 @@ static void do_automute(struct hda_codec *codec, int num_pins, hda_nid_t *pins,
 void snd_hda_gen_update_outputs(struct hda_codec *codec)
 {
        struct hda_gen_spec *spec = codec->spec;
+       int *paths;
        int on;
 
        /* Control HP pins/amps depending on master_mute state;
         * in general, HP pins/amps control should be enabled in all cases,
         * but currently set only for master_mute, just to be safe
         */
+       if (spec->autocfg.line_out_type == AUTO_PIN_HP_OUT)
+               paths = spec->out_paths;
+       else
+               paths = spec->hp_paths;
        do_automute(codec, ARRAY_SIZE(spec->autocfg.hp_pins),
-                   spec->autocfg.hp_pins, spec->master_mute);
+                   spec->autocfg.hp_pins, paths, spec->master_mute);
 
        if (!spec->automute_speaker)
                on = 0;
@@ -3801,8 +3843,12 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
                on = spec->hp_jack_present | spec->line_jack_present;
        on |= spec->master_mute;
        spec->speaker_muted = on;
+       if (spec->autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
+               paths = spec->out_paths;
+       else
+               paths = spec->speaker_paths;
        do_automute(codec, ARRAY_SIZE(spec->autocfg.speaker_pins),
-                   spec->autocfg.speaker_pins, on);
+                   spec->autocfg.speaker_pins, paths, on);
 
        /* toggle line-out mutes if needed, too */
        /* if LO is a copy of either HP or Speaker, don't need to handle it */
@@ -3815,8 +3861,9 @@ void snd_hda_gen_update_outputs(struct hda_codec *codec)
                on = spec->hp_jack_present;
        on |= spec->master_mute;
        spec->line_out_muted = on;
+       paths = spec->out_paths;
        do_automute(codec, ARRAY_SIZE(spec->autocfg.line_out_pins),
-                   spec->autocfg.line_out_pins, on);
+                   spec->autocfg.line_out_pins, paths, on);
 }
 EXPORT_SYMBOL_HDA(snd_hda_gen_update_outputs);
 
@@ -3887,7 +3934,7 @@ void snd_hda_gen_mic_autoswitch(struct hda_codec *codec, struct hda_jack_tbl *ja
                /* don't detect pins retasked as outputs */
                if (snd_hda_codec_get_pin_target(codec, pin) & AC_PINCTL_OUT_EN)
                        continue;
-               if (snd_hda_jack_detect(codec, pin)) {
+               if (snd_hda_jack_detect_state(codec, pin) == HDA_JACK_PRESENT) {
                        mux_select(codec, 0, spec->am_entry[i].idx);
                        return;
                }
index e199a852388b4506148b0cfcbcca7ee380406a52..48d44026705b0f05ac01c51fbdb199215195979f 100644 (file)
@@ -220,6 +220,7 @@ struct hda_gen_spec {
        unsigned int hp_mic:1; /* Allow HP as a mic-in */
        unsigned int suppress_hp_mic_detect:1; /* Don't detect HP/mic */
        unsigned int no_primary_hp:1; /* Don't prefer HP pins to speaker pins */
+       unsigned int no_multi_io:1; /* Don't try multi I/O config */
        unsigned int multi_cap_vol:1; /* allow multiple capture xxx volumes */
        unsigned int inv_dmic_split:1; /* inverted dmic w/a for conexant */
        unsigned int own_eapd_ctl:1; /* set EAPD by own function */
index ce67608734b58da8a762346eb678e082b2d6b747..fe0bda19de153f78da5da0c4add17e5fb3d95d65 100644 (file)
@@ -295,7 +295,7 @@ static ssize_t type##_store(struct device *dev,                     \
        struct snd_hwdep *hwdep = dev_get_drvdata(dev);         \
        struct hda_codec *codec = hwdep->private_data;          \
        unsigned long val;                                      \
-       int err = strict_strtoul(buf, 0, &val);                 \
+       int err = kstrtoul(buf, 0, &val);                       \
        if (err < 0)                                            \
                return err;                                     \
        codec->type = val;                                      \
@@ -654,7 +654,7 @@ int snd_hda_get_int_hint(struct hda_codec *codec, const char *key, int *valp)
        p = snd_hda_get_hint(codec, key);
        if (!p)
                ret = -ENOENT;
-       else if (strict_strtoul(p, 0, &val))
+       else if (kstrtoul(p, 0, &val))
                ret = -EINVAL;
        else {
                *valp = val;
@@ -751,7 +751,7 @@ static void parse_##name##_mode(char *buf, struct hda_bus *bus, \
                                 struct hda_codec **codecp) \
 { \
        unsigned long val; \
-       if (!strict_strtoul(buf, 0, &val)) \
+       if (!kstrtoul(buf, 0, &val)) \
                (*codecp)->name = val; \
 }
 
index 8860dd529520d0335264af050fdb918ae47c1c60..c6c98298ac397c024d35cf55e5d85f3fe095decc 100644 (file)
@@ -1160,7 +1160,7 @@ static int azx_reset(struct azx *chip, int full_reset)
                goto __skip;
 
        /* clear STATESTS */
-       azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
+       azx_writew(chip, STATESTS, STATESTS_INT_MASK);
 
        /* reset controller */
        azx_enter_link_reset(chip);
@@ -1242,7 +1242,7 @@ static void azx_int_clear(struct azx *chip)
        }
 
        /* clear STATESTS */
-       azx_writeb(chip, STATESTS, STATESTS_INT_MASK);
+       azx_writew(chip, STATESTS, STATESTS_INT_MASK);
 
        /* clear rirb status */
        azx_writeb(chip, RIRBSTS, RIRB_INT_MASK);
@@ -1451,8 +1451,8 @@ static irqreturn_t azx_interrupt(int irq, void *dev_id)
 
 #if 0
        /* clear state status int */
-       if (azx_readb(chip, STATESTS) & 0x04)
-               azx_writeb(chip, STATESTS, 0x04);
+       if (azx_readw(chip, STATESTS) & 0x04)
+               azx_writew(chip, STATESTS, 0x04);
 #endif
        spin_unlock(&chip->reg_lock);
        
@@ -2971,6 +2971,10 @@ static int azx_runtime_suspend(struct device *dev)
        struct snd_card *card = dev_get_drvdata(dev);
        struct azx *chip = card->private_data;
 
+       /* enable controller wake up event */
+       azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) |
+                 STATESTS_INT_MASK);
+
        azx_stop_chip(chip);
        azx_enter_link_reset(chip);
        azx_clear_irq_pending(chip);
@@ -2983,11 +2987,31 @@ static int azx_runtime_resume(struct device *dev)
 {
        struct snd_card *card = dev_get_drvdata(dev);
        struct azx *chip = card->private_data;
+       struct hda_bus *bus;
+       struct hda_codec *codec;
+       int status;
 
        if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL)
                hda_display_power(true);
+
+       /* Read STATESTS before controller reset */
+       status = azx_readw(chip, STATESTS);
+
        azx_init_pci(chip);
        azx_init_chip(chip, 1);
+
+       bus = chip->bus;
+       if (status && bus) {
+               list_for_each_entry(codec, &bus->codec_list, list)
+                       if (status & (1 << codec->addr))
+                               queue_delayed_work(codec->bus->workq,
+                                                  &codec->jackpoll_work, codec->jackpoll_interval);
+       }
+
+       /* disable controller Wake Up event*/
+       azx_writew(chip, WAKEEN, azx_readw(chip, WAKEEN) &
+                       ~STATESTS_INT_MASK);
+
        return 0;
 }
 
@@ -3831,11 +3855,13 @@ static int azx_probe_continue(struct azx *chip)
 
        /* Request power well for Haswell HDA controller and codec */
        if (chip->driver_caps & AZX_DCAPS_I915_POWERWELL) {
+#ifdef CONFIG_SND_HDA_I915
                err = hda_i915_init();
                if (err < 0) {
                        snd_printk(KERN_ERR SFX "Error request power-well from i915\n");
                        goto out_free;
                }
+#endif
                hda_display_power(true);
        }
 
index 3fd2973183e2dda266a78c00a96bc5517b5bb29f..05b3e3e9108fda50f16f2472b2dad5bfbc870067 100644 (file)
@@ -194,18 +194,24 @@ u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid)
 EXPORT_SYMBOL_HDA(snd_hda_pin_sense);
 
 /**
- * snd_hda_jack_detect - query pin Presence Detect status
+ * snd_hda_jack_detect_state - query pin Presence Detect status
  * @codec: the CODEC to sense
  * @nid: the pin NID to sense
  *
- * Query and return the pin's Presence Detect status.
+ * Query and return the pin's Presence Detect status, as either
+ * HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT or HDA_JACK_PHANTOM.
  */
-int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid)
 {
-       u32 sense = snd_hda_pin_sense(codec, nid);
-       return get_jack_plug_state(sense);
+       struct hda_jack_tbl *jack = snd_hda_jack_tbl_get(codec, nid);
+       if (jack && jack->phantom_jack)
+               return HDA_JACK_PHANTOM;
+       else if (snd_hda_pin_sense(codec, nid) & AC_PINSENSE_PRESENCE)
+               return HDA_JACK_PRESENT;
+       else
+               return HDA_JACK_NOT_PRESENT;
 }
-EXPORT_SYMBOL_HDA(snd_hda_jack_detect);
+EXPORT_SYMBOL_HDA(snd_hda_jack_detect_state);
 
 /**
  * snd_hda_jack_detect_enable - enable the jack-detection
@@ -247,8 +253,8 @@ EXPORT_SYMBOL_HDA(snd_hda_jack_detect_enable);
 int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
                                 hda_nid_t gating_nid)
 {
-       struct hda_jack_tbl *gated = snd_hda_jack_tbl_get(codec, gated_nid);
-       struct hda_jack_tbl *gating = snd_hda_jack_tbl_get(codec, gating_nid);
+       struct hda_jack_tbl *gated = snd_hda_jack_tbl_new(codec, gated_nid);
+       struct hda_jack_tbl *gating = snd_hda_jack_tbl_new(codec, gating_nid);
 
        if (!gated || !gating)
                return -EINVAL;
index ec12abd452631d12b9915e0c75a4c45fdbc07c95..379420c44eefac051e512b83c67790f9f3ce99a2 100644 (file)
@@ -75,7 +75,18 @@ int snd_hda_jack_set_gating_jack(struct hda_codec *codec, hda_nid_t gated_nid,
                                 hda_nid_t gating_nid);
 
 u32 snd_hda_pin_sense(struct hda_codec *codec, hda_nid_t nid);
-int snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid);
+
+/* the jack state returned from snd_hda_jack_detect_state() */
+enum {
+       HDA_JACK_NOT_PRESENT, HDA_JACK_PRESENT, HDA_JACK_PHANTOM,
+};
+
+int snd_hda_jack_detect_state(struct hda_codec *codec, hda_nid_t nid);
+
+static inline bool snd_hda_jack_detect(struct hda_codec *codec, hda_nid_t nid)
+{
+       return snd_hda_jack_detect_state(codec, nid) != HDA_JACK_NOT_PRESENT;
+}
 
 bool is_jack_detectable(struct hda_codec *codec, hda_nid_t nid);
 
index 9760f001916d8c72add740db2314793a3e027318..a8cb22eec89e5a6c1437267cacccf8b64f1b0dd3 100644 (file)
@@ -582,6 +582,36 @@ static void print_gpio(struct snd_info_buffer *buffer,
        print_nid_array(buffer, codec, nid, &codec->nids);
 }
 
+static void print_device_list(struct snd_info_buffer *buffer,
+                           struct hda_codec *codec, hda_nid_t nid)
+{
+       int i, curr = -1;
+       u8 dev_list[AC_MAX_DEV_LIST_LEN];
+       int devlist_len;
+
+       devlist_len = snd_hda_get_devices(codec, nid, dev_list,
+                                       AC_MAX_DEV_LIST_LEN);
+       snd_iprintf(buffer, "  Devices: %d\n", devlist_len);
+       if (devlist_len <= 0)
+               return;
+
+       curr = snd_hda_codec_read(codec, nid, 0,
+                               AC_VERB_GET_DEVICE_SEL, 0);
+
+       for (i = 0; i < devlist_len; i++) {
+               if (i == curr)
+                       snd_iprintf(buffer, "    *");
+               else
+                       snd_iprintf(buffer, "     ");
+
+               snd_iprintf(buffer,
+                       "Dev %02d: PD = %d, ELDV = %d, IA = %d\n", i,
+                       !!(dev_list[i] & AC_DE_PD),
+                       !!(dev_list[i] & AC_DE_ELDV),
+                       !!(dev_list[i] & AC_DE_IA));
+       }
+}
+
 static void print_codec_info(struct snd_info_entry *entry,
                             struct snd_info_buffer *buffer)
 {
@@ -751,6 +781,9 @@ static void print_codec_info(struct snd_info_entry *entry,
                                    (wid_caps & AC_WCAP_DELAY) >>
                                    AC_WCAP_DELAY_SHIFT);
 
+               if (wid_type == AC_WID_PIN && codec->dp_mst)
+                       print_device_list(buffer, codec, nid);
+
                if (wid_caps & AC_WCAP_CONN_LIST)
                        print_conn_list(buffer, codec, nid, wid_type,
                                        conn, conn_len);
index d97f0d61a15b5779bd10a17ef3d90a184673c008..0cbdd87dde6d90ac67767ddd30331c62b8d02862 100644 (file)
@@ -32,7 +32,6 @@
 #include "hda_jack.h"
 #include "hda_generic.h"
 
-#define ENABLE_AD_STATIC_QUIRKS
 
 struct ad198x_spec {
        struct hda_gen_spec gen;
@@ -43,114 +42,8 @@ struct ad198x_spec {
        hda_nid_t eapd_nid;
 
        unsigned int beep_amp;  /* beep amp value, set via set_beep_amp() */
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
-       const struct snd_kcontrol_new *mixers[6];
-       int num_mixers;
-       const struct hda_verb *init_verbs[6];   /* initialization verbs
-                                                * don't forget NULL termination!
-                                                */
-       unsigned int num_init_verbs;
-
-       /* playback */
-       struct hda_multi_out multiout;  /* playback set-up
-                                        * max_channels, dacs must be set
-                                        * dig_out_nid and hp_nid are optional
-                                        */
-       unsigned int cur_eapd;
-       unsigned int need_dac_fix;
-
-       /* capture */
-       unsigned int num_adc_nids;
-       const hda_nid_t *adc_nids;
-       hda_nid_t dig_in_nid;           /* digital-in NID; optional */
-
-       /* capture source */
-       const struct hda_input_mux *input_mux;
-       const hda_nid_t *capsrc_nids;
-       unsigned int cur_mux[3];
-
-       /* channel model */
-       const struct hda_channel_mode *channel_mode;
-       int num_channel_mode;
-
-       /* PCM information */
-       struct hda_pcm pcm_rec[3];      /* used in alc_build_pcms() */
-
-       unsigned int spdif_route;
-
-       unsigned int jack_present: 1;
-       unsigned int inv_jack_detect: 1;/* inverted jack-detection */
-       unsigned int analog_beep: 1;    /* analog beep input present */
-       unsigned int avoid_init_slave_vol:1;
-
-#ifdef CONFIG_PM
-       struct hda_loopback_check loopback;
-#endif
-       /* for virtual master */
-       hda_nid_t vmaster_nid;
-       const char * const *slave_vols;
-       const char * const *slave_sws;
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-};
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
-/*
- * input MUX handling (common part)
- */
-static int ad198x_mux_enum_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *spec = codec->spec;
-
-       return snd_hda_input_mux_info(spec->input_mux, uinfo);
-}
-
-static int ad198x_mux_enum_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *spec = codec->spec;
-       unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
-       ucontrol->value.enumerated.item[0] = spec->cur_mux[adc_idx];
-       return 0;
-}
-
-static int ad198x_mux_enum_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *spec = codec->spec;
-       unsigned int adc_idx = snd_ctl_get_ioffidx(kcontrol, &ucontrol->id);
-
-       return snd_hda_input_mux_put(codec, spec->input_mux, ucontrol,
-                                    spec->capsrc_nids[adc_idx],
-                                    &spec->cur_mux[adc_idx]);
-}
-
-/*
- * initialization (common callbacks)
- */
-static int ad198x_init(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec = codec->spec;
-       int i;
-
-       for (i = 0; i < spec->num_init_verbs; i++)
-               snd_hda_sequence_write(codec, spec->init_verbs[i]);
-       return 0;
-}
-
-static const char * const ad_slave_pfxs[] = {
-       "Front", "Surround", "Center", "LFE", "Side",
-       "Headphone", "Mono", "Speaker", "IEC958",
-       NULL
 };
 
-static const char * const ad1988_6stack_fp_slave_pfxs[] = {
-       "Front", "Surround", "Center", "LFE", "Side", "IEC958",
-       NULL
-};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
 
 #ifdef CONFIG_SND_HDA_INPUT_BEEP
 /* additional beep mixers; the actual parameters are overwritten at build */
@@ -160,12 +53,6 @@ static const struct snd_kcontrol_new ad_beep_mixer[] = {
        { } /* end */
 };
 
-static const struct snd_kcontrol_new ad_beep2_mixer[] = {
-       HDA_CODEC_VOLUME("Digital Beep Playback Volume", 0, 0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_BEEP("Digital Beep Playback Switch", 0, 0, HDA_OUTPUT),
-       { } /* end */
-};
-
 #define set_beep_amp(spec, nid, idx, dir) \
        ((spec)->beep_amp = HDA_COMPOSE_AMP_VAL(nid, 1, idx, dir)) /* mono */
 #else
@@ -181,8 +68,7 @@ static int create_beep_ctls(struct hda_codec *codec)
        if (!spec->beep_amp)
                return 0;
 
-       knew = spec->analog_beep ? ad_beep2_mixer : ad_beep_mixer;
-       for ( ; knew->name; knew++) {
+       for (knew = ad_beep_mixer ; knew->name; knew++) {
                int err;
                struct snd_kcontrol *kctl;
                kctl = snd_ctl_new1(knew, codec);
@@ -199,268 +85,6 @@ static int create_beep_ctls(struct hda_codec *codec)
 #define create_beep_ctls(codec)                0
 #endif
 
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static int ad198x_build_controls(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec = codec->spec;
-       struct snd_kcontrol *kctl;
-       unsigned int i;
-       int err;
-
-       for (i = 0; i < spec->num_mixers; i++) {
-               err = snd_hda_add_new_ctls(codec, spec->mixers[i]);
-               if (err < 0)
-                       return err;
-       }
-       if (spec->multiout.dig_out_nid) {
-               err = snd_hda_create_spdif_out_ctls(codec,
-                                                   spec->multiout.dig_out_nid,
-                                                   spec->multiout.dig_out_nid);
-               if (err < 0)
-                       return err;
-               err = snd_hda_create_spdif_share_sw(codec,
-                                                   &spec->multiout);
-               if (err < 0)
-                       return err;
-               spec->multiout.share_spdif = 1;
-       } 
-       if (spec->dig_in_nid) {
-               err = snd_hda_create_spdif_in_ctls(codec, spec->dig_in_nid);
-               if (err < 0)
-                       return err;
-       }
-
-       /* create beep controls if needed */
-       err = create_beep_ctls(codec);
-       if (err < 0)
-               return err;
-
-       /* if we have no master control, let's create it */
-       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Volume")) {
-               unsigned int vmaster_tlv[4];
-               snd_hda_set_vmaster_tlv(codec, spec->vmaster_nid,
-                                       HDA_OUTPUT, vmaster_tlv);
-               err = __snd_hda_add_vmaster(codec, "Master Playback Volume",
-                                         vmaster_tlv,
-                                         (spec->slave_vols ?
-                                          spec->slave_vols : ad_slave_pfxs),
-                                         "Playback Volume",
-                                         !spec->avoid_init_slave_vol, NULL);
-               if (err < 0)
-                       return err;
-       }
-       if (!snd_hda_find_mixer_ctl(codec, "Master Playback Switch")) {
-               err = snd_hda_add_vmaster(codec, "Master Playback Switch",
-                                         NULL,
-                                         (spec->slave_sws ?
-                                          spec->slave_sws : ad_slave_pfxs),
-                                         "Playback Switch");
-               if (err < 0)
-                       return err;
-       }
-
-       /* assign Capture Source enums to NID */
-       kctl = snd_hda_find_mixer_ctl(codec, "Capture Source");
-       if (!kctl)
-               kctl = snd_hda_find_mixer_ctl(codec, "Input Source");
-       for (i = 0; kctl && i < kctl->count; i++) {
-               err = snd_hda_add_nid(codec, kctl, i, spec->capsrc_nids[i]);
-               if (err < 0)
-                       return err;
-       }
-
-       /* assign IEC958 enums to NID */
-       kctl = snd_hda_find_mixer_ctl(codec,
-                       SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source");
-       if (kctl) {
-               err = snd_hda_add_nid(codec, kctl, 0,
-                                     spec->multiout.dig_out_nid);
-               if (err < 0)
-                       return err;
-       }
-
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int ad198x_check_power_status(struct hda_codec *codec, hda_nid_t nid)
-{
-       struct ad198x_spec *spec = codec->spec;
-       return snd_hda_check_amp_list_power(codec, &spec->loopback, nid);
-}
-#endif
-
-/*
- * Analog playback callbacks
- */
-static int ad198x_playback_pcm_open(struct hda_pcm_stream *hinfo,
-                                   struct hda_codec *codec,
-                                   struct snd_pcm_substream *substream)
-{
-       struct ad198x_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_open(codec, &spec->multiout, substream,
-                                            hinfo);
-}
-
-static int ad198x_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-                                      struct hda_codec *codec,
-                                      unsigned int stream_tag,
-                                      unsigned int format,
-                                      struct snd_pcm_substream *substream)
-{
-       struct ad198x_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_prepare(codec, &spec->multiout, stream_tag,
-                                               format, substream);
-}
-
-static int ad198x_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
-                                      struct hda_codec *codec,
-                                      struct snd_pcm_substream *substream)
-{
-       struct ad198x_spec *spec = codec->spec;
-       return snd_hda_multi_out_analog_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Digital out
- */
-static int ad198x_dig_playback_pcm_open(struct hda_pcm_stream *hinfo,
-                                       struct hda_codec *codec,
-                                       struct snd_pcm_substream *substream)
-{
-       struct ad198x_spec *spec = codec->spec;
-       return snd_hda_multi_out_dig_open(codec, &spec->multiout);
-}
-
-static int ad198x_dig_playback_pcm_close(struct hda_pcm_stream *hinfo,
-                                        struct hda_codec *codec,
-                                        struct snd_pcm_substream *substream)
-{
-       struct ad198x_spec *spec = codec->spec;
-       return snd_hda_multi_out_dig_close(codec, &spec->multiout);
-}
-
-static int ad198x_dig_playback_pcm_prepare(struct hda_pcm_stream *hinfo,
-                                          struct hda_codec *codec,
-                                          unsigned int stream_tag,
-                                          unsigned int format,
-                                          struct snd_pcm_substream *substream)
-{
-       struct ad198x_spec *spec = codec->spec;
-       return snd_hda_multi_out_dig_prepare(codec, &spec->multiout, stream_tag,
-                                            format, substream);
-}
-
-static int ad198x_dig_playback_pcm_cleanup(struct hda_pcm_stream *hinfo,
-                                          struct hda_codec *codec,
-                                          struct snd_pcm_substream *substream)
-{
-       struct ad198x_spec *spec = codec->spec;
-       return snd_hda_multi_out_dig_cleanup(codec, &spec->multiout);
-}
-
-/*
- * Analog capture
- */
-static int ad198x_capture_pcm_prepare(struct hda_pcm_stream *hinfo,
-                                     struct hda_codec *codec,
-                                     unsigned int stream_tag,
-                                     unsigned int format,
-                                     struct snd_pcm_substream *substream)
-{
-       struct ad198x_spec *spec = codec->spec;
-       snd_hda_codec_setup_stream(codec, spec->adc_nids[substream->number],
-                                  stream_tag, 0, format);
-       return 0;
-}
-
-static int ad198x_capture_pcm_cleanup(struct hda_pcm_stream *hinfo,
-                                     struct hda_codec *codec,
-                                     struct snd_pcm_substream *substream)
-{
-       struct ad198x_spec *spec = codec->spec;
-       snd_hda_codec_cleanup_stream(codec, spec->adc_nids[substream->number]);
-       return 0;
-}
-
-/*
- */
-static const struct hda_pcm_stream ad198x_pcm_analog_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 6, /* changed later */
-       .nid = 0, /* fill later */
-       .ops = {
-               .open = ad198x_playback_pcm_open,
-               .prepare = ad198x_playback_pcm_prepare,
-               .cleanup = ad198x_playback_pcm_cleanup,
-       },
-};
-
-static const struct hda_pcm_stream ad198x_pcm_analog_capture = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0, /* fill later */
-       .ops = {
-               .prepare = ad198x_capture_pcm_prepare,
-               .cleanup = ad198x_capture_pcm_cleanup
-       },
-};
-
-static const struct hda_pcm_stream ad198x_pcm_digital_playback = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0, /* fill later */
-       .ops = {
-               .open = ad198x_dig_playback_pcm_open,
-               .close = ad198x_dig_playback_pcm_close,
-               .prepare = ad198x_dig_playback_pcm_prepare,
-               .cleanup = ad198x_dig_playback_pcm_cleanup
-       },
-};
-
-static const struct hda_pcm_stream ad198x_pcm_digital_capture = {
-       .substreams = 1,
-       .channels_min = 2,
-       .channels_max = 2,
-       /* NID is set in alc_build_pcms */
-};
-
-static int ad198x_build_pcms(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec = codec->spec;
-       struct hda_pcm *info = spec->pcm_rec;
-
-       codec->num_pcms = 1;
-       codec->pcm_info = info;
-
-       info->name = "AD198x Analog";
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_analog_playback;
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK].channels_max = spec->multiout.max_channels;
-       info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dac_nids[0];
-       info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_analog_capture;
-       info->stream[SNDRV_PCM_STREAM_CAPTURE].substreams = spec->num_adc_nids;
-       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->adc_nids[0];
-
-       if (spec->multiout.dig_out_nid) {
-               info++;
-               codec->num_pcms++;
-               codec->spdif_status_reset = 1;
-               info->name = "AD198x Digital";
-               info->pcm_type = HDA_PCM_TYPE_SPDIF;
-               info->stream[SNDRV_PCM_STREAM_PLAYBACK] = ad198x_pcm_digital_playback;
-               info->stream[SNDRV_PCM_STREAM_PLAYBACK].nid = spec->multiout.dig_out_nid;
-               if (spec->dig_in_nid) {
-                       info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad198x_pcm_digital_capture;
-                       info->stream[SNDRV_PCM_STREAM_CAPTURE].nid = spec->dig_in_nid;
-               }
-       }
-
-       return 0;
-}
-#endif /* ENABLE_AD_STATIC_QUIRKS */
 
 static void ad198x_power_eapd_write(struct hda_codec *codec, hda_nid_t front,
                                hda_nid_t hp)
@@ -507,18 +131,6 @@ static void ad198x_shutup(struct hda_codec *codec)
        ad198x_power_eapd(codec);
 }
 
-static void ad198x_free(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec = codec->spec;
-
-       if (!spec)
-               return;
-
-       snd_hda_gen_spec_free(&spec->gen);
-       kfree(spec);
-       snd_hda_detach_beep_device(codec);
-}
-
 #ifdef CONFIG_PM
 static int ad198x_suspend(struct hda_codec *codec)
 {
@@ -527,65 +139,6 @@ static int ad198x_suspend(struct hda_codec *codec)
 }
 #endif
 
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static const struct hda_codec_ops ad198x_patch_ops = {
-       .build_controls = ad198x_build_controls,
-       .build_pcms = ad198x_build_pcms,
-       .init = ad198x_init,
-       .free = ad198x_free,
-#ifdef CONFIG_PM
-       .check_power_status = ad198x_check_power_status,
-       .suspend = ad198x_suspend,
-#endif
-       .reboot_notify = ad198x_shutup,
-};
-
-
-/*
- * EAPD control
- * the private value = nid
- */
-#define ad198x_eapd_info       snd_ctl_boolean_mono_info
-
-static int ad198x_eapd_get(struct snd_kcontrol *kcontrol,
-                          struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *spec = codec->spec;
-       if (codec->inv_eapd)
-               ucontrol->value.integer.value[0] = ! spec->cur_eapd;
-       else
-               ucontrol->value.integer.value[0] = spec->cur_eapd;
-       return 0;
-}
-
-static int ad198x_eapd_put(struct snd_kcontrol *kcontrol,
-                          struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *spec = codec->spec;
-       hda_nid_t nid = kcontrol->private_value & 0xff;
-       unsigned int eapd;
-       eapd = !!ucontrol->value.integer.value[0];
-       if (codec->inv_eapd)
-               eapd = !eapd;
-       if (eapd == spec->cur_eapd)
-               return 0;
-       spec->cur_eapd = eapd;
-       snd_hda_codec_write_cache(codec, nid,
-                                 0, AC_VERB_SET_EAPD_BTLENABLE,
-                                 eapd ? 0x02 : 0x00);
-       return 1;
-}
-
-static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
-                              struct snd_ctl_elem_info *uinfo);
-static int ad198x_ch_mode_get(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol);
-static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
-                             struct snd_ctl_elem_value *ucontrol);
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
 
 /*
  * Automatic parse of I/O pins from the BIOS configuration
@@ -646,580 +199,124 @@ static int ad198x_parse_auto_config(struct hda_codec *codec)
  * AD1986A specific
  */
 
-#ifdef ENABLE_AD_STATIC_QUIRKS
-#define AD1986A_SPDIF_OUT      0x02
-#define AD1986A_FRONT_DAC      0x03
-#define AD1986A_SURR_DAC       0x04
-#define AD1986A_CLFE_DAC       0x05
-#define AD1986A_ADC            0x06
-
-static const hda_nid_t ad1986a_dac_nids[3] = {
-       AD1986A_FRONT_DAC, AD1986A_SURR_DAC, AD1986A_CLFE_DAC
-};
-static const hda_nid_t ad1986a_adc_nids[1] = { AD1986A_ADC };
-static const hda_nid_t ad1986a_capsrc_nids[1] = { 0x12 };
-
-static const struct hda_input_mux ad1986a_capture_source = {
-       .num_items = 7,
-       .items = {
-               { "Mic", 0x0 },
-               { "CD", 0x1 },
-               { "Aux", 0x3 },
-               { "Line", 0x4 },
-               { "Mix", 0x5 },
-               { "Mono", 0x6 },
-               { "Phone", 0x7 },
-       },
-};
-
-
-static const struct hda_bind_ctls ad1986a_bind_pcm_vol = {
-       .ops = &snd_hda_bind_vol,
-       .values = {
-               HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
-               HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
-               HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
-               0
-       },
-};
+static int alloc_ad_spec(struct hda_codec *codec)
+{
+       struct ad198x_spec *spec;
 
-static const struct hda_bind_ctls ad1986a_bind_pcm_sw = {
-       .ops = &snd_hda_bind_sw,
-       .values = {
-               HDA_COMPOSE_AMP_VAL(AD1986A_FRONT_DAC, 3, 0, HDA_OUTPUT),
-               HDA_COMPOSE_AMP_VAL(AD1986A_SURR_DAC, 3, 0, HDA_OUTPUT),
-               HDA_COMPOSE_AMP_VAL(AD1986A_CLFE_DAC, 3, 0, HDA_OUTPUT),
-               0
-       },
-};
+       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
+       if (!spec)
+               return -ENOMEM;
+       codec->spec = spec;
+       snd_hda_gen_spec_init(&spec->gen);
+       return 0;
+}
 
 /*
- * mixers
+ * AD1986A fixup codes
  */
-static const struct snd_kcontrol_new ad1986a_mixers[] = {
-       /*
-        * bind volumes/mutes of 3 DACs as a single PCM control for simplicity
-        */
-       HDA_BIND_VOL("PCM Playback Volume", &ad1986a_bind_pcm_vol),
-       HDA_BIND_SW("PCM Playback Switch", &ad1986a_bind_pcm_sw),
-       HDA_CODEC_VOLUME("Front Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Surround Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Surround Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x1d, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x1d, 2, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x1d, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x1d, 2, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x1a, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x1a, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
-       },
-       HDA_CODEC_MUTE("Stereo Downmix Switch", 0x09, 0x0, HDA_OUTPUT),
-       { } /* end */
-};
-
-/* additional mixers for 3stack mode */
-static const struct snd_kcontrol_new ad1986a_3st_mixers[] = {
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Channel Mode",
-               .info = ad198x_ch_mode_info,
-               .get = ad198x_ch_mode_get,
-               .put = ad198x_ch_mode_put,
-       },
-       { } /* end */
-};
 
-/* laptop model - 2ch only */
-static const hda_nid_t ad1986a_laptop_dac_nids[1] = { AD1986A_FRONT_DAC };
+/* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
+static void ad_fixup_inv_jack_detect(struct hda_codec *codec,
+                                    const struct hda_fixup *fix, int action)
+{
+       if (action == HDA_FIXUP_ACT_PRE_PROBE)
+               codec->inv_jack_detect = 1;
+}
 
-/* master controls both pins 0x1a and 0x1b */
-static const struct hda_bind_ctls ad1986a_laptop_master_vol = {
-       .ops = &snd_hda_bind_vol,
-       .values = {
-               HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
-               HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
-               0,
-       },
+enum {
+       AD1986A_FIXUP_INV_JACK_DETECT,
+       AD1986A_FIXUP_ULTRA,
+       AD1986A_FIXUP_SAMSUNG,
+       AD1986A_FIXUP_3STACK,
+       AD1986A_FIXUP_LAPTOP,
+       AD1986A_FIXUP_LAPTOP_IMIC,
 };
 
-static const struct hda_bind_ctls ad1986a_laptop_master_sw = {
-       .ops = &snd_hda_bind_sw,
-       .values = {
-               HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
-               HDA_COMPOSE_AMP_VAL(0x1b, 3, 0, HDA_OUTPUT),
-               0,
+static const struct hda_fixup ad1986a_fixups[] = {
+       [AD1986A_FIXUP_INV_JACK_DETECT] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = ad_fixup_inv_jack_detect,
        },
-};
-
-static const struct snd_kcontrol_new ad1986a_laptop_mixers[] = {
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
-       HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
-       HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x15, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x15, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Line Playback Volume", 0x17, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Line Playback Switch", 0x17, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Aux Playback Volume", 0x16, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Aux Playback Switch", 0x16, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
-       /* 
-          HDA_CODEC_VOLUME("Mono Playback Volume", 0x1e, 0x0, HDA_OUTPUT),
-          HDA_CODEC_MUTE("Mono Playback Switch", 0x1e, 0x0, HDA_OUTPUT), */
-       HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
+       [AD1986A_FIXUP_ULTRA] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x1b, 0x90170110 }, /* speaker */
+                       { 0x1d, 0x90a7013e }, /* int mic */
+                       {}
+               },
        },
-       { } /* end */
-};
-
-/* laptop-eapd model - 2ch only */
-
-static const struct hda_input_mux ad1986a_laptop_eapd_capture_source = {
-       .num_items = 3,
-       .items = {
-               { "Mic", 0x0 },
-               { "Internal Mic", 0x4 },
-               { "Mix", 0x5 },
+       [AD1986A_FIXUP_SAMSUNG] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x1b, 0x90170110 }, /* speaker */
+                       { 0x1d, 0x90a7013e }, /* int mic */
+                       { 0x20, 0x411111f0 }, /* N/A */
+                       { 0x24, 0x411111f0 }, /* N/A */
+                       {}
+               },
        },
-};
-
-static const struct hda_input_mux ad1986a_automic_capture_source = {
-       .num_items = 2,
-       .items = {
-               { "Mic", 0x0 },
-               { "Mix", 0x5 },
+       [AD1986A_FIXUP_3STACK] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x1a, 0x02214021 }, /* headphone */
+                       { 0x1b, 0x01014011 }, /* front */
+                       { 0x1c, 0x01013012 }, /* surround */
+                       { 0x1d, 0x01019015 }, /* clfe */
+                       { 0x1e, 0x411111f0 }, /* N/A */
+                       { 0x1f, 0x02a190f0 }, /* mic */
+                       { 0x20, 0x018130f0 }, /* line-in */
+                       {}
+               },
        },
-};
-
-static const struct snd_kcontrol_new ad1986a_laptop_master_mixers[] = {
-       HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
-       HDA_BIND_SW("Master Playback Switch", &ad1986a_laptop_master_sw),
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1986a_laptop_eapd_mixers[] = {
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x03, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x03, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x0f, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x12, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
+       [AD1986A_FIXUP_LAPTOP] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x1a, 0x02214021 }, /* headphone */
+                       { 0x1b, 0x90170110 }, /* speaker */
+                       { 0x1c, 0x411111f0 }, /* N/A */
+                       { 0x1d, 0x411111f0 }, /* N/A */
+                       { 0x1e, 0x411111f0 }, /* N/A */
+                       { 0x1f, 0x02a191f0 }, /* mic */
+                       { 0x20, 0x411111f0 }, /* N/A */
+                       {}
+               },
        },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "External Amplifier",
-               .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
-               .info = ad198x_eapd_info,
-               .get = ad198x_eapd_get,
-               .put = ad198x_eapd_put,
-               .private_value = 0x1b, /* port-D */
+       [AD1986A_FIXUP_LAPTOP_IMIC] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x1d, 0x90a7013e }, /* int mic */
+                       {}
+               },
+               .chained_before = 1,
+               .chain_id = AD1986A_FIXUP_LAPTOP,
        },
-       { } /* end */
 };
 
-static const struct snd_kcontrol_new ad1986a_laptop_intmic_mixers[] = {
-       HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x17, 0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x17, 0, HDA_OUTPUT),
-       { } /* end */
+static const struct snd_pci_quirk ad1986a_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_FIXUP_LAPTOP_IMIC),
+       SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8100, "ASUS P5", AD1986A_FIXUP_3STACK),
+       SND_PCI_QUIRK_MASK(0x1043, 0xff00, 0x8200, "ASUS M2", AD1986A_FIXUP_3STACK),
+       SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_FIXUP_3STACK),
+       SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_FIXUP_LAPTOP),
+       SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_FIXUP_SAMSUNG),
+       SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_FIXUP_ULTRA),
+       SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT),
+       SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_FIXUP_3STACK),
+       SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_FIXUP_3STACK),
+       {}
 };
 
-/* re-connect the mic boost input according to the jack sensing */
-static void ad1986a_automic(struct hda_codec *codec)
-{
-       unsigned int present;
-       present = snd_hda_jack_detect(codec, 0x1f);
-       /* 0 = 0x1f, 2 = 0x1d, 4 = mixed */
-       snd_hda_codec_write(codec, 0x0f, 0, AC_VERB_SET_CONNECT_SEL,
-                           present ? 0 : 2);
-}
-
-#define AD1986A_MIC_EVENT              0x36
-
-static void ad1986a_automic_unsol_event(struct hda_codec *codec,
-                                           unsigned int res)
-{
-       if ((res >> 26) != AD1986A_MIC_EVENT)
-               return;
-       ad1986a_automic(codec);
-}
-
-static int ad1986a_automic_init(struct hda_codec *codec)
-{
-       ad198x_init(codec);
-       ad1986a_automic(codec);
-       return 0;
-}
-
-/* laptop-automute - 2ch only */
-
-static void ad1986a_update_hp(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec = codec->spec;
-       unsigned int mute;
-
-       if (spec->jack_present)
-               mute = HDA_AMP_MUTE; /* mute internal speaker */
-       else
-               /* unmute internal speaker if necessary */
-               mute = snd_hda_codec_amp_read(codec, 0x1a, 0, HDA_OUTPUT, 0);
-       snd_hda_codec_amp_stereo(codec, 0x1b, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, mute);
-}
-
-static void ad1986a_hp_automute(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec = codec->spec;
-
-       spec->jack_present = snd_hda_jack_detect(codec, 0x1a);
-       if (spec->inv_jack_detect)
-               spec->jack_present = !spec->jack_present;
-       ad1986a_update_hp(codec);
-}
-
-#define AD1986A_HP_EVENT               0x37
-
-static void ad1986a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       if ((res >> 26) != AD1986A_HP_EVENT)
-               return;
-       ad1986a_hp_automute(codec);
-}
-
-static int ad1986a_hp_init(struct hda_codec *codec)
-{
-       ad198x_init(codec);
-       ad1986a_hp_automute(codec);
-       return 0;
-}
-
-/* bind hp and internal speaker mute (with plug check) */
-static int ad1986a_hp_master_sw_put(struct snd_kcontrol *kcontrol,
-                                   struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       int change = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
-       if (change)
-               ad1986a_update_hp(codec);
-       return change;
-}
-
-static const struct snd_kcontrol_new ad1986a_automute_master_mixers[] = {
-       HDA_BIND_VOL("Master Playback Volume", &ad1986a_laptop_master_vol),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Switch",
-               .subdevice = HDA_SUBDEV_AMP_FLAG,
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
-               .put = ad1986a_hp_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x1a, 3, 0, HDA_OUTPUT),
-       },
-       { } /* end */
-};
-
-
-/*
- * initialization verbs
- */
-static const struct hda_verb ad1986a_init_verbs[] = {
-       /* Front, Surround, CLFE DAC; mute as default */
-       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       /* Downmix - off */
-       {0x09, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       /* HP, Line-Out, Surround, CLFE selectors */
-       {0x0a, AC_VERB_SET_CONNECT_SEL, 0x0},
-       {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0},
-       {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
-       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Mono selector */
-       {0x0e, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Mic selector: Mic 1/2 pin */
-       {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Line-in selector: Line-in */
-       {0x10, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Mic 1/2 swap */
-       {0x11, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Record selector: mic */
-       {0x12, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Mic, Phone, CD, Aux, Line-In amp; mute as default */
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       /* PC beep */
-       {0x18, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* HP, Line-Out, Surround, CLFE, Mono pins; mute as default */
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       /* HP Pin */
-       {0x1a, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
-       /* Front, Surround, CLFE Pins */
-       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-       /* Mono Pin */
-       {0x1e, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-       /* Mic Pin */
-       {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
-       /* Line, Aux, CD, Beep-In Pin */
-       {0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
-       {0x21, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
-       {0x22, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
-       {0x23, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
-       {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
-       { } /* end */
-};
-
-static const struct hda_verb ad1986a_ch2_init[] = {
-       /* Surround out -> Line In */
-       { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
-       /* Line-in selectors */
-       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x1 },
-       /* CLFE -> Mic in */
-       { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
-       /* Mic selector, mix C/LFE (backmic) and Mic (frontmic) */
-       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
-       { } /* end */
-};
-
-static const struct hda_verb ad1986a_ch4_init[] = {
-       /* Surround out -> Surround */
-       { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
-       /* CLFE -> Mic in */
-       { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
-       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x4 },
-       { } /* end */
-};
-
-static const struct hda_verb ad1986a_ch6_init[] = {
-       /* Surround out -> Surround out */
-       { 0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-       { 0x10, AC_VERB_SET_CONNECT_SEL, 0x0 },
-       /* CLFE -> CLFE */
-       { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x0 },
-       { } /* end */
-};
-
-static const struct hda_channel_mode ad1986a_modes[3] = {
-       { 2, ad1986a_ch2_init },
-       { 4, ad1986a_ch4_init },
-       { 6, ad1986a_ch6_init },
-};
-
-/* eapd initialization */
-static const struct hda_verb ad1986a_eapd_init_verbs[] = {
-       {0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
-       {}
-};
-
-static const struct hda_verb ad1986a_automic_verbs[] = {
-       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x1f, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       /*{0x20, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},*/
-       {0x0f, AC_VERB_SET_CONNECT_SEL, 0x0},
-       {0x1f, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_MIC_EVENT},
-       {}
-};
-
-/* Ultra initialization */
-static const struct hda_verb ad1986a_ultra_init[] = {
-       /* eapd initialization */
-       { 0x1b, AC_VERB_SET_EAPD_BTLENABLE, 0x00 },
-       /* CLFE -> Mic in */
-       { 0x0f, AC_VERB_SET_CONNECT_SEL, 0x2 },
-       { 0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
-       { 0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080 },
-       { } /* end */
-};
-
-/* pin sensing on HP jack */
-static const struct hda_verb ad1986a_hp_init_verbs[] = {
-       {0x1a, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1986A_HP_EVENT},
-       {}
-};
-
-static void ad1986a_samsung_p50_unsol_event(struct hda_codec *codec,
-                                           unsigned int res)
-{
-       switch (res >> 26) {
-       case AD1986A_HP_EVENT:
-               ad1986a_hp_automute(codec);
-               break;
-       case AD1986A_MIC_EVENT:
-               ad1986a_automic(codec);
-               break;
-       }
-}
-
-static int ad1986a_samsung_p50_init(struct hda_codec *codec)
-{
-       ad198x_init(codec);
-       ad1986a_hp_automute(codec);
-       ad1986a_automic(codec);
-       return 0;
-}
-
-
-/* models */
-enum {
-       AD1986A_AUTO,
-       AD1986A_6STACK,
-       AD1986A_3STACK,
-       AD1986A_LAPTOP,
-       AD1986A_LAPTOP_EAPD,
-       AD1986A_LAPTOP_AUTOMUTE,
-       AD1986A_ULTRA,
-       AD1986A_SAMSUNG,
-       AD1986A_SAMSUNG_P50,
-       AD1986A_MODELS
-};
-
-static const char * const ad1986a_models[AD1986A_MODELS] = {
-       [AD1986A_AUTO]          = "auto",
-       [AD1986A_6STACK]        = "6stack",
-       [AD1986A_3STACK]        = "3stack",
-       [AD1986A_LAPTOP]        = "laptop",
-       [AD1986A_LAPTOP_EAPD]   = "laptop-eapd",
-       [AD1986A_LAPTOP_AUTOMUTE] = "laptop-automute",
-       [AD1986A_ULTRA]         = "ultra",
-       [AD1986A_SAMSUNG]       = "samsung",
-       [AD1986A_SAMSUNG_P50]   = "samsung-p50",
-};
-
-static const struct snd_pci_quirk ad1986a_cfg_tbl[] = {
-       SND_PCI_QUIRK(0x103c, 0x30af, "HP B2800", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x1043, 0x1153, "ASUS M9", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x1043, 0x11f7, "ASUS U5A", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x1043, 0x1213, "ASUS A6J", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x1043, 0x1263, "ASUS U5F", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x1043, 0x1297, "ASUS Z62F", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x1043, 0x12b3, "ASUS V1j", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x1043, 0x1302, "ASUS W3j", AD1986A_LAPTOP_EAPD),
-       SND_PCI_QUIRK(0x1043, 0x1443, "ASUS VX1", AD1986A_LAPTOP),
-       SND_PCI_QUIRK(0x1043, 0x1447, "ASUS A8J", AD1986A_3STACK),
-       SND_PCI_QUIRK(0x1043, 0x817f, "ASUS P5", AD1986A_3STACK),
-       SND_PCI_QUIRK(0x1043, 0x818f, "ASUS P5", AD1986A_LAPTOP),
-       SND_PCI_QUIRK(0x1043, 0x81b3, "ASUS P5", AD1986A_3STACK),
-       SND_PCI_QUIRK(0x1043, 0x81cb, "ASUS M2N", AD1986A_3STACK),
-       SND_PCI_QUIRK(0x1043, 0x8234, "ASUS M2N", AD1986A_3STACK),
-       SND_PCI_QUIRK(0x10de, 0xcb84, "ASUS A8N-VM", AD1986A_3STACK),
-       SND_PCI_QUIRK(0x1179, 0xff40, "Toshiba Satellite L40-10Q", AD1986A_3STACK),
-       SND_PCI_QUIRK(0x144d, 0xb03c, "Samsung R55", AD1986A_3STACK),
-       SND_PCI_QUIRK(0x144d, 0xc01e, "FSC V2060", AD1986A_LAPTOP),
-       SND_PCI_QUIRK(0x144d, 0xc024, "Samsung P50", AD1986A_SAMSUNG_P50),
-       SND_PCI_QUIRK(0x144d, 0xc027, "Samsung Q1", AD1986A_ULTRA),
-       SND_PCI_QUIRK_MASK(0x144d, 0xff00, 0xc000, "Samsung", AD1986A_SAMSUNG),
-       SND_PCI_QUIRK(0x144d, 0xc504, "Samsung Q35", AD1986A_3STACK),
-       SND_PCI_QUIRK(0x17aa, 0x1011, "Lenovo M55", AD1986A_LAPTOP),
-       SND_PCI_QUIRK(0x17aa, 0x1017, "Lenovo A60", AD1986A_3STACK),
-       SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_LAPTOP_AUTOMUTE),
-       SND_PCI_QUIRK(0x17c0, 0x2017, "Samsung M50", AD1986A_LAPTOP),
-       {}
-};
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1986a_loopbacks[] = {
-       { 0x13, HDA_OUTPUT, 0 }, /* Mic */
-       { 0x14, HDA_OUTPUT, 0 }, /* Phone */
-       { 0x15, HDA_OUTPUT, 0 }, /* CD */
-       { 0x16, HDA_OUTPUT, 0 }, /* Aux */
-       { 0x17, HDA_OUTPUT, 0 }, /* Line */
-       { } /* end */
-};
-#endif
-
-static int is_jack_available(struct hda_codec *codec, hda_nid_t nid)
-{
-       unsigned int conf = snd_hda_codec_get_pincfg(codec, nid);
-       return get_defcfg_connect(conf) != AC_JACK_PORT_NONE;
-}
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-static int alloc_ad_spec(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec;
-
-       spec = kzalloc(sizeof(*spec), GFP_KERNEL);
-       if (!spec)
-               return -ENOMEM;
-       codec->spec = spec;
-       snd_hda_gen_spec_init(&spec->gen);
-       return 0;
-}
-
-/*
- * AD1986A fixup codes
- */
-
-/* Lenovo N100 seems to report the reversed bit for HP jack-sensing */
-static void ad_fixup_inv_jack_detect(struct hda_codec *codec,
-                                    const struct hda_fixup *fix, int action)
-{
-       if (action == HDA_FIXUP_ACT_PRE_PROBE)
-               codec->inv_jack_detect = 1;
-}
-
-enum {
-       AD1986A_FIXUP_INV_JACK_DETECT,
-};
-
-static const struct hda_fixup ad1986a_fixups[] = {
-       [AD1986A_FIXUP_INV_JACK_DETECT] = {
-               .type = HDA_FIXUP_FUNC,
-               .v.func = ad_fixup_inv_jack_detect,
-       },
-};
-
-static const struct snd_pci_quirk ad1986a_fixup_tbl[] = {
-       SND_PCI_QUIRK(0x17aa, 0x2066, "Lenovo N100", AD1986A_FIXUP_INV_JACK_DETECT),
+static const struct hda_model_fixup ad1986a_fixup_models[] = {
+       { .id = AD1986A_FIXUP_3STACK, .name = "3stack" },
+       { .id = AD1986A_FIXUP_LAPTOP, .name = "laptop" },
+       { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-imic" },
+       { .id = AD1986A_FIXUP_LAPTOP_IMIC, .name = "laptop-eapd" }, /* alias */
        {}
 };
 
 /*
  */
-static int ad1986a_parse_auto_config(struct hda_codec *codec)
+static int patch_ad1986a(struct hda_codec *codec)
 {
        int err;
        struct ad198x_spec *spec;
@@ -1244,7 +341,8 @@ static int ad1986a_parse_auto_config(struct hda_codec *codec)
         */
        spec->gen.multiout.no_share_stream = 1;
 
-       snd_hda_pick_fixup(codec, NULL, ad1986a_fixup_tbl, ad1986a_fixups);
+       snd_hda_pick_fixup(codec, ad1986a_fixup_models, ad1986a_fixup_tbl,
+                          ad1986a_fixups);
        snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
 
        err = ad198x_parse_auto_config(codec);
@@ -1258,330 +356,11 @@ static int ad1986a_parse_auto_config(struct hda_codec *codec)
        return 0;
 }
 
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static int patch_ad1986a(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec;
-       int err, board_config;
-
-       board_config = snd_hda_check_board_config(codec, AD1986A_MODELS,
-                                                 ad1986a_models,
-                                                 ad1986a_cfg_tbl);
-       if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
-                      codec->chip_name);
-               board_config = AD1986A_AUTO;
-       }
-
-       if (board_config == AD1986A_AUTO)
-               return ad1986a_parse_auto_config(codec);
-
-       err = alloc_ad_spec(codec);
-       if (err < 0)
-               return err;
-       spec = codec->spec;
-
-       err = snd_hda_attach_beep_device(codec, 0x19);
-       if (err < 0) {
-               ad198x_free(codec);
-               return err;
-       }
-       set_beep_amp(spec, 0x18, 0, HDA_OUTPUT);
-
-       spec->multiout.max_channels = 6;
-       spec->multiout.num_dacs = ARRAY_SIZE(ad1986a_dac_nids);
-       spec->multiout.dac_nids = ad1986a_dac_nids;
-       spec->multiout.dig_out_nid = AD1986A_SPDIF_OUT;
-       spec->num_adc_nids = 1;
-       spec->adc_nids = ad1986a_adc_nids;
-       spec->capsrc_nids = ad1986a_capsrc_nids;
-       spec->input_mux = &ad1986a_capture_source;
-       spec->num_mixers = 1;
-       spec->mixers[0] = ad1986a_mixers;
-       spec->num_init_verbs = 1;
-       spec->init_verbs[0] = ad1986a_init_verbs;
-#ifdef CONFIG_PM
-       spec->loopback.amplist = ad1986a_loopbacks;
-#endif
-       spec->vmaster_nid = 0x1b;
-       codec->inv_eapd = 1; /* AD1986A has the inverted EAPD implementation */
-
-       codec->patch_ops = ad198x_patch_ops;
-
-       /* override some parameters */
-       switch (board_config) {
-       case AD1986A_3STACK:
-               spec->num_mixers = 2;
-               spec->mixers[1] = ad1986a_3st_mixers;
-               spec->num_init_verbs = 2;
-               spec->init_verbs[1] = ad1986a_ch2_init;
-               spec->channel_mode = ad1986a_modes;
-               spec->num_channel_mode = ARRAY_SIZE(ad1986a_modes);
-               spec->need_dac_fix = 1;
-               spec->multiout.max_channels = 2;
-               spec->multiout.num_dacs = 1;
-               break;
-       case AD1986A_LAPTOP:
-               spec->mixers[0] = ad1986a_laptop_mixers;
-               spec->multiout.max_channels = 2;
-               spec->multiout.num_dacs = 1;
-               spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
-               break;
-       case AD1986A_LAPTOP_EAPD:
-               spec->num_mixers = 3;
-               spec->mixers[0] = ad1986a_laptop_master_mixers;
-               spec->mixers[1] = ad1986a_laptop_eapd_mixers;
-               spec->mixers[2] = ad1986a_laptop_intmic_mixers;
-               spec->num_init_verbs = 2;
-               spec->init_verbs[1] = ad1986a_eapd_init_verbs;
-               spec->multiout.max_channels = 2;
-               spec->multiout.num_dacs = 1;
-               spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
-               if (!is_jack_available(codec, 0x25))
-                       spec->multiout.dig_out_nid = 0;
-               spec->input_mux = &ad1986a_laptop_eapd_capture_source;
-               break;
-       case AD1986A_SAMSUNG:
-               spec->num_mixers = 2;
-               spec->mixers[0] = ad1986a_laptop_master_mixers;
-               spec->mixers[1] = ad1986a_laptop_eapd_mixers;
-               spec->num_init_verbs = 3;
-               spec->init_verbs[1] = ad1986a_eapd_init_verbs;
-               spec->init_verbs[2] = ad1986a_automic_verbs;
-               spec->multiout.max_channels = 2;
-               spec->multiout.num_dacs = 1;
-               spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
-               if (!is_jack_available(codec, 0x25))
-                       spec->multiout.dig_out_nid = 0;
-               spec->input_mux = &ad1986a_automic_capture_source;
-               codec->patch_ops.unsol_event = ad1986a_automic_unsol_event;
-               codec->patch_ops.init = ad1986a_automic_init;
-               break;
-       case AD1986A_SAMSUNG_P50:
-               spec->num_mixers = 2;
-               spec->mixers[0] = ad1986a_automute_master_mixers;
-               spec->mixers[1] = ad1986a_laptop_eapd_mixers;
-               spec->num_init_verbs = 4;
-               spec->init_verbs[1] = ad1986a_eapd_init_verbs;
-               spec->init_verbs[2] = ad1986a_automic_verbs;
-               spec->init_verbs[3] = ad1986a_hp_init_verbs;
-               spec->multiout.max_channels = 2;
-               spec->multiout.num_dacs = 1;
-               spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
-               if (!is_jack_available(codec, 0x25))
-                       spec->multiout.dig_out_nid = 0;
-               spec->input_mux = &ad1986a_automic_capture_source;
-               codec->patch_ops.unsol_event = ad1986a_samsung_p50_unsol_event;
-               codec->patch_ops.init = ad1986a_samsung_p50_init;
-               break;
-       case AD1986A_LAPTOP_AUTOMUTE:
-               spec->num_mixers = 3;
-               spec->mixers[0] = ad1986a_automute_master_mixers;
-               spec->mixers[1] = ad1986a_laptop_eapd_mixers;
-               spec->mixers[2] = ad1986a_laptop_intmic_mixers;
-               spec->num_init_verbs = 3;
-               spec->init_verbs[1] = ad1986a_eapd_init_verbs;
-               spec->init_verbs[2] = ad1986a_hp_init_verbs;
-               spec->multiout.max_channels = 2;
-               spec->multiout.num_dacs = 1;
-               spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
-               if (!is_jack_available(codec, 0x25))
-                       spec->multiout.dig_out_nid = 0;
-               spec->input_mux = &ad1986a_laptop_eapd_capture_source;
-               codec->patch_ops.unsol_event = ad1986a_hp_unsol_event;
-               codec->patch_ops.init = ad1986a_hp_init;
-               /* Lenovo N100 seems to report the reversed bit
-                * for HP jack-sensing
-                */
-               spec->inv_jack_detect = 1;
-               break;
-       case AD1986A_ULTRA:
-               spec->mixers[0] = ad1986a_laptop_eapd_mixers;
-               spec->num_init_verbs = 2;
-               spec->init_verbs[1] = ad1986a_ultra_init;
-               spec->multiout.max_channels = 2;
-               spec->multiout.num_dacs = 1;
-               spec->multiout.dac_nids = ad1986a_laptop_dac_nids;
-               spec->multiout.dig_out_nid = 0;
-               break;
-       }
-
-       /* AD1986A has a hardware problem that it can't share a stream
-        * with multiple output pins.  The copy of front to surrounds
-        * causes noisy or silent outputs at a certain timing, e.g.
-        * changing the volume.
-        * So, let's disable the shared stream.
-        */
-       spec->multiout.no_share_stream = 1;
-
-       codec->no_trigger_sense = 1;
-       codec->no_sticky_stream = 1;
-
-       return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1986a  ad1986a_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
 
 /*
  * AD1983 specific
  */
 
-#ifdef ENABLE_AD_STATIC_QUIRKS
-#define AD1983_SPDIF_OUT       0x02
-#define AD1983_DAC             0x03
-#define AD1983_ADC             0x04
-
-static const hda_nid_t ad1983_dac_nids[1] = { AD1983_DAC };
-static const hda_nid_t ad1983_adc_nids[1] = { AD1983_ADC };
-static const hda_nid_t ad1983_capsrc_nids[1] = { 0x15 };
-
-static const struct hda_input_mux ad1983_capture_source = {
-       .num_items = 4,
-       .items = {
-               { "Mic", 0x0 },
-               { "Line", 0x1 },
-               { "Mix", 0x2 },
-               { "Mix Mono", 0x3 },
-       },
-};
-
-/*
- * SPDIF playback route
- */
-static int ad1983_spdif_route_info(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_info *uinfo)
-{
-       static const char * const texts[] = { "PCM", "ADC" };
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-       uinfo->count = 1;
-       uinfo->value.enumerated.items = 2;
-       if (uinfo->value.enumerated.item > 1)
-               uinfo->value.enumerated.item = 1;
-       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-       return 0;
-}
-
-static int ad1983_spdif_route_get(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *spec = codec->spec;
-
-       ucontrol->value.enumerated.item[0] = spec->spdif_route;
-       return 0;
-}
-
-static int ad1983_spdif_route_put(struct snd_kcontrol *kcontrol, struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *spec = codec->spec;
-
-       if (ucontrol->value.enumerated.item[0] > 1)
-               return -EINVAL;
-       if (spec->spdif_route != ucontrol->value.enumerated.item[0]) {
-               spec->spdif_route = ucontrol->value.enumerated.item[0];
-               snd_hda_codec_write_cache(codec, spec->multiout.dig_out_nid, 0,
-                                         AC_VERB_SET_CONNECT_SEL,
-                                         spec->spdif_route);
-               return 1;
-       }
-       return 0;
-}
-
-static const struct snd_kcontrol_new ad1983_mixers[] = {
-       HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
-       },
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
-               .info = ad1983_spdif_route_info,
-               .get = ad1983_spdif_route_get,
-               .put = ad1983_spdif_route_put,
-       },
-       { } /* end */
-};
-
-static const struct hda_verb ad1983_init_verbs[] = {
-       /* Front, HP, Mono; mute as default */
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       /* Beep, PCM, Mic, Line-In: mute */
-       {0x10, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       /* Front, HP selectors; from Mix */
-       {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
-       {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
-       /* Mono selector; from Mix */
-       {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
-       /* Mic selector; Mic */
-       {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Line-in selector: Line-in */
-       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Mic boost: 0dB */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       /* Record selector: mic */
-       {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       /* SPDIF route: PCM */
-       {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Front Pin */
-       {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-       /* HP Pin */
-       {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
-       /* Mono Pin */
-       {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-       /* Mic Pin */
-       {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
-       /* Line Pin */
-       {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
-       { } /* end */
-};
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1983_loopbacks[] = {
-       { 0x12, HDA_OUTPUT, 0 }, /* Mic */
-       { 0x13, HDA_OUTPUT, 0 }, /* Line */
-       { } /* end */
-};
-#endif
-
-/* models */
-enum {
-       AD1983_AUTO,
-       AD1983_BASIC,
-       AD1983_MODELS
-};
-
-static const char * const ad1983_models[AD1983_MODELS] = {
-       [AD1983_AUTO]           = "auto",
-       [AD1983_BASIC]          = "basic",
-};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-
 /*
  * SPDIF mux control for AD1983 auto-parser
  */
@@ -1656,7 +435,7 @@ static int ad1983_add_spdif_mux_ctl(struct hda_codec *codec)
        return 0;
 }
 
-static int ad1983_parse_auto_config(struct hda_codec *codec)
+static int patch_ad1983(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
        int err;
@@ -1681,432 +460,11 @@ static int ad1983_parse_auto_config(struct hda_codec *codec)
        return err;
 }
 
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static int patch_ad1983(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec;
-       int board_config;
-       int err;
-
-       board_config = snd_hda_check_board_config(codec, AD1983_MODELS,
-                                                 ad1983_models, NULL);
-       if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
-                      codec->chip_name);
-               board_config = AD1983_AUTO;
-       }
-
-       if (board_config == AD1983_AUTO)
-               return ad1983_parse_auto_config(codec);
-
-       err = alloc_ad_spec(codec);
-       if (err < 0)
-               return err;
-       spec = codec->spec;
-
-       err = snd_hda_attach_beep_device(codec, 0x10);
-       if (err < 0) {
-               ad198x_free(codec);
-               return err;
-       }
-       set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
-       spec->multiout.max_channels = 2;
-       spec->multiout.num_dacs = ARRAY_SIZE(ad1983_dac_nids);
-       spec->multiout.dac_nids = ad1983_dac_nids;
-       spec->multiout.dig_out_nid = AD1983_SPDIF_OUT;
-       spec->num_adc_nids = 1;
-       spec->adc_nids = ad1983_adc_nids;
-       spec->capsrc_nids = ad1983_capsrc_nids;
-       spec->input_mux = &ad1983_capture_source;
-       spec->num_mixers = 1;
-       spec->mixers[0] = ad1983_mixers;
-       spec->num_init_verbs = 1;
-       spec->init_verbs[0] = ad1983_init_verbs;
-       spec->spdif_route = 0;
-#ifdef CONFIG_PM
-       spec->loopback.amplist = ad1983_loopbacks;
-#endif
-       spec->vmaster_nid = 0x05;
-
-       codec->patch_ops = ad198x_patch_ops;
-
-       codec->no_trigger_sense = 1;
-       codec->no_sticky_stream = 1;
-
-       return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1983   ad1983_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
 
 /*
  * AD1981 HD specific
  */
 
-#ifdef ENABLE_AD_STATIC_QUIRKS
-#define AD1981_SPDIF_OUT       0x02
-#define AD1981_DAC             0x03
-#define AD1981_ADC             0x04
-
-static const hda_nid_t ad1981_dac_nids[1] = { AD1981_DAC };
-static const hda_nid_t ad1981_adc_nids[1] = { AD1981_ADC };
-static const hda_nid_t ad1981_capsrc_nids[1] = { 0x15 };
-
-/* 0x0c, 0x09, 0x0e, 0x0f, 0x19, 0x05, 0x18, 0x17 */
-static const struct hda_input_mux ad1981_capture_source = {
-       .num_items = 7,
-       .items = {
-               { "Front Mic", 0x0 },
-               { "Line", 0x1 },
-               { "Mix", 0x2 },
-               { "Mix Mono", 0x3 },
-               { "CD", 0x4 },
-               { "Mic", 0x6 },
-               { "Aux", 0x7 },
-       },
-};
-
-static const struct snd_kcontrol_new ad1981_mixers[] = {
-       HDA_CODEC_VOLUME("Front Playback Volume", 0x05, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x05, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x06, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x06, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x07, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x07, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Line Playback Volume", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Line Playback Switch", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Aux Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Aux Playback Switch", 0x1b, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
-       },
-       /* identical with AD1983 */
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
-               .info = ad1983_spdif_route_info,
-               .get = ad1983_spdif_route_get,
-               .put = ad1983_spdif_route_put,
-       },
-       { } /* end */
-};
-
-static const struct hda_verb ad1981_init_verbs[] = {
-       /* Front, HP, Mono; mute as default */
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x06, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       /* Beep, PCM, Front Mic, Line, Rear Mic, Aux, CD-In: mute */
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       /* Front, HP selectors; from Mix */
-       {0x05, AC_VERB_SET_CONNECT_SEL, 0x01},
-       {0x06, AC_VERB_SET_CONNECT_SEL, 0x01},
-       /* Mono selector; from Mix */
-       {0x0b, AC_VERB_SET_CONNECT_SEL, 0x03},
-       /* Mic Mixer; select Front Mic */
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-       {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       /* Mic boost: 0dB */
-       {0x08, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x18, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       /* Record selector: Front mic */
-       {0x15, AC_VERB_SET_CONNECT_SEL, 0x0},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       /* SPDIF route: PCM */
-       {0x02, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Front Pin */
-       {0x05, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-       /* HP Pin */
-       {0x06, AC_VERB_SET_PIN_WIDGET_CONTROL, 0xc0 },
-       /* Mono Pin */
-       {0x07, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x40 },
-       /* Front & Rear Mic Pins */
-       {0x08, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
-       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x24 },
-       /* Line Pin */
-       {0x09, AC_VERB_SET_PIN_WIDGET_CONTROL, 0x20 },
-       /* Digital Beep */
-       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x00},
-       /* Line-Out as Input: disabled */
-       {0x1a, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-       { } /* end */
-};
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1981_loopbacks[] = {
-       { 0x12, HDA_OUTPUT, 0 }, /* Front Mic */
-       { 0x13, HDA_OUTPUT, 0 }, /* Line */
-       { 0x1b, HDA_OUTPUT, 0 }, /* Aux */
-       { 0x1c, HDA_OUTPUT, 0 }, /* Mic */
-       { 0x1d, HDA_OUTPUT, 0 }, /* CD */
-       { } /* end */
-};
-#endif
-
-/*
- * Patch for HP nx6320
- *
- * nx6320 uses EAPD in the reverse way - EAPD-on means the internal
- * speaker output enabled _and_ mute-LED off.
- */
-
-#define AD1981_HP_EVENT                0x37
-#define AD1981_MIC_EVENT       0x38
-
-static const struct hda_verb ad1981_hp_init_verbs[] = {
-       {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x00 }, /* default off */
-       /* pin sensing on HP and Mic jacks */
-       {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
-       {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
-       {}
-};
-
-/* turn on/off EAPD (+ mute HP) as a master switch */
-static int ad1981_hp_master_sw_put(struct snd_kcontrol *kcontrol,
-                                  struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *spec = codec->spec;
-
-       if (! ad198x_eapd_put(kcontrol, ucontrol))
-               return 0;
-       /* change speaker pin appropriately */
-       snd_hda_set_pin_ctl(codec, 0x05, spec->cur_eapd ? PIN_OUT : 0);
-       /* toggle HP mute appropriately */
-       snd_hda_codec_amp_stereo(codec, 0x06, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE,
-                                spec->cur_eapd ? 0 : HDA_AMP_MUTE);
-       return 1;
-}
-
-/* bind volumes of both NID 0x05 and 0x06 */
-static const struct hda_bind_ctls ad1981_hp_bind_master_vol = {
-       .ops = &snd_hda_bind_vol,
-       .values = {
-               HDA_COMPOSE_AMP_VAL(0x05, 3, 0, HDA_OUTPUT),
-               HDA_COMPOSE_AMP_VAL(0x06, 3, 0, HDA_OUTPUT),
-               0
-       },
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1981_hp_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_jack_detect(codec, 0x06);
-       snd_hda_codec_amp_stereo(codec, 0x05, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-/* toggle input of built-in and mic jack appropriately */
-static void ad1981_hp_automic(struct hda_codec *codec)
-{
-       static const struct hda_verb mic_jack_on[] = {
-               {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-               {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-               {}
-       };
-       static const struct hda_verb mic_jack_off[] = {
-               {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, 0xb080},
-               {0x1f, AC_VERB_SET_AMP_GAIN_MUTE, 0xb000},
-               {}
-       };
-       unsigned int present;
-
-       present = snd_hda_jack_detect(codec, 0x08);
-       if (present)
-               snd_hda_sequence_write(codec, mic_jack_on);
-       else
-               snd_hda_sequence_write(codec, mic_jack_off);
-}
-
-/* unsolicited event for HP jack sensing */
-static void ad1981_hp_unsol_event(struct hda_codec *codec,
-                                 unsigned int res)
-{
-       res >>= 26;
-       switch (res) {
-       case AD1981_HP_EVENT:
-               ad1981_hp_automute(codec);
-               break;
-       case AD1981_MIC_EVENT:
-               ad1981_hp_automic(codec);
-               break;
-       }
-}
-
-static const struct hda_input_mux ad1981_hp_capture_source = {
-       .num_items = 3,
-       .items = {
-               { "Mic", 0x0 },
-               { "Dock Mic", 0x1 },
-               { "Mix", 0x2 },
-       },
-};
-
-static const struct snd_kcontrol_new ad1981_hp_mixers[] = {
-       HDA_BIND_VOL("Master Playback Volume", &ad1981_hp_bind_master_vol),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .subdevice = HDA_SUBDEV_NID_FLAG | 0x05,
-               .name = "Master Playback Switch",
-               .info = ad198x_eapd_info,
-               .get = ad198x_eapd_get,
-               .put = ad1981_hp_master_sw_put,
-               .private_value = 0x05,
-       },
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
-#if 0
-       /* FIXME: analog mic/line loopback doesn't work with my tests...
-        *        (although recording is OK)
-        */
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x1c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x1c, 0x0, HDA_OUTPUT),
-       /* FIXME: does this laptop have analog CD connection? */
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
-#endif
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x18, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
-       },
-       { } /* end */
-};
-
-/* initialize jack-sensing, too */
-static int ad1981_hp_init(struct hda_codec *codec)
-{
-       ad198x_init(codec);
-       ad1981_hp_automute(codec);
-       ad1981_hp_automic(codec);
-       return 0;
-}
-
-/* configuration for Toshiba Laptops */
-static const struct hda_verb ad1981_toshiba_init_verbs[] = {
-       {0x05, AC_VERB_SET_EAPD_BTLENABLE, 0x01 }, /* default on */
-       /* pin sensing on HP and Mic jacks */
-       {0x06, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_HP_EVENT},
-       {0x08, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1981_MIC_EVENT},
-       {}
-};
-
-static const struct snd_kcontrol_new ad1981_toshiba_mixers[] = {
-       HDA_CODEC_VOLUME("Amp Volume", 0x1a, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Amp Switch", 0x1a, 0x0, HDA_OUTPUT),
-       { }
-};
-
-/* configuration for Lenovo Thinkpad T60 */
-static const struct snd_kcontrol_new ad1981_thinkpad_mixers[] = {
-       HDA_CODEC_VOLUME("Master Playback Volume", 0x05, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Master Playback Switch", 0x05, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x11, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x11, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x1d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x08, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x15, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x15, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
-       },
-       /* identical with AD1983 */
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
-               .info = ad1983_spdif_route_info,
-               .get = ad1983_spdif_route_get,
-               .put = ad1983_spdif_route_put,
-       },
-       { } /* end */
-};
-
-static const struct hda_input_mux ad1981_thinkpad_capture_source = {
-       .num_items = 3,
-       .items = {
-               { "Mic", 0x0 },
-               { "Mix", 0x2 },
-               { "CD", 0x4 },
-       },
-};
-
-/* models */
-enum {
-       AD1981_AUTO,
-       AD1981_BASIC,
-       AD1981_HP,
-       AD1981_THINKPAD,
-       AD1981_TOSHIBA,
-       AD1981_MODELS
-};
-
-static const char * const ad1981_models[AD1981_MODELS] = {
-       [AD1981_AUTO]           = "auto",
-       [AD1981_HP]             = "hp",
-       [AD1981_THINKPAD]       = "thinkpad",
-       [AD1981_BASIC]          = "basic",
-       [AD1981_TOSHIBA]        = "toshiba"
-};
-
-static const struct snd_pci_quirk ad1981_cfg_tbl[] = {
-       SND_PCI_QUIRK(0x1014, 0x0597, "Lenovo Z60", AD1981_THINKPAD),
-       SND_PCI_QUIRK(0x1014, 0x05b7, "Lenovo Z60m", AD1981_THINKPAD),
-       /* All HP models */
-       SND_PCI_QUIRK_VENDOR(0x103c, "HP nx", AD1981_HP),
-       SND_PCI_QUIRK(0x1179, 0x0001, "Toshiba U205", AD1981_TOSHIBA),
-       /* Lenovo Thinkpad T60/X60/Z6xx */
-       SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1981_THINKPAD),
-       /* HP nx6320 (reversed SSID, H/W bug) */
-       SND_PCI_QUIRK(0x30b0, 0x103c, "HP nx6320", AD1981_HP),
-       {}
-};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-
 /* follow EAPD via vmaster hook */
 static void ad_vmaster_eapd_hook(void *private_data, int enabled)
 {
@@ -2172,7 +530,7 @@ static const struct snd_pci_quirk ad1981_fixup_tbl[] = {
        {}
 };
 
-static int ad1981_parse_auto_config(struct hda_codec *codec)
+static int patch_ad1981(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
        int err;
@@ -2205,110 +563,6 @@ static int ad1981_parse_auto_config(struct hda_codec *codec)
        return err;
 }
 
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static int patch_ad1981(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec;
-       int err, board_config;
-
-       board_config = snd_hda_check_board_config(codec, AD1981_MODELS,
-                                                 ad1981_models,
-                                                 ad1981_cfg_tbl);
-       if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
-                      codec->chip_name);
-               board_config = AD1981_AUTO;
-       }
-
-       if (board_config == AD1981_AUTO)
-               return ad1981_parse_auto_config(codec);
-
-       err = alloc_ad_spec(codec);
-       if (err < 0)
-               return -ENOMEM;
-       spec = codec->spec;
-
-       err = snd_hda_attach_beep_device(codec, 0x10);
-       if (err < 0) {
-               ad198x_free(codec);
-               return err;
-       }
-       set_beep_amp(spec, 0x0d, 0, HDA_OUTPUT);
-
-       spec->multiout.max_channels = 2;
-       spec->multiout.num_dacs = ARRAY_SIZE(ad1981_dac_nids);
-       spec->multiout.dac_nids = ad1981_dac_nids;
-       spec->multiout.dig_out_nid = AD1981_SPDIF_OUT;
-       spec->num_adc_nids = 1;
-       spec->adc_nids = ad1981_adc_nids;
-       spec->capsrc_nids = ad1981_capsrc_nids;
-       spec->input_mux = &ad1981_capture_source;
-       spec->num_mixers = 1;
-       spec->mixers[0] = ad1981_mixers;
-       spec->num_init_verbs = 1;
-       spec->init_verbs[0] = ad1981_init_verbs;
-       spec->spdif_route = 0;
-#ifdef CONFIG_PM
-       spec->loopback.amplist = ad1981_loopbacks;
-#endif
-       spec->vmaster_nid = 0x05;
-
-       codec->patch_ops = ad198x_patch_ops;
-
-       /* override some parameters */
-       switch (board_config) {
-       case AD1981_HP:
-               spec->mixers[0] = ad1981_hp_mixers;
-               spec->num_init_verbs = 2;
-               spec->init_verbs[1] = ad1981_hp_init_verbs;
-               if (!is_jack_available(codec, 0x0a))
-                       spec->multiout.dig_out_nid = 0;
-               spec->input_mux = &ad1981_hp_capture_source;
-
-               codec->patch_ops.init = ad1981_hp_init;
-               codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
-               /* set the upper-limit for mixer amp to 0dB for avoiding the
-                * possible damage by overloading
-                */
-               snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
-                                         (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
-                                         (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
-                                         (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
-                                         (1 << AC_AMPCAP_MUTE_SHIFT));
-               break;
-       case AD1981_THINKPAD:
-               spec->mixers[0] = ad1981_thinkpad_mixers;
-               spec->input_mux = &ad1981_thinkpad_capture_source;
-               /* set the upper-limit for mixer amp to 0dB for avoiding the
-                * possible damage by overloading
-                */
-               snd_hda_override_amp_caps(codec, 0x11, HDA_INPUT,
-                                         (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
-                                         (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
-                                         (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
-                                         (1 << AC_AMPCAP_MUTE_SHIFT));
-               break;
-       case AD1981_TOSHIBA:
-               spec->mixers[0] = ad1981_hp_mixers;
-               spec->mixers[1] = ad1981_toshiba_mixers;
-               spec->num_init_verbs = 2;
-               spec->init_verbs[1] = ad1981_toshiba_init_verbs;
-               spec->multiout.dig_out_nid = 0;
-               spec->input_mux = &ad1981_hp_capture_source;
-               codec->patch_ops.init = ad1981_hp_init;
-               codec->patch_ops.unsol_event = ad1981_hp_unsol_event;
-               break;
-       }
-
-       codec->no_trigger_sense = 1;
-       codec->no_sticky_stream = 1;
-
-       return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1981   ad1981_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
 
 /*
  * AD1988
@@ -2395,90 +649,7 @@ static int patch_ad1981(struct hda_codec *codec)
  *      E/F quad mic array
  */
 
-
 #ifdef ENABLE_AD_STATIC_QUIRKS
-/* models */
-enum {
-       AD1988_AUTO,
-       AD1988_6STACK,
-       AD1988_6STACK_DIG,
-       AD1988_3STACK,
-       AD1988_3STACK_DIG,
-       AD1988_LAPTOP,
-       AD1988_LAPTOP_DIG,
-       AD1988_MODEL_LAST,
-};
-
-/* reivision id to check workarounds */
-#define AD1988A_REV2           0x100200
-
-#define is_rev2(codec) \
-       ((codec)->vendor_id == 0x11d41988 && \
-        (codec)->revision_id == AD1988A_REV2)
-
-/*
- * mixers
- */
-
-static const hda_nid_t ad1988_6stack_dac_nids[4] = {
-       0x04, 0x06, 0x05, 0x0a
-};
-
-static const hda_nid_t ad1988_3stack_dac_nids[3] = {
-       0x04, 0x05, 0x0a
-};
-
-/* for AD1988A revision-2, DAC2-4 are swapped */
-static const hda_nid_t ad1988_6stack_dac_nids_rev2[4] = {
-       0x04, 0x05, 0x0a, 0x06
-};
-
-static const hda_nid_t ad1988_alt_dac_nid[1] = {
-       0x03
-};
-
-static const hda_nid_t ad1988_3stack_dac_nids_rev2[3] = {
-       0x04, 0x0a, 0x06
-};
-
-static const hda_nid_t ad1988_adc_nids[3] = {
-       0x08, 0x09, 0x0f
-};
-
-static const hda_nid_t ad1988_capsrc_nids[3] = {
-       0x0c, 0x0d, 0x0e
-};
-
-#define AD1988_SPDIF_OUT               0x02
-#define AD1988_SPDIF_OUT_HDMI  0x0b
-#define AD1988_SPDIF_IN                0x07
-
-static const hda_nid_t ad1989b_slave_dig_outs[] = {
-       AD1988_SPDIF_OUT, AD1988_SPDIF_OUT_HDMI, 0
-};
-
-static const struct hda_input_mux ad1988_6stack_capture_source = {
-       .num_items = 5,
-       .items = {
-               { "Front Mic", 0x1 },   /* port-B */
-               { "Line", 0x2 },        /* port-C */
-               { "Mic", 0x4 },         /* port-E */
-               { "CD", 0x5 },
-               { "Mix", 0x9 },
-       },
-};
-
-static const struct hda_input_mux ad1988_laptop_capture_source = {
-       .num_items = 3,
-       .items = {
-               { "Mic/Line", 0x1 },    /* port-B */
-               { "CD", 0x5 },
-               { "Mix", 0x9 },
-       },
-};
-
-/*
- */
 static int ad198x_ch_mode_info(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_info *uinfo)
 {
@@ -2509,636 +680,73 @@ static int ad198x_ch_mode_put(struct snd_kcontrol *kcontrol,
                spec->multiout.num_dacs = spec->multiout.max_channels / 2;
        return err;
 }
+#endif /* ENABLE_AD_STATIC_QUIRKS */
 
-/* 6-stack mode */
-static const struct snd_kcontrol_new ad1988_6stack_mixers1[] = {
-       HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Surround Playback Volume", 0x06, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Side Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1988_6stack_mixers1_rev2[] = {
-       HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Surround Playback Volume", 0x05, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x0a, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x0a, 2, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Side Playback Volume", 0x06, 0x0, HDA_OUTPUT),
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1988_6stack_mixers2[] = {
-       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
-       HDA_BIND_MUTE("Surround Playback Switch", 0x2a, 2, HDA_INPUT),
-       HDA_BIND_MUTE_MONO("Center Playback Switch", 0x27, 1, 2, HDA_INPUT),
-       HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x27, 2, 2, HDA_INPUT),
-       HDA_BIND_MUTE("Side Playback Switch", 0x28, 2, HDA_INPUT),
-       HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
-       HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
-       HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
-
-       HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
-       HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
-       { } /* end */
-};
-
-/* 3-stack mode */
-static const struct snd_kcontrol_new ad1988_3stack_mixers1[] = {
-       HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1988_3stack_mixers1_rev2[] = {
-       HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Surround Playback Volume", 0x0a, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x06, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x06, 2, 0x0, HDA_OUTPUT),
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1988_3stack_mixers2[] = {
-       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
-       HDA_BIND_MUTE("Front Playback Switch", 0x29, 2, HDA_INPUT),
-       HDA_BIND_MUTE("Surround Playback Switch", 0x2c, 2, HDA_INPUT),
-       HDA_BIND_MUTE_MONO("Center Playback Switch", 0x26, 1, 2, HDA_INPUT),
-       HDA_BIND_MUTE_MONO("LFE Playback Switch", 0x26, 2, 2, HDA_INPUT),
-       HDA_BIND_MUTE("Headphone Playback Switch", 0x22, 2, HDA_INPUT),
-       HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
-       HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x4, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x4, HDA_INPUT),
-
-       HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
-       HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Channel Mode",
-               .info = ad198x_ch_mode_info,
-               .get = ad198x_ch_mode_get,
-               .put = ad198x_ch_mode_put,
-       },
-
-       { } /* end */
-};
-
-/* laptop mode */
-static const struct snd_kcontrol_new ad1988_laptop_mixers[] = {
-       HDA_CODEC_VOLUME("Headphone Playback Volume", 0x03, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x29, 0x0, HDA_INPUT),
-       HDA_BIND_MUTE("Mono Playback Switch", 0x1e, 2, HDA_INPUT),
-
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x6, HDA_INPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x6, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x1, HDA_INPUT),
-       HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x1, HDA_INPUT),
-
-       HDA_CODEC_VOLUME("Analog Mix Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Analog Mix Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
-
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "External Amplifier",
-               .subdevice = HDA_SUBDEV_NID_FLAG | 0x12,
-               .info = ad198x_eapd_info,
-               .get = ad198x_eapd_get,
-               .put = ad198x_eapd_put,
-               .private_value = 0x12, /* port-D */
-       },
-
-       { } /* end */
-};
-
-/* capture */
-static const struct snd_kcontrol_new ad1988_capture_mixers[] = {
-       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 2, 0x0e, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 2, 0x0e, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 3,
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
-       },
-       { } /* end */
-};
-
-static int ad1988_spdif_playback_source_info(struct snd_kcontrol *kcontrol,
-                                            struct snd_ctl_elem_info *uinfo)
+static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_info *uinfo)
 {
+       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
        static const char * const texts[] = {
-               "PCM", "ADC1", "ADC2", "ADC3"
+               "PCM", "ADC1", "ADC2", "ADC3",
        };
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-       uinfo->count = 1;
-       uinfo->value.enumerated.items = 4;
-       if (uinfo->value.enumerated.item >= 4)
-               uinfo->value.enumerated.item = 3;
-       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]);
-       return 0;
+       int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
+       if (num_conns > 4)
+               num_conns = 4;
+       return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts);
 }
 
-static int ad1988_spdif_playback_source_get(struct snd_kcontrol *kcontrol,
-                                           struct snd_ctl_elem_value *ucontrol)
+static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       unsigned int sel;
-
-       sel = snd_hda_codec_read(codec, 0x1d, 0, AC_VERB_GET_AMP_GAIN_MUTE,
-                                AC_AMP_GET_INPUT);
-       if (!(sel & 0x80))
-               ucontrol->value.enumerated.item[0] = 0;
-       else {
-               sel = snd_hda_codec_read(codec, 0x0b, 0,
-                                        AC_VERB_GET_CONNECT_SEL, 0);
-               if (sel < 3)
-                       sel++;
-               else
-                       sel = 0;
-               ucontrol->value.enumerated.item[0] = sel;
-       }
+       struct ad198x_spec *spec = codec->spec;
+
+       ucontrol->value.enumerated.item[0] = spec->cur_smux;
        return 0;
 }
 
-static int ad1988_spdif_playback_source_put(struct snd_kcontrol *kcontrol,
-                                           struct snd_ctl_elem_value *ucontrol)
+static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
+                                    struct snd_ctl_elem_value *ucontrol)
 {
        struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       unsigned int val, sel;
-       int change;
+       struct ad198x_spec *spec = codec->spec;
+       unsigned int val = ucontrol->value.enumerated.item[0];
+       struct nid_path *path;
+       int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
 
-       val = ucontrol->value.enumerated.item[0];
-       if (val > 3)
+       if (val >= num_conns)
                return -EINVAL;
-       if (!val) {
-               sel = snd_hda_codec_read(codec, 0x1d, 0,
-                                        AC_VERB_GET_AMP_GAIN_MUTE,
-                                        AC_AMP_GET_INPUT);
-               change = sel & 0x80;
-               if (change) {
-                       snd_hda_codec_write_cache(codec, 0x1d, 0,
-                                                 AC_VERB_SET_AMP_GAIN_MUTE,
-                                                 AMP_IN_UNMUTE(0));
-                       snd_hda_codec_write_cache(codec, 0x1d, 0,
-                                                 AC_VERB_SET_AMP_GAIN_MUTE,
-                                                 AMP_IN_MUTE(1));
-               }
-       } else {
-               sel = snd_hda_codec_read(codec, 0x1d, 0,
-                                        AC_VERB_GET_AMP_GAIN_MUTE,
-                                        AC_AMP_GET_INPUT | 0x01);
-               change = sel & 0x80;
-               if (change) {
-                       snd_hda_codec_write_cache(codec, 0x1d, 0,
-                                                 AC_VERB_SET_AMP_GAIN_MUTE,
-                                                 AMP_IN_MUTE(0));
-                       snd_hda_codec_write_cache(codec, 0x1d, 0,
-                                                 AC_VERB_SET_AMP_GAIN_MUTE,
-                                                 AMP_IN_UNMUTE(1));
-               }
-               sel = snd_hda_codec_read(codec, 0x0b, 0,
-                                        AC_VERB_GET_CONNECT_SEL, 0) + 1;
-               change |= sel != val;
-               if (change)
-                       snd_hda_codec_write_cache(codec, 0x0b, 0,
-                                                 AC_VERB_SET_CONNECT_SEL,
-                                                 val - 1);
-       }
-       return change;
-}
+       if (spec->cur_smux == val)
+               return 0;
 
-static const struct snd_kcontrol_new ad1988_spdif_out_mixers[] = {
-       HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "IEC958 Playback Source",
-               .subdevice = HDA_SUBDEV_NID_FLAG | 0x1b,
-               .info = ad1988_spdif_playback_source_info,
-               .get = ad1988_spdif_playback_source_get,
-               .put = ad1988_spdif_playback_source_put,
-       },
-       { } /* end */
-};
+       mutex_lock(&codec->control_mutex);
+       codec->cached_write = 1;
+       path = snd_hda_get_path_from_idx(codec,
+                                        spec->smux_paths[spec->cur_smux]);
+       if (path)
+               snd_hda_activate_path(codec, path, false, true);
+       path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]);
+       if (path)
+               snd_hda_activate_path(codec, path, true, true);
+       spec->cur_smux = val;
+       codec->cached_write = 0;
+       mutex_unlock(&codec->control_mutex);
+       snd_hda_codec_flush_cache(codec); /* flush the updates */
+       return 1;
+}
 
-static const struct snd_kcontrol_new ad1988_spdif_in_mixers[] = {
-       HDA_CODEC_VOLUME("IEC958 Capture Volume", 0x1c, 0x0, HDA_INPUT),
-       { } /* end */
+static struct snd_kcontrol_new ad1988_auto_smux_mixer = {
+       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+       .name = "IEC958 Playback Source",
+       .info = ad1988_auto_smux_enum_info,
+       .get = ad1988_auto_smux_enum_get,
+       .put = ad1988_auto_smux_enum_put,
 };
 
-static const struct snd_kcontrol_new ad1989_spdif_out_mixers[] = {
-       HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("HDMI Playback Volume", 0x1d, 0x0, HDA_OUTPUT),
-       { } /* end */
-};
-
-/*
- * initialization verbs
- */
-
-/*
- * for 6-stack (+dig)
- */
-static const struct hda_verb ad1988_6stack_init_verbs[] = {
-       /* Front, Surround, CLFE, side DAC; unmute as default */
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       /* Port-A front headphon path */
-       {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       /* Port-D line-out path */
-       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       /* Port-F surround path */
-       {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x2a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       /* Port-G CLFE path */
-       {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x27, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       /* Port-H side path */
-       {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x28, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x25, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       /* Mono out path */
-       {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
-       /* Port-B front mic-in path */
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       /* Port-C line-in path */
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-       {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Port-E mic-in path */
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Analog CD Input */
-       {0x18, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-       /* Analog Mix output amp */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
-
-       { }
-};
-
-static const struct hda_verb ad1988_6stack_fp_init_verbs[] = {
-       /* Headphone; unmute as default */
-       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       /* Port-A front headphon path */
-       {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-
-       { }
-};
-
-static const struct hda_verb ad1988_capture_init_verbs[] = {
-       /* mute analog mix */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
-       /* select ADCs - front-mic */
-       {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
-       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
-       {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-
-       { }
-};
-
-static const struct hda_verb ad1988_spdif_init_verbs[] = {
-       /* SPDIF out sel */
-       {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
-       {0x0b, AC_VERB_SET_CONNECT_SEL, 0x0}, /* ADC1 */
-       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       /* SPDIF out pin */
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
-
-       { }
-};
-
-static const struct hda_verb ad1988_spdif_in_init_verbs[] = {
-       /* unmute SPDIF input pin */
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       { }
-};
-
-/* AD1989 has no ADC -> SPDIF route */
-static const struct hda_verb ad1989_spdif_init_verbs[] = {
-       /* SPDIF-1 out pin */
-       {0x1b, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
-       /* SPDIF-2/HDMI out pin */
-       {0x1d, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-       {0x1d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
-       { }
-};
-
-/*
- * verbs for 3stack (+dig)
- */
-static const struct hda_verb ad1988_3stack_ch2_init[] = {
-       /* set port-C to line-in */
-       { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-       { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN },
-       /* set port-E to mic-in */
-       { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-       { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80 },
-       { } /* end */
-};
-
-static const struct hda_verb ad1988_3stack_ch6_init[] = {
-       /* set port-C to surround out */
-       { 0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-       { 0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-       /* set port-E to CLFE out */
-       { 0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT },
-       { 0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-       { } /* end */
-};
-
-static const struct hda_channel_mode ad1988_3stack_modes[2] = {
-       { 2, ad1988_3stack_ch2_init },
-       { 6, ad1988_3stack_ch6_init },
-};
-
-static const struct hda_verb ad1988_3stack_init_verbs[] = {
-       /* Front, Surround, CLFE, side DAC; unmute as default */
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       /* Port-A front headphon path */
-       {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       /* Port-D line-out path */
-       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       /* Mono out path */
-       {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
-       /* Port-B front mic-in path */
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       /* Port-C line-in/surround path - 6ch mode as default */
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x31, AC_VERB_SET_CONNECT_SEL, 0x0}, /* output sel: DAC 0x05 */
-       {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* Port-E mic-in/CLFE path - 6ch mode as default */
-       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x32, AC_VERB_SET_CONNECT_SEL, 0x1}, /* output sel: DAC 0x0a */
-       {0x34, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* mute analog mix */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
-       /* select ADCs - front-mic */
-       {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
-       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
-       {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-       /* Analog Mix output amp */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
-       { }
-};
-
-/*
- * verbs for laptop mode (+dig)
- */
-static const struct hda_verb ad1988_laptop_hp_on[] = {
-       /* unmute port-A and mute port-D */
-       { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-       { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-       { } /* end */
-};
-static const struct hda_verb ad1988_laptop_hp_off[] = {
-       /* mute port-A and unmute port-D */
-       { 0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE },
-       { 0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE },
-       { } /* end */
-};
-
-#define AD1988_HP_EVENT        0x01
-
-static const struct hda_verb ad1988_laptop_init_verbs[] = {
-       /* Front, Surround, CLFE, side DAC; unmute as default */
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x06, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       /* Port-A front headphon path */
-       {0x37, AC_VERB_SET_CONNECT_SEL, 0x00}, /* DAC0:03h */
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       /* unsolicited event for pin-sense */
-       {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1988_HP_EVENT },
-       /* Port-D line-out path + EAPD */
-       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x00}, /* EAPD-off */
-       /* Mono out path */
-       {0x36, AC_VERB_SET_CONNECT_SEL, 0x1}, /* DAC1:04h */
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, 0xb01f}, /* unmute, 0dB */
-       /* Port-B mic-in path */
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       /* Port-C docking station - try to output */
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x33, AC_VERB_SET_CONNECT_SEL, 0x0},
-       /* mute analog mix */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
-       /* select ADCs - mic */
-       {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
-       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x1},
-       {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-       /* Analog Mix output amp */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
-       { }
-};
-
-static void ad1988_laptop_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       if ((res >> 26) != AD1988_HP_EVENT)
-               return;
-       if (snd_hda_jack_detect(codec, 0x11))
-               snd_hda_sequence_write(codec, ad1988_laptop_hp_on);
-       else
-               snd_hda_sequence_write(codec, ad1988_laptop_hp_off);
-} 
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1988_loopbacks[] = {
-       { 0x20, HDA_INPUT, 0 }, /* Front Mic */
-       { 0x20, HDA_INPUT, 1 }, /* Line */
-       { 0x20, HDA_INPUT, 4 }, /* Mic */
-       { 0x20, HDA_INPUT, 6 }, /* CD */
-       { } /* end */
-};
-#endif
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-static int ad1988_auto_smux_enum_info(struct snd_kcontrol *kcontrol,
-                                     struct snd_ctl_elem_info *uinfo)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       static const char * const texts[] = {
-               "PCM", "ADC1", "ADC2", "ADC3",
-       };
-       int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
-       if (num_conns > 4)
-               num_conns = 4;
-       return snd_hda_enum_helper_info(kcontrol, uinfo, num_conns, texts);
-}
-
-static int ad1988_auto_smux_enum_get(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *spec = codec->spec;
-
-       ucontrol->value.enumerated.item[0] = spec->cur_smux;
-       return 0;
-}
-
-static int ad1988_auto_smux_enum_put(struct snd_kcontrol *kcontrol,
-                                    struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       struct ad198x_spec *spec = codec->spec;
-       unsigned int val = ucontrol->value.enumerated.item[0];
-       struct nid_path *path;
-       int num_conns = snd_hda_get_num_conns(codec, 0x0b) + 1;
-
-       if (val >= num_conns)
-               return -EINVAL;
-       if (spec->cur_smux == val)
-               return 0;
-
-       mutex_lock(&codec->control_mutex);
-       codec->cached_write = 1;
-       path = snd_hda_get_path_from_idx(codec,
-                                        spec->smux_paths[spec->cur_smux]);
-       if (path)
-               snd_hda_activate_path(codec, path, false, true);
-       path = snd_hda_get_path_from_idx(codec, spec->smux_paths[val]);
-       if (path)
-               snd_hda_activate_path(codec, path, true, true);
-       spec->cur_smux = val;
-       codec->cached_write = 0;
-       mutex_unlock(&codec->control_mutex);
-       snd_hda_codec_flush_cache(codec); /* flush the updates */
-       return 1;
-}
-
-static struct snd_kcontrol_new ad1988_auto_smux_mixer = {
-       .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-       .name = "IEC958 Playback Source",
-       .info = ad1988_auto_smux_enum_info,
-       .get = ad1988_auto_smux_enum_get,
-       .put = ad1988_auto_smux_enum_put,
-};
-
-static int ad1988_auto_init(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec = codec->spec;
-       int i, err;
+static int ad1988_auto_init(struct hda_codec *codec)
+{
+       struct ad198x_spec *spec = codec->spec;
+       int i, err;
 
        err = snd_hda_gen_init(codec);
        if (err < 0)
@@ -3220,7 +828,34 @@ static int ad1988_add_spdif_mux_ctl(struct hda_codec *codec)
 /*
  */
 
-static int ad1988_parse_auto_config(struct hda_codec *codec)
+enum {
+       AD1988_FIXUP_6STACK_DIG,
+};
+
+static const struct hda_fixup ad1988_fixups[] = {
+       [AD1988_FIXUP_6STACK_DIG] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x11, 0x02214130 }, /* front-hp */
+                       { 0x12, 0x01014010 }, /* line-out */
+                       { 0x14, 0x02a19122 }, /* front-mic */
+                       { 0x15, 0x01813021 }, /* line-in */
+                       { 0x16, 0x01011012 }, /* line-out */
+                       { 0x17, 0x01a19020 }, /* mic */
+                       { 0x1b, 0x0145f1f0 }, /* SPDIF */
+                       { 0x24, 0x01016011 }, /* line-out */
+                       { 0x25, 0x01012013 }, /* line-out */
+                       { }
+               }
+       },
+};
+
+static const struct hda_model_fixup ad1988_fixup_models[] = {
+       { .id = AD1988_FIXUP_6STACK_DIG, .name = "6stack-dig" },
+       {}
+};
+
+static int patch_ad1988(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
        int err;
@@ -3234,12 +869,19 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
        spec->gen.mixer_merge_nid = 0x21;
        spec->gen.beep_nid = 0x10;
        set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
+
+       snd_hda_pick_fixup(codec, ad1988_fixup_models, NULL, ad1988_fixups);
+       snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
+
        err = ad198x_parse_auto_config(codec);
        if (err < 0)
                goto error;
        err = ad1988_add_spdif_mux_ctl(codec);
        if (err < 0)
                goto error;
+
+       snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
        return 0;
 
  error:
@@ -3247,169 +889,6 @@ static int ad1988_parse_auto_config(struct hda_codec *codec)
        return err;
 }
 
-/*
- */
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static const char * const ad1988_models[AD1988_MODEL_LAST] = {
-       [AD1988_6STACK]         = "6stack",
-       [AD1988_6STACK_DIG]     = "6stack-dig",
-       [AD1988_3STACK]         = "3stack",
-       [AD1988_3STACK_DIG]     = "3stack-dig",
-       [AD1988_LAPTOP]         = "laptop",
-       [AD1988_LAPTOP_DIG]     = "laptop-dig",
-       [AD1988_AUTO]           = "auto",
-};
-
-static const struct snd_pci_quirk ad1988_cfg_tbl[] = {
-       SND_PCI_QUIRK(0x1043, 0x81ec, "Asus P5B-DLX", AD1988_6STACK_DIG),
-       SND_PCI_QUIRK(0x1043, 0x81f6, "Asus M2N-SLI", AD1988_6STACK_DIG),
-       SND_PCI_QUIRK(0x1043, 0x8277, "Asus P5K-E/WIFI-AP", AD1988_6STACK_DIG),
-       SND_PCI_QUIRK(0x1043, 0x82c0, "Asus M3N-HT Deluxe", AD1988_6STACK_DIG),
-       SND_PCI_QUIRK(0x1043, 0x8311, "Asus P5Q-Premium/Pro", AD1988_6STACK_DIG),
-       {}
-};
-
-static int patch_ad1988(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec;
-       int err, board_config;
-
-       board_config = snd_hda_check_board_config(codec, AD1988_MODEL_LAST,
-                                                 ad1988_models, ad1988_cfg_tbl);
-       if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
-                      codec->chip_name);
-               board_config = AD1988_AUTO;
-       }
-
-       if (board_config == AD1988_AUTO)
-               return ad1988_parse_auto_config(codec);
-
-       err = alloc_ad_spec(codec);
-       if (err < 0)
-               return err;
-       spec = codec->spec;
-
-       if (is_rev2(codec))
-               snd_printk(KERN_INFO "patch_analog: AD1988A rev.2 is detected, enable workarounds\n");
-
-       err = snd_hda_attach_beep_device(codec, 0x10);
-       if (err < 0) {
-               ad198x_free(codec);
-               return err;
-       }
-       set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
-       if (!spec->multiout.hp_nid)
-               spec->multiout.hp_nid = ad1988_alt_dac_nid[0];
-       switch (board_config) {
-       case AD1988_6STACK:
-       case AD1988_6STACK_DIG:
-               spec->multiout.max_channels = 8;
-               spec->multiout.num_dacs = 4;
-               if (is_rev2(codec))
-                       spec->multiout.dac_nids = ad1988_6stack_dac_nids_rev2;
-               else
-                       spec->multiout.dac_nids = ad1988_6stack_dac_nids;
-               spec->input_mux = &ad1988_6stack_capture_source;
-               spec->num_mixers = 2;
-               if (is_rev2(codec))
-                       spec->mixers[0] = ad1988_6stack_mixers1_rev2;
-               else
-                       spec->mixers[0] = ad1988_6stack_mixers1;
-               spec->mixers[1] = ad1988_6stack_mixers2;
-               spec->num_init_verbs = 1;
-               spec->init_verbs[0] = ad1988_6stack_init_verbs;
-               if (board_config == AD1988_6STACK_DIG) {
-                       spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
-                       spec->dig_in_nid = AD1988_SPDIF_IN;
-               }
-               break;
-       case AD1988_3STACK:
-       case AD1988_3STACK_DIG:
-               spec->multiout.max_channels = 6;
-               spec->multiout.num_dacs = 3;
-               if (is_rev2(codec))
-                       spec->multiout.dac_nids = ad1988_3stack_dac_nids_rev2;
-               else
-                       spec->multiout.dac_nids = ad1988_3stack_dac_nids;
-               spec->input_mux = &ad1988_6stack_capture_source;
-               spec->channel_mode = ad1988_3stack_modes;
-               spec->num_channel_mode = ARRAY_SIZE(ad1988_3stack_modes);
-               spec->num_mixers = 2;
-               if (is_rev2(codec))
-                       spec->mixers[0] = ad1988_3stack_mixers1_rev2;
-               else
-                       spec->mixers[0] = ad1988_3stack_mixers1;
-               spec->mixers[1] = ad1988_3stack_mixers2;
-               spec->num_init_verbs = 1;
-               spec->init_verbs[0] = ad1988_3stack_init_verbs;
-               if (board_config == AD1988_3STACK_DIG)
-                       spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
-               break;
-       case AD1988_LAPTOP:
-       case AD1988_LAPTOP_DIG:
-               spec->multiout.max_channels = 2;
-               spec->multiout.num_dacs = 1;
-               spec->multiout.dac_nids = ad1988_3stack_dac_nids;
-               spec->input_mux = &ad1988_laptop_capture_source;
-               spec->num_mixers = 1;
-               spec->mixers[0] = ad1988_laptop_mixers;
-               codec->inv_eapd = 1; /* inverted EAPD */
-               spec->num_init_verbs = 1;
-               spec->init_verbs[0] = ad1988_laptop_init_verbs;
-               if (board_config == AD1988_LAPTOP_DIG)
-                       spec->multiout.dig_out_nid = AD1988_SPDIF_OUT;
-               break;
-       }
-
-       spec->num_adc_nids = ARRAY_SIZE(ad1988_adc_nids);
-       spec->adc_nids = ad1988_adc_nids;
-       spec->capsrc_nids = ad1988_capsrc_nids;
-       spec->mixers[spec->num_mixers++] = ad1988_capture_mixers;
-       spec->init_verbs[spec->num_init_verbs++] = ad1988_capture_init_verbs;
-       if (spec->multiout.dig_out_nid) {
-               if (codec->vendor_id >= 0x11d4989a) {
-                       spec->mixers[spec->num_mixers++] =
-                               ad1989_spdif_out_mixers;
-                       spec->init_verbs[spec->num_init_verbs++] =
-                               ad1989_spdif_init_verbs;
-                       codec->slave_dig_outs = ad1989b_slave_dig_outs;
-               } else {
-                       spec->mixers[spec->num_mixers++] =
-                               ad1988_spdif_out_mixers;
-                       spec->init_verbs[spec->num_init_verbs++] =
-                               ad1988_spdif_init_verbs;
-               }
-       }
-       if (spec->dig_in_nid && codec->vendor_id < 0x11d4989a) {
-               spec->mixers[spec->num_mixers++] = ad1988_spdif_in_mixers;
-               spec->init_verbs[spec->num_init_verbs++] =
-                       ad1988_spdif_in_init_verbs;
-       }
-
-       codec->patch_ops = ad198x_patch_ops;
-       switch (board_config) {
-       case AD1988_LAPTOP:
-       case AD1988_LAPTOP_DIG:
-               codec->patch_ops.unsol_event = ad1988_laptop_unsol_event;
-               break;
-       }
-#ifdef CONFIG_PM
-       spec->loopback.amplist = ad1988_loopbacks;
-#endif
-       spec->vmaster_nid = 0x04;
-
-       codec->no_trigger_sense = 1;
-       codec->no_sticky_stream = 1;
-
-       return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1988   ad1988_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
 
 /*
  * AD1884 / AD1984
@@ -3423,167 +902,19 @@ static int patch_ad1988(struct hda_codec *codec)
  *
  * AD1984 = AD1884 + two digital mic-ins
  *
- * FIXME:
- * For simplicity, we share the single DAC for both HP and line-outs
- * right now.  The inidividual playbacks could be easily implemented,
- * but no build-up framework is given, so far.
- */
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static const hda_nid_t ad1884_dac_nids[1] = {
-       0x04,
-};
-
-static const hda_nid_t ad1884_adc_nids[2] = {
-       0x08, 0x09,
-};
-
-static const hda_nid_t ad1884_capsrc_nids[2] = {
-       0x0c, 0x0d,
-};
-
-#define AD1884_SPDIF_OUT       0x02
-
-static const struct hda_input_mux ad1884_capture_source = {
-       .num_items = 4,
-       .items = {
-               { "Front Mic", 0x0 },
-               { "Mic", 0x1 },
-               { "CD", 0x2 },
-               { "Mix", 0x3 },
-       },
-};
-
-static const struct snd_kcontrol_new ad1884_base_mixers[] = {
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
-       /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
-       },
-       /* SPDIF controls */
-       HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
-               /* identical with ad1983 */
-               .info = ad1983_spdif_route_info,
-               .get = ad1983_spdif_route_get,
-               .put = ad1983_spdif_route_put,
-       },
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1984_dmic_mixers[] = {
-       HDA_CODEC_VOLUME("Digital Mic Capture Volume", 0x05, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Digital Mic Capture Switch", 0x05, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME_IDX("Digital Mic Capture Volume", 1, 0x06, 0x0,
-                            HDA_INPUT),
-       HDA_CODEC_MUTE_IDX("Digital Mic Capture Switch", 1, 0x06, 0x0,
-                          HDA_INPUT),
-       { } /* end */
-};
-
-/*
- * initialization verbs
+ * AD1883 / AD1884A / AD1984A / AD1984B
+ *
+ * port-B (0x14) - front mic-in
+ * port-E (0x1c) - rear mic-in
+ * port-F (0x16) - CD / ext out
+ * port-C (0x15) - rear line-in
+ * port-D (0x12) - rear line-out
+ * port-A (0x11) - front hp-out
+ *
+ * AD1984A = AD1884A + digital-mic
+ * AD1883 = equivalent with AD1984A
+ * AD1984B = AD1984A + extra SPDIF-out
  */
-static const struct hda_verb ad1884_init_verbs[] = {
-       /* DACs; mute as default */
-       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       /* Port-A (HP) mixer */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Port-A pin */
-       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* HP selector - select DAC2 */
-       {0x22, AC_VERB_SET_CONNECT_SEL, 0x1},
-       /* Port-D (Line-out) mixer */
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Port-D pin */
-       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* Mono-out mixer */
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Mono-out pin */
-       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* Mono selector */
-       {0x0e, AC_VERB_SET_CONNECT_SEL, 0x1},
-       /* Port-B (front mic) pin */
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       /* Port-C (rear mic) pin */
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       /* Analog mixer; mute as default */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       /* Analog Mix output amp */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
-       /* SPDIF output selector */
-       {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
-       { } /* end */
-};
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1884_loopbacks[] = {
-       { 0x20, HDA_INPUT, 0 }, /* Front Mic */
-       { 0x20, HDA_INPUT, 1 }, /* Mic */
-       { 0x20, HDA_INPUT, 2 }, /* CD */
-       { 0x20, HDA_INPUT, 4 }, /* Docking */
-       { } /* end */
-};
-#endif
-
-static const char * const ad1884_slave_vols[] = {
-       "PCM", "Mic", "Mono", "Front Mic", "Mic", "CD",
-       "Internal Mic", "Dock Mic", /* "Beep", */ "IEC958",
-       NULL
-};
-
-enum {
-       AD1884_AUTO,
-       AD1884_BASIC,
-       AD1884_MODELS
-};
-
-static const char * const ad1884_models[AD1884_MODELS] = {
-       [AD1884_AUTO]           = "auto",
-       [AD1884_BASIC]          = "basic",
-};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
 
 /* set the upper-limit for mixer amp to 0dB for avoiding the possible
  * damage by overloading
@@ -3599,14 +930,34 @@ static void ad1884_fixup_amp_override(struct hda_codec *codec,
                                          (1 << AC_AMPCAP_MUTE_SHIFT));
 }
 
+/* toggle GPIO1 according to the mute state */
+static void ad1884_vmaster_hp_gpio_hook(void *private_data, int enabled)
+{
+       struct hda_codec *codec = private_data;
+       struct ad198x_spec *spec = codec->spec;
+
+       if (spec->eapd_nid)
+               ad_vmaster_eapd_hook(private_data, enabled);
+       snd_hda_codec_update_cache(codec, 0x01, 0,
+                                  AC_VERB_SET_GPIO_DATA,
+                                  enabled ? 0x00 : 0x02);
+}
+
 static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
                                 const struct hda_fixup *fix, int action)
 {
        struct ad198x_spec *spec = codec->spec;
+       static const struct hda_verb gpio_init_verbs[] = {
+               {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
+               {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
+               {0x01, AC_VERB_SET_GPIO_DATA, 0x02},
+               {},
+       };
 
        switch (action) {
        case HDA_FIXUP_ACT_PRE_PROBE:
-               spec->gen.vmaster_mute.hook = ad_vmaster_eapd_hook;
+               spec->gen.vmaster_mute.hook = ad1884_vmaster_hp_gpio_hook;
+               snd_hda_sequence_write_cache(codec, gpio_init_verbs);
                break;
        case HDA_FIXUP_ACT_PROBE:
                if (spec->gen.autocfg.line_out_type == AUTO_PIN_SPEAKER_OUT)
@@ -3617,9 +968,18 @@ static void ad1884_fixup_hp_eapd(struct hda_codec *codec,
        }
 }
 
+/* set magic COEFs for dmic */
+static const struct hda_verb ad1884_dmic_init_verbs[] = {
+       {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
+       {0x01, AC_VERB_SET_PROC_COEF, 0x08},
+       {}
+};
+
 enum {
        AD1884_FIXUP_AMP_OVERRIDE,
        AD1884_FIXUP_HP_EAPD,
+       AD1884_FIXUP_DMIC_COEF,
+       AD1884_FIXUP_HP_TOUCHSMART,
 };
 
 static const struct hda_fixup ad1884_fixups[] = {
@@ -3633,15 +993,27 @@ static const struct hda_fixup ad1884_fixups[] = {
                .chained = true,
                .chain_id = AD1884_FIXUP_AMP_OVERRIDE,
        },
+       [AD1884_FIXUP_DMIC_COEF] = {
+               .type = HDA_FIXUP_VERBS,
+               .v.verbs = ad1884_dmic_init_verbs,
+       },
+       [AD1884_FIXUP_HP_TOUCHSMART] = {
+               .type = HDA_FIXUP_VERBS,
+               .v.verbs = ad1884_dmic_init_verbs,
+               .chained = true,
+               .chain_id = AD1884_FIXUP_HP_EAPD,
+       },
 };
 
 static const struct snd_pci_quirk ad1884_fixup_tbl[] = {
+       SND_PCI_QUIRK(0x103c, 0x2a82, "HP Touchsmart", AD1884_FIXUP_HP_TOUCHSMART),
        SND_PCI_QUIRK_VENDOR(0x103c, "HP", AD1884_FIXUP_HP_EAPD),
+       SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1884_FIXUP_DMIC_COEF),
        {}
 };
 
 
-static int ad1884_parse_auto_config(struct hda_codec *codec)
+static int patch_ad1884(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
        int err;
@@ -3674,1170 +1046,6 @@ static int ad1884_parse_auto_config(struct hda_codec *codec)
        return err;
 }
 
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static int patch_ad1884_basic(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec;
-       int err;
-
-       err = alloc_ad_spec(codec);
-       if (err < 0)
-               return err;
-       spec = codec->spec;
-
-       err = snd_hda_attach_beep_device(codec, 0x10);
-       if (err < 0) {
-               ad198x_free(codec);
-               return err;
-       }
-       set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
-       spec->multiout.max_channels = 2;
-       spec->multiout.num_dacs = ARRAY_SIZE(ad1884_dac_nids);
-       spec->multiout.dac_nids = ad1884_dac_nids;
-       spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
-       spec->num_adc_nids = ARRAY_SIZE(ad1884_adc_nids);
-       spec->adc_nids = ad1884_adc_nids;
-       spec->capsrc_nids = ad1884_capsrc_nids;
-       spec->input_mux = &ad1884_capture_source;
-       spec->num_mixers = 1;
-       spec->mixers[0] = ad1884_base_mixers;
-       spec->num_init_verbs = 1;
-       spec->init_verbs[0] = ad1884_init_verbs;
-       spec->spdif_route = 0;
-#ifdef CONFIG_PM
-       spec->loopback.amplist = ad1884_loopbacks;
-#endif
-       spec->vmaster_nid = 0x04;
-       /* we need to cover all playback volumes */
-       spec->slave_vols = ad1884_slave_vols;
-       /* slaves may contain input volumes, so we can't raise to 0dB blindly */
-       spec->avoid_init_slave_vol = 1;
-
-       codec->patch_ops = ad198x_patch_ops;
-
-       codec->no_trigger_sense = 1;
-       codec->no_sticky_stream = 1;
-
-       return 0;
-}
-
-static int patch_ad1884(struct hda_codec *codec)
-{
-       int board_config;
-
-       board_config = snd_hda_check_board_config(codec, AD1884_MODELS,
-                                                 ad1884_models, NULL);
-       if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
-                      codec->chip_name);
-               board_config = AD1884_AUTO;
-       }
-
-       if (board_config == AD1884_AUTO)
-               return ad1884_parse_auto_config(codec);
-       else
-               return patch_ad1884_basic(codec);
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1884   ad1884_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
-/*
- * Lenovo Thinkpad T61/X61
- */
-static const struct hda_input_mux ad1984_thinkpad_capture_source = {
-       .num_items = 4,
-       .items = {
-               { "Mic", 0x0 },
-               { "Internal Mic", 0x1 },
-               { "Mix", 0x3 },
-               { "Dock Mic", 0x4 },
-       },
-};
-
-
-/*
- * Dell Precision T3400
- */
-static const struct hda_input_mux ad1984_dell_desktop_capture_source = {
-       .num_items = 3,
-       .items = {
-               { "Front Mic", 0x0 },
-               { "Line-In", 0x1 },
-               { "Mix", 0x3 },
-       },
-};
-
-
-static const struct snd_kcontrol_new ad1984_thinkpad_mixers[] = {
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
-       /* HDA_CODEC_VOLUME_IDX("PCM Playback Volume", 1, 0x03, 0x0, HDA_OUTPUT), */
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_VOLUME("Beep Playback Volume", 0x20, 0x03, HDA_INPUT),
-       HDA_CODEC_MUTE("Beep Playback Switch", 0x20, 0x03, HDA_INPUT),
-       HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
-       HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
-       },
-       /* SPDIF controls */
-       HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
-               /* identical with ad1983 */
-               .info = ad1983_spdif_route_info,
-               .get = ad1983_spdif_route_get,
-               .put = ad1983_spdif_route_put,
-       },
-       { } /* end */
-};
-
-/* additional verbs */
-static const struct hda_verb ad1984_thinkpad_init_verbs[] = {
-       /* Port-E (docking station mic) pin */
-       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* docking mic boost */
-       {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       /* Analog PC Beeper - allow firmware/ACPI beeps */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(3) | 0x1a},
-       /* Analog mixer - docking mic; mute as default */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-       /* enable EAPD bit */
-       {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
-       { } /* end */
-};
-
-/*
- * Dell Precision T3400
- */
-static const struct snd_kcontrol_new ad1984_dell_desktop_mixers[] = {
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x04, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Speaker Playback Switch", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_VOLUME("Line-In Playback Volume", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_MUTE("Line-In Playback Switch", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_VOLUME("Line-In Boost Volume", 0x15, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
-       },
-       { } /* end */
-};
-
-/* Digial MIC ADC NID 0x05 + 0x06 */
-static int ad1984_pcm_dmic_prepare(struct hda_pcm_stream *hinfo,
-                                  struct hda_codec *codec,
-                                  unsigned int stream_tag,
-                                  unsigned int format,
-                                  struct snd_pcm_substream *substream)
-{
-       snd_hda_codec_setup_stream(codec, 0x05 + substream->number,
-                                  stream_tag, 0, format);
-       return 0;
-}
-
-static int ad1984_pcm_dmic_cleanup(struct hda_pcm_stream *hinfo,
-                                  struct hda_codec *codec,
-                                  struct snd_pcm_substream *substream)
-{
-       snd_hda_codec_cleanup_stream(codec, 0x05 + substream->number);
-       return 0;
-}
-
-static const struct hda_pcm_stream ad1984_pcm_dmic_capture = {
-       .substreams = 2,
-       .channels_min = 2,
-       .channels_max = 2,
-       .nid = 0x05,
-       .ops = {
-               .prepare = ad1984_pcm_dmic_prepare,
-               .cleanup = ad1984_pcm_dmic_cleanup
-       },
-};
-
-static int ad1984_build_pcms(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec = codec->spec;
-       struct hda_pcm *info;
-       int err;
-
-       err = ad198x_build_pcms(codec);
-       if (err < 0)
-               return err;
-
-       info = spec->pcm_rec + codec->num_pcms;
-       codec->num_pcms++;
-       info->name = "AD1984 Digital Mic";
-       info->stream[SNDRV_PCM_STREAM_CAPTURE] = ad1984_pcm_dmic_capture;
-       return 0;
-}
-
-/* models */
-enum {
-       AD1984_AUTO,
-       AD1984_BASIC,
-       AD1984_THINKPAD,
-       AD1984_DELL_DESKTOP,
-       AD1984_MODELS
-};
-
-static const char * const ad1984_models[AD1984_MODELS] = {
-       [AD1984_AUTO]           = "auto",
-       [AD1984_BASIC]          = "basic",
-       [AD1984_THINKPAD]       = "thinkpad",
-       [AD1984_DELL_DESKTOP]   = "dell_desktop",
-};
-
-static const struct snd_pci_quirk ad1984_cfg_tbl[] = {
-       /* Lenovo Thinkpad T61/X61 */
-       SND_PCI_QUIRK_VENDOR(0x17aa, "Lenovo Thinkpad", AD1984_THINKPAD),
-       SND_PCI_QUIRK(0x1028, 0x0214, "Dell T3400", AD1984_DELL_DESKTOP),
-       SND_PCI_QUIRK(0x1028, 0x0233, "Dell Latitude E6400", AD1984_DELL_DESKTOP),
-       {}
-};
-
-static int patch_ad1984(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec;
-       int board_config, err;
-
-       board_config = snd_hda_check_board_config(codec, AD1984_MODELS,
-                                                 ad1984_models, ad1984_cfg_tbl);
-       if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
-                      codec->chip_name);
-               board_config = AD1984_AUTO;
-       }
-
-       if (board_config == AD1984_AUTO)
-               return ad1884_parse_auto_config(codec);
-
-       err = patch_ad1884_basic(codec);
-       if (err < 0)
-               return err;
-       spec = codec->spec;
-
-       switch (board_config) {
-       case AD1984_BASIC:
-               /* additional digital mics */
-               spec->mixers[spec->num_mixers++] = ad1984_dmic_mixers;
-               codec->patch_ops.build_pcms = ad1984_build_pcms;
-               break;
-       case AD1984_THINKPAD:
-               if (codec->subsystem_id == 0x17aa20fb) {
-                       /* Thinpad X300 does not have the ability to do SPDIF,
-                          or attach to docking station to use SPDIF */
-                       spec->multiout.dig_out_nid = 0;
-               } else
-                       spec->multiout.dig_out_nid = AD1884_SPDIF_OUT;
-               spec->input_mux = &ad1984_thinkpad_capture_source;
-               spec->mixers[0] = ad1984_thinkpad_mixers;
-               spec->init_verbs[spec->num_init_verbs++] = ad1984_thinkpad_init_verbs;
-               spec->analog_beep = 1;
-               break;
-       case AD1984_DELL_DESKTOP:
-               spec->multiout.dig_out_nid = 0;
-               spec->input_mux = &ad1984_dell_desktop_capture_source;
-               spec->mixers[0] = ad1984_dell_desktop_mixers;
-               break;
-       }
-       return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1984   ad1884_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-
-/*
- * AD1883 / AD1884A / AD1984A / AD1984B
- *
- * port-B (0x14) - front mic-in
- * port-E (0x1c) - rear mic-in
- * port-F (0x16) - CD / ext out
- * port-C (0x15) - rear line-in
- * port-D (0x12) - rear line-out
- * port-A (0x11) - front hp-out
- *
- * AD1984A = AD1884A + digital-mic
- * AD1883 = equivalent with AD1984A
- * AD1984B = AD1984A + extra SPDIF-out
- *
- * FIXME:
- * We share the single DAC for both HP and line-outs (see AD1884/1984).
- */
-
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static const hda_nid_t ad1884a_dac_nids[1] = {
-       0x03,
-};
-
-#define ad1884a_adc_nids       ad1884_adc_nids
-#define ad1884a_capsrc_nids    ad1884_capsrc_nids
-
-#define AD1884A_SPDIF_OUT      0x02
-
-static const struct hda_input_mux ad1884a_capture_source = {
-       .num_items = 5,
-       .items = {
-               { "Front Mic", 0x0 },
-               { "Mic", 0x4 },
-               { "Line", 0x1 },
-               { "CD", 0x2 },
-               { "Mix", 0x3 },
-       },
-};
-
-static const struct snd_kcontrol_new ad1884a_base_mixers[] = {
-       HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x02, HDA_INPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x02, HDA_INPUT),
-       HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Line Boost Volume", 0x15, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
-       },
-       /* SPDIF controls */
-       HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
-               /* identical with ad1983 */
-               .info = ad1983_spdif_route_info,
-               .get = ad1983_spdif_route_get,
-               .put = ad1983_spdif_route_put,
-       },
-       { } /* end */
-};
-
-/*
- * initialization verbs
- */
-static const struct hda_verb ad1884a_init_verbs[] = {
-       /* DACs; unmute as default */
-       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
-       /* Port-A (HP) mixer - route only from analog mixer */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Port-A pin */
-       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* Port-D (Line-out) mixer - route only from analog mixer */
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x0a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Port-D pin */
-       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* Mono-out mixer - route only from analog mixer */
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Mono-out pin */
-       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* Port-B (front mic) pin */
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       /* Port-C (rear line-in) pin */
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       /* Port-E (rear mic) pin */
-       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x25, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* no boost */
-       /* Port-F (CD) pin */
-       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* Analog mixer; mute as default */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(4)}, /* aux */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
-       /* Analog Mix output amp */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* capture sources */
-       {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0},
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* SPDIF output amp */
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
-       { } /* end */
-};
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1884a_loopbacks[] = {
-       { 0x20, HDA_INPUT, 0 }, /* Front Mic */
-       { 0x20, HDA_INPUT, 1 }, /* Mic */
-       { 0x20, HDA_INPUT, 2 }, /* CD */
-       { 0x20, HDA_INPUT, 4 }, /* Docking */
-       { } /* end */
-};
-#endif
-
-/*
- * Laptop model
- *
- * Port A: Headphone jack
- * Port B: MIC jack
- * Port C: Internal MIC
- * Port D: Dock Line Out (if enabled)
- * Port E: Dock Line In (if enabled)
- * Port F: Internal speakers
- */
-
-static int ad1884a_mobile_master_sw_put(struct snd_kcontrol *kcontrol,
-                                       struct snd_ctl_elem_value *ucontrol)
-{
-       struct hda_codec *codec = snd_kcontrol_chip(kcontrol);
-       int ret = snd_hda_mixer_amp_switch_put(kcontrol, ucontrol);
-       int mute = (!ucontrol->value.integer.value[0] &&
-                   !ucontrol->value.integer.value[1]);
-       /* toggle GPIO1 according to the mute state */
-       snd_hda_codec_write_cache(codec, 0x01, 0, AC_VERB_SET_GPIO_DATA,
-                           mute ? 0x02 : 0x0);
-       return ret;
-}
-
-static const struct snd_kcontrol_new ad1884a_laptop_mixers[] = {
-       HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Switch",
-               .subdevice = HDA_SUBDEV_AMP_FLAG,
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
-               .put = ad1884a_mobile_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
-       },
-       HDA_CODEC_MUTE("Dock Playback Switch", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_VOLUME("Internal Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_MUTE("Internal Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_VOLUME("Dock Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
-       HDA_CODEC_MUTE("Dock Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Dock Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1884a_mobile_mixers[] = {
-       HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-       /*HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Master Playback Switch",
-               .subdevice = HDA_SUBDEV_AMP_FLAG,
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
-               .put = ad1884a_mobile_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
-       },
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Capture Volume", 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Internal Mic Capture Volume", 0x15, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
-       { } /* end */
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1884a_hp_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_jack_detect(codec, 0x11);
-       snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-       snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
-                           present ? 0x00 : 0x02);
-}
-
-/* switch to external mic if plugged */
-static void ad1884a_hp_automic(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_jack_detect(codec, 0x14);
-       snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL,
-                           present ? 0 : 1);
-}
-
-#define AD1884A_HP_EVENT               0x37
-#define AD1884A_MIC_EVENT              0x36
-
-/* unsolicited event for HP jack sensing */
-static void ad1884a_hp_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       switch (res >> 26) {
-       case AD1884A_HP_EVENT:
-               ad1884a_hp_automute(codec);
-               break;
-       case AD1884A_MIC_EVENT:
-               ad1884a_hp_automic(codec);
-               break;
-       }
-}
-
-/* initialize jack-sensing, too */
-static int ad1884a_hp_init(struct hda_codec *codec)
-{
-       ad198x_init(codec);
-       ad1884a_hp_automute(codec);
-       ad1884a_hp_automic(codec);
-       return 0;
-}
-
-/* mute internal speaker if HP or docking HP is plugged */
-static void ad1884a_laptop_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_jack_detect(codec, 0x11);
-       if (!present)
-               present = snd_hda_jack_detect(codec, 0x12);
-       snd_hda_codec_amp_stereo(codec, 0x16, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-       snd_hda_codec_write(codec, 0x16, 0, AC_VERB_SET_EAPD_BTLENABLE,
-                           present ? 0x00 : 0x02);
-}
-
-/* switch to external mic if plugged */
-static void ad1884a_laptop_automic(struct hda_codec *codec)
-{
-       unsigned int idx;
-
-       if (snd_hda_jack_detect(codec, 0x14))
-               idx = 0;
-       else if (snd_hda_jack_detect(codec, 0x1c))
-               idx = 4;
-       else
-               idx = 1;
-       snd_hda_codec_write(codec, 0x0c, 0, AC_VERB_SET_CONNECT_SEL, idx);
-}
-
-/* unsolicited event for HP jack sensing */
-static void ad1884a_laptop_unsol_event(struct hda_codec *codec,
-                                      unsigned int res)
-{
-       switch (res >> 26) {
-       case AD1884A_HP_EVENT:
-               ad1884a_laptop_automute(codec);
-               break;
-       case AD1884A_MIC_EVENT:
-               ad1884a_laptop_automic(codec);
-               break;
-       }
-}
-
-/* initialize jack-sensing, too */
-static int ad1884a_laptop_init(struct hda_codec *codec)
-{
-       ad198x_init(codec);
-       ad1884a_laptop_automute(codec);
-       ad1884a_laptop_automic(codec);
-       return 0;
-}
-
-/* additional verbs for laptop model */
-static const struct hda_verb ad1884a_laptop_verbs[] = {
-       /* Port-A (HP) pin - always unmuted */
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       /* Port-F (int speaker) mixer - route only from analog mixer */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Port-F (int speaker) pin */
-       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* required for compaq 6530s/6531s speaker output */
-       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       /* Port-C pin - internal mic-in */
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
-       /* Port-D (docking line-out) pin - default unmuted */
-       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       /* analog mix */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-       /* unsolicited event for pin-sense */
-       {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
-       {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
-       {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
-       {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
-       /* allow to touch GPIO1 (for mute control) */
-       {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
-       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
-       {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
-       { } /* end */
-};
-
-static const struct hda_verb ad1884a_mobile_verbs[] = {
-       /* DACs; unmute as default */
-       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
-       /* Port-A (HP) mixer - route only from analog mixer */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Port-A pin */
-       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       /* Port-A (HP) pin - always unmuted */
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       /* Port-B (mic jack) pin */
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
-       /* Port-C (int mic) pin */
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
-       /* Port-F (int speaker) mixer - route only from analog mixer */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Port-F pin */
-       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* Analog mixer; mute as default */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
-       /* Analog Mix output amp */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* capture sources */
-       /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* unsolicited event for pin-sense */
-       {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
-       {0x14, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
-       /* allow to touch GPIO1 (for mute control) */
-       {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
-       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
-       {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
-       { } /* end */
-};
-
-/*
- * Thinkpad X300
- * 0x11 - HP
- * 0x12 - speaker
- * 0x14 - mic-in
- * 0x17 - built-in mic
- */
-
-static const struct hda_verb ad1984a_thinkpad_verbs[] = {
-       /* HP unmute */
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       /* analog mix */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-       /* turn on EAPD */
-       {0x12, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
-       /* unsolicited event for pin-sense */
-       {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
-       /* internal mic - dmic */
-       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-       /* set magic COEFs for dmic */
-       {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
-       {0x01, AC_VERB_SET_PROC_COEF, 0x08},
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1984a_thinkpad_mixers[] = {
-       HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x14, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Capture Source",
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
-       },
-       { } /* end */
-};
-
-static const struct hda_input_mux ad1984a_thinkpad_capture_source = {
-       .num_items = 3,
-       .items = {
-               { "Mic", 0x0 },
-               { "Internal Mic", 0x5 },
-               { "Mix", 0x3 },
-       },
-};
-
-/* mute internal speaker if HP is plugged */
-static void ad1984a_thinkpad_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_jack_detect(codec, 0x11);
-       snd_hda_codec_amp_stereo(codec, 0x12, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-/* unsolicited event for HP jack sensing */
-static void ad1984a_thinkpad_unsol_event(struct hda_codec *codec,
-                                        unsigned int res)
-{
-       if ((res >> 26) != AD1884A_HP_EVENT)
-               return;
-       ad1984a_thinkpad_automute(codec);
-}
-
-/* initialize jack-sensing, too */
-static int ad1984a_thinkpad_init(struct hda_codec *codec)
-{
-       ad198x_init(codec);
-       ad1984a_thinkpad_automute(codec);
-       return 0;
-}
-
-/*
- * Precision R5500
- * 0x12 - HP/line-out
- * 0x13 - speaker (mono)
- * 0x15 - mic-in
- */
-
-static const struct hda_verb ad1984a_precision_verbs[] = {
-       /* Unmute main output path */
-       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x1f}, /* 0dB */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(5) + 0x17}, /* 0dB */
-       /* Analog mixer; mute as default */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-       /* Select mic as input */
-       {0x0c, AC_VERB_SET_CONNECT_SEL, 0x1},
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE + 0x27}, /* 0dB */
-       /* Configure as mic */
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, 0x7002}, /* raise mic as default */
-       /* HP unmute */
-       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       /* turn on EAPD */
-       {0x13, AC_VERB_SET_EAPD_BTLENABLE, 0x02},
-       /* unsolicited event for pin-sense */
-       {0x12, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1984a_precision_mixers[] = {
-       HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x15, 0x0, HDA_INPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Speaker Playback Volume", 0x13, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
-       { } /* end */
-};
-
-
-/* mute internal speaker if HP is plugged */
-static void ad1984a_precision_automute(struct hda_codec *codec)
-{
-       unsigned int present;
-
-       present = snd_hda_jack_detect(codec, 0x12);
-       snd_hda_codec_amp_stereo(codec, 0x13, HDA_OUTPUT, 0,
-                                HDA_AMP_MUTE, present ? HDA_AMP_MUTE : 0);
-}
-
-
-/* unsolicited event for HP jack sensing */
-static void ad1984a_precision_unsol_event(struct hda_codec *codec,
-                                        unsigned int res)
-{
-       if ((res >> 26) != AD1884A_HP_EVENT)
-               return;
-       ad1984a_precision_automute(codec);
-}
-
-/* initialize jack-sensing, too */
-static int ad1984a_precision_init(struct hda_codec *codec)
-{
-       ad198x_init(codec);
-       ad1984a_precision_automute(codec);
-       return 0;
-}
-
-
-/*
- * HP Touchsmart
- * port-A (0x11)      - front hp-out
- * port-B (0x14)      - unused
- * port-C (0x15)      - unused
- * port-D (0x12)      - rear line out
- * port-E (0x1c)      - front mic-in
- * port-F (0x16)      - Internal speakers
- * digital-mic (0x17) - Internal mic
- */
-
-static const struct hda_verb ad1984a_touchsmart_verbs[] = {
-       /* DACs; unmute as default */
-       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, 0x27}, /* 0dB */
-       /* Port-A (HP) mixer - route only from analog mixer */
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x07, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Port-A pin */
-       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       /* Port-A (HP) pin - always unmuted */
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       /* Port-E (int speaker) mixer - route only from analog mixer */
-       {0x25, AC_VERB_SET_AMP_GAIN_MUTE, 0x03},
-       /* Port-E pin */
-       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-       {0x1c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE},
-       {0x1c, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       /* Port-F (int speaker) mixer - route only from analog mixer */
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x0b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Port-F pin */
-       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* Analog mixer; mute as default */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
-       /* Analog Mix output amp */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* capture sources */
-       /* {0x0c, AC_VERB_SET_CONNECT_SEL, 0x0}, */ /* set via unsol */
-       {0x0c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x0d, AC_VERB_SET_CONNECT_SEL, 0x0},
-       {0x0d, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* unsolicited event for pin-sense */
-       {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_HP_EVENT},
-       {0x1c, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1884A_MIC_EVENT},
-       /* allow to touch GPIO1 (for mute control) */
-       {0x01, AC_VERB_SET_GPIO_MASK, 0x02},
-       {0x01, AC_VERB_SET_GPIO_DIRECTION, 0x02},
-       {0x01, AC_VERB_SET_GPIO_DATA, 0x02}, /* first muted */
-       /* internal mic - dmic */
-       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-       /* set magic COEFs for dmic */
-       {0x01, AC_VERB_SET_COEF_INDEX, 0x13f7},
-       {0x01, AC_VERB_SET_PROC_COEF, 0x08},
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1984a_touchsmart_mixers[] = {
-       HDA_CODEC_VOLUME("Master Playback Volume", 0x21, 0x0, HDA_OUTPUT),
-/*     HDA_CODEC_MUTE("Master Playback Switch", 0x21, 0x0, HDA_OUTPUT),*/
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .subdevice = HDA_SUBDEV_AMP_FLAG,
-               .name = "Master Playback Switch",
-               .info = snd_hda_mixer_amp_switch_info,
-               .get = snd_hda_mixer_amp_switch_get,
-               .put = ad1884a_mobile_master_sw_put,
-               .private_value = HDA_COMPOSE_AMP_VAL(0x21, 3, 0, HDA_OUTPUT),
-       },
-       HDA_CODEC_VOLUME("PCM Playback Volume", 0x20, 0x5, HDA_INPUT),
-       HDA_CODEC_MUTE("PCM Playback Switch", 0x20, 0x5, HDA_INPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x25, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Internal Mic Boost Volume", 0x17, 0x0, HDA_INPUT),
-       { } /* end */
-};
-
-/* switch to external mic if plugged */
-static void ad1984a_touchsmart_automic(struct hda_codec *codec)
-{
-       if (snd_hda_jack_detect(codec, 0x1c))
-               snd_hda_codec_write(codec, 0x0c, 0,
-                                    AC_VERB_SET_CONNECT_SEL, 0x4);
-       else
-               snd_hda_codec_write(codec, 0x0c, 0,
-                                    AC_VERB_SET_CONNECT_SEL, 0x5);
-}
-
-
-/* unsolicited event for HP jack sensing */
-static void ad1984a_touchsmart_unsol_event(struct hda_codec *codec,
-       unsigned int res)
-{
-       switch (res >> 26) {
-       case AD1884A_HP_EVENT:
-               ad1884a_hp_automute(codec);
-               break;
-       case AD1884A_MIC_EVENT:
-               ad1984a_touchsmart_automic(codec);
-               break;
-       }
-}
-
-/* initialize jack-sensing, too */
-static int ad1984a_touchsmart_init(struct hda_codec *codec)
-{
-       ad198x_init(codec);
-       ad1884a_hp_automute(codec);
-       ad1984a_touchsmart_automic(codec);
-       return 0;
-}
-
-
-/*
- */
-
-enum {
-       AD1884A_AUTO,
-       AD1884A_DESKTOP,
-       AD1884A_LAPTOP,
-       AD1884A_MOBILE,
-       AD1884A_THINKPAD,
-       AD1984A_TOUCHSMART,
-       AD1984A_PRECISION,
-       AD1884A_MODELS
-};
-
-static const char * const ad1884a_models[AD1884A_MODELS] = {
-       [AD1884A_AUTO]          = "auto",
-       [AD1884A_DESKTOP]       = "desktop",
-       [AD1884A_LAPTOP]        = "laptop",
-       [AD1884A_MOBILE]        = "mobile",
-       [AD1884A_THINKPAD]      = "thinkpad",
-       [AD1984A_TOUCHSMART]    = "touchsmart",
-       [AD1984A_PRECISION]     = "precision",
-};
-
-static const struct snd_pci_quirk ad1884a_cfg_tbl[] = {
-       SND_PCI_QUIRK(0x1028, 0x04ac, "Precision R5500", AD1984A_PRECISION),
-       SND_PCI_QUIRK(0x103c, 0x3030, "HP", AD1884A_MOBILE),
-       SND_PCI_QUIRK(0x103c, 0x3037, "HP 2230s", AD1884A_LAPTOP),
-       SND_PCI_QUIRK(0x103c, 0x3056, "HP", AD1884A_MOBILE),
-       SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x3070, "HP", AD1884A_MOBILE),
-       SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30d0, "HP laptop", AD1884A_LAPTOP),
-       SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x30e0, "HP laptop", AD1884A_LAPTOP),
-       SND_PCI_QUIRK_MASK(0x103c, 0xff00, 0x3600, "HP laptop", AD1884A_LAPTOP),
-       SND_PCI_QUIRK_MASK(0x103c, 0xfff0, 0x7010, "HP laptop", AD1884A_MOBILE),
-       SND_PCI_QUIRK(0x17aa, 0x20ac, "Thinkpad X300", AD1884A_THINKPAD),
-       SND_PCI_QUIRK(0x103c, 0x2a82, "Touchsmart", AD1984A_TOUCHSMART),
-       {}
-};
-
-static int patch_ad1884a(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec;
-       int err, board_config;
-
-       board_config = snd_hda_check_board_config(codec, AD1884A_MODELS,
-                                                 ad1884a_models,
-                                                 ad1884a_cfg_tbl);
-       if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
-                      codec->chip_name);
-               board_config = AD1884A_AUTO;
-       }
-
-       if (board_config == AD1884A_AUTO)
-               return ad1884_parse_auto_config(codec);
-
-       err = alloc_ad_spec(codec);
-       if (err < 0)
-               return err;
-       spec = codec->spec;
-
-       err = snd_hda_attach_beep_device(codec, 0x10);
-       if (err < 0) {
-               ad198x_free(codec);
-               return err;
-       }
-       set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
-       spec->multiout.max_channels = 2;
-       spec->multiout.num_dacs = ARRAY_SIZE(ad1884a_dac_nids);
-       spec->multiout.dac_nids = ad1884a_dac_nids;
-       spec->multiout.dig_out_nid = AD1884A_SPDIF_OUT;
-       spec->num_adc_nids = ARRAY_SIZE(ad1884a_adc_nids);
-       spec->adc_nids = ad1884a_adc_nids;
-       spec->capsrc_nids = ad1884a_capsrc_nids;
-       spec->input_mux = &ad1884a_capture_source;
-       spec->num_mixers = 1;
-       spec->mixers[0] = ad1884a_base_mixers;
-       spec->num_init_verbs = 1;
-       spec->init_verbs[0] = ad1884a_init_verbs;
-       spec->spdif_route = 0;
-#ifdef CONFIG_PM
-       spec->loopback.amplist = ad1884a_loopbacks;
-#endif
-       codec->patch_ops = ad198x_patch_ops;
-
-       /* override some parameters */
-       switch (board_config) {
-       case AD1884A_LAPTOP:
-               spec->mixers[0] = ad1884a_laptop_mixers;
-               spec->init_verbs[spec->num_init_verbs++] = ad1884a_laptop_verbs;
-               spec->multiout.dig_out_nid = 0;
-               codec->patch_ops.unsol_event = ad1884a_laptop_unsol_event;
-               codec->patch_ops.init = ad1884a_laptop_init;
-               /* set the upper-limit for mixer amp to 0dB for avoiding the
-                * possible damage by overloading
-                */
-               snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
-                                         (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
-                                         (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
-                                         (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
-                                         (1 << AC_AMPCAP_MUTE_SHIFT));
-               break;
-       case AD1884A_MOBILE:
-               spec->mixers[0] = ad1884a_mobile_mixers;
-               spec->init_verbs[0] = ad1884a_mobile_verbs;
-               spec->multiout.dig_out_nid = 0;
-               codec->patch_ops.unsol_event = ad1884a_hp_unsol_event;
-               codec->patch_ops.init = ad1884a_hp_init;
-               /* set the upper-limit for mixer amp to 0dB for avoiding the
-                * possible damage by overloading
-                */
-               snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
-                                         (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
-                                         (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
-                                         (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
-                                         (1 << AC_AMPCAP_MUTE_SHIFT));
-               break;
-       case AD1884A_THINKPAD:
-               spec->mixers[0] = ad1984a_thinkpad_mixers;
-               spec->init_verbs[spec->num_init_verbs++] =
-                       ad1984a_thinkpad_verbs;
-               spec->multiout.dig_out_nid = 0;
-               spec->input_mux = &ad1984a_thinkpad_capture_source;
-               codec->patch_ops.unsol_event = ad1984a_thinkpad_unsol_event;
-               codec->patch_ops.init = ad1984a_thinkpad_init;
-               break;
-       case AD1984A_PRECISION:
-               spec->mixers[0] = ad1984a_precision_mixers;
-               spec->init_verbs[spec->num_init_verbs++] =
-                       ad1984a_precision_verbs;
-               spec->multiout.dig_out_nid = 0;
-               codec->patch_ops.unsol_event = ad1984a_precision_unsol_event;
-               codec->patch_ops.init = ad1984a_precision_init;
-               break;
-       case AD1984A_TOUCHSMART:
-               spec->mixers[0] = ad1984a_touchsmart_mixers;
-               spec->init_verbs[0] = ad1984a_touchsmart_verbs;
-               spec->multiout.dig_out_nid = 0;
-               codec->patch_ops.unsol_event = ad1984a_touchsmart_unsol_event;
-               codec->patch_ops.init = ad1984a_touchsmart_init;
-               /* set the upper-limit for mixer amp to 0dB for avoiding the
-                * possible damage by overloading
-                */
-               snd_hda_override_amp_caps(codec, 0x20, HDA_INPUT,
-                                         (0x17 << AC_AMPCAP_OFFSET_SHIFT) |
-                                         (0x17 << AC_AMPCAP_NUM_STEPS_SHIFT) |
-                                         (0x05 << AC_AMPCAP_STEP_SIZE_SHIFT) |
-                                         (1 << AC_AMPCAP_MUTE_SHIFT));
-               break;
-       }
-
-       codec->no_trigger_sense = 1;
-       codec->no_sticky_stream = 1;
-
-       return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1884a  ad1884_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-
 /*
  * AD1882 / AD1882A
  *
@@ -4850,299 +1058,7 @@ static int patch_ad1884a(struct hda_codec *codec)
  * port-G - rear clfe-out (6stack)
  */
 
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static const hda_nid_t ad1882_dac_nids[3] = {
-       0x04, 0x03, 0x05
-};
-
-static const hda_nid_t ad1882_adc_nids[2] = {
-       0x08, 0x09,
-};
-
-static const hda_nid_t ad1882_capsrc_nids[2] = {
-       0x0c, 0x0d,
-};
-
-#define AD1882_SPDIF_OUT       0x02
-
-/* list: 0x11, 0x39, 0x3a, 0x18, 0x3c, 0x3b, 0x12, 0x20 */
-static const struct hda_input_mux ad1882_capture_source = {
-       .num_items = 5,
-       .items = {
-               { "Front Mic", 0x1 },
-               { "Mic", 0x4 },
-               { "Line", 0x2 },
-               { "CD", 0x3 },
-               { "Mix", 0x7 },
-       },
-};
-
-/* list: 0x11, 0x39, 0x3a, 0x3c, 0x18, 0x1f, 0x12, 0x20 */
-static const struct hda_input_mux ad1882a_capture_source = {
-       .num_items = 5,
-       .items = {
-               { "Front Mic", 0x1 },
-               { "Mic", 0x4},
-               { "Line", 0x2 },
-               { "Digital Mic", 0x06 },
-               { "Mix", 0x7 },
-       },
-};
-
-static const struct snd_kcontrol_new ad1882_base_mixers[] = {
-       HDA_CODEC_VOLUME("Front Playback Volume", 0x04, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Surround Playback Volume", 0x03, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Center Playback Volume", 0x05, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("LFE Playback Volume", 0x05, 2, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Headphone Playback Switch", 0x11, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Front Playback Switch", 0x12, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_MONO("Mono Playback Volume", 0x13, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("Mono Playback Switch", 0x13, 1, 0x0, HDA_OUTPUT),
-
-       HDA_CODEC_VOLUME("Mic Boost Volume", 0x3c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Front Mic Boost Volume", 0x39, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Line-In Boost Volume", 0x3a, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME("Capture Volume", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE("Capture Switch", 0x0c, 0x0, HDA_OUTPUT),
-       HDA_CODEC_VOLUME_IDX("Capture Volume", 1, 0x0d, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_IDX("Capture Switch", 1, 0x0d, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               /* The multiple "Capture Source" controls confuse alsamixer
-                * So call somewhat different..
-                */
-               /* .name = "Capture Source", */
-               .name = "Input Source",
-               .count = 2,
-               .info = ad198x_mux_enum_info,
-               .get = ad198x_mux_enum_get,
-               .put = ad198x_mux_enum_put,
-       },
-       /* SPDIF controls */
-       HDA_CODEC_VOLUME("IEC958 Playback Volume", 0x1b, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = SNDRV_CTL_NAME_IEC958("",PLAYBACK,NONE) "Source",
-               /* identical with ad1983 */
-               .info = ad1983_spdif_route_info,
-               .get = ad1983_spdif_route_get,
-               .put = ad1983_spdif_route_put,
-       },
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1882_loopback_mixers[] = {
-       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x04, HDA_INPUT),
-       HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x04, HDA_INPUT),
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1882a_loopback_mixers[] = {
-       HDA_CODEC_VOLUME("Front Mic Playback Volume", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_MUTE("Front Mic Playback Switch", 0x20, 0x00, HDA_INPUT),
-       HDA_CODEC_VOLUME("Mic Playback Volume", 0x20, 0x04, HDA_INPUT),
-       HDA_CODEC_MUTE("Mic Playback Switch", 0x20, 0x04, HDA_INPUT),
-       HDA_CODEC_VOLUME("Line Playback Volume", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_MUTE("Line Playback Switch", 0x20, 0x01, HDA_INPUT),
-       HDA_CODEC_VOLUME("CD Playback Volume", 0x20, 0x06, HDA_INPUT),
-       HDA_CODEC_MUTE("CD Playback Switch", 0x20, 0x06, HDA_INPUT),
-       HDA_CODEC_VOLUME("Digital Mic Boost Volume", 0x1f, 0x0, HDA_INPUT),
-       { } /* end */
-};
-
-static const struct snd_kcontrol_new ad1882_3stack_mixers[] = {
-       HDA_CODEC_MUTE("Surround Playback Switch", 0x15, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x17, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x17, 2, 0x0, HDA_OUTPUT),
-       {
-               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
-               .name = "Channel Mode",
-               .info = ad198x_ch_mode_info,
-               .get = ad198x_ch_mode_get,
-               .put = ad198x_ch_mode_put,
-       },
-       { } /* end */
-};
-
-/* simple auto-mute control for AD1882 3-stack board */
-#define AD1882_HP_EVENT        0x01
-
-static void ad1882_3stack_automute(struct hda_codec *codec)
-{
-       bool mute = snd_hda_jack_detect(codec, 0x11);
-       snd_hda_codec_write(codec, 0x12, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
-                           mute ? 0 : PIN_OUT);
-}
-
-static int ad1882_3stack_automute_init(struct hda_codec *codec)
-{
-       ad198x_init(codec);
-       ad1882_3stack_automute(codec);
-       return 0;
-}
-
-static void ad1882_3stack_unsol_event(struct hda_codec *codec, unsigned int res)
-{
-       switch (res >> 26) {
-       case AD1882_HP_EVENT:
-               ad1882_3stack_automute(codec);
-               break;
-       }
-}
-
-static const struct snd_kcontrol_new ad1882_6stack_mixers[] = {
-       HDA_CODEC_MUTE("Surround Playback Switch", 0x16, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("Center Playback Switch", 0x24, 1, 0x0, HDA_OUTPUT),
-       HDA_CODEC_MUTE_MONO("LFE Playback Switch", 0x24, 2, 0x0, HDA_OUTPUT),
-       { } /* end */
-};
-
-static const struct hda_verb ad1882_ch2_init[] = {
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       { } /* end */
-};
-
-static const struct hda_verb ad1882_ch4_init[] = {
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       { } /* end */
-};
-
-static const struct hda_verb ad1882_ch6_init[] = {
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       { } /* end */
-};
-
-static const struct hda_channel_mode ad1882_modes[3] = {
-       { 2, ad1882_ch2_init },
-       { 4, ad1882_ch4_init },
-       { 6, ad1882_ch6_init },
-};
-
-/*
- * initialization verbs
- */
-static const struct hda_verb ad1882_init_verbs[] = {
-       /* DACs; mute as default */
-       {0x03, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x04, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       {0x05, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO},
-       /* Port-A (HP) mixer */
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x22, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Port-A pin */
-       {0x11, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       {0x11, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* HP selector - select DAC2 */
-       {0x37, AC_VERB_SET_CONNECT_SEL, 0x1},
-       /* Port-D (Line-out) mixer */
-       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x29, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Port-D pin */
-       {0x12, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       {0x12, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* Mono-out mixer */
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(0)},
-       {0x1e, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_UNMUTE(1)},
-       /* Mono-out pin */
-       {0x13, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_HP},
-       {0x13, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* Port-B (front mic) pin */
-       {0x14, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x14, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x39, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
-       /* Port-C (line-in) pin */
-       {0x15, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_IN},
-       {0x15, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x3a, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
-       /* Port-C mixer - mute as input */
-       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x2c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       /* Port-E (mic-in) pin */
-       {0x17, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_VREF80},
-       {0x17, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       {0x3c, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_ZERO}, /* boost */
-       /* Port-E mixer - mute as input */
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x26, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       /* Port-F (surround) */
-       {0x16, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x16, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* Port-G (CLFE) */
-       {0x24, AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT},
-       {0x24, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE},
-       /* Analog mixer; mute as default */
-       /* list: 0x39, 0x3a, 0x11, 0x12, 0x3c, 0x3b, 0x18, 0x1a */
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(0)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(1)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(2)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(3)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(4)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(5)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(6)},
-       {0x20, AC_VERB_SET_AMP_GAIN_MUTE, AMP_IN_MUTE(7)},
-       /* Analog Mix output amp */
-       {0x21, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x1f}, /* 0dB */
-       /* SPDIF output selector */
-       {0x02, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
-       {0x02, AC_VERB_SET_CONNECT_SEL, 0x0}, /* PCM */
-       {0x1b, AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_UNMUTE | 0x27}, /* 0dB */
-       { } /* end */
-};
-
-static const struct hda_verb ad1882_3stack_automute_verbs[] = {
-       {0x11, AC_VERB_SET_UNSOLICITED_ENABLE, AC_USRSP_EN | AD1882_HP_EVENT},
-       { } /* end */
-};
-
-#ifdef CONFIG_PM
-static const struct hda_amp_list ad1882_loopbacks[] = {
-       { 0x20, HDA_INPUT, 0 }, /* Front Mic */
-       { 0x20, HDA_INPUT, 1 }, /* Mic */
-       { 0x20, HDA_INPUT, 4 }, /* Line */
-       { 0x20, HDA_INPUT, 6 }, /* CD */
-       { } /* end */
-};
-#endif
-
-/* models */
-enum {
-       AD1882_AUTO,
-       AD1882_3STACK,
-       AD1882_6STACK,
-       AD1882_3STACK_AUTOMUTE,
-       AD1882_MODELS
-};
-
-static const char * const ad1882_models[AD1986A_MODELS] = {
-       [AD1882_AUTO]           = "auto",
-       [AD1882_3STACK]         = "3stack",
-       [AD1882_6STACK]         = "6stack",
-       [AD1882_3STACK_AUTOMUTE] = "3stack-automute",
-};
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
-static int ad1882_parse_auto_config(struct hda_codec *codec)
+static int patch_ad1882(struct hda_codec *codec)
 {
        struct ad198x_spec *spec;
        int err;
@@ -5169,110 +1085,20 @@ static int ad1882_parse_auto_config(struct hda_codec *codec)
        return err;
 }
 
-#ifdef ENABLE_AD_STATIC_QUIRKS
-static int patch_ad1882(struct hda_codec *codec)
-{
-       struct ad198x_spec *spec;
-       int err, board_config;
-
-       board_config = snd_hda_check_board_config(codec, AD1882_MODELS,
-                                                 ad1882_models, NULL);
-       if (board_config < 0) {
-               printk(KERN_INFO "hda_codec: %s: BIOS auto-probing.\n",
-                      codec->chip_name);
-               board_config = AD1882_AUTO;
-       }
-
-       if (board_config == AD1882_AUTO)
-               return ad1882_parse_auto_config(codec);
-
-       err = alloc_ad_spec(codec);
-       if (err < 0)
-               return err;
-       spec = codec->spec;
-
-       err = snd_hda_attach_beep_device(codec, 0x10);
-       if (err < 0) {
-               ad198x_free(codec);
-               return err;
-       }
-       set_beep_amp(spec, 0x10, 0, HDA_OUTPUT);
-
-       spec->multiout.max_channels = 6;
-       spec->multiout.num_dacs = 3;
-       spec->multiout.dac_nids = ad1882_dac_nids;
-       spec->multiout.dig_out_nid = AD1882_SPDIF_OUT;
-       spec->num_adc_nids = ARRAY_SIZE(ad1882_adc_nids);
-       spec->adc_nids = ad1882_adc_nids;
-       spec->capsrc_nids = ad1882_capsrc_nids;
-       if (codec->vendor_id == 0x11d41882)
-               spec->input_mux = &ad1882_capture_source;
-       else
-               spec->input_mux = &ad1882a_capture_source;
-       spec->num_mixers = 2;
-       spec->mixers[0] = ad1882_base_mixers;
-       if (codec->vendor_id == 0x11d41882)
-               spec->mixers[1] = ad1882_loopback_mixers;
-       else
-               spec->mixers[1] = ad1882a_loopback_mixers;
-       spec->num_init_verbs = 1;
-       spec->init_verbs[0] = ad1882_init_verbs;
-       spec->spdif_route = 0;
-#ifdef CONFIG_PM
-       spec->loopback.amplist = ad1882_loopbacks;
-#endif
-       spec->vmaster_nid = 0x04;
-
-       codec->patch_ops = ad198x_patch_ops;
-
-       /* override some parameters */
-       switch (board_config) {
-       default:
-       case AD1882_3STACK:
-       case AD1882_3STACK_AUTOMUTE:
-               spec->num_mixers = 3;
-               spec->mixers[2] = ad1882_3stack_mixers;
-               spec->channel_mode = ad1882_modes;
-               spec->num_channel_mode = ARRAY_SIZE(ad1882_modes);
-               spec->need_dac_fix = 1;
-               spec->multiout.max_channels = 2;
-               spec->multiout.num_dacs = 1;
-               if (board_config != AD1882_3STACK) {
-                       spec->init_verbs[spec->num_init_verbs++] =
-                               ad1882_3stack_automute_verbs;
-                       codec->patch_ops.unsol_event = ad1882_3stack_unsol_event;
-                       codec->patch_ops.init = ad1882_3stack_automute_init;
-               }
-               break;
-       case AD1882_6STACK:
-               spec->num_mixers = 3;
-               spec->mixers[2] = ad1882_6stack_mixers;
-               break;
-       }
-
-       codec->no_trigger_sense = 1;
-       codec->no_sticky_stream = 1;
-
-       return 0;
-}
-#else /* ENABLE_AD_STATIC_QUIRKS */
-#define patch_ad1882   ad1882_parse_auto_config
-#endif /* ENABLE_AD_STATIC_QUIRKS */
-
 
 /*
  * patch entries
  */
 static const struct hda_codec_preset snd_hda_preset_analog[] = {
-       { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884a },
+       { .id = 0x11d4184a, .name = "AD1884A", .patch = patch_ad1884 },
        { .id = 0x11d41882, .name = "AD1882", .patch = patch_ad1882 },
-       { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884a },
+       { .id = 0x11d41883, .name = "AD1883", .patch = patch_ad1884 },
        { .id = 0x11d41884, .name = "AD1884", .patch = patch_ad1884 },
-       { .id = 0x11d4194a, .name = "AD1984A", .patch = patch_ad1884a },
-       { .id = 0x11d4194b, .name = "AD1984B", .patch = patch_ad1884a },
+       { .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_ad1984 },
+       { .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 },
index de00ce166470d5c73ac646c452f8a3ccd86e717a..4edd2d0f9a3ce66e625f7576b3b7053548397a51 100644 (file)
@@ -66,6 +66,8 @@ struct conexant_spec {
        hda_nid_t eapds[4];
        bool dynamic_eapd;
 
+       unsigned int parse_flags; /* flag for snd_hda_parse_pin_defcfg() */
+
 #ifdef ENABLE_CXT_STATIC_QUIRKS
        const struct snd_kcontrol_new *mixers[5];
        int num_mixers;
@@ -3200,6 +3202,9 @@ static int cx_auto_init(struct hda_codec *codec)
        snd_hda_gen_init(codec);
        if (!spec->dynamic_eapd)
                cx_auto_turn_eapd(codec, spec->num_eapds, spec->eapds, true);
+
+       snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_INIT);
+
        return 0;
 }
 
@@ -3224,6 +3229,8 @@ enum {
        CXT_PINCFG_LEMOTE_A1205,
        CXT_FIXUP_STEREO_DMIC,
        CXT_FIXUP_INC_MIC_BOOST,
+       CXT_FIXUP_HEADPHONE_MIC_PIN,
+       CXT_FIXUP_HEADPHONE_MIC,
 };
 
 static void cxt_fixup_stereo_dmic(struct hda_codec *codec,
@@ -3246,6 +3253,59 @@ static void cxt5066_increase_mic_boost(struct hda_codec *codec,
                                  (0 << AC_AMPCAP_MUTE_SHIFT));
 }
 
+static void cxt_update_headset_mode(struct hda_codec *codec)
+{
+       /* The verbs used in this function were tested on a Conexant CX20751/2 codec. */
+       int i;
+       bool mic_mode = false;
+       struct conexant_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+
+       hda_nid_t mux_pin = spec->gen.imux_pins[spec->gen.cur_mux[0]];
+
+       for (i = 0; i < cfg->num_inputs; i++)
+               if (cfg->inputs[i].pin == mux_pin) {
+                       mic_mode = !!cfg->inputs[i].is_headphone_mic;
+                       break;
+               }
+
+       if (mic_mode) {
+               snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x7c); /* enable merged mode for analog int-mic */
+               spec->gen.hp_jack_present = false;
+       } else {
+               snd_hda_codec_write_cache(codec, 0x1c, 0, 0x410, 0x54); /* disable merged mode for analog int-mic */
+               spec->gen.hp_jack_present = snd_hda_jack_detect(codec, spec->gen.autocfg.hp_pins[0]);
+       }
+
+       snd_hda_gen_update_outputs(codec);
+}
+
+static void cxt_update_headset_mode_hook(struct hda_codec *codec,
+                            struct snd_ctl_elem_value *ucontrol)
+{
+       cxt_update_headset_mode(codec);
+}
+
+static void cxt_fixup_headphone_mic(struct hda_codec *codec,
+                                   const struct hda_fixup *fix, int action)
+{
+       struct conexant_spec *spec = codec->spec;
+
+       switch (action) {
+       case HDA_FIXUP_ACT_PRE_PROBE:
+               spec->parse_flags |= HDA_PINCFG_HEADPHONE_MIC;
+               break;
+       case HDA_FIXUP_ACT_PROBE:
+               spec->gen.cap_sync_hook = cxt_update_headset_mode_hook;
+               spec->gen.automute_hook = cxt_update_headset_mode;
+               break;
+       case HDA_FIXUP_ACT_INIT:
+               cxt_update_headset_mode(codec);
+               break;
+       }
+}
+
+
 /* ThinkPad X200 & co with cxt5051 */
 static const struct hda_pintbl cxt_pincfg_lenovo_x200[] = {
        { 0x16, 0x042140ff }, /* HP (seq# overridden) */
@@ -3302,6 +3362,19 @@ static const struct hda_fixup cxt_fixups[] = {
                .type = HDA_FIXUP_FUNC,
                .v.func = cxt5066_increase_mic_boost,
        },
+       [CXT_FIXUP_HEADPHONE_MIC_PIN] = {
+               .type = HDA_FIXUP_PINS,
+               .chained = true,
+               .chain_id = CXT_FIXUP_HEADPHONE_MIC,
+               .v.pins = (const struct hda_pintbl[]) {
+                       { 0x18, 0x03a1913d }, /* use as headphone mic, without its own jack detect */
+                       { }
+               }
+       },
+       [CXT_FIXUP_HEADPHONE_MIC] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = cxt_fixup_headphone_mic,
+       },
 };
 
 static const struct snd_pci_quirk cxt5051_fixups[] = {
@@ -3311,6 +3384,7 @@ static const struct snd_pci_quirk cxt5051_fixups[] = {
 
 static const struct snd_pci_quirk cxt5066_fixups[] = {
        SND_PCI_QUIRK(0x1025, 0x0543, "Acer Aspire One 522", CXT_FIXUP_STEREO_DMIC),
+       SND_PCI_QUIRK(0x1043, 0x138d, "Asus", CXT_FIXUP_HEADPHONE_MIC_PIN),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Lenovo T400", CXT_PINCFG_LENOVO_TP410),
        SND_PCI_QUIRK(0x17aa, 0x215e, "Lenovo T410", CXT_PINCFG_LENOVO_TP410),
        SND_PCI_QUIRK(0x17aa, 0x215f, "Lenovo T510", CXT_PINCFG_LENOVO_TP410),
@@ -3395,7 +3469,8 @@ static int patch_conexant_auto(struct hda_codec *codec)
 
        snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PRE_PROBE);
 
-       err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL, 0);
+       err = snd_hda_parse_pin_defcfg(codec, &spec->gen.autocfg, NULL,
+                                      spec->parse_flags);
        if (err < 0)
                goto error;
 
@@ -3416,6 +3491,8 @@ static int patch_conexant_auto(struct hda_codec *codec)
                codec->bus->allow_bus_reset = 1;
        }
 
+       snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
+
        return 0;
 
  error:
index 030ca8652a1cb3b5b8f825d684b69ec1bb2daacc..895a0d3320b48eded617aa8b8d282521beb3c72b 100644 (file)
@@ -959,6 +959,7 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
        int pin_nid;
        int pin_idx;
        struct hda_jack_tbl *jack;
+       int dev_entry = (res & AC_UNSOL_RES_DE) >> AC_UNSOL_RES_DE_SHIFT;
 
        jack = snd_hda_jack_tbl_get_from_tag(codec, tag);
        if (!jack)
@@ -967,8 +968,8 @@ static void hdmi_intrinsic_event(struct hda_codec *codec, unsigned int res)
        jack->jack_dirty = 1;
 
        _snd_printd(SND_PR_VERBOSE,
-               "HDMI hot plug event: Codec=%d Pin=%d Presence_Detect=%d ELD_Valid=%d\n",
-               codec->addr, pin_nid,
+               "HDMI hot plug event: Codec=%d Pin=%d Device=%d Inactive=%d Presence_Detect=%d ELD_Valid=%d\n",
+               codec->addr, pin_nid, dev_entry, !!(res & AC_UNSOL_RES_IA),
                !!(res & AC_UNSOL_RES_PD), !!(res & AC_UNSOL_RES_ELDV));
 
        pin_idx = pin_nid_to_pin_index(spec, pin_nid);
@@ -1989,8 +1990,10 @@ static int patch_generic_hdmi(struct hda_codec *codec)
                return -EINVAL;
        }
        codec->patch_ops = generic_hdmi_patch_ops;
-       if (codec->vendor_id == 0x80862807)
+       if (codec->vendor_id == 0x80862807) {
                codec->patch_ops.set_power_state = haswell_set_power_state;
+               codec->dp_mst = true;
+       }
 
        generic_hdmi_init_per_pins(codec);
 
index f303cd898515d5f20000ec5138400bfd93e63d85..4a909170b59e8e67c4a84ac6d46bb454778dd200 100644 (file)
@@ -282,6 +282,7 @@ static void alc_eapd_shutup(struct hda_codec *codec)
 {
        alc_auto_setup_eapd(codec, false);
        msleep(200);
+       snd_hda_shutup_pins(codec);
 }
 
 /* generic EAPD initialization */
@@ -826,7 +827,8 @@ static inline void alc_shutup(struct hda_codec *codec)
 
        if (spec && spec->shutup)
                spec->shutup(codec);
-       snd_hda_shutup_pins(codec);
+       else
+               snd_hda_shutup_pins(codec);
 }
 
 #define alc_free       snd_hda_gen_free
@@ -1853,8 +1855,10 @@ static void alc882_fixup_no_primary_hp(struct hda_codec *codec,
                                       const struct hda_fixup *fix, int action)
 {
        struct alc_spec *spec = codec->spec;
-       if (action == HDA_FIXUP_ACT_PRE_PROBE)
+       if (action == HDA_FIXUP_ACT_PRE_PROBE) {
                spec->gen.no_primary_hp = 1;
+               spec->gen.no_multi_io = 1;
+       }
 }
 
 static const struct hda_fixup alc882_fixups[] = {
@@ -2533,6 +2537,7 @@ enum {
        ALC269_TYPE_ALC269VD,
        ALC269_TYPE_ALC280,
        ALC269_TYPE_ALC282,
+       ALC269_TYPE_ALC283,
        ALC269_TYPE_ALC284,
        ALC269_TYPE_ALC286,
 };
@@ -2558,6 +2563,7 @@ static int alc269_parse_auto_config(struct hda_codec *codec)
        case ALC269_TYPE_ALC269VB:
        case ALC269_TYPE_ALC269VD:
        case ALC269_TYPE_ALC282:
+       case ALC269_TYPE_ALC283:
        case ALC269_TYPE_ALC286:
                ssids = alc269_ssids;
                break;
@@ -2583,15 +2589,81 @@ static void alc269_shutup(struct hda_codec *codec)
 {
        struct alc_spec *spec = codec->spec;
 
-       if (spec->codec_variant != ALC269_TYPE_ALC269VB)
-               return;
-
        if (spec->codec_variant == ALC269_TYPE_ALC269VB)
                alc269vb_toggle_power_output(codec, 0);
        if (spec->codec_variant == ALC269_TYPE_ALC269VB &&
                        (alc_get_coef0(codec) & 0x00ff) == 0x018) {
                msleep(150);
        }
+       snd_hda_shutup_pins(codec);
+}
+
+static void alc283_init(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+       bool hp_pin_sense;
+       int val;
+
+       if (!hp_pin)
+               return;
+       hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+       /* Index 0x43 Direct Drive HP AMP LPM Control 1 */
+       /* Headphone capless set to high power mode */
+       alc_write_coef_idx(codec, 0x43, 0x9004);
+
+       snd_hda_codec_write(codec, hp_pin, 0,
+                           AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+       if (hp_pin_sense)
+               msleep(85);
+
+       snd_hda_codec_write(codec, hp_pin, 0,
+                           AC_VERB_SET_PIN_WIDGET_CONTROL, PIN_OUT);
+
+       if (hp_pin_sense)
+               msleep(85);
+       /* Index 0x46 Combo jack auto switch control 2 */
+       /* 3k pull low control for Headset jack. */
+       val = alc_read_coef_idx(codec, 0x46);
+       alc_write_coef_idx(codec, 0x46, val & ~(3 << 12));
+       /* Headphone capless set to normal mode */
+       alc_write_coef_idx(codec, 0x43, 0x9614);
+}
+
+static void alc283_shutup(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       hda_nid_t hp_pin = spec->gen.autocfg.hp_pins[0];
+       bool hp_pin_sense;
+       int val;
+
+       if (!hp_pin) {
+               alc269_shutup(codec);
+               return;
+       }
+
+       hp_pin_sense = snd_hda_jack_detect(codec, hp_pin);
+
+       alc_write_coef_idx(codec, 0x43, 0x9004);
+
+       snd_hda_codec_write(codec, hp_pin, 0,
+                           AC_VERB_SET_AMP_GAIN_MUTE, AMP_OUT_MUTE);
+
+       if (hp_pin_sense)
+               msleep(85);
+
+       snd_hda_codec_write(codec, hp_pin, 0,
+                           AC_VERB_SET_PIN_WIDGET_CONTROL, 0x0);
+
+       val = alc_read_coef_idx(codec, 0x46);
+       alc_write_coef_idx(codec, 0x46, val | (3 << 12));
+
+       if (hp_pin_sense)
+               msleep(85);
+       snd_hda_shutup_pins(codec);
+       alc_write_coef_idx(codec, 0x43, 0x9614);
 }
 
 static void alc5505_coef_set(struct hda_codec *codec, unsigned int index_reg,
@@ -2722,6 +2794,7 @@ static int alc269_resume(struct hda_codec *codec)
        hda_call_check_power_status(codec, 0x01);
        if (spec->has_alc5505_dsp)
                alc5505_dsp_resume(codec);
+
        return 0;
 }
 #endif /* CONFIG_PM */
@@ -3261,6 +3334,28 @@ static void alc_fixup_headset_mode_alc668(struct hda_codec *codec,
        alc_fixup_headset_mode(codec, fix, action);
 }
 
+/* Returns the nid of the external mic input pin, or 0 if it cannot be found. */
+static int find_ext_mic_pin(struct hda_codec *codec)
+{
+       struct alc_spec *spec = codec->spec;
+       struct auto_pin_cfg *cfg = &spec->gen.autocfg;
+       hda_nid_t nid;
+       unsigned int defcfg;
+       int i;
+
+       for (i = 0; i < cfg->num_inputs; i++) {
+               if (cfg->inputs[i].type != AUTO_PIN_MIC)
+                       continue;
+               nid = cfg->inputs[i].pin;
+               defcfg = snd_hda_codec_get_pincfg(codec, nid);
+               if (snd_hda_get_input_pin_attr(defcfg) == INPUT_PIN_ATTR_INT)
+                       continue;
+               return nid;
+       }
+
+       return 0;
+}
+
 static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
                                    const struct hda_fixup *fix,
                                    int action)
@@ -3268,11 +3363,12 @@ static void alc271_hp_gate_mic_jack(struct hda_codec *codec,
        struct alc_spec *spec = codec->spec;
 
        if (action == HDA_FIXUP_ACT_PROBE) {
-               if (snd_BUG_ON(!spec->gen.am_entry[1].pin ||
-                              !spec->gen.autocfg.hp_pins[0]))
+               int mic_pin = find_ext_mic_pin(codec);
+               int hp_pin = spec->gen.autocfg.hp_pins[0];
+
+               if (snd_BUG_ON(!mic_pin || !hp_pin))
                        return;
-               snd_hda_jack_set_gating_jack(codec, spec->gen.am_entry[1].pin,
-                                            spec->gen.autocfg.hp_pins[0]);
+               snd_hda_jack_set_gating_jack(codec, mic_pin, hp_pin);
        }
 }
 
@@ -3308,6 +3404,45 @@ static void alc269_fixup_limit_int_mic_boost(struct hda_codec *codec,
        }
 }
 
+static void alc283_hp_automute_hook(struct hda_codec *codec,
+                                   struct hda_jack_tbl *jack)
+{
+       struct alc_spec *spec = codec->spec;
+       int vref;
+
+       msleep(200);
+       snd_hda_gen_hp_automute(codec, jack);
+
+       vref = spec->gen.hp_jack_present ? PIN_VREF80 : 0;
+
+       msleep(600);
+       snd_hda_codec_write(codec, 0x19, 0, AC_VERB_SET_PIN_WIDGET_CONTROL,
+                           vref);
+}
+
+static void alc283_chromebook_caps(struct hda_codec *codec)
+{
+       snd_hda_override_wcaps(codec, 0x03, 0);
+}
+
+static void alc283_fixup_chromebook(struct hda_codec *codec,
+                                   const struct hda_fixup *fix, int action)
+{
+       struct alc_spec *spec = codec->spec;
+       int val;
+
+       switch (action) {
+       case HDA_FIXUP_ACT_PRE_PROBE:
+               alc283_chromebook_caps(codec);
+               spec->gen.hp_automute_hook = alc283_hp_automute_hook;
+               /* MIC2-VREF control */
+               /* Set to manual mode */
+               val = alc_read_coef_idx(codec, 0x06);
+               alc_write_coef_idx(codec, 0x06, val & ~0x000c);
+               break;
+       }
+}
+
 enum {
        ALC269_FIXUP_SONY_VAIO,
        ALC275_FIXUP_SONY_VAIO_GPIO2,
@@ -3344,6 +3479,7 @@ enum {
        ALC269_FIXUP_ACER_AC700,
        ALC269_FIXUP_LIMIT_INT_MIC_BOOST,
        ALC269VB_FIXUP_ORDISSIMO_EVE2,
+       ALC283_FIXUP_CHROME_BOOK,
 };
 
 static const struct hda_fixup alc269_fixups[] = {
@@ -3595,11 +3731,20 @@ static const struct hda_fixup alc269_fixups[] = {
                        { }
                },
        },
+       [ALC283_FIXUP_CHROME_BOOK] = {
+               .type = HDA_FIXUP_FUNC,
+               .v.func = alc283_fixup_chromebook,
+       },
 };
 
 static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x1025, 0x029b, "Acer 1810TZ", ALC269_FIXUP_INV_DMIC),
        SND_PCI_QUIRK(0x1025, 0x0349, "Acer AOD260", ALC269_FIXUP_INV_DMIC),
+       SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700),
+       SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
+       SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
+       SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
+       SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
        SND_PCI_QUIRK(0x1028, 0x05bd, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x05be, "Dell", ALC269_FIXUP_DELL2_MIC_NO_PRESENCE),
        SND_PCI_QUIRK(0x1028, 0x05c4, "Dell", ALC269_FIXUP_DELL1_MIC_NO_PRESENCE),
@@ -3637,6 +3782,7 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x103c, 0x18e6, "HP", ALC269_FIXUP_HP_GPIO_LED),
        SND_PCI_QUIRK(0x103c, 0x1973, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
        SND_PCI_QUIRK(0x103c, 0x1983, "HP Pavilion", ALC269_FIXUP_HP_MUTE_LED_MIC1),
+       SND_PCI_QUIRK(0x103c, 0x21ed, "HP Falco Chromebook", ALC283_FIXUP_CHROME_BOOK),
        SND_PCI_QUIRK_VENDOR(0x103c, "HP", ALC269_FIXUP_HP_MUTE_LED),
        SND_PCI_QUIRK(0x1043, 0x106d, "Asus K53BE", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x1043, 0x115d, "Asus 1015E", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
@@ -3655,11 +3801,6 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x104d, 0x907b, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
        SND_PCI_QUIRK(0x104d, 0x9084, "Sony VAIO", ALC275_FIXUP_SONY_HWEQ),
        SND_PCI_QUIRK_VENDOR(0x104d, "Sony VAIO", ALC269_FIXUP_SONY_VAIO),
-       SND_PCI_QUIRK(0x1028, 0x0470, "Dell M101z", ALC269_FIXUP_DELL_M101Z),
-       SND_PCI_QUIRK(0x1025, 0x047c, "Acer AC700", ALC269_FIXUP_ACER_AC700),
-       SND_PCI_QUIRK(0x1025, 0x0740, "Acer AO725", ALC271_FIXUP_HP_GATE_MIC_JACK),
-       SND_PCI_QUIRK(0x1025, 0x0742, "Acer AO756", ALC271_FIXUP_HP_GATE_MIC_JACK),
-       SND_PCI_QUIRK_VENDOR(0x1025, "Acer Aspire", ALC271_FIXUP_DMIC),
        SND_PCI_QUIRK(0x10cf, 0x1475, "Lifebook", ALC269_FIXUP_LIFEBOOK),
        SND_PCI_QUIRK(0x17aa, 0x20f2, "Thinkpad SL410/510", ALC269_FIXUP_SKU_IGNORE),
        SND_PCI_QUIRK(0x17aa, 0x215e, "Thinkpad L512", ALC269_FIXUP_SKU_IGNORE),
@@ -3670,8 +3811,16 @@ static const struct snd_pci_quirk alc269_fixup_tbl[] = {
        SND_PCI_QUIRK(0x17aa, 0x21fa, "Thinkpad X230", ALC269_FIXUP_LENOVO_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x21f3, "Thinkpad T430", ALC269_FIXUP_LENOVO_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x21fb, "Thinkpad T430s", ALC269_FIXUP_LENOVO_DOCK),
-       SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
        SND_PCI_QUIRK(0x17aa, 0x2203, "Thinkpad X230 Tablet", ALC269_FIXUP_LENOVO_DOCK),
+       SND_PCI_QUIRK(0x17aa, 0x2208, "Thinkpad T431s", ALC269_FIXUP_LENOVO_DOCK),
+       SND_PCI_QUIRK(0x17aa, 0x220c, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+       SND_PCI_QUIRK(0x17aa, 0x2212, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+       SND_PCI_QUIRK(0x17aa, 0x2214, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+       SND_PCI_QUIRK(0x17aa, 0x2215, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+       SND_PCI_QUIRK(0x17aa, 0x5013, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+       SND_PCI_QUIRK(0x17aa, 0x501a, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+       SND_PCI_QUIRK(0x17aa, 0x5026, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
+       SND_PCI_QUIRK(0x17aa, 0x5109, "Thinkpad", ALC269_FIXUP_LIMIT_INT_MIC_BOOST),
        SND_PCI_QUIRK(0x17aa, 0x3bf8, "Quanta FL1", ALC269_FIXUP_PCM_44K),
        SND_PCI_QUIRK(0x17aa, 0x9e54, "LENOVO NB", ALC269_FIXUP_LENOVO_EAPD),
        SND_PCI_QUIRK(0x1b7d, 0xa831, "Ordissimo EVE2 ", ALC269VB_FIXUP_ORDISSIMO_EVE2), /* Also known as Malata PC-B1303 */
@@ -3840,11 +3989,15 @@ static int patch_alc269(struct hda_codec *codec)
        case 0x10ec0290:
                spec->codec_variant = ALC269_TYPE_ALC280;
                break;
-       case 0x10ec0233:
        case 0x10ec0282:
-       case 0x10ec0283:
                spec->codec_variant = ALC269_TYPE_ALC282;
                break;
+       case 0x10ec0233:
+       case 0x10ec0283:
+               spec->codec_variant = ALC269_TYPE_ALC283;
+               spec->shutup = alc283_shutup;
+               spec->init_hook = alc283_init;
+               break;
        case 0x10ec0284:
        case 0x10ec0292:
                spec->codec_variant = ALC269_TYPE_ALC284;
@@ -3872,7 +4025,8 @@ static int patch_alc269(struct hda_codec *codec)
        codec->patch_ops.suspend = alc269_suspend;
        codec->patch_ops.resume = alc269_resume;
 #endif
-       spec->shutup = alc269_shutup;
+       if (!spec->shutup)
+               spec->shutup = alc269_shutup;
 
        snd_hda_apply_fixup(codec, HDA_FIXUP_ACT_PROBE);
 
index 6d1924c19abfdb41d1dddc27e119630c24995abc..fba0cef1c47fff7a49632bf663402785e5cf24f8 100644 (file)
@@ -158,6 +158,7 @@ enum {
        STAC_D965_VERBS,
        STAC_DELL_3ST,
        STAC_DELL_BIOS,
+       STAC_DELL_BIOS_AMIC,
        STAC_DELL_BIOS_SPDIF,
        STAC_927X_DELL_DMIC,
        STAC_927X_VOLKNOB,
@@ -3231,8 +3232,6 @@ static const struct hda_fixup stac927x_fixups[] = {
        [STAC_DELL_BIOS] = {
                .type = HDA_FIXUP_PINS,
                .v.pins = (const struct hda_pintbl[]) {
-                       /* configure the analog microphone on some laptops */
-                       { 0x0c, 0x90a79130 },
                        /* correct the front output jack as a hp out */
                        { 0x0f, 0x0221101f },
                        /* correct the front input jack as a mic */
@@ -3242,6 +3241,16 @@ static const struct hda_fixup stac927x_fixups[] = {
                .chained = true,
                .chain_id = STAC_927X_DELL_DMIC,
        },
+       [STAC_DELL_BIOS_AMIC] = {
+               .type = HDA_FIXUP_PINS,
+               .v.pins = (const struct hda_pintbl[]) {
+                       /* configure the analog microphone on some laptops */
+                       { 0x0c, 0x90a79130 },
+                       {}
+               },
+               .chained = true,
+               .chain_id = STAC_DELL_BIOS,
+       },
        [STAC_DELL_BIOS_SPDIF] = {
                .type = HDA_FIXUP_PINS,
                .v.pins = (const struct hda_pintbl[]) {
@@ -3270,6 +3279,7 @@ static const struct hda_model_fixup stac927x_models[] = {
        { .id = STAC_D965_5ST_NO_FP, .name = "5stack-no-fp" },
        { .id = STAC_DELL_3ST, .name = "dell-3stack" },
        { .id = STAC_DELL_BIOS, .name = "dell-bios" },
+       { .id = STAC_DELL_BIOS_AMIC, .name = "dell-bios-amic" },
        { .id = STAC_927X_VOLKNOB, .name = "volknob" },
        {}
 };
index e2481baddc70ff6ff265ee0ed118377869847db6..0bc20ef5687ac3558996ca3fd56a741c75bc042b 100644 (file)
@@ -207,9 +207,9 @@ static void vt1708_stop_hp_work(struct hda_codec *codec)
                return;
        if (spec->hp_work_active) {
                snd_hda_codec_write(codec, 0x1, 0, 0xf81, 1);
+               codec->jackpoll_interval = 0;
                cancel_delayed_work_sync(&codec->jackpoll_work);
                spec->hp_work_active = false;
-               codec->jackpoll_interval = 0;
        }
 }
 
index 2a8ad9d1a2aea290cf433c0fccb4218ab78cf5ff..bb9ebc5543d7a61ff639bbf91d37957d5fdf9086 100644 (file)
@@ -28,6 +28,7 @@
 #include <linux/interrupt.h>
 #include <linux/pci.h>
 #include <linux/module.h>
+#include <linux/vmalloc.h>
 
 #include <sound/core.h>
 #include <sound/info.h>
@@ -198,6 +199,31 @@ MODULE_PARM_DESC(enable, "Enable RME Digi96 soundcard.");
 #define RME96_AD1852_VOL_BITS 14
 #define RME96_AD1855_VOL_BITS 10
 
+/* Defines for snd_rme96_trigger */
+#define RME96_TB_START_PLAYBACK 1
+#define RME96_TB_START_CAPTURE 2
+#define RME96_TB_STOP_PLAYBACK 4
+#define RME96_TB_STOP_CAPTURE 8
+#define RME96_TB_RESET_PLAYPOS 16
+#define RME96_TB_RESET_CAPTUREPOS 32
+#define RME96_TB_CLEAR_PLAYBACK_IRQ 64
+#define RME96_TB_CLEAR_CAPTURE_IRQ 128
+#define RME96_RESUME_PLAYBACK  (RME96_TB_START_PLAYBACK)
+#define RME96_RESUME_CAPTURE   (RME96_TB_START_CAPTURE)
+#define RME96_RESUME_BOTH      (RME96_RESUME_PLAYBACK \
+                               | RME96_RESUME_CAPTURE)
+#define RME96_START_PLAYBACK   (RME96_TB_START_PLAYBACK \
+                               | RME96_TB_RESET_PLAYPOS)
+#define RME96_START_CAPTURE    (RME96_TB_START_CAPTURE \
+                               | RME96_TB_RESET_CAPTUREPOS)
+#define RME96_START_BOTH       (RME96_START_PLAYBACK \
+                               | RME96_START_CAPTURE)
+#define RME96_STOP_PLAYBACK    (RME96_TB_STOP_PLAYBACK \
+                               | RME96_TB_CLEAR_PLAYBACK_IRQ)
+#define RME96_STOP_CAPTURE     (RME96_TB_STOP_CAPTURE \
+                               | RME96_TB_CLEAR_CAPTURE_IRQ)
+#define RME96_STOP_BOTH                (RME96_STOP_PLAYBACK \
+                               | RME96_STOP_CAPTURE)
 
 struct rme96 {
        spinlock_t    lock;
@@ -214,6 +240,13 @@ struct rme96 {
 
        u8 rev; /* card revision number */
 
+#ifdef CONFIG_PM
+       u32 playback_pointer;
+       u32 capture_pointer;
+       void *playback_suspend_buffer;
+       void *capture_suspend_buffer;
+#endif
+
        struct snd_pcm_substream *playback_substream;
        struct snd_pcm_substream *capture_substream;
 
@@ -344,6 +377,8 @@ static struct snd_pcm_hardware snd_rme96_playback_spdif_info =
 {
        .info =              (SNDRV_PCM_INFO_MMAP_IOMEM |
                              SNDRV_PCM_INFO_MMAP_VALID |
+                             SNDRV_PCM_INFO_SYNC_START |
+                             SNDRV_PCM_INFO_RESUME |
                              SNDRV_PCM_INFO_INTERLEAVED |
                              SNDRV_PCM_INFO_PAUSE),
        .formats =           (SNDRV_PCM_FMTBIT_S16_LE |
@@ -373,6 +408,8 @@ static struct snd_pcm_hardware snd_rme96_capture_spdif_info =
 {
        .info =              (SNDRV_PCM_INFO_MMAP_IOMEM |
                              SNDRV_PCM_INFO_MMAP_VALID |
+                             SNDRV_PCM_INFO_SYNC_START |
+                             SNDRV_PCM_INFO_RESUME |
                              SNDRV_PCM_INFO_INTERLEAVED |
                              SNDRV_PCM_INFO_PAUSE),
        .formats =           (SNDRV_PCM_FMTBIT_S16_LE |
@@ -402,6 +439,8 @@ static struct snd_pcm_hardware snd_rme96_playback_adat_info =
 {
        .info =              (SNDRV_PCM_INFO_MMAP_IOMEM |
                              SNDRV_PCM_INFO_MMAP_VALID |
+                             SNDRV_PCM_INFO_SYNC_START |
+                             SNDRV_PCM_INFO_RESUME |
                              SNDRV_PCM_INFO_INTERLEAVED |
                              SNDRV_PCM_INFO_PAUSE),
        .formats =           (SNDRV_PCM_FMTBIT_S16_LE |
@@ -427,6 +466,8 @@ static struct snd_pcm_hardware snd_rme96_capture_adat_info =
 {
        .info =              (SNDRV_PCM_INFO_MMAP_IOMEM |
                              SNDRV_PCM_INFO_MMAP_VALID |
+                             SNDRV_PCM_INFO_SYNC_START |
+                             SNDRV_PCM_INFO_RESUME |
                              SNDRV_PCM_INFO_INTERLEAVED |
                              SNDRV_PCM_INFO_PAUSE),
        .formats =           (SNDRV_PCM_FMTBIT_S16_LE |
@@ -1045,54 +1086,35 @@ snd_rme96_capture_hw_params(struct snd_pcm_substream *substream,
 }
 
 static void
-snd_rme96_playback_start(struct rme96 *rme96,
-                        int from_pause)
+snd_rme96_trigger(struct rme96 *rme96,
+                 int op)
 {
-       if (!from_pause) {
+       if (op & RME96_TB_RESET_PLAYPOS)
                writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
-       }
-
-       rme96->wcreg |= RME96_WCR_START;
-       writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
-}
-
-static void
-snd_rme96_capture_start(struct rme96 *rme96,
-                       int from_pause)
-{
-       if (!from_pause) {
+       if (op & RME96_TB_RESET_CAPTUREPOS)
                writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
-       }
-
-       rme96->wcreg |= RME96_WCR_START_2;
+       if (op & RME96_TB_CLEAR_PLAYBACK_IRQ) {
+               rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+               if (rme96->rcreg & RME96_RCR_IRQ)
+                       writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
+       }
+       if (op & RME96_TB_CLEAR_CAPTURE_IRQ) {
+               rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
+               if (rme96->rcreg & RME96_RCR_IRQ_2)
+                       writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
+       }
+       if (op & RME96_TB_START_PLAYBACK)
+               rme96->wcreg |= RME96_WCR_START;
+       if (op & RME96_TB_STOP_PLAYBACK)
+               rme96->wcreg &= ~RME96_WCR_START;
+       if (op & RME96_TB_START_CAPTURE)
+               rme96->wcreg |= RME96_WCR_START_2;
+       if (op & RME96_TB_STOP_CAPTURE)
+               rme96->wcreg &= ~RME96_WCR_START_2;
        writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
 }
 
-static void
-snd_rme96_playback_stop(struct rme96 *rme96)
-{
-       /*
-        * Check if there is an unconfirmed IRQ, if so confirm it, or else
-        * the hardware will not stop generating interrupts
-        */
-       rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
-       if (rme96->rcreg & RME96_RCR_IRQ) {
-               writel(0, rme96->iobase + RME96_IO_CONFIRM_PLAY_IRQ);
-       }       
-       rme96->wcreg &= ~RME96_WCR_START;
-       writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
-}
 
-static void
-snd_rme96_capture_stop(struct rme96 *rme96)
-{
-       rme96->rcreg = readl(rme96->iobase + RME96_IO_CONTROL_REGISTER);
-       if (rme96->rcreg & RME96_RCR_IRQ_2) {
-               writel(0, rme96->iobase + RME96_IO_CONFIRM_REC_IRQ);
-       }       
-       rme96->wcreg &= ~RME96_WCR_START_2;
-       writel(rme96->wcreg, rme96->iobase + RME96_IO_CONTROL_REGISTER);
-}
 
 static irqreturn_t
 snd_rme96_interrupt(int irq,
@@ -1155,6 +1177,7 @@ snd_rme96_playback_spdif_open(struct snd_pcm_substream *substream)
        struct rme96 *rme96 = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
 
+       snd_pcm_set_sync(substream);
        spin_lock_irq(&rme96->lock);    
         if (rme96->playback_substream != NULL) {
                spin_unlock_irq(&rme96->lock);
@@ -1191,6 +1214,7 @@ snd_rme96_capture_spdif_open(struct snd_pcm_substream *substream)
        struct rme96 *rme96 = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
 
+       snd_pcm_set_sync(substream);
        runtime->hw = snd_rme96_capture_spdif_info;
         if (snd_rme96_getinputtype(rme96) != RME96_INPUT_ANALOG &&
             (rate = snd_rme96_capture_getrate(rme96, &isadat)) > 0)
@@ -1222,6 +1246,7 @@ snd_rme96_playback_adat_open(struct snd_pcm_substream *substream)
        struct rme96 *rme96 = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;        
        
+       snd_pcm_set_sync(substream);
        spin_lock_irq(&rme96->lock);    
         if (rme96->playback_substream != NULL) {
                spin_unlock_irq(&rme96->lock);
@@ -1253,6 +1278,7 @@ snd_rme96_capture_adat_open(struct snd_pcm_substream *substream)
        struct rme96 *rme96 = snd_pcm_substream_chip(substream);
        struct snd_pcm_runtime *runtime = substream->runtime;
 
+       snd_pcm_set_sync(substream);
        runtime->hw = snd_rme96_capture_adat_info;
         if (snd_rme96_getinputtype(rme96) == RME96_INPUT_ANALOG) {
                 /* makes no sense to use analog input. Note that analog
@@ -1288,7 +1314,7 @@ snd_rme96_playback_close(struct snd_pcm_substream *substream)
 
        spin_lock_irq(&rme96->lock);    
        if (RME96_ISPLAYING(rme96)) {
-               snd_rme96_playback_stop(rme96);
+               snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);
        }
        rme96->playback_substream = NULL;
        rme96->playback_periodsize = 0;
@@ -1309,7 +1335,7 @@ snd_rme96_capture_close(struct snd_pcm_substream *substream)
        
        spin_lock_irq(&rme96->lock);    
        if (RME96_ISRECORDING(rme96)) {
-               snd_rme96_capture_stop(rme96);
+               snd_rme96_trigger(rme96, RME96_STOP_CAPTURE);
        }
        rme96->capture_substream = NULL;
        rme96->capture_periodsize = 0;
@@ -1324,7 +1350,7 @@ snd_rme96_playback_prepare(struct snd_pcm_substream *substream)
        
        spin_lock_irq(&rme96->lock);    
        if (RME96_ISPLAYING(rme96)) {
-               snd_rme96_playback_stop(rme96);
+               snd_rme96_trigger(rme96, RME96_STOP_PLAYBACK);
        }
        writel(0, rme96->iobase + RME96_IO_RESET_PLAY_POS);
        spin_unlock_irq(&rme96->lock);
@@ -1338,7 +1364,7 @@ snd_rme96_capture_prepare(struct snd_pcm_substream *substream)
        
        spin_lock_irq(&rme96->lock);    
        if (RME96_ISRECORDING(rme96)) {
-               snd_rme96_capture_stop(rme96);
+               snd_rme96_trigger(rme96, RME96_STOP_CAPTURE);
        }
        writel(0, rme96->iobase + RME96_IO_RESET_REC_POS);
        spin_unlock_irq(&rme96->lock);
@@ -1350,41 +1376,55 @@ snd_rme96_playback_trigger(struct snd_pcm_substream *substream,
                           int cmd)
 {
        struct rme96 *rme96 = snd_pcm_substream_chip(substream);
+       struct snd_pcm_substream *s;
+       bool sync;
+
+       snd_pcm_group_for_each_entry(s, substream) {
+               if (snd_pcm_substream_chip(s) == rme96)
+                       snd_pcm_trigger_done(s, substream);
+       }
+
+       sync = (rme96->playback_substream && rme96->capture_substream) &&
+              (rme96->playback_substream->group ==
+               rme96->capture_substream->group);
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
                if (!RME96_ISPLAYING(rme96)) {
-                       if (substream != rme96->playback_substream) {
+                       if (substream != rme96->playback_substream)
                                return -EBUSY;
-                       }
-                       snd_rme96_playback_start(rme96, 0);
+                       snd_rme96_trigger(rme96, sync ? RME96_START_BOTH
+                                                : RME96_START_PLAYBACK);
                }
                break;
 
+       case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_STOP:
                if (RME96_ISPLAYING(rme96)) {
-                       if (substream != rme96->playback_substream) {
+                       if (substream != rme96->playback_substream)
                                return -EBUSY;
-                       }
-                       snd_rme96_playback_stop(rme96);
+                       snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
+                                                :  RME96_STOP_PLAYBACK);
                }
                break;
 
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               if (RME96_ISPLAYING(rme96)) {
-                       snd_rme96_playback_stop(rme96);
-               }
+               if (RME96_ISPLAYING(rme96))
+                       snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
+                                                : RME96_STOP_PLAYBACK);
                break;
 
+       case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               if (!RME96_ISPLAYING(rme96)) {
-                       snd_rme96_playback_start(rme96, 1);
-               }
+               if (!RME96_ISPLAYING(rme96))
+                       snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH
+                                                : RME96_RESUME_PLAYBACK);
                break;
-               
+
        default:
                return -EINVAL;
        }
+
        return 0;
 }
 
@@ -1393,38 +1433,51 @@ snd_rme96_capture_trigger(struct snd_pcm_substream *substream,
                          int cmd)
 {
        struct rme96 *rme96 = snd_pcm_substream_chip(substream);
+       struct snd_pcm_substream *s;
+       bool sync;
+
+       snd_pcm_group_for_each_entry(s, substream) {
+               if (snd_pcm_substream_chip(s) == rme96)
+                       snd_pcm_trigger_done(s, substream);
+       }
+
+       sync = (rme96->playback_substream && rme96->capture_substream) &&
+              (rme96->playback_substream->group ==
+               rme96->capture_substream->group);
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
                if (!RME96_ISRECORDING(rme96)) {
-                       if (substream != rme96->capture_substream) {
+                       if (substream != rme96->capture_substream)
                                return -EBUSY;
-                       }
-                       snd_rme96_capture_start(rme96, 0);
+                       snd_rme96_trigger(rme96, sync ? RME96_START_BOTH
+                                                : RME96_START_CAPTURE);
                }
                break;
 
+       case SNDRV_PCM_TRIGGER_SUSPEND:
        case SNDRV_PCM_TRIGGER_STOP:
                if (RME96_ISRECORDING(rme96)) {
-                       if (substream != rme96->capture_substream) {
+                       if (substream != rme96->capture_substream)
                                return -EBUSY;
-                       }
-                       snd_rme96_capture_stop(rme96);
+                       snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
+                                                : RME96_STOP_CAPTURE);
                }
                break;
 
        case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
-               if (RME96_ISRECORDING(rme96)) {
-                       snd_rme96_capture_stop(rme96);
-               }
+               if (RME96_ISRECORDING(rme96))
+                       snd_rme96_trigger(rme96, sync ? RME96_STOP_BOTH
+                                                : RME96_STOP_CAPTURE);
                break;
 
+       case SNDRV_PCM_TRIGGER_RESUME:
        case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
-               if (!RME96_ISRECORDING(rme96)) {
-                       snd_rme96_capture_start(rme96, 1);
-               }
+               if (!RME96_ISRECORDING(rme96))
+                       snd_rme96_trigger(rme96, sync ? RME96_RESUME_BOTH
+                                                : RME96_RESUME_CAPTURE);
                break;
-               
+
        default:
                return -EINVAL;
        }
@@ -1505,8 +1558,7 @@ snd_rme96_free(void *private_data)
                return;
        }
        if (rme96->irq >= 0) {
-               snd_rme96_playback_stop(rme96);
-               snd_rme96_capture_stop(rme96);
+               snd_rme96_trigger(rme96, RME96_STOP_BOTH);
                rme96->areg &= ~RME96_AR_DAC_EN;
                writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
                free_irq(rme96->irq, (void *)rme96);
@@ -1520,6 +1572,10 @@ snd_rme96_free(void *private_data)
                pci_release_regions(rme96->pci);
                rme96->port = 0;
        }
+#ifdef CONFIG_PM
+       vfree(rme96->playback_suspend_buffer);
+       vfree(rme96->capture_suspend_buffer);
+#endif
        pci_disable_device(rme96->pci);
 }
 
@@ -1606,8 +1662,7 @@ snd_rme96_create(struct rme96 *rme96)
        rme96->capture_periodsize = 0;
        
        /* make sure playback/capture is stopped, if by some reason active */
-       snd_rme96_playback_stop(rme96);
-       snd_rme96_capture_stop(rme96);
+       snd_rme96_trigger(rme96, RME96_STOP_BOTH);
        
        /* set default values in registers */
        rme96->wcreg =
@@ -2319,6 +2374,87 @@ snd_rme96_create_switches(struct snd_card *card,
  * Card initialisation
  */
 
+#ifdef CONFIG_PM
+
+static int
+snd_rme96_suspend(struct pci_dev *pci,
+                 pm_message_t state)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct rme96 *rme96 = card->private_data;
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D3hot);
+       snd_pcm_suspend(rme96->playback_substream);
+       snd_pcm_suspend(rme96->capture_substream);
+
+       /* save capture & playback pointers */
+       rme96->playback_pointer = readl(rme96->iobase + RME96_IO_GET_PLAY_POS)
+                                 & RME96_RCR_AUDIO_ADDR_MASK;
+       rme96->capture_pointer = readl(rme96->iobase + RME96_IO_GET_REC_POS)
+                                & RME96_RCR_AUDIO_ADDR_MASK;
+
+       /* save playback and capture buffers */
+       memcpy_fromio(rme96->playback_suspend_buffer,
+                     rme96->iobase + RME96_IO_PLAY_BUFFER, RME96_BUFFER_SIZE);
+       memcpy_fromio(rme96->capture_suspend_buffer,
+                     rme96->iobase + RME96_IO_REC_BUFFER, RME96_BUFFER_SIZE);
+
+       /* disable the DAC  */
+       rme96->areg &= ~RME96_AR_DAC_EN;
+       writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+
+       pci_disable_device(pci);
+       pci_save_state(pci);
+
+       return 0;
+}
+
+static int
+snd_rme96_resume(struct pci_dev *pci)
+{
+       struct snd_card *card = pci_get_drvdata(pci);
+       struct rme96 *rme96 = card->private_data;
+
+       pci_restore_state(pci);
+       if (pci_enable_device(pci) < 0) {
+               printk(KERN_ERR "rme96: pci_enable_device failed, disabling device\n");
+               snd_card_disconnect(card);
+               return -EIO;
+       }
+
+       /* reset playback and record buffer pointers */
+       writel(0, rme96->iobase + RME96_IO_SET_PLAY_POS
+                 + rme96->playback_pointer);
+       writel(0, rme96->iobase + RME96_IO_SET_REC_POS
+                 + rme96->capture_pointer);
+
+       /* restore playback and capture buffers */
+       memcpy_toio(rme96->iobase + RME96_IO_PLAY_BUFFER,
+                   rme96->playback_suspend_buffer, RME96_BUFFER_SIZE);
+       memcpy_toio(rme96->iobase + RME96_IO_REC_BUFFER,
+                   rme96->capture_suspend_buffer, RME96_BUFFER_SIZE);
+
+       /* reset the ADC */
+       writel(rme96->areg | RME96_AR_PD2,
+              rme96->iobase + RME96_IO_ADDITIONAL_REG);
+       writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+
+       /* reset and enable DAC, restore analog volume */
+       snd_rme96_reset_dac(rme96);
+       rme96->areg |= RME96_AR_DAC_EN;
+       writel(rme96->areg, rme96->iobase + RME96_IO_ADDITIONAL_REG);
+       if (RME96_HAS_ANALOG_OUT(rme96)) {
+               usleep_range(3000, 10000);
+               snd_rme96_apply_dac_volume(rme96);
+       }
+
+       snd_power_change_state(card, SNDRV_CTL_POWER_D0);
+
+       return 0;
+}
+
+#endif
+
 static void snd_rme96_card_free(struct snd_card *card)
 {
        snd_rme96_free(card->private_data);
@@ -2355,6 +2491,23 @@ snd_rme96_probe(struct pci_dev *pci,
                return err;
        }
        
+#ifdef CONFIG_PM
+       rme96->playback_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
+       if (!rme96->playback_suspend_buffer) {
+               snd_printk(KERN_ERR
+                          "Failed to allocate playback suspend buffer!\n");
+               snd_card_free(card);
+               return -ENOMEM;
+       }
+       rme96->capture_suspend_buffer = vmalloc(RME96_BUFFER_SIZE);
+       if (!rme96->capture_suspend_buffer) {
+               snd_printk(KERN_ERR
+                          "Failed to allocate capture suspend buffer!\n");
+               snd_card_free(card);
+               return -ENOMEM;
+       }
+#endif
+
        strcpy(card->driver, "Digi96");
        switch (rme96->pci->device) {
        case PCI_DEVICE_ID_RME_DIGI96:
@@ -2397,6 +2550,10 @@ static struct pci_driver rme96_driver = {
        .id_table = snd_rme96_ids,
        .probe = snd_rme96_probe,
        .remove = snd_rme96_remove,
+#ifdef CONFIG_PM
+       .suspend = snd_rme96_suspend,
+       .resume = snd_rme96_resume,
+#endif
 };
 
 module_pci_driver(rme96_driver);
index bd501931ee2341f1a6f913dc1b21a967268de704..3cde55b753e26086c13569dbee92847db02c8a9c 100644 (file)
  *   Foundation, Inc., 59 Temple Place, Suite 330, Boston, MA  02111-1307 USA
  *
  */
+
+/* *************    Register Documentation   *******************************************************
+ *
+ * Work in progress! Documentation is based on the code in this file.
+ *
+ * --------- HDSPM_controlRegister ---------
+ * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :3322.2222:2222.1111:1111.1100:0000.0000: bit number
+ * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
+ * :    .    :    .    :    .    :  x .    :  HDSPM_AudioInterruptEnable \_ setting both bits
+ * :    .    :    .    :    .    :    .   x:  HDSPM_Start                /  enables audio IO
+ * :    .    :    .    :    .    :   x.    :  HDSPM_ClockModeMaster - 1: Master, 0: Slave
+ * :    .    :    .    :    .    :    .210 :  HDSPM_LatencyMask - 3 Bit value for latency
+ * :    .    :    .    :    .    :    .    :      0:64, 1:128, 2:256, 3:512,
+ * :    .    :    .    :    .    :    .    :      4:1024, 5:2048, 6:4096, 7:8192
+ * :x   .    :    .    :    .   x:xx  .    :  HDSPM_FrequencyMask
+ * :    .    :    .    :    .    :10  .    :  HDSPM_Frequency1|HDSPM_Frequency0: 1=32K,2=44.1K,3=48K,0=??
+ * :    .    :    .    :    .   x:    .    :  <MADI> HDSPM_DoubleSpeed
+ * :x   .    :    .    :    .    :    .    :  <MADI> HDSPM_QuadSpeed
+ * :    .  3 :    .  10:  2 .    :    .    :  HDSPM_SyncRefMask :
+ * :    .    :    .   x:    .    :    .    :  HDSPM_SyncRef0
+ * :    .    :    .  x :    .    :    .    :  HDSPM_SyncRef1
+ * :    .    :    .    :  x .    :    .    :  <AES32> HDSPM_SyncRef2
+ * :    .  x :    .    :    .    :    .    :  <AES32> HDSPM_SyncRef3
+ * :    .    :    .  10:    .    :    .    :  <MADI> sync ref: 0:WC, 1:Madi, 2:TCO, 3:SyncIn
+ * :    .  3 :    .  10:  2 .    :    .    :  <AES32>  0:WC, 1:AES1 ... 8:AES8, 9: TCO, 10:SyncIn?
+ * :    .  x :    .    :    .    :    .    :  <MADIe> HDSPe_FLOAT_FORMAT
+ * :    .    :    .    : x  .    :    .    :  <MADI> HDSPM_InputSelect0 : 0=optical,1=coax
+ * :    .    :    .    :x   .    :    .    :  <MADI> HDSPM_InputSelect1
+ * :    .    :    .x   :    .    :    .    :  <MADI> HDSPM_clr_tms
+ * :    .    :    .    :    . x  :    .    :  <MADI> HDSPM_TX_64ch
+ * :    .    :    .    :    . x  :    .    :  <AES32> HDSPM_Emphasis
+ * :    .    :    .    :    .x   :    .    :  <MADI> HDSPM_AutoInp
+ * :    .    :    . x  :    .    :    .    :  <MADI> HDSPM_SMUX
+ * :    .    :    .x   :    .    :    .    :  <MADI> HDSPM_clr_tms
+ * :    .    :   x.    :    .    :    .    :  <MADI> HDSPM_taxi_reset
+ * :    .   x:    .    :    .    :    .    :  <MADI> HDSPM_LineOut
+ * :    .   x:    .    :    .    :    .    :  <AES32> ??????????????????
+ * :    .    :   x.    :    .    :    .    :  <AES32> HDSPM_WCK48
+ * :    .    :    .    :    .x   :    .    :  <AES32> HDSPM_Dolby
+ * :    .    : x  .    :    .    :    .    :  HDSPM_Midi0InterruptEnable
+ * :    .    :x   .    :    .    :    .    :  HDSPM_Midi1InterruptEnable
+ * :    .    :  x .    :    .    :    .    :  HDSPM_Midi2InterruptEnable
+ * :    . x  :    .    :    .    :    .    :  <MADI> HDSPM_Midi3InterruptEnable
+ * :    . x  :    .    :    .    :    .    :  <AES32> HDSPM_DS_DoubleWire
+ * :    .x   :    .    :    .    :    .    :  <AES32> HDSPM_QS_DoubleWire
+ * :   x.    :    .    :    .    :    .    :  <AES32> HDSPM_QS_QuadWire
+ * :    .    :    .    :    .  x :    .    :  <AES32> HDSPM_Professional
+ * : x  .    :    .    :    .    :    .    :  HDSPM_wclk_sel
+ * :    .    :    .    :    .    :    .    :
+ * :7654.3210:7654.3210:7654.3210:7654.3210: bit number per byte
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :3322.2222:2222.1111:1111.1100:0000.0000: bit number
+ * :1098.7654:3210.9876:5432.1098:7654.3210: 0..31
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :8421.8421:8421.8421:8421.8421:8421.8421:hex digit
+ *
+ *
+ *
+ * AIO / RayDAT only
+ *
+ * ------------ HDSPM_WR_SETTINGS ----------
+ * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte
+ * :1098.7654:3210.9876:5432.1098:7654.3210:
+ * :||||.||||:||||.||||:||||.||||:||||.||||: bit number
+ * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
+ * :    .    :    .    :    .    :    .   x: HDSPM_c0Master 1: Master, 0: Slave
+ * :    .    :    .    :    .    :    .  x : HDSPM_c0_SyncRef0
+ * :    .    :    .    :    .    :    . x  : HDSPM_c0_SyncRef1
+ * :    .    :    .    :    .    :    .x   : HDSPM_c0_SyncRef2
+ * :    .    :    .    :    .    :   x.    : HDSPM_c0_SyncRef3
+ * :    .    :    .    :    .    :   3.210 : HDSPM_c0_SyncRefMask:
+ * :    .    :    .    :    .    :    .    :  RayDat: 0:WC, 1:AES, 2:SPDIF, 3..6: ADAT1..4,
+ * :    .    :    .    :    .    :    .    :          9:TCO, 10:SyncIn
+ * :    .    :    .    :    .    :    .    :  AIO: 0:WC, 1:AES, 2: SPDIF, 3: ATAT,
+ * :    .    :    .    :    .    :    .    :          9:TCO, 10:SyncIn
+ * :    .    :    .    :    .    :    .    :
+ * :    .    :    .    :    .    :    .    :
+ * :3322.2222:2222.1111:1111.1100:0000.0000: bit number per byte
+ * :1098.7654:3210.9876:5432.1098:7654.3210:
+ * :||||.||||:||||.||||:||||.||||:||||.||||: bit number
+ * :7654.3210:7654.3210:7654.3210:7654.3210: 0..31
+ * :||||.||||:||||.||||:||||.||||:||||.||||:
+ * :8421.8421:8421.8421:8421.8421:8421.8421: hex digit
+ *
+ */
 #include <linux/init.h>
 #include <linux/delay.h>
 #include <linux/interrupt.h>
@@ -95,7 +186,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 #define HDSPM_controlRegister       64
 #define HDSPM_interruptConfirmation  96
 #define HDSPM_control2Reg           256  /* not in specs ???????? */
-#define HDSPM_freqReg                256  /* for AES32 */
+#define HDSPM_freqReg                256  /* for setting arbitrary clock values (DDS feature) */
 #define HDSPM_midiDataOut0          352  /* just believe in old code */
 #define HDSPM_midiDataOut1          356
 #define HDSPM_eeprom_wr                     384  /* for AES32 */
@@ -258,6 +349,25 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 
 #define HDSPM_wclk_sel (1<<30)
 
+/* additional control register bits for AIO*/
+#define HDSPM_c0_Wck48                         0x20 /* also RayDAT */
+#define HDSPM_c0_Input0                                0x1000
+#define HDSPM_c0_Input1                                0x2000
+#define HDSPM_c0_Spdif_Opt                     0x4000
+#define HDSPM_c0_Pro                           0x8000
+#define HDSPM_c0_clr_tms                       0x10000
+#define HDSPM_c0_AEB1                          0x20000
+#define HDSPM_c0_AEB2                          0x40000
+#define HDSPM_c0_LineOut                       0x80000
+#define HDSPM_c0_AD_GAIN0                      0x100000
+#define HDSPM_c0_AD_GAIN1                      0x200000
+#define HDSPM_c0_DA_GAIN0                      0x400000
+#define HDSPM_c0_DA_GAIN1                      0x800000
+#define HDSPM_c0_PH_GAIN0                      0x1000000
+#define HDSPM_c0_PH_GAIN1                      0x2000000
+#define HDSPM_c0_Sym6db                                0x4000000
+
+
 /* --- bit helper defines */
 #define HDSPM_LatencyMask    (HDSPM_Latency0|HDSPM_Latency1|HDSPM_Latency2)
 #define HDSPM_FrequencyMask  (HDSPM_Frequency0|HDSPM_Frequency1|\
@@ -341,11 +451,11 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 #define HDSPM_madiLock           (1<<3)        /* MADI Locked =1, no=0 */
 #define HDSPM_madiSync          (1<<18) /* MADI is in sync */
 
-#define HDSPM_tcoLock    0x00000020 /* Optional TCO locked status FOR HDSPe MADI! */
-#define HDSPM_tcoSync    0x10000000 /* Optional TCO sync status */
+#define HDSPM_tcoLockMadi    0x00000020 /* Optional TCO locked status for HDSPe MADI*/
+#define HDSPM_tcoSync    0x10000000 /* Optional TCO sync status for HDSPe MADI and AES32!*/
 
-#define HDSPM_syncInLock 0x00010000 /* Sync In lock status FOR HDSPe MADI! */
-#define HDSPM_syncInSync 0x00020000 /* Sync In sync status FOR HDSPe MADI! */
+#define HDSPM_syncInLock 0x00010000 /* Sync In lock status for HDSPe MADI! */
+#define HDSPM_syncInSync 0x00020000 /* Sync In sync status for HDSPe MADI! */
 
 #define HDSPM_BufferPositionMask 0x000FFC0 /* Bit 6..15 : h/w buffer pointer */
                        /* since 64byte accurate, last 6 bits are not used */
@@ -363,7 +473,7 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
                                         * Interrupt
                                         */
 #define HDSPM_tco_detect         0x08000000
-#define HDSPM_tco_lock          0x20000000
+#define HDSPM_tcoLockAes         0x20000000 /* Optional TCO locked status for HDSPe AES */
 
 #define HDSPM_s2_tco_detect      0x00000040
 #define HDSPM_s2_AEBO_D          0x00000080
@@ -461,7 +571,9 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 #define HDSPM_AES32_AUTOSYNC_FROM_AES6 6
 #define HDSPM_AES32_AUTOSYNC_FROM_AES7 7
 #define HDSPM_AES32_AUTOSYNC_FROM_AES8 8
-#define HDSPM_AES32_AUTOSYNC_FROM_NONE 9
+#define HDSPM_AES32_AUTOSYNC_FROM_TCO 9
+#define HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN 10
+#define HDSPM_AES32_AUTOSYNC_FROM_NONE 11
 
 /*  status2 */
 /* HDSPM_LockAES_bit is given by HDSPM_LockAES >> (AES# - 1) */
@@ -537,36 +649,39 @@ MODULE_SUPPORTED_DEVICE("{{RME HDSPM-MADI}}");
 /* names for speed modes */
 static char *hdspm_speed_names[] = { "single", "double", "quad" };
 
-static char *texts_autosync_aes_tco[] = { "Word Clock",
+static const char *const texts_autosync_aes_tco[] = { "Word Clock",
                                          "AES1", "AES2", "AES3", "AES4",
                                          "AES5", "AES6", "AES7", "AES8",
-                                         "TCO" };
-static char *texts_autosync_aes[] = { "Word Clock",
+                                         "TCO", "Sync In"
+};
+static const char *const texts_autosync_aes[] = { "Word Clock",
                                      "AES1", "AES2", "AES3", "AES4",
-                                     "AES5", "AES6", "AES7", "AES8" };
-static char *texts_autosync_madi_tco[] = { "Word Clock",
+                                     "AES5", "AES6", "AES7", "AES8",
+                                     "Sync In"
+};
+static const char *const texts_autosync_madi_tco[] = { "Word Clock",
                                           "MADI", "TCO", "Sync In" };
-static char *texts_autosync_madi[] = { "Word Clock",
+static const char *const texts_autosync_madi[] = { "Word Clock",
                                       "MADI", "Sync In" };
 
-static char *texts_autosync_raydat_tco[] = {
+static const char *const texts_autosync_raydat_tco[] = {
        "Word Clock",
        "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
        "AES", "SPDIF", "TCO", "Sync In"
 };
-static char *texts_autosync_raydat[] = {
+static const char *const texts_autosync_raydat[] = {
        "Word Clock",
        "ADAT 1", "ADAT 2", "ADAT 3", "ADAT 4",
        "AES", "SPDIF", "Sync In"
 };
-static char *texts_autosync_aio_tco[] = {
+static const char *const texts_autosync_aio_tco[] = {
        "Word Clock",
        "ADAT", "AES", "SPDIF", "TCO", "Sync In"
 };
-static char *texts_autosync_aio[] = { "Word Clock",
+static const char *const texts_autosync_aio[] = { "Word Clock",
                                      "ADAT", "AES", "SPDIF", "Sync In" };
 
-static char *texts_freq[] = {
+static const char *const texts_freq[] = {
        "No Lock",
        "32 kHz",
        "44.1 kHz",
@@ -629,7 +744,8 @@ static char *texts_ports_aio_in_ss[] = {
        "AES.L", "AES.R",
        "SPDIF.L", "SPDIF.R",
        "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
-       "ADAT.7", "ADAT.8"
+       "ADAT.7", "ADAT.8",
+       "AEB.1", "AEB.2", "AEB.3", "AEB.4"
 };
 
 static char *texts_ports_aio_out_ss[] = {
@@ -638,14 +754,16 @@ static char *texts_ports_aio_out_ss[] = {
        "SPDIF.L", "SPDIF.R",
        "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4", "ADAT.5", "ADAT.6",
        "ADAT.7", "ADAT.8",
-       "Phone.L", "Phone.R"
+       "Phone.L", "Phone.R",
+       "AEB.1", "AEB.2", "AEB.3", "AEB.4"
 };
 
 static char *texts_ports_aio_in_ds[] = {
        "Analogue.L", "Analogue.R",
        "AES.L", "AES.R",
        "SPDIF.L", "SPDIF.R",
-       "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4"
+       "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
+       "AEB.1", "AEB.2", "AEB.3", "AEB.4"
 };
 
 static char *texts_ports_aio_out_ds[] = {
@@ -653,14 +771,16 @@ static char *texts_ports_aio_out_ds[] = {
        "AES.L", "AES.R",
        "SPDIF.L", "SPDIF.R",
        "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
-       "Phone.L", "Phone.R"
+       "Phone.L", "Phone.R",
+       "AEB.1", "AEB.2", "AEB.3", "AEB.4"
 };
 
 static char *texts_ports_aio_in_qs[] = {
        "Analogue.L", "Analogue.R",
        "AES.L", "AES.R",
        "SPDIF.L", "SPDIF.R",
-       "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4"
+       "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
+       "AEB.1", "AEB.2", "AEB.3", "AEB.4"
 };
 
 static char *texts_ports_aio_out_qs[] = {
@@ -668,7 +788,8 @@ static char *texts_ports_aio_out_qs[] = {
        "AES.L", "AES.R",
        "SPDIF.L", "SPDIF.R",
        "ADAT.1", "ADAT.2", "ADAT.3", "ADAT.4",
-       "Phone.L", "Phone.R"
+       "Phone.L", "Phone.R",
+       "AEB.1", "AEB.2", "AEB.3", "AEB.4"
 };
 
 static char *texts_ports_aes32[] = {
@@ -745,8 +866,8 @@ static char channel_map_aio_in_ss[HDSPM_MAX_CHANNELS] = {
        8, 9,                   /* aes in, */
        10, 11,                 /* spdif in */
        12, 13, 14, 15, 16, 17, 18, 19, /* ADAT in */
-       -1, -1,
-       -1, -1, -1, -1, -1, -1, -1, -1,
+       2, 3, 4, 5,             /* AEB */
+       -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
@@ -760,7 +881,8 @@ static char channel_map_aio_out_ss[HDSPM_MAX_CHANNELS] = {
        10, 11,                 /* spdif out */
        12, 13, 14, 15, 16, 17, 18, 19, /* ADAT out */
        6, 7,                   /* phone out */
-       -1, -1, -1, -1, -1, -1, -1, -1,
+       2, 3, 4, 5,             /* AEB */
+       -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
@@ -773,7 +895,8 @@ static char channel_map_aio_in_ds[HDSPM_MAX_CHANNELS] = {
        8, 9,                   /* aes in */
        10, 11,                 /* spdif in */
        12, 14, 16, 18,         /* adat in */
-       -1, -1, -1, -1, -1, -1,
+       2, 3, 4, 5,             /* AEB */
+       -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
@@ -788,7 +911,7 @@ static char channel_map_aio_out_ds[HDSPM_MAX_CHANNELS] = {
        10, 11,                 /* spdif out */
        12, 14, 16, 18,         /* adat out */
        6, 7,                   /* phone out */
-       -1, -1, -1, -1,
+       2, 3, 4, 5,             /* AEB */
        -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
@@ -802,7 +925,8 @@ static char channel_map_aio_in_qs[HDSPM_MAX_CHANNELS] = {
        8, 9,                   /* aes in */
        10, 11,                 /* spdif in */
        12, 16,                 /* adat in */
-       -1, -1, -1, -1, -1, -1, -1, -1,
+       2, 3, 4, 5,             /* AEB */
+       -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
@@ -817,7 +941,8 @@ static char channel_map_aio_out_qs[HDSPM_MAX_CHANNELS] = {
        10, 11,                 /* spdif out */
        12, 16,                 /* adat out */
        6, 7,                   /* phone out */
-       -1, -1, -1, -1, -1, -1,
+       2, 3, 4, 5,             /* AEB */
+       -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
        -1, -1, -1, -1, -1, -1, -1, -1,
@@ -856,11 +981,11 @@ struct hdspm_midi {
 };
 
 struct hdspm_tco {
-       int input;
-       int framerate;
-       int wordclock;
-       int samplerate;
-       int pull;
+       int input; /* 0: LTC, 1:Video, 2: WC*/
+       int framerate; /* 0=24, 1=25, 2=29.97, 3=29.97d, 4=30, 5=30d */
+       int wordclock; /* 0=1:1, 1=44.1->48, 2=48->44.1 */
+       int samplerate; /* 0=44.1, 1=48, 2= freq from app */
+       int pull; /*   0=0, 1=+0.1%, 2=-0.1%, 3=+4%, 4=-4%*/
        int term; /* 0 = off, 1 = on */
 };
 
@@ -879,7 +1004,7 @@ struct hdspm {
 
        u32 control_register;   /* cached value */
        u32 control2_register;  /* cached value */
-       u32 settings_register;
+       u32 settings_register;  /* cached value for AIO / RayDat (sync reference, master/slave) */
 
        struct hdspm_midi midi[4];
        struct tasklet_struct midi_tasklet;
@@ -941,7 +1066,7 @@ struct hdspm {
 
        struct hdspm_tco *tco;  /* NULL if no TCO detected */
 
-       char **texts_autosync;
+       const char *const *texts_autosync;
        int texts_autosync_items;
 
        cycles_t last_interrupt;
@@ -976,12 +1101,24 @@ static inline void snd_hdspm_initialize_midi_flush(struct hdspm *hdspm);
 static inline int hdspm_get_pll_freq(struct hdspm *hdspm);
 static int hdspm_update_simple_mixer_controls(struct hdspm *hdspm);
 static int hdspm_autosync_ref(struct hdspm *hdspm);
+static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out);
 static int snd_hdspm_set_defaults(struct hdspm *hdspm);
 static int hdspm_system_clock_mode(struct hdspm *hdspm);
 static void hdspm_set_sgbuf(struct hdspm *hdspm,
                            struct snd_pcm_substream *substream,
                             unsigned int reg, int channels);
 
+static int hdspm_aes_sync_check(struct hdspm *hdspm, int idx);
+static int hdspm_wc_sync_check(struct hdspm *hdspm);
+static int hdspm_tco_sync_check(struct hdspm *hdspm);
+static int hdspm_sync_in_sync_check(struct hdspm *hdspm);
+
+static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index);
+static int hdspm_get_tco_sample_rate(struct hdspm *hdspm);
+static int hdspm_get_wc_sample_rate(struct hdspm *hdspm);
+
+
+
 static inline int HDSPM_bit2freq(int n)
 {
        static const int bit2freq_tab[] = {
@@ -992,6 +1129,12 @@ static inline int HDSPM_bit2freq(int n)
        return bit2freq_tab[n];
 }
 
+static bool hdspm_is_raydat_or_aio(struct hdspm *hdspm)
+{
+       return ((AIO == hdspm->io_type) || (RayDAT == hdspm->io_type));
+}
+
+
 /* Write/read to/from HDSPM with Adresses in Bytes
    not words but only 32Bit writes are allowed */
 
@@ -1107,14 +1250,11 @@ static int hdspm_rate_multiplier(struct hdspm *hdspm, int rate)
                else if (hdspm->control_register &
                                HDSPM_DoubleSpeed)
                        return rate * 2;
-       };
+       }
        return rate;
 }
 
-static int hdspm_tco_sync_check(struct hdspm *hdspm);
-static int hdspm_sync_in_sync_check(struct hdspm *hdspm);
-
-/* check for external sample rate */
+/* check for external sample rate, returns the sample rate in Hz*/
 static int hdspm_external_sample_rate(struct hdspm *hdspm)
 {
        unsigned int status, status2, timecode;
@@ -1127,17 +1267,36 @@ static int hdspm_external_sample_rate(struct hdspm *hdspm)
                timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
 
                syncref = hdspm_autosync_ref(hdspm);
+               switch (syncref) {
+               case HDSPM_AES32_AUTOSYNC_FROM_WORD:
+               /* Check WC sync and get sample rate */
+                       if (hdspm_wc_sync_check(hdspm))
+                               return HDSPM_bit2freq(hdspm_get_wc_sample_rate(hdspm));
+                       break;
 
-               if (syncref == HDSPM_AES32_AUTOSYNC_FROM_WORD &&
-                               status & HDSPM_AES32_wcLock)
-                       return HDSPM_bit2freq((status >> HDSPM_AES32_wcFreq_bit) & 0xF);
+               case HDSPM_AES32_AUTOSYNC_FROM_AES1:
+               case HDSPM_AES32_AUTOSYNC_FROM_AES2:
+               case HDSPM_AES32_AUTOSYNC_FROM_AES3:
+               case HDSPM_AES32_AUTOSYNC_FROM_AES4:
+               case HDSPM_AES32_AUTOSYNC_FROM_AES5:
+               case HDSPM_AES32_AUTOSYNC_FROM_AES6:
+               case HDSPM_AES32_AUTOSYNC_FROM_AES7:
+               case HDSPM_AES32_AUTOSYNC_FROM_AES8:
+               /* Check AES sync and get sample rate */
+                       if (hdspm_aes_sync_check(hdspm, syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1))
+                               return HDSPM_bit2freq(hdspm_get_aes_sample_rate(hdspm,
+                                                       syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1));
+                       break;
 
-               if (syncref >= HDSPM_AES32_AUTOSYNC_FROM_AES1 &&
-                               syncref <= HDSPM_AES32_AUTOSYNC_FROM_AES8 &&
-                               status2 & (HDSPM_LockAES >>
-                               (syncref - HDSPM_AES32_AUTOSYNC_FROM_AES1)))
-                       return HDSPM_bit2freq((timecode >> (4*(syncref-HDSPM_AES32_AUTOSYNC_FROM_AES1))) & 0xF);
-               return 0;
+
+               case HDSPM_AES32_AUTOSYNC_FROM_TCO:
+               /* Check TCO sync and get sample rate */
+                       if (hdspm_tco_sync_check(hdspm))
+                               return HDSPM_bit2freq(hdspm_get_tco_sample_rate(hdspm));
+                       break;
+               default:
+                       return 0;
+               } /* end switch(syncref) */
                break;
 
        case MADIface:
@@ -2129,6 +2288,9 @@ static int hdspm_get_wc_sample_rate(struct hdspm *hdspm)
                status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
                return (status >> 16) & 0xF;
                break;
+       case AES32:
+               status = hdspm_read(hdspm, HDSPM_statusRegister);
+               return (status >> HDSPM_AES32_wcFreq_bit) & 0xF;
        default:
                break;
        }
@@ -2152,6 +2314,9 @@ static int hdspm_get_tco_sample_rate(struct hdspm *hdspm)
                        status = hdspm_read(hdspm, HDSPM_RD_STATUS_1);
                        return (status >> 20) & 0xF;
                        break;
+               case AES32:
+                       status = hdspm_read(hdspm, HDSPM_statusRegister);
+                       return (status >> 1) & 0xF;
                default:
                        break;
                }
@@ -2183,6 +2348,23 @@ static int hdspm_get_sync_in_sample_rate(struct hdspm *hdspm)
        return 0;
 }
 
+/**
+ * Returns the AES sample rate class for the given card.
+ **/
+static int hdspm_get_aes_sample_rate(struct hdspm *hdspm, int index)
+{
+       int timecode;
+
+       switch (hdspm->io_type) {
+       case AES32:
+               timecode = hdspm_read(hdspm, HDSPM_timecodeRegister);
+               return (timecode >> (4*index)) & 0xF;
+               break;
+       default:
+               break;
+       }
+       return 0;
+}
 
 /**
  * Returns the sample rate class for input source <idx> for
@@ -2196,15 +2378,23 @@ static int hdspm_get_s1_sample_rate(struct hdspm *hdspm, unsigned int idx)
 }
 
 #define ENUMERATED_CTL_INFO(info, texts) \
-{ \
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED; \
-       uinfo->count = 1; \
-       uinfo->value.enumerated.items = ARRAY_SIZE(texts); \
-       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items) \
-               uinfo->value.enumerated.item =  uinfo->value.enumerated.items - 1; \
-       strcpy(uinfo->value.enumerated.name, texts[uinfo->value.enumerated.item]); \
-}
+       snd_ctl_enum_info(info, 1, ARRAY_SIZE(texts), texts)
+
 
+/* Helper function to query the external sample rate and return the
+ * corresponding enum to be returned to userspace.
+ */
+static int hdspm_external_rate_to_enum(struct hdspm *hdspm)
+{
+       int rate = hdspm_external_sample_rate(hdspm);
+       int i, selected_rate = 0;
+       for (i = 1; i < 10; i++)
+               if (HDSPM_bit2freq(i) == rate) {
+                       selected_rate = i;
+                       break;
+               }
+       return selected_rate;
+}
 
 
 #define HDSPM_AUTOSYNC_SAMPLE_RATE(xname, xindex) \
@@ -2270,7 +2460,7 @@ static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol,
                default:
                        ucontrol->value.enumerated.item[0] =
                                hdspm_get_s1_sample_rate(hdspm,
-                                               ucontrol->id.index-1);
+                                               kcontrol->private_value-1);
                }
                break;
 
@@ -2289,28 +2479,24 @@ static int snd_hdspm_get_autosync_sample_rate(struct snd_kcontrol *kcontrol,
                        ucontrol->value.enumerated.item[0] =
                                hdspm_get_sync_in_sample_rate(hdspm);
                        break;
+               case 11: /* External Rate */
+                       ucontrol->value.enumerated.item[0] =
+                               hdspm_external_rate_to_enum(hdspm);
+                       break;
                default: /* AES1 to AES8 */
                        ucontrol->value.enumerated.item[0] =
-                               hdspm_get_s1_sample_rate(hdspm,
-                                               kcontrol->private_value-1);
+                               hdspm_get_aes_sample_rate(hdspm,
+                                               kcontrol->private_value -
+                                               HDSPM_AES32_AUTOSYNC_FROM_AES1);
                        break;
                }
                break;
 
        case MADI:
        case MADIface:
-               {
-                       int rate = hdspm_external_sample_rate(hdspm);
-                       int i, selected_rate = 0;
-                       for (i = 1; i < 10; i++)
-                               if (HDSPM_bit2freq(i) == rate) {
-                                       selected_rate = i;
-                                       break;
-                               }
-                       ucontrol->value.enumerated.item[0] = selected_rate;
-               }
+               ucontrol->value.enumerated.item[0] =
+                       hdspm_external_rate_to_enum(hdspm);
                break;
-
        default:
                break;
        }
@@ -2359,33 +2545,17 @@ static int hdspm_system_clock_mode(struct hdspm *hdspm)
  **/
 static void hdspm_set_system_clock_mode(struct hdspm *hdspm, int mode)
 {
-       switch (hdspm->io_type) {
-       case AIO:
-       case RayDAT:
-               if (0 == mode)
-                       hdspm->settings_register |= HDSPM_c0Master;
-               else
-                       hdspm->settings_register &= ~HDSPM_c0Master;
-
-               hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
-               break;
-
-       default:
-               if (0 == mode)
-                       hdspm->control_register |= HDSPM_ClockModeMaster;
-               else
-                       hdspm->control_register &= ~HDSPM_ClockModeMaster;
-
-               hdspm_write(hdspm, HDSPM_controlRegister,
-                               hdspm->control_register);
-       }
+       hdspm_set_toggle_setting(hdspm,
+                       (hdspm_is_raydat_or_aio(hdspm)) ?
+                       HDSPM_c0Master : HDSPM_ClockModeMaster,
+                       (0 == mode));
 }
 
 
 static int snd_hdspm_info_system_clock_mode(struct snd_kcontrol *kcontrol,
                                            struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "Master", "AutoSync" };
+       static const char *const texts[] = { "Master", "AutoSync" };
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
 }
@@ -2809,16 +2979,7 @@ static int snd_hdspm_info_pref_sync_ref(struct snd_kcontrol *kcontrol,
 {
        struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
 
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-       uinfo->count = 1;
-       uinfo->value.enumerated.items = hdspm->texts_autosync_items;
-
-       if (uinfo->value.enumerated.item >= uinfo->value.enumerated.items)
-               uinfo->value.enumerated.item =
-                       uinfo->value.enumerated.items - 1;
-
-       strcpy(uinfo->value.enumerated.name,
-                       hdspm->texts_autosync[uinfo->value.enumerated.item]);
+       snd_ctl_enum_info(uinfo, 1, hdspm->texts_autosync_items, hdspm->texts_autosync);
 
        return 0;
 }
@@ -2873,19 +3034,20 @@ static int snd_hdspm_put_pref_sync_ref(struct snd_kcontrol *kcontrol,
 
 static int hdspm_autosync_ref(struct hdspm *hdspm)
 {
+       /* This looks at the autosync selected sync reference */
        if (AES32 == hdspm->io_type) {
+
                unsigned int status = hdspm_read(hdspm, HDSPM_statusRegister);
-               unsigned int syncref =
-                       (status >> HDSPM_AES32_syncref_bit) & 0xF;
-               if (syncref == 0)
-                       return HDSPM_AES32_AUTOSYNC_FROM_WORD;
-               if (syncref <= 8)
+               unsigned int syncref = (status >> HDSPM_AES32_syncref_bit) & 0xF;
+               if ((syncref >= HDSPM_AES32_AUTOSYNC_FROM_WORD) &&
+                               (syncref <= HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN)) {
                        return syncref;
+               }
                return HDSPM_AES32_AUTOSYNC_FROM_NONE;
+
        } else if (MADI == hdspm->io_type) {
-               /* This looks at the autosync selected sync reference */
-               unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
 
+               unsigned int status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
                switch (status2 & HDSPM_SelSyncRefMask) {
                case HDSPM_SelSyncRef_WORD:
                        return HDSPM_AUTOSYNC_FROM_WORD;
@@ -2898,7 +3060,7 @@ static int hdspm_autosync_ref(struct hdspm *hdspm)
                case HDSPM_SelSyncRef_NVALID:
                        return HDSPM_AUTOSYNC_FROM_NONE;
                default:
-                       return 0;
+                       return HDSPM_AUTOSYNC_FROM_NONE;
                }
 
        }
@@ -2912,31 +3074,15 @@ static int snd_hdspm_info_autosync_ref(struct snd_kcontrol *kcontrol,
        struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
 
        if (AES32 == hdspm->io_type) {
-               static char *texts[] = { "WordClock", "AES1", "AES2", "AES3",
-                       "AES4", "AES5", "AES6", "AES7", "AES8", "None"};
-
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-               uinfo->count = 1;
-               uinfo->value.enumerated.items = 10;
-               if (uinfo->value.enumerated.item >=
-                   uinfo->value.enumerated.items)
-                       uinfo->value.enumerated.item =
-                               uinfo->value.enumerated.items - 1;
-               strcpy(uinfo->value.enumerated.name,
-                               texts[uinfo->value.enumerated.item]);
+               static const char *const texts[] = { "WordClock", "AES1", "AES2", "AES3",
+                       "AES4", "AES5", "AES6", "AES7", "AES8", "TCO", "Sync In", "None"};
+
+               ENUMERATED_CTL_INFO(uinfo, texts);
        } else if (MADI == hdspm->io_type) {
-               static char *texts[] = {"Word Clock", "MADI", "TCO",
+               static const char *const texts[] = {"Word Clock", "MADI", "TCO",
                        "Sync In", "None" };
 
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-               uinfo->count = 1;
-               uinfo->value.enumerated.items = 5;
-               if (uinfo->value.enumerated.item >=
-                               uinfo->value.enumerated.items)
-                       uinfo->value.enumerated.item =
-                               uinfo->value.enumerated.items - 1;
-               strcpy(uinfo->value.enumerated.name,
-                               texts[uinfo->value.enumerated.item]);
+               ENUMERATED_CTL_INFO(uinfo, texts);
        }
        return 0;
 }
@@ -2964,7 +3110,7 @@ static int snd_hdspm_get_autosync_ref(struct snd_kcontrol *kcontrol,
 static int snd_hdspm_info_tco_video_input_format(struct snd_kcontrol *kcontrol,
                                       struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = {"No video", "NTSC", "PAL"};
+       static const char *const texts[] = {"No video", "NTSC", "PAL"};
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
 }
@@ -3010,7 +3156,7 @@ static int snd_hdspm_get_tco_video_input_format(struct snd_kcontrol *kcontrol,
 static int snd_hdspm_info_tco_ltc_frames(struct snd_kcontrol *kcontrol,
                                       struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps",
+       static const char *const texts[] = {"No lock", "24 fps", "25 fps", "29.97 fps",
                                "30 fps"};
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
@@ -3027,19 +3173,19 @@ static int hdspm_tco_ltc_frames(struct hdspm *hdspm)
                                        HDSPM_TCO1_LTC_Format_MSB)) {
                case 0:
                        /* 24 fps */
-                       ret = 1;
+                       ret = fps_24;
                        break;
                case HDSPM_TCO1_LTC_Format_LSB:
                        /* 25 fps */
-                       ret = 2;
+                       ret = fps_25;
                        break;
                case HDSPM_TCO1_LTC_Format_MSB:
-                       /* 25 fps */
-                       ret = 3;
+                       /* 29.97 fps */
+                       ret = fps_2997;
                        break;
                default:
                        /* 30 fps */
-                       ret = 4;
+                       ret = fps_30;
                        break;
                }
        }
@@ -3067,16 +3213,35 @@ static int snd_hdspm_get_tco_ltc_frames(struct snd_kcontrol *kcontrol,
 
 static int hdspm_toggle_setting(struct hdspm *hdspm, u32 regmask)
 {
-       return (hdspm->control_register & regmask) ? 1 : 0;
+       u32 reg;
+
+       if (hdspm_is_raydat_or_aio(hdspm))
+               reg = hdspm->settings_register;
+       else
+               reg = hdspm->control_register;
+
+       return (reg & regmask) ? 1 : 0;
 }
 
 static int hdspm_set_toggle_setting(struct hdspm *hdspm, u32 regmask, int out)
 {
+       u32 *reg;
+       u32 target_reg;
+
+       if (hdspm_is_raydat_or_aio(hdspm)) {
+               reg = &(hdspm->settings_register);
+               target_reg = HDSPM_WR_SETTINGS;
+       } else {
+               reg = &(hdspm->control_register);
+               target_reg = HDSPM_controlRegister;
+       }
+
        if (out)
-               hdspm->control_register |= regmask;
+               *reg |= regmask;
        else
-               hdspm->control_register &= ~regmask;
-       hdspm_write(hdspm, HDSPM_controlRegister, hdspm->control_register);
+               *reg &= ~regmask;
+
+       hdspm_write(hdspm, target_reg, *reg);
 
        return 0;
 }
@@ -3141,7 +3306,7 @@ static int hdspm_set_input_select(struct hdspm * hdspm, int out)
 static int snd_hdspm_info_input_select(struct snd_kcontrol *kcontrol,
                                       struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "optical", "coaxial" };
+       static const char *const texts[] = { "optical", "coaxial" };
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
 }
@@ -3203,7 +3368,7 @@ static int hdspm_set_ds_wire(struct hdspm * hdspm, int ds)
 static int snd_hdspm_info_ds_wire(struct snd_kcontrol *kcontrol,
                                  struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "Single", "Double" };
+       static const char *const texts[] = { "Single", "Double" };
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
 }
@@ -3276,7 +3441,7 @@ static int hdspm_set_qs_wire(struct hdspm * hdspm, int mode)
 static int snd_hdspm_info_qs_wire(struct snd_kcontrol *kcontrol,
                                       struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "Single", "Double", "Quad" };
+       static const char *const texts[] = { "Single", "Double", "Quad" };
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
 }
@@ -3313,6 +3478,84 @@ static int snd_hdspm_put_qs_wire(struct snd_kcontrol *kcontrol,
        return change;
 }
 
+#define HDSPM_CONTROL_TRISTATE(xname, xindex) \
+{      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
+       .name = xname, \
+       .private_value = xindex, \
+       .info = snd_hdspm_info_tristate, \
+       .get = snd_hdspm_get_tristate, \
+       .put = snd_hdspm_put_tristate \
+}
+
+static int hdspm_tristate(struct hdspm *hdspm, u32 regmask)
+{
+       u32 reg = hdspm->settings_register & (regmask * 3);
+       return reg / regmask;
+}
+
+static int hdspm_set_tristate(struct hdspm *hdspm, int mode, u32 regmask)
+{
+       hdspm->settings_register &= ~(regmask * 3);
+       hdspm->settings_register |= (regmask * mode);
+       hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
+
+       return 0;
+}
+
+static int snd_hdspm_info_tristate(struct snd_kcontrol *kcontrol,
+                                      struct snd_ctl_elem_info *uinfo)
+{
+       u32 regmask = kcontrol->private_value;
+
+       static const char *const texts_spdif[] = { "Optical", "Coaxial", "Internal" };
+       static const char *const texts_levels[] = { "Hi Gain", "+4 dBu", "-10 dBV" };
+
+       switch (regmask) {
+       case HDSPM_c0_Input0:
+               ENUMERATED_CTL_INFO(uinfo, texts_spdif);
+               break;
+       default:
+               ENUMERATED_CTL_INFO(uinfo, texts_levels);
+               break;
+       }
+       return 0;
+}
+
+static int snd_hdspm_get_tristate(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+       u32 regmask = kcontrol->private_value;
+
+       spin_lock_irq(&hdspm->lock);
+       ucontrol->value.enumerated.item[0] = hdspm_tristate(hdspm, regmask);
+       spin_unlock_irq(&hdspm->lock);
+       return 0;
+}
+
+static int snd_hdspm_put_tristate(struct snd_kcontrol *kcontrol,
+                                     struct snd_ctl_elem_value *ucontrol)
+{
+       struct hdspm *hdspm = snd_kcontrol_chip(kcontrol);
+       u32 regmask = kcontrol->private_value;
+       int change;
+       int val;
+
+       if (!snd_hdspm_use_is_exclusive(hdspm))
+               return -EBUSY;
+       val = ucontrol->value.integer.value[0];
+       if (val < 0)
+               val = 0;
+       if (val > 2)
+               val = 2;
+
+       spin_lock_irq(&hdspm->lock);
+       change = val != hdspm_tristate(hdspm, regmask);
+       hdspm_set_tristate(hdspm, val, regmask);
+       spin_unlock_irq(&hdspm->lock);
+       return change;
+}
+
 #define HDSPM_MADI_SPEEDMODE(xname, xindex) \
 {      .iface = SNDRV_CTL_ELEM_IFACE_MIXER, \
        .name = xname, \
@@ -3352,7 +3595,7 @@ static int hdspm_set_madi_speedmode(struct hdspm *hdspm, int mode)
 static int snd_hdspm_info_madi_speedmode(struct snd_kcontrol *kcontrol,
                                       struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "Single", "Double", "Quad" };
+       static const char *const texts[] = { "Single", "Double", "Quad" };
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
 }
@@ -3587,7 +3830,7 @@ static int snd_hdspm_put_playback_mixer(struct snd_kcontrol *kcontrol,
 static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
                                     struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "No Lock", "Lock", "Sync", "N/A" };
+       static const char *const texts[] = { "No Lock", "Lock", "Sync", "N/A" };
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
 }
@@ -3595,7 +3838,7 @@ static int snd_hdspm_info_sync_check(struct snd_kcontrol *kcontrol,
 static int snd_hdspm_tco_info_lock_check(struct snd_kcontrol *kcontrol,
                                     struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "No Lock", "Lock" };
+       static const char *const texts[] = { "No Lock", "Lock" };
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
 }
@@ -3745,9 +3988,18 @@ static int hdspm_tco_sync_check(struct hdspm *hdspm)
        if (hdspm->tco) {
                switch (hdspm->io_type) {
                case MADI:
+                       status = hdspm_read(hdspm, HDSPM_statusRegister);
+                       if (status & HDSPM_tcoLockMadi) {
+                               if (status & HDSPM_tcoSync)
+                                       return 2;
+                               else
+                                       return 1;
+                       }
+                       return 0;
+                       break;
                case AES32:
                        status = hdspm_read(hdspm, HDSPM_statusRegister);
-                       if (status & HDSPM_tcoLock) {
+                       if (status & HDSPM_tcoLockAes) {
                                if (status & HDSPM_tcoSync)
                                        return 2;
                                else
@@ -3807,7 +4059,8 @@ static int snd_hdspm_get_sync_check(struct snd_kcontrol *kcontrol,
                case 5: /* SYNC IN */
                        val = hdspm_sync_in_sync_check(hdspm); break;
                default:
-                       val = hdspm_s1_sync_check(hdspm, ucontrol->id.index-1);
+                       val = hdspm_s1_sync_check(hdspm,
+                                       kcontrol->private_value-1);
                }
                break;
 
@@ -3975,7 +4228,8 @@ static void hdspm_tco_write(struct hdspm *hdspm)
 static int snd_hdspm_info_tco_sample_rate(struct snd_kcontrol *kcontrol,
                                          struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "44.1 kHz", "48 kHz" };
+       /* TODO freq from app could be supported here, see tco->samplerate */
+       static const char *const texts[] = { "44.1 kHz", "48 kHz" };
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
 }
@@ -4021,7 +4275,8 @@ static int snd_hdspm_put_tco_sample_rate(struct snd_kcontrol *kcontrol,
 static int snd_hdspm_info_tco_pull(struct snd_kcontrol *kcontrol,
                                   struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "0", "+ 0.1 %", "- 0.1 %", "+ 4 %", "- 4 %" };
+       static const char *const texts[] = { "0", "+ 0.1 %", "- 0.1 %",
+               "+ 4 %", "- 4 %" };
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
 }
@@ -4066,7 +4321,7 @@ static int snd_hdspm_put_tco_pull(struct snd_kcontrol *kcontrol,
 static int snd_hdspm_info_tco_wck_conversion(struct snd_kcontrol *kcontrol,
                                             struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" };
+       static const char *const texts[] = { "1:1", "44.1 -> 48", "48 -> 44.1" };
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
 }
@@ -4112,7 +4367,7 @@ static int snd_hdspm_put_tco_wck_conversion(struct snd_kcontrol *kcontrol,
 static int snd_hdspm_info_tco_frame_rate(struct snd_kcontrol *kcontrol,
                                          struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "24 fps", "25 fps", "29.97fps",
+       static const char *const texts[] = { "24 fps", "25 fps", "29.97fps",
                "29.97 dfps", "30 fps", "30 dfps" };
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
@@ -4159,7 +4414,7 @@ static int snd_hdspm_put_tco_frame_rate(struct snd_kcontrol *kcontrol,
 static int snd_hdspm_info_tco_sync_source(struct snd_kcontrol *kcontrol,
                                          struct snd_ctl_elem_info *uinfo)
 {
-       static char *texts[] = { "LTC", "Video", "WCK" };
+       static const char *const texts[] = { "LTC", "Video", "WCK" };
        ENUMERATED_CTL_INFO(uinfo, texts);
        return 0;
 }
@@ -4284,7 +4539,6 @@ static struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
        HDSPM_INTERNAL_CLOCK("Internal Clock", 0),
        HDSPM_SYSTEM_CLOCK_MODE("System Clock Mode", 0),
        HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
-       HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
        HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
        HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
        HDSPM_SYNC_CHECK("WC SyncCheck", 0),
@@ -4298,7 +4552,16 @@ static struct snd_kcontrol_new snd_hdspm_controls_aio[] = {
        HDSPM_AUTOSYNC_SAMPLE_RATE("SPDIF Frequency", 2),
        HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT Frequency", 3),
        HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 4),
-       HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5)
+       HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 5),
+       HDSPM_CONTROL_TRISTATE("S/PDIF Input", HDSPM_c0_Input0),
+       HDSPM_TOGGLE_SETTING("S/PDIF Out Optical", HDSPM_c0_Spdif_Opt),
+       HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro),
+       HDSPM_TOGGLE_SETTING("ADAT internal (AEB/TEB)", HDSPM_c0_AEB1),
+       HDSPM_TOGGLE_SETTING("XLR Breakout Cable", HDSPM_c0_Sym6db),
+       HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48),
+       HDSPM_CONTROL_TRISTATE("Input Level", HDSPM_c0_AD_GAIN0),
+       HDSPM_CONTROL_TRISTATE("Output Level", HDSPM_c0_DA_GAIN0),
+       HDSPM_CONTROL_TRISTATE("Phones Level", HDSPM_c0_PH_GAIN0)
 
                /*
                   HDSPM_INPUT_SELECT("Input Select", 0),
@@ -4335,7 +4598,9 @@ static struct snd_kcontrol_new snd_hdspm_controls_raydat[] = {
        HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT3 Frequency", 5),
        HDSPM_AUTOSYNC_SAMPLE_RATE("ADAT4 Frequency", 6),
        HDSPM_AUTOSYNC_SAMPLE_RATE("TCO Frequency", 7),
-       HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8)
+       HDSPM_AUTOSYNC_SAMPLE_RATE("SYNC IN Frequency", 8),
+       HDSPM_TOGGLE_SETTING("S/PDIF Out Professional", HDSPM_c0_Pro),
+       HDSPM_TOGGLE_SETTING("Single Speed WordClock Out", HDSPM_c0_Wck48)
 };
 
 static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
@@ -4345,7 +4610,7 @@ static struct snd_kcontrol_new snd_hdspm_controls_aes32[] = {
        HDSPM_PREF_SYNC_REF("Preferred Sync Reference", 0),
        HDSPM_AUTOSYNC_REF("AutoSync Reference", 0),
        HDSPM_SYSTEM_SAMPLE_RATE("System Sample Rate", 0),
-       HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 0),
+       HDSPM_AUTOSYNC_SAMPLE_RATE("External Rate", 11),
        HDSPM_SYNC_CHECK("WC Sync Check", 0),
        HDSPM_SYNC_CHECK("AES1 Sync Check", 1),
        HDSPM_SYNC_CHECK("AES2 Sync Check", 2),
@@ -4501,77 +4766,22 @@ static int snd_hdspm_create_controls(struct snd_card *card,
  ------------------------------------------------------------*/
 
 static void
-snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
-                        struct snd_info_buffer *buffer)
+snd_hdspm_proc_read_tco(struct snd_info_entry *entry,
+                                       struct snd_info_buffer *buffer)
 {
        struct hdspm *hdspm = entry->private_data;
-       unsigned int status, status2, control, freq;
-
-       char *pref_sync_ref;
-       char *autosync_ref;
-       char *system_clock_mode;
-       char *insel;
-       int x, x2;
-
-       /* TCO stuff */
+       unsigned int status, control;
        int a, ltc, frames, seconds, minutes, hours;
        unsigned int period;
        u64 freq_const = 0;
        u32 rate;
 
+       snd_iprintf(buffer, "--- TCO ---\n");
+
        status = hdspm_read(hdspm, HDSPM_statusRegister);
-       status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
        control = hdspm->control_register;
-       freq = hdspm_read(hdspm, HDSPM_timecodeRegister);
 
-       snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n",
-                       hdspm->card_name, hdspm->card->number + 1,
-                       hdspm->firmware_rev,
-                       (status2 & HDSPM_version0) |
-                       (status2 & HDSPM_version1) | (status2 &
-                               HDSPM_version2));
 
-       snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n",
-                       (hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF,
-                       hdspm->serial);
-
-       snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
-                       hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
-
-       snd_iprintf(buffer, "--- System ---\n");
-
-       snd_iprintf(buffer,
-               "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
-               status & HDSPM_audioIRQPending,
-               (status & HDSPM_midi0IRQPending) ? 1 : 0,
-               (status & HDSPM_midi1IRQPending) ? 1 : 0,
-               hdspm->irq_count);
-       snd_iprintf(buffer,
-               "HW pointer: id = %d, rawptr = %d (%d->%d) "
-               "estimated= %ld (bytes)\n",
-               ((status & HDSPM_BufferID) ? 1 : 0),
-               (status & HDSPM_BufferPositionMask),
-               (status & HDSPM_BufferPositionMask) %
-               (2 * (int)hdspm->period_bytes),
-               ((status & HDSPM_BufferPositionMask) - 64) %
-               (2 * (int)hdspm->period_bytes),
-               (long) hdspm_hw_pointer(hdspm) * 4);
-
-       snd_iprintf(buffer,
-               "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
-               hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
-               hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
-               hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
-               hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
-       snd_iprintf(buffer,
-               "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
-               hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
-               hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
-       snd_iprintf(buffer,
-               "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
-               "status2=0x%x\n",
-               hdspm->control_register, hdspm->control2_register,
-               status, status2);
        if (status & HDSPM_tco_detect) {
                snd_iprintf(buffer, "TCO module detected.\n");
                a = hdspm_read(hdspm, HDSPM_RD_TCO+4);
@@ -4665,6 +4875,75 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
        } else {
                snd_iprintf(buffer, "No TCO module detected.\n");
        }
+}
+
+static void
+snd_hdspm_proc_read_madi(struct snd_info_entry *entry,
+                        struct snd_info_buffer *buffer)
+{
+       struct hdspm *hdspm = entry->private_data;
+       unsigned int status, status2, control, freq;
+
+       char *pref_sync_ref;
+       char *autosync_ref;
+       char *system_clock_mode;
+       char *insel;
+       int x, x2;
+
+       status = hdspm_read(hdspm, HDSPM_statusRegister);
+       status2 = hdspm_read(hdspm, HDSPM_statusRegister2);
+       control = hdspm->control_register;
+       freq = hdspm_read(hdspm, HDSPM_timecodeRegister);
+
+       snd_iprintf(buffer, "%s (Card #%d) Rev.%x Status2first3bits: %x\n",
+                       hdspm->card_name, hdspm->card->number + 1,
+                       hdspm->firmware_rev,
+                       (status2 & HDSPM_version0) |
+                       (status2 & HDSPM_version1) | (status2 &
+                               HDSPM_version2));
+
+       snd_iprintf(buffer, "HW Serial: 0x%06x%06x\n",
+                       (hdspm_read(hdspm, HDSPM_midiStatusIn1)>>8) & 0xFFFFFF,
+                       hdspm->serial);
+
+       snd_iprintf(buffer, "IRQ: %d Registers bus: 0x%lx VM: 0x%lx\n",
+                       hdspm->irq, hdspm->port, (unsigned long)hdspm->iobase);
+
+       snd_iprintf(buffer, "--- System ---\n");
+
+       snd_iprintf(buffer,
+               "IRQ Pending: Audio=%d, MIDI0=%d, MIDI1=%d, IRQcount=%d\n",
+               status & HDSPM_audioIRQPending,
+               (status & HDSPM_midi0IRQPending) ? 1 : 0,
+               (status & HDSPM_midi1IRQPending) ? 1 : 0,
+               hdspm->irq_count);
+       snd_iprintf(buffer,
+               "HW pointer: id = %d, rawptr = %d (%d->%d) "
+               "estimated= %ld (bytes)\n",
+               ((status & HDSPM_BufferID) ? 1 : 0),
+               (status & HDSPM_BufferPositionMask),
+               (status & HDSPM_BufferPositionMask) %
+               (2 * (int)hdspm->period_bytes),
+               ((status & HDSPM_BufferPositionMask) - 64) %
+               (2 * (int)hdspm->period_bytes),
+               (long) hdspm_hw_pointer(hdspm) * 4);
+
+       snd_iprintf(buffer,
+               "MIDI FIFO: Out1=0x%x, Out2=0x%x, In1=0x%x, In2=0x%x \n",
+               hdspm_read(hdspm, HDSPM_midiStatusOut0) & 0xFF,
+               hdspm_read(hdspm, HDSPM_midiStatusOut1) & 0xFF,
+               hdspm_read(hdspm, HDSPM_midiStatusIn0) & 0xFF,
+               hdspm_read(hdspm, HDSPM_midiStatusIn1) & 0xFF);
+       snd_iprintf(buffer,
+               "MIDIoverMADI FIFO: In=0x%x, Out=0x%x \n",
+               hdspm_read(hdspm, HDSPM_midiStatusIn2) & 0xFF,
+               hdspm_read(hdspm, HDSPM_midiStatusOut2) & 0xFF);
+       snd_iprintf(buffer,
+               "Register: ctrl1=0x%x, ctrl2=0x%x, status1=0x%x, "
+               "status2=0x%x\n",
+               hdspm->control_register, hdspm->control2_register,
+               status, status2);
+
 
        snd_iprintf(buffer, "--- Settings ---\n");
 
@@ -4768,6 +5047,9 @@ snd_hdspm_proc_read_madi(struct snd_info_entry * entry,
                (status & HDSPM_RX_64ch) ? "64 channels" :
                "56 channels");
 
+       /* call readout function for TCO specific status */
+       snd_hdspm_proc_read_tco(entry, buffer);
+
        snd_iprintf(buffer, "\n");
 }
 
@@ -4909,11 +5191,18 @@ snd_hdspm_proc_read_aes32(struct snd_info_entry * entry,
                autosync_ref = "AES7"; break;
        case HDSPM_AES32_AUTOSYNC_FROM_AES8:
                autosync_ref = "AES8"; break;
+       case HDSPM_AES32_AUTOSYNC_FROM_TCO:
+               autosync_ref = "TCO"; break;
+       case HDSPM_AES32_AUTOSYNC_FROM_SYNC_IN:
+               autosync_ref = "Sync In"; break;
        default:
                autosync_ref = "---"; break;
        }
        snd_iprintf(buffer, "AutoSync ref = %s\n", autosync_ref);
 
+       /* call readout function for TCO specific status */
+       snd_hdspm_proc_read_tco(entry, buffer);
+
        snd_iprintf(buffer, "\n");
 }
 
@@ -5097,7 +5386,7 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
 
        case AES32:
                hdspm->control_register =
-                       HDSPM_ClockModeMaster | /* Master Cloack Mode on */
+                       HDSPM_ClockModeMaster | /* Master Clock Mode on */
                        hdspm_encode_latency(7) | /* latency max=8192samples */
                        HDSPM_SyncRef0 |        /* AES1 is syncclock */
                        HDSPM_LineOut | /* Analog output in */
@@ -5123,9 +5412,8 @@ static int snd_hdspm_set_defaults(struct hdspm * hdspm)
 
        all_in_all_mixer(hdspm, 0 * UNITY_GAIN);
 
-       if (hdspm->io_type == AIO || hdspm->io_type == RayDAT) {
+       if (hdspm_is_raydat_or_aio(hdspm))
                hdspm_write(hdspm, HDSPM_WR_SETTINGS, hdspm->settings_register);
-       }
 
        /* set a default rate so that the channel map is set up. */
        hdspm_set_rate(hdspm, 48000, 1);
@@ -5371,6 +5659,16 @@ static int snd_hdspm_hw_params(struct snd_pcm_substream *substream,
           */
 
 
+       /*  For AES cards, the float format bit is the same as the
+        *  preferred sync reference. Since we don't want to break
+        *  sync settings, we have to skip the remaining part of this
+        *  function.
+        */
+       if (hdspm->io_type == AES32) {
+               return 0;
+       }
+
+
        /* Switch to native float format if requested */
        if (SNDRV_PCM_FORMAT_FLOAT_LE == params_format(params)) {
                if (!(hdspm->control_register & HDSPe_FLOAT_FORMAT))
@@ -6013,7 +6311,7 @@ static int snd_hdspm_hwdep_ioctl(struct snd_hwdep *hw, struct file *file,
                                ltc.format = fps_2997;
                                break;
                        default:
-                               ltc.format = 30;
+                               ltc.format = fps_30;
                                break;
                        }
                        if (i & HDSPM_TCO1_set_drop_frame_flag) {
@@ -6479,10 +6777,6 @@ static int snd_hdspm_create(struct snd_card *card,
                break;
 
        case AIO:
-               if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) {
-                       snd_printk(KERN_INFO "HDSPM: AEB input board found, but not supported\n");
-               }
-
                hdspm->ss_in_channels = AIO_IN_SS_CHANNELS;
                hdspm->ds_in_channels = AIO_IN_DS_CHANNELS;
                hdspm->qs_in_channels = AIO_IN_QS_CHANNELS;
@@ -6490,6 +6784,20 @@ static int snd_hdspm_create(struct snd_card *card,
                hdspm->ds_out_channels = AIO_OUT_DS_CHANNELS;
                hdspm->qs_out_channels = AIO_OUT_QS_CHANNELS;
 
+               if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBI_D)) {
+                       snd_printk(KERN_INFO "HDSPM: AEB input board found\n");
+                       hdspm->ss_in_channels += 4;
+                       hdspm->ds_in_channels += 4;
+                       hdspm->qs_in_channels += 4;
+               }
+
+               if (0 == (hdspm_read(hdspm, HDSPM_statusRegister2) & HDSPM_s2_AEBO_D)) {
+                       snd_printk(KERN_INFO "HDSPM: AEB output board found\n");
+                       hdspm->ss_out_channels += 4;
+                       hdspm->ds_out_channels += 4;
+                       hdspm->qs_out_channels += 4;
+               }
+
                hdspm->channel_map_out_ss = channel_map_aio_out_ss;
                hdspm->channel_map_out_ds = channel_map_aio_out_ds;
                hdspm->channel_map_out_qs = channel_map_aio_out_qs;
@@ -6558,6 +6866,7 @@ static int snd_hdspm_create(struct snd_card *card,
                break;
 
        case MADI:
+       case AES32:
                if (hdspm_read(hdspm, HDSPM_statusRegister) & HDSPM_tco_detect) {
                        hdspm->midiPorts++;
                        hdspm->tco = kzalloc(sizeof(struct hdspm_tco),
@@ -6565,7 +6874,7 @@ static int snd_hdspm_create(struct snd_card *card,
                        if (NULL != hdspm->tco) {
                                hdspm_tco_write(hdspm);
                        }
-                       snd_printk(KERN_INFO "HDSPM: MADI TCO module found\n");
+                       snd_printk(KERN_INFO "HDSPM: MADI/AES TCO module found\n");
                } else {
                        hdspm->tco = NULL;
                }
@@ -6580,10 +6889,12 @@ static int snd_hdspm_create(struct snd_card *card,
        case AES32:
                if (hdspm->tco) {
                        hdspm->texts_autosync = texts_autosync_aes_tco;
-                       hdspm->texts_autosync_items = 10;
+                       hdspm->texts_autosync_items =
+                               ARRAY_SIZE(texts_autosync_aes_tco);
                } else {
                        hdspm->texts_autosync = texts_autosync_aes;
-                       hdspm->texts_autosync_items = 9;
+                       hdspm->texts_autosync_items =
+                               ARRAY_SIZE(texts_autosync_aes);
                }
                break;
 
index 45eeaa9f7fec49f035cf8faa54fdfdb5a8ea9df5..5138b8493051fd54c674fe26ea87d9d5fd1411c6 100644 (file)
@@ -26,12 +26,9 @@ if SND_SOC
 config SND_SOC_AC97_BUS
        bool
 
-config SND_SOC_DMAENGINE_PCM
-       bool
-
 config SND_SOC_GENERIC_DMAENGINE_PCM
        bool
-       select SND_SOC_DMAENGINE_PCM
+       select SND_DMAENGINE_PCM
 
 # All the supported SoCs
 source "sound/soc/atmel/Kconfig"
index bc0261476d7afc78767856683398ec1bd7bc463e..61a64d281905e4b8997bc2ae3eb42ce387de9e7c 100644 (file)
@@ -1,10 +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
 
-ifneq ($(CONFIG_SND_SOC_DMAENGINE_PCM),)
-snd-soc-core-objs += soc-dmaengine-pcm.o
-endif
-
 ifneq ($(CONFIG_SND_SOC_GENERIC_DMAENGINE_PCM),)
 snd-soc-core-objs += soc-generic-dmaengine-pcm.o
 endif
index 3fdd87fa18a970db151fdadcc578ca61c87d3241..e48d38a1b95c6d6b13d3eef9bb09efed5d970964 100644 (file)
@@ -13,6 +13,7 @@ config SND_ATMEL_SOC_PDC
 config SND_ATMEL_SOC_DMA
        tristate
        depends on SND_ATMEL_SOC
+       select SND_SOC_GENERIC_DMAENGINE_PCM
 
 config SND_ATMEL_SOC_SSC
        tristate
@@ -32,6 +33,26 @@ config SND_AT91_SOC_SAM9G20_WM8731
          Say Y if you want to add support for SoC audio on WM8731-based
          AT91sam9g20 evaluation board.
 
+config SND_ATMEL_SOC_WM8904
+       tristate "Atmel ASoC driver for boards using WM8904 codec"
+       depends on ARCH_AT91 && ATMEL_SSC && SND_ATMEL_SOC
+       select SND_ATMEL_SOC_SSC
+       select SND_ATMEL_SOC_DMA
+       select SND_SOC_WM8904
+       help
+         Say Y if you want to add support for Atmel ASoC driver for boards using
+         WM8904 codec.
+
+config SND_AT91_SOC_SAM9X5_WM8731
+       tristate "SoC Audio support for WM8731-based at91sam9x5 board"
+       depends on ATMEL_SSC && SND_ATMEL_SOC && SOC_AT91SAM9X5
+       select SND_ATMEL_SOC_SSC
+       select SND_ATMEL_SOC_DMA
+       select SND_SOC_WM8731
+       help
+         Say Y if you want to add support for audio SoC on an
+         at91sam9x5 based board that is using WM8731 codec.
+
 config SND_AT91_SOC_AFEB9260
        tristate "SoC Audio support for AFEB9260 board"
        depends on ARCH_AT91 && ATMEL_SSC && ARCH_AT91 && MACH_AFEB9260 && SND_ATMEL_SOC
index 41967ccb6f41e3968753d4922ad03f2439ad81b9..5baabc8bde3abfa4dfcb833c1c3fbcb858bf7ced 100644 (file)
@@ -11,6 +11,10 @@ obj-$(CONFIG_SND_ATMEL_SOC_SSC) += snd-soc-atmel_ssc_dai.o
 
 # AT91 Machine Support
 snd-soc-sam9g20-wm8731-objs := sam9g20_wm8731.o
+snd-atmel-soc-wm8904-objs := atmel_wm8904.o
+snd-soc-sam9x5-wm8731-objs := sam9x5_wm8731.o
 
 obj-$(CONFIG_SND_AT91_SOC_SAM9G20_WM8731) += snd-soc-sam9g20-wm8731.o
+obj-$(CONFIG_SND_ATMEL_SOC_WM8904) += snd-atmel-soc-wm8904.o
+obj-$(CONFIG_SND_AT91_SOC_SAM9X5_WM8731) += snd-soc-sam9x5-wm8731.o
 obj-$(CONFIG_SND_AT91_SOC_AFEB9260) += snd-soc-afeb9260.o
index d12826526798fc47620e4cb613df9e95da8a8f7c..06082e5e5dcb7d726e2bdba6e5f2d7f816dcf262 100644 (file)
@@ -91,138 +91,52 @@ static void atmel_pcm_dma_irq(u32 ssc_sr,
        }
 }
 
-/*--------------------------------------------------------------------------*\
- * DMAENGINE operations
-\*--------------------------------------------------------------------------*/
-static bool filter(struct dma_chan *chan, void *slave)
-{
-       struct at_dma_slave *sl = slave;
-
-       if (sl->dma_dev == chan->device->dev) {
-               chan->private = sl;
-               return true;
-       } else {
-               return false;
-       }
-}
-
 static int atmel_pcm_configure_dma(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params, struct atmel_pcm_dma_params *prtd)
+       struct snd_pcm_hw_params *params, struct dma_slave_config *slave_config)
 {
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct atmel_pcm_dma_params *prtd;
        struct ssc_device *ssc;
-       struct dma_chan *dma_chan;
-       struct dma_slave_config slave_config;
        int ret;
 
+       prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
        ssc = prtd->ssc;
 
-       ret = snd_hwparams_to_dma_slave_config(substream, params,
-                       &slave_config);
+       ret = snd_hwparams_to_dma_slave_config(substream, params, slave_config);
        if (ret) {
                pr_err("atmel-pcm: hwparams to dma slave configure failed\n");
                return ret;
        }
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               slave_config.dst_addr = (dma_addr_t)ssc->phybase + SSC_THR;
-               slave_config.dst_maxburst = 1;
+               slave_config->dst_addr = ssc->phybase + SSC_THR;
+               slave_config->dst_maxburst = 1;
        } else {
-               slave_config.src_addr = (dma_addr_t)ssc->phybase + SSC_RHR;
-               slave_config.src_maxburst = 1;
-       }
-
-       dma_chan = snd_dmaengine_pcm_get_chan(substream);
-       if (dmaengine_slave_config(dma_chan, &slave_config)) {
-               pr_err("atmel-pcm: failed to configure dma channel\n");
-               ret = -EBUSY;
-               return ret;
-       }
-
-       return 0;
-}
-
-static int atmel_pcm_hw_params(struct snd_pcm_substream *substream,
-       struct snd_pcm_hw_params *params)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct atmel_pcm_dma_params *prtd;
-       struct ssc_device *ssc;
-       struct at_dma_slave *sdata = NULL;
-       int ret;
-
-       snd_pcm_set_runtime_buffer(substream, &substream->dma_buffer);
-
-       prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-       ssc = prtd->ssc;
-       if (ssc->pdev)
-               sdata = ssc->pdev->dev.platform_data;
-
-       ret = snd_dmaengine_pcm_open_request_chan(substream, filter, sdata);
-       if (ret) {
-               pr_err("atmel-pcm: dmaengine pcm open failed\n");
-               return -EINVAL;
-       }
-
-       ret = atmel_pcm_configure_dma(substream, params, prtd);
-       if (ret) {
-               pr_err("atmel-pcm: failed to configure dmai\n");
-               goto err;
+               slave_config->src_addr = ssc->phybase + SSC_RHR;
+               slave_config->src_maxburst = 1;
        }
 
        prtd->dma_intr_handler = atmel_pcm_dma_irq;
 
        return 0;
-err:
-       snd_dmaengine_pcm_close_release_chan(substream);
-       return ret;
 }
 
-static int atmel_pcm_dma_prepare(struct snd_pcm_substream *substream)
-{
-       struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct atmel_pcm_dma_params *prtd;
-
-       prtd = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
-
-       ssc_writex(prtd->ssc->regs, SSC_IER, prtd->mask->ssc_error);
-       ssc_writex(prtd->ssc->regs, SSC_CR, prtd->mask->ssc_enable);
-
-       return 0;
-}
-
-static int atmel_pcm_open(struct snd_pcm_substream *substream)
-{
-       snd_soc_set_runtime_hwparams(substream, &atmel_pcm_dma_hardware);
-
-       return 0;
-}
-
-static struct snd_pcm_ops atmel_pcm_ops = {
-       .open           = atmel_pcm_open,
-       .close          = snd_dmaengine_pcm_close_release_chan,
-       .ioctl          = snd_pcm_lib_ioctl,
-       .hw_params      = atmel_pcm_hw_params,
-       .prepare        = atmel_pcm_dma_prepare,
-       .trigger        = snd_dmaengine_pcm_trigger,
-       .pointer        = snd_dmaengine_pcm_pointer_no_residue,
-       .mmap           = atmel_pcm_mmap,
-};
-
-static struct snd_soc_platform_driver atmel_soc_platform = {
-       .ops            = &atmel_pcm_ops,
-       .pcm_new        = atmel_pcm_new,
-       .pcm_free       = atmel_pcm_free,
+static const struct snd_dmaengine_pcm_config atmel_dmaengine_pcm_config = {
+       .prepare_slave_config = atmel_pcm_configure_dma,
+       .pcm_hardware = &atmel_pcm_dma_hardware,
+       .prealloc_buffer_size = ATMEL_SSC_DMABUF_SIZE,
 };
 
 int atmel_pcm_dma_platform_register(struct device *dev)
 {
-       return snd_soc_register_platform(dev, &atmel_soc_platform);
+       return snd_dmaengine_pcm_register(dev, &atmel_dmaengine_pcm_config,
+                       SND_DMAENGINE_PCM_FLAG_NO_RESIDUE);
 }
 EXPORT_SYMBOL(atmel_pcm_dma_platform_register);
 
 void atmel_pcm_dma_platform_unregister(struct device *dev)
 {
-       snd_soc_unregister_platform(dev);
+       snd_dmaengine_pcm_unregister(dev);
 }
 EXPORT_SYMBOL(atmel_pcm_dma_platform_unregister);
 
index f3fdfa07fcb9fafa76d913b64be070b54d31486e..0ecf356027f6c1fcc91693e4c7063152fe3ac706 100644 (file)
@@ -73,6 +73,7 @@ static struct atmel_ssc_mask ssc_tx_mask = {
        .ssc_disable    = SSC_BIT(CR_TXDIS),
        .ssc_endx       = SSC_BIT(SR_ENDTX),
        .ssc_endbuf     = SSC_BIT(SR_TXBUFE),
+       .ssc_error      = SSC_BIT(SR_OVRUN),
        .pdc_enable     = ATMEL_PDC_TXTEN,
        .pdc_disable    = ATMEL_PDC_TXTDIS,
 };
@@ -82,6 +83,7 @@ static struct atmel_ssc_mask ssc_rx_mask = {
        .ssc_disable    = SSC_BIT(CR_RXDIS),
        .ssc_endx       = SSC_BIT(SR_ENDRX),
        .ssc_endbuf     = SSC_BIT(SR_RXBUFF),
+       .ssc_error      = SSC_BIT(SR_OVRUN),
        .pdc_enable     = ATMEL_PDC_RXTEN,
        .pdc_disable    = ATMEL_PDC_RXTDIS,
 };
@@ -196,15 +198,27 @@ static int atmel_ssc_startup(struct snd_pcm_substream *substream,
                             struct snd_soc_dai *dai)
 {
        struct atmel_ssc_info *ssc_p = &ssc_info[dai->id];
-       int dir_mask;
+       struct atmel_pcm_dma_params *dma_params;
+       int dir, dir_mask;
 
        pr_debug("atmel_ssc_startup: SSC_SR=0x%u\n",
                ssc_readl(ssc_p->ssc->regs, SR));
 
-       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               dir = 0;
                dir_mask = SSC_DIR_MASK_PLAYBACK;
-       else
+       } else {
+               dir = 1;
                dir_mask = SSC_DIR_MASK_CAPTURE;
+       }
+
+       dma_params = &ssc_dma_params[dai->id][dir];
+       dma_params->ssc = ssc_p->ssc;
+       dma_params->substream = substream;
+
+       ssc_p->dma_params[dir] = dma_params;
+
+       snd_soc_dai_set_dma_data(dai, substream, dma_params);
 
        spin_lock_irq(&ssc_p->lock);
        if (ssc_p->dir_mask & dir_mask) {
@@ -325,7 +339,6 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params,
        struct snd_soc_dai *dai)
 {
-       struct snd_soc_pcm_runtime *rtd = snd_pcm_substream_chip(substream);
        int id = dai->id;
        struct atmel_ssc_info *ssc_p = &ssc_info[id];
        struct atmel_pcm_dma_params *dma_params;
@@ -344,19 +357,7 @@ static int atmel_ssc_hw_params(struct snd_pcm_substream *substream,
        else
                dir = 1;
 
-       dma_params = &ssc_dma_params[id][dir];
-       dma_params->ssc = ssc_p->ssc;
-       dma_params->substream = substream;
-
-       ssc_p->dma_params[dir] = dma_params;
-
-       /*
-        * The snd_soc_pcm_stream->dma_data field is only used to communicate
-        * the appropriate DMA parameters to the pcm driver hw_params()
-        * function.  It should not be used for other purposes
-        * as it is common to all substreams.
-        */
-       snd_soc_dai_set_dma_data(rtd->cpu_dai, substream, dma_params);
+       dma_params = ssc_p->dma_params[dir];
 
        channels = params_channels(params);
 
@@ -648,6 +649,7 @@ static int atmel_ssc_prepare(struct snd_pcm_substream *substream,
        dma_params = ssc_p->dma_params[dir];
 
        ssc_writel(ssc_p->ssc->regs, CR, dma_params->mask->ssc_enable);
+       ssc_writel(ssc_p->ssc->regs, IER, dma_params->mask->ssc_error);
 
        pr_debug("%s enabled SSC_SR=0x%08x\n",
                        dir ? "receive" : "transmit",
diff --git a/sound/soc/atmel/atmel_wm8904.c b/sound/soc/atmel/atmel_wm8904.c
new file mode 100644 (file)
index 0000000..7222380
--- /dev/null
@@ -0,0 +1,254 @@
+/*
+ * atmel_wm8904 - Atmel ASoC driver for boards with WM8904 codec.
+ *
+ * Copyright (C) 2012 Atmel
+ *
+ * Author: Bo Shen <voice.shen@atmel.com>
+ *
+ * GPLv2 or later
+ */
+
+#include <linux/clk.h>
+#include <linux/module.h>
+#include <linux/of.h>
+#include <linux/of_device.h>
+#include <linux/pinctrl/consumer.h>
+
+#include <sound/soc.h>
+
+#include "../codecs/wm8904.h"
+#include "atmel_ssc_dai.h"
+
+#define MCLK_RATE 32768
+
+static struct clk *mclk;
+
+static const struct snd_soc_dapm_widget atmel_asoc_wm8904_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_MIC("Mic", NULL),
+       SND_SOC_DAPM_LINE("Line In Jack", NULL),
+};
+
+static int atmel_asoc_wm8904_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_pll(codec_dai, WM8904_FLL_MCLK, WM8904_FLL_MCLK,
+               32768, params_rate(params) * 256);
+       if (ret < 0) {
+               pr_err("%s - failed to set wm8904 codec PLL.", __func__);
+               return ret;
+       }
+
+       /*
+        * As here wm8904 use FLL output as its system clock
+        * so calling set_sysclk won't care freq parameter
+        * then we pass 0
+        */
+       ret = snd_soc_dai_set_sysclk(codec_dai, WM8904_CLK_FLL,
+                       0, SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               pr_err("%s -failed to set wm8904 SYSCLK\n", __func__);
+               return ret;
+       }
+
+       return 0;
+}
+
+static struct snd_soc_ops atmel_asoc_wm8904_ops = {
+       .hw_params = atmel_asoc_wm8904_hw_params,
+};
+
+static int atmel_set_bias_level(struct snd_soc_card *card,
+               struct snd_soc_dapm_context *dapm,
+               enum snd_soc_bias_level level)
+{
+       if (dapm->bias_level == SND_SOC_BIAS_STANDBY) {
+               switch (level) {
+               case SND_SOC_BIAS_PREPARE:
+                       clk_prepare_enable(mclk);
+                       break;
+               case SND_SOC_BIAS_OFF:
+                       clk_disable_unprepare(mclk);
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return 0;
+};
+
+static struct snd_soc_dai_link atmel_asoc_wm8904_dailink = {
+       .name = "WM8904",
+       .stream_name = "WM8904 PCM",
+       .codec_dai_name = "wm8904-hifi",
+       .dai_fmt = SND_SOC_DAIFMT_I2S
+               | SND_SOC_DAIFMT_NB_NF
+               | SND_SOC_DAIFMT_CBM_CFM,
+       .ops = &atmel_asoc_wm8904_ops,
+};
+
+static struct snd_soc_card atmel_asoc_wm8904_card = {
+       .name = "atmel_asoc_wm8904",
+       .owner = THIS_MODULE,
+       .set_bias_level = atmel_set_bias_level,
+       .dai_link = &atmel_asoc_wm8904_dailink,
+       .num_links = 1,
+       .dapm_widgets = atmel_asoc_wm8904_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(atmel_asoc_wm8904_dapm_widgets),
+       .fully_routed = true,
+};
+
+static int atmel_asoc_wm8904_dt_init(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *codec_np, *cpu_np;
+       struct snd_soc_card *card = &atmel_asoc_wm8904_card;
+       struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
+       int ret;
+
+       if (!np) {
+               dev_err(&pdev->dev, "only device tree supported\n");
+               return -EINVAL;
+       }
+
+       ret = snd_soc_of_parse_card_name(card, "atmel,model");
+       if (ret) {
+               dev_err(&pdev->dev, "failed to parse card name\n");
+               return ret;
+       }
+
+       ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing");
+       if (ret) {
+               dev_err(&pdev->dev, "failed to parse audio routing\n");
+               return ret;
+       }
+
+       cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
+       if (!cpu_np) {
+               dev_err(&pdev->dev, "failed to get dai and pcm info\n");
+               ret = -EINVAL;
+               return ret;
+       }
+       dailink->cpu_of_node = cpu_np;
+       dailink->platform_of_node = cpu_np;
+       of_node_put(cpu_np);
+
+       codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
+       if (!codec_np) {
+               dev_err(&pdev->dev, "failed to get codec info\n");
+               ret = -EINVAL;
+               return ret;
+       }
+       dailink->codec_of_node = codec_np;
+       of_node_put(codec_np);
+
+       return 0;
+}
+
+static int atmel_asoc_wm8904_probe(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = &atmel_asoc_wm8904_card;
+       struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
+       struct clk *clk_src;
+       struct pinctrl *pinctrl;
+       int id, ret;
+
+       pinctrl = devm_pinctrl_get_select_default(&pdev->dev);
+       if (IS_ERR(pinctrl)) {
+               dev_err(&pdev->dev, "failed to request pinctrl\n");
+               return PTR_ERR(pinctrl);
+       }
+
+       card->dev = &pdev->dev;
+       ret = atmel_asoc_wm8904_dt_init(pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to init dt info\n");
+               return ret;
+       }
+
+       id = of_alias_get_id((struct device_node *)dailink->cpu_of_node, "ssc");
+       ret = atmel_ssc_set_audio(id);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "failed to set SSC %d for audio\n", id);
+               return ret;
+       }
+
+       mclk = clk_get(NULL, "pck0");
+       if (IS_ERR(mclk)) {
+               dev_err(&pdev->dev, "failed to get pck0\n");
+               ret = PTR_ERR(mclk);
+               goto err_set_audio;
+       }
+
+       clk_src = clk_get(NULL, "clk32k");
+       if (IS_ERR(clk_src)) {
+               dev_err(&pdev->dev, "failed to get clk32k\n");
+               ret = PTR_ERR(clk_src);
+               goto err_set_audio;
+       }
+
+       ret = clk_set_parent(mclk, clk_src);
+       clk_put(clk_src);
+       if (ret != 0) {
+               dev_err(&pdev->dev, "failed to set MCLK parent\n");
+               goto err_set_audio;
+       }
+
+       dev_info(&pdev->dev, "setting pck0 to %dHz\n", MCLK_RATE);
+       clk_set_rate(mclk, MCLK_RATE);
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev, "snd_soc_register_card failed\n");
+               goto err_set_audio;
+       }
+
+       return 0;
+
+err_set_audio:
+       atmel_ssc_put_audio(id);
+       return ret;
+}
+
+static int atmel_asoc_wm8904_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct snd_soc_dai_link *dailink = &atmel_asoc_wm8904_dailink;
+       int id;
+
+       id = of_alias_get_id((struct device_node *)dailink->cpu_of_node, "ssc");
+
+       snd_soc_unregister_card(card);
+       atmel_ssc_put_audio(id);
+
+       return 0;
+}
+
+#ifdef CONFIG_OF
+static const struct of_device_id atmel_asoc_wm8904_dt_ids[] = {
+       { .compatible = "atmel,asoc-wm8904", },
+       { }
+};
+#endif
+
+static struct platform_driver atmel_asoc_wm8904_driver = {
+       .driver = {
+               .name = "atmel-wm8904-audio",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(atmel_asoc_wm8904_dt_ids),
+       },
+       .probe = atmel_asoc_wm8904_probe,
+       .remove = atmel_asoc_wm8904_remove,
+};
+
+module_platform_driver(atmel_asoc_wm8904_driver);
+
+/* Module information */
+MODULE_AUTHOR("Bo Shen <voice.shen@atmel.com>");
+MODULE_DESCRIPTION("ALSA SoC machine driver for Atmel EK with WM8904 codec");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/atmel/sam9x5_wm8731.c b/sound/soc/atmel/sam9x5_wm8731.c
new file mode 100644 (file)
index 0000000..992ae38
--- /dev/null
@@ -0,0 +1,208 @@
+/*
+ * sam9x5_wm8731   --  SoC audio for AT91SAM9X5-based boards
+ *                     that are using WM8731 as codec.
+ *
+ *  Copyright (C) 2011 Atmel,
+ *               Nicolas Ferre <nicolas.ferre@atmel.com>
+ *
+ *  Copyright (C) 2013 Paratronic,
+ *               Richard Genoud <richard.genoud@gmail.com>
+ *
+ * Based on sam9g20_wm8731.c by:
+ * Sedji Gaouaou <sedji.gaouaou@atmel.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/of.h>
+#include <linux/export.h>
+#include <linux/module.h>
+#include <linux/mod_devicetable.h>
+#include <linux/platform_device.h>
+#include <linux/device.h>
+
+#include <sound/soc.h>
+#include <sound/soc-dai.h>
+#include <sound/soc-dapm.h>
+
+#include "../codecs/wm8731.h"
+#include "atmel_ssc_dai.h"
+
+
+#define MCLK_RATE 12288000
+
+#define DRV_NAME "sam9x5-snd-wm8731"
+
+struct sam9x5_drvdata {
+       int ssc_id;
+};
+
+/*
+ * Logic for a wm8731 as connected on a at91sam9x5ek based board.
+ */
+static int sam9x5_wm8731_init(struct snd_soc_pcm_runtime *rtd)
+{
+       struct snd_soc_dai *codec_dai = rtd->codec_dai;
+       struct device *dev = rtd->dev;
+       int ret;
+
+       dev_dbg(dev, "ASoC: %s called\n", __func__);
+
+       /* set the codec system clock for DAC and ADC */
+       ret = snd_soc_dai_set_sysclk(codec_dai, WM8731_SYSCLK_XTAL,
+                                    MCLK_RATE, SND_SOC_CLOCK_IN);
+       if (ret < 0) {
+               dev_err(dev, "ASoC: Failed to set WM8731 SYSCLK: %d\n", ret);
+               return ret;
+       }
+
+       return 0;
+}
+
+/*
+ * Audio paths on at91sam9x5ek board:
+ *
+ *  |A| ------------> |      | ---R----> Headphone Jack
+ *  |T| <----\        |  WM  | ---L--/
+ *  |9| ---> CLK <--> | 8731 | <--R----- Line In Jack
+ *  |1| <------------ |      | <--L--/
+ */
+static const struct snd_soc_dapm_widget sam9x5_dapm_widgets[] = {
+       SND_SOC_DAPM_HP("Headphone Jack", NULL),
+       SND_SOC_DAPM_LINE("Line In Jack", NULL),
+};
+
+static int sam9x5_wm8731_driver_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct device_node *codec_np, *cpu_np;
+       struct snd_soc_card *card;
+       struct snd_soc_dai_link *dai;
+       struct sam9x5_drvdata *priv;
+       int ret;
+
+       if (!np) {
+               dev_err(&pdev->dev, "No device node supplied\n");
+               return -EINVAL;
+       }
+
+       card = devm_kzalloc(&pdev->dev, sizeof(*card), GFP_KERNEL);
+       priv = devm_kzalloc(&pdev->dev, sizeof(*priv), GFP_KERNEL);
+       dai = devm_kzalloc(&pdev->dev, sizeof(*dai), GFP_KERNEL);
+       if (!dai || !card || !priv) {
+               ret = -ENOMEM;
+               goto out;
+       }
+
+       card->dev = &pdev->dev;
+       card->owner = THIS_MODULE;
+       card->dai_link = dai;
+       card->num_links = 1;
+       card->dapm_widgets = sam9x5_dapm_widgets;
+       card->num_dapm_widgets = ARRAY_SIZE(sam9x5_dapm_widgets);
+       dai->name = "WM8731";
+       dai->stream_name = "WM8731 PCM";
+       dai->codec_dai_name = "wm8731-hifi";
+       dai->init = sam9x5_wm8731_init;
+       dai->dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF
+               | SND_SOC_DAIFMT_CBM_CFM;
+
+       ret = snd_soc_of_parse_card_name(card, "atmel,model");
+       if (ret) {
+               dev_err(&pdev->dev, "atmel,model node missing\n");
+               goto out;
+       }
+
+       ret = snd_soc_of_parse_audio_routing(card, "atmel,audio-routing");
+       if (ret) {
+               dev_err(&pdev->dev, "atmel,audio-routing node missing\n");
+               goto out;
+       }
+
+       codec_np = of_parse_phandle(np, "atmel,audio-codec", 0);
+       if (!codec_np) {
+               dev_err(&pdev->dev, "atmel,audio-codec node missing\n");
+               ret = -EINVAL;
+               goto out;
+       }
+
+       dai->codec_of_node = codec_np;
+
+       cpu_np = of_parse_phandle(np, "atmel,ssc-controller", 0);
+       if (!cpu_np) {
+               dev_err(&pdev->dev, "atmel,ssc-controller node missing\n");
+               ret = -EINVAL;
+               goto out;
+       }
+       dai->cpu_of_node = cpu_np;
+       dai->platform_of_node = cpu_np;
+
+       priv->ssc_id = of_alias_get_id(cpu_np, "ssc");
+
+       ret = atmel_ssc_set_audio(priv->ssc_id);
+       if (ret != 0) {
+               dev_err(&pdev->dev,
+                       "ASoC: Failed to set SSC %d for audio: %d\n",
+                       ret, priv->ssc_id);
+               goto out;
+       }
+
+       of_node_put(codec_np);
+       of_node_put(cpu_np);
+
+       platform_set_drvdata(pdev, card);
+
+       ret = snd_soc_register_card(card);
+       if (ret) {
+               dev_err(&pdev->dev,
+                       "ASoC: Platform device allocation failed\n");
+               goto out_put_audio;
+       }
+
+       dev_dbg(&pdev->dev, "ASoC: %s ok\n", __func__);
+
+       return ret;
+
+out_put_audio:
+       atmel_ssc_put_audio(priv->ssc_id);
+out:
+       return ret;
+}
+
+static int sam9x5_wm8731_driver_remove(struct platform_device *pdev)
+{
+       struct snd_soc_card *card = platform_get_drvdata(pdev);
+       struct sam9x5_drvdata *priv = card->drvdata;
+
+       snd_soc_unregister_card(card);
+       atmel_ssc_put_audio(priv->ssc_id);
+
+       return 0;
+}
+
+static const struct of_device_id sam9x5_wm8731_of_match[] = {
+       { .compatible = "atmel,sam9x5-wm8731-audio", },
+       {},
+};
+MODULE_DEVICE_TABLE(of, sam9x5_wm8731_of_match);
+
+static struct platform_driver sam9x5_wm8731_driver = {
+       .driver = {
+               .name = DRV_NAME,
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(sam9x5_wm8731_of_match),
+       },
+       .probe = sam9x5_wm8731_driver_probe,
+       .remove = sam9x5_wm8731_driver_remove,
+};
+module_platform_driver(sam9x5_wm8731_driver);
+
+/* Module information */
+MODULE_AUTHOR("Nicolas Ferre <nicolas.ferre@atmel.com>");
+MODULE_AUTHOR("Richard Genoud <richard.genoud@gmail.com>");
+MODULE_DESCRIPTION("ALSA SoC machine driver for AT91SAM9x5 - WM8731");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:" DRV_NAME);
index a497a0cfeba153aa70630b0dfadc7e69bf2d0e7c..decba87a074c6267eb1bd93265fc8fc96787db7a 100644 (file)
@@ -73,12 +73,14 @@ static struct snd_soc_dai_link db1300_ac97_dai = {
 
 static struct snd_soc_card db1300_ac97_machine = {
        .name           = "DB1300_AC97",
+       .owner          = THIS_MODULE,
        .dai_link       = &db1300_ac97_dai,
        .num_links      = 1,
 };
 
 static struct snd_soc_card db1550_ac97_machine = {
        .name           = "DB1550_AC97",
+       .owner          = THIS_MODULE,
        .dai_link       = &db1200_ac97_dai,
        .num_links      = 1,
 };
@@ -145,6 +147,7 @@ static struct snd_soc_dai_link db1300_i2s_dai = {
 
 static struct snd_soc_card db1300_i2s_machine = {
        .name           = "DB1300_I2S",
+       .owner          = THIS_MODULE,
        .dai_link       = &db1300_i2s_dai,
        .num_links      = 1,
 };
@@ -161,6 +164,7 @@ static struct snd_soc_dai_link db1550_i2s_dai = {
 
 static struct snd_soc_card db1550_i2s_machine = {
        .name           = "DB1550_I2S",
+       .owner          = THIS_MODULE,
        .dai_link       = &db1550_i2s_dai,
        .num_links      = 1,
 };
index a822ab822bb7ba40f5f1ead77a04a7caa7069233..986dcec79fa02e1552fb631456878cb9dbe5f940 100644 (file)
@@ -379,9 +379,6 @@ static int au1xpsc_ac97_drvprobe(struct platform_device *pdev)
        mutex_init(&wd->lock);
 
        iores = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!iores)
-               return -ENODEV;
-
        wd->mmio = devm_ioremap_resource(&pdev->dev, iores);
        if (IS_ERR(wd->mmio))
                return PTR_ERR(wd->mmio);
index 0c3e22d90a8d903fbf1495936c5a97a79ab8a77e..a680fdc9bb42012f72366a4a09f7ece684a06ce1 100644 (file)
@@ -9,7 +9,6 @@
 #ifndef _BF5XX_AC97_H
 #define _BF5XX_AC97_H
 
-extern struct snd_ac97 *ac97;
 /* Frame format in memory, only support stereo currently */
 struct ac97_frame {
        u16 ac97_tag;           /* slot 0 */
index 04491f0e8d1bde2636f8772e3b7d3bb65284d44a..efa75b5086a4b00e83802d476c87aae0a6914d0e 100644 (file)
@@ -363,9 +363,6 @@ static int ep93xx_ac97_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res)
-               return -ENODEV;
-
        info->regs = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(info->regs))
                return PTR_ERR(info->regs);
index 17ad70bca9fe5828d4a8b3b5327dfc6b30bdb260..a57643d6402f08ef5613aedd72ba005b4181ac77 100644 (file)
@@ -376,9 +376,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!res)
-               return -ENODEV;
-
        info->regs = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(info->regs))
                return PTR_ERR(info->regs);
@@ -411,7 +408,6 @@ static int ep93xx_i2s_probe(struct platform_device *pdev)
        return 0;
 
 fail_put_lrclk:
-       dev_set_drvdata(&pdev->dev, NULL);
        clk_put(info->lrclk);
 fail_put_sclk:
        clk_put(info->sclk);
@@ -426,7 +422,6 @@ static int ep93xx_i2s_remove(struct platform_device *pdev)
        struct ep93xx_i2s_info *info = dev_get_drvdata(&pdev->dev);
 
        snd_soc_unregister_component(&pdev->dev);
-       dev_set_drvdata(&pdev->dev, NULL);
        clk_put(info->lrclk);
        clk_put(info->sclk);
        clk_put(info->mclk);
index badb6fbacaa66222bea9cef708c1379783746c7f..15106c045478e644719a73e76edb6ea795909ff1 100644 (file)
@@ -10,6 +10,7 @@ config SND_SOC_I2C_AND_SPI
 
 config SND_SOC_ALL_CODECS
        tristate "Build all ASoC CODEC drivers"
+       depends on COMPILE_TEST
        select SND_SOC_88PM860X if MFD_88PM860X
        select SND_SOC_L3
        select SND_SOC_AB8500_CODEC if ABX500_CORE
@@ -20,6 +21,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_AD73311
        select SND_SOC_ADAU1373 if I2C
        select SND_SOC_ADAV80X if SND_SOC_I2C_AND_SPI
+       select SND_SOC_ADAU1701 if I2C
        select SND_SOC_ADS117X
        select SND_SOC_AK4104 if SPI_MASTER
        select SND_SOC_AK4535 if I2C
@@ -54,6 +56,8 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_MC13783 if MFD_MC13XXX
        select SND_SOC_ML26124 if I2C
        select SND_SOC_HDMI_CODEC
+       select SND_SOC_PCM1681 if I2C
+       select SND_SOC_PCM1792A if SPI_MASTER
        select SND_SOC_PCM3008
        select SND_SOC_RT5631 if I2C
        select SND_SOC_RT5640 if I2C
@@ -122,6 +126,7 @@ config SND_SOC_ALL_CODECS
        select SND_SOC_WM8994 if MFD_WM8994
        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_WM9081 if I2C
        select SND_SOC_WM9090 if I2C
        select SND_SOC_WM9705 if SND_SOC_AC97_BUS
@@ -145,8 +150,10 @@ config SND_SOC_ARIZONA
        tristate
        default y if SND_SOC_WM5102=y
        default y if SND_SOC_WM5110=y
+       default y if SND_SOC_WM8997=y
        default m if SND_SOC_WM5102=m
        default m if SND_SOC_WM5110=m
+       default m if SND_SOC_WM8997=m
 
 config SND_SOC_WM_HUBS
        tristate
@@ -198,6 +205,9 @@ config SND_SOC_AK4104
 config SND_SOC_AK4535
        tristate
 
+config SND_SOC_AK4554
+       tristate
+
 config SND_SOC_AK4641
        tristate
 
@@ -292,6 +302,12 @@ config SND_SOC_MAX9850
 config SND_SOC_HDMI_CODEC
        tristate
 
+config SND_SOC_PCM1681
+       tristate
+
+config SND_SOC_PCM1792A
+       tristate
+
 config SND_SOC_PCM3008
        tristate
 
@@ -500,6 +516,9 @@ config SND_SOC_WM8995
 config SND_SOC_WM8996
        tristate
 
+config SND_SOC_WM8997
+       tristate
+
 config SND_SOC_WM9081
        tristate
 
index 70fd8066f546e9cfc39dcb024f1efa6e5ea6bf1f..bc126764a44d02cb38b50f3ae74900a9ce490da2 100644 (file)
@@ -11,6 +11,7 @@ snd-soc-adav80x-objs := adav80x.o
 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-ak4641-objs := ak4641.o
 snd-soc-ak4642-objs := ak4642.o
 snd-soc-ak4671-objs := ak4671.o
@@ -42,6 +43,8 @@ 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-pcm1681-objs := pcm1681.o
+snd-soc-pcm1792a-codec-objs := pcm1792a.o
 snd-soc-pcm3008-objs := pcm3008.o
 snd-soc-rt5631-objs := rt5631.o
 snd-soc-rt5640-objs := rt5640.o
@@ -114,6 +117,7 @@ snd-soc-wm8991-objs := wm8991.o
 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-wm9081-objs := wm9081.o
 snd-soc-wm9090-objs := wm9090.o
 snd-soc-wm9705-objs := wm9705.o
@@ -138,6 +142,7 @@ obj-$(CONFIG_SND_SOC_ADAV80X)  += snd-soc-adav80x.o
 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_AK4641)   += snd-soc-ak4641.o
 obj-$(CONFIG_SND_SOC_AK4642)   += snd-soc-ak4642.o
 obj-$(CONFIG_SND_SOC_AK4671)   += snd-soc-ak4671.o
@@ -171,6 +176,8 @@ 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_PCM1681)  += snd-soc-pcm1681.o
+obj-$(CONFIG_SND_SOC_PCM1792A) += snd-soc-pcm1792a-codec.o
 obj-$(CONFIG_SND_SOC_PCM3008)  += snd-soc-pcm3008.o
 obj-$(CONFIG_SND_SOC_RT5631)   += snd-soc-rt5631.o
 obj-$(CONFIG_SND_SOC_RT5640)   += snd-soc-rt5640.o
@@ -239,6 +246,7 @@ obj-$(CONFIG_SND_SOC_WM8991)        += snd-soc-wm8991.o
 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_WM9081)   += snd-soc-wm9081.o
 obj-$(CONFIG_SND_SOC_WM9090)   += snd-soc-wm9090.o
 obj-$(CONFIG_SND_SOC_WM9705)   += snd-soc-wm9705.o
index ec7351803c245b7ba209a582e0d82fc958effb24..8d9ba4ba4bfe0b2fece2dc5c30f955fd5d6dcc00 100644 (file)
 #include <sound/initval.h>
 #include <sound/soc.h>
 
+static const struct snd_soc_dapm_widget ac97_widgets[] = {
+       SND_SOC_DAPM_INPUT("RX"),
+       SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route ac97_routes[] = {
+       { "AC97 Capture", NULL, "RX" },
+       { "TX", NULL, "AC97 Playback" },
+};
+
 static int ac97_prepare(struct snd_pcm_substream *substream,
                        struct snd_soc_dai *dai)
 {
@@ -117,6 +127,11 @@ static struct snd_soc_codec_driver soc_codec_dev_ac97 = {
        .probe =        ac97_soc_probe,
        .suspend =      ac97_soc_suspend,
        .resume =       ac97_soc_resume,
+
+       .dapm_widgets = ac97_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(ac97_widgets),
+       .dapm_routes = ac97_routes,
+       .num_dapm_routes = ARRAY_SIZE(ac97_routes),
 };
 
 static int ac97_probe(struct platform_device *pdev)
index 89fcf7d6e7b8fdfb94bac498c935ad9bf3646fcc..7257a8885f426d7cf05b017fbda1aa3f01abe236 100644 (file)
@@ -96,6 +96,44 @@ SOC_ENUM("Capture Source", ad1980_cap_src),
 SOC_SINGLE("Mic Boost Switch", AC97_MIC, 6, 1, 0),
 };
 
+static const struct snd_soc_dapm_widget ad1980_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MIC1"),
+SND_SOC_DAPM_INPUT("MIC2"),
+SND_SOC_DAPM_INPUT("CD_L"),
+SND_SOC_DAPM_INPUT("CD_R"),
+SND_SOC_DAPM_INPUT("AUX_L"),
+SND_SOC_DAPM_INPUT("AUX_R"),
+SND_SOC_DAPM_INPUT("LINE_IN_L"),
+SND_SOC_DAPM_INPUT("LINE_IN_R"),
+
+SND_SOC_DAPM_OUTPUT("LFE_OUT"),
+SND_SOC_DAPM_OUTPUT("CENTER_OUT"),
+SND_SOC_DAPM_OUTPUT("LINE_OUT_L"),
+SND_SOC_DAPM_OUTPUT("LINE_OUT_R"),
+SND_SOC_DAPM_OUTPUT("MONO_OUT"),
+SND_SOC_DAPM_OUTPUT("HP_OUT_L"),
+SND_SOC_DAPM_OUTPUT("HP_OUT_R"),
+};
+
+static const struct snd_soc_dapm_route ad1980_dapm_routes[] = {
+       { "Capture", NULL, "MIC1" },
+       { "Capture", NULL, "MIC2" },
+       { "Capture", NULL, "CD_L" },
+       { "Capture", NULL, "CD_R" },
+       { "Capture", NULL, "AUX_L" },
+       { "Capture", NULL, "AUX_R" },
+       { "Capture", NULL, "LINE_IN_L" },
+       { "Capture", NULL, "LINE_IN_R" },
+
+       { "LFE_OUT", NULL, "Playback" },
+       { "CENTER_OUT", NULL, "Playback" },
+       { "LINE_OUT_L", NULL, "Playback" },
+       { "LINE_OUT_R", NULL, "Playback" },
+       { "MONO_OUT", NULL, "Playback" },
+       { "HP_OUT_L", NULL, "Playback" },
+       { "HP_OUT_R", NULL, "Playback" },
+};
+
 static unsigned int ac97_read(struct snd_soc_codec *codec,
        unsigned int reg)
 {
@@ -253,6 +291,11 @@ static struct snd_soc_codec_driver soc_codec_dev_ad1980 = {
        .reg_cache_step = 2,
        .write = ac97_write,
        .read = ac97_read,
+
+       .dapm_widgets = ad1980_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(ad1980_dapm_widgets),
+       .dapm_routes = ad1980_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(ad1980_dapm_routes),
 };
 
 static int ad1980_probe(struct platform_device *pdev)
index b1f2baf42b48233678ae051bea1bd2b9fb747327..5fac8adbc1367a8b01d1243e9768a25b2b015643 100644 (file)
 
 #include "ad73311.h"
 
+static const struct snd_soc_dapm_widget ad73311_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("VINP"),
+SND_SOC_DAPM_INPUT("VINN"),
+SND_SOC_DAPM_OUTPUT("VOUTN"),
+SND_SOC_DAPM_OUTPUT("VOUTP"),
+};
+
+static const struct snd_soc_dapm_route ad73311_dapm_routes[] = {
+       { "Capture", NULL, "VINP" },
+       { "Capture", NULL, "VINN" },
+
+       { "VOUTN", NULL, "Playback" },
+       { "VOUTP", NULL, "Playback" },
+};
+
 static struct snd_soc_dai_driver ad73311_dai = {
        .name = "ad73311-hifi",
        .playback = {
@@ -39,7 +54,12 @@ static struct snd_soc_dai_driver ad73311_dai = {
                .formats = SNDRV_PCM_FMTBIT_S16_LE, },
 };
 
-static struct snd_soc_codec_driver soc_codec_dev_ad73311;
+static struct snd_soc_codec_driver soc_codec_dev_ad73311 = {
+       .dapm_widgets = ad73311_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(ad73311_dapm_widgets),
+       .dapm_routes = ad73311_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(ad73311_dapm_routes),
+};
 
 static int ad73311_probe(struct platform_device *pdev)
 {
index d1124a5b34713c688d92846e25cca563ed9c20ff..ebff1128be595ca7ff63f5ca049d2f30342dee45 100644 (file)
@@ -91,7 +91,7 @@
 #define ADAU1701_OSCIPOW_OPD           0x04
 #define ADAU1701_DACSET_DACINIT                1
 
-#define ADAU1707_CLKDIV_UNSET          (-1UL)
+#define ADAU1707_CLKDIV_UNSET          (-1U)
 
 #define ADAU1701_FIRMWARE "adau1701.bin"
 
@@ -247,21 +247,21 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
            gpio_is_valid(adau1701->gpio_pll_mode[1])) {
                switch (clkdiv) {
                case 64:
-                       gpio_set_value(adau1701->gpio_pll_mode[0], 0);
-                       gpio_set_value(adau1701->gpio_pll_mode[1], 0);
+                       gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
+                       gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
                        break;
                case 256:
-                       gpio_set_value(adau1701->gpio_pll_mode[0], 0);
-                       gpio_set_value(adau1701->gpio_pll_mode[1], 1);
+                       gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 0);
+                       gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
                        break;
                case 384:
-                       gpio_set_value(adau1701->gpio_pll_mode[0], 1);
-                       gpio_set_value(adau1701->gpio_pll_mode[1], 0);
+                       gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
+                       gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 0);
                        break;
                case 0: /* fallback */
                case 512:
-                       gpio_set_value(adau1701->gpio_pll_mode[0], 1);
-                       gpio_set_value(adau1701->gpio_pll_mode[1], 1);
+                       gpio_set_value_cansleep(adau1701->gpio_pll_mode[0], 1);
+                       gpio_set_value_cansleep(adau1701->gpio_pll_mode[1], 1);
                        break;
                }
        }
@@ -269,10 +269,10 @@ static int adau1701_reset(struct snd_soc_codec *codec, unsigned int clkdiv)
        adau1701->pll_clkdiv = clkdiv;
 
        if (gpio_is_valid(adau1701->gpio_nreset)) {
-               gpio_set_value(adau1701->gpio_nreset, 0);
+               gpio_set_value_cansleep(adau1701->gpio_nreset, 0);
                /* minimum reset time is 20ns */
                udelay(1);
-               gpio_set_value(adau1701->gpio_nreset, 1);
+               gpio_set_value_cansleep(adau1701->gpio_nreset, 1);
                /* power-up time may be as long as 85ms */
                mdelay(85);
        }
@@ -734,7 +734,10 @@ static int adau1701_i2c_remove(struct i2c_client *client)
 }
 
 static const struct i2c_device_id adau1701_i2c_id[] = {
+       { "adau1401", 0 },
+       { "adau1401a", 0 },
        { "adau1701", 0 },
+       { "adau1702", 0 },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, adau1701_i2c_id);
index 3c839cc4e00ecb48a5e3948567c14dbede7706a3..15b012d0f226c48a4178b1c9f4d10468708dcd1d 100644 (file)
@@ -868,6 +868,12 @@ static int adav80x_bus_remove(struct device *dev)
 }
 
 #if defined(CONFIG_SPI_MASTER)
+static const struct spi_device_id adav80x_spi_id[] = {
+       { "adav801", 0 },
+       { }
+};
+MODULE_DEVICE_TABLE(spi, adav80x_spi_id);
+
 static int adav80x_spi_probe(struct spi_device *spi)
 {
        return adav80x_bus_probe(&spi->dev, SND_SOC_SPI);
@@ -885,15 +891,16 @@ static struct spi_driver adav80x_spi_driver = {
        },
        .probe          = adav80x_spi_probe,
        .remove         = adav80x_spi_remove,
+       .id_table       = adav80x_spi_id,
 };
 #endif
 
 #if defined(CONFIG_I2C) || defined(CONFIG_I2C_MODULE)
-static const struct i2c_device_id adav80x_id[] = {
+static const struct i2c_device_id adav80x_i2c_id[] = {
        { "adav803", 0 },
        { }
 };
-MODULE_DEVICE_TABLE(i2c, adav80x_id);
+MODULE_DEVICE_TABLE(i2c, adav80x_i2c_id);
 
 static int adav80x_i2c_probe(struct i2c_client *client,
                             const struct i2c_device_id *id)
@@ -913,7 +920,7 @@ static struct i2c_driver adav80x_i2c_driver = {
        },
        .probe = adav80x_i2c_probe,
        .remove = adav80x_i2c_remove,
-       .id_table = adav80x_id,
+       .id_table = adav80x_i2c_id,
 };
 #endif
 
index 506d474c4d2227372b6b8e4a6743ca8f67ef7da3..8f388edff5864632bf1eb9f8382bdeedf4267486 100644 (file)
 #define ADS117X_RATES (SNDRV_PCM_RATE_8000_48000)
 #define ADS117X_FORMATS (SNDRV_PCM_FMTBIT_S16_LE)
 
+static const struct snd_soc_dapm_widget ads117x_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("Input1"),
+SND_SOC_DAPM_INPUT("Input2"),
+SND_SOC_DAPM_INPUT("Input3"),
+SND_SOC_DAPM_INPUT("Input4"),
+SND_SOC_DAPM_INPUT("Input5"),
+SND_SOC_DAPM_INPUT("Input6"),
+SND_SOC_DAPM_INPUT("Input7"),
+SND_SOC_DAPM_INPUT("Input8"),
+};
+
+static const struct snd_soc_dapm_route ads117x_dapm_routes[] = {
+       { "Capture", NULL, "Input1" },
+       { "Capture", NULL, "Input2" },
+       { "Capture", NULL, "Input3" },
+       { "Capture", NULL, "Input4" },
+       { "Capture", NULL, "Input5" },
+       { "Capture", NULL, "Input6" },
+       { "Capture", NULL, "Input7" },
+       { "Capture", NULL, "Input8" },
+};
+
 static struct snd_soc_dai_driver ads117x_dai = {
 /* ADC */
        .name = "ads117x-hifi",
@@ -34,7 +56,12 @@ static struct snd_soc_dai_driver ads117x_dai = {
                .formats = ADS117X_FORMATS,},
 };
 
-static struct snd_soc_codec_driver soc_codec_dev_ads117x;
+static struct snd_soc_codec_driver soc_codec_dev_ads117x = {
+       .dapm_widgets = ads117x_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(ads117x_dapm_widgets),
+       .dapm_routes = ads117x_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(ads117x_dapm_routes),
+};
 
 static int ads117x_probe(struct platform_device *pdev)
 {
index c7cfdf957e4dac91c04ffad2f9e2d2dc1e2434aa..71059c07ae7be5abc3a9c70fd5443bce27493ced 100644 (file)
@@ -51,6 +51,17 @@ struct ak4104_private {
        struct regmap *regmap;
 };
 
+static const struct snd_soc_dapm_widget ak4104_dapm_widgets[] = {
+SND_SOC_DAPM_PGA("TXE", AK4104_REG_TX, AK4104_TX_TXE, 0, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route ak4104_dapm_routes[] = {
+       { "TXE", NULL, "Playback" },
+       { "TX", NULL, "TXE" },
+};
+
 static int ak4104_set_dai_fmt(struct snd_soc_dai *codec_dai,
                              unsigned int format)
 {
@@ -138,29 +149,11 @@ static int ak4104_hw_params(struct snd_pcm_substream *substream,
        if (ret < 0)
                return ret;
 
-       /* enable transmitter */
-       ret = regmap_update_bits(ak4104->regmap, AK4104_REG_TX,
-                                AK4104_TX_TXE, AK4104_TX_TXE);
-       if (ret < 0)
-               return ret;
-
        return 0;
 }
 
-static int ak4104_hw_free(struct snd_pcm_substream *substream,
-                         struct snd_soc_dai *dai)
-{
-       struct snd_soc_codec *codec = dai->codec;
-       struct ak4104_private *ak4104 = snd_soc_codec_get_drvdata(codec);
-
-       /* disable transmitter */
-       return regmap_update_bits(ak4104->regmap, AK4104_REG_TX,
-                                 AK4104_TX_TXE, 0);
-}
-
 static const struct snd_soc_dai_ops ak4101_dai_ops = {
        .hw_params = ak4104_hw_params,
-       .hw_free = ak4104_hw_free,
        .set_fmt = ak4104_set_dai_fmt,
 };
 
@@ -214,6 +207,11 @@ static int ak4104_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_device_ak4104 = {
        .probe =        ak4104_probe,
        .remove =       ak4104_remove,
+
+       .dapm_widgets = ak4104_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(ak4104_dapm_widgets),
+       .dapm_routes = ak4104_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(ak4104_dapm_routes),
 };
 
 static const struct regmap_config ak4104_regmap = {
diff --git a/sound/soc/codecs/ak4554.c b/sound/soc/codecs/ak4554.c
new file mode 100644 (file)
index 0000000..79e9555
--- /dev/null
@@ -0,0 +1,106 @@
+/*
+ * ak4554.c
+ *
+ * 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.
+ */
+
+#include <linux/module.h>
+#include <sound/soc.h>
+
+/*
+ * ak4554 is very simple DA/AD converter which has no setting register.
+ *
+ * CAUTION
+ *
+ * ak4554 playback format is SND_SOC_DAIFMT_RIGHT_J,
+ * and,   capture  format is SND_SOC_DAIFMT_LEFT_J
+ * on same bit clock, LR clock.
+ * But, this driver doesn't have snd_soc_dai_ops :: set_fmt
+ *
+ * CPU/Codec DAI image
+ *
+ * CPU-DAI1 (plaback only fmt = RIGHT_J) --+-- ak4554
+ *                                        |
+ * CPU-DAI2 (capture only fmt = LEFT_J) ---+
+ */
+
+static const struct snd_soc_dapm_widget ak4554_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+
+SND_SOC_DAPM_OUTPUT("AOUTL"),
+SND_SOC_DAPM_OUTPUT("AOUTR"),
+};
+
+static const struct snd_soc_dapm_route ak4554_dapm_routes[] = {
+       { "Capture", NULL, "AINL" },
+       { "Capture", NULL, "AINR" },
+
+       { "AOUTL", NULL, "Playback" },
+       { "AOUTR", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver ak4554_dai = {
+       .name = "ak4554-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "Capture",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .symmetric_rates = 1,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_ak4554 = {
+       .dapm_widgets = ak4554_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(ak4554_dapm_widgets),
+       .dapm_routes = ak4554_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(ak4554_dapm_routes),
+};
+
+static int ak4554_soc_probe(struct platform_device *pdev)
+{
+       return snd_soc_register_codec(&pdev->dev,
+                                     &soc_codec_dev_ak4554,
+                                     &ak4554_dai, 1);
+}
+
+static int ak4554_soc_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       return 0;
+}
+
+static struct of_device_id ak4554_of_match[] = {
+       { .compatible = "asahi-kasei,ak4554" },
+       {},
+};
+MODULE_DEVICE_TABLE(of, ak4554_of_match);
+
+static struct platform_driver ak4554_driver = {
+       .driver = {
+               .name = "ak4554-adc-dac",
+               .owner = THIS_MODULE,
+               .of_match_table = ak4554_of_match,
+       },
+       .probe  = ak4554_soc_probe,
+       .remove = ak4554_soc_remove,
+};
+module_platform_driver(ak4554_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("SoC AK4554 driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
index 1f303983ae02ddaf0a433ff32c188046d3da68a7..72e953b2cb41d8e4e7611b4f3141c59caf68041a 100644 (file)
@@ -22,7 +22,22 @@ struct ak5386_priv {
        int reset_gpio;
 };
 
-static struct snd_soc_codec_driver soc_codec_ak5386;
+static const struct snd_soc_dapm_widget ak5386_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+};
+
+static const struct snd_soc_dapm_route ak5386_dapm_routes[] = {
+       { "Capture", NULL, "AINL" },
+       { "Capture", NULL, "AINR" },
+};
+
+static struct snd_soc_codec_driver soc_codec_ak5386 = {
+       .dapm_widgets = ak5386_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(ak5386_dapm_widgets),
+       .dapm_routes = ak5386_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(ak5386_dapm_routes),
+};
 
 static int ak5386_set_dai_fmt(struct snd_soc_dai *codec_dai,
                              unsigned int format)
index de625813c0e65c7aff7807d6904c53d2c581249d..657808ba1418d5588e961081c5e1578cbb671400 100644 (file)
@@ -19,6 +19,7 @@
 #include <sound/tlv.h>
 
 #include <linux/mfd/arizona/core.h>
+#include <linux/mfd/arizona/gpio.h>
 #include <linux/mfd/arizona/registers.h>
 
 #include "arizona.h"
@@ -199,9 +200,16 @@ int arizona_init_spk(struct snd_soc_codec *codec)
        if (ret != 0)
                return ret;
 
-       ret = snd_soc_dapm_new_controls(&codec->dapm, &arizona_spkr, 1);
-       if (ret != 0)
-               return ret;
+       switch (arizona->type) {
+       case WM8997:
+               break;
+       default:
+               ret = snd_soc_dapm_new_controls(&codec->dapm,
+                                               &arizona_spkr, 1);
+               if (ret != 0)
+                       return ret;
+               break;
+       }
 
        ret = arizona_request_irq(arizona, ARIZONA_IRQ_SPK_SHUTDOWN_WARN,
                                  "Thermal warning", arizona_thermal_warn,
@@ -223,6 +231,41 @@ int arizona_init_spk(struct snd_soc_codec *codec)
 }
 EXPORT_SYMBOL_GPL(arizona_init_spk);
 
+int arizona_init_gpio(struct snd_soc_codec *codec)
+{
+       struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
+       struct arizona *arizona = priv->arizona;
+       int i;
+
+       switch (arizona->type) {
+       case WM5110:
+               snd_soc_dapm_disable_pin(&codec->dapm, "DRC2 Signal Activity");
+               break;
+       default:
+               break;
+       }
+
+       snd_soc_dapm_disable_pin(&codec->dapm, "DRC1 Signal Activity");
+
+       for (i = 0; i < ARRAY_SIZE(arizona->pdata.gpio_defaults); i++) {
+               switch (arizona->pdata.gpio_defaults[i] & ARIZONA_GPN_FN_MASK) {
+               case ARIZONA_GP_FN_DRC1_SIGNAL_DETECT:
+                       snd_soc_dapm_enable_pin(&codec->dapm,
+                                               "DRC1 Signal Activity");
+                       break;
+               case ARIZONA_GP_FN_DRC2_SIGNAL_DETECT:
+                       snd_soc_dapm_enable_pin(&codec->dapm,
+                                               "DRC2 Signal Activity");
+                       break;
+               default:
+                       break;
+               }
+       }
+
+       return 0;
+}
+EXPORT_SYMBOL_GPL(arizona_init_gpio);
+
 const char *arizona_mixer_texts[ARIZONA_NUM_MIXER_INPUTS] = {
        "None",
        "Tone Generator 1",
@@ -517,6 +560,26 @@ const struct soc_enum arizona_ng_hold =
                        4, arizona_ng_hold_text);
 EXPORT_SYMBOL_GPL(arizona_ng_hold);
 
+static const char * const arizona_in_dmic_osr_text[] = {
+       "1.536MHz", "3.072MHz", "6.144MHz",
+};
+
+const struct soc_enum arizona_in_dmic_osr[] = {
+       SOC_ENUM_SINGLE(ARIZONA_IN1L_CONTROL, ARIZONA_IN1_OSR_SHIFT,
+                       ARRAY_SIZE(arizona_in_dmic_osr_text),
+                       arizona_in_dmic_osr_text),
+       SOC_ENUM_SINGLE(ARIZONA_IN2L_CONTROL, ARIZONA_IN2_OSR_SHIFT,
+                       ARRAY_SIZE(arizona_in_dmic_osr_text),
+                       arizona_in_dmic_osr_text),
+       SOC_ENUM_SINGLE(ARIZONA_IN3L_CONTROL, ARIZONA_IN3_OSR_SHIFT,
+                       ARRAY_SIZE(arizona_in_dmic_osr_text),
+                       arizona_in_dmic_osr_text),
+       SOC_ENUM_SINGLE(ARIZONA_IN4L_CONTROL, ARIZONA_IN4_OSR_SHIFT,
+                       ARRAY_SIZE(arizona_in_dmic_osr_text),
+                       arizona_in_dmic_osr_text),
+};
+EXPORT_SYMBOL_GPL(arizona_in_dmic_osr);
+
 static void arizona_in_set_vu(struct snd_soc_codec *codec, int ena)
 {
        struct arizona_priv *priv = snd_soc_codec_get_drvdata(codec);
index b60b08ccc1d06e54bf09579ec1ffff3640cb2d15..9e81b6392692cbd78e7e9ad7b263fcd00c214a1d 100644 (file)
@@ -150,7 +150,8 @@ extern int arizona_mixer_values[ARIZONA_NUM_MIXER_INPUTS];
        ARIZONA_MUX(name_str " Aux 5", &name##_aux5_mux), \
        ARIZONA_MUX(name_str " Aux 6", &name##_aux6_mux)
 
-#define ARIZONA_MUX_ROUTES(name) \
+#define ARIZONA_MUX_ROUTES(widget, name) \
+       { widget, NULL, name " Input" }, \
        ARIZONA_MIXER_INPUT_ROUTES(name " Input")
 
 #define ARIZONA_MIXER_ROUTES(widget, name) \
@@ -198,6 +199,7 @@ extern const struct soc_enum arizona_lhpf3_mode;
 extern const struct soc_enum arizona_lhpf4_mode;
 
 extern const struct soc_enum arizona_ng_hold;
+extern const struct soc_enum arizona_in_dmic_osr[];
 
 extern int arizona_in_ev(struct snd_soc_dapm_widget *w,
                         struct snd_kcontrol *kcontrol,
@@ -242,6 +244,7 @@ extern int arizona_set_fll(struct arizona_fll *fll, int source,
                           unsigned int Fref, unsigned int Fout);
 
 extern int arizona_init_spk(struct snd_soc_codec *codec);
+extern int arizona_init_gpio(struct snd_soc_codec *codec);
 
 extern int arizona_init_dai(struct arizona_priv *priv, int dai);
 
index a081d9fcb1668ed8393429d60d3cecac63ea7ae5..c4cf0699e77fe4cc98182f3fa877eb02960a9235 100644 (file)
 
 #include <sound/soc.h>
 
+static const struct snd_soc_dapm_widget bt_sco_widgets[] = {
+       SND_SOC_DAPM_INPUT("RX"),
+       SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route bt_sco_routes[] = {
+       { "Capture", NULL, "RX" },
+       { "TX", NULL, "Playback" },
+};
+
 static struct snd_soc_dai_driver bt_sco_dai = {
        .name = "bt-sco-pcm",
        .playback = {
+               .stream_name = "Playback",
                .channels_min = 1,
                .channels_max = 1,
                .rates = SNDRV_PCM_RATE_8000,
                .formats = SNDRV_PCM_FMTBIT_S16_LE,
        },
        .capture = {
+                .stream_name = "Capture",
                .channels_min = 1,
                .channels_max = 1,
                .rates = SNDRV_PCM_RATE_8000,
@@ -31,7 +43,12 @@ static struct snd_soc_dai_driver bt_sco_dai = {
        },
 };
 
-static struct snd_soc_codec_driver soc_codec_dev_bt_sco;
+static struct snd_soc_codec_driver soc_codec_dev_bt_sco = {
+       .dapm_widgets = bt_sco_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(bt_sco_widgets),
+       .dapm_routes = bt_sco_routes,
+       .num_dapm_routes = ARRAY_SIZE(bt_sco_routes),
+};
 
 static int bt_sco_probe(struct platform_device *pdev)
 {
@@ -50,6 +67,9 @@ static struct platform_device_id bt_sco_driver_ids[] = {
        {
                .name           = "dfbmcs320",
        },
+       {
+               .name           = "bt-sco",
+       },
        {},
 };
 MODULE_DEVICE_TABLE(platform, bt_sco_driver_ids);
index 8e4779812b96c03d11f07ca6a4c47ca628b3295b..83c835d9fd884b18a87cdbb5420838b8fd9229f0 100644 (file)
@@ -139,6 +139,22 @@ struct cs4270_private {
        struct regulator_bulk_data supplies[ARRAY_SIZE(supply_names)];
 };
 
+static const struct snd_soc_dapm_widget cs4270_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+
+SND_SOC_DAPM_OUTPUT("AOUTL"),
+SND_SOC_DAPM_OUTPUT("AOUTR"),
+};
+
+static const struct snd_soc_dapm_route cs4270_dapm_routes[] = {
+       { "Capture", NULL, "AINA" },
+       { "Capture", NULL, "AINB" },
+
+       { "AOUTA", NULL, "Playback" },
+       { "AOUTB", NULL, "Playback" },
+};
+
 /**
  * struct cs4270_mode_ratios - clock ratio tables
  * @ratio: the ratio of MCLK to the sample rate
@@ -612,6 +628,10 @@ static const struct snd_soc_codec_driver soc_codec_device_cs4270 = {
 
        .controls =             cs4270_snd_controls,
        .num_controls =         ARRAY_SIZE(cs4270_snd_controls),
+       .dapm_widgets =         cs4270_dapm_widgets,
+       .num_dapm_widgets =     ARRAY_SIZE(cs4270_dapm_widgets),
+       .dapm_routes =          cs4270_dapm_routes,
+       .num_dapm_routes =      ARRAY_SIZE(cs4270_dapm_routes),
 };
 
 /*
index 03036b326732ddfec0fad5b390806d9000a58589..a20f1bb8f0715011dad1a710cf038a80121e30b9 100644 (file)
@@ -173,6 +173,26 @@ struct cs4271_private {
        bool                            enable_soft_reset;
 };
 
+static const struct snd_soc_dapm_widget cs4271_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINA"),
+SND_SOC_DAPM_INPUT("AINB"),
+
+SND_SOC_DAPM_OUTPUT("AOUTA+"),
+SND_SOC_DAPM_OUTPUT("AOUTA-"),
+SND_SOC_DAPM_OUTPUT("AOUTB+"),
+SND_SOC_DAPM_OUTPUT("AOUTB-"),
+};
+
+static const struct snd_soc_dapm_route cs4271_dapm_routes[] = {
+       { "Capture", NULL, "AINA" },
+       { "Capture", NULL, "AINB" },
+
+       { "AOUTA+", NULL, "Playback" },
+       { "AOUTA-", NULL, "Playback" },
+       { "AOUTB+", NULL, "Playback" },
+       { "AOUTB-", NULL, "Playback" },
+};
+
 /*
  * @freq is the desired MCLK rate
  * MCLK rate should (c) be the sample rate, multiplied by one of the
@@ -576,8 +596,7 @@ static int cs4271_probe(struct snd_soc_codec *codec)
                                   CS4271_MODE2_MUTECAEQUB,
                                   CS4271_MODE2_MUTECAEQUB);
 
-       return snd_soc_add_codec_controls(codec, cs4271_snd_controls,
-               ARRAY_SIZE(cs4271_snd_controls));
+       return 0;
 }
 
 static int cs4271_remove(struct snd_soc_codec *codec)
@@ -596,6 +615,13 @@ static struct snd_soc_codec_driver soc_codec_dev_cs4271 = {
        .remove                 = cs4271_remove,
        .suspend                = cs4271_soc_suspend,
        .resume                 = cs4271_soc_resume,
+
+       .controls               = cs4271_snd_controls,
+       .num_controls           = ARRAY_SIZE(cs4271_snd_controls),
+       .dapm_widgets           = cs4271_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(cs4271_dapm_widgets),
+       .dapm_routes            = cs4271_dapm_routes,
+       .num_dapm_routes        = ARRAY_SIZE(cs4271_dapm_routes),
 };
 
 #if defined(CONFIG_SPI_MASTER)
index 66967ba6f7576c8f1fc816bb7e894f3e7799e3a2..b2090b2a5e2d9fb9e4aa24459f73075788bb8dc8 100644 (file)
@@ -50,20 +50,11 @@ static const struct snd_soc_dapm_route intercon[] = {
        {"DMIC AIF", NULL, "DMic"},
 };
 
-static int dmic_probe(struct snd_soc_codec *codec)
-{
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-
-       snd_soc_dapm_new_controls(dapm, dmic_dapm_widgets,
-                                 ARRAY_SIZE(dmic_dapm_widgets));
-        snd_soc_dapm_add_routes(dapm, intercon, ARRAY_SIZE(intercon));
-       snd_soc_dapm_new_widgets(dapm);
-
-       return 0;
-}
-
 static struct snd_soc_codec_driver soc_dmic = {
-       .probe  = dmic_probe,
+       .dapm_widgets = dmic_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(dmic_dapm_widgets),
+       .dapm_routes = intercon,
+       .num_dapm_routes = ARRAY_SIZE(intercon),
 };
 
 static int dmic_dev_probe(struct platform_device *pdev)
index 2bcae2b40c92cd48a35aaf59ad333bb689851ff2..68342b121c966aa7c90ace1384bbfbefe4737ab7 100644 (file)
 
 #define DRV_NAME "hdmi-audio-codec"
 
-static struct snd_soc_codec_driver hdmi_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 |
@@ -37,6 +46,25 @@ static struct snd_soc_dai_driver hdmi_codec_dai = {
                .formats = SNDRV_PCM_FMTBIT_S16_LE |
                        SNDRV_PCM_FMTBIT_S24_LE,
        },
+       .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,
+       },
+
+};
+
+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),
 };
 
 static int hdmi_codec_probe(struct platform_device *pdev)
index 9f9f59573f721344995233da13ddf875f43436fc..0e5743ea79dfdf6c4a4191694955883fbede59ca 100644 (file)
@@ -16,6 +16,7 @@
 #include <linux/init.h>
 #include <linux/module.h>
 #include <linux/i2c.h>
+#include <linux/regmap.h>
 #include <linux/slab.h>
 
 #include <sound/core.h>
 #include <sound/tlv.h>
 
 struct lm4857 {
-       struct i2c_client *i2c;
+       struct regmap *regmap;
        uint8_t mode;
 };
 
-static const uint8_t lm4857_default_regs[] = {
-       0x00, 0x00, 0x00, 0x00,
+static const struct reg_default lm4857_default_regs[] = {
+       { 0x0, 0x00 },
+       { 0x1, 0x00 },
+       { 0x2, 0x00 },
+       { 0x3, 0x00 },
 };
 
 /* The register offsets in the cache array */
@@ -42,39 +46,6 @@ static const uint8_t lm4857_default_regs[] = {
 #define LM4857_WAKEUP 5
 #define LM4857_EPGAIN 4
 
-static int lm4857_write(struct snd_soc_codec *codec, unsigned int reg,
-               unsigned int value)
-{
-       uint8_t data;
-       int ret;
-
-       ret = snd_soc_cache_write(codec, reg, value);
-       if (ret < 0)
-               return ret;
-
-       data = (reg << 6) | value;
-       ret = i2c_master_send(codec->control_data, &data, 1);
-       if (ret != 1) {
-               dev_err(codec->dev, "Failed to write register: %d\n", ret);
-               return ret;
-       }
-
-       return 0;
-}
-
-static unsigned int lm4857_read(struct snd_soc_codec *codec,
-               unsigned int reg)
-{
-       unsigned int val;
-       int ret;
-
-       ret = snd_soc_cache_read(codec, reg, &val);
-       if (ret)
-               return -1;
-
-       return val;
-}
-
 static int lm4857_get_mode(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
@@ -96,7 +67,7 @@ static int lm4857_set_mode(struct snd_kcontrol *kcontrol,
        lm4857->mode = value;
 
        if (codec->dapm.bias_level == SND_SOC_BIAS_ON)
-               snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, value + 6);
+               regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, value + 6);
 
        return 1;
 }
@@ -108,10 +79,11 @@ static int lm4857_set_bias_level(struct snd_soc_codec *codec,
 
        switch (level) {
        case SND_SOC_BIAS_ON:
-               snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, lm4857->mode + 6);
+               regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F,
+                       lm4857->mode + 6);
                break;
        case SND_SOC_BIAS_STANDBY:
-               snd_soc_update_bits(codec, LM4857_CTRL, 0x0F, 0);
+               regmap_update_bits(lm4857->regmap, LM4857_CTRL, 0x0F, 0);
                break;
        default:
                break;
@@ -171,49 +143,32 @@ static const struct snd_soc_dapm_route lm4857_routes[] = {
        {"EP", NULL, "IN"},
 };
 
-static int lm4857_probe(struct snd_soc_codec *codec)
-{
-       struct lm4857 *lm4857 = snd_soc_codec_get_drvdata(codec);
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-       int ret;
-
-       codec->control_data = lm4857->i2c;
-
-       ret = snd_soc_add_codec_controls(codec, lm4857_controls,
-                       ARRAY_SIZE(lm4857_controls));
-       if (ret)
-               return ret;
-
-       ret = snd_soc_dapm_new_controls(dapm, lm4857_dapm_widgets,
-                       ARRAY_SIZE(lm4857_dapm_widgets));
-       if (ret)
-               return ret;
+static struct snd_soc_codec_driver soc_codec_dev_lm4857 = {
+       .set_bias_level = lm4857_set_bias_level,
 
-       ret = snd_soc_dapm_add_routes(dapm, lm4857_routes,
-                       ARRAY_SIZE(lm4857_routes));
-       if (ret)
-               return ret;
+       .controls = lm4857_controls,
+       .num_controls = ARRAY_SIZE(lm4857_controls),
+       .dapm_widgets = lm4857_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(lm4857_dapm_widgets),
+       .dapm_routes = lm4857_routes,
+       .num_dapm_routes = ARRAY_SIZE(lm4857_routes),
+};
 
-       snd_soc_dapm_new_widgets(dapm);
+static const struct regmap_config lm4857_regmap_config = {
+       .val_bits = 6,
+       .reg_bits = 2,
 
-       return 0;
-}
+       .max_register = LM4857_CTRL,
 
-static struct snd_soc_codec_driver soc_codec_dev_lm4857 = {
-       .write = lm4857_write,
-       .read = lm4857_read,
-       .probe = lm4857_probe,
-       .reg_cache_size = ARRAY_SIZE(lm4857_default_regs),
-       .reg_word_size = sizeof(uint8_t),
-       .reg_cache_default = lm4857_default_regs,
-       .set_bias_level = lm4857_set_bias_level,
+       .cache_type = REGCACHE_FLAT,
+       .reg_defaults = lm4857_default_regs,
+       .num_reg_defaults = ARRAY_SIZE(lm4857_default_regs),
 };
 
 static int lm4857_i2c_probe(struct i2c_client *i2c,
                            const struct i2c_device_id *id)
 {
        struct lm4857 *lm4857;
-       int ret;
 
        lm4857 = devm_kzalloc(&i2c->dev, sizeof(*lm4857), GFP_KERNEL);
        if (!lm4857)
@@ -221,11 +176,11 @@ static int lm4857_i2c_probe(struct i2c_client *i2c,
 
        i2c_set_clientdata(i2c, lm4857);
 
-       lm4857->i2c = i2c;
-
-       ret = snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0);
+       lm4857->regmap = devm_regmap_init_i2c(i2c, &lm4857_regmap_config);
+       if (IS_ERR(lm4857->regmap))
+               return PTR_ERR(lm4857->regmap);
 
-       return ret;
+       return snd_soc_register_codec(&i2c->dev, &soc_codec_dev_lm4857, NULL, 0);
 }
 
 static int lm4857_i2c_remove(struct i2c_client *i2c)
index a6ac2313047def253ddc0ff4ebddde4947803c1b..31f91560e9f6df0fb115ea862b712e9863cad9dd 100644 (file)
@@ -118,6 +118,18 @@ static const struct snd_kcontrol_new max9768_mute[] = {
        SOC_SINGLE_BOOL_EXT("Playback Switch", 0, max9768_get_gpio, max9768_set_gpio),
 };
 
+static const struct snd_soc_dapm_widget max9768_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("IN"),
+
+SND_SOC_DAPM_OUTPUT("OUT+"),
+SND_SOC_DAPM_OUTPUT("OUT-"),
+};
+
+static const struct snd_soc_dapm_route max9768_dapm_routes[] = {
+       { "OUT+", NULL, "IN" },
+       { "OUT-", NULL, "IN" },
+};
+
 static int max9768_probe(struct snd_soc_codec *codec)
 {
        struct max9768 *max9768 = snd_soc_codec_get_drvdata(codec);
@@ -148,6 +160,10 @@ static struct snd_soc_codec_driver max9768_codec_driver = {
        .probe = max9768_probe,
        .controls = max9768_volume,
        .num_controls = ARRAY_SIZE(max9768_volume),
+       .dapm_widgets = max9768_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(max9768_dapm_widgets),
+       .dapm_routes = max9768_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(max9768_dapm_routes),
 };
 
 static const struct regmap_config max9768_i2c_regmap_config = {
index ad5313f98f286b8f95223b56ef88ae4718f7a73d..0569a4c3ae00e7ce8b8fb27952387d87913f4098 100644 (file)
@@ -2084,8 +2084,9 @@ static irqreturn_t max98090_interrupt(int irq, void *data)
 
                pm_wakeup_event(codec->dev, 100);
 
-               schedule_delayed_work(&max98090->jack_work,
-                       msecs_to_jiffies(100));
+               queue_delayed_work(system_power_efficient_wq,
+                                  &max98090->jack_work,
+                                  msecs_to_jiffies(100));
        }
 
        if (active & M98090_DRCACT_MASK)
@@ -2132,8 +2133,9 @@ int max98090_mic_detect(struct snd_soc_codec *codec,
        snd_soc_jack_report(max98090->jack, 0,
                            SND_JACK_HEADSET | SND_JACK_BTN_0);
 
-       schedule_delayed_work(&max98090->jack_work,
-               msecs_to_jiffies(100));
+       queue_delayed_work(system_power_efficient_wq,
+                          &max98090->jack_work,
+                          msecs_to_jiffies(100));
 
        return 0;
 }
index 6b6c74cd83e2bcc2d9e5292cca7c2b70618eaa18..29549cdbf4c1c6138e825e3372644aa9e3008f90 100644 (file)
 #include <linux/module.h>
 #include <linux/init.h>
 #include <linux/i2c.h>
+#include <linux/regmap.h>
 #include <sound/soc.h>
 #include <sound/tlv.h>
 
 #include "max9877.h"
 
-static struct i2c_client *i2c;
+static struct regmap *regmap;
 
-static u8 max9877_regs[5] = { 0x40, 0x00, 0x00, 0x00, 0x49 };
-
-static void max9877_write_regs(void)
-{
-       unsigned int i;
-       u8 data[6];
-
-       data[0] = MAX9877_INPUT_MODE;
-       for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
-               data[i + 1] = max9877_regs[i];
-
-       if (i2c_master_send(i2c, data, 6) != 6)
-               dev_err(&i2c->dev, "i2c write failed\n");
-}
-
-static int max9877_get_reg(struct snd_kcontrol *kcontrol,
-               struct snd_ctl_elem_value *ucontrol)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int shift = mc->shift;
-       unsigned int mask = mc->max;
-       unsigned int invert = mc->invert;
-
-       ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
-
-       if (invert)
-               ucontrol->value.integer.value[0] =
-                       mask - ucontrol->value.integer.value[0];
-
-       return 0;
-}
-
-static int max9877_set_reg(struct snd_kcontrol *kcontrol,
-               struct snd_ctl_elem_value *ucontrol)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int shift = mc->shift;
-       unsigned int mask = mc->max;
-       unsigned int invert = mc->invert;
-       unsigned int val = (ucontrol->value.integer.value[0] & mask);
-
-       if (invert)
-               val = mask - val;
-
-       if (((max9877_regs[reg] >> shift) & mask) == val)
-               return 0;
-
-       max9877_regs[reg] &= ~(mask << shift);
-       max9877_regs[reg] |= val << shift;
-       max9877_write_regs();
-
-       return 1;
-}
-
-static int max9877_get_2reg(struct snd_kcontrol *kcontrol,
-               struct snd_ctl_elem_value *ucontrol)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       unsigned int mask = mc->max;
-
-       ucontrol->value.integer.value[0] = (max9877_regs[reg] >> shift) & mask;
-       ucontrol->value.integer.value[1] = (max9877_regs[reg2] >> shift) & mask;
-
-       return 0;
-}
-
-static int max9877_set_2reg(struct snd_kcontrol *kcontrol,
-               struct snd_ctl_elem_value *ucontrol)
-{
-       struct soc_mixer_control *mc =
-               (struct soc_mixer_control *)kcontrol->private_value;
-       unsigned int reg = mc->reg;
-       unsigned int reg2 = mc->rreg;
-       unsigned int shift = mc->shift;
-       unsigned int mask = mc->max;
-       unsigned int val = (ucontrol->value.integer.value[0] & mask);
-       unsigned int val2 = (ucontrol->value.integer.value[1] & mask);
-       unsigned int change = 0;
-
-       if (((max9877_regs[reg] >> shift) & mask) != val)
-               change = 1;
-
-       if (((max9877_regs[reg2] >> shift) & mask) != val2)
-               change = 1;
-
-       if (change) {
-               max9877_regs[reg] &= ~(mask << shift);
-               max9877_regs[reg] |= val << shift;
-               max9877_regs[reg2] &= ~(mask << shift);
-               max9877_regs[reg2] |= val2 << shift;
-               max9877_write_regs();
-       }
-
-       return change;
-}
-
-static int max9877_get_out_mode(struct snd_kcontrol *kcontrol,
-               struct snd_ctl_elem_value *ucontrol)
-{
-       u8 value = max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK;
-
-       if (value)
-               value -= 1;
-
-       ucontrol->value.integer.value[0] = value;
-       return 0;
-}
-
-static int max9877_set_out_mode(struct snd_kcontrol *kcontrol,
-               struct snd_ctl_elem_value *ucontrol)
-{
-       u8 value = ucontrol->value.integer.value[0];
-
-       value += 1;
-
-       if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OUTMODE_MASK) == value)
-               return 0;
-
-       max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OUTMODE_MASK;
-       max9877_regs[MAX9877_OUTPUT_MODE] |= value;
-       max9877_write_regs();
-       return 1;
-}
-
-static int max9877_get_osc_mode(struct snd_kcontrol *kcontrol,
-               struct snd_ctl_elem_value *ucontrol)
-{
-       u8 value = (max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK);
-
-       value = value >> MAX9877_OSC_OFFSET;
-
-       ucontrol->value.integer.value[0] = value;
-       return 0;
-}
-
-static int max9877_set_osc_mode(struct snd_kcontrol *kcontrol,
-               struct snd_ctl_elem_value *ucontrol)
-{
-       u8 value = ucontrol->value.integer.value[0];
-
-       value = value << MAX9877_OSC_OFFSET;
-       if ((max9877_regs[MAX9877_OUTPUT_MODE] & MAX9877_OSC_MASK) == value)
-               return 0;
-
-       max9877_regs[MAX9877_OUTPUT_MODE] &= ~MAX9877_OSC_MASK;
-       max9877_regs[MAX9877_OUTPUT_MODE] |= value;
-       max9877_write_regs();
-       return 1;
-}
+static struct reg_default max9877_regs[] = {
+       { 0, 0x40 },
+       { 1, 0x00 },
+       { 2, 0x00 },
+       { 3, 0x00 },
+       { 4, 0x49 },
+};
 
 static const unsigned int max9877_pgain_tlv[] = {
        TLV_DB_RANGE_HEAD(2),
@@ -212,65 +63,104 @@ static const char *max9877_osc_mode[] = {
 };
 
 static const struct soc_enum max9877_enum[] = {
-       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_out_mode), max9877_out_mode),
-       SOC_ENUM_SINGLE_EXT(ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode),
+       SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, 0, ARRAY_SIZE(max9877_out_mode),
+                       max9877_out_mode),
+       SOC_ENUM_SINGLE(MAX9877_OUTPUT_MODE, MAX9877_OSC_OFFSET,
+                       ARRAY_SIZE(max9877_osc_mode), max9877_osc_mode),
 };
 
 static const struct snd_kcontrol_new max9877_controls[] = {
-       SOC_SINGLE_EXT_TLV("MAX9877 PGAINA Playback Volume",
-                       MAX9877_INPUT_MODE, 0, 2, 0,
-                       max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
-       SOC_SINGLE_EXT_TLV("MAX9877 PGAINB Playback Volume",
-                       MAX9877_INPUT_MODE, 2, 2, 0,
-                       max9877_get_reg, max9877_set_reg, max9877_pgain_tlv),
-       SOC_SINGLE_EXT_TLV("MAX9877 Amp Speaker Playback Volume",
-                       MAX9877_SPK_VOLUME, 0, 31, 0,
-                       max9877_get_reg, max9877_set_reg, max9877_output_tlv),
-       SOC_DOUBLE_R_EXT_TLV("MAX9877 Amp HP Playback Volume",
-                       MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0,
-                       max9877_get_2reg, max9877_set_2reg, max9877_output_tlv),
-       SOC_SINGLE_EXT("MAX9877 INB Stereo Switch",
-                       MAX9877_INPUT_MODE, 4, 1, 1,
-                       max9877_get_reg, max9877_set_reg),
-       SOC_SINGLE_EXT("MAX9877 INA Stereo Switch",
-                       MAX9877_INPUT_MODE, 5, 1, 1,
-                       max9877_get_reg, max9877_set_reg),
-       SOC_SINGLE_EXT("MAX9877 Zero-crossing detection Switch",
-                       MAX9877_INPUT_MODE, 6, 1, 0,
-                       max9877_get_reg, max9877_set_reg),
-       SOC_SINGLE_EXT("MAX9877 Bypass Mode Switch",
-                       MAX9877_OUTPUT_MODE, 6, 1, 0,
-                       max9877_get_reg, max9877_set_reg),
-       SOC_SINGLE_EXT("MAX9877 Shutdown Mode Switch",
-                       MAX9877_OUTPUT_MODE, 7, 1, 1,
-                       max9877_get_reg, max9877_set_reg),
-       SOC_ENUM_EXT("MAX9877 Output Mode", max9877_enum[0],
-                       max9877_get_out_mode, max9877_set_out_mode),
-       SOC_ENUM_EXT("MAX9877 Oscillator Mode", max9877_enum[1],
-                       max9877_get_osc_mode, max9877_set_osc_mode),
+       SOC_SINGLE_TLV("MAX9877 PGAINA Playback Volume",
+                      MAX9877_INPUT_MODE, 0, 2, 0, max9877_pgain_tlv),
+       SOC_SINGLE_TLV("MAX9877 PGAINB Playback Volume",
+                      MAX9877_INPUT_MODE, 2, 2, 0, max9877_pgain_tlv),
+       SOC_SINGLE_TLV("MAX9877 Amp Speaker Playback Volume",
+                      MAX9877_SPK_VOLUME, 0, 31, 0, max9877_output_tlv),
+       SOC_DOUBLE_R_TLV("MAX9877 Amp HP Playback Volume",
+                        MAX9877_HPL_VOLUME, MAX9877_HPR_VOLUME, 0, 31, 0,
+                        max9877_output_tlv),
+       SOC_SINGLE("MAX9877 INB Stereo Switch",
+                  MAX9877_INPUT_MODE, 4, 1, 1),
+       SOC_SINGLE("MAX9877 INA Stereo Switch",
+                  MAX9877_INPUT_MODE, 5, 1, 1),
+       SOC_SINGLE("MAX9877 Zero-crossing detection Switch",
+                  MAX9877_INPUT_MODE, 6, 1, 0),
+       SOC_SINGLE("MAX9877 Bypass Mode Switch",
+                  MAX9877_OUTPUT_MODE, 6, 1, 0),
+       SOC_ENUM("MAX9877 Output Mode", max9877_enum[0]),
+       SOC_ENUM("MAX9877 Oscillator Mode", max9877_enum[1]),
 };
 
-/* This function is called from ASoC machine driver */
-int max9877_add_controls(struct snd_soc_codec *codec)
-{
-       return snd_soc_add_codec_controls(codec, max9877_controls,
-                       ARRAY_SIZE(max9877_controls));
-}
-EXPORT_SYMBOL_GPL(max9877_add_controls);
+static const struct snd_soc_dapm_widget max9877_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("INA1"),
+SND_SOC_DAPM_INPUT("INA2"),
+SND_SOC_DAPM_INPUT("INB1"),
+SND_SOC_DAPM_INPUT("INB2"),
+SND_SOC_DAPM_INPUT("RXIN+"),
+SND_SOC_DAPM_INPUT("RXIN-"),
+
+SND_SOC_DAPM_PGA("SHDN", MAX9877_OUTPUT_MODE, 7, 1, NULL, 0),
+
+SND_SOC_DAPM_OUTPUT("OUT+"),
+SND_SOC_DAPM_OUTPUT("OUT-"),
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+static const struct snd_soc_dapm_route max9877_dapm_routes[] = {
+       { "SHDN", NULL, "INA1" },
+       { "SHDN", NULL, "INA2" },
+       { "SHDN", NULL, "INB1" },
+       { "SHDN", NULL, "INB2" },
+
+       { "OUT+", NULL, "RXIN+" },
+       { "OUT+", NULL, "SHDN" },
+
+       { "OUT-", NULL, "SHDN" },
+       { "OUT-", NULL, "RXIN-" },
+
+       { "HPL", NULL, "SHDN" },
+       { "HPR", NULL, "SHDN" },
+};
+
+static const struct snd_soc_codec_driver max9877_codec = {
+       .controls = max9877_controls,
+       .num_controls = ARRAY_SIZE(max9877_controls),
+
+       .dapm_widgets = max9877_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(max9877_dapm_widgets),
+       .dapm_routes = max9877_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(max9877_dapm_routes),
+};
+
+static const struct regmap_config max9877_regmap = {
+       .reg_bits = 8,
+       .val_bits = 8,
+
+       .reg_defaults = max9877_regs,
+       .num_reg_defaults = ARRAY_SIZE(max9877_regs),
+       .cache_type = REGCACHE_RBTREE,
+};
 
 static int max9877_i2c_probe(struct i2c_client *client,
                             const struct i2c_device_id *id)
 {
-       i2c = client;
+       int i;
 
-       max9877_write_regs();
+       regmap = devm_regmap_init_i2c(client, &max9877_regmap);
+       if (IS_ERR(regmap))
+               return PTR_ERR(regmap);
 
-       return 0;
+       /* Ensure the device is in reset state */
+       for (i = 0; i < ARRAY_SIZE(max9877_regs); i++)
+               regmap_write(regmap, max9877_regs[i].reg, max9877_regs[i].def);
+
+       return snd_soc_register_codec(&client->dev, &max9877_codec, NULL, 0);
 }
 
 static int max9877_i2c_remove(struct i2c_client *client)
 {
-       i2c = NULL;
+       snd_soc_unregister_codec(&client->dev);
 
        return 0;
 }
index 5402dfbbb7162f668d8841a83ad3c04db5d2516f..4d3c8fd8c5db5aeec4632e958d3737a0d59adc0f 100644 (file)
@@ -94,7 +94,6 @@
 #define AUDIO_DAC_CFS_DLY_B            (1 << 10)
 
 struct mc13783_priv {
-       struct snd_soc_codec codec;
        struct mc13xxx *mc13xxx;
 
        enum mc13783_ssi_port adc_ssi_port;
diff --git a/sound/soc/codecs/pcm1681.c b/sound/soc/codecs/pcm1681.c
new file mode 100644 (file)
index 0000000..651ce09
--- /dev/null
@@ -0,0 +1,339 @@
+/*
+ * PCM1681 ASoC codec driver
+ *
+ * Copyright (c) StreamUnlimited GmbH 2013
+ *     Marek Belisko <marek.belisko@streamunlimited.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/delay.h>
+#include <linux/gpio.h>
+#include <linux/i2c.h>
+#include <linux/regmap.h>
+#include <linux/of_device.h>
+#include <linux/of_gpio.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+
+#define PCM1681_PCM_FORMATS (SNDRV_PCM_FMTBIT_S16_LE  |                \
+                            SNDRV_PCM_FMTBIT_S24_LE)
+
+#define PCM1681_PCM_RATES   (SNDRV_PCM_RATE_8000 | SNDRV_PCM_RATE_16000 | \
+                            SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100  | \
+                            SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_88200  | \
+                            SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+
+#define PCM1681_SOFT_MUTE_ALL          0xff
+#define PCM1681_DEEMPH_RATE_MASK       0x18
+#define PCM1681_DEEMPH_MASK            0x01
+
+#define PCM1681_ATT_CONTROL(X) (X <= 6 ? X : X + 9) /* Attenuation level */
+#define PCM1681_SOFT_MUTE      0x07    /* Soft mute control register */
+#define PCM1681_DAC_CONTROL    0x08    /* DAC operation control */
+#define PCM1681_FMT_CONTROL    0x09    /* Audio interface data format */
+#define PCM1681_DEEMPH_CONTROL 0x0a    /* De-emphasis control */
+#define PCM1681_ZERO_DETECT_STATUS     0x0e    /* Zero detect status reg */
+
+static const struct reg_default pcm1681_reg_defaults[] = {
+       { 0x01, 0xff },
+       { 0x02, 0xff },
+       { 0x03, 0xff },
+       { 0x04, 0xff },
+       { 0x05, 0xff },
+       { 0x06, 0xff },
+       { 0x07, 0x00 },
+       { 0x08, 0x00 },
+       { 0x09, 0x06 },
+       { 0x0A, 0x00 },
+       { 0x0B, 0xff },
+       { 0x0C, 0x0f },
+       { 0x0D, 0x00 },
+       { 0x10, 0xff },
+       { 0x11, 0xff },
+       { 0x12, 0x00 },
+       { 0x13, 0x00 },
+};
+
+static bool pcm1681_accessible_reg(struct device *dev, unsigned int reg)
+{
+       return !((reg == 0x00) || (reg == 0x0f));
+}
+
+static bool pcm1681_writeable_reg(struct device *dev, unsigned register reg)
+{
+       return pcm1681_accessible_reg(dev, reg) &&
+               (reg != PCM1681_ZERO_DETECT_STATUS);
+}
+
+struct pcm1681_private {
+       struct regmap *regmap;
+       unsigned int format;
+       /* Current deemphasis status */
+       unsigned int deemph;
+       /* Current rate for deemphasis control */
+       unsigned int rate;
+};
+
+static const int pcm1681_deemph[] = { 44100, 48000, 32000 };
+
+static int pcm1681_set_deemph(struct snd_soc_codec *codec)
+{
+       struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+       int i = 0, val = -1, enable = 0;
+
+       if (priv->deemph)
+               for (i = 0; i < ARRAY_SIZE(pcm1681_deemph); i++)
+                       if (pcm1681_deemph[i] == priv->rate)
+                               val = i;
+
+       if (val != -1) {
+               regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
+                                       PCM1681_DEEMPH_RATE_MASK, val);
+               enable = 1;
+       } else
+               enable = 0;
+
+       /* enable/disable deemphasis functionality */
+       return regmap_update_bits(priv->regmap, PCM1681_DEEMPH_CONTROL,
+                                       PCM1681_DEEMPH_MASK, enable);
+}
+
+static int pcm1681_get_deemph(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+
+       ucontrol->value.enumerated.item[0] = priv->deemph;
+
+       return 0;
+}
+
+static int pcm1681_put_deemph(struct snd_kcontrol *kcontrol,
+                             struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_codec *codec = snd_kcontrol_chip(kcontrol);
+       struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+
+       priv->deemph = ucontrol->value.enumerated.item[0];
+
+       return pcm1681_set_deemph(codec);
+}
+
+static int pcm1681_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                             unsigned int format)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+
+       /* The PCM1681 can only be slave to all clocks */
+       if ((format & SND_SOC_DAIFMT_MASTER_MASK) != SND_SOC_DAIFMT_CBS_CFS) {
+               dev_err(codec->dev, "Invalid clocking mode\n");
+               return -EINVAL;
+       }
+
+       priv->format = format;
+
+       return 0;
+}
+
+static int pcm1681_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+       int val;
+
+       if (mute)
+               val = PCM1681_SOFT_MUTE_ALL;
+       else
+               val = 0;
+
+       return regmap_write(priv->regmap, PCM1681_SOFT_MUTE, val);
+}
+
+static int pcm1681_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 pcm1681_private *priv = snd_soc_codec_get_drvdata(codec);
+       int val = 0, ret;
+       int pcm_format = params_format(params);
+
+       priv->rate = params_rate(params);
+
+       switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_RIGHT_J:
+               if (pcm_format == SNDRV_PCM_FORMAT_S24_LE)
+                       val = 0x00;
+               else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE)
+                       val = 0x03;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               val = 0x04;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               val = 0x05;
+               break;
+       default:
+               dev_err(codec->dev, "Invalid DAI format\n");
+               return -EINVAL;
+       }
+
+       ret = regmap_update_bits(priv->regmap, PCM1681_FMT_CONTROL, 0x0f, val);
+       if (ret < 0)
+               return ret;
+
+       return pcm1681_set_deemph(codec);
+}
+
+static const struct snd_soc_dai_ops pcm1681_dai_ops = {
+       .set_fmt        = pcm1681_set_dai_fmt,
+       .hw_params      = pcm1681_hw_params,
+       .digital_mute   = pcm1681_digital_mute,
+};
+
+static const struct snd_soc_dapm_widget pcm1681_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("VOUT1"),
+SND_SOC_DAPM_OUTPUT("VOUT2"),
+SND_SOC_DAPM_OUTPUT("VOUT3"),
+SND_SOC_DAPM_OUTPUT("VOUT4"),
+SND_SOC_DAPM_OUTPUT("VOUT5"),
+SND_SOC_DAPM_OUTPUT("VOUT6"),
+SND_SOC_DAPM_OUTPUT("VOUT7"),
+SND_SOC_DAPM_OUTPUT("VOUT8"),
+};
+
+static const struct snd_soc_dapm_route pcm1681_dapm_routes[] = {
+       { "VOUT1", NULL, "Playback" },
+       { "VOUT2", NULL, "Playback" },
+       { "VOUT3", NULL, "Playback" },
+       { "VOUT4", NULL, "Playback" },
+       { "VOUT5", NULL, "Playback" },
+       { "VOUT6", NULL, "Playback" },
+       { "VOUT7", NULL, "Playback" },
+       { "VOUT8", NULL, "Playback" },
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm1681_dac_tlv, -6350, 50, 1);
+
+static const struct snd_kcontrol_new pcm1681_controls[] = {
+       SOC_DOUBLE_R_TLV("Channel 1/2 Playback Volume",
+                       PCM1681_ATT_CONTROL(1), PCM1681_ATT_CONTROL(2), 0,
+                       0x7f, 0, pcm1681_dac_tlv),
+       SOC_DOUBLE_R_TLV("Channel 3/4 Playback Volume",
+                       PCM1681_ATT_CONTROL(3), PCM1681_ATT_CONTROL(4), 0,
+                       0x7f, 0, pcm1681_dac_tlv),
+       SOC_DOUBLE_R_TLV("Channel 5/6 Playback Volume",
+                       PCM1681_ATT_CONTROL(5), PCM1681_ATT_CONTROL(6), 0,
+                       0x7f, 0, pcm1681_dac_tlv),
+       SOC_DOUBLE_R_TLV("Channel 7/8 Playback Volume",
+                       PCM1681_ATT_CONTROL(7), PCM1681_ATT_CONTROL(8), 0,
+                       0x7f, 0, pcm1681_dac_tlv),
+       SOC_SINGLE_BOOL_EXT("De-emphasis Switch", 0,
+                           pcm1681_get_deemph, pcm1681_put_deemph),
+};
+
+static struct snd_soc_dai_driver pcm1681_dai = {
+       .name = "pcm1681-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 8,
+               .rates = PCM1681_PCM_RATES,
+               .formats = PCM1681_PCM_FORMATS,
+       },
+       .ops = &pcm1681_dai_ops,
+};
+
+#ifdef CONFIG_OF
+static const struct of_device_id pcm1681_dt_ids[] = {
+       { .compatible = "ti,pcm1681", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, pcm1681_dt_ids);
+#endif
+
+static const struct regmap_config pcm1681_regmap = {
+       .reg_bits               = 8,
+       .val_bits               = 8,
+       .max_register           = ARRAY_SIZE(pcm1681_reg_defaults) + 1,
+       .reg_defaults           = pcm1681_reg_defaults,
+       .num_reg_defaults       = ARRAY_SIZE(pcm1681_reg_defaults),
+       .writeable_reg          = pcm1681_writeable_reg,
+       .readable_reg           = pcm1681_accessible_reg,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_pcm1681 = {
+       .controls               = pcm1681_controls,
+       .num_controls           = ARRAY_SIZE(pcm1681_controls),
+       .dapm_widgets           = pcm1681_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(pcm1681_dapm_widgets),
+       .dapm_routes            = pcm1681_dapm_routes,
+       .num_dapm_routes        = ARRAY_SIZE(pcm1681_dapm_routes),
+};
+
+static const struct i2c_device_id pcm1681_i2c_id[] = {
+       {"pcm1681", 0},
+       {}
+};
+MODULE_DEVICE_TABLE(i2c, pcm1681_i2c_id);
+
+static int pcm1681_i2c_probe(struct i2c_client *client,
+                             const struct i2c_device_id *id)
+{
+       int ret;
+       struct pcm1681_private *priv;
+
+       priv = devm_kzalloc(&client->dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv)
+               return -ENOMEM;
+
+       priv->regmap = devm_regmap_init_i2c(client, &pcm1681_regmap);
+       if (IS_ERR(priv->regmap)) {
+               ret = PTR_ERR(priv->regmap);
+               dev_err(&client->dev, "Failed to create regmap: %d\n", ret);
+               return ret;
+       }
+
+       i2c_set_clientdata(client, priv);
+
+       return snd_soc_register_codec(&client->dev, &soc_codec_dev_pcm1681,
+               &pcm1681_dai, 1);
+}
+
+static int pcm1681_i2c_remove(struct i2c_client *client)
+{
+       snd_soc_unregister_codec(&client->dev);
+       return 0;
+}
+
+static struct i2c_driver pcm1681_i2c_driver = {
+       .driver = {
+               .name   = "pcm1681",
+               .owner  = THIS_MODULE,
+               .of_match_table = of_match_ptr(pcm1681_dt_ids),
+       },
+       .id_table       = pcm1681_i2c_id,
+       .probe          = pcm1681_i2c_probe,
+       .remove         = pcm1681_i2c_remove,
+};
+
+module_i2c_driver(pcm1681_i2c_driver);
+
+MODULE_DESCRIPTION("Texas Instruments PCM1681 ALSA SoC Codec Driver");
+MODULE_AUTHOR("Marek Belisko <marek.belisko@streamunlimited.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1792a.c b/sound/soc/codecs/pcm1792a.c
new file mode 100644 (file)
index 0000000..2a8eccf
--- /dev/null
@@ -0,0 +1,257 @@
+/*
+ * PCM1792A ASoC codec driver
+ *
+ * Copyright (c) Amarula Solutions B.V. 2013
+ *
+ *     Michael Trimarchi <michael@amarulasolutions.com>
+ *
+ * This program is free software; you can redistribute it and/or
+ * modify it under the terms of the GNU General Public License
+ * as published by the Free Software Foundation; either version 2
+ * of the License, or (at your option) any later version.
+ *
+ * This program is distributed in the hope that it will be useful,
+ * but WITHOUT ANY WARRANTY; without even the implied warranty of
+ * MERCHANTABILITY or FITNESS FOR A PARTICULAR PURPOSE.  See the
+ * GNU General Public License for more details.
+ */
+
+#include <linux/module.h>
+#include <linux/slab.h>
+#include <linux/kernel.h>
+#include <linux/device.h>
+#include <linux/spi/spi.h>
+
+#include <sound/core.h>
+#include <sound/pcm.h>
+#include <sound/pcm_params.h>
+#include <sound/initval.h>
+#include <sound/soc.h>
+#include <sound/tlv.h>
+#include <linux/of_device.h>
+
+#include "pcm1792a.h"
+
+#define PCM1792A_DAC_VOL_LEFT  0x10
+#define PCM1792A_DAC_VOL_RIGHT 0x11
+#define PCM1792A_FMT_CONTROL   0x12
+#define PCM1792A_SOFT_MUTE     PCM1792A_FMT_CONTROL
+
+#define PCM1792A_FMT_MASK      0x70
+#define PCM1792A_FMT_SHIFT     4
+#define PCM1792A_MUTE_MASK     0x01
+#define PCM1792A_MUTE_SHIFT    0
+#define PCM1792A_ATLD_ENABLE   (1 << 7)
+
+static const struct reg_default pcm1792a_reg_defaults[] = {
+       { 0x10, 0xff },
+       { 0x11, 0xff },
+       { 0x12, 0x50 },
+       { 0x13, 0x00 },
+       { 0x14, 0x00 },
+       { 0x15, 0x01 },
+       { 0x16, 0x00 },
+       { 0x17, 0x00 },
+};
+
+static bool pcm1792a_accessible_reg(struct device *dev, unsigned int reg)
+{
+       return reg >= 0x10 && reg <= 0x17;
+}
+
+static bool pcm1792a_writeable_reg(struct device *dev, unsigned register reg)
+{
+       bool accessible;
+
+       accessible = pcm1792a_accessible_reg(dev, reg);
+
+       return accessible && reg != 0x16 && reg != 0x17;
+}
+
+struct pcm1792a_private {
+       struct regmap *regmap;
+       unsigned int format;
+       unsigned int rate;
+};
+
+static int pcm1792a_set_dai_fmt(struct snd_soc_dai *codec_dai,
+                             unsigned int format)
+{
+       struct snd_soc_codec *codec = codec_dai->codec;
+       struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
+
+       priv->format = format;
+
+       return 0;
+}
+
+static int pcm1792a_digital_mute(struct snd_soc_dai *dai, int mute)
+{
+       struct snd_soc_codec *codec = dai->codec;
+       struct pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       ret = regmap_update_bits(priv->regmap, PCM1792A_SOFT_MUTE,
+                                PCM1792A_MUTE_MASK, !!mute);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static int pcm1792a_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 pcm1792a_private *priv = snd_soc_codec_get_drvdata(codec);
+       int val = 0, ret;
+       int pcm_format = params_format(params);
+
+       priv->rate = params_rate(params);
+
+       switch (priv->format & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_RIGHT_J:
+               if (pcm_format == SNDRV_PCM_FORMAT_S24_LE ||
+                   pcm_format == SNDRV_PCM_FORMAT_S32_LE)
+                       val = 0x02;
+               else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE)
+                       val = 0x00;
+               break;
+       case SND_SOC_DAIFMT_I2S:
+               if (pcm_format == SNDRV_PCM_FORMAT_S24_LE ||
+                   pcm_format == SNDRV_PCM_FORMAT_S32_LE)
+                       val = 0x05;
+               else if (pcm_format == SNDRV_PCM_FORMAT_S16_LE)
+                       val = 0x04;
+               break;
+       default:
+               dev_err(codec->dev, "Invalid DAI format\n");
+               return -EINVAL;
+       }
+
+       val = val << PCM1792A_FMT_SHIFT | PCM1792A_ATLD_ENABLE;
+
+       ret = regmap_update_bits(priv->regmap, PCM1792A_FMT_CONTROL,
+                                PCM1792A_FMT_MASK | PCM1792A_ATLD_ENABLE, val);
+       if (ret < 0)
+               return ret;
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops pcm1792a_dai_ops = {
+       .set_fmt        = pcm1792a_set_dai_fmt,
+       .hw_params      = pcm1792a_hw_params,
+       .digital_mute   = pcm1792a_digital_mute,
+};
+
+static const DECLARE_TLV_DB_SCALE(pcm1792a_dac_tlv, -12000, 50, 1);
+
+static const struct snd_kcontrol_new pcm1792a_controls[] = {
+       SOC_DOUBLE_R_RANGE_TLV("DAC Playback Volume", PCM1792A_DAC_VOL_LEFT,
+                        PCM1792A_DAC_VOL_RIGHT, 0, 0xf, 0xff, 0,
+                        pcm1792a_dac_tlv),
+};
+
+static const struct snd_soc_dapm_widget pcm1792a_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("IOUTL+"),
+SND_SOC_DAPM_OUTPUT("IOUTL-"),
+SND_SOC_DAPM_OUTPUT("IOUTR+"),
+SND_SOC_DAPM_OUTPUT("IOUTR-"),
+};
+
+static const struct snd_soc_dapm_route pcm1792a_dapm_routes[] = {
+       { "IOUTL+", NULL, "Playback" },
+       { "IOUTL-", NULL, "Playback" },
+       { "IOUTR+", NULL, "Playback" },
+       { "IOUTR-", NULL, "Playback" },
+};
+
+static struct snd_soc_dai_driver pcm1792a_dai = {
+       .name = "pcm1792a-hifi",
+       .playback = {
+               .stream_name = "Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = PCM1792A_RATES,
+               .formats = PCM1792A_FORMATS, },
+       .ops = &pcm1792a_dai_ops,
+};
+
+static const struct of_device_id pcm1792a_of_match[] = {
+       { .compatible = "ti,pcm1792a", },
+       { }
+};
+MODULE_DEVICE_TABLE(of, pcm1792a_of_match);
+
+static const struct regmap_config pcm1792a_regmap = {
+       .reg_bits               = 8,
+       .val_bits               = 8,
+       .max_register           = 24,
+       .reg_defaults           = pcm1792a_reg_defaults,
+       .num_reg_defaults       = ARRAY_SIZE(pcm1792a_reg_defaults),
+       .writeable_reg          = pcm1792a_writeable_reg,
+       .readable_reg           = pcm1792a_accessible_reg,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_pcm1792a = {
+       .controls               = pcm1792a_controls,
+       .num_controls           = ARRAY_SIZE(pcm1792a_controls),
+       .dapm_widgets           = pcm1792a_dapm_widgets,
+       .num_dapm_widgets       = ARRAY_SIZE(pcm1792a_dapm_widgets),
+       .dapm_routes            = pcm1792a_dapm_routes,
+       .num_dapm_routes        = ARRAY_SIZE(pcm1792a_dapm_routes),
+};
+
+static int pcm1792a_spi_probe(struct spi_device *spi)
+{
+       struct pcm1792a_private *pcm1792a;
+       int ret;
+
+       pcm1792a = devm_kzalloc(&spi->dev, sizeof(struct pcm1792a_private),
+                               GFP_KERNEL);
+       if (!pcm1792a)
+               return -ENOMEM;
+
+       spi_set_drvdata(spi, pcm1792a);
+
+       pcm1792a->regmap = devm_regmap_init_spi(spi, &pcm1792a_regmap);
+       if (IS_ERR(pcm1792a->regmap)) {
+               ret = PTR_ERR(pcm1792a->regmap);
+               dev_err(&spi->dev, "Failed to register regmap: %d\n", ret);
+               return ret;
+       }
+
+       return snd_soc_register_codec(&spi->dev,
+                       &soc_codec_dev_pcm1792a, &pcm1792a_dai, 1);
+}
+
+static int pcm1792a_spi_remove(struct spi_device *spi)
+{
+       snd_soc_unregister_codec(&spi->dev);
+       return 0;
+}
+
+static const struct spi_device_id pcm1792a_spi_ids[] = {
+       { "pcm1792a", 0 },
+       { },
+};
+MODULE_DEVICE_TABLE(spi, pcm1792a_spi_ids);
+
+static struct spi_driver pcm1792a_codec_driver = {
+       .driver = {
+               .name = "pcm1792a",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(pcm1792a_of_match),
+       },
+       .id_table = pcm1792a_spi_ids,
+       .probe = pcm1792a_spi_probe,
+       .remove = pcm1792a_spi_remove,
+};
+
+module_spi_driver(pcm1792a_codec_driver);
+
+MODULE_DESCRIPTION("ASoC PCM1792A driver");
+MODULE_AUTHOR("Michael Trimarchi <michael@amarulasolutions.com>");
+MODULE_LICENSE("GPL");
diff --git a/sound/soc/codecs/pcm1792a.h b/sound/soc/codecs/pcm1792a.h
new file mode 100644 (file)
index 0000000..7a83d1f
--- /dev/null
@@ -0,0 +1,26 @@
+/*
+ * definitions for PCM1792A
+ *
+ * Copyright 2013 Amarula Solutions
+ *
+  * 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.
+ */
+
+#ifndef __PCM1792A_H__
+#define __PCM1792A_H__
+
+#define PCM1792A_RATES (SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_8000_48000 | \
+                       SNDRV_PCM_RATE_96000 | SNDRV_PCM_RATE_192000)
+
+#define PCM1792A_FORMATS (SNDRV_PCM_FMTBIT_S32_LE | SNDRV_PCM_FMTBIT_S24_LE | \
+                         SNDRV_PCM_FMTBIT_S16_LE)
+
+#endif
index f2a6282b41f4e07a1627463222b4ba9d091a7cf1..b6618c4a7597fee9d0bf11528f5997f7548a747f 100644 (file)
 
 #include "pcm3008.h"
 
-#define PCM3008_VERSION "0.2"
+static int pcm3008_dac_ev(struct snd_soc_dapm_widget *w,
+                         struct snd_kcontrol *kcontrol,
+                         int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct pcm3008_setup_data *setup = codec->dev->platform_data;
+
+       gpio_set_value_cansleep(setup->pdda_pin,
+                               SND_SOC_DAPM_EVENT_ON(event));
+
+       return 0;
+}
+
+static int pcm3008_adc_ev(struct snd_soc_dapm_widget *w,
+                         struct snd_kcontrol *kcontrol,
+                         int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct pcm3008_setup_data *setup = codec->dev->platform_data;
+
+       gpio_set_value_cansleep(setup->pdad_pin,
+                               SND_SOC_DAPM_EVENT_ON(event));
+
+       return 0;
+}
+
+static const struct snd_soc_dapm_widget pcm3008_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("VINL"),
+SND_SOC_DAPM_INPUT("VINR"),
+
+SND_SOC_DAPM_DAC_E("DAC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_dac_ev,
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+SND_SOC_DAPM_ADC_E("ADC", NULL, SND_SOC_NOPM, 0, 0, pcm3008_adc_ev,
+                  SND_SOC_DAPM_PRE_PMU | SND_SOC_DAPM_POST_PMD),
+
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route pcm3008_dapm_routes[] = {
+       { "PCM3008 Capture", NULL, "ADC" },
+       { "ADC", NULL, "VINL" },
+       { "ADC", NULL, "VINR" },
+
+       { "DAC", NULL, "PCM3008 Playback" },
+       { "VOUTL", NULL, "DAC" },
+       { "VOUTR", NULL, "DAC" },
+};
 
 #define PCM3008_RATES (SNDRV_PCM_RATE_32000 | SNDRV_PCM_RATE_44100 |   \
                       SNDRV_PCM_RATE_48000)
@@ -51,20 +98,20 @@ static struct snd_soc_dai_driver pcm3008_dai = {
        },
 };
 
-static void pcm3008_gpio_free(struct pcm3008_setup_data *setup)
-{
-       gpio_free(setup->dem0_pin);
-       gpio_free(setup->dem1_pin);
-       gpio_free(setup->pdad_pin);
-       gpio_free(setup->pdda_pin);
-}
+static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = {
+       .dapm_widgets = pcm3008_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(pcm3008_dapm_widgets),
+       .dapm_routes = pcm3008_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(pcm3008_dapm_routes),
+};
 
-static int pcm3008_soc_probe(struct snd_soc_codec *codec)
+static int pcm3008_codec_probe(struct platform_device *pdev)
 {
-       struct pcm3008_setup_data *setup = codec->dev->platform_data;
-       int ret = 0;
+       struct pcm3008_setup_data *setup = pdev->dev.platform_data;
+       int ret;
 
-       printk(KERN_INFO "PCM3008 SoC Audio Codec %s\n", PCM3008_VERSION);
+       if (!setup)
+               return -EINVAL;
 
        /* DEM1  DEM0  DE-EMPHASIS_MODE
         * Low   Low   De-emphasis 44.1 kHz ON
@@ -74,83 +121,29 @@ static int pcm3008_soc_probe(struct snd_soc_codec *codec)
         */
 
        /* Configure DEM0 GPIO (turning OFF DAC De-emphasis). */
-       ret = gpio_request(setup->dem0_pin, "codec_dem0");
-       if (ret == 0)
-               ret = gpio_direction_output(setup->dem0_pin, 1);
+       ret = devm_gpio_request_one(&pdev->dev, setup->dem0_pin,
+                                   GPIOF_OUT_INIT_HIGH, "codec_dem0");
        if (ret != 0)
-               goto gpio_err;
+               return ret;
 
        /* Configure DEM1 GPIO (turning OFF DAC De-emphasis). */
-       ret = gpio_request(setup->dem1_pin, "codec_dem1");
-       if (ret == 0)
-               ret = gpio_direction_output(setup->dem1_pin, 0);
+       ret = devm_gpio_request_one(&pdev->dev, setup->dem1_pin,
+                                   GPIOF_OUT_INIT_LOW, "codec_dem1");
        if (ret != 0)
-               goto gpio_err;
+               return ret;
 
        /* Configure PDAD GPIO. */
-       ret = gpio_request(setup->pdad_pin, "codec_pdad");
-       if (ret == 0)
-               ret = gpio_direction_output(setup->pdad_pin, 1);
+       ret = devm_gpio_request_one(&pdev->dev, setup->pdad_pin,
+                                   GPIOF_OUT_INIT_LOW, "codec_pdad");
        if (ret != 0)
-               goto gpio_err;
+               return ret;
 
        /* Configure PDDA GPIO. */
-       ret = gpio_request(setup->pdda_pin, "codec_pdda");
-       if (ret == 0)
-               ret = gpio_direction_output(setup->pdda_pin, 1);
+       ret = devm_gpio_request_one(&pdev->dev, setup->pdda_pin,
+                                   GPIOF_OUT_INIT_LOW, "codec_pdda");
        if (ret != 0)
-               goto gpio_err;
-
-       return ret;
-
-gpio_err:
-       pcm3008_gpio_free(setup);
+               return ret;
 
-       return ret;
-}
-
-static int pcm3008_soc_remove(struct snd_soc_codec *codec)
-{
-       struct pcm3008_setup_data *setup = codec->dev->platform_data;
-
-       pcm3008_gpio_free(setup);
-       return 0;
-}
-
-#ifdef CONFIG_PM
-static int pcm3008_soc_suspend(struct snd_soc_codec *codec)
-{
-       struct pcm3008_setup_data *setup = codec->dev->platform_data;
-
-       gpio_set_value(setup->pdad_pin, 0);
-       gpio_set_value(setup->pdda_pin, 0);
-
-       return 0;
-}
-
-static int pcm3008_soc_resume(struct snd_soc_codec *codec)
-{
-       struct pcm3008_setup_data *setup = codec->dev->platform_data;
-
-       gpio_set_value(setup->pdad_pin, 1);
-       gpio_set_value(setup->pdda_pin, 1);
-
-       return 0;
-}
-#else
-#define pcm3008_soc_suspend NULL
-#define pcm3008_soc_resume NULL
-#endif
-
-static struct snd_soc_codec_driver soc_codec_dev_pcm3008 = {
-       .probe =        pcm3008_soc_probe,
-       .remove =       pcm3008_soc_remove,
-       .suspend =      pcm3008_soc_suspend,
-       .resume =       pcm3008_soc_resume,
-};
-
-static int pcm3008_codec_probe(struct platform_device *pdev)
-{
        return snd_soc_register_codec(&pdev->dev,
                        &soc_codec_dev_pcm3008, &pcm3008_dai, 1);
 }
@@ -158,6 +151,7 @@ static int pcm3008_codec_probe(struct platform_device *pdev)
 static int pcm3008_codec_remove(struct platform_device *pdev)
 {
        snd_soc_unregister_codec(&pdev->dev);
+
        return 0;
 }
 
index ce585e37e38a8f8edda36a0146ed477ec61224bc..4db7314baabcab95feb9f1b0a1e5e2ab83d7a54a 100644 (file)
@@ -737,29 +737,6 @@ static const struct snd_kcontrol_new rt5640_mono_mix[] = {
                        RT5640_M_BST1_MM_SFT, 1, 1),
 };
 
-/* INL/R source */
-static const char * const rt5640_inl_src[] = {
-       "IN2P", "MONOP"
-};
-
-static const SOC_ENUM_SINGLE_DECL(
-       rt5640_inl_enum, RT5640_INL_INR_VOL,
-       RT5640_INL_SEL_SFT, rt5640_inl_src);
-
-static const struct snd_kcontrol_new rt5640_inl_mux =
-       SOC_DAPM_ENUM("INL source", rt5640_inl_enum);
-
-static const char * const rt5640_inr_src[] = {
-       "IN2N", "MONON"
-};
-
-static const SOC_ENUM_SINGLE_DECL(
-       rt5640_inr_enum, RT5640_INL_INR_VOL,
-       RT5640_INR_SEL_SFT, rt5640_inr_src);
-
-static const struct snd_kcontrol_new rt5640_inr_mux =
-       SOC_DAPM_ENUM("INR source", rt5640_inr_enum);
-
 /* Stereo ADC source */
 static const char * const rt5640_stereo_adc1_src[] = {
        "DIG MIX", "ADC"
@@ -1005,9 +982,6 @@ static const struct snd_soc_dapm_widget rt5640_dapm_widgets[] = {
                RT5640_PWR_IN_L_BIT, 0, NULL, 0),
        SND_SOC_DAPM_PGA("INR VOL", RT5640_PWR_VOL,
                RT5640_PWR_IN_R_BIT, 0, NULL, 0),
-       /* IN Mux */
-       SND_SOC_DAPM_MUX("INL Mux", SND_SOC_NOPM, 0, 0, &rt5640_inl_mux),
-       SND_SOC_DAPM_MUX("INR Mux", SND_SOC_NOPM, 0, 0, &rt5640_inr_mux),
        /* REC Mixer */
        SND_SOC_DAPM_MIXER("RECMIXL", RT5640_PWR_MIXER, RT5640_PWR_RM_L_BIT, 0,
                        rt5640_rec_l_mix, ARRAY_SIZE(rt5640_rec_l_mix)),
index 760e8bfeacaadef370eb0ce30e1ce5ad124da295..1f4093f3f3a19219318eb0f3c407d8f302ce34d3 100644 (file)
@@ -654,16 +654,19 @@ static int sgtl5000_set_clock(struct snd_soc_codec *codec, int frame_rate)
                snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
                        SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
                        SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP);
+
+               /* if using pll, clk_ctrl must be set after pll power up */
+               snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
        } else {
+               /* otherwise, clk_ctrl must be set before pll power down */
+               snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
+
                /* power down pll */
                snd_soc_update_bits(codec, SGTL5000_CHIP_ANA_POWER,
                        SGTL5000_PLL_POWERUP | SGTL5000_VCOAMP_POWERUP,
                        0);
        }
 
-       /* if using pll, clk_ctrl must be set after pll power up */
-       snd_soc_write(codec, SGTL5000_CHIP_CLK_CTRL, clk_ctl);
-
        return 0;
 }
 
@@ -1480,6 +1483,7 @@ static struct snd_soc_codec_driver sgtl5000_driver = {
 static const struct regmap_config sgtl5000_regmap = {
        .reg_bits = 16,
        .val_bits = 16,
+       .reg_stride = 2,
 
        .max_register = SGTL5000_MAX_REG_OFFSET,
        .volatile_reg = sgtl5000_volatile,
index 73e205c892a0d087b4cfc8a6cd0a7de5c26873f3..38f3b105c17dc2da0809c903190dd37644f2508d 100644 (file)
@@ -102,6 +102,16 @@ static int si476x_codec_write(struct snd_soc_codec *codec,
        return err;
 }
 
+static const struct snd_soc_dapm_widget si476x_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("LOUT"),
+SND_SOC_DAPM_OUTPUT("ROUT"),
+};
+
+static const struct snd_soc_dapm_route si476x_dapm_routes[] = {
+       { "Capture", NULL, "LOUT" },
+       { "Capture", NULL, "ROUT" },
+};
+
 static int si476x_codec_set_dai_fmt(struct snd_soc_dai *codec_dai,
                                    unsigned int fmt)
 {
@@ -260,6 +270,10 @@ static struct snd_soc_codec_driver soc_codec_dev_si476x = {
        .probe  = si476x_codec_probe,
        .read   = si476x_codec_read,
        .write  = si476x_codec_write,
+       .dapm_widgets = si476x_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(si476x_dapm_widgets),
+       .dapm_routes = si476x_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(si476x_dapm_routes),
 };
 
 static int si476x_platform_probe(struct platform_device *pdev)
index e9d7881ed2c8272c2c64c7255e60728356aab009..e3501f40c7b3d866f965131e64ce2ae1227e1b77 100644 (file)
 #include <sound/initval.h>
 #include <linux/of.h>
 
+static const struct snd_soc_dapm_widget dir_widgets[] = {
+       SND_SOC_DAPM_INPUT("spdif-in"),
+};
+
+static const struct snd_soc_dapm_route dir_routes[] = {
+       { "Capture", NULL, "spdif-in" },
+};
+
 #define STUB_RATES     SNDRV_PCM_RATE_8000_192000
 #define STUB_FORMATS   (SNDRV_PCM_FMTBIT_S16_LE | \
+                       SNDRV_PCM_FMTBIT_S20_3LE | \
+                       SNDRV_PCM_FMTBIT_S24_LE | \
                        SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE)
 
-static struct snd_soc_codec_driver soc_codec_spdif_dir;
+static struct snd_soc_codec_driver soc_codec_spdif_dir = {
+       .dapm_widgets = dir_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(dir_widgets),
+       .dapm_routes = dir_routes,
+       .num_dapm_routes = ARRAY_SIZE(dir_routes),
+};
 
 static struct snd_soc_dai_driver dir_stub_dai = {
        .name           = "dir-hifi",
index 18280499fd554ee13ca7f9c17d86c8594a40d8bb..a078aa31052a589e2ee4115f6657806b62ba0418 100644 (file)
 #define DRV_NAME "spdif-dit"
 
 #define STUB_RATES     SNDRV_PCM_RATE_8000_96000
-#define STUB_FORMATS   SNDRV_PCM_FMTBIT_S16_LE
+#define STUB_FORMATS   (SNDRV_PCM_FMTBIT_S16_LE | \
+                       SNDRV_PCM_FMTBIT_S20_3LE | \
+                       SNDRV_PCM_FMTBIT_S24_LE)
 
+static const struct snd_soc_dapm_widget dit_widgets[] = {
+       SND_SOC_DAPM_OUTPUT("spdif-out"),
+};
+
+static const struct snd_soc_dapm_route dit_routes[] = {
+       { "spdif-out", NULL, "Playback" },
+};
 
-static struct snd_soc_codec_driver soc_codec_spdif_dit;
+static struct snd_soc_codec_driver soc_codec_spdif_dit = {
+       .dapm_widgets = dit_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(dit_widgets),
+       .dapm_routes = dit_routes,
+       .num_dapm_routes = ARRAY_SIZE(dit_routes),
+};
 
 static struct snd_soc_dai_driver dit_stub_dai = {
        .name           = "dit-hifi",
index cfb55fe35e98691cbaf7b18404b93e02c1cce73c..06edb396e733f1ad2ef26698a41b4eb1fdc18ea6 100644 (file)
@@ -363,16 +363,18 @@ static void sta32x_watchdog(struct work_struct *work)
        }
 
        if (!sta32x->shutdown)
-               schedule_delayed_work(&sta32x->watchdog_work,
-                                     round_jiffies_relative(HZ));
+               queue_delayed_work(system_power_efficient_wq,
+                                  &sta32x->watchdog_work,
+                                  round_jiffies_relative(HZ));
 }
 
 static void sta32x_watchdog_start(struct sta32x_priv *sta32x)
 {
        if (sta32x->pdata->needs_esd_watchdog) {
                sta32x->shutdown = 0;
-               schedule_delayed_work(&sta32x->watchdog_work,
-                                     round_jiffies_relative(HZ));
+               queue_delayed_work(system_power_efficient_wq,
+                                  &sta32x->watchdog_work,
+                                  round_jiffies_relative(HZ));
        }
 }
 
index b1f6982c7c9c2615cc187649501f231098d1696c..7b8f3d965f43c814b9b5f5f918ddc6c8778129d4 100644 (file)
@@ -29,7 +29,7 @@ MODULE_LICENSE("GPL");
 /* AIC26 driver private data */
 struct aic26 {
        struct spi_device *spi;
-       struct snd_soc_codec codec;
+       struct snd_soc_codec *codec;
        int master;
        int datfm;
        int mclk;
@@ -119,6 +119,22 @@ static int aic26_reg_write(struct snd_soc_codec *codec, unsigned int reg,
        return 0;
 }
 
+static const struct snd_soc_dapm_widget tlv320aic26_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("MICIN"),
+SND_SOC_DAPM_INPUT("AUX"),
+
+SND_SOC_DAPM_OUTPUT("HPL"),
+SND_SOC_DAPM_OUTPUT("HPR"),
+};
+
+static const struct snd_soc_dapm_route tlv320aic26_dapm_routes[] = {
+       { "Capture", NULL, "MICIN" },
+       { "Capture", NULL, "AUX" },
+
+       { "HPL", NULL, "Playback" },
+       { "HPR", NULL, "Playback" },
+};
+
 /* ---------------------------------------------------------------------
  * Digital Audio Interface Operations
  */
@@ -174,9 +190,9 @@ static int aic26_hw_params(struct snd_pcm_substream *substream,
        dev_dbg(&aic26->spi->dev, "Setting PLLM to %d.%04d\n", jval, dval);
        qval = 0;
        reg = 0x8000 | qval << 11 | pval << 8 | jval << 2;
-       aic26_reg_write(codec, AIC26_REG_PLL_PROG1, reg);
+       snd_soc_write(codec, AIC26_REG_PLL_PROG1, reg);
        reg = dval << 2;
-       aic26_reg_write(codec, AIC26_REG_PLL_PROG2, reg);
+       snd_soc_write(codec, AIC26_REG_PLL_PROG2, reg);
 
        /* Audio Control 3 (master mode, fsref rate) */
        reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL3);
@@ -185,13 +201,13 @@ static int aic26_hw_params(struct snd_pcm_substream *substream,
                reg |= 0x0800;
        if (fsref == 48000)
                reg |= 0x2000;
-       aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
+       snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
 
        /* Audio Control 1 (FSref divisor) */
        reg = aic26_reg_read_cache(codec, AIC26_REG_AUDIO_CTRL1);
        reg &= ~0x0fff;
        reg |= wlen | aic26->datfm | (divisor << 3) | divisor;
-       aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL1, reg);
+       snd_soc_write(codec, AIC26_REG_AUDIO_CTRL1, reg);
 
        return 0;
 }
@@ -212,7 +228,7 @@ static int aic26_mute(struct snd_soc_dai *dai, int mute)
                reg |= 0x8080;
        else
                reg &= ~0x8080;
-       aic26_reg_write(codec, AIC26_REG_DAC_GAIN, reg);
+       snd_soc_write(codec, AIC26_REG_DAC_GAIN, reg);
 
        return 0;
 }
@@ -330,7 +346,7 @@ static ssize_t aic26_keyclick_show(struct device *dev,
        struct aic26 *aic26 = dev_get_drvdata(dev);
        int val, amp, freq, len;
 
-       val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
+       val = aic26_reg_read_cache(aic26->codec, AIC26_REG_AUDIO_CTRL2);
        amp = (val >> 12) & 0x7;
        freq = (125 << ((val >> 8) & 0x7)) >> 1;
        len = 2 * (1 + ((val >> 4) & 0xf));
@@ -346,9 +362,9 @@ static ssize_t aic26_keyclick_set(struct device *dev,
        struct aic26 *aic26 = dev_get_drvdata(dev);
        int val;
 
-       val = aic26_reg_read_cache(&aic26->codec, AIC26_REG_AUDIO_CTRL2);
+       val = aic26_reg_read_cache(aic26->codec, AIC26_REG_AUDIO_CTRL2);
        val |= 0x8000;
-       aic26_reg_write(&aic26->codec, AIC26_REG_AUDIO_CTRL2, val);
+       snd_soc_write(aic26->codec, AIC26_REG_AUDIO_CTRL2, val);
 
        return count;
 }
@@ -360,25 +376,26 @@ static DEVICE_ATTR(keyclick, 0644, aic26_keyclick_show, aic26_keyclick_set);
  */
 static int aic26_probe(struct snd_soc_codec *codec)
 {
+       struct aic26 *aic26 = dev_get_drvdata(codec->dev);
        int ret, err, i, reg;
 
-       dev_info(codec->dev, "Probing AIC26 SoC CODEC driver\n");
+       aic26->codec = codec;
 
        /* Reset the codec to power on defaults */
-       aic26_reg_write(codec, AIC26_REG_RESET, 0xBB00);
+       snd_soc_write(codec, AIC26_REG_RESET, 0xBB00);
 
        /* Power up CODEC */
-       aic26_reg_write(codec, AIC26_REG_POWER_CTRL, 0);
+       snd_soc_write(codec, AIC26_REG_POWER_CTRL, 0);
 
        /* Audio Control 3 (master mode, fsref rate) */
-       reg = aic26_reg_read(codec, AIC26_REG_AUDIO_CTRL3);
+       reg = snd_soc_read(codec, AIC26_REG_AUDIO_CTRL3);
        reg &= ~0xf800;
        reg |= 0x0800; /* set master mode */
-       aic26_reg_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
+       snd_soc_write(codec, AIC26_REG_AUDIO_CTRL3, reg);
 
        /* Fill register cache */
        for (i = 0; i < codec->driver->reg_cache_size; i++)
-               aic26_reg_read(codec, i);
+               snd_soc_read(codec, i);
 
        /* Register the sysfs files for debugging */
        /* Create SysFS files */
@@ -401,6 +418,10 @@ static struct snd_soc_codec_driver aic26_soc_codec_dev = {
        .write = aic26_reg_write,
        .reg_cache_size = AIC26_NUM_REGS,
        .reg_word_size = sizeof(u16),
+       .dapm_widgets = tlv320aic26_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(tlv320aic26_dapm_widgets),
+       .dapm_routes = tlv320aic26_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(tlv320aic26_dapm_routes),
 };
 
 /* ---------------------------------------------------------------------
index 17df4e32feac401c551ffef564dd10c25ae6bf49..2ed57d4aa4456e4e614ca85765846d1d8aa78cf2 100644 (file)
@@ -338,18 +338,6 @@ static inline int aic32x4_get_divs(int mclk, int rate)
        return -EINVAL;
 }
 
-static int aic32x4_add_widgets(struct snd_soc_codec *codec)
-{
-       snd_soc_dapm_new_controls(&codec->dapm, aic32x4_dapm_widgets,
-                                 ARRAY_SIZE(aic32x4_dapm_widgets));
-
-       snd_soc_dapm_add_routes(&codec->dapm, aic32x4_dapm_routes,
-                               ARRAY_SIZE(aic32x4_dapm_routes));
-
-       snd_soc_dapm_new_widgets(&codec->dapm);
-       return 0;
-}
-
 static int aic32x4_set_dai_sysclk(struct snd_soc_dai *codec_dai,
                                  int clk_id, unsigned int freq, int dir)
 {
@@ -683,9 +671,6 @@ static int aic32x4_probe(struct snd_soc_codec *codec)
        }
 
        aic32x4_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
-       snd_soc_add_codec_controls(codec, aic32x4_snd_controls,
-                            ARRAY_SIZE(aic32x4_snd_controls));
-       aic32x4_add_widgets(codec);
 
        /*
         * Workaround: for an unknown reason, the ADC needs to be powered up
@@ -714,6 +699,13 @@ static struct snd_soc_codec_driver soc_codec_dev_aic32x4 = {
        .suspend = aic32x4_suspend,
        .resume = aic32x4_resume,
        .set_bias_level = aic32x4_set_bias_level,
+
+       .controls = aic32x4_snd_controls,
+       .num_controls = ARRAY_SIZE(aic32x4_snd_controls),
+       .dapm_widgets = aic32x4_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(aic32x4_dapm_widgets),
+       .dapm_routes = aic32x4_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(aic32x4_dapm_routes),
 };
 
 static int aic32x4_i2c_probe(struct i2c_client *i2c,
index e5b926883131180c71cfe6239d1bca3be8fcf674..6e3f269243e050fe5149e60807c0e41cbf6e4147 100644 (file)
@@ -138,8 +138,7 @@ static const u8 aic3x_reg[AIC3X_CACHEREGNUM] = {
 static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
                                        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        unsigned int reg = mc->reg;
@@ -147,10 +146,9 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
        int max = mc->max;
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
-       unsigned short val, val_mask;
-       int ret;
-       struct snd_soc_dapm_path *path;
-       int found = 0;
+       unsigned short val;
+       struct snd_soc_dapm_update update;
+       int connect, change;
 
        val = (ucontrol->value.integer.value[0] & mask);
 
@@ -158,42 +156,26 @@ static int snd_soc_dapm_put_volsw_aic3x(struct snd_kcontrol *kcontrol,
        if (val)
                val = mask;
 
+       connect = !!val;
+
        if (invert)
                val = mask - val;
-       val_mask = mask << shift;
-       val = val << shift;
-
-       mutex_lock(&widget->codec->mutex);
 
-       if (snd_soc_test_bits(widget->codec, reg, val_mask, val)) {
-               /* find dapm widget path assoc with kcontrol */
-               list_for_each_entry(path, &widget->dapm->card->paths, list) {
-                       if (path->kcontrol != kcontrol)
-                               continue;
+       mask <<= shift;
+       val <<= shift;
 
-                       /* found, now check type */
-                       found = 1;
-                       if (val)
-                               /* new connection */
-                               path->connect = invert ? 0 : 1;
-                       else
-                               /* old connection must be powered down */
-                               path->connect = invert ? 1 : 0;
+       change = snd_soc_test_bits(codec, val, mask, reg);
+       if (change) {
+               update.kcontrol = kcontrol;
+               update.reg = reg;
+               update.mask = mask;
+               update.val = val;
 
-                       dapm_mark_dirty(path->source, "tlv320aic3x source");
-                       dapm_mark_dirty(path->sink, "tlv320aic3x sink");
-
-                       break;
-               }
+               snd_soc_dapm_mixer_update_power(&codec->dapm, kcontrol, connect,
+                       &update);
        }
 
-       mutex_unlock(&widget->codec->mutex);
-
-       if (found)
-               snd_soc_dapm_sync(widget->dapm);
-
-       ret = snd_soc_update_bits_locked(widget->codec, reg, val_mask, val);
-       return ret;
+       return change;
 }
 
 /*
@@ -1492,6 +1474,7 @@ static const struct i2c_device_id aic3x_i2c_id[] = {
        { "tlv320aic3x", AIC3X_MODEL_3X },
        { "tlv320aic33", AIC3X_MODEL_33 },
        { "tlv320aic3007", AIC3X_MODEL_3007 },
+       { "tlv320aic3106", AIC3X_MODEL_3X },
        { }
 };
 MODULE_DEVICE_TABLE(i2c, aic3x_i2c_id);
@@ -1582,6 +1565,9 @@ static int aic3x_i2c_remove(struct i2c_client *client)
 #if defined(CONFIG_OF)
 static const struct of_device_id tlv320aic3x_of_match[] = {
        { .compatible = "ti,tlv320aic3x", },
+       { .compatible = "ti,tlv320aic33" },
+       { .compatible = "ti,tlv320aic3007" },
+       { .compatible = "ti,tlv320aic3106" },
        {},
 };
 MODULE_DEVICE_TABLE(of, tlv320aic3x_of_match);
index 8e6e5b0160219ef140748a3dbf31bc7561b9a8d1..1e3884d6b3fbc7202cedb6fe478e5d00e71da49b 100644 (file)
@@ -137,8 +137,6 @@ static const u8 twl4030_reg[TWL4030_CACHEREGNUM] = {
 
 /* codec private data */
 struct twl4030_priv {
-       struct snd_soc_codec codec;
-
        unsigned int codec_powered;
 
        /* reference counts of AIF/APLL users */
index 44621ddc332d881b5e4b05a58507060259446e55..3c79dbb6c32323b36bc974616c5ff1157498f281 100644 (file)
@@ -429,7 +429,8 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data)
        struct snd_soc_codec *codec = data;
        struct twl6040_data *priv = snd_soc_codec_get_drvdata(codec);
 
-       schedule_delayed_work(&priv->hs_jack.work, msecs_to_jiffies(200));
+       queue_delayed_work(system_power_efficient_wq,
+                          &priv->hs_jack.work, msecs_to_jiffies(200));
 
        return IRQ_HANDLED;
 }
@@ -437,9 +438,7 @@ static irqreturn_t twl6040_audio_handler(int irq, void *data)
 static int twl6040_soc_dapm_put_vibra_enum(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-       struct snd_soc_codec *codec = widget->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val;
 
index 6d0aa44c375755fb8715c2089d27d4a3686598f1..c94d4c1e3dacc1d7a51f2c6708848d56af6ecf9e 100644 (file)
@@ -325,7 +325,6 @@ static int uda134x_set_dai_fmt(struct snd_soc_dai *codec_dai,
 static int uda134x_set_bias_level(struct snd_soc_codec *codec,
                                  enum snd_soc_bias_level level)
 {
-       u8 reg;
        struct uda134x_platform_data *pd = codec->control_data;
        int i;
        u8 *cache = codec->reg_cache;
@@ -334,23 +333,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec,
 
        switch (level) {
        case SND_SOC_BIAS_ON:
-               /* ADC, DAC on */
-               switch (pd->model) {
-               case UDA134X_UDA1340:
-               case UDA134X_UDA1344:
-               case UDA134X_UDA1345:
-                       reg = uda134x_read_reg_cache(codec, UDA134X_DATA011);
-                       uda134x_write(codec, UDA134X_DATA011, reg | 0x03);
-                       break;
-               case UDA134X_UDA1341:
-                       reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
-                       uda134x_write(codec, UDA134X_STATUS1, reg | 0x03);
-                       break;
-               default:
-                       printk(KERN_ERR "UDA134X SoC codec: "
-                              "unsupported model %d\n", pd->model);
-                       return -EINVAL;
-               }
                break;
        case SND_SOC_BIAS_PREPARE:
                /* power on */
@@ -362,23 +344,6 @@ static int uda134x_set_bias_level(struct snd_soc_codec *codec,
                }
                break;
        case SND_SOC_BIAS_STANDBY:
-               /* ADC, DAC power off */
-               switch (pd->model) {
-               case UDA134X_UDA1340:
-               case UDA134X_UDA1344:
-               case UDA134X_UDA1345:
-                       reg = uda134x_read_reg_cache(codec, UDA134X_DATA011);
-                       uda134x_write(codec, UDA134X_DATA011, reg & ~(0x03));
-                       break;
-               case UDA134X_UDA1341:
-                       reg = uda134x_read_reg_cache(codec, UDA134X_STATUS1);
-                       uda134x_write(codec, UDA134X_STATUS1, reg & ~(0x03));
-                       break;
-               default:
-                       printk(KERN_ERR "UDA134X SoC codec: "
-                              "unsupported model %d\n", pd->model);
-                       return -EINVAL;
-               }
                break;
        case SND_SOC_BIAS_OFF:
                /* power off */
@@ -450,6 +415,37 @@ SOC_ENUM("PCM Playback De-emphasis", uda134x_mixer_enum[1]),
 SOC_SINGLE("DC Filter Enable Switch", UDA134X_STATUS0, 0, 1, 0),
 };
 
+/* UDA1341 has the DAC/ADC power down in STATUS1 */
+static const struct snd_soc_dapm_widget uda1341_dapm_widgets[] = {
+       SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_STATUS1, 0, 0),
+       SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_STATUS1, 1, 0),
+};
+
+/* UDA1340/4/5 has the DAC/ADC pwoer down in DATA0 11 */
+static const struct snd_soc_dapm_widget uda1340_dapm_widgets[] = {
+       SND_SOC_DAPM_DAC("DAC", "Playback", UDA134X_DATA011, 0, 0),
+       SND_SOC_DAPM_ADC("ADC", "Capture", UDA134X_DATA011, 1, 0),
+};
+
+/* Common DAPM widgets */
+static const struct snd_soc_dapm_widget uda134x_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("VINL1"),
+       SND_SOC_DAPM_INPUT("VINR1"),
+       SND_SOC_DAPM_INPUT("VINL2"),
+       SND_SOC_DAPM_INPUT("VINR2"),
+       SND_SOC_DAPM_OUTPUT("VOUTL"),
+       SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route uda134x_dapm_routes[] = {
+       { "ADC", NULL, "VINL1" },
+       { "ADC", NULL, "VINR1" },
+       { "ADC", NULL, "VINL2" },
+       { "ADC", NULL, "VINR2" },
+       { "VOUTL", NULL, "DAC" },
+       { "VOUTR", NULL, "DAC" },
+};
+
 static const struct snd_soc_dai_ops uda134x_dai_ops = {
        .startup        = uda134x_startup,
        .shutdown       = uda134x_shutdown,
@@ -485,6 +481,8 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
 {
        struct uda134x_priv *uda134x;
        struct uda134x_platform_data *pd = codec->card->dev->platform_data;
+       const struct snd_soc_dapm_widget *widgets;
+       unsigned num_widgets;
 
        int ret;
 
@@ -526,6 +524,22 @@ static int uda134x_soc_probe(struct snd_soc_codec *codec)
        else
                uda134x_set_bias_level(codec, SND_SOC_BIAS_STANDBY);
 
+       if (pd->model == UDA134X_UDA1341) {
+               widgets = uda1341_dapm_widgets;
+               num_widgets = ARRAY_SIZE(uda1341_dapm_widgets);
+       } else {
+               widgets = uda1340_dapm_widgets;
+               num_widgets = ARRAY_SIZE(uda1340_dapm_widgets);
+       }
+
+       ret = snd_soc_dapm_new_controls(&codec->dapm, widgets, num_widgets);
+       if (ret) {
+               printk(KERN_ERR "%s failed to register dapm controls: %d",
+                       __func__, ret);
+               kfree(uda134x);
+               return ret;
+       }
+
        switch (pd->model) {
        case UDA134X_UDA1340:
        case UDA134X_UDA1344:
@@ -599,6 +613,10 @@ static struct snd_soc_codec_driver soc_codec_dev_uda134x = {
        .read = uda134x_read_reg_cache,
        .write = uda134x_write,
        .set_bias_level = uda134x_set_bias_level,
+       .dapm_widgets = uda134x_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(uda134x_dapm_widgets),
+       .dapm_routes = uda134x_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(uda134x_dapm_routes),
 };
 
 static int uda134x_codec_probe(struct platform_device *pdev)
index 54cd3da09abd036471291901461331231ce78ab7..b7ab2ef567c889ad7969c9bae6e2a4ca640b7de4 100644 (file)
@@ -290,6 +290,18 @@ static const struct snd_kcontrol_new wl1273_controls[] = {
                       snd_wl1273_fm_volume_get, snd_wl1273_fm_volume_put),
 };
 
+static const struct snd_soc_dapm_widget wl1273_dapm_widgets[] = {
+       SND_SOC_DAPM_INPUT("RX"),
+
+       SND_SOC_DAPM_OUTPUT("TX"),
+};
+
+static const struct snd_soc_dapm_route wl1273_dapm_routes[] = {
+       { "Capture", NULL, "RX" },
+
+       { "TX", NULL, "Playback" },
+};
+
 static int wl1273_startup(struct snd_pcm_substream *substream,
                          struct snd_soc_dai *dai)
 {
@@ -483,6 +495,11 @@ static int wl1273_remove(struct snd_soc_codec *codec)
 static struct snd_soc_codec_driver soc_codec_dev_wl1273 = {
        .probe = wl1273_probe,
        .remove = wl1273_remove,
+
+       .dapm_widgets = wl1273_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(wl1273_dapm_widgets),
+       .dapm_routes = wl1273_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(wl1273_dapm_routes),
 };
 
 static int wl1273_platform_probe(struct platform_device *pdev)
index 10adc4145d4639e5e44d3c587a595f476650b6fa..d5ebcb00019b7b807d9ad7e3940333c5ad2a4c4f 100644 (file)
@@ -420,7 +420,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec)
                xfer->codec = codec;
                list_add_tail(&xfer->list, &xfer_list);
 
-               out = kzalloc(len, GFP_KERNEL);
+               out = kzalloc(len, GFP_KERNEL | GFP_DMA);
                if (!out) {
                        dev_err(codec->dev,
                                "Failed to allocate RX buffer\n");
@@ -429,7 +429,7 @@ static int wm0010_firmware_load(const char *name, struct snd_soc_codec *codec)
                }
                xfer->t.rx_buf = out;
 
-               img = kzalloc(len, GFP_KERNEL);
+               img = kzalloc(len, GFP_KERNEL | GFP_DMA);
                if (!img) {
                        dev_err(codec->dev,
                                "Failed to allocate image buffer\n");
@@ -523,14 +523,14 @@ static int wm0010_stage2_load(struct snd_soc_codec *codec)
        dev_dbg(codec->dev, "Downloading %zu byte stage 2 loader\n", fw->size);
 
        /* Copy to local buffer first as vmalloc causes problems for dma */
-       img = kzalloc(fw->size, GFP_KERNEL);
+       img = kzalloc(fw->size, GFP_KERNEL | GFP_DMA);
        if (!img) {
                dev_err(codec->dev, "Failed to allocate image buffer\n");
                ret = -ENOMEM;
                goto abort2;
        }
 
-       out = kzalloc(fw->size, GFP_KERNEL);
+       out = kzalloc(fw->size, GFP_KERNEL | GFP_DMA);
        if (!out) {
                dev_err(codec->dev, "Failed to allocate output buffer\n");
                ret = -ENOMEM;
@@ -670,14 +670,14 @@ static int wm0010_boot(struct snd_soc_codec *codec)
 
                ret = -ENOMEM;
                len = pll_rec.length + 8;
-               out = kzalloc(len, GFP_KERNEL);
+               out = kzalloc(len, GFP_KERNEL | GFP_DMA);
                if (!out) {
                        dev_err(codec->dev,
                                "Failed to allocate RX buffer\n");
                        goto abort;
                }
 
-               img_swap = kzalloc(len, GFP_KERNEL);
+               img_swap = kzalloc(len, GFP_KERNEL | GFP_DMA);
                if (!img_swap) {
                        dev_err(codec->dev,
                                "Failed to allocate image buffer\n");
index 282fd232cdf7cf62ce6d836578e3c518cd839d59..8bbddc151aa842eed14cae97cdabdcbbd9d8e469 100644 (file)
@@ -998,6 +998,8 @@ SND_SOC_DAPM_INPUT("IN2R"),
 SND_SOC_DAPM_INPUT("IN3L"),
 SND_SOC_DAPM_INPUT("IN3R"),
 
+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 |
@@ -1421,9 +1423,6 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
        { "Tone Generator 1", NULL, "TONE" },
        { "Tone Generator 2", NULL, "TONE" },
 
-       { "Mic Mute Mixer", NULL, "Noise Mixer" },
-       { "Mic Mute Mixer", NULL, "Mic Mixer" },
-
        { "AIF1 Capture", NULL, "AIF1TX1" },
        { "AIF1 Capture", NULL, "AIF1TX2" },
        { "AIF1 Capture", NULL, "AIF1TX3" },
@@ -1499,23 +1498,6 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
        { "IN3L PGA", NULL, "IN3L" },
        { "IN3R PGA", NULL, "IN3R" },
 
-       { "ASRC1L", NULL, "ASRC1L Input" },
-       { "ASRC1R", NULL, "ASRC1R Input" },
-       { "ASRC2L", NULL, "ASRC2L Input" },
-       { "ASRC2R", NULL, "ASRC2R Input" },
-
-       { "ISRC1DEC1", NULL, "ISRC1DEC1 Input" },
-       { "ISRC1DEC2", NULL, "ISRC1DEC2 Input" },
-
-       { "ISRC1INT1", NULL, "ISRC1INT1 Input" },
-       { "ISRC1INT2", NULL, "ISRC1INT2 Input" },
-
-       { "ISRC2DEC1", NULL, "ISRC2DEC1 Input" },
-       { "ISRC2DEC2", NULL, "ISRC2DEC2 Input" },
-
-       { "ISRC2INT1", NULL, "ISRC2INT1 Input" },
-       { "ISRC2INT2", NULL, "ISRC2INT2 Input" },
-
        ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
        ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
        ARIZONA_MIXER_ROUTES("OUT2L", "HPOUT2L"),
@@ -1567,22 +1549,25 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
        ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
        ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
 
-       ARIZONA_MUX_ROUTES("ASRC1L"),
-       ARIZONA_MUX_ROUTES("ASRC1R"),
-       ARIZONA_MUX_ROUTES("ASRC2L"),
-       ARIZONA_MUX_ROUTES("ASRC2R"),
+       ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"),
+       ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
 
-       ARIZONA_MUX_ROUTES("ISRC1INT1"),
-       ARIZONA_MUX_ROUTES("ISRC1INT2"),
+       ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
+       ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
+       ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
+       ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
 
-       ARIZONA_MUX_ROUTES("ISRC1DEC1"),
-       ARIZONA_MUX_ROUTES("ISRC1DEC2"),
+       ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+       ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC1INT2"),
 
-       ARIZONA_MUX_ROUTES("ISRC2INT1"),
-       ARIZONA_MUX_ROUTES("ISRC2INT2"),
+       ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+       ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
 
-       ARIZONA_MUX_ROUTES("ISRC2DEC1"),
-       ARIZONA_MUX_ROUTES("ISRC2DEC2"),
+       ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+       ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+       ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+       ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
 
        ARIZONA_DSP_ROUTES("DSP1"),
 
@@ -1614,6 +1599,9 @@ static const struct snd_soc_dapm_route wm5102_dapm_routes[] = {
        { "SPKDAT1R", NULL, "OUT5R" },
 
        { "MICSUPP", NULL, "SYSCLK" },
+
+       { "DRC1 Signal Activity", NULL, "DRC1L" },
+       { "DRC1 Signal Activity", NULL, "DRC1R" },
 };
 
 static int wm5102_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
@@ -1781,6 +1769,7 @@ static int wm5102_codec_probe(struct snd_soc_codec *codec)
                return ret;
 
        arizona_init_spk(codec);
+       arizona_init_gpio(codec);
 
        snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
 
index 2e7cb4ba161a5fa2a6980ec59eae33eb9c49ad18..bbd64384ca1ce9b76f8efc63f8ead2e3bc8a1c0c 100644 (file)
@@ -58,14 +58,10 @@ static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
        SOC_SINGLE(name " NG SPKDAT2R Switch", base, 11, 1, 0)
 
 static const struct snd_kcontrol_new wm5110_snd_controls[] = {
-SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
-          ARIZONA_IN1_OSR_SHIFT, 1, 0),
-SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL,
-          ARIZONA_IN2_OSR_SHIFT, 1, 0),
-SOC_SINGLE("IN3 High Performance Switch", ARIZONA_IN3L_CONTROL,
-          ARIZONA_IN3_OSR_SHIFT, 1, 0),
-SOC_SINGLE("IN4 High Performance Switch", ARIZONA_IN4L_CONTROL,
-          ARIZONA_IN4_OSR_SHIFT, 1, 0),
+SOC_ENUM("IN1 OSR", arizona_in_dmic_osr[0]),
+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),
@@ -432,6 +428,9 @@ SND_SOC_DAPM_INPUT("IN3R"),
 SND_SOC_DAPM_INPUT("IN4L"),
 SND_SOC_DAPM_INPUT("IN4R"),
 
+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,
                   SND_SOC_DAPM_PRE_PMD | SND_SOC_DAPM_POST_PMD |
@@ -842,9 +841,6 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
        { "Tone Generator 1", NULL, "TONE" },
        { "Tone Generator 2", NULL, "TONE" },
 
-       { "Mic Mute Mixer", NULL, "Noise Mixer" },
-       { "Mic Mute Mixer", NULL, "Mic Mixer" },
-
        { "AIF1 Capture", NULL, "AIF1TX1" },
        { "AIF1 Capture", NULL, "AIF1TX2" },
        { "AIF1 Capture", NULL, "AIF1TX3" },
@@ -979,10 +975,13 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
        ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
        ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
 
-       ARIZONA_MUX_ROUTES("ASRC1L"),
-       ARIZONA_MUX_ROUTES("ASRC1R"),
-       ARIZONA_MUX_ROUTES("ASRC2L"),
-       ARIZONA_MUX_ROUTES("ASRC2R"),
+       ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"),
+       ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
+
+       ARIZONA_MUX_ROUTES("ASRC1L", "ASRC1L"),
+       ARIZONA_MUX_ROUTES("ASRC1R", "ASRC1R"),
+       ARIZONA_MUX_ROUTES("ASRC2L", "ASRC2L"),
+       ARIZONA_MUX_ROUTES("ASRC2R", "ASRC2R"),
 
        { "HPOUT1L", NULL, "OUT1L" },
        { "HPOUT1R", NULL, "OUT1R" },
@@ -1006,6 +1005,11 @@ static const struct snd_soc_dapm_route wm5110_dapm_routes[] = {
        { "SPKDAT2R", NULL, "OUT6R" },
 
        { "MICSUPP", NULL, "SYSCLK" },
+
+       { "DRC1 Signal Activity", NULL, "DRC1L" },
+       { "DRC1 Signal Activity", NULL, "DRC1R" },
+       { "DRC2 Signal Activity", NULL, "DRC2L" },
+       { "DRC2 Signal Activity", NULL, "DRC2R" },
 };
 
 static int wm5110_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
@@ -1170,6 +1174,7 @@ static int wm5110_codec_probe(struct snd_soc_codec *codec)
                return ret;
 
        arizona_init_spk(codec);
+       arizona_init_gpio(codec);
 
        snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
 
index 0e8b3aaf6c8d698e9dad1f25fdccbabc9ee9f1ef..af1318ddb0620e8425b5606a3d794c2b57918078 100644 (file)
@@ -1301,7 +1301,8 @@ static irqreturn_t wm8350_hpl_jack_handler(int irq, void *data)
        if (device_may_wakeup(wm8350->dev))
                pm_wakeup_event(wm8350->dev, 250);
 
-       schedule_delayed_work(&priv->hpl.work, msecs_to_jiffies(200));
+       queue_delayed_work(system_power_efficient_wq,
+                          &priv->hpl.work, msecs_to_jiffies(200));
 
        return IRQ_HANDLED;
 }
@@ -1318,7 +1319,8 @@ static irqreturn_t wm8350_hpr_jack_handler(int irq, void *data)
        if (device_may_wakeup(wm8350->dev))
                pm_wakeup_event(wm8350->dev, 250);
 
-       schedule_delayed_work(&priv->hpr.work, msecs_to_jiffies(200));
+       queue_delayed_work(system_power_efficient_wq,
+                          &priv->hpr.work, msecs_to_jiffies(200));
 
        return IRQ_HANDLED;
 }
index 462f5e4d5c05ffcc3c4876d35bca320ff1743afb..7b1a6d5c11c6d63eef94cda1e545f28625f7f786 100644 (file)
 #include <sound/initval.h>
 #include <sound/soc.h>
 
+static const struct snd_soc_dapm_widget wm8727_dapm_widgets[] = {
+SND_SOC_DAPM_OUTPUT("VOUTL"),
+SND_SOC_DAPM_OUTPUT("VOUTR"),
+};
+
+static const struct snd_soc_dapm_route wm8727_dapm_routes[] = {
+       { "VOUTL", NULL, "Playback" },
+       { "VOUTR", NULL, "Playback" },
+};
+
 /*
  * Note this is a simple chip with no configuration interface, sample rate is
  * determined automatically by examining the Master clock and Bit clock ratios
@@ -43,7 +53,12 @@ static struct snd_soc_dai_driver wm8727_dai = {
                },
 };
 
-static struct snd_soc_codec_driver soc_codec_dev_wm8727;
+static struct snd_soc_codec_driver soc_codec_dev_wm8727 = {
+       .dapm_widgets = wm8727_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(wm8727_dapm_widgets),
+       .dapm_routes = wm8727_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(wm8727_dapm_routes),
+};
 
 static int wm8727_probe(struct platform_device *pdev)
 {
index 5276062d6c79fa59d0c2c0992a5f9b52993fa156..456bb8c6d759176a3bf31c5f7003a6e8cfc2d799 100644 (file)
@@ -45,6 +45,7 @@ static const char *wm8731_supply_names[WM8731_NUM_SUPPLIES] = {
 struct wm8731_priv {
        struct regmap *regmap;
        struct regulator_bulk_data supplies[WM8731_NUM_SUPPLIES];
+       const struct snd_pcm_hw_constraint_list *constraints;
        unsigned int sysclk;
        int sysclk_type;
        int playback_fs;
@@ -290,6 +291,36 @@ static const struct _coeff_div coeff_div[] = {
        {12000000, 88200, 136, 0xf, 0x1, 0x1},
 };
 
+/* rates constraints */
+static const unsigned int wm8731_rates_12000000[] = {
+       8000, 32000, 44100, 48000, 96000, 88200,
+};
+
+static const unsigned int wm8731_rates_12288000_18432000[] = {
+       8000, 32000, 48000, 96000,
+};
+
+static const unsigned int wm8731_rates_11289600_16934400[] = {
+       8000, 44100, 88200,
+};
+
+static const struct snd_pcm_hw_constraint_list wm8731_constraints_12000000 = {
+       .list = wm8731_rates_12000000,
+       .count = ARRAY_SIZE(wm8731_rates_12000000),
+};
+
+static const
+struct snd_pcm_hw_constraint_list wm8731_constraints_12288000_18432000 = {
+       .list = wm8731_rates_12288000_18432000,
+       .count = ARRAY_SIZE(wm8731_rates_12288000_18432000),
+};
+
+static const
+struct snd_pcm_hw_constraint_list wm8731_constraints_11289600_16934400 = {
+       .list = wm8731_rates_11289600_16934400,
+       .count = ARRAY_SIZE(wm8731_rates_11289600_16934400),
+};
+
 static inline int get_coeff(int mclk, int rate)
 {
        int i;
@@ -362,17 +393,26 @@ static int wm8731_set_dai_sysclk(struct snd_soc_dai *codec_dai,
        }
 
        switch (freq) {
-       case 11289600:
+       case 0:
+               wm8731->constraints = NULL;
+               break;
        case 12000000:
+               wm8731->constraints = &wm8731_constraints_12000000;
+               break;
        case 12288000:
-       case 16934400:
        case 18432000:
-               wm8731->sysclk = freq;
+               wm8731->constraints = &wm8731_constraints_12288000_18432000;
+               break;
+       case 16934400:
+       case 11289600:
+               wm8731->constraints = &wm8731_constraints_11289600_16934400;
                break;
        default:
                return -EINVAL;
        }
 
+       wm8731->sysclk = freq;
+
        snd_soc_dapm_sync(&codec->dapm);
 
        return 0;
@@ -475,12 +515,26 @@ static int wm8731_set_bias_level(struct snd_soc_codec *codec,
        return 0;
 }
 
+static int wm8731_startup(struct snd_pcm_substream *substream,
+       struct snd_soc_dai *dai)
+{
+       struct wm8731_priv *wm8731 = snd_soc_codec_get_drvdata(dai->codec);
+
+       if (wm8731->constraints)
+               snd_pcm_hw_constraint_list(substream->runtime, 0,
+                                          SNDRV_PCM_HW_PARAM_RATE,
+                                          wm8731->constraints);
+
+       return 0;
+}
+
 #define WM8731_RATES SNDRV_PCM_RATE_8000_96000
 
 #define WM8731_FORMATS (SNDRV_PCM_FMTBIT_S16_LE | SNDRV_PCM_FMTBIT_S20_3LE |\
        SNDRV_PCM_FMTBIT_S24_LE)
 
 static const struct snd_soc_dai_ops wm8731_dai_ops = {
+       .startup        = wm8731_startup,
        .hw_params      = wm8731_hw_params,
        .digital_mute   = wm8731_mute,
        .set_sysclk     = wm8731_set_dai_sysclk,
index 0a4ab4c423d123f0bf1ce0e16ed6d261c4b5a52d..d96ebf52d953e850016184f4615f2faf979a738d 100644 (file)
@@ -1456,8 +1456,9 @@ static int wm8753_resume(struct snd_soc_codec *codec)
        if (codec->dapm.suspend_bias_level == SND_SOC_BIAS_ON) {
                wm8753_set_bias_level(codec, SND_SOC_BIAS_PREPARE);
                codec->dapm.bias_level = SND_SOC_BIAS_ON;
-               schedule_delayed_work(&codec->dapm.delayed_work,
-                       msecs_to_jiffies(caps_charge));
+               queue_delayed_work(system_power_efficient_wq,
+                                  &codec->dapm.delayed_work,
+                                  msecs_to_jiffies(caps_charge));
        }
 
        return 0;
index f1fdbf63abb4f41e4e8c1a69b92e1357f3d6df00..8092495605ce3ff147c762d452963f2d8f41c3b8 100644 (file)
 #include <sound/initval.h>
 #include <sound/soc.h>
 
+static const struct snd_soc_dapm_widget wm8782_dapm_widgets[] = {
+SND_SOC_DAPM_INPUT("AINL"),
+SND_SOC_DAPM_INPUT("AINR"),
+};
+
+static const struct snd_soc_dapm_route wm8782_dapm_routes[] = {
+       { "Capture", NULL, "AINL" },
+       { "Capture", NULL, "AINR" },
+};
+
 static struct snd_soc_dai_driver wm8782_dai = {
        .name = "wm8782",
        .capture = {
@@ -40,7 +50,12 @@ static struct snd_soc_dai_driver wm8782_dai = {
        },
 };
 
-static struct snd_soc_codec_driver soc_codec_dev_wm8782;
+static struct snd_soc_codec_driver soc_codec_dev_wm8782 = {
+       .dapm_widgets = wm8782_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(wm8782_dapm_widgets),
+       .dapm_routes = wm8782_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(wm8782_dapm_routes),
+};
 
 static int wm8782_probe(struct platform_device *pdev)
 {
index fa24cedee68769f2b723e2fb8b25c03869ee286a..eebcb1da3b7b10769f0e265f95c90a6638266f71 100644 (file)
@@ -364,9 +364,7 @@ static void wm8903_seq_notifier(struct snd_soc_dapm_context *dapm,
 static int wm8903_class_w_put(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-       struct snd_soc_codec *codec = widget->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
        struct wm8903_priv *wm8903 = snd_soc_codec_get_drvdata(codec);
        u16 reg;
        int ret;
index 4c9fb142cb2d21d18ac90dd1b6e0272bf48c7e24..91dfbfeda6f81763c8afff38cc567b93b33bb218 100644 (file)
@@ -1012,7 +1012,7 @@ static const struct soc_enum liner_enum =
        SOC_ENUM_SINGLE(WM8904_ANALOGUE_OUT12_ZC, 0, 2, out_mux_text);
 
 static const struct snd_kcontrol_new liner_mux =
-       SOC_DAPM_ENUM("LINEL Mux", liner_enum);
+       SOC_DAPM_ENUM("LINER Mux", liner_enum);
 
 static const char *sidetone_text[] = {
        "None", "Left", "Right"
index 0a4ffdd1d2a70eb19c22445365d0acc5cc854dee..f156010e52bc1ae5f206e1ddcf1b3a62aa1c1f2d 100644 (file)
@@ -263,8 +263,8 @@ SOC_SINGLE("ALC Attack", WM8960_ALC3, 0, 15, 0),
 SOC_SINGLE("Noise Gate Threshold", WM8960_NOISEG, 3, 31, 0),
 SOC_SINGLE("Noise Gate Switch", WM8960_NOISEG, 0, 1, 0),
 
-SOC_DOUBLE_R("ADC PCM Capture Volume", WM8960_LINPATH, WM8960_RINPATH,
-       0, 127, 0),
+SOC_DOUBLE_R_TLV("ADC PCM Capture Volume", WM8960_LADC, WM8960_RADC,
+       0, 255, 0, adc_tlv),
 
 SOC_SINGLE_TLV("Left Output Mixer Boost Bypass Volume",
               WM8960_BYPASS1, 4, 7, 1, bypass_tlv),
@@ -857,9 +857,9 @@ static int wm8960_set_dai_pll(struct snd_soc_dai *codec_dai, int pll_id,
        if (pll_div.k) {
                reg |= 0x20;
 
-               snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 18) & 0x3f);
-               snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 9) & 0x1ff);
-               snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0x1ff);
+               snd_soc_write(codec, WM8960_PLL2, (pll_div.k >> 16) & 0xff);
+               snd_soc_write(codec, WM8960_PLL3, (pll_div.k >> 8) & 0xff);
+               snd_soc_write(codec, WM8960_PLL4, pll_div.k & 0xff);
        }
        snd_soc_write(codec, WM8960_PLL1, reg);
 
index e2de9ecfd6417146dc9976f85017bd34be9aef4b..11d80f3b61372d357870b44432d5e1f1c9f6e25c 100644 (file)
@@ -2621,8 +2621,6 @@ static int wm8962_set_dai_sysclk(struct snd_soc_dai *dai, int clk_id,
 
        wm8962->sysclk_rate = freq;
 
-       wm8962_configure_bclk(codec);
-
        return 0;
 }
 
@@ -3046,8 +3044,9 @@ static irqreturn_t wm8962_irq(int irq, void *data)
 
                pm_wakeup_event(dev, 300);
 
-               schedule_delayed_work(&wm8962->mic_work,
-                                     msecs_to_jiffies(250));
+               queue_delayed_work(system_power_efficient_wq,
+                                  &wm8962->mic_work,
+                                  msecs_to_jiffies(250));
        }
 
        return IRQ_HANDLED;
@@ -3175,7 +3174,7 @@ static ssize_t wm8962_beep_set(struct device *dev,
        long int time;
        int ret;
 
-       ret = strict_strtol(buf, 10, &time);
+       ret = kstrtol(buf, 10, &time);
        if (ret != 0)
                return ret;
 
index ba832b77c543af1cbdd13f2618836186c399f789..86426a117b07923dd4f89aa4811cb5d5dc21b907 100644 (file)
@@ -819,8 +819,9 @@ static int clk_sys_event(struct snd_soc_dapm_widget *w,
                 * don't want false reports.
                 */
                if (wm8994->jackdet && !wm8994->clk_has_run) {
-                       schedule_delayed_work(&wm8994->jackdet_bootstrap,
-                                             msecs_to_jiffies(1000));
+                       queue_delayed_work(system_power_efficient_wq,
+                                          &wm8994->jackdet_bootstrap,
+                                          msecs_to_jiffies(1000));
                        wm8994->clk_has_run = true;
                }
                break;
@@ -1432,14 +1433,12 @@ SOC_DAPM_SINGLE("AIF1.1 Switch", WM8994_DAC2_RIGHT_MIXER_ROUTING,
 
 #define WM8994_CLASS_W_SWITCH(xname, reg, shift, max, invert) \
        SOC_SINGLE_EXT(xname, reg, shift, max, invert, \
-               snd_soc_get_volsw, wm8994_put_class_w)
+               snd_soc_dapm_get_volsw, wm8994_put_class_w)
 
 static int wm8994_put_class_w(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *w = wlist->widgets[0];
-       struct snd_soc_codec *codec = w->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
        int ret;
 
        ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
@@ -3487,7 +3486,8 @@ static irqreturn_t wm8994_mic_irq(int irq, void *data)
 
        pm_wakeup_event(codec->dev, 300);
 
-       schedule_delayed_work(&priv->mic_work, msecs_to_jiffies(250));
+       queue_delayed_work(system_power_efficient_wq,
+                          &priv->mic_work, msecs_to_jiffies(250));
 
        return IRQ_HANDLED;
 }
@@ -3575,8 +3575,9 @@ static void wm8958_mic_id(void *data, u16 status)
                /* If nothing present then clear our statuses */
                dev_dbg(codec->dev, "Detected open circuit\n");
 
-               schedule_delayed_work(&wm8994->open_circuit_work,
-                                     msecs_to_jiffies(2500));
+               queue_delayed_work(system_power_efficient_wq,
+                                  &wm8994->open_circuit_work,
+                                  msecs_to_jiffies(2500));
                return;
        }
 
@@ -3690,8 +3691,9 @@ static irqreturn_t wm1811_jackdet_irq(int irq, void *data)
                                    WM1811_JACKDET_DB, 0);
 
                delay = control->pdata.micdet_delay;
-               schedule_delayed_work(&wm8994->mic_work,
-                                     msecs_to_jiffies(delay));
+               queue_delayed_work(system_power_efficient_wq,
+                                  &wm8994->mic_work,
+                                  msecs_to_jiffies(delay));
        } else {
                dev_dbg(codec->dev, "Jack not detected\n");
 
@@ -3936,8 +3938,9 @@ static irqreturn_t wm8958_mic_irq(int irq, void *data)
        id_delay = wm8994->wm8994->pdata.mic_id_delay;
 
        if (wm8994->mic_detecting)
-               schedule_delayed_work(&wm8994->mic_complete_work,
-                                     msecs_to_jiffies(id_delay));
+               queue_delayed_work(system_power_efficient_wq,
+                                  &wm8994->mic_complete_work,
+                                  msecs_to_jiffies(id_delay));
        else
                wm8958_button_det(codec, reg);
 
@@ -4010,9 +4013,6 @@ static int wm8994_codec_probe(struct snd_soc_codec *codec)
 
        wm8994->micdet_irq = control->pdata.micdet_irq;
 
-       pm_runtime_enable(codec->dev);
-       pm_runtime_idle(codec->dev);
-
        /* By default use idle_bias_off, will override for WM8994 */
        codec->dapm.idle_bias_off = 1;
 
@@ -4385,8 +4385,6 @@ static int wm8994_codec_remove(struct snd_soc_codec *codec)
 
        wm8994_set_bias_level(codec, SND_SOC_BIAS_OFF);
 
-       pm_runtime_disable(codec->dev);
-
        for (i = 0; i < ARRAY_SIZE(wm8994->fll_locked); i++)
                wm8994_free_irq(wm8994->wm8994, WM8994_IRQ_FLL1_LOCK + i,
                                &wm8994->fll_locked[i]);
@@ -4445,6 +4443,9 @@ static int wm8994_probe(struct platform_device *pdev)
 
        wm8994->wm8994 = dev_get_drvdata(pdev->dev.parent);
 
+       pm_runtime_enable(&pdev->dev);
+       pm_runtime_idle(&pdev->dev);
+
        return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8994,
                        wm8994_dai, ARRAY_SIZE(wm8994_dai));
 }
@@ -4452,6 +4453,8 @@ static int wm8994_probe(struct platform_device *pdev)
 static int wm8994_remove(struct platform_device *pdev)
 {
        snd_soc_unregister_codec(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+
        return 0;
 }
 
index 90a65c427541fb9290616379f7b53bc17c01d0ea..da2899e6c4018fae6cd0a2411b58027f4048b6e4 100644 (file)
@@ -549,12 +549,9 @@ static int check_clk_sys(struct snd_soc_dapm_widget *source,
 static int wm8995_put_class_w(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *w = wlist->widgets[0];
-       struct snd_soc_codec *codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
        int ret;
 
-       codec = w->codec;
        ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
        wm8995_update_class_w(codec);
        return ret;
diff --git a/sound/soc/codecs/wm8997.c b/sound/soc/codecs/wm8997.c
new file mode 100644 (file)
index 0000000..6ec3de3
--- /dev/null
@@ -0,0 +1,1175 @@
+/*
+ * wm8997.c  --  WM8997 ALSA SoC Audio driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Charles Keepax <ckeepax@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 "wm8997.h"
+
+struct wm8997_priv {
+       struct arizona_priv core;
+       struct arizona_fll fll[2];
+};
+
+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(noise_tlv, 0, 600, 0);
+static DECLARE_TLV_DB_SCALE(ng_tlv, -10200, 600, 0);
+
+static const struct reg_default wm8997_sysclk_reva_patch[] = {
+       { 0x301D, 0x7B15 },
+       { 0x301B, 0x0050 },
+       { 0x305D, 0x7B17 },
+       { 0x305B, 0x0050 },
+       { 0x3001, 0x08FE },
+       { 0x3003, 0x00F4 },
+       { 0x3041, 0x08FF },
+       { 0x3043, 0x0005 },
+       { 0x3020, 0x0225 },
+       { 0x3021, 0x0A00 },
+       { 0x3022, 0xE24D },
+       { 0x3023, 0x0800 },
+       { 0x3024, 0xE24D },
+       { 0x3025, 0xF000 },
+       { 0x3060, 0x0226 },
+       { 0x3061, 0x0A00 },
+       { 0x3062, 0xE252 },
+       { 0x3063, 0x0800 },
+       { 0x3064, 0xE252 },
+       { 0x3065, 0xF000 },
+       { 0x3116, 0x022B },
+       { 0x3117, 0xFA00 },
+       { 0x3110, 0x246C },
+       { 0x3111, 0x0A03 },
+       { 0x3112, 0x246E },
+       { 0x3113, 0x0A03 },
+       { 0x3114, 0x2470 },
+       { 0x3115, 0x0A03 },
+       { 0x3126, 0x246C },
+       { 0x3127, 0x0A02 },
+       { 0x3128, 0x246E },
+       { 0x3129, 0x0A02 },
+       { 0x312A, 0x2470 },
+       { 0x312B, 0xFA02 },
+       { 0x3125, 0x0800 },
+};
+
+static int wm8997_sysclk_ev(struct snd_soc_dapm_widget *w,
+                           struct snd_kcontrol *kcontrol, int event)
+{
+       struct snd_soc_codec *codec = w->codec;
+       struct arizona *arizona = dev_get_drvdata(codec->dev->parent);
+       struct regmap *regmap = codec->control_data;
+       const struct reg_default *patch = NULL;
+       int i, patch_size;
+
+       switch (arizona->rev) {
+       case 0:
+               patch = wm8997_sysclk_reva_patch;
+               patch_size = ARRAY_SIZE(wm8997_sysclk_reva_patch);
+               break;
+       default:
+               break;
+       }
+
+       switch (event) {
+       case SND_SOC_DAPM_POST_PMU:
+               if (patch)
+                       for (i = 0; i < patch_size; i++)
+                               regmap_write(regmap, patch[i].reg,
+                                            patch[i].def);
+               break;
+       default:
+               break;
+       }
+
+       return 0;
+}
+
+static const char *wm8997_osr_text[] = {
+       "Low power", "Normal", "High performance",
+};
+
+static const unsigned int wm8997_osr_val[] = {
+       0x0, 0x3, 0x5,
+};
+
+static const struct soc_enum wm8997_hpout_osr[] = {
+       SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_1L,
+                             ARIZONA_OUT1_OSR_SHIFT, 0x7, 3,
+                             wm8997_osr_text, wm8997_osr_val),
+       SOC_VALUE_ENUM_SINGLE(ARIZONA_OUTPUT_PATH_CONFIG_3L,
+                             ARIZONA_OUT3_OSR_SHIFT, 0x7, 3,
+                             wm8997_osr_text, wm8997_osr_val),
+};
+
+#define WM8997_NG_SRC(name, base) \
+       SOC_SINGLE(name " NG HPOUT1L Switch",  base, 0, 1, 0), \
+       SOC_SINGLE(name " NG HPOUT1R Switch",  base, 1, 1, 0), \
+       SOC_SINGLE(name " NG EPOUT Switch",    base, 4, 1, 0), \
+       SOC_SINGLE(name " NG SPKOUT Switch",   base, 6, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT1L Switch", base, 8, 1, 0), \
+       SOC_SINGLE(name " NG SPKDAT1R Switch", base, 9, 1, 0)
+
+static const struct snd_kcontrol_new wm8997_snd_controls[] = {
+SOC_SINGLE("IN1 High Performance Switch", ARIZONA_IN1L_CONTROL,
+          ARIZONA_IN1_OSR_SHIFT, 1, 0),
+SOC_SINGLE("IN2 High Performance Switch", ARIZONA_IN2L_CONTROL,
+          ARIZONA_IN2_OSR_SHIFT, 1, 0),
+
+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_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("IN2L Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2L,
+              ARIZONA_IN2L_DIG_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_SINGLE_TLV("IN2R Digital Volume", ARIZONA_ADC_DIGITAL_VOLUME_2R,
+              ARIZONA_IN2R_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_MIXER_CONTROLS("EQ1", ARIZONA_EQ1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ2", ARIZONA_EQ2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ3", ARIZONA_EQ3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EQ4", ARIZONA_EQ4MIX_INPUT_1_SOURCE),
+
+SND_SOC_BYTES_MASK("EQ1 Coefficeints", ARIZONA_EQ1_1, 21,
+                  ARIZONA_EQ1_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ2 Coefficeints", ARIZONA_EQ2_1, 21,
+                  ARIZONA_EQ2_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ3 Coefficeints", ARIZONA_EQ3_1, 21,
+                  ARIZONA_EQ3_ENA_MASK),
+SND_SOC_BYTES_MASK("EQ4 Coefficeints", ARIZONA_EQ4_1, 21,
+                  ARIZONA_EQ4_ENA_MASK),
+
+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),
+
+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),
+
+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),
+
+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_MIXER_CONTROLS("DRC1L", ARIZONA_DRC1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_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),
+
+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),
+
+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_VALUE_ENUM("ISRC1 FSL", arizona_isrc_fsl[0]),
+SOC_VALUE_ENUM("ISRC2 FSL", arizona_isrc_fsl[1]),
+
+ARIZONA_MIXER_CONTROLS("Mic", ARIZONA_MICMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("Noise", ARIZONA_NOISEMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE_TLV("Noise Generator Volume", ARIZONA_COMFORT_NOISE_GENERATOR,
+              ARIZONA_NOISE_GEN_GAIN_SHIFT, 0x16, 0, noise_tlv),
+
+ARIZONA_MIXER_CONTROLS("HPOUT1L", ARIZONA_OUT1LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("HPOUT1R", ARIZONA_OUT1RMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("EPOUT", ARIZONA_OUT3LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKOUT", ARIZONA_OUT4LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1L", ARIZONA_OUT5LMIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SPKDAT1R", ARIZONA_OUT5RMIX_INPUT_1_SOURCE),
+
+SOC_SINGLE("Speaker High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_4L,
+          ARIZONA_OUT4_OSR_SHIFT, 1, 0),
+SOC_SINGLE("SPKDAT1 High Performance Switch", ARIZONA_OUTPUT_PATH_CONFIG_5L,
+          ARIZONA_OUT5_OSR_SHIFT, 1, 0),
+
+SOC_DOUBLE_R("HPOUT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+            ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("EPOUT Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_3L,
+          ARIZONA_OUT3L_MUTE_SHIFT, 1, 1),
+SOC_SINGLE("Speaker Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+          ARIZONA_OUT4L_MUTE_SHIFT, 1, 1),
+SOC_DOUBLE_R("SPKDAT1 Digital Switch", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+            ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_MUTE_SHIFT, 1, 1),
+
+SOC_DOUBLE_R_TLV("HPOUT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_1L,
+                ARIZONA_DAC_DIGITAL_VOLUME_1R, ARIZONA_OUT1L_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_SINGLE_TLV("Speaker Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_4L,
+              ARIZONA_OUT4L_VOL_SHIFT, 0xbf, 0, digital_tlv),
+SOC_DOUBLE_R_TLV("SPKDAT1 Digital Volume", ARIZONA_DAC_DIGITAL_VOLUME_5L,
+                ARIZONA_DAC_DIGITAL_VOLUME_5R, ARIZONA_OUT5L_VOL_SHIFT,
+                0xbf, 0, digital_tlv),
+
+SOC_VALUE_ENUM("HPOUT1 OSR", wm8997_hpout_osr[0]),
+SOC_VALUE_ENUM("EPOUT OSR", wm8997_hpout_osr[1]),
+
+SOC_ENUM("Output Ramp Up", arizona_out_vi_ramp),
+SOC_ENUM("Output Ramp Down", arizona_out_vd_ramp),
+
+SOC_DOUBLE("SPKDAT1 Switch", ARIZONA_PDM_SPK1_CTRL_1, ARIZONA_SPK1L_MUTE_SHIFT,
+          ARIZONA_SPK1R_MUTE_SHIFT, 1, 1),
+
+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),
+
+WM8997_NG_SRC("HPOUT1L", ARIZONA_NOISE_GATE_SELECT_1L),
+WM8997_NG_SRC("HPOUT1R", ARIZONA_NOISE_GATE_SELECT_1R),
+WM8997_NG_SRC("EPOUT", ARIZONA_NOISE_GATE_SELECT_3L),
+WM8997_NG_SRC("SPKOUT", ARIZONA_NOISE_GATE_SELECT_4L),
+WM8997_NG_SRC("SPKDAT1L", ARIZONA_NOISE_GATE_SELECT_5L),
+WM8997_NG_SRC("SPKDAT1R", 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("AIF1TX7", ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF1TX8", ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("AIF2TX1", ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("AIF2TX2", ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE),
+
+ARIZONA_MIXER_CONTROLS("SLIMTX1", ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX2", ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX3", ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX4", ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX5", ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX6", ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX7", ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE),
+ARIZONA_MIXER_CONTROLS("SLIMTX8", ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE),
+};
+
+ARIZONA_MIXER_ENUMS(EQ1, ARIZONA_EQ1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ2, ARIZONA_EQ2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ3, ARIZONA_EQ3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(EQ4, ARIZONA_EQ4MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(DRC1L, ARIZONA_DRC1LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_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(Mic, ARIZONA_MICMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(Noise, ARIZONA_NOISEMIX_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(OUT3, ARIZONA_OUT3LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKOUT, ARIZONA_OUT4LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1L, ARIZONA_OUT5LMIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SPKDAT1R, 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(AIF1TX7, ARIZONA_AIF1TX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF1TX8, ARIZONA_AIF1TX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(AIF2TX1, ARIZONA_AIF2TX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(AIF2TX2, ARIZONA_AIF2TX2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MIXER_ENUMS(SLIMTX1, ARIZONA_SLIMTX1MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX2, ARIZONA_SLIMTX2MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX3, ARIZONA_SLIMTX3MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX4, ARIZONA_SLIMTX4MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX5, ARIZONA_SLIMTX5MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX6, ARIZONA_SLIMTX6MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX7, ARIZONA_SLIMTX7MIX_INPUT_1_SOURCE);
+ARIZONA_MIXER_ENUMS(SLIMTX8, ARIZONA_SLIMTX8MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1INT1, ARIZONA_ISRC1INT1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1INT2, ARIZONA_ISRC1INT2MIX_INPUT_1_SOURCE);
+
+ARIZONA_MUX_ENUMS(ISRC1DEC1, ARIZONA_ISRC1DEC1MIX_INPUT_1_SOURCE);
+ARIZONA_MUX_ENUMS(ISRC1DEC2, ARIZONA_ISRC1DEC2MIX_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 *wm8997_aec_loopback_texts[] = {
+       "HPOUT1L", "HPOUT1R", "EPOUT", "SPKOUT", "SPKDAT1L", "SPKDAT1R",
+};
+
+static const unsigned int wm8997_aec_loopback_values[] = {
+       0, 1, 4, 6, 8, 9,
+};
+
+static const struct soc_enum wm8997_aec_loopback =
+       SOC_VALUE_ENUM_SINGLE(ARIZONA_DAC_AEC_CONTROL_1,
+                             ARIZONA_AEC_LOOPBACK_SRC_SHIFT, 0xf,
+                             ARRAY_SIZE(wm8997_aec_loopback_texts),
+                             wm8997_aec_loopback_texts,
+                             wm8997_aec_loopback_values);
+
+static const struct snd_kcontrol_new wm8997_aec_loopback_mux =
+       SOC_DAPM_VALUE_ENUM("AEC Loopback", wm8997_aec_loopback);
+
+static const struct snd_soc_dapm_widget wm8997_dapm_widgets[] = {
+SND_SOC_DAPM_SUPPLY("SYSCLK", ARIZONA_SYSTEM_CLOCK_1, ARIZONA_SYSCLK_ENA_SHIFT,
+                   0, wm8997_sysclk_ev, SND_SOC_DAPM_POST_PMU),
+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("CPVDD", 20, 0),
+SND_SOC_DAPM_REGULATOR_SUPPLY("MICVDD", 0, SND_SOC_DAPM_REGULATOR_BYPASS),
+SND_SOC_DAPM_REGULATOR_SUPPLY("SPKVDD", 0, 0),
+
+SND_SOC_DAPM_SIGGEN("TONE"),
+SND_SOC_DAPM_SIGGEN("NOISE"),
+SND_SOC_DAPM_SIGGEN("HAPTICS"),
+
+SND_SOC_DAPM_INPUT("IN1L"),
+SND_SOC_DAPM_INPUT("IN1R"),
+SND_SOC_DAPM_INPUT("IN2L"),
+SND_SOC_DAPM_INPUT("IN2R"),
+
+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("IN2L 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_PGA_E("IN2R PGA", ARIZONA_INPUT_ENABLES, ARIZONA_IN2R_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_MICB2_ENA_SHIFT, 0, NULL, 0),
+SND_SOC_DAPM_SUPPLY("MICBIAS3", ARIZONA_MIC_BIAS_CTRL_3,
+                   ARIZONA_MICB3_ENA_SHIFT, 0, NULL, 0),
+
+SND_SOC_DAPM_PGA("Noise Generator", ARIZONA_COMFORT_NOISE_GENERATOR,
+                ARIZONA_NOISE_GEN_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("Mic Mute Mixer", ARIZONA_MIC_NOISE_MIX_CONTROL_1,
+                ARIZONA_MICMUTE_MIX_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("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("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("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_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_OUT("AIF1TX7", NULL, 0,
+                    ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF1TX8", NULL, 0,
+                    ARIZONA_AIF1_TX_ENABLES, ARIZONA_AIF1TX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_IN("AIF1RX1", NULL, 0,
+                   ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX2", NULL, 0,
+                   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_IN("AIF1RX7", NULL, 0,
+                   ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("AIF1RX8", NULL, 0,
+                   ARIZONA_AIF1_RX_ENABLES, ARIZONA_AIF1RX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_AIF_OUT("AIF2TX1", NULL, 0,
+                    ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX1_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("AIF2TX2", NULL, 0,
+                    ARIZONA_AIF2_TX_ENABLES, ARIZONA_AIF2TX2_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_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("SLIMTX7", NULL, 0,
+                    ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+                    ARIZONA_SLIMTX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_OUT("SLIMTX8", NULL, 0,
+                    ARIZONA_SLIMBUS_TX_CHANNEL_ENABLE,
+                    ARIZONA_SLIMTX8_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_IN("SLIMRX5", NULL, 0,
+                   ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+                   ARIZONA_SLIMRX5_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX6", NULL, 0,
+                   ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+                   ARIZONA_SLIMRX6_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX7", NULL, 0,
+                   ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+                   ARIZONA_SLIMRX7_ENA_SHIFT, 0),
+SND_SOC_DAPM_AIF_IN("SLIMRX8", NULL, 0,
+                   ARIZONA_SLIMBUS_RX_CHANNEL_ENABLE,
+                   ARIZONA_SLIMRX8_ENA_SHIFT, 0),
+
+SND_SOC_DAPM_VALUE_MUX("AEC Loopback", ARIZONA_DAC_AEC_CONTROL_1,
+                      ARIZONA_AEC_LOOPBACK_ENA_SHIFT, 0,
+                      &wm8997_aec_loopback_mux),
+
+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("OUT3L", 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),
+
+ARIZONA_MIXER_WIDGETS(EQ1, "EQ1"),
+ARIZONA_MIXER_WIDGETS(EQ2, "EQ2"),
+ARIZONA_MIXER_WIDGETS(EQ3, "EQ3"),
+ARIZONA_MIXER_WIDGETS(EQ4, "EQ4"),
+
+ARIZONA_MIXER_WIDGETS(DRC1L, "DRC1L"),
+ARIZONA_MIXER_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(Mic, "Mic"),
+ARIZONA_MIXER_WIDGETS(Noise, "Noise"),
+
+ARIZONA_MIXER_WIDGETS(PWM1, "PWM1"),
+ARIZONA_MIXER_WIDGETS(PWM2, "PWM2"),
+
+ARIZONA_MIXER_WIDGETS(OUT1L, "HPOUT1L"),
+ARIZONA_MIXER_WIDGETS(OUT1R, "HPOUT1R"),
+ARIZONA_MIXER_WIDGETS(OUT3, "EPOUT"),
+ARIZONA_MIXER_WIDGETS(SPKOUT, "SPKOUT"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1L, "SPKDAT1L"),
+ARIZONA_MIXER_WIDGETS(SPKDAT1R, "SPKDAT1R"),
+
+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(AIF1TX7, "AIF1TX7"),
+ARIZONA_MIXER_WIDGETS(AIF1TX8, "AIF1TX8"),
+
+ARIZONA_MIXER_WIDGETS(AIF2TX1, "AIF2TX1"),
+ARIZONA_MIXER_WIDGETS(AIF2TX2, "AIF2TX2"),
+
+ARIZONA_MIXER_WIDGETS(SLIMTX1, "SLIMTX1"),
+ARIZONA_MIXER_WIDGETS(SLIMTX2, "SLIMTX2"),
+ARIZONA_MIXER_WIDGETS(SLIMTX3, "SLIMTX3"),
+ARIZONA_MIXER_WIDGETS(SLIMTX4, "SLIMTX4"),
+ARIZONA_MIXER_WIDGETS(SLIMTX5, "SLIMTX5"),
+ARIZONA_MIXER_WIDGETS(SLIMTX6, "SLIMTX6"),
+ARIZONA_MIXER_WIDGETS(SLIMTX7, "SLIMTX7"),
+ARIZONA_MIXER_WIDGETS(SLIMTX8, "SLIMTX8"),
+
+ARIZONA_MUX_WIDGETS(ISRC1DEC1, "ISRC1DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC1DEC2, "ISRC1DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC1INT1, "ISRC1INT1"),
+ARIZONA_MUX_WIDGETS(ISRC1INT2, "ISRC1INT2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2DEC1, "ISRC2DEC1"),
+ARIZONA_MUX_WIDGETS(ISRC2DEC2, "ISRC2DEC2"),
+
+ARIZONA_MUX_WIDGETS(ISRC2INT1, "ISRC2INT1"),
+ARIZONA_MUX_WIDGETS(ISRC2INT2, "ISRC2INT2"),
+
+SND_SOC_DAPM_OUTPUT("HPOUT1L"),
+SND_SOC_DAPM_OUTPUT("HPOUT1R"),
+SND_SOC_DAPM_OUTPUT("EPOUTN"),
+SND_SOC_DAPM_OUTPUT("EPOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKOUTN"),
+SND_SOC_DAPM_OUTPUT("SPKOUTP"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1L"),
+SND_SOC_DAPM_OUTPUT("SPKDAT1R"),
+
+SND_SOC_DAPM_OUTPUT("MICSUPP"),
+};
+
+#define ARIZONA_MIXER_INPUT_ROUTES(name)       \
+       { name, "Noise Generator", "Noise Generator" }, \
+       { name, "Tone Generator 1", "Tone Generator 1" }, \
+       { name, "Tone Generator 2", "Tone Generator 2" }, \
+       { name, "Haptics", "HAPTICS" }, \
+       { name, "AEC", "AEC Loopback" }, \
+       { name, "IN1L", "IN1L PGA" }, \
+       { name, "IN1R", "IN1R PGA" }, \
+       { name, "IN2L", "IN2L PGA" }, \
+       { name, "IN2R", "IN2R PGA" }, \
+       { name, "Mic Mute Mixer", "Mic Mute Mixer" }, \
+       { name, "AIF1RX1", "AIF1RX1" }, \
+       { name, "AIF1RX2", "AIF1RX2" }, \
+       { name, "AIF1RX3", "AIF1RX3" }, \
+       { name, "AIF1RX4", "AIF1RX4" }, \
+       { name, "AIF1RX5", "AIF1RX5" }, \
+       { name, "AIF1RX6", "AIF1RX6" }, \
+       { name, "AIF1RX7", "AIF1RX7" }, \
+       { name, "AIF1RX8", "AIF1RX8" }, \
+       { name, "AIF2RX1", "AIF2RX1" }, \
+       { name, "AIF2RX2", "AIF2RX2" }, \
+       { name, "SLIMRX1", "SLIMRX1" }, \
+       { name, "SLIMRX2", "SLIMRX2" }, \
+       { name, "SLIMRX3", "SLIMRX3" }, \
+       { name, "SLIMRX4", "SLIMRX4" }, \
+       { name, "SLIMRX5", "SLIMRX5" }, \
+       { name, "SLIMRX6", "SLIMRX6" }, \
+       { name, "SLIMRX7", "SLIMRX7" }, \
+       { name, "SLIMRX8", "SLIMRX8" }, \
+       { 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, "ISRC1DEC1", "ISRC1DEC1" }, \
+       { name, "ISRC1DEC2", "ISRC1DEC2" }, \
+       { name, "ISRC1INT1", "ISRC1INT1" }, \
+       { name, "ISRC1INT2", "ISRC1INT2" }, \
+       { name, "ISRC2DEC1", "ISRC2DEC1" }, \
+       { name, "ISRC2DEC2", "ISRC2DEC2" }, \
+       { name, "ISRC2INT1", "ISRC2INT1" }, \
+       { name, "ISRC2INT2", "ISRC2INT2" }
+
+static const struct snd_soc_dapm_route wm8997_dapm_routes[] = {
+       { "AIF2 Capture", NULL, "DBVDD2" },
+       { "AIF2 Playback", NULL, "DBVDD2" },
+
+       { "OUT1L", NULL, "CPVDD" },
+       { "OUT1R", NULL, "CPVDD" },
+       { "OUT3L", NULL, "CPVDD" },
+
+       { "OUT4L", NULL, "SPKVDD" },
+
+       { "OUT1L", NULL, "SYSCLK" },
+       { "OUT1R", NULL, "SYSCLK" },
+       { "OUT3L", NULL, "SYSCLK" },
+       { "OUT4L", NULL, "SYSCLK" },
+
+       { "IN1L", NULL, "SYSCLK" },
+       { "IN1R", NULL, "SYSCLK" },
+       { "IN2L", NULL, "SYSCLK" },
+       { "IN2R", NULL, "SYSCLK" },
+
+       { "MICBIAS1", NULL, "MICVDD" },
+       { "MICBIAS2", NULL, "MICVDD" },
+       { "MICBIAS3", NULL, "MICVDD" },
+
+       { "Noise Generator", NULL, "SYSCLK" },
+       { "Tone Generator 1", NULL, "SYSCLK" },
+       { "Tone Generator 2", NULL, "SYSCLK" },
+
+       { "Noise Generator", NULL, "NOISE" },
+       { "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" },
+       { "AIF1 Capture", NULL, "AIF1TX7" },
+       { "AIF1 Capture", NULL, "AIF1TX8" },
+
+       { "AIF1RX1", NULL, "AIF1 Playback" },
+       { "AIF1RX2", NULL, "AIF1 Playback" },
+       { "AIF1RX3", NULL, "AIF1 Playback" },
+       { "AIF1RX4", NULL, "AIF1 Playback" },
+       { "AIF1RX5", NULL, "AIF1 Playback" },
+       { "AIF1RX6", NULL, "AIF1 Playback" },
+       { "AIF1RX7", NULL, "AIF1 Playback" },
+       { "AIF1RX8", NULL, "AIF1 Playback" },
+
+       { "AIF2 Capture", NULL, "AIF2TX1" },
+       { "AIF2 Capture", NULL, "AIF2TX2" },
+
+       { "AIF2RX1", NULL, "AIF2 Playback" },
+       { "AIF2RX2", NULL, "AIF2 Playback" },
+
+       { "Slim1 Capture", NULL, "SLIMTX1" },
+       { "Slim1 Capture", NULL, "SLIMTX2" },
+       { "Slim1 Capture", NULL, "SLIMTX3" },
+       { "Slim1 Capture", NULL, "SLIMTX4" },
+
+       { "SLIMRX1", NULL, "Slim1 Playback" },
+       { "SLIMRX2", NULL, "Slim1 Playback" },
+       { "SLIMRX3", NULL, "Slim1 Playback" },
+       { "SLIMRX4", NULL, "Slim1 Playback" },
+
+       { "Slim2 Capture", NULL, "SLIMTX5" },
+       { "Slim2 Capture", NULL, "SLIMTX6" },
+
+       { "SLIMRX5", NULL, "Slim2 Playback" },
+       { "SLIMRX6", NULL, "Slim2 Playback" },
+
+       { "Slim3 Capture", NULL, "SLIMTX7" },
+       { "Slim3 Capture", NULL, "SLIMTX8" },
+
+       { "SLIMRX7", NULL, "Slim3 Playback" },
+       { "SLIMRX8", NULL, "Slim3 Playback" },
+
+       { "AIF1 Playback", NULL, "SYSCLK" },
+       { "AIF2 Playback", NULL, "SYSCLK" },
+       { "Slim1 Playback", NULL, "SYSCLK" },
+       { "Slim2 Playback", NULL, "SYSCLK" },
+       { "Slim3 Playback", NULL, "SYSCLK" },
+
+       { "AIF1 Capture", NULL, "SYSCLK" },
+       { "AIF2 Capture", NULL, "SYSCLK" },
+       { "Slim1 Capture", NULL, "SYSCLK" },
+       { "Slim2 Capture", NULL, "SYSCLK" },
+       { "Slim3 Capture", NULL, "SYSCLK" },
+
+       { "IN1L PGA", NULL, "IN1L" },
+       { "IN1R PGA", NULL, "IN1R" },
+
+       { "IN2L PGA", NULL, "IN2L" },
+       { "IN2R PGA", NULL, "IN2R" },
+
+       ARIZONA_MIXER_ROUTES("OUT1L", "HPOUT1L"),
+       ARIZONA_MIXER_ROUTES("OUT1R", "HPOUT1R"),
+       ARIZONA_MIXER_ROUTES("OUT3L", "EPOUT"),
+
+       ARIZONA_MIXER_ROUTES("OUT4L", "SPKOUT"),
+       ARIZONA_MIXER_ROUTES("OUT5L", "SPKDAT1L"),
+       ARIZONA_MIXER_ROUTES("OUT5R", "SPKDAT1R"),
+
+       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("AIF1TX7", "AIF1TX7"),
+       ARIZONA_MIXER_ROUTES("AIF1TX8", "AIF1TX8"),
+
+       ARIZONA_MIXER_ROUTES("AIF2TX1", "AIF2TX1"),
+       ARIZONA_MIXER_ROUTES("AIF2TX2", "AIF2TX2"),
+
+       ARIZONA_MIXER_ROUTES("SLIMTX1", "SLIMTX1"),
+       ARIZONA_MIXER_ROUTES("SLIMTX2", "SLIMTX2"),
+       ARIZONA_MIXER_ROUTES("SLIMTX3", "SLIMTX3"),
+       ARIZONA_MIXER_ROUTES("SLIMTX4", "SLIMTX4"),
+       ARIZONA_MIXER_ROUTES("SLIMTX5", "SLIMTX5"),
+       ARIZONA_MIXER_ROUTES("SLIMTX6", "SLIMTX6"),
+       ARIZONA_MIXER_ROUTES("SLIMTX7", "SLIMTX7"),
+       ARIZONA_MIXER_ROUTES("SLIMTX8", "SLIMTX8"),
+
+       ARIZONA_MIXER_ROUTES("EQ1", "EQ1"),
+       ARIZONA_MIXER_ROUTES("EQ2", "EQ2"),
+       ARIZONA_MIXER_ROUTES("EQ3", "EQ3"),
+       ARIZONA_MIXER_ROUTES("EQ4", "EQ4"),
+
+       ARIZONA_MIXER_ROUTES("DRC1L", "DRC1L"),
+       ARIZONA_MIXER_ROUTES("DRC1R", "DRC1R"),
+
+       ARIZONA_MIXER_ROUTES("LHPF1", "LHPF1"),
+       ARIZONA_MIXER_ROUTES("LHPF2", "LHPF2"),
+       ARIZONA_MIXER_ROUTES("LHPF3", "LHPF3"),
+       ARIZONA_MIXER_ROUTES("LHPF4", "LHPF4"),
+
+       ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Noise"),
+       ARIZONA_MIXER_ROUTES("Mic Mute Mixer", "Mic"),
+
+       ARIZONA_MUX_ROUTES("ISRC1INT1", "ISRC1INT1"),
+       ARIZONA_MUX_ROUTES("ISRC1INT2", "ISRC2INT2"),
+
+       ARIZONA_MUX_ROUTES("ISRC1DEC1", "ISRC1DEC1"),
+       ARIZONA_MUX_ROUTES("ISRC1DEC2", "ISRC1DEC2"),
+
+       ARIZONA_MUX_ROUTES("ISRC2INT1", "ISRC2INT1"),
+       ARIZONA_MUX_ROUTES("ISRC2INT2", "ISRC2INT2"),
+
+       ARIZONA_MUX_ROUTES("ISRC2DEC1", "ISRC2DEC1"),
+       ARIZONA_MUX_ROUTES("ISRC2DEC2", "ISRC2DEC2"),
+
+       { "AEC Loopback", "HPOUT1L", "OUT1L" },
+       { "AEC Loopback", "HPOUT1R", "OUT1R" },
+       { "HPOUT1L", NULL, "OUT1L" },
+       { "HPOUT1R", NULL, "OUT1R" },
+
+       { "AEC Loopback", "EPOUT", "OUT3L" },
+       { "EPOUTN", NULL, "OUT3L" },
+       { "EPOUTP", NULL, "OUT3L" },
+
+       { "AEC Loopback", "SPKOUT", "OUT4L" },
+       { "SPKOUTN", NULL, "OUT4L" },
+       { "SPKOUTP", NULL, "OUT4L" },
+
+       { "AEC Loopback", "SPKDAT1L", "OUT5L" },
+       { "AEC Loopback", "SPKDAT1R", "OUT5R" },
+       { "SPKDAT1L", NULL, "OUT5L" },
+       { "SPKDAT1R", NULL, "OUT5R" },
+
+       { "MICSUPP", NULL, "SYSCLK" },
+};
+
+static int wm8997_set_fll(struct snd_soc_codec *codec, int fll_id, int source,
+                         unsigned int Fref, unsigned int Fout)
+{
+       struct wm8997_priv *wm8997 = snd_soc_codec_get_drvdata(codec);
+
+       switch (fll_id) {
+       case WM8997_FLL1:
+               return arizona_set_fll(&wm8997->fll[0], source, Fref, Fout);
+       case WM8997_FLL2:
+               return arizona_set_fll(&wm8997->fll[1], source, Fref, Fout);
+       case WM8997_FLL1_REFCLK:
+               return arizona_set_fll_refclk(&wm8997->fll[0], source, Fref,
+                                             Fout);
+       case WM8997_FLL2_REFCLK:
+               return arizona_set_fll_refclk(&wm8997->fll[1], source, Fref,
+                                             Fout);
+       default:
+               return -EINVAL;
+       }
+}
+
+#define WM8997_RATES SNDRV_PCM_RATE_8000_192000
+
+#define WM8997_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 wm8997_dai[] = {
+       {
+               .name = "wm8997-aif1",
+               .id = 1,
+               .base = ARIZONA_AIF1_BCLK_CTRL,
+               .playback = {
+                       .stream_name = "AIF1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 8,
+                       .rates = WM8997_RATES,
+                       .formats = WM8997_FORMATS,
+               },
+               .capture = {
+                        .stream_name = "AIF1 Capture",
+                        .channels_min = 1,
+                        .channels_max = 8,
+                        .rates = WM8997_RATES,
+                        .formats = WM8997_FORMATS,
+                },
+               .ops = &arizona_dai_ops,
+               .symmetric_rates = 1,
+       },
+       {
+               .name = "wm8997-aif2",
+               .id = 2,
+               .base = ARIZONA_AIF2_BCLK_CTRL,
+               .playback = {
+                       .stream_name = "AIF2 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = WM8997_RATES,
+                       .formats = WM8997_FORMATS,
+               },
+               .capture = {
+                        .stream_name = "AIF2 Capture",
+                        .channels_min = 1,
+                        .channels_max = 2,
+                        .rates = WM8997_RATES,
+                        .formats = WM8997_FORMATS,
+                },
+               .ops = &arizona_dai_ops,
+               .symmetric_rates = 1,
+       },
+       {
+               .name = "wm8997-slim1",
+               .id = 3,
+               .playback = {
+                       .stream_name = "Slim1 Playback",
+                       .channels_min = 1,
+                       .channels_max = 4,
+                       .rates = WM8997_RATES,
+                       .formats = WM8997_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "Slim1 Capture",
+                       .channels_min = 1,
+                       .channels_max = 4,
+                       .rates = WM8997_RATES,
+                       .formats = WM8997_FORMATS,
+               },
+               .ops = &arizona_simple_dai_ops,
+       },
+       {
+               .name = "wm8997-slim2",
+               .id = 4,
+               .playback = {
+                       .stream_name = "Slim2 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = WM8997_RATES,
+                       .formats = WM8997_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "Slim2 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = WM8997_RATES,
+                       .formats = WM8997_FORMATS,
+               },
+               .ops = &arizona_simple_dai_ops,
+       },
+       {
+               .name = "wm8997-slim3",
+               .id = 5,
+               .playback = {
+                       .stream_name = "Slim3 Playback",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = WM8997_RATES,
+                       .formats = WM8997_FORMATS,
+               },
+               .capture = {
+                       .stream_name = "Slim3 Capture",
+                       .channels_min = 1,
+                       .channels_max = 2,
+                       .rates = WM8997_RATES,
+                       .formats = WM8997_FORMATS,
+               },
+               .ops = &arizona_simple_dai_ops,
+       },
+};
+
+static int wm8997_codec_probe(struct snd_soc_codec *codec)
+{
+       struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
+       int ret;
+
+       codec->control_data = priv->core.arizona->regmap;
+
+       ret = snd_soc_codec_set_cache_io(codec, 32, 16, SND_SOC_REGMAP);
+       if (ret != 0)
+               return ret;
+
+       arizona_init_spk(codec);
+
+       snd_soc_dapm_disable_pin(&codec->dapm, "HAPTICS");
+
+       priv->core.arizona->dapm = &codec->dapm;
+
+       return 0;
+}
+
+static int wm8997_codec_remove(struct snd_soc_codec *codec)
+{
+       struct wm8997_priv *priv = snd_soc_codec_get_drvdata(codec);
+
+       priv->core.arizona->dapm = NULL;
+
+       return 0;
+}
+
+#define WM8997_DIG_VU 0x0200
+
+static unsigned int wm8997_digital_vu[] = {
+       ARIZONA_DAC_DIGITAL_VOLUME_1L,
+       ARIZONA_DAC_DIGITAL_VOLUME_1R,
+       ARIZONA_DAC_DIGITAL_VOLUME_3L,
+       ARIZONA_DAC_DIGITAL_VOLUME_4L,
+       ARIZONA_DAC_DIGITAL_VOLUME_5L,
+       ARIZONA_DAC_DIGITAL_VOLUME_5R,
+};
+
+static struct snd_soc_codec_driver soc_codec_dev_wm8997 = {
+       .probe = wm8997_codec_probe,
+       .remove = wm8997_codec_remove,
+
+       .idle_bias_off = true,
+
+       .set_sysclk = arizona_set_sysclk,
+       .set_pll = wm8997_set_fll,
+
+       .controls = wm8997_snd_controls,
+       .num_controls = ARRAY_SIZE(wm8997_snd_controls),
+       .dapm_widgets = wm8997_dapm_widgets,
+       .num_dapm_widgets = ARRAY_SIZE(wm8997_dapm_widgets),
+       .dapm_routes = wm8997_dapm_routes,
+       .num_dapm_routes = ARRAY_SIZE(wm8997_dapm_routes),
+};
+
+static int wm8997_probe(struct platform_device *pdev)
+{
+       struct arizona *arizona = dev_get_drvdata(pdev->dev.parent);
+       struct wm8997_priv *wm8997;
+       int i;
+
+       wm8997 = devm_kzalloc(&pdev->dev, sizeof(struct wm8997_priv),
+                             GFP_KERNEL);
+       if (wm8997 == NULL)
+               return -ENOMEM;
+       platform_set_drvdata(pdev, wm8997);
+
+       wm8997->core.arizona = arizona;
+       wm8997->core.num_inputs = 4;
+
+       for (i = 0; i < ARRAY_SIZE(wm8997->fll); i++)
+               wm8997->fll[i].vco_mult = 1;
+
+       arizona_init_fll(arizona, 1, ARIZONA_FLL1_CONTROL_1 - 1,
+                        ARIZONA_IRQ_FLL1_LOCK, ARIZONA_IRQ_FLL1_CLOCK_OK,
+                        &wm8997->fll[0]);
+       arizona_init_fll(arizona, 2, ARIZONA_FLL2_CONTROL_1 - 1,
+                        ARIZONA_IRQ_FLL2_LOCK, ARIZONA_IRQ_FLL2_CLOCK_OK,
+                        &wm8997->fll[1]);
+
+       /* SR2 fixed at 8kHz, SR3 fixed at 16kHz */
+       regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_2,
+                          ARIZONA_SAMPLE_RATE_2_MASK, 0x11);
+       regmap_update_bits(arizona->regmap, ARIZONA_SAMPLE_RATE_3,
+                          ARIZONA_SAMPLE_RATE_3_MASK, 0x12);
+
+       for (i = 0; i < ARRAY_SIZE(wm8997_dai); i++)
+               arizona_init_dai(&wm8997->core, i);
+
+       /* Latch volume update bits */
+       for (i = 0; i < ARRAY_SIZE(wm8997_digital_vu); i++)
+               regmap_update_bits(arizona->regmap, wm8997_digital_vu[i],
+                                  WM8997_DIG_VU, WM8997_DIG_VU);
+
+       pm_runtime_enable(&pdev->dev);
+       pm_runtime_idle(&pdev->dev);
+
+       return snd_soc_register_codec(&pdev->dev, &soc_codec_dev_wm8997,
+                                     wm8997_dai, ARRAY_SIZE(wm8997_dai));
+}
+
+static int wm8997_remove(struct platform_device *pdev)
+{
+       snd_soc_unregister_codec(&pdev->dev);
+       pm_runtime_disable(&pdev->dev);
+
+       return 0;
+}
+
+static struct platform_driver wm8997_codec_driver = {
+       .driver = {
+               .name = "wm8997-codec",
+               .owner = THIS_MODULE,
+       },
+       .probe = wm8997_probe,
+       .remove = wm8997_remove,
+};
+
+module_platform_driver(wm8997_codec_driver);
+
+MODULE_DESCRIPTION("ASoC WM8997 driver");
+MODULE_AUTHOR("Charles Keepax <ckeepax@opensource.wolfsonmicro.com>");
+MODULE_LICENSE("GPL");
+MODULE_ALIAS("platform:wm8997-codec");
diff --git a/sound/soc/codecs/wm8997.h b/sound/soc/codecs/wm8997.h
new file mode 100644 (file)
index 0000000..5e91c6a
--- /dev/null
@@ -0,0 +1,23 @@
+/*
+ * wm8997.h  --  WM8997 ALSA SoC Audio driver
+ *
+ * Copyright 2012 Wolfson Microelectronics plc
+ *
+ * Author: Mark Brown <broonie@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 _WM8997_H
+#define _WM8997_H
+
+#include "arizona.h"
+
+#define WM8997_FLL1        1
+#define WM8997_FLL2        2
+#define WM8997_FLL1_REFCLK 3
+#define WM8997_FLL2_REFCLK 4
+
+#endif
index 05252ac936a369cb210a9043105d9d95cb614679..b38f3506418ff0d43dc927fe45424e3d9d998d75 100644 (file)
@@ -225,15 +225,8 @@ struct wm_coeff_ctl_ops {
                     struct snd_ctl_elem_info *uinfo);
 };
 
-struct wm_coeff {
-       struct device *dev;
-       struct list_head ctl_list;
-       struct regmap *regmap;
-};
-
 struct wm_coeff_ctl {
        const char *name;
-       struct snd_card *card;
        struct wm_adsp_alg_region region;
        struct wm_coeff_ctl_ops ops;
        struct wm_adsp *adsp;
@@ -378,7 +371,6 @@ static int wm_coeff_info(struct snd_kcontrol *kcontrol,
 static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
                                  const void *buf, size_t len)
 {
-       struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
        struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
        struct wm_adsp_alg_region *region = &ctl->region;
        const struct wm_adsp_region *mem;
@@ -401,7 +393,7 @@ static int wm_coeff_write_control(struct snd_kcontrol *kcontrol,
        if (!scratch)
                return -ENOMEM;
 
-       ret = regmap_raw_write(wm_coeff->regmap, reg, scratch,
+       ret = regmap_raw_write(adsp->regmap, reg, scratch,
                               ctl->len);
        if (ret) {
                adsp_err(adsp, "Failed to write %zu bytes to %x\n",
@@ -434,7 +426,6 @@ static int wm_coeff_put(struct snd_kcontrol *kcontrol,
 static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
                                 void *buf, size_t len)
 {
-       struct wm_coeff *wm_coeff= snd_kcontrol_chip(kcontrol);
        struct wm_coeff_ctl *ctl = (struct wm_coeff_ctl *)kcontrol->private_value;
        struct wm_adsp_alg_region *region = &ctl->region;
        const struct wm_adsp_region *mem;
@@ -457,7 +448,7 @@ static int wm_coeff_read_control(struct snd_kcontrol *kcontrol,
        if (!scratch)
                return -ENOMEM;
 
-       ret = regmap_raw_read(wm_coeff->regmap, reg, scratch, ctl->len);
+       ret = regmap_raw_read(adsp->regmap, reg, scratch, ctl->len);
        if (ret) {
                adsp_err(adsp, "Failed to read %zu bytes from %x\n",
                         ctl->len, reg);
@@ -481,37 +472,18 @@ static int wm_coeff_get(struct snd_kcontrol *kcontrol,
        return 0;
 }
 
-static int wm_coeff_add_kcontrol(struct wm_coeff *wm_coeff,
-                                struct wm_coeff_ctl *ctl,
-                                const struct snd_kcontrol_new *kctl)
-{
-       int ret;
-       struct snd_kcontrol *kcontrol;
-
-       kcontrol = snd_ctl_new1(kctl, wm_coeff);
-       ret = snd_ctl_add(ctl->card, kcontrol);
-       if (ret < 0) {
-               dev_err(wm_coeff->dev, "Failed to add %s: %d\n",
-                       kctl->name, ret);
-               return ret;
-       }
-       ctl->kcontrol = kcontrol;
-       return 0;
-}
-
 struct wmfw_ctl_work {
-       struct wm_coeff *wm_coeff;
+       struct wm_adsp *adsp;
        struct wm_coeff_ctl *ctl;
        struct work_struct work;
 };
 
-static int wmfw_add_ctl(struct wm_coeff *wm_coeff,
-                       struct wm_coeff_ctl *ctl)
+static int wmfw_add_ctl(struct wm_adsp *adsp, struct wm_coeff_ctl *ctl)
 {
        struct snd_kcontrol_new *kcontrol;
        int ret;
 
-       if (!wm_coeff || !ctl || !ctl->name || !ctl->card)
+       if (!ctl || !ctl->name)
                return -EINVAL;
 
        kcontrol = kzalloc(sizeof(*kcontrol), GFP_KERNEL);
@@ -525,14 +497,17 @@ static int wmfw_add_ctl(struct wm_coeff *wm_coeff,
        kcontrol->put = wm_coeff_put;
        kcontrol->private_value = (unsigned long)ctl;
 
-       ret = wm_coeff_add_kcontrol(wm_coeff,
-                                   ctl, kcontrol);
+       ret = snd_soc_add_card_controls(adsp->card,
+                                       kcontrol, 1);
        if (ret < 0)
                goto err_kcontrol;
 
        kfree(kcontrol);
 
-       list_add(&ctl->list, &wm_coeff->ctl_list);
+       ctl->kcontrol = snd_soc_card_get_kcontrol(adsp->card,
+                                                 ctl->name);
+
+       list_add(&ctl->list, &adsp->ctl_list);
        return 0;
 
 err_kcontrol:
@@ -753,13 +728,12 @@ out:
        return ret;
 }
 
-static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff)
+static int wm_coeff_init_control_caches(struct wm_adsp *adsp)
 {
        struct wm_coeff_ctl *ctl;
        int ret;
 
-       list_for_each_entry(ctl, &wm_coeff->ctl_list,
-                           list) {
+       list_for_each_entry(ctl, &adsp->ctl_list, list) {
                if (!ctl->enabled || ctl->set)
                        continue;
                ret = wm_coeff_read_control(ctl->kcontrol,
@@ -772,13 +746,12 @@ static int wm_coeff_init_control_caches(struct wm_coeff *wm_coeff)
        return 0;
 }
 
-static int wm_coeff_sync_controls(struct wm_coeff *wm_coeff)
+static int wm_coeff_sync_controls(struct wm_adsp *adsp)
 {
        struct wm_coeff_ctl *ctl;
        int ret;
 
-       list_for_each_entry(ctl, &wm_coeff->ctl_list,
-                           list) {
+       list_for_each_entry(ctl, &adsp->ctl_list, list) {
                if (!ctl->enabled)
                        continue;
                if (ctl->set) {
@@ -799,15 +772,14 @@ static void wm_adsp_ctl_work(struct work_struct *work)
                                                      struct wmfw_ctl_work,
                                                      work);
 
-       wmfw_add_ctl(ctl_work->wm_coeff, ctl_work->ctl);
+       wmfw_add_ctl(ctl_work->adsp, ctl_work->ctl);
        kfree(ctl_work);
 }
 
-static int wm_adsp_create_control(struct snd_soc_codec *codec,
+static int wm_adsp_create_control(struct wm_adsp *dsp,
                                  const struct wm_adsp_alg_region *region)
 
 {
-       struct wm_adsp *dsp = snd_soc_codec_get_drvdata(codec);
        struct wm_coeff_ctl *ctl;
        struct wmfw_ctl_work *ctl_work;
        char *name;
@@ -842,7 +814,7 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec,
        snprintf(name, PAGE_SIZE, "DSP%d %s %x",
                 dsp->num, region_name, region->alg);
 
-       list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
+       list_for_each_entry(ctl, &dsp->ctl_list,
                            list) {
                if (!strcmp(ctl->name, name)) {
                        if (!ctl->enabled)
@@ -866,7 +838,6 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec,
        ctl->set = 0;
        ctl->ops.xget = wm_coeff_get;
        ctl->ops.xput = wm_coeff_put;
-       ctl->card = codec->card->snd_card;
        ctl->adsp = dsp;
 
        ctl->len = region->len;
@@ -882,7 +853,7 @@ static int wm_adsp_create_control(struct snd_soc_codec *codec,
                goto err_ctl_cache;
        }
 
-       ctl_work->wm_coeff = dsp->wm_coeff;
+       ctl_work->adsp = dsp;
        ctl_work->ctl = ctl;
        INIT_WORK(&ctl_work->work, wm_adsp_ctl_work);
        schedule_work(&ctl_work->work);
@@ -903,7 +874,7 @@ err_name:
        return ret;
 }
 
-static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
+static int wm_adsp_setup_algs(struct wm_adsp *dsp)
 {
        struct regmap *regmap = dsp->regmap;
        struct wmfw_adsp1_id_hdr adsp1_id;
@@ -1091,7 +1062,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
                        if (i + 1 < algs) {
                                region->len = be32_to_cpu(adsp1_alg[i + 1].dm);
                                region->len -= be32_to_cpu(adsp1_alg[i].dm);
-                               wm_adsp_create_control(codec, region);
+                               wm_adsp_create_control(dsp, region);
                        } else {
                                adsp_warn(dsp, "Missing length info for region DM with ID %x\n",
                                          be32_to_cpu(adsp1_alg[i].alg.id));
@@ -1108,7 +1079,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
                        if (i + 1 < algs) {
                                region->len = be32_to_cpu(adsp1_alg[i + 1].zm);
                                region->len -= be32_to_cpu(adsp1_alg[i].zm);
-                               wm_adsp_create_control(codec, region);
+                               wm_adsp_create_control(dsp, region);
                        } else {
                                adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
                                          be32_to_cpu(adsp1_alg[i].alg.id));
@@ -1137,7 +1108,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
                        if (i + 1 < algs) {
                                region->len = be32_to_cpu(adsp2_alg[i + 1].xm);
                                region->len -= be32_to_cpu(adsp2_alg[i].xm);
-                               wm_adsp_create_control(codec, region);
+                               wm_adsp_create_control(dsp, region);
                        } else {
                                adsp_warn(dsp, "Missing length info for region XM with ID %x\n",
                                          be32_to_cpu(adsp2_alg[i].alg.id));
@@ -1154,7 +1125,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
                        if (i + 1 < algs) {
                                region->len = be32_to_cpu(adsp2_alg[i + 1].ym);
                                region->len -= be32_to_cpu(adsp2_alg[i].ym);
-                               wm_adsp_create_control(codec, region);
+                               wm_adsp_create_control(dsp, region);
                        } else {
                                adsp_warn(dsp, "Missing length info for region YM with ID %x\n",
                                          be32_to_cpu(adsp2_alg[i].alg.id));
@@ -1171,7 +1142,7 @@ static int wm_adsp_setup_algs(struct wm_adsp *dsp, struct snd_soc_codec *codec)
                        if (i + 1 < algs) {
                                region->len = be32_to_cpu(adsp2_alg[i + 1].zm);
                                region->len -= be32_to_cpu(adsp2_alg[i].zm);
-                               wm_adsp_create_control(codec, region);
+                               wm_adsp_create_control(dsp, region);
                        } else {
                                adsp_warn(dsp, "Missing length info for region ZM with ID %x\n",
                                          be32_to_cpu(adsp2_alg[i].alg.id));
@@ -1391,6 +1362,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
        int ret;
        int val;
 
+       dsp->card = codec->card;
+
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
                regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
@@ -1425,7 +1398,7 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
                if (ret != 0)
                        goto err;
 
-               ret = wm_adsp_setup_algs(dsp, codec);
+               ret = wm_adsp_setup_algs(dsp);
                if (ret != 0)
                        goto err;
 
@@ -1434,12 +1407,12 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
                        goto err;
 
                /* Initialize caches for enabled and unset controls */
-               ret = wm_coeff_init_control_caches(dsp->wm_coeff);
+               ret = wm_coeff_init_control_caches(dsp);
                if (ret != 0)
                        goto err;
 
                /* Sync set controls */
-               ret = wm_coeff_sync_controls(dsp->wm_coeff);
+               ret = wm_coeff_sync_controls(dsp);
                if (ret != 0)
                        goto err;
 
@@ -1460,10 +1433,8 @@ int wm_adsp1_event(struct snd_soc_dapm_widget *w,
                regmap_update_bits(dsp->regmap, dsp->base + ADSP1_CONTROL_30,
                                   ADSP1_SYS_ENA, 0);
 
-               list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
-                                   list) {
+               list_for_each_entry(ctl, &dsp->ctl_list, list)
                        ctl->enabled = 0;
-               }
                break;
 
        default:
@@ -1520,6 +1491,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
        unsigned int val;
        int ret;
 
+       dsp->card = codec->card;
+
        switch (event) {
        case SND_SOC_DAPM_POST_PMU:
                /*
@@ -1582,7 +1555,7 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                if (ret != 0)
                        goto err;
 
-               ret = wm_adsp_setup_algs(dsp, codec);
+               ret = wm_adsp_setup_algs(dsp);
                if (ret != 0)
                        goto err;
 
@@ -1591,12 +1564,12 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                        goto err;
 
                /* Initialize caches for enabled and unset controls */
-               ret = wm_coeff_init_control_caches(dsp->wm_coeff);
+               ret = wm_coeff_init_control_caches(dsp);
                if (ret != 0)
                        goto err;
 
                /* Sync set controls */
-               ret = wm_coeff_sync_controls(dsp->wm_coeff);
+               ret = wm_coeff_sync_controls(dsp);
                if (ret != 0)
                        goto err;
 
@@ -1637,10 +1610,8 @@ int wm_adsp2_event(struct snd_soc_dapm_widget *w,
                                        ret);
                }
 
-               list_for_each_entry(ctl, &dsp->wm_coeff->ctl_list,
-                                   list) {
+               list_for_each_entry(ctl, &dsp->ctl_list, list)
                        ctl->enabled = 0;
-               }
 
                while (!list_empty(&dsp->alg_regions)) {
                        alg_region = list_first_entry(&dsp->alg_regions,
@@ -1679,49 +1650,38 @@ int wm_adsp2_init(struct wm_adsp *adsp, bool dvfs)
        }
 
        INIT_LIST_HEAD(&adsp->alg_regions);
-
-       adsp->wm_coeff = kzalloc(sizeof(*adsp->wm_coeff),
-                                GFP_KERNEL);
-       if (!adsp->wm_coeff)
-               return -ENOMEM;
-       adsp->wm_coeff->regmap = adsp->regmap;
-       adsp->wm_coeff->dev = adsp->dev;
-       INIT_LIST_HEAD(&adsp->wm_coeff->ctl_list);
+       INIT_LIST_HEAD(&adsp->ctl_list);
 
        if (dvfs) {
                adsp->dvfs = devm_regulator_get(adsp->dev, "DCVDD");
                if (IS_ERR(adsp->dvfs)) {
                        ret = PTR_ERR(adsp->dvfs);
                        dev_err(adsp->dev, "Failed to get DCVDD: %d\n", ret);
-                       goto out_coeff;
+                       return ret;
                }
 
                ret = regulator_enable(adsp->dvfs);
                if (ret != 0) {
                        dev_err(adsp->dev, "Failed to enable DCVDD: %d\n",
                                ret);
-                       goto out_coeff;
+                       return ret;
                }
 
                ret = regulator_set_voltage(adsp->dvfs, 1200000, 1800000);
                if (ret != 0) {
                        dev_err(adsp->dev, "Failed to initialise DVFS: %d\n",
                                ret);
-                       goto out_coeff;
+                       return ret;
                }
 
                ret = regulator_disable(adsp->dvfs);
                if (ret != 0) {
                        dev_err(adsp->dev, "Failed to disable DCVDD: %d\n",
                                ret);
-                       goto out_coeff;
+                       return ret;
                }
        }
 
        return 0;
-
-out_coeff:
-       kfree(adsp->wm_coeff);
-       return ret;
 }
 EXPORT_SYMBOL_GPL(wm_adsp2_init);
index 9f922c82536c52f8939db5834bbcfda80e1c5b59..d018dea6254de44c5997813fcb9b958eeca09706 100644 (file)
@@ -39,6 +39,7 @@ struct wm_adsp {
        int type;
        struct device *dev;
        struct regmap *regmap;
+       struct snd_soc_card *card;
 
        int base;
        int sysclk_reg;
@@ -57,7 +58,7 @@ struct wm_adsp {
 
        struct regulator *dvfs;
 
-       struct wm_coeff *wm_coeff;
+       struct list_head ctl_list;
 };
 
 #define WM_ADSP1(wname, num) \
index 2d9e099415a58cbe353c99f32f6f089a200db2de..8b50e5958de5a43030ac854d18c046cf903e5841 100644 (file)
@@ -699,9 +699,7 @@ EXPORT_SYMBOL_GPL(wm_hubs_update_class_w);
 static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-       struct snd_soc_codec *codec = widget->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
        int ret;
 
        ret = snd_soc_dapm_put_volsw(kcontrol, ucontrol);
@@ -721,9 +719,7 @@ static int class_w_put_volsw(struct snd_kcontrol *kcontrol,
 static int class_w_put_double(struct snd_kcontrol *kcontrol,
                              struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-       struct snd_soc_codec *codec = widget->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
        int ret;
 
        ret = snd_soc_dapm_put_enum_double(kcontrol, ucontrol);
index 70eb37a5dd16337614abd50462521706b6f7f9b3..25c31f1655f682f3e5c630c08c8b09fdfe2901cc 100644 (file)
@@ -421,13 +421,11 @@ static int dw_i2s_probe(struct platform_device *pdev)
                                         dw_i2s_dai, 1);
        if (ret != 0) {
                dev_err(&pdev->dev, "not able to register dai\n");
-               goto err_set_drvdata;
+               goto err_clk_disable;
        }
 
        return 0;
 
-err_set_drvdata:
-       dev_set_drvdata(&pdev->dev, NULL);
 err_clk_disable:
        clk_disable(dev->clk);
 err_clk_put:
@@ -440,7 +438,6 @@ static int dw_i2s_remove(struct platform_device *pdev)
        struct dw_i2s_dev *dev = dev_get_drvdata(&pdev->dev);
 
        snd_soc_unregister_component(&pdev->dev);
-       dev_set_drvdata(&pdev->dev, NULL);
 
        clk_put(dev->clk);
 
index aa438546c91271f69c26beb1646b85f4b34357cf..cd088cc8c866f1559b2945e5b8df6932eb56856a 100644 (file)
@@ -1,6 +1,9 @@
 config SND_SOC_FSL_SSI
        tristate
 
+config SND_SOC_FSL_SPDIF
+       tristate
+
 config SND_SOC_FSL_UTILS
        tristate
 
@@ -98,7 +101,7 @@ endif # SND_POWERPC_SOC
 
 menuconfig SND_IMX_SOC
        tristate "SoC Audio for Freescale i.MX CPUs"
-       depends on ARCH_MXC
+       depends on ARCH_MXC || COMPILE_TEST
        help
          Say Y or M if you want to add support for codecs attached to
          the i.MX CPUs.
@@ -109,11 +112,11 @@ config SND_SOC_IMX_SSI
        tristate
 
 config SND_SOC_IMX_PCM_FIQ
-       bool
+       tristate
        select FIQ
 
 config SND_SOC_IMX_PCM_DMA
-       bool
+       tristate
        select SND_SOC_GENERIC_DMAENGINE_PCM
 
 config SND_SOC_IMX_AUDMUX
@@ -175,7 +178,6 @@ config SND_SOC_IMX_WM8962
        select SND_SOC_IMX_PCM_DMA
        select SND_SOC_IMX_AUDMUX
        select SND_SOC_FSL_SSI
-       select SND_SOC_FSL_UTILS
        help
          Say Y if you want to add support for SoC audio on an i.MX board with
          a wm8962 codec.
@@ -187,14 +189,13 @@ config SND_SOC_IMX_SGTL5000
        select SND_SOC_IMX_PCM_DMA
        select SND_SOC_IMX_AUDMUX
        select SND_SOC_FSL_SSI
-       select SND_SOC_FSL_UTILS
        help
          Say Y if you want to add support for SoC audio on an i.MX board with
          a sgtl5000 codec.
 
 config SND_SOC_IMX_MC13783
        tristate "SoC Audio support for I.MX boards with mc13783"
-       depends on MFD_MC13783
+       depends on MFD_MC13783 && ARM
        select SND_SOC_IMX_SSI
        select SND_SOC_IMX_AUDMUX
        select SND_SOC_MC13783
index d4b4aa8b5649d3ccefdedc6a51852dafacf2286f..4b5970e014ddde0243dbee0e0c24445988b63200 100644 (file)
@@ -12,9 +12,11 @@ obj-$(CONFIG_SND_SOC_P1022_RDK) += snd-soc-p1022-rdk.o
 
 # Freescale PowerPC SSI/DMA Platform Support
 snd-soc-fsl-ssi-objs := fsl_ssi.o
+snd-soc-fsl-spdif-objs := fsl_spdif.o
 snd-soc-fsl-utils-objs := fsl_utils.o
 snd-soc-fsl-dma-objs := fsl_dma.o
 obj-$(CONFIG_SND_SOC_FSL_SSI) += snd-soc-fsl-ssi.o
+obj-$(CONFIG_SND_SOC_FSL_SPDIF) += snd-soc-fsl-spdif.o
 obj-$(CONFIG_SND_SOC_FSL_UTILS) += snd-soc-fsl-utils.o
 obj-$(CONFIG_SND_SOC_POWERPC_DMA) += snd-soc-fsl-dma.o
 
diff --git a/sound/soc/fsl/fsl_spdif.c b/sound/soc/fsl/fsl_spdif.c
new file mode 100644 (file)
index 0000000..42a4382
--- /dev/null
@@ -0,0 +1,1236 @@
+/*
+ * Freescale S/PDIF ALSA SoC Digital Audio Interface (DAI) driver
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * Based on stmp3xxx_spdif_dai.c
+ * Vladimir Barinov <vbarinov@embeddedalley.com>
+ * Copyright 2008 SigmaTel, Inc
+ * Copyright 2008 Embedded Alley Solutions, Inc
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program  is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#include <linux/module.h>
+#include <linux/clk.h>
+#include <linux/clk-private.h>
+#include <linux/bitrev.h>
+#include <linux/regmap.h>
+#include <linux/of_address.h>
+#include <linux/of_device.h>
+#include <linux/of_irq.h>
+
+#include <sound/asoundef.h>
+#include <sound/soc.h>
+#include <sound/dmaengine_pcm.h>
+
+#include "fsl_spdif.h"
+#include "imx-pcm.h"
+
+#define FSL_SPDIF_TXFIFO_WML   0x8
+#define FSL_SPDIF_RXFIFO_WML   0x8
+
+#define INTR_FOR_PLAYBACK (INT_TXFIFO_RESYNC)
+#define INTR_FOR_CAPTURE (INT_SYM_ERR | INT_BIT_ERR | INT_URX_FUL | INT_URX_OV|\
+               INT_QRX_FUL | INT_QRX_OV | INT_UQ_SYNC | INT_UQ_ERR |\
+               INT_RXFIFO_RESYNC | INT_LOSS_LOCK | INT_DPLL_LOCKED)
+
+/* Index list for the values that has if (DPLL Locked) condition */
+static u8 srpc_dpll_locked[] = { 0x0, 0x1, 0x2, 0x3, 0x4, 0xa, 0xb };
+#define SRPC_NODPLL_START1     0x5
+#define SRPC_NODPLL_START2     0xc
+
+#define DEFAULT_RXCLK_SRC      1
+
+/*
+ * SPDIF control structure
+ * Defines channel status, subcode and Q sub
+ */
+struct spdif_mixer_control {
+       /* spinlock to access control data */
+       spinlock_t ctl_lock;
+
+       /* IEC958 channel tx status bit */
+       unsigned char ch_status[4];
+
+       /* User bits */
+       unsigned char subcode[2 * SPDIF_UBITS_SIZE];
+
+       /* Q subcode part of user bits */
+       unsigned char qsub[2 * SPDIF_QSUB_SIZE];
+
+       /* Buffer offset for U/Q */
+       u32 upos;
+       u32 qpos;
+
+       /* Ready buffer index of the two buffers */
+       u32 ready_buf;
+};
+
+struct fsl_spdif_priv {
+       struct spdif_mixer_control fsl_spdif_control;
+       struct snd_soc_dai_driver cpu_dai_drv;
+       struct platform_device *pdev;
+       struct regmap *regmap;
+       bool dpll_locked;
+       u8 txclk_div[SPDIF_TXRATE_MAX];
+       u8 txclk_src[SPDIF_TXRATE_MAX];
+       u8 rxclk_src;
+       struct clk *txclk[SPDIF_TXRATE_MAX];
+       struct clk *rxclk;
+       struct snd_dmaengine_dai_dma_data dma_params_tx;
+       struct snd_dmaengine_dai_dma_data dma_params_rx;
+
+       /* The name space will be allocated dynamically */
+       char name[0];
+};
+
+
+/* DPLL locked and lock loss interrupt handler */
+static void spdif_irq_dpll_lock(struct fsl_spdif_priv *spdif_priv)
+{
+       struct regmap *regmap = spdif_priv->regmap;
+       struct platform_device *pdev = spdif_priv->pdev;
+       u32 locked;
+
+       regmap_read(regmap, REG_SPDIF_SRPC, &locked);
+       locked &= SRPC_DPLL_LOCKED;
+
+       dev_dbg(&pdev->dev, "isr: Rx dpll %s \n",
+                       locked ? "locked" : "loss lock");
+
+       spdif_priv->dpll_locked = locked ? true : false;
+}
+
+/* Receiver found illegal symbol interrupt handler */
+static void spdif_irq_sym_error(struct fsl_spdif_priv *spdif_priv)
+{
+       struct regmap *regmap = spdif_priv->regmap;
+       struct platform_device *pdev = spdif_priv->pdev;
+
+       dev_dbg(&pdev->dev, "isr: receiver found illegal symbol\n");
+
+       if (!spdif_priv->dpll_locked) {
+               /* DPLL unlocked seems no audio stream */
+               regmap_update_bits(regmap, REG_SPDIF_SIE, INT_SYM_ERR, 0);
+       }
+}
+
+/* U/Q Channel receive register full */
+static void spdif_irq_uqrx_full(struct fsl_spdif_priv *spdif_priv, char name)
+{
+       struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+       struct regmap *regmap = spdif_priv->regmap;
+       struct platform_device *pdev = spdif_priv->pdev;
+       u32 *pos, size, val, reg;
+
+       switch (name) {
+       case 'U':
+               pos = &ctrl->upos;
+               size = SPDIF_UBITS_SIZE;
+               reg = REG_SPDIF_SRU;
+               break;
+       case 'Q':
+               pos = &ctrl->qpos;
+               size = SPDIF_QSUB_SIZE;
+               reg = REG_SPDIF_SRQ;
+               break;
+       default:
+               dev_err(&pdev->dev, "unsupported channel name\n");
+               return;
+       }
+
+       dev_dbg(&pdev->dev, "isr: %c Channel receive register full\n", name);
+
+       if (*pos >= size * 2) {
+               *pos = 0;
+       } else if (unlikely((*pos % size) + 3 > size)) {
+               dev_err(&pdev->dev, "User bit receivce buffer overflow\n");
+               return;
+       }
+
+       regmap_read(regmap, reg, &val);
+       ctrl->subcode[*pos++] = val >> 16;
+       ctrl->subcode[*pos++] = val >> 8;
+       ctrl->subcode[*pos++] = val;
+}
+
+/* U/Q Channel sync found */
+static void spdif_irq_uq_sync(struct fsl_spdif_priv *spdif_priv)
+{
+       struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+       struct platform_device *pdev = spdif_priv->pdev;
+
+       dev_dbg(&pdev->dev, "isr: U/Q Channel sync found\n");
+
+       /* U/Q buffer reset */
+       if (ctrl->qpos == 0)
+               return;
+
+       /* Set ready to this buffer */
+       ctrl->ready_buf = (ctrl->qpos - 1) / SPDIF_QSUB_SIZE + 1;
+}
+
+/* U/Q Channel framing error */
+static void spdif_irq_uq_err(struct fsl_spdif_priv *spdif_priv)
+{
+       struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+       struct regmap *regmap = spdif_priv->regmap;
+       struct platform_device *pdev = spdif_priv->pdev;
+       u32 val;
+
+       dev_dbg(&pdev->dev, "isr: U/Q Channel framing error\n");
+
+       /* Read U/Q data to clear the irq and do buffer reset */
+       regmap_read(regmap, REG_SPDIF_SRU, &val);
+       regmap_read(regmap, REG_SPDIF_SRQ, &val);
+
+       /* Drop this U/Q buffer */
+       ctrl->ready_buf = 0;
+       ctrl->upos = 0;
+       ctrl->qpos = 0;
+}
+
+/* Get spdif interrupt status and clear the interrupt */
+static u32 spdif_intr_status_clear(struct fsl_spdif_priv *spdif_priv)
+{
+       struct regmap *regmap = spdif_priv->regmap;
+       u32 val, val2;
+
+       regmap_read(regmap, REG_SPDIF_SIS, &val);
+       regmap_read(regmap, REG_SPDIF_SIE, &val2);
+
+       regmap_write(regmap, REG_SPDIF_SIC, val & val2);
+
+       return val;
+}
+
+static irqreturn_t spdif_isr(int irq, void *devid)
+{
+       struct fsl_spdif_priv *spdif_priv = (struct fsl_spdif_priv *)devid;
+       struct platform_device *pdev = spdif_priv->pdev;
+       u32 sis;
+
+       sis = spdif_intr_status_clear(spdif_priv);
+
+       if (sis & INT_DPLL_LOCKED)
+               spdif_irq_dpll_lock(spdif_priv);
+
+       if (sis & INT_TXFIFO_UNOV)
+               dev_dbg(&pdev->dev, "isr: Tx FIFO under/overrun\n");
+
+       if (sis & INT_TXFIFO_RESYNC)
+               dev_dbg(&pdev->dev, "isr: Tx FIFO resync\n");
+
+       if (sis & INT_CNEW)
+               dev_dbg(&pdev->dev, "isr: cstatus new\n");
+
+       if (sis & INT_VAL_NOGOOD)
+               dev_dbg(&pdev->dev, "isr: validity flag no good\n");
+
+       if (sis & INT_SYM_ERR)
+               spdif_irq_sym_error(spdif_priv);
+
+       if (sis & INT_BIT_ERR)
+               dev_dbg(&pdev->dev, "isr: receiver found parity bit error\n");
+
+       if (sis & INT_URX_FUL)
+               spdif_irq_uqrx_full(spdif_priv, 'U');
+
+       if (sis & INT_URX_OV)
+               dev_dbg(&pdev->dev, "isr: U Channel receive register overrun\n");
+
+       if (sis & INT_QRX_FUL)
+               spdif_irq_uqrx_full(spdif_priv, 'Q');
+
+       if (sis & INT_QRX_OV)
+               dev_dbg(&pdev->dev, "isr: Q Channel receive register overrun\n");
+
+       if (sis & INT_UQ_SYNC)
+               spdif_irq_uq_sync(spdif_priv);
+
+       if (sis & INT_UQ_ERR)
+               spdif_irq_uq_err(spdif_priv);
+
+       if (sis & INT_RXFIFO_UNOV)
+               dev_dbg(&pdev->dev, "isr: Rx FIFO under/overrun\n");
+
+       if (sis & INT_RXFIFO_RESYNC)
+               dev_dbg(&pdev->dev, "isr: Rx FIFO resync\n");
+
+       if (sis & INT_LOSS_LOCK)
+               spdif_irq_dpll_lock(spdif_priv);
+
+       /* FIXME: Write Tx FIFO to clear TxEm */
+       if (sis & INT_TX_EM)
+               dev_dbg(&pdev->dev, "isr: Tx FIFO empty\n");
+
+       /* FIXME: Read Rx FIFO to clear RxFIFOFul */
+       if (sis & INT_RXFIFO_FUL)
+               dev_dbg(&pdev->dev, "isr: Rx FIFO full\n");
+
+       return IRQ_HANDLED;
+}
+
+static int spdif_softreset(struct fsl_spdif_priv *spdif_priv)
+{
+       struct regmap *regmap = spdif_priv->regmap;
+       u32 val, cycle = 1000;
+
+       regmap_write(regmap, REG_SPDIF_SCR, SCR_SOFT_RESET);
+
+       /*
+        * RESET bit would be cleared after finishing its reset procedure,
+        * which typically lasts 8 cycles. 1000 cycles will keep it safe.
+        */
+       do {
+               regmap_read(regmap, REG_SPDIF_SCR, &val);
+       } while ((val & SCR_SOFT_RESET) && cycle--);
+
+       if (cycle)
+               return 0;
+       else
+               return -EBUSY;
+}
+
+static void spdif_set_cstatus(struct spdif_mixer_control *ctrl,
+                               u8 mask, u8 cstatus)
+{
+       ctrl->ch_status[3] &= ~mask;
+       ctrl->ch_status[3] |= cstatus & mask;
+}
+
+static void spdif_write_channel_status(struct fsl_spdif_priv *spdif_priv)
+{
+       struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+       struct regmap *regmap = spdif_priv->regmap;
+       struct platform_device *pdev = spdif_priv->pdev;
+       u32 ch_status;
+
+       ch_status = (bitrev8(ctrl->ch_status[0]) << 16) |
+               (bitrev8(ctrl->ch_status[1]) << 8) |
+               bitrev8(ctrl->ch_status[2]);
+       regmap_write(regmap, REG_SPDIF_STCSCH, ch_status);
+
+       dev_dbg(&pdev->dev, "STCSCH: 0x%06x\n", ch_status);
+
+       ch_status = bitrev8(ctrl->ch_status[3]) << 16;
+       regmap_write(regmap, REG_SPDIF_STCSCL, ch_status);
+
+       dev_dbg(&pdev->dev, "STCSCL: 0x%06x\n", ch_status);
+}
+
+/* Set SPDIF PhaseConfig register for rx clock */
+static int spdif_set_rx_clksrc(struct fsl_spdif_priv *spdif_priv,
+                               enum spdif_gainsel gainsel, int dpll_locked)
+{
+       struct regmap *regmap = spdif_priv->regmap;
+       u8 clksrc = spdif_priv->rxclk_src;
+
+       if (clksrc >= SRPC_CLKSRC_MAX || gainsel >= GAINSEL_MULTI_MAX)
+               return -EINVAL;
+
+       regmap_update_bits(regmap, REG_SPDIF_SRPC,
+                       SRPC_CLKSRC_SEL_MASK | SRPC_GAINSEL_MASK,
+                       SRPC_CLKSRC_SEL_SET(clksrc) | SRPC_GAINSEL_SET(gainsel));
+
+       return 0;
+}
+
+static int spdif_set_sample_rate(struct snd_pcm_substream *substream,
+                               int sample_rate)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+       struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+       struct regmap *regmap = spdif_priv->regmap;
+       struct platform_device *pdev = spdif_priv->pdev;
+       unsigned long csfs = 0;
+       u32 stc, mask, rate;
+       u8 clk, div;
+       int ret;
+
+       switch (sample_rate) {
+       case 32000:
+               rate = SPDIF_TXRATE_32000;
+               csfs = IEC958_AES3_CON_FS_32000;
+               break;
+       case 44100:
+               rate = SPDIF_TXRATE_44100;
+               csfs = IEC958_AES3_CON_FS_44100;
+               break;
+       case 48000:
+               rate = SPDIF_TXRATE_48000;
+               csfs = IEC958_AES3_CON_FS_48000;
+               break;
+       default:
+               dev_err(&pdev->dev, "unsupported sample rate %d\n", sample_rate);
+               return -EINVAL;
+       }
+
+       clk = spdif_priv->txclk_src[rate];
+       if (clk >= STC_TXCLK_SRC_MAX) {
+               dev_err(&pdev->dev, "tx clock source is out of range\n");
+               return -EINVAL;
+       }
+
+       div = spdif_priv->txclk_div[rate];
+       if (div == 0) {
+               dev_err(&pdev->dev, "the divisor can't be zero\n");
+               return -EINVAL;
+       }
+
+       /*
+        * The S/PDIF block needs a clock of 64 * fs * div.  The S/PDIF block
+        * will divide by (div).  So request 64 * fs * (div+1) which will
+        * get rounded.
+        */
+       ret = clk_set_rate(spdif_priv->txclk[rate], 64 * sample_rate * (div + 1));
+       if (ret) {
+               dev_err(&pdev->dev, "failed to set tx clock rate\n");
+               return ret;
+       }
+
+       dev_dbg(&pdev->dev, "expected clock rate = %d\n",
+                       (64 * sample_rate * div));
+       dev_dbg(&pdev->dev, "actual clock rate = %ld\n",
+                       clk_get_rate(spdif_priv->txclk[rate]));
+
+       /* set fs field in consumer channel status */
+       spdif_set_cstatus(ctrl, IEC958_AES3_CON_FS, csfs);
+
+       /* select clock source and divisor */
+       stc = STC_TXCLK_ALL_EN | STC_TXCLK_SRC_SET(clk) | STC_TXCLK_DIV(div);
+       mask = STC_TXCLK_ALL_EN_MASK | STC_TXCLK_SRC_MASK | STC_TXCLK_DIV_MASK;
+       regmap_update_bits(regmap, REG_SPDIF_STC, mask, stc);
+
+       dev_dbg(&pdev->dev, "set sample rate to %d\n", sample_rate);
+
+       return 0;
+}
+
+int fsl_spdif_startup(struct snd_pcm_substream *substream,
+                       struct snd_soc_dai *cpu_dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+       struct platform_device *pdev = spdif_priv->pdev;
+       struct regmap *regmap = spdif_priv->regmap;
+       u32 scr, mask, i;
+       int ret;
+
+       /* Reset module and interrupts only for first initialization */
+       if (!cpu_dai->active) {
+               ret = spdif_softreset(spdif_priv);
+               if (ret) {
+                       dev_err(&pdev->dev, "failed to soft reset\n");
+                       return ret;
+               }
+
+               /* Disable all the interrupts */
+               regmap_update_bits(regmap, REG_SPDIF_SIE, 0xffffff, 0);
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               scr = SCR_TXFIFO_AUTOSYNC | SCR_TXFIFO_CTRL_NORMAL |
+                       SCR_TXSEL_NORMAL | SCR_USRC_SEL_CHIP |
+                       SCR_TXFIFO_FSEL_IF8;
+               mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
+                       SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
+                       SCR_TXFIFO_FSEL_MASK;
+               for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+                       clk_prepare_enable(spdif_priv->txclk[i]);
+       } else {
+               scr = SCR_RXFIFO_FSEL_IF8 | SCR_RXFIFO_AUTOSYNC;
+               mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
+                       SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
+               clk_prepare_enable(spdif_priv->rxclk);
+       }
+       regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+       /* Power up SPDIF module */
+       regmap_update_bits(regmap, REG_SPDIF_SCR, SCR_LOW_POWER, 0);
+
+       return 0;
+}
+
+static void fsl_spdif_shutdown(struct snd_pcm_substream *substream,
+                               struct snd_soc_dai *cpu_dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+       struct regmap *regmap = spdif_priv->regmap;
+       u32 scr, mask, i;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               scr = 0;
+               mask = SCR_TXFIFO_AUTOSYNC_MASK | SCR_TXFIFO_CTRL_MASK |
+                       SCR_TXSEL_MASK | SCR_USRC_SEL_MASK |
+                       SCR_TXFIFO_FSEL_MASK;
+               for (i = 0; i < SPDIF_TXRATE_MAX; i++)
+                       clk_disable_unprepare(spdif_priv->txclk[i]);
+       } else {
+               scr = SCR_RXFIFO_OFF | SCR_RXFIFO_CTL_ZERO;
+               mask = SCR_RXFIFO_FSEL_MASK | SCR_RXFIFO_AUTOSYNC_MASK|
+                       SCR_RXFIFO_CTL_MASK | SCR_RXFIFO_OFF_MASK;
+               clk_disable_unprepare(spdif_priv->rxclk);
+       }
+       regmap_update_bits(regmap, REG_SPDIF_SCR, mask, scr);
+
+       /* Power down SPDIF module only if tx&rx are both inactive */
+       if (!cpu_dai->active) {
+               spdif_intr_status_clear(spdif_priv);
+               regmap_update_bits(regmap, REG_SPDIF_SCR,
+                               SCR_LOW_POWER, SCR_LOW_POWER);
+       }
+}
+
+static int fsl_spdif_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 fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+       struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+       struct platform_device *pdev = spdif_priv->pdev;
+       u32 sample_rate = params_rate(params);
+       int ret = 0;
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               ret  = spdif_set_sample_rate(substream, sample_rate);
+               if (ret) {
+                       dev_err(&pdev->dev, "%s: set sample rate failed: %d\n",
+                                       __func__, sample_rate);
+                       return ret;
+               }
+               spdif_set_cstatus(ctrl, IEC958_AES3_CON_CLOCK,
+                               IEC958_AES3_CON_CLOCK_1000PPM);
+               spdif_write_channel_status(spdif_priv);
+       } else {
+               /* Setup rx clock source */
+               ret = spdif_set_rx_clksrc(spdif_priv, SPDIF_DEFAULT_GAINSEL, 1);
+       }
+
+       return ret;
+}
+
+static int fsl_spdif_trigger(struct snd_pcm_substream *substream,
+                               int cmd, struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(rtd->cpu_dai);
+       struct regmap *regmap = spdif_priv->regmap;
+       int is_playack = (substream->stream == SNDRV_PCM_STREAM_PLAYBACK);
+       u32 intr = is_playack ? INTR_FOR_PLAYBACK : INTR_FOR_CAPTURE;
+       u32 dmaen = is_playack ? SCR_DMA_TX_EN : SCR_DMA_RX_EN;;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_RESUME:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               regmap_update_bits(regmap, REG_SPDIF_SIE, intr, intr);
+               regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, dmaen);
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_SUSPEND:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               regmap_update_bits(regmap, REG_SPDIF_SCR, dmaen, 0);
+               regmap_update_bits(regmap, REG_SPDIF_SIE, intr, 0);
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       return 0;
+}
+
+struct snd_soc_dai_ops fsl_spdif_dai_ops = {
+       .startup = fsl_spdif_startup,
+       .hw_params = fsl_spdif_hw_params,
+       .trigger = fsl_spdif_trigger,
+       .shutdown = fsl_spdif_shutdown,
+};
+
+
+/*
+ * ============================================
+ * FSL SPDIF IEC958 controller(mixer) functions
+ *
+ *     Channel status get/put control
+ *     User bit value get/put control
+ *     Valid bit value get control
+ *     DPLL lock status get control
+ *     User bit sync mode selection control
+ * ============================================
+ */
+
+static int fsl_spdif_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_IEC958;
+       uinfo->count = 1;
+
+       return 0;
+}
+
+static int fsl_spdif_pb_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *uvalue)
+{
+       struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+       struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+       struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+
+       uvalue->value.iec958.status[0] = ctrl->ch_status[0];
+       uvalue->value.iec958.status[1] = ctrl->ch_status[1];
+       uvalue->value.iec958.status[2] = ctrl->ch_status[2];
+       uvalue->value.iec958.status[3] = ctrl->ch_status[3];
+
+       return 0;
+}
+
+static int fsl_spdif_pb_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *uvalue)
+{
+       struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+       struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+       struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+
+       ctrl->ch_status[0] = uvalue->value.iec958.status[0];
+       ctrl->ch_status[1] = uvalue->value.iec958.status[1];
+       ctrl->ch_status[2] = uvalue->value.iec958.status[2];
+       ctrl->ch_status[3] = uvalue->value.iec958.status[3];
+
+       spdif_write_channel_status(spdif_priv);
+
+       return 0;
+}
+
+/* Get channel status from SPDIF_RX_CCHAN register */
+static int fsl_spdif_capture_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+       struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+       struct regmap *regmap = spdif_priv->regmap;
+       u32 cstatus, val;
+
+       regmap_read(regmap, REG_SPDIF_SIS, &val);
+       if (!(val & INT_CNEW)) {
+               return -EAGAIN;
+       }
+
+       regmap_read(regmap, REG_SPDIF_SRCSH, &cstatus);
+       ucontrol->value.iec958.status[0] = (cstatus >> 16) & 0xFF;
+       ucontrol->value.iec958.status[1] = (cstatus >> 8) & 0xFF;
+       ucontrol->value.iec958.status[2] = cstatus & 0xFF;
+
+       regmap_read(regmap, REG_SPDIF_SRCSL, &cstatus);
+       ucontrol->value.iec958.status[3] = (cstatus >> 16) & 0xFF;
+       ucontrol->value.iec958.status[4] = (cstatus >> 8) & 0xFF;
+       ucontrol->value.iec958.status[5] = cstatus & 0xFF;
+
+       /* Clear intr */
+       regmap_write(regmap, REG_SPDIF_SIC, INT_CNEW);
+
+       return 0;
+}
+
+/*
+ * Get User bits (subcode) from chip value which readed out
+ * in UChannel register.
+ */
+static int fsl_spdif_subcode_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+       struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+       struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&ctrl->ctl_lock, flags);
+       if (ctrl->ready_buf) {
+               int idx = (ctrl->ready_buf - 1) * SPDIF_UBITS_SIZE;
+               memcpy(&ucontrol->value.iec958.subcode[0],
+                               &ctrl->subcode[idx], SPDIF_UBITS_SIZE);
+       } else {
+               ret = -EAGAIN;
+       }
+       spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
+
+       return ret;
+}
+
+/* Q-subcode infomation. The byte size is SPDIF_UBITS_SIZE/8 */
+static int fsl_spdif_qinfo(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BYTES;
+       uinfo->count = SPDIF_QSUB_SIZE;
+
+       return 0;
+}
+
+/* Get Q subcode from chip value which readed out in QChannel register */
+static int fsl_spdif_qget(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+       struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+       struct spdif_mixer_control *ctrl = &spdif_priv->fsl_spdif_control;
+       unsigned long flags;
+       int ret = 0;
+
+       spin_lock_irqsave(&ctrl->ctl_lock, flags);
+       if (ctrl->ready_buf) {
+               int idx = (ctrl->ready_buf - 1) * SPDIF_QSUB_SIZE;
+               memcpy(&ucontrol->value.bytes.data[0],
+                               &ctrl->qsub[idx], SPDIF_QSUB_SIZE);
+       } else {
+               ret = -EAGAIN;
+       }
+       spin_unlock_irqrestore(&ctrl->ctl_lock, flags);
+
+       return ret;
+}
+
+/* Valid bit infomation */
+static int fsl_spdif_vbit_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+
+       return 0;
+}
+
+/* Get valid good bit from interrupt status register */
+static int fsl_spdif_vbit_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+       struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+       struct regmap *regmap = spdif_priv->regmap;
+       u32 val;
+
+       val = regmap_read(regmap, REG_SPDIF_SIS, &val);
+       ucontrol->value.integer.value[0] = (val & INT_VAL_NOGOOD) != 0;
+       regmap_write(regmap, REG_SPDIF_SIC, INT_VAL_NOGOOD);
+
+       return 0;
+}
+
+/* DPLL lock infomation */
+static int fsl_spdif_rxrate_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 16000;
+       uinfo->value.integer.max = 96000;
+
+       return 0;
+}
+
+static u32 gainsel_multi[GAINSEL_MULTI_MAX] = {
+       24, 16, 12, 8, 6, 4, 3,
+};
+
+/* Get RX data clock rate given the SPDIF bus_clk */
+static int spdif_get_rxclk_rate(struct fsl_spdif_priv *spdif_priv,
+                               enum spdif_gainsel gainsel)
+{
+       struct regmap *regmap = spdif_priv->regmap;
+       struct platform_device *pdev = spdif_priv->pdev;
+       u64 tmpval64, busclk_freq = 0;
+       u32 freqmeas, phaseconf;
+       u8 clksrc;
+
+       regmap_read(regmap, REG_SPDIF_SRFM, &freqmeas);
+       regmap_read(regmap, REG_SPDIF_SRPC, &phaseconf);
+
+       clksrc = (phaseconf >> SRPC_CLKSRC_SEL_OFFSET) & 0xf;
+       if (srpc_dpll_locked[clksrc] && (phaseconf & SRPC_DPLL_LOCKED)) {
+               /* Get bus clock from system */
+               busclk_freq = clk_get_rate(spdif_priv->rxclk);
+       }
+
+       /* FreqMeas_CLK = (BUS_CLK * FreqMeas) / 2 ^ 10 / GAINSEL / 128 */
+       tmpval64 = (u64) busclk_freq * freqmeas;
+       do_div(tmpval64, gainsel_multi[gainsel] * 1024);
+       do_div(tmpval64, 128 * 1024);
+
+       dev_dbg(&pdev->dev, "FreqMeas: %d\n", freqmeas);
+       dev_dbg(&pdev->dev, "BusclkFreq: %lld\n", busclk_freq);
+       dev_dbg(&pdev->dev, "RxRate: %lld\n", tmpval64);
+
+       return (int)tmpval64;
+}
+
+/*
+ * Get DPLL lock or not info from stable interrupt status register.
+ * User application must use this control to get locked,
+ * then can do next PCM operation
+ */
+static int fsl_spdif_rxrate_get(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+       struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+       int rate = spdif_get_rxclk_rate(spdif_priv, SPDIF_DEFAULT_GAINSEL);
+
+       if (spdif_priv->dpll_locked)
+               ucontrol->value.integer.value[0] = rate;
+       else
+               ucontrol->value.integer.value[0] = 0;
+
+       return 0;
+}
+
+/* User bit sync mode info */
+static int fsl_spdif_usync_info(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_info *uinfo)
+{
+       uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
+       uinfo->count = 1;
+       uinfo->value.integer.min = 0;
+       uinfo->value.integer.max = 1;
+
+       return 0;
+}
+
+/*
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int fsl_spdif_usync_get(struct snd_kcontrol *kcontrol,
+                              struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+       struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+       struct regmap *regmap = spdif_priv->regmap;
+       u32 val;
+
+       regmap_read(regmap, REG_SPDIF_SRCD, &val);
+       ucontrol->value.integer.value[0] = (val & SRCD_CD_USER) != 0;
+
+       return 0;
+}
+
+/*
+ * User bit sync mode:
+ * 1 CD User channel subcode
+ * 0 Non-CD data
+ */
+static int fsl_spdif_usync_put(struct snd_kcontrol *kcontrol,
+                               struct snd_ctl_elem_value *ucontrol)
+{
+       struct snd_soc_dai *cpu_dai = snd_kcontrol_chip(kcontrol);
+       struct fsl_spdif_priv *spdif_priv = snd_soc_dai_get_drvdata(cpu_dai);
+       struct regmap *regmap = spdif_priv->regmap;
+       u32 val = ucontrol->value.integer.value[0] << SRCD_CD_USER_OFFSET;
+
+       regmap_update_bits(regmap, REG_SPDIF_SRCD, SRCD_CD_USER, val);
+
+       return 0;
+}
+
+/* FSL SPDIF IEC958 controller defines */
+static struct snd_kcontrol_new fsl_spdif_ctrls[] = {
+       /* Status cchanel controller */
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_MIXER,
+               .name = SNDRV_CTL_NAME_IEC958("", PLAYBACK, DEFAULT),
+               .access = SNDRV_CTL_ELEM_ACCESS_READ |
+                       SNDRV_CTL_ELEM_ACCESS_WRITE |
+                       SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+               .info = fsl_spdif_info,
+               .get = fsl_spdif_pb_get,
+               .put = fsl_spdif_pb_put,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+               .name = SNDRV_CTL_NAME_IEC958("", CAPTURE, DEFAULT),
+               .access = SNDRV_CTL_ELEM_ACCESS_READ |
+                       SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+               .info = fsl_spdif_info,
+               .get = fsl_spdif_capture_get,
+       },
+       /* User bits controller */
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+               .name = "IEC958 Subcode Capture Default",
+               .access = SNDRV_CTL_ELEM_ACCESS_READ |
+                       SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+               .info = fsl_spdif_info,
+               .get = fsl_spdif_subcode_get,
+       },
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+               .name = "IEC958 Q-subcode Capture Default",
+               .access = SNDRV_CTL_ELEM_ACCESS_READ |
+                       SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+               .info = fsl_spdif_qinfo,
+               .get = fsl_spdif_qget,
+       },
+       /* Valid bit error controller */
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+               .name = "IEC958 V-Bit Errors",
+               .access = SNDRV_CTL_ELEM_ACCESS_READ |
+                       SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+               .info = fsl_spdif_vbit_info,
+               .get = fsl_spdif_vbit_get,
+       },
+       /* DPLL lock info get controller */
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+               .name = "RX Sample Rate",
+               .access = SNDRV_CTL_ELEM_ACCESS_READ |
+                       SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+               .info = fsl_spdif_rxrate_info,
+               .get = fsl_spdif_rxrate_get,
+       },
+       /* User bit sync mode set/get controller */
+       {
+               .iface = SNDRV_CTL_ELEM_IFACE_PCM,
+               .name = "IEC958 USyncMode CDText",
+               .access = SNDRV_CTL_ELEM_ACCESS_READ |
+                       SNDRV_CTL_ELEM_ACCESS_WRITE |
+                       SNDRV_CTL_ELEM_ACCESS_VOLATILE,
+               .info = fsl_spdif_usync_info,
+               .get = fsl_spdif_usync_get,
+               .put = fsl_spdif_usync_put,
+       },
+};
+
+static int fsl_spdif_dai_probe(struct snd_soc_dai *dai)
+{
+       struct fsl_spdif_priv *spdif_private = snd_soc_dai_get_drvdata(dai);
+
+       dai->playback_dma_data = &spdif_private->dma_params_tx;
+       dai->capture_dma_data = &spdif_private->dma_params_rx;
+
+       snd_soc_add_dai_controls(dai, fsl_spdif_ctrls, ARRAY_SIZE(fsl_spdif_ctrls));
+
+       return 0;
+}
+
+struct snd_soc_dai_driver fsl_spdif_dai = {
+       .probe = &fsl_spdif_dai_probe,
+       .playback = {
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = FSL_SPDIF_RATES_PLAYBACK,
+               .formats = FSL_SPDIF_FORMATS_PLAYBACK,
+       },
+       .capture = {
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = FSL_SPDIF_RATES_CAPTURE,
+               .formats = FSL_SPDIF_FORMATS_CAPTURE,
+       },
+       .ops = &fsl_spdif_dai_ops,
+};
+
+static const struct snd_soc_component_driver fsl_spdif_component = {
+       .name           = "fsl-spdif",
+};
+
+/*
+ * ================
+ * FSL SPDIF REGMAP
+ * ================
+ */
+
+static bool fsl_spdif_readable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case REG_SPDIF_SCR:
+       case REG_SPDIF_SRCD:
+       case REG_SPDIF_SRPC:
+       case REG_SPDIF_SIE:
+       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_STCSCH:
+       case REG_SPDIF_STCSCL:
+       case REG_SPDIF_SRFM:
+       case REG_SPDIF_STC:
+               return true;
+       default:
+               return false;
+       };
+}
+
+static bool fsl_spdif_writeable_reg(struct device *dev, unsigned int reg)
+{
+       switch (reg) {
+       case REG_SPDIF_SCR:
+       case REG_SPDIF_SRCD:
+       case REG_SPDIF_SRPC:
+       case REG_SPDIF_SIE:
+       case REG_SPDIF_SIC:
+       case REG_SPDIF_STL:
+       case REG_SPDIF_STR:
+       case REG_SPDIF_STCSCH:
+       case REG_SPDIF_STCSCL:
+       case REG_SPDIF_STC:
+               return true;
+       default:
+               return false;
+       };
+}
+
+static const struct regmap_config fsl_spdif_regmap_config = {
+       .reg_bits = 32,
+       .reg_stride = 4,
+       .val_bits = 32,
+
+       .max_register = REG_SPDIF_STC,
+       .readable_reg = fsl_spdif_readable_reg,
+       .writeable_reg = fsl_spdif_writeable_reg,
+};
+
+static u32 fsl_spdif_txclk_caldiv(struct fsl_spdif_priv *spdif_priv,
+                               struct clk *clk, u64 savesub,
+                               enum spdif_txrate index)
+{
+       const u32 rate[] = { 32000, 44100, 48000 };
+       u64 rate_ideal, rate_actual, sub;
+       u32 div, arate;
+
+       for (div = 1; div <= 128; div++) {
+               rate_ideal = rate[index] * (div + 1) * 64;
+               rate_actual = clk_round_rate(clk, rate_ideal);
+
+               arate = rate_actual / 64;
+               arate /= div;
+
+               if (arate == rate[index]) {
+                       /* We are lucky */
+                       savesub = 0;
+                       spdif_priv->txclk_div[index] = div;
+                       break;
+               } else if (arate / rate[index] == 1) {
+                       /* A little bigger than expect */
+                       sub = (arate - rate[index]) * 100000;
+                       do_div(sub, rate[index]);
+                       if (sub < savesub) {
+                               savesub = sub;
+                               spdif_priv->txclk_div[index] = div;
+                       }
+               } else if (rate[index] / arate == 1) {
+                       /* A little smaller than expect */
+                       sub = (rate[index] - arate) * 100000;
+                       do_div(sub, rate[index]);
+                       if (sub < savesub) {
+                               savesub = sub;
+                               spdif_priv->txclk_div[index] = div;
+                       }
+               }
+       }
+
+       return savesub;
+}
+
+static int fsl_spdif_probe_txclk(struct fsl_spdif_priv *spdif_priv,
+                               enum spdif_txrate index)
+{
+       const u32 rate[] = { 32000, 44100, 48000 };
+       struct platform_device *pdev = spdif_priv->pdev;
+       struct device *dev = &pdev->dev;
+       u64 savesub = 100000, ret;
+       struct clk *clk;
+       char tmp[16];
+       int i;
+
+       for (i = 0; i < STC_TXCLK_SRC_MAX; i++) {
+               sprintf(tmp, "rxtx%d", i);
+               clk = devm_clk_get(&pdev->dev, tmp);
+               if (IS_ERR(clk)) {
+                       dev_err(dev, "no rxtx%d clock in devicetree\n", i);
+                       return PTR_ERR(clk);
+               }
+               if (!clk_get_rate(clk))
+                       continue;
+
+               ret = fsl_spdif_txclk_caldiv(spdif_priv, clk, savesub, index);
+               if (savesub == ret)
+                       continue;
+
+               savesub = ret;
+               spdif_priv->txclk[index] = clk;
+               spdif_priv->txclk_src[index] = i;
+
+               /* To quick catch a divisor, we allow a 0.1% deviation */
+               if (savesub < 100)
+                       break;
+       }
+
+       dev_dbg(&pdev->dev, "use rxtx%d as tx clock source for %dHz sample rate",
+                       spdif_priv->txclk_src[index], rate[index]);
+       dev_dbg(&pdev->dev, "use divisor %d for %dHz sample rate",
+                       spdif_priv->txclk_div[index], rate[index]);
+
+       return 0;
+}
+
+static int fsl_spdif_probe(struct platform_device *pdev)
+{
+       struct device_node *np = pdev->dev.of_node;
+       struct fsl_spdif_priv *spdif_priv;
+       struct spdif_mixer_control *ctrl;
+       struct resource *res;
+       void __iomem *regs;
+       int irq, ret, i;
+
+       if (!np)
+               return -ENODEV;
+
+       spdif_priv = devm_kzalloc(&pdev->dev,
+                       sizeof(struct fsl_spdif_priv) + strlen(np->name) + 1,
+                       GFP_KERNEL);
+       if (!spdif_priv)
+               return -ENOMEM;
+
+       strcpy(spdif_priv->name, np->name);
+
+       spdif_priv->pdev = pdev;
+
+       /* Initialize this copy of the CPU DAI driver structure */
+       memcpy(&spdif_priv->cpu_dai_drv, &fsl_spdif_dai, sizeof(fsl_spdif_dai));
+       spdif_priv->cpu_dai_drv.name = spdif_priv->name;
+
+       /* Get the addresses and IRQ */
+       res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
+       if (IS_ERR(res)) {
+               dev_err(&pdev->dev, "could not determine device resources\n");
+               return PTR_ERR(res);
+       }
+
+       regs = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(regs)) {
+               dev_err(&pdev->dev, "could not map device resources\n");
+               return PTR_ERR(regs);
+       }
+
+       spdif_priv->regmap = devm_regmap_init_mmio_clk(&pdev->dev,
+                       "core", regs, &fsl_spdif_regmap_config);
+       if (IS_ERR(spdif_priv->regmap)) {
+               dev_err(&pdev->dev, "regmap init failed\n");
+               return PTR_ERR(spdif_priv->regmap);
+       }
+
+       irq = platform_get_irq(pdev, 0);
+       if (irq < 0) {
+               dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
+               return irq;
+       }
+
+       ret = devm_request_irq(&pdev->dev, irq, spdif_isr, 0,
+                       spdif_priv->name, spdif_priv);
+       if (ret) {
+               dev_err(&pdev->dev, "could not claim irq %u\n", irq);
+               return ret;
+       }
+
+       /* Select clock source for rx/tx clock */
+       spdif_priv->rxclk = devm_clk_get(&pdev->dev, "rxtx1");
+       if (IS_ERR(spdif_priv->rxclk)) {
+               dev_err(&pdev->dev, "no rxtx1 clock in devicetree\n");
+               return PTR_ERR(spdif_priv->rxclk);
+       }
+       spdif_priv->rxclk_src = DEFAULT_RXCLK_SRC;
+
+       for (i = 0; i < SPDIF_TXRATE_MAX; i++) {
+               ret = fsl_spdif_probe_txclk(spdif_priv, i);
+               if (ret)
+                       return ret;
+       }
+
+       /* Initial spinlock for control data */
+       ctrl = &spdif_priv->fsl_spdif_control;
+       spin_lock_init(&ctrl->ctl_lock);
+
+       /* Init tx channel status default value */
+       ctrl->ch_status[0] =
+               IEC958_AES0_CON_NOT_COPYRIGHT | IEC958_AES0_CON_EMPHASIS_5015;
+       ctrl->ch_status[1] = IEC958_AES1_CON_DIGDIGCONV_ID;
+       ctrl->ch_status[2] = 0x00;
+       ctrl->ch_status[3] =
+               IEC958_AES3_CON_FS_44100 | IEC958_AES3_CON_CLOCK_1000PPM;
+
+       spdif_priv->dpll_locked = false;
+
+       spdif_priv->dma_params_tx.maxburst = FSL_SPDIF_TXFIFO_WML;
+       spdif_priv->dma_params_rx.maxburst = FSL_SPDIF_RXFIFO_WML;
+       spdif_priv->dma_params_tx.addr = res->start + REG_SPDIF_STL;
+       spdif_priv->dma_params_rx.addr = res->start + REG_SPDIF_SRL;
+
+       /* Register with ASoC */
+       dev_set_drvdata(&pdev->dev, spdif_priv);
+
+       ret = snd_soc_register_component(&pdev->dev, &fsl_spdif_component,
+                                        &spdif_priv->cpu_dai_drv, 1);
+       if (ret) {
+               dev_err(&pdev->dev, "failed to register DAI: %d\n", ret);
+               goto error_dev;
+       }
+
+       ret = imx_pcm_dma_init(pdev);
+       if (ret) {
+               dev_err(&pdev->dev, "imx_pcm_dma_init failed: %d\n", ret);
+               goto error_component;
+       }
+
+       return ret;
+
+error_component:
+       snd_soc_unregister_component(&pdev->dev);
+error_dev:
+       dev_set_drvdata(&pdev->dev, NULL);
+
+       return ret;
+}
+
+static int fsl_spdif_remove(struct platform_device *pdev)
+{
+       imx_pcm_dma_exit(pdev);
+       snd_soc_unregister_component(&pdev->dev);
+       dev_set_drvdata(&pdev->dev, NULL);
+
+       return 0;
+}
+
+static const struct of_device_id fsl_spdif_dt_ids[] = {
+       { .compatible = "fsl,imx35-spdif", },
+       {}
+};
+MODULE_DEVICE_TABLE(of, fsl_spdif_dt_ids);
+
+static struct platform_driver fsl_spdif_driver = {
+       .driver = {
+               .name = "fsl-spdif-dai",
+               .owner = THIS_MODULE,
+               .of_match_table = fsl_spdif_dt_ids,
+       },
+       .probe = fsl_spdif_probe,
+       .remove = fsl_spdif_remove,
+};
+
+module_platform_driver(fsl_spdif_driver);
+
+MODULE_AUTHOR("Freescale Semiconductor, Inc.");
+MODULE_DESCRIPTION("Freescale S/PDIF CPU DAI Driver");
+MODULE_LICENSE("GPL v2");
+MODULE_ALIAS("platform:fsl-spdif-dai");
diff --git a/sound/soc/fsl/fsl_spdif.h b/sound/soc/fsl/fsl_spdif.h
new file mode 100644 (file)
index 0000000..b126679
--- /dev/null
@@ -0,0 +1,191 @@
+/*
+ * fsl_spdif.h - ALSA S/PDIF interface for the Freescale i.MX SoC
+ *
+ * Copyright (C) 2013 Freescale Semiconductor, Inc.
+ *
+ * Author: Nicolin Chen <b42378@freescale.com>
+ *
+ * Based on fsl_ssi.h
+ * Author: Timur Tabi <timur@freescale.com>
+ * Copyright 2007-2008 Freescale Semiconductor, Inc.
+ *
+ * This file is licensed under the terms of the GNU General Public License
+ * version 2.  This program  is licensed "as is" without any warranty of any
+ * kind, whether express or implied.
+ */
+
+#ifndef _FSL_SPDIF_DAI_H
+#define _FSL_SPDIF_DAI_H
+
+/* S/PDIF Register Map */
+#define REG_SPDIF_SCR                  0x0     /* SPDIF Configuration Register */
+#define REG_SPDIF_SRCD                 0x4     /* CDText Control Register */
+#define REG_SPDIF_SRPC                 0x8     /* PhaseConfig Register */
+#define REG_SPDIF_SIE                  0xc     /* InterruptEn Register */
+#define REG_SPDIF_SIS                  0x10    /* InterruptStat Register */
+#define REG_SPDIF_SIC                  0x10    /* InterruptClear Register */
+#define REG_SPDIF_SRL                  0x14    /* SPDIFRxLeft Register */
+#define REG_SPDIF_SRR                  0x18    /* SPDIFRxRight Register */
+#define REG_SPDIF_SRCSH                        0x1c    /* SPDIFRxCChannel_h Register */
+#define REG_SPDIF_SRCSL                        0x20    /* SPDIFRxCChannel_l Register */
+#define REG_SPDIF_SRU                  0x24    /* UchannelRx Register */
+#define REG_SPDIF_SRQ                  0x28    /* QchannelRx Register */
+#define REG_SPDIF_STL                  0x2C    /* SPDIFTxLeft Register */
+#define REG_SPDIF_STR                  0x30    /* SPDIFTxRight Register */
+#define REG_SPDIF_STCSCH               0x34    /* SPDIFTxCChannelCons_h Register */
+#define REG_SPDIF_STCSCL               0x38    /* SPDIFTxCChannelCons_l Register */
+#define REG_SPDIF_SRFM                 0x44    /* FreqMeas Register */
+#define REG_SPDIF_STC                  0x50    /* SPDIFTxClk Register */
+
+
+/* SPDIF Configuration register */
+#define SCR_RXFIFO_CTL_OFFSET          23
+#define SCR_RXFIFO_CTL_MASK            (1 << SCR_RXFIFO_CTL_OFFSET)
+#define SCR_RXFIFO_CTL_ZERO            (1 << SCR_RXFIFO_CTL_OFFSET)
+#define SCR_RXFIFO_OFF_OFFSET          22
+#define SCR_RXFIFO_OFF_MASK            (1 << SCR_RXFIFO_OFF_OFFSET)
+#define SCR_RXFIFO_OFF                 (1 << SCR_RXFIFO_OFF_OFFSET)
+#define SCR_RXFIFO_RST_OFFSET          21
+#define SCR_RXFIFO_RST_MASK            (1 << SCR_RXFIFO_RST_OFFSET)
+#define SCR_RXFIFO_RST                 (1 << SCR_RXFIFO_RST_OFFSET)
+#define SCR_RXFIFO_FSEL_OFFSET         19
+#define SCR_RXFIFO_FSEL_MASK           (0x3 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF0            (0x0 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF4            (0x1 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF8            (0x2 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_FSEL_IF12           (0x3 << SCR_RXFIFO_FSEL_OFFSET)
+#define SCR_RXFIFO_AUTOSYNC_OFFSET     18
+#define SCR_RXFIFO_AUTOSYNC_MASK       (1 << SCR_RXFIFO_AUTOSYNC_OFFSET)
+#define SCR_RXFIFO_AUTOSYNC            (1 << SCR_RXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_AUTOSYNC_OFFSET     17
+#define SCR_TXFIFO_AUTOSYNC_MASK       (1 << SCR_TXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_AUTOSYNC            (1 << SCR_TXFIFO_AUTOSYNC_OFFSET)
+#define SCR_TXFIFO_FSEL_OFFSET         15
+#define SCR_TXFIFO_FSEL_MASK           (0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF0            (0x0 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF4            (0x1 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF8            (0x2 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_TXFIFO_FSEL_IF12           (0x3 << SCR_TXFIFO_FSEL_OFFSET)
+#define SCR_LOW_POWER                  (1 << 13)
+#define SCR_SOFT_RESET                 (1 << 12)
+#define SCR_TXFIFO_CTRL_OFFSET         10
+#define SCR_TXFIFO_CTRL_MASK           (0x3 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_ZERO           (0x0 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_NORMAL         (0x1 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_TXFIFO_CTRL_ONESAMPLE      (0x2 << SCR_TXFIFO_CTRL_OFFSET)
+#define SCR_DMA_RX_EN_OFFSET           9
+#define SCR_DMA_RX_EN_MASK             (1 << SCR_DMA_RX_EN_OFFSET)
+#define SCR_DMA_RX_EN                  (1 << SCR_DMA_RX_EN_OFFSET)
+#define SCR_DMA_TX_EN_OFFSET           8
+#define SCR_DMA_TX_EN_MASK             (1 << SCR_DMA_TX_EN_OFFSET)
+#define SCR_DMA_TX_EN                  (1 << SCR_DMA_TX_EN_OFFSET)
+#define SCR_VAL_OFFSET                 5
+#define SCR_VAL_MASK                   (1 << SCR_VAL_OFFSET)
+#define SCR_VAL_CLEAR                  (1 << SCR_VAL_OFFSET)
+#define SCR_TXSEL_OFFSET               2
+#define SCR_TXSEL_MASK                 (0x7 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_OFF                  (0 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_RX                   (1 << SCR_TXSEL_OFFSET)
+#define SCR_TXSEL_NORMAL               (0x5 << SCR_TXSEL_OFFSET)
+#define SCR_USRC_SEL_OFFSET            0x0
+#define SCR_USRC_SEL_MASK              (0x3 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_NONE              (0x0 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_RECV              (0x1 << SCR_USRC_SEL_OFFSET)
+#define SCR_USRC_SEL_CHIP              (0x3 << SCR_USRC_SEL_OFFSET)
+
+/* SPDIF CDText control */
+#define SRCD_CD_USER_OFFSET            1
+#define SRCD_CD_USER                   (1 << SRCD_CD_USER_OFFSET)
+
+/* SPDIF Phase Configuration register */
+#define SRPC_DPLL_LOCKED               (1 << 6)
+#define SRPC_CLKSRC_SEL_OFFSET         7
+#define SRPC_CLKSRC_SEL_MASK           (0xf << SRPC_CLKSRC_SEL_OFFSET)
+#define SRPC_CLKSRC_SEL_SET(x)         ((x << SRPC_CLKSRC_SEL_OFFSET) & SRPC_CLKSRC_SEL_MASK)
+#define SRPC_CLKSRC_SEL_LOCKED_OFFSET1 5
+#define SRPC_CLKSRC_SEL_LOCKED_OFFSET2 2
+#define SRPC_GAINSEL_OFFSET            3
+#define SRPC_GAINSEL_MASK              (0x7 << SRPC_GAINSEL_OFFSET)
+#define SRPC_GAINSEL_SET(x)            ((x << SRPC_GAINSEL_OFFSET) & SRPC_GAINSEL_MASK)
+
+#define SRPC_CLKSRC_MAX                        16
+
+enum spdif_gainsel {
+       GAINSEL_MULTI_24 = 0,
+       GAINSEL_MULTI_16,
+       GAINSEL_MULTI_12,
+       GAINSEL_MULTI_8,
+       GAINSEL_MULTI_6,
+       GAINSEL_MULTI_4,
+       GAINSEL_MULTI_3,
+};
+#define GAINSEL_MULTI_MAX              (GAINSEL_MULTI_3 + 1)
+#define SPDIF_DEFAULT_GAINSEL          GAINSEL_MULTI_8
+
+/* SPDIF interrupt mask define */
+#define INT_DPLL_LOCKED                        (1 << 20)
+#define INT_TXFIFO_UNOV                        (1 << 19)
+#define INT_TXFIFO_RESYNC              (1 << 18)
+#define INT_CNEW                       (1 << 17)
+#define INT_VAL_NOGOOD                 (1 << 16)
+#define INT_SYM_ERR                    (1 << 15)
+#define INT_BIT_ERR                    (1 << 14)
+#define INT_URX_FUL                    (1 << 10)
+#define INT_URX_OV                     (1 << 9)
+#define INT_QRX_FUL                    (1 << 8)
+#define INT_QRX_OV                     (1 << 7)
+#define INT_UQ_SYNC                    (1 << 6)
+#define INT_UQ_ERR                     (1 << 5)
+#define INT_RXFIFO_UNOV                        (1 << 4)
+#define INT_RXFIFO_RESYNC              (1 << 3)
+#define INT_LOSS_LOCK                  (1 << 2)
+#define INT_TX_EM                      (1 << 1)
+#define INT_RXFIFO_FUL                 (1 << 0)
+
+/* SPDIF Clock register */
+#define STC_SYSCLK_DIV_OFFSET          11
+#define STC_SYSCLK_DIV_MASK            (0x1ff << STC_TXCLK_SRC_OFFSET)
+#define STC_SYSCLK_DIV(x)              ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_SYSCLK_DIV_MASK)
+#define STC_TXCLK_SRC_OFFSET           8
+#define STC_TXCLK_SRC_MASK             (0x7 << STC_TXCLK_SRC_OFFSET)
+#define STC_TXCLK_SRC_SET(x)           ((x << STC_TXCLK_SRC_OFFSET) & STC_TXCLK_SRC_MASK)
+#define STC_TXCLK_ALL_EN_OFFSET                7
+#define STC_TXCLK_ALL_EN_MASK          (1 << STC_TXCLK_ALL_EN_OFFSET)
+#define STC_TXCLK_ALL_EN               (1 << STC_TXCLK_ALL_EN_OFFSET)
+#define STC_TXCLK_DIV_OFFSET           0
+#define STC_TXCLK_DIV_MASK             (0x7ff << STC_TXCLK_DIV_OFFSET)
+#define STC_TXCLK_DIV(x)               ((((x) - 1) << STC_TXCLK_DIV_OFFSET) & STC_TXCLK_DIV_MASK)
+#define STC_TXCLK_SRC_MAX              8
+
+/* SPDIF tx rate */
+enum spdif_txrate {
+       SPDIF_TXRATE_32000 = 0,
+       SPDIF_TXRATE_44100,
+       SPDIF_TXRATE_48000,
+};
+#define SPDIF_TXRATE_MAX               (SPDIF_TXRATE_48000 + 1)
+
+
+#define SPDIF_CSTATUS_BYTE             6
+#define SPDIF_UBITS_SIZE               96
+#define SPDIF_QSUB_SIZE                        (SPDIF_UBITS_SIZE / 8)
+
+
+#define FSL_SPDIF_RATES_PLAYBACK       (SNDRV_PCM_RATE_32000 | \
+                                        SNDRV_PCM_RATE_44100 | \
+                                        SNDRV_PCM_RATE_48000)
+
+#define FSL_SPDIF_RATES_CAPTURE                (SNDRV_PCM_RATE_16000 | \
+                                        SNDRV_PCM_RATE_32000 | \
+                                        SNDRV_PCM_RATE_44100 | \
+                                        SNDRV_PCM_RATE_48000 | \
+                                        SNDRV_PCM_RATE_64000 | \
+                                        SNDRV_PCM_RATE_96000)
+
+#define FSL_SPDIF_FORMATS_PLAYBACK     (SNDRV_PCM_FMTBIT_S16_LE | \
+                                        SNDRV_PCM_FMTBIT_S20_3LE | \
+                                        SNDRV_PCM_FMTBIT_S24_LE)
+
+#define FSL_SPDIF_FORMATS_CAPTURE      (SNDRV_PCM_FMTBIT_S24_LE)
+
+#endif /* _FSL_SPDIF_DAI_H */
index 2f2d837df07f078e5b60da38308cbdb8d53b9969..5cf626c4dc96f7615f92bae6ef28c25b2ced500c 100644 (file)
@@ -8,6 +8,26 @@
  * This file is licensed under the terms of the GNU General Public License
  * version 2.  This program is licensed "as is" without any warranty of any
  * kind, whether express or implied.
+ *
+ *
+ * Some notes why imx-pcm-fiq is used instead of DMA on some boards:
+ *
+ * The i.MX SSI core has some nasty limitations in AC97 mode. While most
+ * sane processor vendors have a FIFO per AC97 slot, the i.MX has only
+ * one FIFO which combines all valid receive slots. We cannot even select
+ * which slots we want to receive. The WM9712 with which this driver
+ * was developed with always sends GPIO status data in slot 12 which
+ * we receive in our (PCM-) data stream. The only chance we have is to
+ * manually skip this data in the FIQ handler. With sampling rates different
+ * from 48000Hz not every frame has valid receive data, so the ratio
+ * between pcm data and GPIO status data changes. Our FIQ handler is not
+ * able to handle this, hence this driver only works with 48000Hz sampling
+ * rate.
+ * Reading and writing AC97 registers is another challenge. The core
+ * provides us status bits when the read register is updated with *another*
+ * value. When we read the same register two times (and the register still
+ * contains the same value) these status bits are not set. We work
+ * around this by not polling these bits but only wait a fixed delay.
  */
 
 #include <linux/init.h>
@@ -36,7 +56,7 @@
 #define read_ssi(addr)                  in_be32(addr)
 #define write_ssi(val, addr)            out_be32(addr, val)
 #define write_ssi_mask(addr, clear, set) clrsetbits_be32(addr, clear, set)
-#elif defined ARM
+#else
 #define read_ssi(addr)                  readl(addr)
 #define write_ssi(val, addr)            writel(val, addr)
 /*
@@ -121,11 +141,14 @@ struct fsl_ssi_private {
 
        bool new_binding;
        bool ssi_on_imx;
+       bool imx_ac97;
+       bool use_dma;
        struct clk *clk;
        struct snd_dmaengine_dai_dma_data dma_params_tx;
        struct snd_dmaengine_dai_dma_data dma_params_rx;
        struct imx_dma_data filter_data_tx;
        struct imx_dma_data filter_data_rx;
+       struct imx_pcm_fiq_params fiq_params;
 
        struct {
                unsigned int rfrc;
@@ -298,6 +321,102 @@ static irqreturn_t fsl_ssi_isr(int irq, void *dev_id)
        return ret;
 }
 
+static int fsl_ssi_setup(struct fsl_ssi_private *ssi_private)
+{
+       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+       u8 i2s_mode;
+       u8 wm;
+       int synchronous = ssi_private->cpu_dai_drv.symmetric_rates;
+
+       if (ssi_private->imx_ac97)
+               i2s_mode = CCSR_SSI_SCR_I2S_MODE_NORMAL | CCSR_SSI_SCR_NET;
+       else
+               i2s_mode = CCSR_SSI_SCR_I2S_MODE_SLAVE;
+
+       /*
+        * Section 16.5 of the MPC8610 reference manual says that the SSI needs
+        * to be disabled before updating the registers we set here.
+        */
+       write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
+
+       /*
+        * Program the SSI into I2S Slave Non-Network Synchronous mode. Also
+        * enable the transmit and receive FIFO.
+        *
+        * FIXME: Little-endian samples require a different shift dir
+        */
+       write_ssi_mask(&ssi->scr,
+               CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
+               CCSR_SSI_SCR_TFR_CLK_DIS |
+               i2s_mode |
+               (synchronous ? CCSR_SSI_SCR_SYN : 0));
+
+       write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
+                CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
+                CCSR_SSI_STCR_TSCKP, &ssi->stcr);
+
+       write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
+                CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
+                CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
+       /*
+        * The DC and PM bits are only used if the SSI is the clock master.
+        */
+
+       /*
+        * Set the watermark for transmit FIFI 0 and receive FIFO 0. We don't
+        * use FIFO 1. We program the transmit water to signal a DMA transfer
+        * if there are only two (or fewer) elements left in the FIFO. Two
+        * elements equals one frame (left channel, right channel). This value,
+        * however, depends on the depth of the transmit buffer.
+        *
+        * We set the watermark on the same level as the DMA burstsize.  For
+        * fiq it is probably better to use the biggest possible watermark
+        * size.
+        */
+       if (ssi_private->use_dma)
+               wm = ssi_private->fifo_depth - 2;
+       else
+               wm = ssi_private->fifo_depth;
+
+       write_ssi(CCSR_SSI_SFCSR_TFWM0(wm) | CCSR_SSI_SFCSR_RFWM0(wm) |
+               CCSR_SSI_SFCSR_TFWM1(wm) | CCSR_SSI_SFCSR_RFWM1(wm),
+               &ssi->sfcsr);
+
+       /*
+        * For ac97 interrupts are enabled with the startup of the substream
+        * because it is also running without an active substream. Normally SSI
+        * is only enabled when there is a substream.
+        */
+       if (ssi_private->imx_ac97) {
+               /*
+                * Setup the clock control register
+                */
+               write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
+                               &ssi->stccr);
+               write_ssi(CCSR_SSI_SxCCR_WL(17) | CCSR_SSI_SxCCR_DC(13),
+                               &ssi->srccr);
+
+               /*
+                * Enable AC97 mode and startup the SSI
+                */
+               write_ssi(CCSR_SSI_SACNT_AC97EN | CCSR_SSI_SACNT_FV,
+                               &ssi->sacnt);
+               write_ssi(0xff, &ssi->saccdis);
+               write_ssi(0x300, &ssi->saccen);
+
+               /*
+                * Enable SSI, Transmit and Receive
+                */
+               write_ssi_mask(&ssi->scr, 0, CCSR_SSI_SCR_SSIEN |
+                               CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE);
+
+               write_ssi(CCSR_SSI_SOR_WAIT(3), &ssi->sor);
+       }
+
+       return 0;
+}
+
+
 /**
  * fsl_ssi_startup: create a new substream
  *
@@ -319,70 +438,14 @@ static int fsl_ssi_startup(struct snd_pcm_substream *substream,
         * and initialize the SSI registers.
         */
        if (!ssi_private->first_stream) {
-               struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-
                ssi_private->first_stream = substream;
 
                /*
-                * Section 16.5 of the MPC8610 reference manual says that the
-                * SSI needs to be disabled before updating the registers we set
-                * here.
-                */
-               write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
-
-               /*
-                * Program the SSI into I2S Slave Non-Network Synchronous mode.
-                * Also enable the transmit and receive FIFO.
-                *
-                * FIXME: Little-endian samples require a different shift dir
-                */
-               write_ssi_mask(&ssi->scr,
-                       CCSR_SSI_SCR_I2S_MODE_MASK | CCSR_SSI_SCR_SYN,
-                       CCSR_SSI_SCR_TFR_CLK_DIS | CCSR_SSI_SCR_I2S_MODE_SLAVE
-                       | (synchronous ? CCSR_SSI_SCR_SYN : 0));
-
-               write_ssi(CCSR_SSI_STCR_TXBIT0 | CCSR_SSI_STCR_TFEN0 |
-                        CCSR_SSI_STCR_TFSI | CCSR_SSI_STCR_TEFS |
-                        CCSR_SSI_STCR_TSCKP, &ssi->stcr);
-
-               write_ssi(CCSR_SSI_SRCR_RXBIT0 | CCSR_SSI_SRCR_RFEN0 |
-                        CCSR_SSI_SRCR_RFSI | CCSR_SSI_SRCR_REFS |
-                        CCSR_SSI_SRCR_RSCKP, &ssi->srcr);
-
-               /*
-                * The DC and PM bits are only used if the SSI is the clock
-                * master.
-                */
-
-               /* Enable the interrupts and DMA requests */
-               write_ssi(SIER_FLAGS, &ssi->sier);
-
-               /*
-                * Set the watermark for transmit FIFI 0 and receive FIFO 0. We
-                * don't use FIFO 1.  We program the transmit water to signal a
-                * DMA transfer if there are only two (or fewer) elements left
-                * in the FIFO.  Two elements equals one frame (left channel,
-                * right channel).  This value, however, depends on the depth of
-                * the transmit buffer.
-                *
-                * We program the receive FIFO to notify us if at least two
-                * elements (one frame) have been written to the FIFO.  We could
-                * make this value larger (and maybe we should), but this way
-                * data will be written to memory as soon as it's available.
-                */
-               write_ssi(CCSR_SSI_SFCSR_TFWM0(ssi_private->fifo_depth - 2) |
-                       CCSR_SSI_SFCSR_RFWM0(ssi_private->fifo_depth - 2),
-                       &ssi->sfcsr);
-
-               /*
-                * We keep the SSI disabled because if we enable it, then the
-                * DMA controller will start.  It's not supposed to start until
-                * the SCR.TE (or SCR.RE) bit is set, but it does anyway.  The
-                * DMA controller will transfer one "BWC" of data (i.e. the
-                * amount of data that the MR.BWC bits are set to).  The reason
-                * this is bad is because at this point, the PCM driver has not
-                * finished initializing the DMA controller.
+                * fsl_ssi_setup was already called by ac97_init earlier if
+                * the driver is in ac97 mode.
                 */
+               if (!ssi_private->imx_ac97)
+                       fsl_ssi_setup(ssi_private);
        } else {
                if (synchronous) {
                        struct snd_pcm_runtime *first_runtime =
@@ -492,6 +555,27 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
        struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(rtd->cpu_dai);
        struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+       unsigned int sier_bits;
+
+       /*
+        *  Enable only the interrupts and DMA requests
+        *  that are needed for the channel. As the fiq
+        *  is polling for this bits, we have to ensure
+        *  that this are aligned with the preallocated
+        *  buffers
+        */
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
+               if (ssi_private->use_dma)
+                       sier_bits = SIER_FLAGS;
+               else
+                       sier_bits = CCSR_SSI_SIER_TIE | CCSR_SSI_SIER_TFE0_EN;
+       } else {
+               if (ssi_private->use_dma)
+                       sier_bits = SIER_FLAGS;
+               else
+                       sier_bits = CCSR_SSI_SIER_RIE | CCSR_SSI_SIER_RFF0_EN;
+       }
 
        switch (cmd) {
        case SNDRV_PCM_TRIGGER_START:
@@ -510,12 +594,18 @@ static int fsl_ssi_trigger(struct snd_pcm_substream *substream, int cmd,
                        write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_TE, 0);
                else
                        write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_RE, 0);
+
+               if (!ssi_private->imx_ac97 && (read_ssi(&ssi->scr) &
+                                       (CCSR_SSI_SCR_TE | CCSR_SSI_SCR_RE)) == 0)
+                       write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
                break;
 
        default:
                return -EINVAL;
        }
 
+       write_ssi(sier_bits, &ssi->sier);
+
        return 0;
 }
 
@@ -534,22 +624,13 @@ static void fsl_ssi_shutdown(struct snd_pcm_substream *substream,
                ssi_private->first_stream = ssi_private->second_stream;
 
        ssi_private->second_stream = NULL;
-
-       /*
-        * If this is the last active substream, disable the SSI.
-        */
-       if (!ssi_private->first_stream) {
-               struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
-
-               write_ssi_mask(&ssi->scr, CCSR_SSI_SCR_SSIEN, 0);
-       }
 }
 
 static int fsl_ssi_dai_probe(struct snd_soc_dai *dai)
 {
        struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(dai);
 
-       if (ssi_private->ssi_on_imx) {
+       if (ssi_private->ssi_on_imx && ssi_private->use_dma) {
                dai->playback_dma_data = &ssi_private->dma_params_tx;
                dai->capture_dma_data = &ssi_private->dma_params_rx;
        }
@@ -587,6 +668,133 @@ static const struct snd_soc_component_driver fsl_ssi_component = {
        .name           = "fsl-ssi",
 };
 
+/**
+ * fsl_ssi_ac97_trigger: start and stop the AC97 receive/transmit.
+ *
+ * This function is called by ALSA to start, stop, pause, and resume the
+ * transfer of data.
+ */
+static int fsl_ssi_ac97_trigger(struct snd_pcm_substream *substream, int cmd,
+                          struct snd_soc_dai *dai)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+       struct fsl_ssi_private *ssi_private = snd_soc_dai_get_drvdata(
+                       rtd->cpu_dai);
+       struct ccsr_ssi __iomem *ssi = ssi_private->ssi;
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+       case SNDRV_PCM_TRIGGER_PAUSE_RELEASE:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_TIE |
+                                       CCSR_SSI_SIER_TFE0_EN);
+               else
+                       write_ssi_mask(&ssi->sier, 0, CCSR_SSI_SIER_RIE |
+                                       CCSR_SSI_SIER_RFF0_EN);
+               break;
+
+       case SNDRV_PCM_TRIGGER_STOP:
+       case SNDRV_PCM_TRIGGER_PAUSE_PUSH:
+               if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+                       write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_TIE |
+                                       CCSR_SSI_SIER_TFE0_EN, 0);
+               else
+                       write_ssi_mask(&ssi->sier, CCSR_SSI_SIER_RIE |
+                                       CCSR_SSI_SIER_RFF0_EN, 0);
+               break;
+
+       default:
+               return -EINVAL;
+       }
+
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               write_ssi(CCSR_SSI_SOR_TX_CLR, &ssi->sor);
+       else
+               write_ssi(CCSR_SSI_SOR_RX_CLR, &ssi->sor);
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops fsl_ssi_ac97_dai_ops = {
+       .startup        = fsl_ssi_startup,
+       .shutdown       = fsl_ssi_shutdown,
+       .trigger        = fsl_ssi_ac97_trigger,
+};
+
+static struct snd_soc_dai_driver fsl_ssi_ac97_dai = {
+       .ac97_control = 1,
+       .playback = {
+               .stream_name = "AC97 Playback",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_8000_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .capture = {
+               .stream_name = "AC97 Capture",
+               .channels_min = 2,
+               .channels_max = 2,
+               .rates = SNDRV_PCM_RATE_48000,
+               .formats = SNDRV_PCM_FMTBIT_S16_LE,
+       },
+       .ops = &fsl_ssi_ac97_dai_ops,
+};
+
+
+static struct fsl_ssi_private *fsl_ac97_data;
+
+static void fsl_ssi_ac97_init(void)
+{
+       fsl_ssi_setup(fsl_ac97_data);
+}
+
+void fsl_ssi_ac97_write(struct snd_ac97 *ac97, unsigned short reg,
+               unsigned short val)
+{
+       struct ccsr_ssi *ssi = fsl_ac97_data->ssi;
+       unsigned int lreg;
+       unsigned int lval;
+
+       if (reg > 0x7f)
+               return;
+
+
+       lreg = reg <<  12;
+       write_ssi(lreg, &ssi->sacadd);
+
+       lval = val << 4;
+       write_ssi(lval , &ssi->sacdat);
+
+       write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK,
+                       CCSR_SSI_SACNT_WR);
+       udelay(100);
+}
+
+unsigned short fsl_ssi_ac97_read(struct snd_ac97 *ac97,
+               unsigned short reg)
+{
+       struct ccsr_ssi *ssi = fsl_ac97_data->ssi;
+
+       unsigned short val = -1;
+       unsigned int lreg;
+
+       lreg = (reg & 0x7f) <<  12;
+       write_ssi(lreg, &ssi->sacadd);
+       write_ssi_mask(&ssi->sacnt, CCSR_SSI_SACNT_RDWR_MASK,
+                       CCSR_SSI_SACNT_RD);
+
+       udelay(100);
+
+       val = (read_ssi(&ssi->sacdat) >> 4) & 0xffff;
+
+       return val;
+}
+
+static struct snd_ac97_bus_ops fsl_ssi_ac97_ops = {
+       .read           = fsl_ssi_ac97_read,
+       .write          = fsl_ssi_ac97_write,
+};
+
 /* Show the statistics of a flag only if its interrupt is enabled.  The
  * compiler will optimze this code to a no-op if the interrupt is not
  * enabled.
@@ -663,6 +871,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        struct resource res;
        char name[64];
        bool shared;
+       bool ac97 = false;
 
        /* SSIs that are not connected on the board should have a
         *      status = "disabled"
@@ -673,14 +882,20 @@ static int fsl_ssi_probe(struct platform_device *pdev)
 
        /* We only support the SSI in "I2S Slave" mode */
        sprop = of_get_property(np, "fsl,mode", NULL);
-       if (!sprop || strcmp(sprop, "i2s-slave")) {
+       if (!sprop) {
+               dev_err(&pdev->dev, "fsl,mode property is necessary\n");
+               return -EINVAL;
+       }
+       if (!strcmp(sprop, "ac97-slave")) {
+               ac97 = true;
+       } else if (strcmp(sprop, "i2s-slave")) {
                dev_notice(&pdev->dev, "mode %s is unsupported\n", sprop);
                return -ENODEV;
        }
 
        /* The DAI name is the last part of the full name of the node. */
        p = strrchr(np->full_name, '/') + 1;
-       ssi_private = kzalloc(sizeof(struct fsl_ssi_private) + strlen(p),
+       ssi_private = devm_kzalloc(&pdev->dev, sizeof(*ssi_private) + strlen(p),
                              GFP_KERNEL);
        if (!ssi_private) {
                dev_err(&pdev->dev, "could not allocate DAI object\n");
@@ -689,38 +904,41 @@ static int fsl_ssi_probe(struct platform_device *pdev)
 
        strcpy(ssi_private->name, p);
 
-       /* Initialize this copy of the CPU DAI driver structure */
-       memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
-              sizeof(fsl_ssi_dai_template));
+       ssi_private->use_dma = !of_property_read_bool(np,
+                       "fsl,fiq-stream-filter");
+
+       if (ac97) {
+               memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_ac97_dai,
+                               sizeof(fsl_ssi_ac97_dai));
+
+               fsl_ac97_data = ssi_private;
+               ssi_private->imx_ac97 = true;
+
+               snd_soc_set_ac97_ops_of_reset(&fsl_ssi_ac97_ops, pdev);
+       } else {
+               /* Initialize this copy of the CPU DAI driver structure */
+               memcpy(&ssi_private->cpu_dai_drv, &fsl_ssi_dai_template,
+                      sizeof(fsl_ssi_dai_template));
+       }
        ssi_private->cpu_dai_drv.name = ssi_private->name;
 
        /* Get the addresses and IRQ */
        ret = of_address_to_resource(np, 0, &res);
        if (ret) {
                dev_err(&pdev->dev, "could not determine device resources\n");
-               goto error_kmalloc;
+               return ret;
        }
        ssi_private->ssi = of_iomap(np, 0);
        if (!ssi_private->ssi) {
                dev_err(&pdev->dev, "could not map device resources\n");
-               ret = -ENOMEM;
-               goto error_kmalloc;
+               return -ENOMEM;
        }
        ssi_private->ssi_phys = res.start;
 
        ssi_private->irq = irq_of_parse_and_map(np, 0);
        if (ssi_private->irq == NO_IRQ) {
                dev_err(&pdev->dev, "no irq for node %s\n", np->full_name);
-               ret = -ENXIO;
-               goto error_iomap;
-       }
-
-       /* The 'name' should not have any slashes in it. */
-       ret = request_irq(ssi_private->irq, fsl_ssi_isr, 0, ssi_private->name,
-                         ssi_private);
-       if (ret < 0) {
-               dev_err(&pdev->dev, "could not claim irq %u\n", ssi_private->irq);
-               goto error_irqmap;
+               return -ENXIO;
        }
 
        /* Are the RX and the TX clocks locked? */
@@ -739,13 +957,18 @@ static int fsl_ssi_probe(struct platform_device *pdev)
                u32 dma_events[2];
                ssi_private->ssi_on_imx = true;
 
-               ssi_private->clk = clk_get(&pdev->dev, NULL);
+               ssi_private->clk = devm_clk_get(&pdev->dev, NULL);
                if (IS_ERR(ssi_private->clk)) {
                        ret = PTR_ERR(ssi_private->clk);
                        dev_err(&pdev->dev, "could not get clock: %d\n", ret);
-                       goto error_irq;
+                       goto error_irqmap;
+               }
+               ret = clk_prepare_enable(ssi_private->clk);
+               if (ret) {
+                       dev_err(&pdev->dev, "clk_prepare_enable failed: %d\n",
+                               ret);
+                       goto error_irqmap;
                }
-               clk_prepare_enable(ssi_private->clk);
 
                /*
                 * We have burstsize be "fifo_depth - 2" to match the SSI
@@ -763,24 +986,38 @@ static int fsl_ssi_probe(struct platform_device *pdev)
                        &ssi_private->filter_data_tx;
                ssi_private->dma_params_rx.filter_data =
                        &ssi_private->filter_data_rx;
-               /*
-                * TODO: This is a temporary solution and should be changed
-                * to use generic DMA binding later when the helplers get in.
-                */
-               ret = of_property_read_u32_array(pdev->dev.of_node,
+               if (!of_property_read_bool(pdev->dev.of_node, "dmas") &&
+                               ssi_private->use_dma) {
+                       /*
+                        * FIXME: This is a temporary solution until all
+                        * necessary dma drivers support the generic dma
+                        * bindings.
+                        */
+                       ret = of_property_read_u32_array(pdev->dev.of_node,
                                        "fsl,ssi-dma-events", dma_events, 2);
-               if (ret) {
-                       dev_err(&pdev->dev, "could not get dma events\n");
-                       goto error_clk;
+                       if (ret && ssi_private->use_dma) {
+                               dev_err(&pdev->dev, "could not get dma events but fsl-ssi is configured to use DMA\n");
+                               goto error_clk;
+                       }
                }
 
                shared = of_device_is_compatible(of_get_parent(np),
                            "fsl,spba-bus");
 
                imx_pcm_dma_params_init_data(&ssi_private->filter_data_tx,
-                       dma_events[0], shared);
+                       dma_events[0], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
                imx_pcm_dma_params_init_data(&ssi_private->filter_data_rx,
-                       dma_events[1], shared);
+                       dma_events[1], shared ? IMX_DMATYPE_SSI_SP : IMX_DMATYPE_SSI);
+       } else if (ssi_private->use_dma) {
+               /* The 'name' should not have any slashes in it. */
+               ret = devm_request_irq(&pdev->dev, ssi_private->irq,
+                                       fsl_ssi_isr, 0, ssi_private->name,
+                                       ssi_private);
+               if (ret < 0) {
+                       dev_err(&pdev->dev, "could not claim irq %u\n",
+                                       ssi_private->irq);
+                       goto error_irqmap;
+               }
        }
 
        /* Initialize the the device_attribute structure */
@@ -794,7 +1031,7 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        if (ret) {
                dev_err(&pdev->dev, "could not create sysfs %s file\n",
                        ssi_private->dev_attr.attr.name);
-               goto error_irq;
+               goto error_clk;
        }
 
        /* Register with ASoC */
@@ -808,9 +1045,30 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        }
 
        if (ssi_private->ssi_on_imx) {
-               ret = imx_pcm_dma_init(pdev);
-               if (ret)
-                       goto error_dev;
+               if (!ssi_private->use_dma) {
+
+                       /*
+                        * Some boards use an incompatible codec. To get it
+                        * working, we are using imx-fiq-pcm-audio, that
+                        * can handle those codecs. DMA is not possible in this
+                        * situation.
+                        */
+
+                       ssi_private->fiq_params.irq = ssi_private->irq;
+                       ssi_private->fiq_params.base = ssi_private->ssi;
+                       ssi_private->fiq_params.dma_params_rx =
+                               &ssi_private->dma_params_rx;
+                       ssi_private->fiq_params.dma_params_tx =
+                               &ssi_private->dma_params_tx;
+
+                       ret = imx_pcm_fiq_init(pdev, &ssi_private->fiq_params);
+                       if (ret)
+                               goto error_dev;
+               } else {
+                       ret = imx_pcm_dma_init(pdev);
+                       if (ret)
+                               goto error_dev;
+               }
        }
 
        /*
@@ -845,6 +1103,9 @@ static int fsl_ssi_probe(struct platform_device *pdev)
        }
 
 done:
+       if (ssi_private->imx_ac97)
+               fsl_ssi_ac97_init();
+
        return 0;
 
 error_dai:
@@ -857,23 +1118,12 @@ error_dev:
        device_remove_file(&pdev->dev, dev_attr);
 
 error_clk:
-       if (ssi_private->ssi_on_imx) {
+       if (ssi_private->ssi_on_imx)
                clk_disable_unprepare(ssi_private->clk);
-               clk_put(ssi_private->clk);
-       }
-
-error_irq:
-       free_irq(ssi_private->irq, ssi_private);
 
 error_irqmap:
        irq_dispose_mapping(ssi_private->irq);
 
-error_iomap:
-       iounmap(ssi_private->ssi);
-
-error_kmalloc:
-       kfree(ssi_private);
-
        return ret;
 }
 
@@ -883,20 +1133,15 @@ static int fsl_ssi_remove(struct platform_device *pdev)
 
        if (!ssi_private->new_binding)
                platform_device_unregister(ssi_private->pdev);
-       if (ssi_private->ssi_on_imx) {
+       if (ssi_private->ssi_on_imx)
                imx_pcm_dma_exit(pdev);
-               clk_disable_unprepare(ssi_private->clk);
-               clk_put(ssi_private->clk);
-       }
        snd_soc_unregister_component(&pdev->dev);
+       dev_set_drvdata(&pdev->dev, NULL);
        device_remove_file(&pdev->dev, &ssi_private->dev_attr);
-
-       free_irq(ssi_private->irq, ssi_private);
+       if (ssi_private->ssi_on_imx)
+               clk_disable_unprepare(ssi_private->clk);
        irq_dispose_mapping(ssi_private->irq);
 
-       kfree(ssi_private);
-       dev_set_drvdata(&pdev->dev, NULL);
-
        return 0;
 }
 
@@ -919,6 +1164,7 @@ static struct platform_driver fsl_ssi_driver = {
 
 module_platform_driver(fsl_ssi_driver);
 
+MODULE_ALIAS("platform:fsl-ssi-dai");
 MODULE_AUTHOR("Timur Tabi <timur@freescale.com>");
 MODULE_DESCRIPTION("Freescale Synchronous Serial Interface (SSI) ASoC Driver");
 MODULE_LICENSE("GPL v2");
index e260f1f899dbb132135cb6745376f5dad8891fb9..ab17381cc9812a7e0538f30ea44496bfdd944c6d 100644 (file)
@@ -73,8 +73,11 @@ static ssize_t audmux_read_file(struct file *file, char __user *user_buf,
        if (!buf)
                return -ENOMEM;
 
-       if (audmux_clk)
-               clk_prepare_enable(audmux_clk);
+       if (audmux_clk) {
+               ret = clk_prepare_enable(audmux_clk);
+               if (ret)
+                       return ret;
+       }
 
        ptcr = readl(audmux_base + IMX_AUDMUX_V2_PTCR(port));
        pdcr = readl(audmux_base + IMX_AUDMUX_V2_PDCR(port));
@@ -224,14 +227,19 @@ EXPORT_SYMBOL_GPL(imx_audmux_v1_configure_port);
 int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr,
                unsigned int pdcr)
 {
+       int ret;
+
        if (audmux_type != IMX31_AUDMUX)
                return -EINVAL;
 
        if (!audmux_base)
                return -ENOSYS;
 
-       if (audmux_clk)
-               clk_prepare_enable(audmux_clk);
+       if (audmux_clk) {
+               ret = clk_prepare_enable(audmux_clk);
+               if (ret)
+                       return ret;
+       }
 
        writel(ptcr, audmux_base + IMX_AUDMUX_V2_PTCR(port));
        writel(pdcr, audmux_base + IMX_AUDMUX_V2_PDCR(port));
@@ -243,6 +251,66 @@ int imx_audmux_v2_configure_port(unsigned int port, unsigned int ptcr,
 }
 EXPORT_SYMBOL_GPL(imx_audmux_v2_configure_port);
 
+static int imx_audmux_parse_dt_defaults(struct platform_device *pdev,
+               struct device_node *of_node)
+{
+       struct device_node *child;
+
+       for_each_available_child_of_node(of_node, child) {
+               unsigned int port;
+               unsigned int ptcr = 0;
+               unsigned int pdcr = 0;
+               unsigned int pcr = 0;
+               unsigned int val;
+               int ret;
+               int i = 0;
+
+               ret = of_property_read_u32(child, "fsl,audmux-port", &port);
+               if (ret) {
+                       dev_warn(&pdev->dev, "Failed to get fsl,audmux-port of child node \"%s\"\n",
+                                       child->full_name);
+                       continue;
+               }
+               if (!of_property_read_bool(child, "fsl,port-config")) {
+                       dev_warn(&pdev->dev, "child node \"%s\" does not have property fsl,port-config\n",
+                                       child->full_name);
+                       continue;
+               }
+
+               for (i = 0; (ret = of_property_read_u32_index(child,
+                                       "fsl,port-config", i, &val)) == 0;
+                               ++i) {
+                       if (audmux_type == IMX31_AUDMUX) {
+                               if (i % 2)
+                                       pdcr |= val;
+                               else
+                                       ptcr |= val;
+                       } else {
+                               pcr |= val;
+                       }
+               }
+
+               if (ret != -EOVERFLOW) {
+                       dev_err(&pdev->dev, "Failed to read u32 at index %d of child %s\n",
+                                       i, child->full_name);
+                       continue;
+               }
+
+               if (audmux_type == IMX31_AUDMUX) {
+                       if (i % 2) {
+                               dev_err(&pdev->dev, "One pdcr value is missing in child node %s\n",
+                                               child->full_name);
+                               continue;
+                       }
+                       imx_audmux_v2_configure_port(port, ptcr, pdcr);
+               } else {
+                       imx_audmux_v1_configure_port(port, pcr);
+               }
+       }
+
+       return 0;
+}
+
 static int imx_audmux_probe(struct platform_device *pdev)
 {
        struct resource *res;
@@ -267,6 +335,8 @@ static int imx_audmux_probe(struct platform_device *pdev)
        if (audmux_type == IMX31_AUDMUX)
                audmux_debugfs_init();
 
+       imx_audmux_parse_dt_defaults(pdev, pdev->dev.of_node);
+
        return 0;
 }
 
index b8ff44b9dafaaaeecd1a5c9fbc0ab4efd0e69b6b..38a4209af7c6f6e43e5ebe4cd74aae9895e09896 100644 (file)
@@ -1,57 +1,7 @@
 #ifndef __IMX_AUDMUX_H
 #define __IMX_AUDMUX_H
 
-#define MX27_AUDMUX_HPCR1_SSI0         0
-#define MX27_AUDMUX_HPCR2_SSI1         1
-#define MX27_AUDMUX_HPCR3_SSI_PINS_4   2
-#define MX27_AUDMUX_PPCR1_SSI_PINS_1   3
-#define MX27_AUDMUX_PPCR2_SSI_PINS_2   4
-#define MX27_AUDMUX_PPCR3_SSI_PINS_3   5
-
-#define MX31_AUDMUX_PORT1_SSI0         0
-#define MX31_AUDMUX_PORT2_SSI1         1
-#define MX31_AUDMUX_PORT3_SSI_PINS_3   2
-#define MX31_AUDMUX_PORT4_SSI_PINS_4   3
-#define MX31_AUDMUX_PORT5_SSI_PINS_5   4
-#define MX31_AUDMUX_PORT6_SSI_PINS_6   5
-#define MX31_AUDMUX_PORT7_SSI_PINS_7   6
-
-#define MX51_AUDMUX_PORT1_SSI0         0
-#define MX51_AUDMUX_PORT2_SSI1         1
-#define MX51_AUDMUX_PORT3              2
-#define MX51_AUDMUX_PORT4              3
-#define MX51_AUDMUX_PORT5              4
-#define MX51_AUDMUX_PORT6              5
-#define MX51_AUDMUX_PORT7              6
-
-/* Register definitions for the i.MX21/27 Digital Audio Multiplexer */
-#define IMX_AUDMUX_V1_PCR_INMMASK(x)   ((x) & 0xff)
-#define IMX_AUDMUX_V1_PCR_INMEN                (1 << 8)
-#define IMX_AUDMUX_V1_PCR_TXRXEN       (1 << 10)
-#define IMX_AUDMUX_V1_PCR_SYN          (1 << 12)
-#define IMX_AUDMUX_V1_PCR_RXDSEL(x)    (((x) & 0x7) << 13)
-#define IMX_AUDMUX_V1_PCR_RFCSEL(x)    (((x) & 0xf) << 20)
-#define IMX_AUDMUX_V1_PCR_RCLKDIR      (1 << 24)
-#define IMX_AUDMUX_V1_PCR_RFSDIR       (1 << 25)
-#define IMX_AUDMUX_V1_PCR_TFCSEL(x)    (((x) & 0xf) << 26)
-#define IMX_AUDMUX_V1_PCR_TCLKDIR      (1 << 30)
-#define IMX_AUDMUX_V1_PCR_TFSDIR       (1 << 31)
-
-/* Register definitions for the i.MX25/31/35/51 Digital Audio Multiplexer */
-#define IMX_AUDMUX_V2_PTCR_TFSDIR      (1 << 31)
-#define IMX_AUDMUX_V2_PTCR_TFSEL(x)    (((x) & 0xf) << 27)
-#define IMX_AUDMUX_V2_PTCR_TCLKDIR     (1 << 26)
-#define IMX_AUDMUX_V2_PTCR_TCSEL(x)    (((x) & 0xf) << 22)
-#define IMX_AUDMUX_V2_PTCR_RFSDIR      (1 << 21)
-#define IMX_AUDMUX_V2_PTCR_RFSEL(x)    (((x) & 0xf) << 17)
-#define IMX_AUDMUX_V2_PTCR_RCLKDIR     (1 << 16)
-#define IMX_AUDMUX_V2_PTCR_RCSEL(x)    (((x) & 0xf) << 12)
-#define IMX_AUDMUX_V2_PTCR_SYN         (1 << 11)
-
-#define IMX_AUDMUX_V2_PDCR_RXDSEL(x)   (((x) & 0x7) << 13)
-#define IMX_AUDMUX_V2_PDCR_TXRXEN      (1 << 12)
-#define IMX_AUDMUX_V2_PDCR_MODE(x)     (((x) & 0x3) << 8)
-#define IMX_AUDMUX_V2_PDCR_INMMASK(x)  ((x) & 0xff)
+#include <dt-bindings/sound/fsl-imx-audmux.h>
 
 int imx_audmux_v1_configure_port(unsigned int port, unsigned int pcr);
 
index 9df173c091a66b4fbb676036b9a66cacfe2eff17..a3d60d4bea4ce8ace84d2860921b5c46d3f00895 100644 (file)
@@ -90,6 +90,7 @@ static const struct snd_soc_dapm_route imx_mc13783_routes[] = {
 
 static struct snd_soc_card imx_mc13783 = {
        .name           = "imx_mc13783",
+       .owner          = THIS_MODULE,
        .dai_link       = imx_mc13783_dai_mc13783,
        .num_links      = ARRAY_SIZE(imx_mc13783_dai_mc13783),
        .dapm_widgets   = imx_mc13783_widget,
index fde4d2ea68c88afb32275963ae0b51e81be9112a..4dc1296688e9210531f904366dcc19603e0022ce 100644 (file)
@@ -14,6 +14,7 @@
 #include <linux/platform_device.h>
 #include <linux/dmaengine.h>
 #include <linux/types.h>
+#include <linux/module.h>
 
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -64,7 +65,6 @@ int imx_pcm_dma_init(struct platform_device *pdev)
 {
        return snd_dmaengine_pcm_register(&pdev->dev, &imx_dmaengine_pcm_config,
                SND_DMAENGINE_PCM_FLAG_NO_RESIDUE |
-               SND_DMAENGINE_PCM_FLAG_NO_DT |
                SND_DMAENGINE_PCM_FLAG_COMPAT);
 }
 EXPORT_SYMBOL_GPL(imx_pcm_dma_init);
@@ -74,3 +74,5 @@ void imx_pcm_dma_exit(struct platform_device *pdev)
        snd_dmaengine_pcm_unregister(&pdev->dev);
 }
 EXPORT_SYMBOL_GPL(imx_pcm_dma_exit);
+
+MODULE_LICENSE("GPL");
index 310d90290320c751b572f2a409f8c4800f6c60b2..34043c55f2a62f048232b09ff2f6165fd3726116 100644 (file)
@@ -22,6 +22,7 @@
 #include <linux/slab.h>
 
 #include <sound/core.h>
+#include <sound/dmaengine_pcm.h>
 #include <sound/initval.h>
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
@@ -32,6 +33,7 @@
 #include <linux/platform_data/asoc-imx-ssi.h>
 
 #include "imx-ssi.h"
+#include "imx-pcm.h"
 
 struct imx_pcm_runtime_data {
        unsigned int period;
@@ -366,9 +368,9 @@ static struct snd_soc_platform_driver imx_soc_platform_fiq = {
        .pcm_free       = imx_pcm_fiq_free,
 };
 
-int imx_pcm_fiq_init(struct platform_device *pdev)
+int imx_pcm_fiq_init(struct platform_device *pdev,
+               struct imx_pcm_fiq_params *params)
 {
-       struct imx_ssi *ssi = platform_get_drvdata(pdev);
        int ret;
 
        ret = claim_fiq(&fh);
@@ -377,15 +379,15 @@ int imx_pcm_fiq_init(struct platform_device *pdev)
                return ret;
        }
 
-       mxc_set_irq_fiq(ssi->irq, 1);
-       ssi_irq = ssi->irq;
+       mxc_set_irq_fiq(params->irq, 1);
+       ssi_irq = params->irq;
 
-       imx_pcm_fiq = ssi->irq;
+       imx_pcm_fiq = params->irq;
 
-       imx_ssi_fiq_base = (unsigned long)ssi->base;
+       imx_ssi_fiq_base = (unsigned long)params->base;
 
-       ssi->dma_params_tx.maxburst = 4;
-       ssi->dma_params_rx.maxburst = 6;
+       params->dma_params_tx->maxburst = 4;
+       params->dma_params_rx->maxburst = 6;
 
        ret = snd_soc_register_platform(&pdev->dev, &imx_soc_platform_fiq);
        if (ret)
@@ -406,3 +408,5 @@ void imx_pcm_fiq_exit(struct platform_device *pdev)
        snd_soc_unregister_platform(&pdev->dev);
 }
 EXPORT_SYMBOL_GPL(imx_pcm_fiq_exit);
+
+MODULE_LICENSE("GPL");
index 67f656c7c320744334d4ca5d8233d36eacbe6d4d..5d5b73303e1169b7f6c2254e06d66a87409050cc 100644 (file)
 
 static inline void
 imx_pcm_dma_params_init_data(struct imx_dma_data *dma_data,
-       int dma, bool shared)
+       int dma, enum sdma_peripheral_type peripheral_type)
 {
        dma_data->dma_request = dma;
        dma_data->priority = DMA_PRIO_HIGH;
-       if (shared)
-               dma_data->peripheral_type = IMX_DMATYPE_SSI_SP;
-       else
-               dma_data->peripheral_type = IMX_DMATYPE_SSI;
+       dma_data->peripheral_type = peripheral_type;
 }
 
-#ifdef CONFIG_SND_SOC_IMX_PCM_DMA
+struct imx_pcm_fiq_params {
+       int irq;
+       void __iomem *base;
+
+       /* Pointer to original ssi driver to setup tx rx sizes */
+       struct snd_dmaengine_dai_dma_data *dma_params_rx;
+       struct snd_dmaengine_dai_dma_data *dma_params_tx;
+};
+
+#if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_DMA)
 int imx_pcm_dma_init(struct platform_device *pdev);
 void imx_pcm_dma_exit(struct platform_device *pdev);
 #else
@@ -46,11 +52,13 @@ static inline void imx_pcm_dma_exit(struct platform_device *pdev)
 }
 #endif
 
-#ifdef CONFIG_SND_SOC_IMX_PCM_FIQ
-int imx_pcm_fiq_init(struct platform_device *pdev);
+#if IS_ENABLED(CONFIG_SND_SOC_IMX_PCM_FIQ)
+int imx_pcm_fiq_init(struct platform_device *pdev,
+               struct imx_pcm_fiq_params *params);
 void imx_pcm_fiq_exit(struct platform_device *pdev);
 #else
-static inline int imx_pcm_fiq_init(struct platform_device *pdev)
+static inline int imx_pcm_fiq_init(struct platform_device *pdev,
+               struct imx_pcm_fiq_params *params)
 {
        return -ENODEV;
 }
index 3f726e4f88db8c2900840819e5d86ede5e9415d1..389cbfa6dca79d6100b34e64f57d6bc2514d20f8 100644 (file)
@@ -129,8 +129,10 @@ static int imx_sgtl5000_probe(struct platform_device *pdev)
        }
 
        data->codec_clk = devm_clk_get(&codec_dev->dev, NULL);
-       if (IS_ERR(data->codec_clk))
+       if (IS_ERR(data->codec_clk)) {
+               ret = PTR_ERR(data->codec_clk);
                goto fail;
+       }
 
        data->clk_frequency = clk_get_rate(data->codec_clk);
 
index 51be3772cba901bc17d245ab0077ec744fcf64b2..f58bcd85c07fbd8b302c0c3bcd97fd3cf6ed4d0c 100644 (file)
@@ -571,13 +571,13 @@ static int imx_ssi_probe(struct platform_device *pdev)
        res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "tx0");
        if (res) {
                imx_pcm_dma_params_init_data(&ssi->filter_data_tx, res->start,
-                       false);
+                       IMX_DMATYPE_SSI);
        }
 
        res = platform_get_resource_byname(pdev, IORESOURCE_DMA, "rx0");
        if (res) {
                imx_pcm_dma_params_init_data(&ssi->filter_data_rx, res->start,
-                       false);
+                       IMX_DMATYPE_SSI);
        }
 
        platform_set_drvdata(pdev, ssi);
@@ -595,7 +595,12 @@ static int imx_ssi_probe(struct platform_device *pdev)
                goto failed_register;
        }
 
-       ret = imx_pcm_fiq_init(pdev);
+       ssi->fiq_params.irq = ssi->irq;
+       ssi->fiq_params.base = ssi->base;
+       ssi->fiq_params.dma_params_rx = &ssi->dma_params_rx;
+       ssi->fiq_params.dma_params_tx = &ssi->dma_params_tx;
+
+       ret = imx_pcm_fiq_init(pdev, &ssi->fiq_params);
        if (ret)
                goto failed_pcm_fiq;
 
index d5003cefca8decee028d937a9a9a975bb703102d..fb1616ba8c5967e1892b4ff6c7e180b2b9447047 100644 (file)
@@ -209,6 +209,7 @@ struct imx_ssi {
        struct snd_dmaengine_dai_dma_data dma_params_tx;
        struct imx_dma_data filter_data_tx;
        struct imx_dma_data filter_data_rx;
+       struct imx_pcm_fiq_params fiq_params;
 
        int enabled;
 };
index 52a36a90f4f4eb19c4739fda1714f48e715d1c68..1d70e278e9154e7f21d1ba230b4f573ad5b31f95 100644 (file)
@@ -217,7 +217,8 @@ static int imx_wm8962_probe(struct platform_device *pdev)
        codec_dev = of_find_i2c_device_by_node(codec_np);
        if (!codec_dev || !codec_dev->driver) {
                dev_err(&pdev->dev, "failed to find codec platform device\n");
-               return -EINVAL;
+               ret = -EINVAL;
+               goto fail;
        }
 
        data = devm_kzalloc(&pdev->dev, sizeof(*data), GFP_KERNEL);
index 6cf8355a8542656c89dda5f1564c721d3f2cb4aa..8c49147db84c43cf73d5870a4802959e66330ce2 100644 (file)
@@ -105,6 +105,7 @@ static int asoc_simple_card_remove(struct platform_device *pdev)
 static struct platform_driver asoc_simple_card = {
        .driver = {
                .name   = "asoc-simple-card",
+               .owner = THIS_MODULE,
        },
        .probe          = asoc_simple_card_probe,
        .remove         = asoc_simple_card_remove,
@@ -112,6 +113,7 @@ static struct platform_driver asoc_simple_card = {
 
 module_platform_driver(asoc_simple_card);
 
+MODULE_ALIAS("platform:asoc-simple-card");
 MODULE_LICENSE("GPL");
 MODULE_DESCRIPTION("ASoC Simple Sound Card");
 MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
index c62d715235e29ac5fa20639271d79958a57fd853..9e1970c44e86148b0ae0b4396977d0691b84adc6 100644 (file)
@@ -1,19 +1,15 @@
 config SND_KIRKWOOD_SOC
        tristate "SoC Audio for the Marvell Kirkwood chip"
-       depends on ARCH_KIRKWOOD
+       depends on ARCH_KIRKWOOD || COMPILE_TEST
        help
          Say Y or M if you want to add support for codecs attached to
          the Kirkwood I2S interface. You will also need to select the
          audio interfaces to support below.
 
-config SND_KIRKWOOD_SOC_I2S
-       tristate
-
 config SND_KIRKWOOD_SOC_OPENRD
        tristate "SoC Audio support for Kirkwood Openrd Client"
-       depends on SND_KIRKWOOD_SOC && (MACH_OPENRD_CLIENT || MACH_OPENRD_ULTIMATE)
+       depends on SND_KIRKWOOD_SOC && (MACH_OPENRD_CLIENT || MACH_OPENRD_ULTIMATE || COMPILE_TEST)
        depends on I2C
-       select SND_KIRKWOOD_SOC_I2S
        select SND_SOC_CS42L51
        help
          Say Y if you want to add support for SoC audio on
@@ -21,8 +17,7 @@ config SND_KIRKWOOD_SOC_OPENRD
 
 config SND_KIRKWOOD_SOC_T5325
        tristate "SoC Audio support for HP t5325"
-       depends on SND_KIRKWOOD_SOC && MACH_T5325 && I2C
-       select SND_KIRKWOOD_SOC_I2S
+       depends on SND_KIRKWOOD_SOC && (MACH_T5325 || COMPILE_TEST) && I2C
        select SND_SOC_ALC5623
        help
          Say Y if you want to add support for SoC audio on
index 3e62ae9e7bbeadc0924b05447cc41a01d894dd5b..9e781385cb88be80d8b4eb3da2b2090ca92f3a15 100644 (file)
@@ -1,8 +1,6 @@
-snd-soc-kirkwood-objs := kirkwood-dma.o
-snd-soc-kirkwood-i2s-objs := kirkwood-i2s.o
+snd-soc-kirkwood-objs := kirkwood-dma.o kirkwood-i2s.o
 
 obj-$(CONFIG_SND_KIRKWOOD_SOC) += snd-soc-kirkwood.o
-obj-$(CONFIG_SND_KIRKWOOD_SOC_I2S) += snd-soc-kirkwood-i2s.o
 
 snd-soc-openrd-objs := kirkwood-openrd.o
 snd-soc-t5325-objs := kirkwood-t5325.o
index a9f14530c3db6a5eb9d3425f7bd6ef5ba4106add..b238434f92b099db8bf3ba613fc596b7006ec96d 100644 (file)
         SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_LE | \
         SNDRV_PCM_FMTBIT_IEC958_SUBFRAME_BE)
 
-struct kirkwood_dma_priv {
-       struct snd_pcm_substream *play_stream;
-       struct snd_pcm_substream *rec_stream;
-       struct kirkwood_dma_data *data;
-};
+static struct kirkwood_dma_data *kirkwood_priv(struct snd_pcm_substream *subs)
+{
+       struct snd_soc_pcm_runtime *soc_runtime = subs->private_data;
+       return snd_soc_dai_get_drvdata(soc_runtime->cpu_dai);
+}
 
 static struct snd_pcm_hardware kirkwood_dma_snd_hw = {
        .info = (SNDRV_PCM_INFO_INTERLEAVED |
@@ -51,7 +51,7 @@ static struct snd_pcm_hardware kirkwood_dma_snd_hw = {
        .rate_max               = 384000,
        .channels_min           = 1,
        .channels_max           = 8,
-       .buffer_bytes_max       = KIRKWOOD_SND_MAX_PERIOD_BYTES * KIRKWOOD_SND_MAX_PERIODS,
+       .buffer_bytes_max       = KIRKWOOD_SND_MAX_BUFFER_BYTES,
        .period_bytes_min       = KIRKWOOD_SND_MIN_PERIOD_BYTES,
        .period_bytes_max       = KIRKWOOD_SND_MAX_PERIOD_BYTES,
        .periods_min            = KIRKWOOD_SND_MIN_PERIODS,
@@ -63,8 +63,7 @@ static u64 kirkwood_dma_dmamask = DMA_BIT_MASK(32);
 
 static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id)
 {
-       struct kirkwood_dma_priv *prdata = dev_id;
-       struct kirkwood_dma_data *priv = prdata->data;
+       struct kirkwood_dma_data *priv = dev_id;
        unsigned long mask, status, cause;
 
        mask = readl(priv->io + KIRKWOOD_INT_MASK);
@@ -89,10 +88,10 @@ static irqreturn_t kirkwood_dma_irq(int irq, void *dev_id)
        writel(status, priv->io + KIRKWOOD_INT_CAUSE);
 
        if (status & KIRKWOOD_INT_CAUSE_PLAY_BYTES)
-               snd_pcm_period_elapsed(prdata->play_stream);
+               snd_pcm_period_elapsed(priv->substream_play);
 
        if (status & KIRKWOOD_INT_CAUSE_REC_BYTES)
-               snd_pcm_period_elapsed(prdata->rec_stream);
+               snd_pcm_period_elapsed(priv->substream_rec);
 
        return IRQ_HANDLED;
 }
@@ -126,15 +125,10 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream)
 {
        int err;
        struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
-       struct snd_soc_platform *platform = soc_runtime->platform;
-       struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
-       struct kirkwood_dma_data *priv;
-       struct kirkwood_dma_priv *prdata = snd_soc_platform_get_drvdata(platform);
+       struct kirkwood_dma_data *priv = kirkwood_priv(substream);
        const struct mbus_dram_target_info *dram;
        unsigned long addr;
 
-       priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
        snd_soc_set_runtime_hwparams(substream, &kirkwood_dma_snd_hw);
 
        /* Ensure that all constraints linked to dma burst are fulfilled */
@@ -157,21 +151,11 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream)
        if (err < 0)
                return err;
 
-       if (prdata == NULL) {
-               prdata = kzalloc(sizeof(struct kirkwood_dma_priv), GFP_KERNEL);
-               if (prdata == NULL)
-                       return -ENOMEM;
-
-               prdata->data = priv;
-
+       if (!priv->substream_play && !priv->substream_rec) {
                err = request_irq(priv->irq, kirkwood_dma_irq, IRQF_SHARED,
-                                 "kirkwood-i2s", prdata);
-               if (err) {
-                       kfree(prdata);
+                                 "kirkwood-i2s", priv);
+               if (err)
                        return -EBUSY;
-               }
-
-               snd_soc_platform_set_drvdata(platform, prdata);
 
                /*
                 * Enable Error interrupts. We're only ack'ing them but
@@ -183,11 +167,11 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream)
        dram = mv_mbus_dram_info();
        addr = substream->dma_buffer.addr;
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               prdata->play_stream = substream;
+               priv->substream_play = substream;
                kirkwood_dma_conf_mbus_windows(priv->io,
                        KIRKWOOD_PLAYBACK_WIN, addr, dram);
        } else {
-               prdata->rec_stream = substream;
+               priv->substream_rec = substream;
                kirkwood_dma_conf_mbus_windows(priv->io,
                        KIRKWOOD_RECORD_WIN, addr, dram);
        }
@@ -197,27 +181,19 @@ static int kirkwood_dma_open(struct snd_pcm_substream *substream)
 
 static int kirkwood_dma_close(struct snd_pcm_substream *substream)
 {
-       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
-       struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
-       struct snd_soc_platform *platform = soc_runtime->platform;
-       struct kirkwood_dma_priv *prdata = snd_soc_platform_get_drvdata(platform);
-       struct kirkwood_dma_data *priv;
-
-       priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
+       struct kirkwood_dma_data *priv = kirkwood_priv(substream);
 
-       if (!prdata || !priv)
+       if (!priv)
                return 0;
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
-               prdata->play_stream = NULL;
+               priv->substream_play = NULL;
        else
-               prdata->rec_stream = NULL;
+               priv->substream_rec = NULL;
 
-       if (!prdata->play_stream && !prdata->rec_stream) {
+       if (!priv->substream_play && !priv->substream_rec) {
                writel(0, priv->io + KIRKWOOD_ERR_MASK);
-               free_irq(priv->irq, prdata);
-               kfree(prdata);
-               snd_soc_platform_set_drvdata(platform, NULL);
+               free_irq(priv->irq, priv);
        }
 
        return 0;
@@ -243,13 +219,9 @@ static int kirkwood_dma_hw_free(struct snd_pcm_substream *substream)
 static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
 {
        struct snd_pcm_runtime *runtime = substream->runtime;
-       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
-       struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
-       struct kirkwood_dma_data *priv;
+       struct kirkwood_dma_data *priv = kirkwood_priv(substream);
        unsigned long size, count;
 
-       priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
-
        /* compute buffer size in term of "words" as requested in specs */
        size = frames_to_bytes(runtime, runtime->buffer_size);
        size = (size>>2)-1;
@@ -272,13 +244,9 @@ static int kirkwood_dma_prepare(struct snd_pcm_substream *substream)
 static snd_pcm_uframes_t kirkwood_dma_pointer(struct snd_pcm_substream
                                                *substream)
 {
-       struct snd_soc_pcm_runtime *soc_runtime = substream->private_data;
-       struct snd_soc_dai *cpu_dai = soc_runtime->cpu_dai;
-       struct kirkwood_dma_data *priv;
+       struct kirkwood_dma_data *priv = kirkwood_priv(substream);
        snd_pcm_uframes_t count;
 
-       priv = snd_soc_dai_get_dma_data(cpu_dai, substream);
-
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                count = bytes_to_frames(substream->runtime,
                        readl(priv->io + KIRKWOOD_PLAY_BYTE_COUNT));
@@ -366,36 +334,8 @@ static void kirkwood_dma_free_dma_buffers(struct snd_pcm *pcm)
        }
 }
 
-static struct snd_soc_platform_driver kirkwood_soc_platform = {
+struct snd_soc_platform_driver kirkwood_soc_platform = {
        .ops            = &kirkwood_dma_ops,
        .pcm_new        = kirkwood_dma_new,
        .pcm_free       = kirkwood_dma_free_dma_buffers,
 };
-
-static int kirkwood_soc_platform_probe(struct platform_device *pdev)
-{
-       return snd_soc_register_platform(&pdev->dev, &kirkwood_soc_platform);
-}
-
-static int kirkwood_soc_platform_remove(struct platform_device *pdev)
-{
-       snd_soc_unregister_platform(&pdev->dev);
-       return 0;
-}
-
-static struct platform_driver kirkwood_pcm_driver = {
-       .driver = {
-                       .name = "kirkwood-pcm-audio",
-                       .owner = THIS_MODULE,
-       },
-
-       .probe = kirkwood_soc_platform_probe,
-       .remove = kirkwood_soc_platform_remove,
-};
-
-module_platform_driver(kirkwood_pcm_driver);
-
-MODULE_AUTHOR("Arnaud Patard <arnaud.patard@rtp-net.org>");
-MODULE_DESCRIPTION("Marvell Kirkwood Audio DMA module");
-MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:kirkwood-pcm-audio");
index 4c9dad3263c5f7b754d108b3bf66a3ebdb6d67f3..e5f3f7a9ea269cc0d4ef28868ac8148fe8d90013 100644 (file)
 #include <linux/platform_data/asoc-kirkwood.h>
 #include "kirkwood.h"
 
-#define DRV_NAME       "kirkwood-i2s"
+#define DRV_NAME       "mvebu-audio"
 
-#define KIRKWOOD_I2S_RATES \
-       (SNDRV_PCM_RATE_44100 | \
-        SNDRV_PCM_RATE_48000 | SNDRV_PCM_RATE_96000)
 #define KIRKWOOD_I2S_FORMATS \
        (SNDRV_PCM_FMTBIT_S16_LE | \
         SNDRV_PCM_FMTBIT_S24_LE | \
@@ -105,14 +102,16 @@ static void kirkwood_set_rate(struct snd_soc_dai *dai,
        uint32_t clks_ctrl;
 
        if (rate == 44100 || rate == 48000 || rate == 96000) {
-               /* use internal dco for supported rates */
+               /* use internal dco for the supported rates
+                * defined in kirkwood_i2s_dai */
                dev_dbg(dai->dev, "%s: dco set rate = %lu\n",
                        __func__, rate);
                kirkwood_set_dco(priv->io, rate);
 
                clks_ctrl = KIRKWOOD_MCLK_SOURCE_DCO;
-       } else if (!IS_ERR(priv->extclk)) {
-               /* use optional external clk for other rates */
+       } else {
+               /* use the external clock for the other rates
+                * defined in kirkwood_i2s_dai_extclk */
                dev_dbg(dai->dev, "%s: extclk set rate = %lu -> %lu\n",
                        __func__, rate, 256 * rate);
                clk_set_rate(priv->extclk, 256 * rate);
@@ -199,8 +198,7 @@ static int kirkwood_i2s_hw_params(struct snd_pcm_substream *substream,
                        ctl_play |= KIRKWOOD_PLAYCTL_MONO_OFF;
 
                priv->ctl_play &= ~(KIRKWOOD_PLAYCTL_MONO_MASK |
-                                   KIRKWOOD_PLAYCTL_I2S_EN |
-                                   KIRKWOOD_PLAYCTL_SPDIF_EN |
+                                   KIRKWOOD_PLAYCTL_ENABLE_MASK |
                                    KIRKWOOD_PLAYCTL_SIZE_MASK);
                priv->ctl_play |= ctl_play;
        } else {
@@ -244,8 +242,7 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
        case SNDRV_PCM_TRIGGER_START:
                /* configure */
                ctl = priv->ctl_play;
-               value = ctl & ~(KIRKWOOD_PLAYCTL_I2S_EN |
-                               KIRKWOOD_PLAYCTL_SPDIF_EN);
+               value = ctl & ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
                writel(value, priv->io + KIRKWOOD_PLAYCTL);
 
                /* enable interrupts */
@@ -267,7 +264,7 @@ static int kirkwood_i2s_play_trigger(struct snd_pcm_substream *substream,
                writel(value, priv->io + KIRKWOOD_INT_MASK);
 
                /* disable all playbacks */
-               ctl &= ~(KIRKWOOD_PLAYCTL_I2S_EN | KIRKWOOD_PLAYCTL_SPDIF_EN);
+               ctl &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
                writel(ctl, priv->io + KIRKWOOD_PLAYCTL);
                break;
 
@@ -387,7 +384,7 @@ static int kirkwood_i2s_probe(struct snd_soc_dai *dai)
 
        /* disable playback/record */
        value = readl(priv->io + KIRKWOOD_PLAYCTL);
-       value &= ~(KIRKWOOD_PLAYCTL_I2S_EN|KIRKWOOD_PLAYCTL_SPDIF_EN);
+       value &= ~KIRKWOOD_PLAYCTL_ENABLE_MASK;
        writel(value, priv->io + KIRKWOOD_PLAYCTL);
 
        value = readl(priv->io + KIRKWOOD_RECCTL);
@@ -398,11 +395,6 @@ static int kirkwood_i2s_probe(struct snd_soc_dai *dai)
 
 }
 
-static int kirkwood_i2s_remove(struct snd_soc_dai *dai)
-{
-       return 0;
-}
-
 static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
        .startup        = kirkwood_i2s_startup,
        .trigger        = kirkwood_i2s_trigger,
@@ -413,17 +405,18 @@ static const struct snd_soc_dai_ops kirkwood_i2s_dai_ops = {
 
 static struct snd_soc_dai_driver kirkwood_i2s_dai = {
        .probe = kirkwood_i2s_probe,
-       .remove = kirkwood_i2s_remove,
        .playback = {
                .channels_min = 1,
                .channels_max = 2,
-               .rates = KIRKWOOD_I2S_RATES,
+               .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+                               SNDRV_PCM_RATE_96000,
                .formats = KIRKWOOD_I2S_FORMATS,
        },
        .capture = {
                .channels_min = 1,
                .channels_max = 2,
-               .rates = KIRKWOOD_I2S_RATES,
+               .rates = SNDRV_PCM_RATE_44100 | SNDRV_PCM_RATE_48000 |
+                               SNDRV_PCM_RATE_96000,
                .formats = KIRKWOOD_I2S_FORMATS,
        },
        .ops = &kirkwood_i2s_dai_ops,
@@ -431,7 +424,6 @@ static struct snd_soc_dai_driver kirkwood_i2s_dai = {
 
 static struct snd_soc_dai_driver kirkwood_i2s_dai_extclk = {
        .probe = kirkwood_i2s_probe,
-       .remove = kirkwood_i2s_remove,
        .playback = {
                .channels_min = 1,
                .channels_max = 2,
@@ -498,10 +490,10 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
        if (err < 0)
                return err;
 
-       priv->extclk = clk_get(&pdev->dev, "extclk");
+       priv->extclk = devm_clk_get(&pdev->dev, "extclk");
        if (!IS_ERR(priv->extclk)) {
                if (priv->extclk == priv->clk) {
-                       clk_put(priv->extclk);
+                       devm_clk_put(&pdev->dev, priv->extclk);
                        priv->extclk = ERR_PTR(-EINVAL);
                } else {
                        dev_info(&pdev->dev, "found external clock\n");
@@ -525,14 +517,22 @@ static int kirkwood_i2s_dev_probe(struct platform_device *pdev)
 
        err = snd_soc_register_component(&pdev->dev, &kirkwood_i2s_component,
                                         soc_dai, 1);
-       if (!err)
-               return 0;
-       dev_err(&pdev->dev, "snd_soc_register_component failed\n");
+       if (err) {
+               dev_err(&pdev->dev, "snd_soc_register_component failed\n");
+               goto err_component;
+       }
 
-       if (!IS_ERR(priv->extclk)) {
-               clk_disable_unprepare(priv->extclk);
-               clk_put(priv->extclk);
+       err = snd_soc_register_platform(&pdev->dev, &kirkwood_soc_platform);
+       if (err) {
+               dev_err(&pdev->dev, "snd_soc_register_platform failed\n");
+               goto err_platform;
        }
+       return 0;
+ err_platform:
+       snd_soc_unregister_component(&pdev->dev);
+ err_component:
+       if (!IS_ERR(priv->extclk))
+               clk_disable_unprepare(priv->extclk);
        clk_disable_unprepare(priv->clk);
 
        return err;
@@ -542,12 +542,11 @@ static int kirkwood_i2s_dev_remove(struct platform_device *pdev)
 {
        struct kirkwood_dma_data *priv = dev_get_drvdata(&pdev->dev);
 
+       snd_soc_unregister_platform(&pdev->dev);
        snd_soc_unregister_component(&pdev->dev);
 
-       if (!IS_ERR(priv->extclk)) {
+       if (!IS_ERR(priv->extclk))
                clk_disable_unprepare(priv->extclk);
-               clk_put(priv->extclk);
-       }
        clk_disable_unprepare(priv->clk);
 
        return 0;
@@ -568,4 +567,4 @@ module_platform_driver(kirkwood_i2s_driver);
 MODULE_AUTHOR("Arnaud Patard, <arnaud.patard@rtp-net.org>");
 MODULE_DESCRIPTION("Kirkwood I2S SoC Interface");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:kirkwood-i2s");
+MODULE_ALIAS("platform:mvebu-audio");
index b979c7154715a0ee9407c908f76f9f1ca9f91748..025be0e97164283cbd4ed7516a477bb15bee404b 100644 (file)
@@ -16,9 +16,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <sound/soc.h>
-#include <mach/kirkwood.h>
 #include <linux/platform_data/asoc-kirkwood.h>
-#include <asm/mach-types.h>
 #include "../codecs/cs42l51.h"
 
 static int openrd_client_hw_params(struct snd_pcm_substream *substream,
@@ -54,8 +52,8 @@ static struct snd_soc_dai_link openrd_client_dai[] = {
 {
        .name = "CS42L51",
        .stream_name = "CS42L51 HiFi",
-       .cpu_dai_name = "kirkwood-i2s",
-       .platform_name = "kirkwood-pcm-audio",
+       .cpu_dai_name = "mvebu-audio",
+       .platform_name = "mvebu-audio",
        .codec_dai_name = "cs42l51-hifi",
        .codec_name = "cs42l51-codec.0-004a",
        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
index 1d0ed6f8add7ba5a7236f428476ab6f981522ab6..27545b0c48561470e787c3c167c60514172f10aa 100644 (file)
@@ -15,9 +15,7 @@
 #include <linux/platform_device.h>
 #include <linux/slab.h>
 #include <sound/soc.h>
-#include <mach/kirkwood.h>
 #include <linux/platform_data/asoc-kirkwood.h>
-#include <asm/mach-types.h>
 #include "../codecs/alc5623.h"
 
 static int t5325_hw_params(struct snd_pcm_substream *substream,
@@ -70,8 +68,8 @@ static struct snd_soc_dai_link t5325_dai[] = {
 {
        .name = "ALC5621",
        .stream_name = "ALC5621 HiFi",
-       .cpu_dai_name = "kirkwood-i2s",
-       .platform_name = "kirkwood-pcm-audio",
+       .cpu_dai_name = "mvebu-audio",
+       .platform_name = "mvebu-audio",
        .codec_dai_name = "alc5621-hifi",
        .codec_name = "alc562x-codec.0-001a",
        .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_CBS_CFS,
index 4d92637ddb3fd878f86e4d4525fbd024d94b2618..f8e1ccc1c58cc76f8ce2a314a1c8a0fcc5e998a5 100644 (file)
@@ -54,7 +54,7 @@
 #define KIRKWOOD_PLAYCTL_MONO_OFF              (0<<5)
 #define KIRKWOOD_PLAYCTL_I2S_MUTE              (1<<7)
 #define KIRKWOOD_PLAYCTL_SPDIF_EN              (1<<4)
-#define KIRKWOOD_PLAYCTL_I2S_EN                (1<<3)
+#define KIRKWOOD_PLAYCTL_I2S_EN                        (1<<3)
 #define KIRKWOOD_PLAYCTL_SIZE_MASK             (7<<0)
 #define KIRKWOOD_PLAYCTL_SIZE_16               (7<<0)
 #define KIRKWOOD_PLAYCTL_SIZE_16_C             (3<<0)
@@ -62,6 +62,9 @@
 #define KIRKWOOD_PLAYCTL_SIZE_24               (1<<0)
 #define KIRKWOOD_PLAYCTL_SIZE_32               (0<<0)
 
+#define KIRKWOOD_PLAYCTL_ENABLE_MASK           (KIRKWOOD_PLAYCTL_SPDIF_EN | \
+                                                KIRKWOOD_PLAYCTL_I2S_EN)
+
 #define KIRKWOOD_PLAY_BUF_ADDR                 0x1104
 #define KIRKWOOD_PLAY_BUF_SIZE                 0x1108
 #define KIRKWOOD_PLAY_BYTE_COUNT               0x110C
 #define KIRKWOOD_SND_MAX_PERIODS               16
 #define KIRKWOOD_SND_MIN_PERIOD_BYTES          0x4000
 #define KIRKWOOD_SND_MAX_PERIOD_BYTES          0x4000
+#define KIRKWOOD_SND_MAX_BUFFER_BYTES          (KIRKWOOD_SND_MAX_PERIOD_BYTES \
+                                                * KIRKWOOD_SND_MAX_PERIODS)
 
 struct kirkwood_dma_data {
        void __iomem *io;
@@ -129,8 +134,12 @@ struct kirkwood_dma_data {
        struct clk *extclk;
        uint32_t ctl_play;
        uint32_t ctl_rec;
+       struct snd_pcm_substream *substream_play;
+       struct snd_pcm_substream *substream_rec;
        int irq;
        int burst;
 };
 
+extern struct snd_soc_platform_driver kirkwood_soc_platform;
+
 #endif
index 78d321cbe8b44ab0b06c9142da04d47d725dedf4..219235c022125d84a7565a4c9c962dd5098990d0 100644 (file)
@@ -1,6 +1,7 @@
 menuconfig SND_MXS_SOC
        tristate "SoC Audio for Freescale MXS CPUs"
-       depends on ARCH_MXS
+       depends on ARCH_MXS || COMPILE_TEST
+       depends on COMMON_CLK
        select SND_SOC_GENERIC_DMAENGINE_PCM
        help
          Say Y or M if you want to add support for codecs attached to
index 54511c5e6a7ce8a75a2bc5897dd6a7d6085e007d..b56b8a0e8deb43b6fdca0668fb43677bb1ce47ba 100644 (file)
@@ -31,7 +31,6 @@
 #include <sound/pcm.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
-#include <asm/mach-types.h>
 
 #include "mxs-saif.h"
 
index 1b134d72f120a60b5956032986b964c6f659ab0e..ce084eb10c49f2f826fac137a14e0ba48e98db6b 100644 (file)
@@ -25,7 +25,6 @@
 #include <sound/soc.h>
 #include <sound/jack.h>
 #include <sound/soc-dapm.h>
-#include <asm/mach-types.h>
 
 #include "../codecs/sgtl5000.h"
 #include "mxs-saif.h"
@@ -51,18 +50,27 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
        }
 
        /* Sgtl5000 sysclk should be >= 8MHz and <= 27M */
-       if (mclk < 8000000 || mclk > 27000000)
+       if (mclk < 8000000 || mclk > 27000000) {
+               dev_err(codec_dai->dev, "Invalid mclk frequency: %u.%03uMHz\n",
+                       mclk / 1000000, mclk / 1000 % 1000);
                return -EINVAL;
+       }
 
        /* Set SGTL5000's SYSCLK (provided by SAIF MCLK) */
        ret = snd_soc_dai_set_sysclk(codec_dai, SGTL5000_SYSCLK, mclk, 0);
-       if (ret)
+       if (ret) {
+               dev_err(codec_dai->dev, "Failed to set sysclk to %u.%03uMHz\n",
+                       mclk / 1000000, mclk / 1000 % 1000);
                return ret;
+       }
 
        /* The SAIF MCLK should be the same as SGTL5000_SYSCLK */
        ret = snd_soc_dai_set_sysclk(cpu_dai, MXS_SAIF_MCLK, mclk, 0);
-       if (ret)
+       if (ret) {
+               dev_err(cpu_dai->dev, "Failed to set sysclk to %u.%03uMHz\n",
+                       mclk / 1000000, mclk / 1000 % 1000);
                return ret;
+       }
 
        /* set codec to slave mode */
        dai_format = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
@@ -70,13 +78,19 @@ static int mxs_sgtl5000_hw_params(struct snd_pcm_substream *substream,
 
        /* set codec DAI configuration */
        ret = snd_soc_dai_set_fmt(codec_dai, dai_format);
-       if (ret)
+       if (ret) {
+               dev_err(codec_dai->dev, "Failed to set dai format to %08x\n",
+                       dai_format);
                return ret;
+       }
 
        /* set cpu DAI configuration */
        ret = snd_soc_dai_set_fmt(cpu_dai, dai_format);
-       if (ret)
+       if (ret) {
+               dev_err(cpu_dai->dev, "Failed to set dai format to %08x\n",
+                       dai_format);
                return ret;
+       }
 
        return 0;
 }
@@ -154,8 +168,10 @@ static int mxs_sgtl5000_probe(struct platform_device *pdev)
         * should be >= 8MHz and <= 27M.
         */
        ret = mxs_saif_get_mclk(0, 44100 * 256, 44100);
-       if (ret)
+       if (ret) {
+               dev_err(&pdev->dev, "failed to get mclk\n");
                return ret;
+       }
 
        card->dev = &pdev->dev;
        platform_set_drvdata(pdev, card);
index f4c2417a8730bcf0ae99c37fe1bfcc76a685cb51..8987bf987e584f6ba7a53b93516056dfbf5bc9b5 100644 (file)
@@ -333,9 +333,6 @@ static int nuc900_ac97_drvprobe(struct platform_device *pdev)
        spin_lock_init(&nuc900_audio->lock);
 
        nuc900_audio->res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!nuc900_audio->res)
-               return ret;
-
        nuc900_audio->mmio = devm_ioremap_resource(&pdev->dev,
                                                   nuc900_audio->res);
        if (IS_ERR(nuc900_audio->mmio))
index 9f5d55e6b17a53d7e50f2a3c392f565b0fb2e014..daa78a0095facf5ff35d1eb0b6ca61836b7c9210 100644 (file)
@@ -1,7 +1,7 @@
 config SND_OMAP_SOC
        tristate "SoC Audio for the Texas Instruments OMAP chips"
-       depends on ARCH_OMAP && DMA_OMAP
-       select SND_SOC_DMAENGINE_PCM
+       depends on (ARCH_OMAP && DMA_OMAP) || (ARCH_ARM && COMPILE_TEST)
+       select SND_DMAENGINE_PCM
 
 config SND_OMAP_SOC_DMIC
        tristate
@@ -26,7 +26,7 @@ config SND_OMAP_SOC_N810
 
 config SND_OMAP_SOC_RX51
        tristate "SoC Audio support for Nokia RX-51"
-       depends on SND_OMAP_SOC && MACH_NOKIA_RX51
+       depends on SND_OMAP_SOC && ARCH_ARM && (MACH_NOKIA_RX51 || COMPILE_TEST)
        select SND_OMAP_SOC_MCBSP
        select SND_SOC_TLV320AIC3X
        select SND_SOC_TPA6130A2
@@ -87,7 +87,7 @@ config SND_OMAP_SOC_OMAP_TWL4030
 
 config SND_OMAP_SOC_OMAP_ABE_TWL6040
        tristate "SoC Audio support for OMAP boards using ABE and twl6040 codec"
-       depends on TWL6040_CORE && SND_OMAP_SOC && ARCH_OMAP4
+       depends on TWL6040_CORE && SND_OMAP_SOC && (ARCH_OMAP4 || COMPILE_TEST)
        select SND_OMAP_SOC_DMIC
        select SND_OMAP_SOC_MCPDM
        select SND_SOC_TWL6040
index 361e4c03646e32a9b73d76122a13e3e071c7a9b0..83433fdea32ad790f839edd357c14824def13c07 100644 (file)
@@ -781,7 +781,7 @@ static ssize_t prop##_store(struct device *dev,                             \
        unsigned long val;                                              \
        int status;                                                     \
                                                                        \
-       status = strict_strtoul(buf, 0, &val);                          \
+       status = kstrtoul(buf, 0, &val);                                \
        if (status)                                                     \
                return status;                                          \
                                                                        \
index 70cd5c7b2e145622b031ffd320786599738091de..ebb13906b3a0b41b4d3c20509c613378da5ae248 100644 (file)
@@ -23,7 +23,6 @@
 #include <linux/clk.h>
 #include <linux/platform_device.h>
 #include <linux/mfd/twl6040.h>
-#include <linux/platform_data/omap-abe-twl6040.h>
 #include <linux/module.h>
 #include <linux/of.h>
 
@@ -166,19 +165,10 @@ static const struct snd_soc_dapm_route audio_map[] = {
        {"AFMR", NULL, "Line In"},
 };
 
-static inline void twl6040_disconnect_pin(struct snd_soc_dapm_context *dapm,
-                                         int connected, char *pin)
-{
-       if (!connected)
-               snd_soc_dapm_disable_pin(dapm, pin);
-}
-
 static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
 {
        struct snd_soc_codec *codec = rtd->codec;
        struct snd_soc_card *card = codec->card;
-       struct snd_soc_dapm_context *dapm = &codec->dapm;
-       struct omap_abe_twl6040_data *pdata = dev_get_platdata(card->dev);
        struct abe_twl6040 *priv = snd_soc_card_get_drvdata(card);
        int hs_trim;
        int ret = 0;
@@ -203,24 +193,6 @@ static int omap_abe_twl6040_init(struct snd_soc_pcm_runtime *rtd)
                twl6040_hs_jack_detect(codec, &hs_jack, SND_JACK_HEADSET);
        }
 
-       /*
-        * NULL pdata means we booted with DT. In this case the routing is
-        * provided and the card is fully routed, no need to mark pins.
-        */
-       if (!pdata)
-               return ret;
-
-       /* Disable not connected paths if not used */
-       twl6040_disconnect_pin(dapm, pdata->has_hs, "Headset Stereophone");
-       twl6040_disconnect_pin(dapm, pdata->has_hf, "Ext Spk");
-       twl6040_disconnect_pin(dapm, pdata->has_ep, "Earphone Spk");
-       twl6040_disconnect_pin(dapm, pdata->has_aux, "Line Out");
-       twl6040_disconnect_pin(dapm, pdata->has_vibra, "Vibrator");
-       twl6040_disconnect_pin(dapm, pdata->has_hsmic, "Headset Mic");
-       twl6040_disconnect_pin(dapm, pdata->has_mainmic, "Main Handset Mic");
-       twl6040_disconnect_pin(dapm, pdata->has_submic, "Sub Handset Mic");
-       twl6040_disconnect_pin(dapm, pdata->has_afm, "Line In");
-
        return ret;
 }
 
@@ -274,13 +246,18 @@ static struct snd_soc_card omap_abe_card = {
 
 static int omap_abe_probe(struct platform_device *pdev)
 {
-       struct omap_abe_twl6040_data *pdata = dev_get_platdata(&pdev->dev);
        struct device_node *node = pdev->dev.of_node;
        struct snd_soc_card *card = &omap_abe_card;
+       struct device_node *dai_node;
        struct abe_twl6040 *priv;
        int num_links = 0;
        int ret = 0;
 
+       if (!node) {
+               dev_err(&pdev->dev, "of node is missing.\n");
+               return -ENODEV;
+       }
+
        card->dev = &pdev->dev;
 
        priv = devm_kzalloc(&pdev->dev, sizeof(struct abe_twl6040), GFP_KERNEL);
@@ -289,78 +266,50 @@ static int omap_abe_probe(struct platform_device *pdev)
 
        priv->dmic_codec_dev = ERR_PTR(-EINVAL);
 
-       if (node) {
-               struct device_node *dai_node;
-
-               if (snd_soc_of_parse_card_name(card, "ti,model")) {
-                       dev_err(&pdev->dev, "Card name is not provided\n");
-                       return -ENODEV;
-               }
+       if (snd_soc_of_parse_card_name(card, "ti,model")) {
+               dev_err(&pdev->dev, "Card name is not provided\n");
+               return -ENODEV;
+       }
 
-               ret = snd_soc_of_parse_audio_routing(card,
-                                               "ti,audio-routing");
-               if (ret) {
-                       dev_err(&pdev->dev,
-                               "Error while parsing DAPM routing\n");
-                       return ret;
-               }
+       ret = snd_soc_of_parse_audio_routing(card, "ti,audio-routing");
+       if (ret) {
+               dev_err(&pdev->dev, "Error while parsing DAPM routing\n");
+               return ret;
+       }
 
-               dai_node = of_parse_phandle(node, "ti,mcpdm", 0);
-               if (!dai_node) {
-                       dev_err(&pdev->dev, "McPDM node is not provided\n");
-                       return -EINVAL;
-               }
-               abe_twl6040_dai_links[0].cpu_dai_name  = NULL;
-               abe_twl6040_dai_links[0].cpu_of_node = dai_node;
+       dai_node = of_parse_phandle(node, "ti,mcpdm", 0);
+       if (!dai_node) {
+               dev_err(&pdev->dev, "McPDM node is not provided\n");
+               return -EINVAL;
+       }
+       abe_twl6040_dai_links[0].cpu_dai_name  = NULL;
+       abe_twl6040_dai_links[0].cpu_of_node = dai_node;
 
-               dai_node = of_parse_phandle(node, "ti,dmic", 0);
-               if (dai_node) {
-                       num_links = 2;
-                       abe_twl6040_dai_links[1].cpu_dai_name  = NULL;
-                       abe_twl6040_dai_links[1].cpu_of_node = dai_node;
+       dai_node = of_parse_phandle(node, "ti,dmic", 0);
+       if (dai_node) {
+               num_links = 2;
+               abe_twl6040_dai_links[1].cpu_dai_name  = NULL;
+               abe_twl6040_dai_links[1].cpu_of_node = dai_node;
 
-                       priv->dmic_codec_dev = platform_device_register_simple(
+               priv->dmic_codec_dev = platform_device_register_simple(
                                                "dmic-codec", -1, NULL, 0);
-                       if (IS_ERR(priv->dmic_codec_dev)) {
-                               dev_err(&pdev->dev,
-                                       "Can't instantiate dmic-codec\n");
-                               return PTR_ERR(priv->dmic_codec_dev);
-                       }
-               } else {
-                       num_links = 1;
-               }
-
-               priv->jack_detection = of_property_read_bool(node,
-                                                          "ti,jack-detection");
-               of_property_read_u32(node, "ti,mclk-freq",
-                                    &priv->mclk_freq);
-               if (!priv->mclk_freq) {
-                       dev_err(&pdev->dev, "MCLK frequency not provided\n");
-                       ret = -EINVAL;
-                       goto err_unregister;
+               if (IS_ERR(priv->dmic_codec_dev)) {
+                       dev_err(&pdev->dev, "Can't instantiate dmic-codec\n");
+                       return PTR_ERR(priv->dmic_codec_dev);
                }
-
-               omap_abe_card.fully_routed = 1;
-       } else if (pdata) {
-               if (pdata->card_name) {
-                       card->name = pdata->card_name;
-               } else {
-                       dev_err(&pdev->dev, "Card name is not provided\n");
-                       return -ENODEV;
-               }
-
-               if (pdata->has_dmic)
-                       num_links = 2;
-               else
-                       num_links = 1;
-
-               priv->jack_detection = pdata->jack_detection;
-               priv->mclk_freq = pdata->mclk_freq;
        } else {
-               dev_err(&pdev->dev, "Missing pdata\n");
-               return -ENODEV;
+               num_links = 1;
+       }
+
+       priv->jack_detection = of_property_read_bool(node, "ti,jack-detection");
+       of_property_read_u32(node, "ti,mclk-freq", &priv->mclk_freq);
+       if (!priv->mclk_freq) {
+               dev_err(&pdev->dev, "MCLK frequency not provided\n");
+               ret = -EINVAL;
+               goto err_unregister;
        }
 
+       card->fully_routed = 1;
 
        if (!priv->mclk_freq) {
                dev_err(&pdev->dev, "MCLK frequency missing\n");
index 4db1f8e6e17268e042555fcae6f732d17fed7946..12e566be3793e61eb4fdd7d9ac47a23d638bc260 100644 (file)
@@ -480,15 +480,12 @@ static int asoc_dmic_probe(struct platform_device *pdev)
        dmic->dma_data.filter_data = "up_link";
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
-       if (!res) {
-               dev_err(dmic->dev, "invalid memory resource\n");
-               ret = -ENODEV;
+       dmic->io_base = devm_ioremap_resource(&pdev->dev, res);
+       if (IS_ERR(dmic->io_base)) {
+               ret = PTR_ERR(dmic->io_base);
                goto err_put_clk;
        }
 
-       dmic->io_base = devm_ioremap_resource(&pdev->dev, res);
-       if (IS_ERR(dmic->io_base))
-               return PTR_ERR(dmic->io_base);
 
        ret = snd_soc_register_component(&pdev->dev, &omap_dmic_component,
                                         &omap_dmic_dai, 1);
index 7483efb6dc674e9bdb90e9ea3bd39a7505a9c34b..6c19bba2357004903477a7300582d193b26f2962 100644 (file)
@@ -433,6 +433,11 @@ static int omap_mcbsp_dai_set_dai_fmt(struct snd_soc_dai *cpu_dai,
                /* Sample rate generator drives the FS */
                regs->srgr2     |= FSGM;
                break;
+       case SND_SOC_DAIFMT_CBM_CFS:
+               /* McBSP slave. FS clock as output */
+               regs->srgr2     |= FSGM;
+               regs->pcr0      |= FSXM;
+               break;
        case SND_SOC_DAIFMT_CBM_CFM:
                /* McBSP slave */
                break;
index a49dc52f8abc03fac075a56c879aa2bdcaaa5568..90d2a7cd2563d06170d450b5bb6a9cc8a6615210 100644 (file)
@@ -480,9 +480,6 @@ static int asoc_mcpdm_probe(struct platform_device *pdev)
        mcpdm->dma_data[1].filter_data = "up_link";
 
        res = platform_get_resource_byname(pdev, IORESOURCE_MEM, "mpu");
-       if (res == NULL)
-               return -ENOMEM;
-
        mcpdm->io_base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(mcpdm->io_base))
                return PTR_ERR(mcpdm->io_base);
index b3580946754702b87a883220d547aeee26b04388..4db74a083db1ef792f90dde317ee9e7556eb4e1d 100644 (file)
@@ -11,7 +11,7 @@ config SND_PXA2XX_SOC
 config SND_MMP_SOC
        bool "Soc Audio for Marvell MMP chips"
        depends on ARCH_MMP
-       select SND_SOC_DMAENGINE_PCM
+       select SND_DMAENGINE_PCM
        select SND_ARM
        help
          Say Y if you want to add support for codecs attached to
index 4ad76099dd43fdec0d9e7262db77f1e07cd20713..5b7d969f89a96eebd418687ef5b7abc79d74ee97 100644 (file)
@@ -129,6 +129,7 @@ static struct snd_soc_dai_link brownstone_wm8994_dai[] = {
 /* audio machine driver */
 static struct snd_soc_card brownstone = {
        .name         = "brownstone",
+       .owner        = THIS_MODULE,
        .dai_link     = brownstone_wm8994_dai,
        .num_links    = ARRAY_SIZE(brownstone_wm8994_dai),
 
index 97b711e12821e02c2b62c6ceb377fb4a942ee71f..bbea7780eac640630803a024d3d0670b01c3c1eb 100644 (file)
@@ -56,8 +56,6 @@
 #include "pxa2xx-ac97.h"
 #include "../codecs/wm9713.h"
 
-#define ARRAY_AND_SIZE(x)      (x), ARRAY_SIZE(x)
-
 #define AC97_GPIO_PULL         0x58
 
 /* Use GPIO8 for rear speaker amplifier */
@@ -133,10 +131,11 @@ static int mioa701_wm9713_init(struct snd_soc_pcm_runtime *rtd)
        unsigned short reg;
 
        /* Add mioa701 specific widgets */
-       snd_soc_dapm_new_controls(dapm, ARRAY_AND_SIZE(mioa701_dapm_widgets));
+       snd_soc_dapm_new_controls(dapm, mioa701_dapm_widgets,
+                                 ARRAY_SIZE(mioa701_dapm_widgets));
 
        /* Set up mioa701 specific audio path audio_mapnects */
-       snd_soc_dapm_add_routes(dapm, ARRAY_AND_SIZE(audio_map));
+       snd_soc_dapm_add_routes(dapm, audio_map, ARRAY_SIZE(audio_map));
 
        /* Prepare GPIO8 for rear speaker amplifier */
        reg = codec->driver->read(codec, AC97_GPIO_CFG);
index 5d57e071cdf522e8f8b11670c32a47595b5d6e92..8235e231d89c013dd957f08b3336a0d3d3a941e4 100644 (file)
@@ -17,6 +17,7 @@
 #include <linux/dmaengine.h>
 #include <linux/platform_data/dma-mmp_tdma.h>
 #include <linux/platform_data/mmp_audio.h>
+
 #include <sound/pxa2xx-lib.h>
 #include <sound/core.h>
 #include <sound/pcm.h>
@@ -67,7 +68,7 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
 {
        struct dma_chan *chan = snd_dmaengine_pcm_get_chan(substream);
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct pxa2xx_pcm_dma_params *dma_params;
+       struct snd_dmaengine_dai_dma_data *dma_params;
        struct dma_slave_config slave_config;
        int ret;
 
@@ -80,10 +81,10 @@ static int mmp_pcm_hw_params(struct snd_pcm_substream *substream,
                return ret;
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK) {
-               slave_config.dst_addr     = dma_params->dev_addr;
+               slave_config.dst_addr     = dma_params->addr;
                slave_config.dst_maxburst = 4;
        } else {
-               slave_config.src_addr     = dma_params->dev_addr;
+               slave_config.src_addr     = dma_params->addr;
                slave_config.src_maxburst = 4;
        }
 
index 62142ce367c7e68fcffbe7b34b92b7364d7a0af3..41752a5fe3b07148c5402b2ea66d6f292fdc3133 100644 (file)
 #include <linux/slab.h>
 #include <linux/pxa2xx_ssp.h>
 #include <linux/io.h>
+#include <linux/dmaengine.h>
+
 #include <sound/core.h>
 #include <sound/pcm.h>
 #include <sound/initval.h>
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
 #include "mmp-sspa.h"
 
 /*
@@ -40,7 +43,7 @@
  */
 struct sspa_priv {
        struct ssp_device *sspa;
-       struct pxa2xx_pcm_dma_params *dma_params;
+       struct snd_dmaengine_dai_dma_data *dma_params;
        struct clk *audio_clk;
        struct clk *sysclk;
        int dai_fmt;
@@ -266,7 +269,7 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream,
        struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct sspa_priv *sspa_priv = snd_soc_dai_get_drvdata(dai);
        struct ssp_device *sspa = sspa_priv->sspa;
-       struct pxa2xx_pcm_dma_params *dma_params;
+       struct snd_dmaengine_dai_dma_data *dma_params;
        u32 sspa_ctrl;
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
@@ -309,7 +312,7 @@ static int mmp_sspa_hw_params(struct snd_pcm_substream *substream,
        }
 
        dma_params = &sspa_priv->dma_params[substream->stream];
-       dma_params->dev_addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+       dma_params->addr = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
                                (sspa->phys_base + SSPA_TXD) :
                                (sspa->phys_base + SSPA_RXD);
        snd_soc_dai_set_dma_data(cpu_dai, substream, dma_params);
@@ -425,14 +428,12 @@ static int asoc_mmp_sspa_probe(struct platform_device *pdev)
                return -ENOMEM;
 
        priv->dma_params = devm_kzalloc(&pdev->dev,
-                       2 * sizeof(struct pxa2xx_pcm_dma_params), GFP_KERNEL);
+                       2 * sizeof(struct snd_dmaengine_dai_dma_data),
+                       GFP_KERNEL);
        if (priv->dma_params == NULL)
                return -ENOMEM;
 
        res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (res == NULL)
-               return -ENOMEM;
-
        priv->sspa->mmio_base = devm_ioremap_resource(&pdev->dev, res);
        if (IS_ERR(priv->sspa->mmio_base))
                return PTR_ERR(priv->sspa->mmio_base);
index 6f4dd7543e829db8e73577f1184c667a596c89d0..a3119a00d8fac8ef8295ef3593f16a60c2603aab 100644 (file)
@@ -21,6 +21,8 @@
 #include <linux/clk.h>
 #include <linux/io.h>
 #include <linux/pxa2xx_ssp.h>
+#include <linux/of.h>
+#include <linux/dmaengine.h>
 
 #include <asm/irq.h>
 
@@ -30,9 +32,9 @@
 #include <sound/pcm_params.h>
 #include <sound/soc.h>
 #include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
 
 #include <mach/hardware.h>
-#include <mach/dma.h>
 
 #include "../../arm/pxa2xx-pcm.h"
 #include "pxa-ssp.h"
@@ -79,27 +81,13 @@ static void pxa_ssp_disable(struct ssp_device *ssp)
        __raw_writel(sscr0, ssp->mmio_base + SSCR0);
 }
 
-struct pxa2xx_pcm_dma_data {
-       struct pxa2xx_pcm_dma_params params;
-       char name[20];
-};
-
 static void pxa_ssp_set_dma_params(struct ssp_device *ssp, int width4,
-                       int out, struct pxa2xx_pcm_dma_params *dma_data)
+                       int out, struct snd_dmaengine_dai_dma_data *dma)
 {
-       struct pxa2xx_pcm_dma_data *dma;
-
-       dma = container_of(dma_data, struct pxa2xx_pcm_dma_data, params);
-
-       snprintf(dma->name, 20, "SSP%d PCM %s %s", ssp->port_id,
-                       width4 ? "32-bit" : "16-bit", out ? "out" : "in");
-
-       dma->params.name = dma->name;
-       dma->params.drcmr = &DRCMR(out ? ssp->drcmr_tx : ssp->drcmr_rx);
-       dma->params.dcmd = (out ? (DCMD_INCSRCADDR | DCMD_FLOWTRG) :
-                                 (DCMD_INCTRGADDR | DCMD_FLOWSRC)) |
-                       (width4 ? DCMD_WIDTH4 : DCMD_WIDTH2) | DCMD_BURST16;
-       dma->params.dev_addr = ssp->phys_base + SSDR;
+       dma->addr_width = width4 ? DMA_SLAVE_BUSWIDTH_4_BYTES :
+                                  DMA_SLAVE_BUSWIDTH_2_BYTES;
+       dma->maxburst = 16;
+       dma->addr = ssp->phys_base + SSDR;
 }
 
 static int pxa_ssp_startup(struct snd_pcm_substream *substream,
@@ -107,7 +95,7 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream,
 {
        struct ssp_priv *priv = snd_soc_dai_get_drvdata(cpu_dai);
        struct ssp_device *ssp = priv->ssp;
-       struct pxa2xx_pcm_dma_data *dma;
+       struct snd_dmaengine_dai_dma_data *dma;
        int ret = 0;
 
        if (!cpu_dai->active) {
@@ -115,10 +103,14 @@ static int pxa_ssp_startup(struct snd_pcm_substream *substream,
                pxa_ssp_disable(ssp);
        }
 
-       dma = kzalloc(sizeof(struct pxa2xx_pcm_dma_data), GFP_KERNEL);
+       dma = kzalloc(sizeof(struct snd_dmaengine_dai_dma_data), GFP_KERNEL);
        if (!dma)
                return -ENOMEM;
-       snd_soc_dai_set_dma_data(cpu_dai, substream, &dma->params);
+
+       dma->filter_data = substream->stream == SNDRV_PCM_STREAM_PLAYBACK ?
+                               &ssp->drcmr_tx : &ssp->drcmr_rx;
+
+       snd_soc_dai_set_dma_data(cpu_dai, substream, dma);
 
        return ret;
 }
@@ -559,7 +551,7 @@ static int pxa_ssp_hw_params(struct snd_pcm_substream *substream,
        u32 sspsp;
        int width = snd_pcm_format_physical_width(params_format(params));
        int ttsa = pxa_ssp_read_reg(ssp, SSTSA) & 0xf;
-       struct pxa2xx_pcm_dma_params *dma_data;
+       struct snd_dmaengine_dai_dma_data *dma_data;
 
        dma_data = snd_soc_dai_get_dma_data(cpu_dai, substream);
 
@@ -719,6 +711,7 @@ static int pxa_ssp_trigger(struct snd_pcm_substream *substream, int cmd,
 
 static int pxa_ssp_probe(struct snd_soc_dai *dai)
 {
+       struct device *dev = dai->dev;
        struct ssp_priv *priv;
        int ret;
 
@@ -726,10 +719,26 @@ static int pxa_ssp_probe(struct snd_soc_dai *dai)
        if (!priv)
                return -ENOMEM;
 
-       priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio");
-       if (priv->ssp == NULL) {
-               ret = -ENODEV;
-               goto err_priv;
+       if (dev->of_node) {
+               struct device_node *ssp_handle;
+
+               ssp_handle = of_parse_phandle(dev->of_node, "port", 0);
+               if (!ssp_handle) {
+                       dev_err(dev, "unable to get 'port' phandle\n");
+                       return -ENODEV;
+               }
+
+               priv->ssp = pxa_ssp_request_of(ssp_handle, "SoC audio");
+               if (priv->ssp == NULL) {
+                       ret = -ENODEV;
+                       goto err_priv;
+               }
+       } else {
+               priv->ssp = pxa_ssp_request(dai->id + 1, "SoC audio");
+               if (priv->ssp == NULL) {
+                       ret = -ENODEV;
+                       goto err_priv;
+               }
        }
 
        priv->dai_fmt = (unsigned int) -1;
@@ -798,6 +807,12 @@ static const struct snd_soc_component_driver pxa_ssp_component = {
        .name           = "pxa-ssp",
 };
 
+#ifdef CONFIG_OF
+static const struct of_device_id pxa_ssp_of_ids[] = {
+       { .compatible = "mrvl,pxa-ssp-dai" },
+};
+#endif
+
 static int asoc_ssp_probe(struct platform_device *pdev)
 {
        return snd_soc_register_component(&pdev->dev, &pxa_ssp_component,
@@ -812,8 +827,9 @@ static int asoc_ssp_remove(struct platform_device *pdev)
 
 static struct platform_driver asoc_ssp_driver = {
        .driver = {
-                       .name = "pxa-ssp-dai",
-                       .owner = THIS_MODULE,
+               .name = "pxa-ssp-dai",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(pxa_ssp_of_ids),
        },
 
        .probe = asoc_ssp_probe,
index 1475515712e65616b345f3d7e56ddedf954f6b4a..f1059d999de6d4128791861d2325cd8bf68371dd 100644 (file)
 #include <linux/io.h>
 #include <linux/module.h>
 #include <linux/platform_device.h>
+#include <linux/dmaengine.h>
 
 #include <sound/core.h>
 #include <sound/ac97_codec.h>
 #include <sound/soc.h>
 #include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
 
 #include <mach/hardware.h>
 #include <mach/regs-ac97.h>
-#include <mach/dma.h>
 #include <mach/audio.h>
 
 #include "pxa2xx-ac97.h"
@@ -48,44 +49,44 @@ static struct snd_ac97_bus_ops pxa2xx_ac97_ops = {
        .reset  = pxa2xx_ac97_cold_reset,
 };
 
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_out = {
-       .name                   = "AC97 PCM Stereo out",
-       .dev_addr               = __PREG(PCDR),
-       .drcmr                  = &DRCMR(12),
-       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-                                 DCMD_BURST32 | DCMD_WIDTH4,
+static unsigned long pxa2xx_ac97_pcm_stereo_in_req = 12;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_in = {
+       .addr           = __PREG(PCDR),
+       .addr_width     = DMA_SLAVE_BUSWIDTH_4_BYTES,
+       .maxburst       = 32,
+       .filter_data    = &pxa2xx_ac97_pcm_stereo_in_req,
 };
 
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_stereo_in = {
-       .name                   = "AC97 PCM Stereo in",
-       .dev_addr               = __PREG(PCDR),
-       .drcmr                  = &DRCMR(11),
-       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-                                 DCMD_BURST32 | DCMD_WIDTH4,
+static unsigned long pxa2xx_ac97_pcm_stereo_out_req = 11;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_stereo_out = {
+       .addr           = __PREG(PCDR),
+       .addr_width     = DMA_SLAVE_BUSWIDTH_4_BYTES,
+       .maxburst       = 32,
+       .filter_data    = &pxa2xx_ac97_pcm_stereo_out_req,
 };
 
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_out = {
-       .name                   = "AC97 Aux PCM (Slot 5) Mono out",
-       .dev_addr               = __PREG(MODR),
-       .drcmr                  = &DRCMR(10),
-       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-                                 DCMD_BURST16 | DCMD_WIDTH2,
+static unsigned long pxa2xx_ac97_pcm_aux_mono_out_req = 10;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_out = {
+       .addr           = __PREG(MODR),
+       .addr_width     = DMA_SLAVE_BUSWIDTH_2_BYTES,
+       .maxburst       = 16,
+       .filter_data    = &pxa2xx_ac97_pcm_aux_mono_out_req,
 };
 
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_aux_mono_in = {
-       .name                   = "AC97 Aux PCM (Slot 5) Mono in",
-       .dev_addr               = __PREG(MODR),
-       .drcmr                  = &DRCMR(9),
-       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-                                 DCMD_BURST16 | DCMD_WIDTH2,
+static unsigned long pxa2xx_ac97_pcm_aux_mono_in_req = 9;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_aux_mono_in = {
+       .addr           = __PREG(MODR),
+       .addr_width     = DMA_SLAVE_BUSWIDTH_2_BYTES,
+       .maxburst       = 16,
+       .filter_data    = &pxa2xx_ac97_pcm_aux_mono_in_req,
 };
 
-static struct pxa2xx_pcm_dma_params pxa2xx_ac97_pcm_mic_mono_in = {
-       .name                   = "AC97 Mic PCM (Slot 6) Mono in",
-       .dev_addr               = __PREG(MCDR),
-       .drcmr                  = &DRCMR(8),
-       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-                                 DCMD_BURST16 | DCMD_WIDTH2,
+static unsigned long pxa2xx_ac97_pcm_aux_mic_mono_req = 8;
+static struct snd_dmaengine_dai_dma_data pxa2xx_ac97_pcm_mic_mono_in = {
+       .addr           = __PREG(MCDR),
+       .addr_width     = DMA_SLAVE_BUSWIDTH_2_BYTES,
+       .maxburst       = 16,
+       .filter_data    = &pxa2xx_ac97_pcm_aux_mic_mono_req,
 };
 
 #ifdef CONFIG_PM
@@ -119,7 +120,7 @@ static int pxa2xx_ac97_hw_params(struct snd_pcm_substream *substream,
                                 struct snd_pcm_hw_params *params,
                                 struct snd_soc_dai *cpu_dai)
 {
-       struct pxa2xx_pcm_dma_params *dma_data;
+       struct snd_dmaengine_dai_dma_data *dma_data;
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                dma_data = &pxa2xx_ac97_pcm_stereo_out;
@@ -135,7 +136,7 @@ static int pxa2xx_ac97_hw_aux_params(struct snd_pcm_substream *substream,
                                     struct snd_pcm_hw_params *params,
                                     struct snd_soc_dai *cpu_dai)
 {
-       struct pxa2xx_pcm_dma_params *dma_data;
+       struct snd_dmaengine_dai_dma_data *dma_data;
 
        if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
                dma_data = &pxa2xx_ac97_pcm_aux_mono_out;
index f7ca716641126bdb65ff5815823a500719bbeba8..d5340a088858eff3f8a8b578a38a2ea3e7476bf1 100644 (file)
@@ -23,9 +23,9 @@
 #include <sound/initval.h>
 #include <sound/soc.h>
 #include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
 
 #include <mach/hardware.h>
-#include <mach/dma.h>
 #include <mach/audio.h>
 
 #include "pxa2xx-i2s.h"
@@ -82,20 +82,20 @@ static struct pxa_i2s_port pxa_i2s;
 static struct clk *clk_i2s;
 static int clk_ena = 0;
 
-static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_out = {
-       .name                   = "I2S PCM Stereo out",
-       .dev_addr               = __PREG(SADR),
-       .drcmr                  = &DRCMR(3),
-       .dcmd                   = DCMD_INCSRCADDR | DCMD_FLOWTRG |
-                                 DCMD_BURST32 | DCMD_WIDTH4,
+static unsigned long pxa2xx_i2s_pcm_stereo_out_req = 3;
+static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_out = {
+       .addr           = __PREG(SADR),
+       .addr_width     = DMA_SLAVE_BUSWIDTH_4_BYTES,
+       .maxburst       = 32,
+       .filter_data    = &pxa2xx_i2s_pcm_stereo_out_req,
 };
 
-static struct pxa2xx_pcm_dma_params pxa2xx_i2s_pcm_stereo_in = {
-       .name                   = "I2S PCM Stereo in",
-       .dev_addr               = __PREG(SADR),
-       .drcmr                  = &DRCMR(2),
-       .dcmd                   = DCMD_INCTRGADDR | DCMD_FLOWSRC |
-                                 DCMD_BURST32 | DCMD_WIDTH4,
+static unsigned long pxa2xx_i2s_pcm_stereo_in_req = 2;
+static struct snd_dmaengine_dai_dma_data pxa2xx_i2s_pcm_stereo_in = {
+       .addr           = __PREG(SADR),
+       .addr_width     = DMA_SLAVE_BUSWIDTH_4_BYTES,
+       .maxburst       = 32,
+       .filter_data    = &pxa2xx_i2s_pcm_stereo_in_req,
 };
 
 static int pxa2xx_i2s_startup(struct snd_pcm_substream *substream,
@@ -163,7 +163,7 @@ static int pxa2xx_i2s_hw_params(struct snd_pcm_substream *substream,
                                struct snd_pcm_hw_params *params,
                                struct snd_soc_dai *dai)
 {
-       struct pxa2xx_pcm_dma_params *dma_data;
+       struct snd_dmaengine_dai_dma_data *dma_data;
 
        BUG_ON(IS_ERR(clk_i2s));
        clk_prepare_enable(clk_i2s);
index ecff116cb7b034484cd3c420ae483d576ef0db95..806da27b8b671ed8596484856ec59f8092c75e8d 100644 (file)
 
 #include <linux/dma-mapping.h>
 #include <linux/module.h>
+#include <linux/dmaengine.h>
+#include <linux/of.h>
 
 #include <sound/core.h>
 #include <sound/soc.h>
 #include <sound/pxa2xx-lib.h>
+#include <sound/dmaengine_pcm.h>
 
 #include "../../arm/pxa2xx-pcm.h"
 
@@ -25,7 +28,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
        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 pxa2xx_pcm_dma_params *dma;
+       struct snd_dmaengine_dai_dma_data *dma;
        int ret;
 
        dma = snd_soc_dai_get_dma_data(rtd->cpu_dai, substream);
@@ -39,7 +42,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
         * with different params */
        if (prtd->params == NULL) {
                prtd->params = dma;
-               ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW,
+               ret = pxa_request_dma("name", DMA_PRIO_LOW,
                              pxa2xx_pcm_dma_irq, substream);
                if (ret < 0)
                        return ret;
@@ -47,7 +50,7 @@ static int pxa2xx_pcm_hw_params(struct snd_pcm_substream *substream,
        } else if (prtd->params != dma) {
                pxa_free_dma(prtd->dma_ch);
                prtd->params = dma;
-               ret = pxa_request_dma(prtd->params->name, DMA_PRIO_LOW,
+               ret = pxa_request_dma("name", DMA_PRIO_LOW,
                              pxa2xx_pcm_dma_irq, substream);
                if (ret < 0)
                        return ret;
@@ -131,10 +134,18 @@ static int pxa2xx_soc_platform_remove(struct platform_device *pdev)
        return 0;
 }
 
+#ifdef CONFIG_OF
+static const struct of_device_id snd_soc_pxa_audio_match[] = {
+       { .compatible   = "mrvl,pxa-pcm-audio" },
+       { }
+};
+#endif
+
 static struct platform_driver pxa_pcm_driver = {
        .driver = {
-                       .name = "pxa-pcm-audio",
-                       .owner = THIS_MODULE,
+               .name = "pxa-pcm-audio",
+               .owner = THIS_MODULE,
+               .of_match_table = of_match_ptr(snd_soc_pxa_audio_match),
        },
 
        .probe = pxa2xx_soc_platform_probe,
index f4ea4f6663a2c0d396997d7aef5cc1871f6ea6e5..13c9ee0cb83b988b06cb9b84963128b1cd703032 100644 (file)
@@ -122,6 +122,7 @@ static struct snd_soc_dai_link ttc_pm860x_hifi_dai[] = {
 /* ttc/td audio machine driver */
 static struct snd_soc_card ttc_dkb_card = {
        .name = "ttc-dkb-hifi",
+       .owner = THIS_MODULE,
        .dai_link = ttc_pm860x_hifi_dai,
        .num_links = ARRAY_SIZE(ttc_pm860x_hifi_dai),
 
index 58cfb1eb7dd3aece2e20b7c45421b0aa5cab5533..945e8abdc10fbf9579a1f89bd9a6a0610980256f 100644 (file)
@@ -192,7 +192,7 @@ static struct snd_soc_card snd_soc_card_s6105 = {
        .num_links = 1,
 };
 
-static struct s6000_snd_platform_data __initdata s6105_snd_data = {
+static struct s6000_snd_platform_data s6105_snd_data __initdata = {
        .wide           = 0,
        .channel_in     = 0,
        .channel_out    = 1,
index 2dd623fa3882b8fe17ceab2c6a86f45f54059f9d..2acf987844e8089704a4bf6d0ccde5ece2cc39e3 100644 (file)
@@ -404,18 +404,13 @@ static int s3c_ac97_probe(struct platform_device *pdev)
                return -ENXIO;
        }
 
-       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!mem_res) {
-               dev_err(&pdev->dev, "Unable to get register resource\n");
-               return -ENXIO;
-       }
-
        irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
        if (!irq_res) {
                dev_err(&pdev->dev, "AC97 IRQ not provided!\n");
                return -ENXIO;
        }
 
+       mem_res = platform_get_resource(pdev, IORESOURCE_MEM, 0);
        s3c_ac97.regs = devm_ioremap_resource(&pdev->dev, mem_res);
        if (IS_ERR(s3c_ac97.regs))
                return PTR_ERR(s3c_ac97.regs);
@@ -462,7 +457,7 @@ static int s3c_ac97_probe(struct platform_device *pdev)
        if (ret)
                goto err5;
 
-       ret = asoc_dma_platform_register(&pdev->dev);
+       ret = samsung_asoc_dma_platform_register(&pdev->dev);
        if (ret) {
                dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
                goto err6;
@@ -485,7 +480,7 @@ static int s3c_ac97_remove(struct platform_device *pdev)
 {
        struct resource *irq_res;
 
-       asoc_dma_platform_unregister(&pdev->dev);
+       samsung_asoc_dma_platform_unregister(&pdev->dev);
        snd_soc_unregister_component(&pdev->dev);
 
        irq_res = platform_get_resource(pdev, IORESOURCE_IRQ, 0);
index 21b79262010ecb3774d8a5ad5ad7f8f87707549c..a0c67f60f594cdcc1af41958d3c8efa199b9c98d 100644 (file)
@@ -176,6 +176,10 @@ static int dma_hw_params(struct snd_pcm_substream *substream,
                prtd->params->ch = prtd->params->ops->request(
                                prtd->params->channel, &req, rtd->cpu_dai->dev,
                                prtd->params->ch_name);
+               if (!prtd->params->ch) {
+                       pr_err("Failed to allocate DMA channel\n");
+                       return -ENXIO;
+               }
                prtd->params->ops->config(prtd->params->ch, &config);
        }
 
@@ -433,17 +437,17 @@ static struct snd_soc_platform_driver samsung_asoc_platform = {
        .pcm_free       = dma_free_dma_buffers,
 };
 
-int asoc_dma_platform_register(struct device *dev)
+int samsung_asoc_dma_platform_register(struct device *dev)
 {
        return snd_soc_register_platform(dev, &samsung_asoc_platform);
 }
-EXPORT_SYMBOL_GPL(asoc_dma_platform_register);
+EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_register);
 
-void asoc_dma_platform_unregister(struct device *dev)
+void samsung_asoc_dma_platform_unregister(struct device *dev)
 {
        snd_soc_unregister_platform(dev);
 }
-EXPORT_SYMBOL_GPL(asoc_dma_platform_unregister);
+EXPORT_SYMBOL_GPL(samsung_asoc_dma_platform_unregister);
 
 MODULE_AUTHOR("Ben Dooks, <ben@simtec.co.uk>");
 MODULE_DESCRIPTION("Samsung ASoC DMA Driver");
index 189a7a6d5020c637d1523ba8261ac9a421882c14..0e86315a3eaf11859d32ab78b1327ce1a60e39c4 100644 (file)
@@ -22,7 +22,7 @@ struct s3c_dma_params {
        char *ch_name;
 };
 
-int asoc_dma_platform_register(struct device *dev);
-void asoc_dma_platform_unregister(struct device *dev);
+int samsung_asoc_dma_platform_register(struct device *dev);
+void samsung_asoc_dma_platform_unregister(struct device *dev);
 
 #endif
index c0e6d9a19efc72562c31157acf776f1e24e57085..821a502310024ba221a2dee4fa33d7937ebe4828 100644 (file)
 #define I2SLVL1ADDR    0x34
 #define I2SLVL2ADDR    0x38
 #define I2SLVL3ADDR    0x3c
+#define I2SSTR1                0x40
+#define I2SVER         0x44
+#define I2SFIC2                0x48
+#define I2STDM         0x4c
 
 #define CON_RSTCLR             (1 << 31)
 #define CON_FRXOFSTATUS                (1 << 26)
 #define MOD_RXONLY             (1 << 8)
 #define MOD_TXRX               (2 << 8)
 #define MOD_MASK               (3 << 8)
-#define MOD_LR_LLOW            (0 << 7)
-#define MOD_LR_RLOW            (1 << 7)
-#define MOD_SDF_IIS            (0 << 5)
-#define MOD_SDF_MSB            (1 << 5)
-#define MOD_SDF_LSB            (2 << 5)
-#define MOD_SDF_MASK           (3 << 5)
-#define MOD_RCLK_256FS         (0 << 3)
-#define MOD_RCLK_512FS         (1 << 3)
-#define MOD_RCLK_384FS         (2 << 3)
-#define MOD_RCLK_768FS         (3 << 3)
-#define MOD_RCLK_MASK          (3 << 3)
-#define MOD_BCLK_32FS          (0 << 1)
-#define MOD_BCLK_48FS          (1 << 1)
-#define MOD_BCLK_16FS          (2 << 1)
-#define MOD_BCLK_24FS          (3 << 1)
-#define MOD_BCLK_MASK          (3 << 1)
+#define MOD_LRP_SHIFT          7
+#define MOD_LR_LLOW            0
+#define MOD_LR_RLOW            1
+#define MOD_SDF_SHIFT          5
+#define MOD_SDF_IIS            0
+#define MOD_SDF_MSB            1
+#define MOD_SDF_LSB            2
+#define MOD_SDF_MASK           3
+#define MOD_RCLK_SHIFT         3
+#define MOD_RCLK_256FS         0
+#define MOD_RCLK_512FS         1
+#define MOD_RCLK_384FS         2
+#define MOD_RCLK_768FS         3
+#define MOD_RCLK_MASK          3
+#define MOD_BCLK_SHIFT         1
+#define MOD_BCLK_32FS          0
+#define MOD_BCLK_48FS          1
+#define MOD_BCLK_16FS          2
+#define MOD_BCLK_24FS          3
+#define MOD_BCLK_MASK          3
 #define MOD_8BIT               (1 << 0)
 
+#define EXYNOS5420_MOD_LRP_SHIFT       15
+#define EXYNOS5420_MOD_SDF_SHIFT       6
+#define EXYNOS5420_MOD_RCLK_SHIFT      4
+#define EXYNOS5420_MOD_BCLK_SHIFT      0
+#define EXYNOS5420_MOD_BCLK_64FS       4
+#define EXYNOS5420_MOD_BCLK_96FS       5
+#define EXYNOS5420_MOD_BCLK_128FS      6
+#define EXYNOS5420_MOD_BCLK_192FS      7
+#define EXYNOS5420_MOD_BCLK_256FS      8
+#define EXYNOS5420_MOD_BCLK_MASK       0xf
+
 #define MOD_CDCLKCON           (1 << 12)
 
 #define PSR_PSREN              (1 << 15)
index 959c702235c8c6028d0946f29b622266f043ab2f..b302f3b7a587f3e480586419affc8e300c80ad8d 100644 (file)
@@ -40,6 +40,7 @@ enum samsung_dai_type {
 
 struct samsung_i2s_dai_data {
        int dai_type;
+       u32 quirks;
 };
 
 struct i2s_dai {
@@ -198,7 +199,13 @@ static inline bool is_manager(struct i2s_dai *i2s)
 /* Read RCLK of I2S (in multiples of LRCLK) */
 static inline unsigned get_rfs(struct i2s_dai *i2s)
 {
-       u32 rfs = (readl(i2s->addr + I2SMOD) >> 3) & 0x3;
+       u32 rfs;
+
+       if (i2s->quirks & QUIRK_SUPPORTS_TDM)
+               rfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_RCLK_SHIFT;
+       else
+               rfs = (readl(i2s->addr + I2SMOD) >> MOD_RCLK_SHIFT);
+       rfs &= MOD_RCLK_MASK;
 
        switch (rfs) {
        case 3: return 768;
@@ -212,21 +219,26 @@ static inline unsigned get_rfs(struct i2s_dai *i2s)
 static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
 {
        u32 mod = readl(i2s->addr + I2SMOD);
+       int rfs_shift;
 
-       mod &= ~MOD_RCLK_MASK;
+       if (i2s->quirks & QUIRK_SUPPORTS_TDM)
+               rfs_shift = EXYNOS5420_MOD_RCLK_SHIFT;
+       else
+               rfs_shift = MOD_RCLK_SHIFT;
+       mod &= ~(MOD_RCLK_MASK << rfs_shift);
 
        switch (rfs) {
        case 768:
-               mod |= MOD_RCLK_768FS;
+               mod |= (MOD_RCLK_768FS << rfs_shift);
                break;
        case 512:
-               mod |= MOD_RCLK_512FS;
+               mod |= (MOD_RCLK_512FS << rfs_shift);
                break;
        case 384:
-               mod |= MOD_RCLK_384FS;
+               mod |= (MOD_RCLK_384FS << rfs_shift);
                break;
        default:
-               mod |= MOD_RCLK_256FS;
+               mod |= (MOD_RCLK_256FS << rfs_shift);
                break;
        }
 
@@ -236,9 +248,22 @@ static inline void set_rfs(struct i2s_dai *i2s, unsigned rfs)
 /* Read Bit-Clock of I2S (in multiples of LRCLK) */
 static inline unsigned get_bfs(struct i2s_dai *i2s)
 {
-       u32 bfs = (readl(i2s->addr + I2SMOD) >> 1) & 0x3;
+       u32 bfs;
+
+       if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
+               bfs = readl(i2s->addr + I2SMOD) >> EXYNOS5420_MOD_BCLK_SHIFT;
+               bfs &= EXYNOS5420_MOD_BCLK_MASK;
+       } else {
+               bfs =  readl(i2s->addr + I2SMOD) >> MOD_BCLK_SHIFT;
+               bfs &= MOD_BCLK_MASK;
+       }
 
        switch (bfs) {
+       case 8: return 256;
+       case 7: return 192;
+       case 6: return 128;
+       case 5: return 96;
+       case 4: return 64;
        case 3: return 24;
        case 2: return 16;
        case 1: return 48;
@@ -250,21 +275,50 @@ static inline unsigned get_bfs(struct i2s_dai *i2s)
 static inline void set_bfs(struct i2s_dai *i2s, unsigned bfs)
 {
        u32 mod = readl(i2s->addr + I2SMOD);
+       int bfs_shift;
+       int tdm = i2s->quirks & QUIRK_SUPPORTS_TDM;
 
-       mod &= ~MOD_BCLK_MASK;
+       if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
+               bfs_shift = EXYNOS5420_MOD_BCLK_SHIFT;
+               mod &= ~(EXYNOS5420_MOD_BCLK_MASK << bfs_shift);
+       } else {
+               bfs_shift = MOD_BCLK_SHIFT;
+               mod &= ~(MOD_BCLK_MASK << bfs_shift);
+       }
+
+       /* Non-TDM I2S controllers do not support BCLK > 48 * FS */
+       if (!tdm && bfs > 48) {
+               dev_err(&i2s->pdev->dev, "Unsupported BCLK divider\n");
+               return;
+       }
 
        switch (bfs) {
        case 48:
-               mod |= MOD_BCLK_48FS;
+               mod |= (MOD_BCLK_48FS << bfs_shift);
                break;
        case 32:
-               mod |= MOD_BCLK_32FS;
+               mod |= (MOD_BCLK_32FS << bfs_shift);
                break;
        case 24:
-               mod |= MOD_BCLK_24FS;
+               mod |= (MOD_BCLK_24FS << bfs_shift);
                break;
        case 16:
-               mod |= MOD_BCLK_16FS;
+               mod |= (MOD_BCLK_16FS << bfs_shift);
+               break;
+       case 64:
+               mod |= (EXYNOS5420_MOD_BCLK_64FS << bfs_shift);
+               break;
+       case 96:
+               mod |= (EXYNOS5420_MOD_BCLK_96FS << bfs_shift);
+               break;
+       case 128:
+               mod |= (EXYNOS5420_MOD_BCLK_128FS << bfs_shift);
+               break;
+       case 192:
+               mod |= (EXYNOS5420_MOD_BCLK_192FS << bfs_shift);
+               break;
+       case 256:
+               mod |= (EXYNOS5420_MOD_BCLK_256FS << bfs_shift);
                break;
        default:
                dev_err(&i2s->pdev->dev, "Wrong BCLK Divider!\n");
@@ -491,20 +545,32 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
 {
        struct i2s_dai *i2s = to_info(dai);
        u32 mod = readl(i2s->addr + I2SMOD);
+       int lrp_shift, sdf_shift, sdf_mask, lrp_rlow;
        u32 tmp = 0;
 
+       if (i2s->quirks & QUIRK_SUPPORTS_TDM) {
+               lrp_shift = EXYNOS5420_MOD_LRP_SHIFT;
+               sdf_shift = EXYNOS5420_MOD_SDF_SHIFT;
+       } else {
+               lrp_shift = MOD_LRP_SHIFT;
+               sdf_shift = MOD_SDF_SHIFT;
+       }
+
+       sdf_mask = MOD_SDF_MASK << sdf_shift;
+       lrp_rlow = MOD_LR_RLOW << lrp_shift;
+
        /* Format is priority */
        switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
        case SND_SOC_DAIFMT_RIGHT_J:
-               tmp |= MOD_LR_RLOW;
-               tmp |= MOD_SDF_MSB;
+               tmp |= lrp_rlow;
+               tmp |= (MOD_SDF_MSB << sdf_shift);
                break;
        case SND_SOC_DAIFMT_LEFT_J:
-               tmp |= MOD_LR_RLOW;
-               tmp |= MOD_SDF_LSB;
+               tmp |= lrp_rlow;
+               tmp |= (MOD_SDF_LSB << sdf_shift);
                break;
        case SND_SOC_DAIFMT_I2S:
-               tmp |= MOD_SDF_IIS;
+               tmp |= (MOD_SDF_IIS << sdf_shift);
                break;
        default:
                dev_err(&i2s->pdev->dev, "Format not supported\n");
@@ -519,10 +585,10 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
        case SND_SOC_DAIFMT_NB_NF:
                break;
        case SND_SOC_DAIFMT_NB_IF:
-               if (tmp & MOD_LR_RLOW)
-                       tmp &= ~MOD_LR_RLOW;
+               if (tmp & lrp_rlow)
+                       tmp &= ~lrp_rlow;
                else
-                       tmp |= MOD_LR_RLOW;
+                       tmp |= lrp_rlow;
                break;
        default:
                dev_err(&i2s->pdev->dev, "Polarity not supported\n");
@@ -544,15 +610,18 @@ static int i2s_set_fmt(struct snd_soc_dai *dai,
                return -EINVAL;
        }
 
+       /*
+        * Don't change the I2S mode if any controller is active on this
+        * channel.
+        */
        if (any_active(i2s) &&
-                       ((mod & (MOD_SDF_MASK | MOD_LR_RLOW
-                               | MOD_SLAVE)) != tmp)) {
+               ((mod & (sdf_mask | lrp_rlow | MOD_SLAVE)) != tmp)) {
                dev_err(&i2s->pdev->dev,
                                "%s:%d Other DAI busy\n", __func__, __LINE__);
                return -EAGAIN;
        }
 
-       mod &= ~(MOD_SDF_MASK | MOD_LR_RLOW | MOD_SLAVE);
+       mod &= ~(sdf_mask | lrp_rlow | MOD_SLAVE);
        mod |= tmp;
        writel(mod, i2s->addr + I2SMOD);
 
@@ -1007,6 +1076,8 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
                if (IS_ERR(i2s->pdev))
                        return NULL;
 
+               i2s->pdev->dev.parent = &pdev->dev;
+
                platform_set_drvdata(i2s->pdev, i2s);
                ret = platform_device_add(i2s->pdev);
                if (ret < 0)
@@ -1018,18 +1089,18 @@ static struct i2s_dai *i2s_alloc_dai(struct platform_device *pdev, bool sec)
 
 static const struct of_device_id exynos_i2s_match[];
 
-static inline int samsung_i2s_get_driver_data(struct platform_device *pdev)
+static inline const struct samsung_i2s_dai_data *samsung_i2s_get_driver_data(
+                                               struct platform_device *pdev)
 {
 #ifdef CONFIG_OF
-       struct samsung_i2s_dai_data *data;
        if (pdev->dev.of_node) {
                const struct of_device_id *match;
                match = of_match_node(exynos_i2s_match, pdev->dev.of_node);
-               data = (struct samsung_i2s_dai_data *) match->data;
-               return data->dai_type;
+               return match->data;
        } else
 #endif
-               return platform_get_device_id(pdev)->driver_data;
+               return (struct samsung_i2s_dai_data *)
+                               platform_get_device_id(pdev)->driver_data;
 }
 
 #ifdef CONFIG_PM_RUNTIME
@@ -1060,13 +1131,13 @@ static int samsung_i2s_probe(struct platform_device *pdev)
        struct resource *res;
        u32 regs_base, quirks = 0, idma_addr = 0;
        struct device_node *np = pdev->dev.of_node;
-       enum samsung_dai_type samsung_dai_type;
+       const struct samsung_i2s_dai_data *i2s_dai_data;
        int ret = 0;
 
        /* Call during Seconday interface registration */
-       samsung_dai_type = samsung_i2s_get_driver_data(pdev);
+       i2s_dai_data = samsung_i2s_get_driver_data(pdev);
 
-       if (samsung_dai_type == TYPE_SEC) {
+       if (i2s_dai_data->dai_type == TYPE_SEC) {
                sec_dai = dev_get_drvdata(&pdev->dev);
                if (!sec_dai) {
                        dev_err(&pdev->dev, "Unable to get drvdata\n");
@@ -1075,7 +1146,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                snd_soc_register_component(&sec_dai->pdev->dev,
                                           &samsung_i2s_component,
                                           &sec_dai->i2s_dai_drv, 1);
-               asoc_dma_platform_register(&pdev->dev);
+               samsung_asoc_dma_platform_register(&pdev->dev);
                return 0;
        }
 
@@ -1115,15 +1186,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
                        idma_addr = i2s_cfg->idma_addr;
                }
        } else {
-               if (of_find_property(np, "samsung,supports-6ch", NULL))
-                       quirks |= QUIRK_PRI_6CHAN;
-
-               if (of_find_property(np, "samsung,supports-secdai", NULL))
-                       quirks |= QUIRK_SEC_DAI;
-
-               if (of_find_property(np, "samsung,supports-rstclr", NULL))
-                       quirks |= QUIRK_NEED_RSTCLR;
-
+               quirks = i2s_dai_data->quirks;
                if (of_property_read_u32(np, "samsung,idma-addr",
                                         &idma_addr)) {
                        if (quirks & QUIRK_SEC_DAI) {
@@ -1200,7 +1263,7 @@ static int samsung_i2s_probe(struct platform_device *pdev)
 
        pm_runtime_enable(&pdev->dev);
 
-       asoc_dma_platform_register(&pdev->dev);
+       samsung_asoc_dma_platform_register(&pdev->dev);
 
        return 0;
 err:
@@ -1230,33 +1293,59 @@ static int samsung_i2s_remove(struct platform_device *pdev)
        i2s->pri_dai = NULL;
        i2s->sec_dai = NULL;
 
-       asoc_dma_platform_unregister(&pdev->dev);
+       samsung_asoc_dma_platform_unregister(&pdev->dev);
        snd_soc_unregister_component(&pdev->dev);
 
        return 0;
 }
 
+static const struct samsung_i2s_dai_data i2sv3_dai_type = {
+       .dai_type = TYPE_PRI,
+       .quirks = QUIRK_NO_MUXPSR,
+};
+
+static const struct samsung_i2s_dai_data i2sv5_dai_type = {
+       .dai_type = TYPE_PRI,
+       .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR,
+};
+
+static const struct samsung_i2s_dai_data i2sv6_dai_type = {
+       .dai_type = TYPE_PRI,
+       .quirks = QUIRK_PRI_6CHAN | QUIRK_SEC_DAI | QUIRK_NEED_RSTCLR |
+                       QUIRK_SUPPORTS_TDM,
+};
+
+static const struct samsung_i2s_dai_data samsung_dai_type_pri = {
+       .dai_type = TYPE_PRI,
+};
+
+static const struct samsung_i2s_dai_data samsung_dai_type_sec = {
+       .dai_type = TYPE_SEC,
+};
+
 static struct platform_device_id samsung_i2s_driver_ids[] = {
        {
                .name           = "samsung-i2s",
-               .driver_data    = TYPE_PRI,
+               .driver_data    = (kernel_ulong_t)&samsung_dai_type_pri,
        }, {
                .name           = "samsung-i2s-sec",
-               .driver_data    = TYPE_SEC,
+               .driver_data    = (kernel_ulong_t)&samsung_dai_type_sec,
        },
        {},
 };
 MODULE_DEVICE_TABLE(platform, samsung_i2s_driver_ids);
 
 #ifdef CONFIG_OF
-static struct samsung_i2s_dai_data samsung_i2s_dai_data_array[] = {
-       [TYPE_PRI] = { TYPE_PRI },
-       [TYPE_SEC] = { TYPE_SEC },
-};
-
 static const struct of_device_id exynos_i2s_match[] = {
-       { .compatible = "samsung,i2s-v5",
-         .data = &samsung_i2s_dai_data_array[TYPE_PRI],
+       {
+               .compatible = "samsung,s3c6410-i2s",
+               .data = &i2sv3_dai_type,
+       }, {
+               .compatible = "samsung,s5pv210-i2s",
+               .data = &i2sv5_dai_type,
+       }, {
+               .compatible = "samsung,exynos5420-i2s",
+               .data = &i2sv6_dai_type,
        },
        {},
 };
index 1566afe9ef525097ee45ce737af33bc63b47fba7..e54256fc4b2c6d35faa47f8c3a1d42ddb3f5c74e 100644 (file)
@@ -594,7 +594,7 @@ static int s3c_pcm_dev_probe(struct platform_device *pdev)
                goto err5;
        }
 
-       ret = asoc_dma_platform_register(&pdev->dev);
+       ret = samsung_asoc_dma_platform_register(&pdev->dev);
        if (ret) {
                dev_err(&pdev->dev, "failed to get register DMA: %d\n", ret);
                goto err6;
@@ -623,7 +623,7 @@ static int s3c_pcm_dev_remove(struct platform_device *pdev)
        struct s3c_pcm_info *pcm = &s3c_pcm[pdev->id];
        struct resource *mem_res;
 
-       asoc_dma_platform_unregister(&pdev->dev);
+       samsung_asoc_dma_platform_unregister(&pdev->dev);
        snd_soc_unregister_component(&pdev->dev);
 
        pm_runtime_disable(&pdev->dev);
index 47e23864ea72f762deea79ee195ae0b633f8c6db..ea885cb9f76c300645228a9b9daa75e81a560d82 100644 (file)
@@ -176,7 +176,7 @@ static int s3c2412_iis_dev_probe(struct platform_device *pdev)
                return ret;
        }
 
-       ret = asoc_dma_platform_register(&pdev->dev);
+       ret = samsung_asoc_dma_platform_register(&pdev->dev);
        if (ret) {
                pr_err("failed to register the DMA: %d\n", ret);
                goto err;
@@ -190,7 +190,7 @@ err:
 
 static int s3c2412_iis_dev_remove(struct platform_device *pdev)
 {
-       asoc_dma_platform_unregister(&pdev->dev);
+       samsung_asoc_dma_platform_unregister(&pdev->dev);
        snd_soc_unregister_component(&pdev->dev);
        return 0;
 }
index 8b3414551a6231ffe6556989570831dd46c15dc7..9c8ebd872fac2ac67bcaecae2aca1c9e0a051e3b 100644 (file)
@@ -480,7 +480,7 @@ static int s3c24xx_iis_dev_probe(struct platform_device *pdev)
                return ret;
        }
 
-       ret = asoc_dma_platform_register(&pdev->dev);
+       ret = samsung_asoc_dma_platform_register(&pdev->dev);
        if (ret) {
                pr_err("failed to register the dma: %d\n", ret);
                goto err;
@@ -494,7 +494,7 @@ err:
 
 static int s3c24xx_iis_dev_remove(struct platform_device *pdev)
 {
-       asoc_dma_platform_unregister(&pdev->dev);
+       samsung_asoc_dma_platform_unregister(&pdev->dev);
        snd_soc_unregister_component(&pdev->dev);
        return 0;
 }
index 581ea4a06fc684d1e3f677477326426d7334b427..5fd7a05a9b9e2fe9730a962bcdca73482a9e5d71 100644 (file)
@@ -11,6 +11,7 @@
 #include <sound/pcm_params.h>
 #include <linux/module.h>
 #include <linux/of.h>
+#include <linux/of_device.h>
 
  /*
   * Default CFG switch settings to use this driver:
 /* SMDK has a 16.934MHZ crystal attached to WM8994 */
 #define SMDK_WM8994_FREQ 16934000
 
+struct smdk_wm8994_data {
+       int mclk1_rate;
+};
+
+/* Default SMDKs */
+static struct smdk_wm8994_data smdk_board_data = {
+       .mclk1_rate = SMDK_WM8994_FREQ,
+};
+
 static int smdk_hw_params(struct snd_pcm_substream *substream,
        struct snd_pcm_hw_params *params)
 {
        struct snd_soc_pcm_runtime *rtd = substream->private_data;
-       struct snd_soc_dai *cpu_dai = rtd->cpu_dai;
        struct snd_soc_dai *codec_dai = rtd->codec_dai;
        unsigned int pll_out;
        int ret;
@@ -54,18 +63,6 @@ static int smdk_hw_params(struct snd_pcm_substream *substream,
        else
                pll_out = params_rate(params) * 256;
 
-       ret = snd_soc_dai_set_fmt(codec_dai, SND_SOC_DAIFMT_I2S
-                                        | SND_SOC_DAIFMT_NB_NF
-                                        | SND_SOC_DAIFMT_CBM_CFM);
-       if (ret < 0)
-               return ret;
-
-       ret = snd_soc_dai_set_fmt(cpu_dai, SND_SOC_DAIFMT_I2S
-                                        | SND_SOC_DAIFMT_NB_NF
-                                        | SND_SOC_DAIFMT_CBM_CFM);
-       if (ret < 0)
-               return ret;
-
        ret = snd_soc_dai_set_pll(codec_dai, WM8994_FLL1, WM8994_FLL_SRC_MCLK1,
                                        SMDK_WM8994_FREQ, pll_out);
        if (ret < 0)
@@ -131,6 +128,8 @@ static struct snd_soc_dai_link smdk_dai[] = {
                .platform_name = "samsung-i2s.0",
                .codec_name = "wm8994-codec",
                .init = smdk_wm8994_init_paiftx,
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+                       SND_SOC_DAIFMT_CBM_CFM,
                .ops = &smdk_ops,
        }, { /* Sec_Fifo Playback i/f */
                .name = "Sec_FIFO TX",
@@ -139,6 +138,8 @@ static struct snd_soc_dai_link smdk_dai[] = {
                .codec_dai_name = "wm8994-aif1",
                .platform_name = "samsung-i2s-sec",
                .codec_name = "wm8994-codec",
+               .dai_fmt = SND_SOC_DAIFMT_I2S | SND_SOC_DAIFMT_NB_NF |
+                       SND_SOC_DAIFMT_CBM_CFM,
                .ops = &smdk_ops,
        },
 };
@@ -150,15 +151,28 @@ static struct snd_soc_card smdk = {
        .num_links = ARRAY_SIZE(smdk_dai),
 };
 
+#ifdef CONFIG_OF
+static const struct of_device_id samsung_wm8994_of_match[] = {
+       { .compatible = "samsung,smdk-wm8994", .data = &smdk_board_data },
+       {},
+};
+MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match);
+#endif /* CONFIG_OF */
 
 static int smdk_audio_probe(struct platform_device *pdev)
 {
        int ret;
        struct device_node *np = pdev->dev.of_node;
        struct snd_soc_card *card = &smdk;
+       struct smdk_wm8994_data *board;
+       const struct of_device_id *id;
 
        card->dev = &pdev->dev;
 
+       board = devm_kzalloc(&pdev->dev, sizeof(*board), GFP_KERNEL);
+       if (!board)
+               return -ENOMEM;
+
        if (np) {
                smdk_dai[0].cpu_dai_name = NULL;
                smdk_dai[0].cpu_of_node = of_parse_phandle(np,
@@ -173,6 +187,12 @@ static int smdk_audio_probe(struct platform_device *pdev)
                smdk_dai[0].platform_of_node = smdk_dai[0].cpu_of_node;
        }
 
+       id = of_match_device(samsung_wm8994_of_match, &pdev->dev);
+       if (id)
+               *board = *((struct smdk_wm8994_data *)id->data);
+
+       platform_set_drvdata(pdev, board);
+
        ret = snd_soc_register_card(card);
 
        if (ret)
@@ -190,17 +210,9 @@ static int smdk_audio_remove(struct platform_device *pdev)
        return 0;
 }
 
-#ifdef CONFIG_OF
-static const struct of_device_id samsung_wm8994_of_match[] = {
-       { .compatible = "samsung,smdk-wm8994", },
-       {},
-};
-MODULE_DEVICE_TABLE(of, samsung_wm8994_of_match);
-#endif /* CONFIG_OF */
-
 static struct platform_driver smdk_audio_driver = {
        .driver         = {
-               .name   = "smdk-audio",
+               .name   = "smdk-audio-wm8894",
                .owner  = THIS_MODULE,
                .of_match_table = of_match_ptr(samsung_wm8994_of_match),
        },
@@ -212,4 +224,4 @@ module_platform_driver(smdk_audio_driver);
 
 MODULE_DESCRIPTION("ALSA SoC SMDK WM8994");
 MODULE_LICENSE("GPL");
-MODULE_ALIAS("platform:smdk-audio");
+MODULE_ALIAS("platform:smdk-audio-wm8994");
index 2e5ebb2f1982c2e849e8a8adc7924034a6931675..28487dcc4538b939bd86a82395bb7299c2a5d243 100644 (file)
@@ -395,7 +395,7 @@ static int spdif_probe(struct platform_device *pdev)
 
        spin_lock_init(&spdif->lock);
 
-       spdif->pclk = clk_get(&pdev->dev, "spdif");
+       spdif->pclk = devm_clk_get(&pdev->dev, "spdif");
        if (IS_ERR(spdif->pclk)) {
                dev_err(&pdev->dev, "failed to get peri-clock\n");
                ret = -ENOENT;
@@ -403,7 +403,7 @@ static int spdif_probe(struct platform_device *pdev)
        }
        clk_prepare_enable(spdif->pclk);
 
-       spdif->sclk = clk_get(&pdev->dev, "sclk_spdif");
+       spdif->sclk = devm_clk_get(&pdev->dev, "sclk_spdif");
        if (IS_ERR(spdif->sclk)) {
                dev_err(&pdev->dev, "failed to get internal source clock\n");
                ret = -ENOENT;
@@ -442,7 +442,7 @@ static int spdif_probe(struct platform_device *pdev)
 
        spdif->dma_playback = &spdif_stereo_out;
 
-       ret = asoc_dma_platform_register(&pdev->dev);
+       ret = samsung_asoc_dma_platform_register(&pdev->dev);
        if (ret) {
                dev_err(&pdev->dev, "failed to register DMA: %d\n", ret);
                goto err5;
@@ -457,10 +457,8 @@ err3:
        release_mem_region(mem_res->start, resource_size(mem_res));
 err2:
        clk_disable_unprepare(spdif->sclk);
-       clk_put(spdif->sclk);
 err1:
        clk_disable_unprepare(spdif->pclk);
-       clk_put(spdif->pclk);
 err0:
        return ret;
 }
@@ -470,7 +468,7 @@ static int spdif_remove(struct platform_device *pdev)
        struct samsung_spdif_info *spdif = &spdif_info;
        struct resource *mem_res;
 
-       asoc_dma_platform_unregister(&pdev->dev);
+       samsung_asoc_dma_platform_unregister(&pdev->dev);
        snd_soc_unregister_component(&pdev->dev);
 
        iounmap(spdif->regs);
@@ -480,9 +478,7 @@ static int spdif_remove(struct platform_device *pdev)
                release_mem_region(mem_res->start, resource_size(mem_res));
 
        clk_disable_unprepare(spdif->sclk);
-       clk_put(spdif->sclk);
        clk_disable_unprepare(spdif->pclk);
-       clk_put(spdif->pclk);
 
        return 0;
 }
index 6bcb1164d599d61bec029fdb94d3f00495b8f66c..56d8ff6a402d2eaffdb4aeb5bc65faa5b815678a 100644 (file)
@@ -34,6 +34,13 @@ config SND_SOC_SH4_SIU
        select SH_DMAE
        select FW_LOADER
 
+config SND_SOC_RCAR
+       tristate "R-Car series SRU/SCU/SSIU/SSI support"
+       select SND_SIMPLE_CARD
+       select RCAR_CLK_ADG
+       help
+         This option enables R-Car SUR/SCU/SSIU/SSI sound support
+
 ##
 ## Boards
 ##
index 849b387d17d99a7d91b21bd1f64adc228341cfb9..aaf3dcd1ee2a197fae36c1f577443a386e8371a1 100644 (file)
@@ -12,6 +12,9 @@ obj-$(CONFIG_SND_SOC_SH4_SSI) += snd-soc-ssi.o
 obj-$(CONFIG_SND_SOC_SH4_FSI)  += snd-soc-fsi.o
 obj-$(CONFIG_SND_SOC_SH4_SIU)  += snd-soc-siu.o
 
+## audio units for R-Car
+obj-$(CONFIG_SND_SOC_RCAR)     += rcar/
+
 ## boards
 snd-soc-sh7760-ac97-objs       := sh7760-ac97.o
 snd-soc-migor-objs             := migor.o
diff --git a/sound/soc/sh/rcar/Makefile b/sound/soc/sh/rcar/Makefile
new file mode 100644 (file)
index 0000000..0ff492d
--- /dev/null
@@ -0,0 +1,2 @@
+snd-soc-rcar-objs      := core.o gen.o scu.o adg.o ssi.o
+obj-$(CONFIG_SND_SOC_RCAR)     += snd-soc-rcar.o
\ No newline at end of file
diff --git a/sound/soc/sh/rcar/adg.c b/sound/soc/sh/rcar/adg.c
new file mode 100644 (file)
index 0000000..d80deb7
--- /dev/null
@@ -0,0 +1,234 @@
+/*
+ * Helper routines for R-Car sound ADG.
+ *
+ *  Copyright (C) 2013  Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * This file is subject to the terms and conditions of the GNU General Public
+ * License.  See the file "COPYING" in the main directory of this archive
+ * for more details.
+ */
+#include <linux/sh_clk.h>
+#include <mach/clock.h>
+#include "rsnd.h"
+
+#define CLKA   0
+#define CLKB   1
+#define CLKC   2
+#define CLKI   3
+#define CLKMAX 4
+
+struct rsnd_adg {
+       struct clk *clk[CLKMAX];
+
+       int rate_of_441khz_div_6;
+       int rate_of_48khz_div_6;
+};
+
+#define for_each_rsnd_clk(pos, adg, i)         \
+       for (i = 0, (pos) = adg->clk[i];        \
+            i < CLKMAX;                        \
+            i++, (pos) = adg->clk[i])
+#define rsnd_priv_to_adg(priv) ((struct rsnd_adg *)(priv)->adg)
+
+static enum rsnd_reg rsnd_adg_ssi_reg_get(int id)
+{
+       enum rsnd_reg reg;
+
+       /*
+        * SSI 8 is not connected to ADG.
+        * it works with SSI 7
+        */
+       if (id == 8)
+               return RSND_REG_MAX;
+
+       if (0 <= id && id <= 3)
+               reg = RSND_REG_AUDIO_CLK_SEL0;
+       else if (4 <= id && id <= 7)
+               reg = RSND_REG_AUDIO_CLK_SEL1;
+       else
+               reg = RSND_REG_AUDIO_CLK_SEL2;
+
+       return reg;
+}
+
+int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       enum rsnd_reg reg;
+       int id;
+
+       /*
+        * "mod" = "ssi" here.
+        * we can get "ssi id" from mod
+        */
+       id  = rsnd_mod_id(mod);
+       reg = rsnd_adg_ssi_reg_get(id);
+
+       rsnd_write(priv, mod, reg, 0);
+
+       return 0;
+}
+
+int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_adg *adg = rsnd_priv_to_adg(priv);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct clk *clk;
+       enum rsnd_reg reg;
+       int id, shift, i;
+       u32 data;
+       int sel_table[] = {
+               [CLKA] = 0x1,
+               [CLKB] = 0x2,
+               [CLKC] = 0x3,
+               [CLKI] = 0x0,
+       };
+
+       dev_dbg(dev, "request clock = %d\n", rate);
+
+       /*
+        * find suitable clock from
+        * AUDIO_CLKA/AUDIO_CLKB/AUDIO_CLKC/AUDIO_CLKI.
+        */
+       data = 0;
+       for_each_rsnd_clk(clk, adg, i) {
+               if (rate == clk_get_rate(clk)) {
+                       data = sel_table[i];
+                       goto found_clock;
+               }
+       }
+
+       /*
+        * find 1/6 clock from BRGA/BRGB
+        */
+       if (rate == adg->rate_of_441khz_div_6) {
+               data = 0x10;
+               goto found_clock;
+       }
+
+       if (rate == adg->rate_of_48khz_div_6) {
+               data = 0x20;
+               goto found_clock;
+       }
+
+       return -EIO;
+
+found_clock:
+
+       /*
+        * This "mod" = "ssi" here.
+        * we can get "ssi id" from mod
+        */
+       id  = rsnd_mod_id(mod);
+       reg = rsnd_adg_ssi_reg_get(id);
+
+       dev_dbg(dev, "ADG: ssi%d selects clk%d = %d", id, i, rate);
+
+       /*
+        * Enable SSIx clock
+        */
+       shift = (id % 4) * 8;
+
+       rsnd_bset(priv, mod, reg,
+                  0xFF << shift,
+                  data << shift);
+
+       return 0;
+}
+
+static void rsnd_adg_ssi_clk_init(struct rsnd_priv *priv, struct rsnd_adg *adg)
+{
+       struct clk *clk;
+       unsigned long rate;
+       u32 ckr;
+       int i;
+       int brg_table[] = {
+               [CLKA] = 0x0,
+               [CLKB] = 0x1,
+               [CLKC] = 0x4,
+               [CLKI] = 0x2,
+       };
+
+       /*
+        * 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()
+        */
+       ckr = 0;
+       adg->rate_of_441khz_div_6 = 0;
+       adg->rate_of_48khz_div_6  = 0;
+       for_each_rsnd_clk(clk, adg, i) {
+               rate = clk_get_rate(clk);
+
+               if (0 == rate) /* not used */
+                       continue;
+
+               /* RBGA */
+               if (!adg->rate_of_441khz_div_6 && (0 == rate % 44100)) {
+                       adg->rate_of_441khz_div_6 = rate / 6;
+                       ckr |= brg_table[i] << 20;
+               }
+
+               /* RBGB */
+               if (!adg->rate_of_48khz_div_6 && (0 == rate % 48000)) {
+                       adg->rate_of_48khz_div_6 = rate / 6;
+                       ckr |= brg_table[i] << 16;
+               }
+       }
+
+       rsnd_priv_bset(priv, SSICKR, 0x00FF0000, ckr);
+       rsnd_priv_write(priv, BRRA,  0x00000002); /* 1/6 */
+       rsnd_priv_write(priv, BRRB,  0x00000002); /* 1/6 */
+}
+
+int rsnd_adg_probe(struct platform_device *pdev,
+                  struct rcar_snd_info *info,
+                  struct rsnd_priv *priv)
+{
+       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) {
+               dev_err(dev, "ADG allocate failed\n");
+               return -ENOMEM;
+       }
+
+       adg->clk[CLKA] = clk_get(NULL, "audio_clk_a");
+       adg->clk[CLKB] = clk_get(NULL, "audio_clk_b");
+       adg->clk[CLKC] = clk_get(NULL, "audio_clk_c");
+       adg->clk[CLKI] = clk_get(NULL, "audio_clk_internal");
+       for_each_rsnd_clk(clk, adg, i) {
+               if (IS_ERR(clk)) {
+                       dev_err(dev, "Audio clock failed\n");
+                       return -EIO;
+               }
+       }
+
+       rsnd_adg_ssi_clk_init(priv, adg);
+
+       priv->adg = adg;
+
+       dev_dbg(dev, "adg probed\n");
+
+       return 0;
+}
+
+void rsnd_adg_remove(struct platform_device *pdev,
+                    struct rsnd_priv *priv)
+{
+       struct rsnd_adg *adg = priv->adg;
+       struct clk *clk;
+       int i;
+
+       for_each_rsnd_clk(clk, adg, i)
+               clk_put(clk);
+}
diff --git a/sound/soc/sh/rcar/core.c b/sound/soc/sh/rcar/core.c
new file mode 100644 (file)
index 0000000..a357060
--- /dev/null
@@ -0,0 +1,861 @@
+/*
+ * Renesas R-Car SRU/SCU/SSIU/SSI support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * Based on fsi.c
+ * Kuninori Morimoto <morimoto.kuninori@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.
+ */
+
+/*
+ * Renesas R-Car sound device structure
+ *
+ * Gen1
+ *
+ * SRU         : Sound Routing Unit
+ *  - SRC      : Sampling Rate Converter
+ *  - CMD
+ *    - CTU    : Channel Count Conversion Unit
+ *    - MIX    : Mixer
+ *    - DVC    : Digital Volume and Mute Function
+ *  - SSI      : Serial Sound Interface
+ *
+ * Gen2
+ *
+ * SCU         : Sampling Rate Converter Unit
+ *  - SRC      : Sampling Rate Converter
+ *  - CMD
+ *   - CTU     : Channel Count Conversion Unit
+ *   - MIX     : Mixer
+ *   - DVC     : Digital Volume and Mute Function
+ * SSIU                : Serial Sound Interface Unit
+ *  - SSI      : Serial Sound Interface
+ */
+
+/*
+ *     driver data Image
+ *
+ * rsnd_priv
+ *   |
+ *   | ** this depends on Gen1/Gen2
+ *   |
+ *   +- gen
+ *   |
+ *   | ** these depend on data path
+ *   | ** gen and platform data control it
+ *   |
+ *   +- rdai[0]
+ *   |   |              sru     ssiu      ssi
+ *   |   +- playback -> [mod] -> [mod] -> [mod] -> ...
+ *   |   |
+ *   |   |              sru     ssiu      ssi
+ *   |   +- capture  -> [mod] -> [mod] -> [mod] -> ...
+ *   |
+ *   +- rdai[1]
+ *   |   |              sru     ssiu      ssi
+ *   |   +- playback -> [mod] -> [mod] -> [mod] -> ...
+ *   |   |
+ *   |   |              sru     ssiu      ssi
+ *   |   +- capture  -> [mod] -> [mod] -> [mod] -> ...
+ *   ...
+ *   |
+ *   | ** these control ssi
+ *   |
+ *   +- ssi
+ *   |  |
+ *   |  +- ssi[0]
+ *   |  +- ssi[1]
+ *   |  +- ssi[2]
+ *   |  ...
+ *   |
+ *   | ** these control scu
+ *   |
+ *   +- scu
+ *      |
+ *      +- scu[0]
+ *      +- scu[1]
+ *      +- scu[2]
+ *      ...
+ *
+ *
+ * for_each_rsnd_dai(xx, priv, xx)
+ *  rdai[0] => rdai[1] => rdai[2] => ...
+ *
+ * for_each_rsnd_mod(xx, rdai, xx)
+ *  [mod] => [mod] => [mod] => ...
+ *
+ * rsnd_dai_call(xxx, fn )
+ *  [mod]->fn() -> [mod]->fn() -> [mod]->fn()...
+ *
+ */
+#include <linux/pm_runtime.h>
+#include "rsnd.h"
+
+#define RSND_RATES SNDRV_PCM_RATE_8000_96000
+#define RSND_FMTS (SNDRV_PCM_FMTBIT_S24_LE | SNDRV_PCM_FMTBIT_S16_LE)
+
+/*
+ *     rsnd_platform functions
+ */
+#define rsnd_platform_call(priv, dai, func, param...)  \
+       (!(priv->info->func) ? -ENODEV :                \
+        priv->info->func(param))
+
+
+/*
+ *     basic function
+ */
+u32 rsnd_read(struct rsnd_priv *priv,
+             struct rsnd_mod *mod, enum rsnd_reg reg)
+{
+       void __iomem *base = rsnd_gen_reg_get(priv, mod, reg);
+
+       BUG_ON(!base);
+
+       return ioread32(base);
+}
+
+void rsnd_write(struct rsnd_priv *priv,
+               struct rsnd_mod *mod,
+               enum rsnd_reg reg, u32 data)
+{
+       void __iomem *base = rsnd_gen_reg_get(priv, mod, reg);
+       struct device *dev = rsnd_priv_to_dev(priv);
+
+       BUG_ON(!base);
+
+       dev_dbg(dev, "w %p : %08x\n", base, data);
+
+       iowrite32(data, base);
+}
+
+void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod,
+              enum rsnd_reg reg, u32 mask, u32 data)
+{
+       void __iomem *base = rsnd_gen_reg_get(priv, mod, reg);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       u32 val;
+
+       BUG_ON(!base);
+
+       val = ioread32(base);
+       val &= ~mask;
+       val |= data & mask;
+       iowrite32(val, base);
+
+       dev_dbg(dev, "s %p : %08x\n", base, val);
+}
+
+/*
+ *     rsnd_mod functions
+ */
+char *rsnd_mod_name(struct rsnd_mod *mod)
+{
+       if (!mod || !mod->ops)
+               return "unknown";
+
+       return mod->ops->name;
+}
+
+void rsnd_mod_init(struct rsnd_priv *priv,
+                  struct rsnd_mod *mod,
+                  struct rsnd_mod_ops *ops,
+                  int id)
+{
+       mod->priv       = priv;
+       mod->id         = id;
+       mod->ops        = ops;
+       INIT_LIST_HEAD(&mod->list);
+}
+
+/*
+ *     rsnd_dma functions
+ */
+static void rsnd_dma_continue(struct rsnd_dma *dma)
+{
+       /* push next A or B plane */
+       dma->submit_loop = 1;
+       schedule_work(&dma->work);
+}
+
+void rsnd_dma_start(struct rsnd_dma *dma)
+{
+       /* push both A and B plane*/
+       dma->submit_loop = 2;
+       schedule_work(&dma->work);
+}
+
+void rsnd_dma_stop(struct rsnd_dma *dma)
+{
+       dma->submit_loop = 0;
+       cancel_work_sync(&dma->work);
+       dmaengine_terminate_all(dma->chan);
+}
+
+static void rsnd_dma_complete(void *data)
+{
+       struct rsnd_dma *dma = (struct rsnd_dma *)data;
+       struct rsnd_priv *priv = dma->priv;
+       unsigned long flags;
+
+       rsnd_lock(priv, flags);
+
+       dma->complete(dma);
+
+       if (dma->submit_loop)
+               rsnd_dma_continue(dma);
+
+       rsnd_unlock(priv, flags);
+}
+
+static void rsnd_dma_do_work(struct work_struct *work)
+{
+       struct rsnd_dma *dma = container_of(work, struct rsnd_dma, work);
+       struct rsnd_priv *priv = dma->priv;
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct dma_async_tx_descriptor *desc;
+       dma_addr_t buf;
+       size_t len;
+       int i;
+
+       for (i = 0; i < dma->submit_loop; i++) {
+
+               if (dma->inquiry(dma, &buf, &len) < 0)
+                       return;
+
+               desc = dmaengine_prep_slave_single(
+                       dma->chan, buf, len, dma->dir,
+                       DMA_PREP_INTERRUPT | DMA_CTRL_ACK);
+               if (!desc) {
+                       dev_err(dev, "dmaengine_prep_slave_sg() fail\n");
+                       return;
+               }
+
+               desc->callback          = rsnd_dma_complete;
+               desc->callback_param    = dma;
+
+               if (dmaengine_submit(desc) < 0) {
+                       dev_err(dev, "dmaengine_submit() fail\n");
+                       return;
+               }
+
+       }
+
+       dma_async_issue_pending(dma->chan);
+}
+
+int rsnd_dma_available(struct rsnd_dma *dma)
+{
+       return !!dma->chan;
+}
+
+static bool rsnd_dma_filter(struct dma_chan *chan, void *param)
+{
+       chan->private = param;
+
+       return true;
+}
+
+int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
+                 int is_play, int id,
+                 int (*inquiry)(struct rsnd_dma *dma,
+                                 dma_addr_t *buf, int *len),
+                 int (*complete)(struct rsnd_dma *dma))
+{
+       struct device *dev = rsnd_priv_to_dev(priv);
+       dma_cap_mask_t mask;
+
+       if (dma->chan) {
+               dev_err(dev, "it already has dma channel\n");
+               return -EIO;
+       }
+
+       dma_cap_zero(mask);
+       dma_cap_set(DMA_SLAVE, mask);
+
+       dma->slave.shdma_slave.slave_id = id;
+
+       dma->chan = dma_request_channel(mask, rsnd_dma_filter,
+                                       &dma->slave.shdma_slave);
+       if (!dma->chan) {
+               dev_err(dev, "can't get dma channel\n");
+               return -EIO;
+       }
+
+       dma->dir = is_play ? DMA_TO_DEVICE : DMA_FROM_DEVICE;
+       dma->priv = priv;
+       dma->inquiry = inquiry;
+       dma->complete = complete;
+       INIT_WORK(&dma->work, rsnd_dma_do_work);
+
+       return 0;
+}
+
+void  rsnd_dma_quit(struct rsnd_priv *priv,
+                   struct rsnd_dma *dma)
+{
+       if (dma->chan)
+               dma_release_channel(dma->chan);
+
+       dma->chan = NULL;
+}
+
+/*
+ *     rsnd_dai functions
+ */
+#define rsnd_dai_call(rdai, io, fn)                    \
+({                                                     \
+       struct rsnd_mod *mod, *n;                       \
+       int ret = 0;                                    \
+       for_each_rsnd_mod(mod, n, io) {                 \
+               ret = rsnd_mod_call(mod, fn, rdai, io); \
+               if (ret < 0)                            \
+                       break;                          \
+       }                                               \
+       ret;                                            \
+})
+
+int rsnd_dai_connect(struct rsnd_dai *rdai,
+                    struct rsnd_mod *mod,
+                    struct rsnd_dai_stream *io)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+
+       if (!mod) {
+               dev_err(dev, "NULL mod\n");
+               return -EIO;
+       }
+
+       if (!list_empty(&mod->list)) {
+               dev_err(dev, "%s%d is not empty\n",
+                       rsnd_mod_name(mod),
+                       rsnd_mod_id(mod));
+               return -EIO;
+       }
+
+       list_add_tail(&mod->list, &io->head);
+
+       return 0;
+}
+
+int rsnd_dai_disconnect(struct rsnd_mod *mod)
+{
+       list_del_init(&mod->list);
+
+       return 0;
+}
+
+int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai)
+{
+       int id = rdai - priv->rdai;
+
+       if ((id < 0) || (id >= rsnd_dai_nr(priv)))
+               return -EINVAL;
+
+       return id;
+}
+
+struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id)
+{
+       return priv->rdai + id;
+}
+
+static struct rsnd_dai *rsnd_dai_to_rdai(struct snd_soc_dai *dai)
+{
+       struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
+
+       return rsnd_dai_get(priv, dai->id);
+}
+
+int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io)
+{
+       return &rdai->playback == io;
+}
+
+/*
+ *     rsnd_soc_dai functions
+ */
+int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional)
+{
+       struct snd_pcm_substream *substream = io->substream;
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int pos = io->byte_pos + additional;
+
+       pos %= (runtime->periods * io->byte_per_period);
+
+       return pos;
+}
+
+void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int byte)
+{
+       io->byte_pos += byte;
+
+       if (io->byte_pos >= io->next_period_byte) {
+               struct snd_pcm_substream *substream = io->substream;
+               struct snd_pcm_runtime *runtime = substream->runtime;
+
+               io->period_pos++;
+               io->next_period_byte += io->byte_per_period;
+
+               if (io->period_pos >= runtime->periods) {
+                       io->byte_pos = 0;
+                       io->period_pos = 0;
+                       io->next_period_byte = io->byte_per_period;
+               }
+
+               snd_pcm_period_elapsed(substream);
+       }
+}
+
+static int rsnd_dai_stream_init(struct rsnd_dai_stream *io,
+                               struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+
+       if (!list_empty(&io->head))
+               return -EIO;
+
+       INIT_LIST_HEAD(&io->head);
+       io->substream           = substream;
+       io->byte_pos            = 0;
+       io->period_pos          = 0;
+       io->byte_per_period     = runtime->period_size *
+                                 runtime->channels *
+                                 samples_to_bytes(runtime, 1);
+       io->next_period_byte    = io->byte_per_period;
+
+       return 0;
+}
+
+static
+struct snd_soc_dai *rsnd_substream_to_dai(struct snd_pcm_substream *substream)
+{
+       struct snd_soc_pcm_runtime *rtd = substream->private_data;
+
+       return  rtd->cpu_dai;
+}
+
+static
+struct rsnd_dai_stream *rsnd_rdai_to_io(struct rsnd_dai *rdai,
+                                       struct snd_pcm_substream *substream)
+{
+       if (substream->stream == SNDRV_PCM_STREAM_PLAYBACK)
+               return &rdai->playback;
+       else
+               return &rdai->capture;
+}
+
+static int rsnd_soc_dai_trigger(struct snd_pcm_substream *substream, int cmd,
+                           struct snd_soc_dai *dai)
+{
+       struct rsnd_priv *priv = snd_soc_dai_get_drvdata(dai);
+       struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+       struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+       struct rsnd_mod *mod = rsnd_ssi_mod_get_frm_dai(priv,
+                                               rsnd_dai_id(priv, rdai),
+                                               rsnd_dai_is_play(rdai, io));
+       int ssi_id = rsnd_mod_id(mod);
+       int ret;
+       unsigned long flags;
+
+       rsnd_lock(priv, flags);
+
+       switch (cmd) {
+       case SNDRV_PCM_TRIGGER_START:
+               ret = rsnd_dai_stream_init(io, substream);
+               if (ret < 0)
+                       goto dai_trigger_end;
+
+               ret = rsnd_platform_call(priv, dai, start, ssi_id);
+               if (ret < 0)
+                       goto dai_trigger_end;
+
+               ret = rsnd_gen_path_init(priv, rdai, io);
+               if (ret < 0)
+                       goto dai_trigger_end;
+
+               ret = rsnd_dai_call(rdai, io, init);
+               if (ret < 0)
+                       goto dai_trigger_end;
+
+               ret = rsnd_dai_call(rdai, io, start);
+               if (ret < 0)
+                       goto dai_trigger_end;
+               break;
+       case SNDRV_PCM_TRIGGER_STOP:
+               ret = rsnd_dai_call(rdai, io, stop);
+               if (ret < 0)
+                       goto dai_trigger_end;
+
+               ret = rsnd_dai_call(rdai, io, quit);
+               if (ret < 0)
+                       goto dai_trigger_end;
+
+               ret = rsnd_gen_path_exit(priv, rdai, io);
+               if (ret < 0)
+                       goto dai_trigger_end;
+
+               ret = rsnd_platform_call(priv, dai, stop, ssi_id);
+               if (ret < 0)
+                       goto dai_trigger_end;
+               break;
+       default:
+               ret = -EINVAL;
+       }
+
+dai_trigger_end:
+       rsnd_unlock(priv, flags);
+
+       return ret;
+}
+
+static int rsnd_soc_dai_set_fmt(struct snd_soc_dai *dai, unsigned int fmt)
+{
+       struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+
+       /* set master/slave audio interface */
+       switch (fmt & SND_SOC_DAIFMT_MASTER_MASK) {
+       case SND_SOC_DAIFMT_CBM_CFM:
+               rdai->clk_master = 1;
+               break;
+       case SND_SOC_DAIFMT_CBS_CFS:
+               rdai->clk_master = 0;
+               break;
+       default:
+               return -EINVAL;
+       }
+
+       /* set clock inversion */
+       switch (fmt & SND_SOC_DAIFMT_INV_MASK) {
+       case SND_SOC_DAIFMT_NB_IF:
+               rdai->bit_clk_inv = 0;
+               rdai->frm_clk_inv = 1;
+               break;
+       case SND_SOC_DAIFMT_IB_NF:
+               rdai->bit_clk_inv = 1;
+               rdai->frm_clk_inv = 0;
+               break;
+       case SND_SOC_DAIFMT_IB_IF:
+               rdai->bit_clk_inv = 1;
+               rdai->frm_clk_inv = 1;
+               break;
+       case SND_SOC_DAIFMT_NB_NF:
+       default:
+               rdai->bit_clk_inv = 0;
+               rdai->frm_clk_inv = 0;
+               break;
+       }
+
+       /* set format */
+       switch (fmt & SND_SOC_DAIFMT_FORMAT_MASK) {
+       case SND_SOC_DAIFMT_I2S:
+               rdai->sys_delay = 0;
+               rdai->data_alignment = 0;
+               break;
+       case SND_SOC_DAIFMT_LEFT_J:
+               rdai->sys_delay = 1;
+               rdai->data_alignment = 0;
+               break;
+       case SND_SOC_DAIFMT_RIGHT_J:
+               rdai->sys_delay = 1;
+               rdai->data_alignment = 1;
+               break;
+       }
+
+       return 0;
+}
+
+static const struct snd_soc_dai_ops rsnd_soc_dai_ops = {
+       .trigger        = rsnd_soc_dai_trigger,
+       .set_fmt        = rsnd_soc_dai_set_fmt,
+};
+
+static int rsnd_dai_probe(struct platform_device *pdev,
+                         struct rcar_snd_info *info,
+                         struct rsnd_priv *priv)
+{
+       struct snd_soc_dai_driver *drv;
+       struct rsnd_dai *rdai;
+       struct rsnd_mod *pmod, *cmod;
+       struct device *dev = rsnd_priv_to_dev(priv);
+       int dai_nr;
+       int i;
+
+       /* get max dai nr */
+       for (dai_nr = 0; dai_nr < 32; dai_nr++) {
+               pmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 1);
+               cmod = rsnd_ssi_mod_get_frm_dai(priv, dai_nr, 0);
+
+               if (!pmod && !cmod)
+                       break;
+       }
+
+       if (!dai_nr) {
+               dev_err(dev, "no dai\n");
+               return -EIO;
+       }
+
+       drv  = devm_kzalloc(dev, sizeof(*drv)  * dai_nr, GFP_KERNEL);
+       rdai = devm_kzalloc(dev, sizeof(*rdai) * dai_nr, GFP_KERNEL);
+       if (!drv || !rdai) {
+               dev_err(dev, "dai allocate failed\n");
+               return -ENOMEM;
+       }
+
+       for (i = 0; i < dai_nr; i++) {
+
+               pmod = rsnd_ssi_mod_get_frm_dai(priv, i, 1);
+               cmod = rsnd_ssi_mod_get_frm_dai(priv, i, 0);
+
+               /*
+                *      init rsnd_dai
+                */
+               INIT_LIST_HEAD(&rdai[i].playback.head);
+               INIT_LIST_HEAD(&rdai[i].capture.head);
+
+               snprintf(rdai[i].name, RSND_DAI_NAME_SIZE, "rsnd-dai.%d", i);
+
+               /*
+                *      init snd_soc_dai_driver
+                */
+               drv[i].name     = rdai[i].name;
+               drv[i].ops      = &rsnd_soc_dai_ops;
+               if (pmod) {
+                       drv[i].playback.rates           = RSND_RATES;
+                       drv[i].playback.formats         = RSND_FMTS;
+                       drv[i].playback.channels_min    = 2;
+                       drv[i].playback.channels_max    = 2;
+               }
+               if (cmod) {
+                       drv[i].capture.rates            = RSND_RATES;
+                       drv[i].capture.formats          = RSND_FMTS;
+                       drv[i].capture.channels_min     = 2;
+                       drv[i].capture.channels_max     = 2;
+               }
+
+               dev_dbg(dev, "%s (%s/%s)\n", rdai[i].name,
+                       pmod ? "play"    : " -- ",
+                       cmod ? "capture" : "  --   ");
+       }
+
+       priv->dai_nr    = dai_nr;
+       priv->daidrv    = drv;
+       priv->rdai      = rdai;
+
+       return 0;
+}
+
+static void rsnd_dai_remove(struct platform_device *pdev,
+                         struct rsnd_priv *priv)
+{
+}
+
+/*
+ *             pcm ops
+ */
+static struct snd_pcm_hardware rsnd_pcm_hardware = {
+       .info =         SNDRV_PCM_INFO_INTERLEAVED      |
+                       SNDRV_PCM_INFO_MMAP             |
+                       SNDRV_PCM_INFO_MMAP_VALID       |
+                       SNDRV_PCM_INFO_PAUSE,
+       .formats                = RSND_FMTS,
+       .rates                  = RSND_RATES,
+       .rate_min               = 8000,
+       .rate_max               = 192000,
+       .channels_min           = 2,
+       .channels_max           = 2,
+       .buffer_bytes_max       = 64 * 1024,
+       .period_bytes_min       = 32,
+       .period_bytes_max       = 8192,
+       .periods_min            = 1,
+       .periods_max            = 32,
+       .fifo_size              = 256,
+};
+
+static int rsnd_pcm_open(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       int ret = 0;
+
+       snd_soc_set_runtime_hwparams(substream, &rsnd_pcm_hardware);
+
+       ret = snd_pcm_hw_constraint_integer(runtime,
+                                           SNDRV_PCM_HW_PARAM_PERIODS);
+
+       return ret;
+}
+
+static int rsnd_hw_params(struct snd_pcm_substream *substream,
+                        struct snd_pcm_hw_params *hw_params)
+{
+       return snd_pcm_lib_malloc_pages(substream,
+                                       params_buffer_bytes(hw_params));
+}
+
+static snd_pcm_uframes_t rsnd_pointer(struct snd_pcm_substream *substream)
+{
+       struct snd_pcm_runtime *runtime = substream->runtime;
+       struct snd_soc_dai *dai = rsnd_substream_to_dai(substream);
+       struct rsnd_dai *rdai = rsnd_dai_to_rdai(dai);
+       struct rsnd_dai_stream *io = rsnd_rdai_to_io(rdai, substream);
+
+       return bytes_to_frames(runtime, io->byte_pos);
+}
+
+static struct snd_pcm_ops rsnd_pcm_ops = {
+       .open           = rsnd_pcm_open,
+       .ioctl          = snd_pcm_lib_ioctl,
+       .hw_params      = rsnd_hw_params,
+       .hw_free        = snd_pcm_lib_free_pages,
+       .pointer        = rsnd_pointer,
+};
+
+/*
+ *             snd_soc_platform
+ */
+
+#define PREALLOC_BUFFER                (32 * 1024)
+#define PREALLOC_BUFFER_MAX    (32 * 1024)
+
+static int rsnd_pcm_new(struct snd_soc_pcm_runtime *rtd)
+{
+       return snd_pcm_lib_preallocate_pages_for_all(
+               rtd->pcm,
+               SNDRV_DMA_TYPE_DEV,
+               rtd->card->snd_card->dev,
+               PREALLOC_BUFFER, PREALLOC_BUFFER_MAX);
+}
+
+static void rsnd_pcm_free(struct snd_pcm *pcm)
+{
+       snd_pcm_lib_preallocate_free_for_all(pcm);
+}
+
+static struct snd_soc_platform_driver rsnd_soc_platform = {
+       .ops            = &rsnd_pcm_ops,
+       .pcm_new        = rsnd_pcm_new,
+       .pcm_free       = rsnd_pcm_free,
+};
+
+static const struct snd_soc_component_driver rsnd_soc_component = {
+       .name           = "rsnd",
+};
+
+/*
+ *     rsnd probe
+ */
+static int rsnd_probe(struct platform_device *pdev)
+{
+       struct rcar_snd_info *info;
+       struct rsnd_priv *priv;
+       struct device *dev = &pdev->dev;
+       int ret;
+
+       info = pdev->dev.platform_data;
+       if (!info) {
+               dev_err(dev, "driver needs R-Car sound information\n");
+               return -ENODEV;
+       }
+
+       /*
+        *      init priv data
+        */
+       priv = devm_kzalloc(dev, sizeof(*priv), GFP_KERNEL);
+       if (!priv) {
+               dev_err(dev, "priv allocate failed\n");
+               return -ENODEV;
+       }
+
+       priv->dev       = dev;
+       priv->info      = info;
+       spin_lock_init(&priv->lock);
+
+       /*
+        *      init each module
+        */
+       ret = rsnd_gen_probe(pdev, info, priv);
+       if (ret < 0)
+               return ret;
+
+       ret = rsnd_scu_probe(pdev, info, priv);
+       if (ret < 0)
+               return ret;
+
+       ret = rsnd_adg_probe(pdev, info, priv);
+       if (ret < 0)
+               return ret;
+
+       ret = rsnd_ssi_probe(pdev, info, priv);
+       if (ret < 0)
+               return ret;
+
+       ret = rsnd_dai_probe(pdev, info, priv);
+       if (ret < 0)
+               return ret;
+
+       /*
+        *      asoc register
+        */
+       ret = snd_soc_register_platform(dev, &rsnd_soc_platform);
+       if (ret < 0) {
+               dev_err(dev, "cannot snd soc register\n");
+               return ret;
+       }
+
+       ret = snd_soc_register_component(dev, &rsnd_soc_component,
+                                        priv->daidrv, rsnd_dai_nr(priv));
+       if (ret < 0) {
+               dev_err(dev, "cannot snd dai register\n");
+               goto exit_snd_soc;
+       }
+
+       dev_set_drvdata(dev, priv);
+
+       pm_runtime_enable(dev);
+
+       dev_info(dev, "probed\n");
+       return ret;
+
+exit_snd_soc:
+       snd_soc_unregister_platform(dev);
+
+       return ret;
+}
+
+static int rsnd_remove(struct platform_device *pdev)
+{
+       struct rsnd_priv *priv = dev_get_drvdata(&pdev->dev);
+
+       pm_runtime_disable(&pdev->dev);
+
+       /*
+        *      remove each module
+        */
+       rsnd_ssi_remove(pdev, priv);
+       rsnd_adg_remove(pdev, priv);
+       rsnd_scu_remove(pdev, priv);
+       rsnd_dai_remove(pdev, priv);
+       rsnd_gen_remove(pdev, priv);
+
+       return 0;
+}
+
+static struct platform_driver rsnd_driver = {
+       .driver = {
+               .name   = "rcar_sound",
+       },
+       .probe          = rsnd_probe,
+       .remove         = rsnd_remove,
+};
+module_platform_driver(rsnd_driver);
+
+MODULE_LICENSE("GPL");
+MODULE_DESCRIPTION("Renesas R-Car audio driver");
+MODULE_AUTHOR("Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>");
+MODULE_ALIAS("platform:rcar-pcm-audio");
diff --git a/sound/soc/sh/rcar/gen.c b/sound/soc/sh/rcar/gen.c
new file mode 100644 (file)
index 0000000..babb203
--- /dev/null
@@ -0,0 +1,280 @@
+/*
+ * Renesas R-Car Gen1 SRU/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.
+ */
+#include "rsnd.h"
+
+struct rsnd_gen_ops {
+       int (*path_init)(struct rsnd_priv *priv,
+                        struct rsnd_dai *rdai,
+                        struct rsnd_dai_stream *io);
+       int (*path_exit)(struct rsnd_priv *priv,
+                        struct rsnd_dai *rdai,
+                        struct rsnd_dai_stream *io);
+};
+
+struct rsnd_gen_reg_map {
+       int index;      /* -1 : not supported */
+       u32 offset_id;  /* offset of ssi0, ssi1, ssi2... */
+       u32 offset_adr; /* offset of SSICR, SSISR, ... */
+};
+
+struct rsnd_gen {
+       void __iomem *base[RSND_BASE_MAX];
+
+       struct rsnd_gen_reg_map reg_map[RSND_REG_MAX];
+       struct rsnd_gen_ops *ops;
+};
+
+#define rsnd_priv_to_gen(p)    ((struct rsnd_gen *)(p)->gen)
+
+/*
+ *             Gen2
+ *             will be filled in the future
+ */
+
+/*
+ *             Gen1
+ */
+static int rsnd_gen1_path_init(struct rsnd_priv *priv,
+                              struct rsnd_dai *rdai,
+                              struct rsnd_dai_stream *io)
+{
+       struct rsnd_mod *mod;
+       int ret;
+       int id;
+
+       /*
+        * Gen1 is created by SRU/SSI, and this SRU is base module of
+        * Gen2's SCU/SSIU/SSI. (Gen2 SCU/SSIU came from SRU)
+        *
+        * Easy image is..
+        *      Gen1 SRU = Gen2 SCU + SSIU + etc
+        *
+        * Gen2 SCU path is very flexible, but, Gen1 SRU (SCU parts) is
+        * using fixed path.
+        *
+        * Then, SSI id = SCU id here
+        */
+
+       /* get SSI's ID */
+       mod = rsnd_ssi_mod_get_frm_dai(priv,
+                                      rsnd_dai_id(priv, rdai),
+                                      rsnd_dai_is_play(rdai, io));
+       id = rsnd_mod_id(mod);
+
+       /* SSI */
+       mod = rsnd_ssi_mod_get(priv, id);
+       ret = rsnd_dai_connect(rdai, mod, io);
+       if (ret < 0)
+               return ret;
+
+       /* SCU */
+       mod = rsnd_scu_mod_get(priv, id);
+       ret = rsnd_dai_connect(rdai, mod, io);
+
+       return ret;
+}
+
+static int rsnd_gen1_path_exit(struct rsnd_priv *priv,
+                              struct rsnd_dai *rdai,
+                              struct rsnd_dai_stream *io)
+{
+       struct rsnd_mod *mod, *n;
+       int ret = 0;
+
+       /*
+        * remove all mod from rdai
+        */
+       for_each_rsnd_mod(mod, n, io)
+               ret |= rsnd_dai_disconnect(mod);
+
+       return ret;
+}
+
+static struct rsnd_gen_ops rsnd_gen1_ops = {
+       .path_init      = rsnd_gen1_path_init,
+       .path_exit      = rsnd_gen1_path_exit,
+};
+
+#define RSND_GEN1_REG_MAP(g, s, i, oi, oa)                             \
+       do {                                                            \
+               (g)->reg_map[RSND_REG_##i].index  = RSND_GEN1_##s;      \
+               (g)->reg_map[RSND_REG_##i].offset_id = oi;              \
+               (g)->reg_map[RSND_REG_##i].offset_adr = oa;             \
+       } while (0)
+
+static void rsnd_gen1_reg_map_init(struct rsnd_gen *gen)
+{
+       RSND_GEN1_REG_MAP(gen, SRU,     SRC_ROUTE_SEL,  0x0,    0x00);
+       RSND_GEN1_REG_MAP(gen, SRU,     SRC_TMG_SEL0,   0x0,    0x08);
+       RSND_GEN1_REG_MAP(gen, SRU,     SRC_TMG_SEL1,   0x0,    0x0c);
+       RSND_GEN1_REG_MAP(gen, SRU,     SRC_TMG_SEL2,   0x0,    0x10);
+       RSND_GEN1_REG_MAP(gen, SRU,     SRC_CTRL,       0x0,    0xc0);
+       RSND_GEN1_REG_MAP(gen, SRU,     SSI_MODE0,      0x0,    0xD0);
+       RSND_GEN1_REG_MAP(gen, SRU,     SSI_MODE1,      0x0,    0xD4);
+       RSND_GEN1_REG_MAP(gen, SRU,     BUSIF_MODE,     0x4,    0x20);
+       RSND_GEN1_REG_MAP(gen, SRU,     BUSIF_ADINR,    0x40,   0x214);
+
+       RSND_GEN1_REG_MAP(gen, ADG,     BRRA,           0x0,    0x00);
+       RSND_GEN1_REG_MAP(gen, ADG,     BRRB,           0x0,    0x04);
+       RSND_GEN1_REG_MAP(gen, ADG,     SSICKR,         0x0,    0x08);
+       RSND_GEN1_REG_MAP(gen, ADG,     AUDIO_CLK_SEL0, 0x0,    0x0c);
+       RSND_GEN1_REG_MAP(gen, ADG,     AUDIO_CLK_SEL1, 0x0,    0x10);
+       RSND_GEN1_REG_MAP(gen, ADG,     AUDIO_CLK_SEL3, 0x0,    0x18);
+       RSND_GEN1_REG_MAP(gen, ADG,     AUDIO_CLK_SEL4, 0x0,    0x1c);
+       RSND_GEN1_REG_MAP(gen, ADG,     AUDIO_CLK_SEL5, 0x0,    0x20);
+
+       RSND_GEN1_REG_MAP(gen, SSI,     SSICR,          0x40,   0x00);
+       RSND_GEN1_REG_MAP(gen, SSI,     SSISR,          0x40,   0x04);
+       RSND_GEN1_REG_MAP(gen, SSI,     SSITDR,         0x40,   0x08);
+       RSND_GEN1_REG_MAP(gen, SSI,     SSIRDR,         0x40,   0x0c);
+       RSND_GEN1_REG_MAP(gen, SSI,     SSIWSR,         0x40,   0x20);
+}
+
+static int rsnd_gen1_probe(struct platform_device *pdev,
+                          struct rcar_snd_info *info,
+                          struct rsnd_priv *priv)
+{
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+       struct resource *sru_res;
+       struct resource *adg_res;
+       struct resource *ssi_res;
+
+       /*
+        * map address
+        */
+       sru_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SRU);
+       adg_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_ADG);
+       ssi_res = platform_get_resource(pdev, IORESOURCE_MEM, RSND_GEN1_SSI);
+
+       gen->base[RSND_GEN1_SRU] = devm_ioremap_resource(dev, sru_res);
+       gen->base[RSND_GEN1_ADG] = devm_ioremap_resource(dev, adg_res);
+       gen->base[RSND_GEN1_SSI] = devm_ioremap_resource(dev, ssi_res);
+       if (IS_ERR(gen->base[RSND_GEN1_SRU]) ||
+           IS_ERR(gen->base[RSND_GEN1_ADG]) ||
+           IS_ERR(gen->base[RSND_GEN1_SSI]))
+               return -ENODEV;
+
+       gen->ops = &rsnd_gen1_ops;
+       rsnd_gen1_reg_map_init(gen);
+
+       dev_dbg(dev, "Gen1 device probed\n");
+       dev_dbg(dev, "SRU : %08x => %p\n",      sru_res->start,
+                                               gen->base[RSND_GEN1_SRU]);
+       dev_dbg(dev, "ADG : %08x => %p\n",      adg_res->start,
+                                               gen->base[RSND_GEN1_ADG]);
+       dev_dbg(dev, "SSI : %08x => %p\n",      ssi_res->start,
+                                               gen->base[RSND_GEN1_SSI]);
+
+       return 0;
+
+}
+
+static void rsnd_gen1_remove(struct platform_device *pdev,
+                            struct rsnd_priv *priv)
+{
+}
+
+/*
+ *             Gen
+ */
+int rsnd_gen_path_init(struct rsnd_priv *priv,
+                      struct rsnd_dai *rdai,
+                      struct rsnd_dai_stream *io)
+{
+       struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+       return gen->ops->path_init(priv, rdai, io);
+}
+
+int rsnd_gen_path_exit(struct rsnd_priv *priv,
+                      struct rsnd_dai *rdai,
+                      struct rsnd_dai_stream *io)
+{
+       struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+
+       return gen->ops->path_exit(priv, rdai, io);
+}
+
+void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
+                              struct rsnd_mod *mod,
+                              enum rsnd_reg reg)
+{
+       struct rsnd_gen *gen = rsnd_priv_to_gen(priv);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       int index;
+       u32 offset_id, offset_adr;
+
+       if (reg >= RSND_REG_MAX) {
+               dev_err(dev, "rsnd_reg reg error\n");
+               return NULL;
+       }
+
+       index           = gen->reg_map[reg].index;
+       offset_id       = gen->reg_map[reg].offset_id;
+       offset_adr      = gen->reg_map[reg].offset_adr;
+
+       if (index < 0) {
+               dev_err(dev, "unsupported reg access %d\n", reg);
+               return NULL;
+       }
+
+       if (offset_id && mod)
+               offset_id *= rsnd_mod_id(mod);
+
+       /*
+        * index/offset were set on gen1/gen2
+        */
+
+       return gen->base[index] + offset_id + offset_adr;
+}
+
+int rsnd_gen_probe(struct platform_device *pdev,
+                  struct rcar_snd_info *info,
+                  struct rsnd_priv *priv)
+{
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_gen *gen;
+       int i;
+
+       gen = devm_kzalloc(dev, sizeof(*gen), GFP_KERNEL);
+       if (!gen) {
+               dev_err(dev, "GEN allocate failed\n");
+               return -ENOMEM;
+       }
+
+       priv->gen = gen;
+
+       /*
+        * see
+        *      rsnd_reg_get()
+        *      rsnd_gen_probe()
+        */
+       for (i = 0; i < RSND_REG_MAX; i++)
+               gen->reg_map[i].index = -1;
+
+       /*
+        *      init each module
+        */
+       if (rsnd_is_gen1(priv))
+               return rsnd_gen1_probe(pdev, info, priv);
+
+       dev_err(dev, "unknown generation R-Car sound device\n");
+
+       return -ENODEV;
+}
+
+void rsnd_gen_remove(struct platform_device *pdev,
+                    struct rsnd_priv *priv)
+{
+       if (rsnd_is_gen1(priv))
+               rsnd_gen1_remove(pdev, priv);
+}
diff --git a/sound/soc/sh/rcar/rsnd.h b/sound/soc/sh/rcar/rsnd.h
new file mode 100644 (file)
index 0000000..9cc6986
--- /dev/null
@@ -0,0 +1,302 @@
+/*
+ * Renesas R-Car
+ *
+ * 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 RSND_H
+#define RSND_H
+
+#include <linux/clk.h>
+#include <linux/device.h>
+#include <linux/dma-mapping.h>
+#include <linux/io.h>
+#include <linux/list.h>
+#include <linux/module.h>
+#include <linux/sh_dma.h>
+#include <linux/workqueue.h>
+#include <sound/rcar_snd.h>
+#include <sound/soc.h>
+#include <sound/pcm_params.h>
+
+/*
+ *     pseudo register
+ *
+ * The register address offsets SRU/SCU/SSIU on Gen1/Gen2 are very different.
+ * This driver uses pseudo register in order to hide it.
+ * see gen1/gen2 for detail
+ */
+enum rsnd_reg {
+       /* SRU/SCU */
+       RSND_REG_SRC_ROUTE_SEL,
+       RSND_REG_SRC_TMG_SEL0,
+       RSND_REG_SRC_TMG_SEL1,
+       RSND_REG_SRC_TMG_SEL2,
+       RSND_REG_SRC_CTRL,
+       RSND_REG_SSI_MODE0,
+       RSND_REG_SSI_MODE1,
+       RSND_REG_BUSIF_MODE,
+       RSND_REG_BUSIF_ADINR,
+
+       /* ADG */
+       RSND_REG_BRRA,
+       RSND_REG_BRRB,
+       RSND_REG_SSICKR,
+       RSND_REG_AUDIO_CLK_SEL0,
+       RSND_REG_AUDIO_CLK_SEL1,
+       RSND_REG_AUDIO_CLK_SEL2,
+       RSND_REG_AUDIO_CLK_SEL3,
+       RSND_REG_AUDIO_CLK_SEL4,
+       RSND_REG_AUDIO_CLK_SEL5,
+
+       /* SSI */
+       RSND_REG_SSICR,
+       RSND_REG_SSISR,
+       RSND_REG_SSITDR,
+       RSND_REG_SSIRDR,
+       RSND_REG_SSIWSR,
+
+       RSND_REG_MAX,
+};
+
+struct rsnd_priv;
+struct rsnd_mod;
+struct rsnd_dai;
+struct rsnd_dai_stream;
+
+/*
+ *     R-Car basic functions
+ */
+#define rsnd_mod_read(m, r) \
+       rsnd_read(rsnd_mod_to_priv(m), m, RSND_REG_##r)
+#define rsnd_mod_write(m, r, d) \
+       rsnd_write(rsnd_mod_to_priv(m), m, RSND_REG_##r, d)
+#define rsnd_mod_bset(m, r, s, d) \
+       rsnd_bset(rsnd_mod_to_priv(m), m, RSND_REG_##r, s, d)
+
+#define rsnd_priv_read(p, r)           rsnd_read(p, NULL, RSND_REG_##r)
+#define rsnd_priv_write(p, r, d)       rsnd_write(p, NULL, RSND_REG_##r, d)
+#define rsnd_priv_bset(p, r, s, d)     rsnd_bset(p, NULL, RSND_REG_##r, s, d)
+
+u32 rsnd_read(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg);
+void rsnd_write(struct rsnd_priv *priv, struct rsnd_mod *mod,
+               enum rsnd_reg reg, u32 data);
+void rsnd_bset(struct rsnd_priv *priv, struct rsnd_mod *mod, enum rsnd_reg reg,
+                   u32 mask, u32 data);
+
+/*
+ *     R-Car DMA
+ */
+struct rsnd_dma {
+       struct rsnd_priv        *priv;
+       struct sh_dmae_slave    slave;
+       struct work_struct      work;
+       struct dma_chan         *chan;
+       enum dma_data_direction dir;
+       int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len);
+       int (*complete)(struct rsnd_dma *dma);
+
+       int submit_loop;
+};
+
+void rsnd_dma_start(struct rsnd_dma *dma);
+void rsnd_dma_stop(struct rsnd_dma *dma);
+int rsnd_dma_available(struct rsnd_dma *dma);
+int rsnd_dma_init(struct rsnd_priv *priv, struct rsnd_dma *dma,
+       int is_play, int id,
+       int (*inquiry)(struct rsnd_dma *dma, dma_addr_t *buf, int *len),
+       int (*complete)(struct rsnd_dma *dma));
+void  rsnd_dma_quit(struct rsnd_priv *priv,
+                   struct rsnd_dma *dma);
+
+
+/*
+ *     R-Car sound mod
+ */
+
+struct rsnd_mod_ops {
+       char *name;
+       int (*init)(struct rsnd_mod *mod,
+                   struct rsnd_dai *rdai,
+                   struct rsnd_dai_stream *io);
+       int (*quit)(struct rsnd_mod *mod,
+                   struct rsnd_dai *rdai,
+                   struct rsnd_dai_stream *io);
+       int (*start)(struct rsnd_mod *mod,
+                    struct rsnd_dai *rdai,
+                    struct rsnd_dai_stream *io);
+       int (*stop)(struct rsnd_mod *mod,
+                   struct rsnd_dai *rdai,
+                   struct rsnd_dai_stream *io);
+};
+
+struct rsnd_mod {
+       int id;
+       struct rsnd_priv *priv;
+       struct rsnd_mod_ops *ops;
+       struct list_head list; /* connect to rsnd_dai playback/capture */
+       struct rsnd_dma dma;
+};
+
+#define rsnd_mod_to_priv(mod) ((mod)->priv)
+#define rsnd_mod_to_dma(mod) (&(mod)->dma)
+#define rsnd_dma_to_mod(_dma) container_of((_dma), struct rsnd_mod, dma)
+#define rsnd_mod_id(mod) ((mod)->id)
+#define for_each_rsnd_mod(pos, n, io)  \
+       list_for_each_entry_safe(pos, n, &(io)->head, list)
+#define rsnd_mod_call(mod, func, rdai, io)     \
+       (!(mod) ? -ENODEV :                     \
+        !((mod)->ops->func) ? 0 :              \
+        (mod)->ops->func(mod, rdai, io))
+
+void rsnd_mod_init(struct rsnd_priv *priv,
+                  struct rsnd_mod *mod,
+                  struct rsnd_mod_ops *ops,
+                  int id);
+char *rsnd_mod_name(struct rsnd_mod *mod);
+
+/*
+ *     R-Car sound DAI
+ */
+#define RSND_DAI_NAME_SIZE     16
+struct rsnd_dai_stream {
+       struct list_head head; /* head of rsnd_mod list */
+       struct snd_pcm_substream *substream;
+       int byte_pos;
+       int period_pos;
+       int byte_per_period;
+       int next_period_byte;
+};
+
+struct rsnd_dai {
+       char name[RSND_DAI_NAME_SIZE];
+       struct rsnd_dai_platform_info *info; /* rcar_snd.h */
+       struct rsnd_dai_stream playback;
+       struct rsnd_dai_stream capture;
+
+       int clk_master:1;
+       int bit_clk_inv:1;
+       int frm_clk_inv:1;
+       int sys_delay:1;
+       int data_alignment:1;
+};
+
+#define rsnd_dai_nr(priv) ((priv)->dai_nr)
+#define for_each_rsnd_dai(rdai, priv, i)               \
+       for (i = 0, (rdai) = rsnd_dai_get(priv, i);     \
+            i < rsnd_dai_nr(priv);                     \
+            i++, (rdai) = rsnd_dai_get(priv, i))
+
+struct rsnd_dai *rsnd_dai_get(struct rsnd_priv *priv, int id);
+int rsnd_dai_disconnect(struct rsnd_mod *mod);
+int rsnd_dai_connect(struct rsnd_dai *rdai, struct rsnd_mod *mod,
+                    struct rsnd_dai_stream *io);
+int rsnd_dai_is_play(struct rsnd_dai *rdai, struct rsnd_dai_stream *io);
+int rsnd_dai_id(struct rsnd_priv *priv, struct rsnd_dai *rdai);
+#define rsnd_dai_get_platform_info(rdai) ((rdai)->info)
+#define rsnd_io_to_runtime(io) ((io)->substream->runtime)
+
+void rsnd_dai_pointer_update(struct rsnd_dai_stream *io, int cnt);
+int rsnd_dai_pointer_offset(struct rsnd_dai_stream *io, int additional);
+
+/*
+ *     R-Car Gen1/Gen2
+ */
+int rsnd_gen_probe(struct platform_device *pdev,
+                  struct rcar_snd_info *info,
+                  struct rsnd_priv *priv);
+void rsnd_gen_remove(struct platform_device *pdev,
+                    struct rsnd_priv *priv);
+int rsnd_gen_path_init(struct rsnd_priv *priv,
+                      struct rsnd_dai *rdai,
+                      struct rsnd_dai_stream *io);
+int rsnd_gen_path_exit(struct rsnd_priv *priv,
+                      struct rsnd_dai *rdai,
+                      struct rsnd_dai_stream *io);
+void __iomem *rsnd_gen_reg_get(struct rsnd_priv *priv,
+                              struct rsnd_mod *mod,
+                              enum rsnd_reg reg);
+#define rsnd_is_gen1(s)                ((s)->info->flags & RSND_GEN1)
+#define rsnd_is_gen2(s)                ((s)->info->flags & RSND_GEN2)
+
+/*
+ *     R-Car ADG
+ */
+int rsnd_adg_ssi_clk_stop(struct rsnd_mod *mod);
+int rsnd_adg_ssi_clk_try_start(struct rsnd_mod *mod, unsigned int rate);
+int rsnd_adg_probe(struct platform_device *pdev,
+                  struct rcar_snd_info *info,
+                  struct rsnd_priv *priv);
+void rsnd_adg_remove(struct platform_device *pdev,
+                  struct rsnd_priv *priv);
+
+/*
+ *     R-Car sound priv
+ */
+struct rsnd_priv {
+
+       struct device *dev;
+       struct rcar_snd_info *info;
+       spinlock_t lock;
+
+       /*
+        * below value will be filled on rsnd_gen_probe()
+        */
+       void *gen;
+
+       /*
+        * below value will be filled on rsnd_scu_probe()
+        */
+       void *scu;
+       int scu_nr;
+
+       /*
+        * below value will be filled on rsnd_adg_probe()
+        */
+       void *adg;
+
+       /*
+        * below value will be filled on rsnd_ssi_probe()
+        */
+       void *ssiu;
+
+       /*
+        * below value will be filled on rsnd_dai_probe()
+        */
+       struct snd_soc_dai_driver *daidrv;
+       struct rsnd_dai *rdai;
+       int dai_nr;
+};
+
+#define rsnd_priv_to_dev(priv) ((priv)->dev)
+#define rsnd_lock(priv, flags) spin_lock_irqsave(&priv->lock, flags)
+#define rsnd_unlock(priv, flags) spin_unlock_irqrestore(&priv->lock, flags)
+
+/*
+ *     R-Car SCU
+ */
+int rsnd_scu_probe(struct platform_device *pdev,
+                  struct rcar_snd_info *info,
+                  struct rsnd_priv *priv);
+void rsnd_scu_remove(struct platform_device *pdev,
+                    struct rsnd_priv *priv);
+struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id);
+#define rsnd_scu_nr(priv) ((priv)->scu_nr)
+
+/*
+ *     R-Car SSI
+ */
+int rsnd_ssi_probe(struct platform_device *pdev,
+                  struct rcar_snd_info *info,
+                  struct rsnd_priv *priv);
+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);
+struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv,
+                                         int dai_id, int is_play);
+
+#endif
diff --git a/sound/soc/sh/rcar/scu.c b/sound/soc/sh/rcar/scu.c
new file mode 100644 (file)
index 0000000..184d900
--- /dev/null
@@ -0,0 +1,236 @@
+/*
+ * Renesas R-Car SCU 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.
+ */
+#include "rsnd.h"
+
+struct rsnd_scu {
+       struct rsnd_scu_platform_info *info; /* rcar_snd.h */
+       struct rsnd_mod mod;
+};
+
+#define rsnd_scu_mode_flags(p) ((p)->info->flags)
+
+/*
+ * ADINR
+ */
+#define OTBL_24                (0 << 16)
+#define OTBL_22                (2 << 16)
+#define OTBL_20                (4 << 16)
+#define OTBL_18                (6 << 16)
+#define OTBL_16                (8 << 16)
+
+
+#define rsnd_mod_to_scu(_mod)  \
+       container_of((_mod), struct rsnd_scu, mod)
+
+#define for_each_rsnd_scu(pos, priv, i)                                        \
+       for ((i) = 0;                                                   \
+            ((i) < rsnd_scu_nr(priv)) &&                               \
+                    ((pos) = (struct rsnd_scu *)(priv)->scu + i);      \
+            i++)
+
+static int rsnd_scu_set_route(struct rsnd_priv *priv,
+                             struct rsnd_mod *mod,
+                             struct rsnd_dai *rdai,
+                             struct rsnd_dai_stream *io)
+{
+       struct scu_route_config {
+               u32 mask;
+               int shift;
+       } routes[] = {
+               { 0xF,  0, }, /* 0 */
+               { 0xF,  4, }, /* 1 */
+               { 0xF,  8, }, /* 2 */
+               { 0x7, 12, }, /* 3 */
+               { 0x7, 16, }, /* 4 */
+               { 0x7, 20, }, /* 5 */
+               { 0x7, 24, }, /* 6 */
+               { 0x3, 28, }, /* 7 */
+               { 0x3, 30, }, /* 8 */
+       };
+
+       u32 mask;
+       u32 val;
+       int shift;
+       int id;
+
+       /*
+        * Gen1 only
+        */
+       if (!rsnd_is_gen1(priv))
+               return 0;
+
+       id = rsnd_mod_id(mod);
+       if (id < 0 || id > ARRAY_SIZE(routes))
+               return -EIO;
+
+       /*
+        * SRC_ROUTE_SELECT
+        */
+       val = rsnd_dai_is_play(rdai, io) ? 0x1 : 0x2;
+       val = val               << routes[id].shift;
+       mask = routes[id].mask  << routes[id].shift;
+
+       rsnd_mod_bset(mod, SRC_ROUTE_SEL, mask, val);
+
+       /*
+        * SRC_TIMING_SELECT
+        */
+       shift   = (id % 4) * 8;
+       mask    = 0x1F << shift;
+       if (8 == id) /* SRU8 is very special */
+               val = id << shift;
+       else
+               val = (id + 1) << shift;
+
+       switch (id / 4) {
+       case 0:
+               rsnd_mod_bset(mod, SRC_TMG_SEL0, mask, val);
+               break;
+       case 1:
+               rsnd_mod_bset(mod, SRC_TMG_SEL1, mask, val);
+               break;
+       case 2:
+               rsnd_mod_bset(mod, SRC_TMG_SEL2, mask, val);
+               break;
+       }
+
+       return 0;
+}
+
+static int rsnd_scu_set_mode(struct rsnd_priv *priv,
+                            struct rsnd_mod *mod,
+                            struct rsnd_dai *rdai,
+                            struct rsnd_dai_stream *io)
+{
+       int id = rsnd_mod_id(mod);
+       u32 val;
+
+       if (rsnd_is_gen1(priv)) {
+               val = (1 << id);
+               rsnd_mod_bset(mod, SRC_CTRL, val, val);
+       }
+
+       return 0;
+}
+
+static int rsnd_scu_set_hpbif(struct rsnd_priv *priv,
+                             struct rsnd_mod *mod,
+                             struct rsnd_dai *rdai,
+                             struct rsnd_dai_stream *io)
+{
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+       u32 adinr = runtime->channels;
+
+       switch (runtime->sample_bits) {
+       case 16:
+               adinr |= OTBL_16;
+               break;
+       case 32:
+               adinr |= OTBL_24;
+               break;
+       default:
+               return -EIO;
+       }
+
+       rsnd_mod_write(mod, BUSIF_MODE, 1);
+       rsnd_mod_write(mod, BUSIF_ADINR, adinr);
+
+       return 0;
+}
+
+static int rsnd_scu_start(struct rsnd_mod *mod,
+                         struct rsnd_dai *rdai,
+                         struct rsnd_dai_stream *io)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_scu *scu = rsnd_mod_to_scu(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       u32 flags = rsnd_scu_mode_flags(scu);
+       int ret;
+
+       /*
+        * SCU will be used if it has RSND_SCU_USB_HPBIF flags
+        */
+       if (!(flags & RSND_SCU_USB_HPBIF)) {
+               /* it use PIO transter */
+               dev_dbg(dev, "%s%d is not used\n",
+                       rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+               return 0;
+       }
+
+       /* it use DMA transter */
+       ret = rsnd_scu_set_route(priv, mod, rdai, io);
+       if (ret < 0)
+               return ret;
+
+       ret = rsnd_scu_set_mode(priv, mod, rdai, io);
+       if (ret < 0)
+               return ret;
+
+       ret = rsnd_scu_set_hpbif(priv, mod, rdai, io);
+       if (ret < 0)
+               return ret;
+
+       dev_dbg(dev, "%s%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+       return 0;
+}
+
+static struct rsnd_mod_ops rsnd_scu_ops = {
+       .name   = "scu",
+       .start  = rsnd_scu_start,
+};
+
+struct rsnd_mod *rsnd_scu_mod_get(struct rsnd_priv *priv, int id)
+{
+       BUG_ON(id < 0 || id >= rsnd_scu_nr(priv));
+
+       return &((struct rsnd_scu *)(priv->scu) + id)->mod;
+}
+
+int rsnd_scu_probe(struct platform_device *pdev,
+                  struct rcar_snd_info *info,
+                  struct rsnd_priv *priv)
+{
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_scu *scu;
+       int i, nr;
+
+       /*
+        * init SCU
+        */
+       nr      = info->scu_info_nr;
+       scu     = devm_kzalloc(dev, sizeof(*scu) * nr, GFP_KERNEL);
+       if (!scu) {
+               dev_err(dev, "SCU allocate failed\n");
+               return -ENOMEM;
+       }
+
+       priv->scu_nr    = nr;
+       priv->scu       = scu;
+
+       for_each_rsnd_scu(scu, priv, i) {
+               rsnd_mod_init(priv, &scu->mod,
+                             &rsnd_scu_ops, i);
+               scu->info = &info->scu_info[i];
+
+               dev_dbg(dev, "SCU%d probed\n", i);
+       }
+       dev_dbg(dev, "scu probed\n");
+
+       return 0;
+}
+
+void rsnd_scu_remove(struct platform_device *pdev,
+                    struct rsnd_priv *priv)
+{
+}
diff --git a/sound/soc/sh/rcar/ssi.c b/sound/soc/sh/rcar/ssi.c
new file mode 100644 (file)
index 0000000..fae26d3
--- /dev/null
@@ -0,0 +1,728 @@
+/*
+ * Renesas R-Car SSIU/SSI support
+ *
+ * Copyright (C) 2013 Renesas Solutions Corp.
+ * Kuninori Morimoto <kuninori.morimoto.gx@renesas.com>
+ *
+ * Based on fsi.c
+ * Kuninori Morimoto <morimoto.kuninori@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.
+ */
+#include <linux/delay.h>
+#include "rsnd.h"
+#define RSND_SSI_NAME_SIZE 16
+
+/*
+ * SSICR
+ */
+#define        FORCE           (1 << 31)       /* Fixed */
+#define        DMEN            (1 << 28)       /* DMA Enable */
+#define        UIEN            (1 << 27)       /* Underflow Interrupt Enable */
+#define        OIEN            (1 << 26)       /* Overflow Interrupt Enable */
+#define        IIEN            (1 << 25)       /* Idle Mode Interrupt Enable */
+#define        DIEN            (1 << 24)       /* Data Interrupt Enable */
+
+#define        DWL_8           (0 << 19)       /* Data Word Length */
+#define        DWL_16          (1 << 19)       /* Data Word Length */
+#define        DWL_18          (2 << 19)       /* Data Word Length */
+#define        DWL_20          (3 << 19)       /* Data Word Length */
+#define        DWL_22          (4 << 19)       /* Data Word Length */
+#define        DWL_24          (5 << 19)       /* Data Word Length */
+#define        DWL_32          (6 << 19)       /* Data Word Length */
+
+#define        SWL_32          (3 << 16)       /* R/W System Word Length */
+#define        SCKD            (1 << 15)       /* Serial Bit Clock Direction */
+#define        SWSD            (1 << 14)       /* Serial WS Direction */
+#define        SCKP            (1 << 13)       /* Serial Bit Clock Polarity */
+#define        SWSP            (1 << 12)       /* Serial WS Polarity */
+#define        SDTA            (1 << 10)       /* Serial Data Alignment */
+#define        DEL             (1 <<  8)       /* Serial Data Delay */
+#define        CKDV(v)         (v <<  4)       /* Serial Clock Division Ratio */
+#define        TRMD            (1 <<  1)       /* Transmit/Receive Mode Select */
+#define        EN              (1 <<  0)       /* SSI Module Enable */
+
+/*
+ * SSISR
+ */
+#define        UIRQ            (1 << 27)       /* Underflow Error Interrupt Status */
+#define        OIRQ            (1 << 26)       /* Overflow Error Interrupt Status */
+#define        IIRQ            (1 << 25)       /* Idle Mode Interrupt Status */
+#define        DIRQ            (1 << 24)       /* Data Interrupt Status Flag */
+
+/*
+ * SSIWSR
+ */
+#define CONT           (1 << 8)        /* WS Continue Function */
+
+struct rsnd_ssi {
+       struct clk *clk;
+       struct rsnd_ssi_platform_info *info; /* rcar_snd.h */
+       struct rsnd_ssi *parent;
+       struct rsnd_mod mod;
+
+       struct rsnd_dai *rdai;
+       struct rsnd_dai_stream *io;
+       u32 cr_own;
+       u32 cr_clk;
+       u32 cr_etc;
+       int err;
+       int dma_offset;
+       unsigned int usrcnt;
+       unsigned int rate;
+};
+
+struct rsnd_ssiu {
+       u32 ssi_mode0;
+       u32 ssi_mode1;
+
+       int ssi_nr;
+       struct rsnd_ssi *ssi;
+};
+
+#define for_each_rsnd_ssi(pos, priv, i)                                        \
+       for (i = 0;                                                     \
+            (i < rsnd_ssi_nr(priv)) &&                                 \
+               ((pos) = ((struct rsnd_ssiu *)((priv)->ssiu))->ssi + i); \
+            i++)
+
+#define rsnd_ssi_nr(priv) (((struct rsnd_ssiu *)((priv)->ssiu))->ssi_nr)
+#define rsnd_mod_to_ssi(_mod) container_of((_mod), struct rsnd_ssi, mod)
+#define rsnd_dma_to_ssi(dma)  rsnd_mod_to_ssi(rsnd_dma_to_mod(dma))
+#define rsnd_ssi_pio_available(ssi) ((ssi)->info->pio_irq > 0)
+#define rsnd_ssi_dma_available(ssi) \
+       rsnd_dma_available(rsnd_mod_to_dma(&(ssi)->mod))
+#define rsnd_ssi_clk_from_parent(ssi) ((ssi)->parent)
+#define rsnd_rdai_is_clk_master(rdai) ((rdai)->clk_master)
+#define rsnd_ssi_mode_flags(p) ((p)->info->flags)
+#define rsnd_ssi_dai_id(ssi) ((ssi)->info->dai_id)
+#define rsnd_ssi_to_ssiu(ssi)\
+       (((struct rsnd_ssiu *)((ssi) - rsnd_mod_id(&(ssi)->mod))) - 1)
+
+static void rsnd_ssi_mode_init(struct rsnd_priv *priv,
+                              struct rsnd_ssiu *ssiu)
+{
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_ssi *ssi;
+       u32 flags;
+       u32 val;
+       int i;
+
+       /*
+        * SSI_MODE0
+        */
+       ssiu->ssi_mode0 = 0;
+       for_each_rsnd_ssi(ssi, priv, i) {
+               flags = rsnd_ssi_mode_flags(ssi);
+
+               /* see also BUSIF_MODE */
+               if (!(flags & RSND_SSI_DEPENDENT)) {
+                       ssiu->ssi_mode0 |= (1 << i);
+                       dev_dbg(dev, "SSI%d uses INDEPENDENT mode\n", i);
+               } else {
+                       dev_dbg(dev, "SSI%d uses DEPENDENT mode\n", i);
+               }
+       }
+
+       /*
+        * SSI_MODE1
+        */
+#define ssi_parent_set(p, sync, adg, ext)              \
+       do {                                            \
+               ssi->parent = ssiu->ssi + p;            \
+               if (flags & RSND_SSI_CLK_FROM_ADG)      \
+                       val = adg;                      \
+               else                                    \
+                       val = ext;                      \
+               if (flags & RSND_SSI_SYNC)              \
+                       val |= sync;                    \
+       } while (0)
+
+       ssiu->ssi_mode1 = 0;
+       for_each_rsnd_ssi(ssi, priv, i) {
+               flags = rsnd_ssi_mode_flags(ssi);
+
+               if (!(flags & RSND_SSI_CLK_PIN_SHARE))
+                       continue;
+
+               val = 0;
+               switch (i) {
+               case 1:
+                       ssi_parent_set(0, (1 << 4), (0x2 << 0), (0x1 << 0));
+                       break;
+               case 2:
+                       ssi_parent_set(0, (1 << 4), (0x2 << 2), (0x1 << 2));
+                       break;
+               case 4:
+                       ssi_parent_set(3, (1 << 20), (0x2 << 16), (0x1 << 16));
+                       break;
+               case 8:
+                       ssi_parent_set(7, 0, 0, 0);
+                       break;
+               }
+
+               ssiu->ssi_mode1 |= val;
+       }
+}
+
+static void rsnd_ssi_mode_set(struct rsnd_ssi *ssi)
+{
+       struct rsnd_ssiu *ssiu = rsnd_ssi_to_ssiu(ssi);
+
+       rsnd_mod_write(&ssi->mod, SSI_MODE0, ssiu->ssi_mode0);
+       rsnd_mod_write(&ssi->mod, SSI_MODE1, ssiu->ssi_mode1);
+}
+
+static void rsnd_ssi_status_check(struct rsnd_mod *mod,
+                                 u32 bit)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       u32 status;
+       int i;
+
+       for (i = 0; i < 1024; i++) {
+               status = rsnd_mod_read(mod, SSISR);
+               if (status & bit)
+                       return;
+
+               udelay(50);
+       }
+
+       dev_warn(dev, "status check failed\n");
+}
+
+static int rsnd_ssi_master_clk_start(struct rsnd_ssi *ssi,
+                                    unsigned int rate)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       int i, j, ret;
+       int adg_clk_div_table[] = {
+               1, 6, /* see adg.c */
+       };
+       int ssi_clk_mul_table[] = {
+               1, 2, 4, 8, 16, 6, 12,
+       };
+       unsigned int main_rate;
+
+       /*
+        * 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_start()
+                        */
+                       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->rate       = rate;
+                               ssi->cr_clk     = FORCE | SWL_32 |
+                                                 SCKD | SWSD | CKDV(j);
+
+                               dev_dbg(dev, "ssi%d outputs %u Hz\n",
+                                       rsnd_mod_id(&ssi->mod), rate);
+
+                               return 0;
+                       }
+               }
+       }
+
+       dev_err(dev, "unsupported clock rate\n");
+       return -EIO;
+}
+
+static void rsnd_ssi_master_clk_stop(struct rsnd_ssi *ssi)
+{
+       ssi->rate = 0;
+       ssi->cr_clk = 0;
+       rsnd_adg_ssi_clk_stop(&ssi->mod);
+}
+
+static void rsnd_ssi_hw_start(struct rsnd_ssi *ssi,
+                             struct rsnd_dai *rdai,
+                             struct rsnd_dai_stream *io)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       u32 cr;
+
+       if (0 == ssi->usrcnt) {
+               clk_enable(ssi->clk);
+
+               if (rsnd_rdai_is_clk_master(rdai)) {
+                       struct snd_pcm_runtime *runtime;
+
+                       runtime = rsnd_io_to_runtime(io);
+
+                       if (rsnd_ssi_clk_from_parent(ssi))
+                               rsnd_ssi_hw_start(ssi->parent, rdai, io);
+                       else
+                               rsnd_ssi_master_clk_start(ssi, runtime->rate);
+               }
+       }
+
+       cr  =   ssi->cr_own     |
+               ssi->cr_clk     |
+               ssi->cr_etc     |
+               EN;
+
+       rsnd_mod_write(&ssi->mod, SSICR, cr);
+
+       ssi->usrcnt++;
+
+       dev_dbg(dev, "ssi%d hw started\n", rsnd_mod_id(&ssi->mod));
+}
+
+static void rsnd_ssi_hw_stop(struct rsnd_ssi *ssi,
+                            struct rsnd_dai *rdai)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(&ssi->mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       u32 cr;
+
+       if (0 == ssi->usrcnt) /* stop might be called without start */
+               return;
+
+       ssi->usrcnt--;
+
+       if (0 == ssi->usrcnt) {
+               /*
+                * disable all IRQ,
+                * and, wait all data was sent
+                */
+               cr  =   ssi->cr_own     |
+                       ssi->cr_clk;
+
+               rsnd_mod_write(&ssi->mod, SSICR, cr | EN);
+               rsnd_ssi_status_check(&ssi->mod, DIRQ);
+
+               /*
+                * disable SSI,
+                * and, wait idle state
+                */
+               rsnd_mod_write(&ssi->mod, SSICR, cr);   /* disabled all */
+               rsnd_ssi_status_check(&ssi->mod, IIRQ);
+
+               if (rsnd_rdai_is_clk_master(rdai)) {
+                       if (rsnd_ssi_clk_from_parent(ssi))
+                               rsnd_ssi_hw_stop(ssi->parent, rdai);
+                       else
+                               rsnd_ssi_master_clk_stop(ssi);
+               }
+
+               clk_disable(ssi->clk);
+       }
+
+       dev_dbg(dev, "ssi%d hw stopped\n", rsnd_mod_id(&ssi->mod));
+}
+
+/*
+ *     SSI mod common functions
+ */
+static int rsnd_ssi_init(struct rsnd_mod *mod,
+                        struct rsnd_dai *rdai,
+                        struct rsnd_dai_stream *io)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+       u32 cr;
+
+       cr = FORCE;
+
+       /*
+        * always use 32bit system word for easy clock calculation.
+        * see also rsnd_ssi_master_clk_enable()
+        */
+       cr |= SWL_32;
+
+       /*
+        * init clock settings for SSICR
+        */
+       switch (runtime->sample_bits) {
+       case 16:
+               cr |= DWL_16;
+               break;
+       case 32:
+               cr |= DWL_24;
+               break;
+       default:
+               return -EIO;
+       }
+
+       if (rdai->bit_clk_inv)
+               cr |= SCKP;
+       if (rdai->frm_clk_inv)
+               cr |= SWSP;
+       if (rdai->data_alignment)
+               cr |= SDTA;
+       if (rdai->sys_delay)
+               cr |= DEL;
+       if (rsnd_dai_is_play(rdai, io))
+               cr |= TRMD;
+
+       /*
+        * set ssi parameter
+        */
+       ssi->rdai       = rdai;
+       ssi->io         = io;
+       ssi->cr_own     = cr;
+       ssi->err        = -1; /* ignore 1st error */
+
+       rsnd_ssi_mode_set(ssi);
+
+       dev_dbg(dev, "%s.%d init\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+       return 0;
+}
+
+static int rsnd_ssi_quit(struct rsnd_mod *mod,
+                        struct rsnd_dai *rdai,
+                        struct rsnd_dai_stream *io)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+
+       dev_dbg(dev, "%s.%d quit\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+       if (ssi->err > 0)
+               dev_warn(dev, "ssi under/over flow err = %d\n", ssi->err);
+
+       ssi->rdai       = NULL;
+       ssi->io         = NULL;
+       ssi->cr_own     = 0;
+       ssi->err        = 0;
+
+       return 0;
+}
+
+static void rsnd_ssi_record_error(struct rsnd_ssi *ssi, u32 status)
+{
+       /* under/over flow error */
+       if (status & (UIRQ | OIRQ)) {
+               ssi->err++;
+
+               /* clear error status */
+               rsnd_mod_write(&ssi->mod, SSISR, 0);
+       }
+}
+
+/*
+ *             SSI PIO
+ */
+static irqreturn_t rsnd_ssi_pio_interrupt(int irq, void *data)
+{
+       struct rsnd_ssi *ssi = data;
+       struct rsnd_dai_stream *io = ssi->io;
+       u32 status = rsnd_mod_read(&ssi->mod, SSISR);
+       irqreturn_t ret = IRQ_NONE;
+
+       if (io && (status & DIRQ)) {
+               struct rsnd_dai *rdai = ssi->rdai;
+               struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+               u32 *buf = (u32 *)(runtime->dma_area +
+                                  rsnd_dai_pointer_offset(io, 0));
+
+               rsnd_ssi_record_error(ssi, status);
+
+               /*
+                * 8/16/32 data can be assesse to TDR/RDR register
+                * directly as 32bit data
+                * see rsnd_ssi_init()
+                */
+               if (rsnd_dai_is_play(rdai, io))
+                       rsnd_mod_write(&ssi->mod, SSITDR, *buf);
+               else
+                       *buf = rsnd_mod_read(&ssi->mod, SSIRDR);
+
+               rsnd_dai_pointer_update(io, sizeof(*buf));
+
+               ret = IRQ_HANDLED;
+       }
+
+       return ret;
+}
+
+static int rsnd_ssi_pio_start(struct rsnd_mod *mod,
+                             struct rsnd_dai *rdai,
+                             struct rsnd_dai_stream *io)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+
+       /* enable PIO IRQ */
+       ssi->cr_etc = UIEN | OIEN | DIEN;
+
+       rsnd_ssi_hw_start(ssi, rdai, io);
+
+       dev_dbg(dev, "%s.%d start\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+       return 0;
+}
+
+static int rsnd_ssi_pio_stop(struct rsnd_mod *mod,
+                            struct rsnd_dai *rdai,
+                            struct rsnd_dai_stream *io)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+
+       dev_dbg(dev, "%s.%d stop\n", rsnd_mod_name(mod), rsnd_mod_id(mod));
+
+       ssi->cr_etc = 0;
+
+       rsnd_ssi_hw_stop(ssi, rdai);
+
+       return 0;
+}
+
+static struct rsnd_mod_ops rsnd_ssi_pio_ops = {
+       .name   = "ssi (pio)",
+       .init   = rsnd_ssi_init,
+       .quit   = rsnd_ssi_quit,
+       .start  = rsnd_ssi_pio_start,
+       .stop   = rsnd_ssi_pio_stop,
+};
+
+static int rsnd_ssi_dma_inquiry(struct rsnd_dma *dma, dma_addr_t *buf, int *len)
+{
+       struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma);
+       struct rsnd_dai_stream *io = ssi->io;
+       struct snd_pcm_runtime *runtime = rsnd_io_to_runtime(io);
+
+       *len = io->byte_per_period;
+       *buf = runtime->dma_addr +
+               rsnd_dai_pointer_offset(io, ssi->dma_offset + *len);
+       ssi->dma_offset = *len; /* it cares A/B plane */
+
+       return 0;
+}
+
+static int rsnd_ssi_dma_complete(struct rsnd_dma *dma)
+{
+       struct rsnd_ssi *ssi = rsnd_dma_to_ssi(dma);
+       struct rsnd_dai_stream *io = ssi->io;
+       u32 status = rsnd_mod_read(&ssi->mod, SSISR);
+
+       rsnd_ssi_record_error(ssi, status);
+
+       rsnd_dai_pointer_update(ssi->io, io->byte_per_period);
+
+       return 0;
+}
+
+static int rsnd_ssi_dma_start(struct rsnd_mod *mod,
+                             struct rsnd_dai *rdai,
+                             struct rsnd_dai_stream *io)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
+
+       /* enable DMA transfer */
+       ssi->cr_etc = DMEN;
+       ssi->dma_offset = 0;
+
+       rsnd_dma_start(dma);
+
+       rsnd_ssi_hw_start(ssi, ssi->rdai, io);
+
+       /* enable WS continue */
+       if (rsnd_rdai_is_clk_master(rdai))
+               rsnd_mod_write(&ssi->mod, SSIWSR, CONT);
+
+       return 0;
+}
+
+static int rsnd_ssi_dma_stop(struct rsnd_mod *mod,
+                            struct rsnd_dai *rdai,
+                            struct rsnd_dai_stream *io)
+{
+       struct rsnd_ssi *ssi = rsnd_mod_to_ssi(mod);
+       struct rsnd_dma *dma = rsnd_mod_to_dma(&ssi->mod);
+
+       ssi->cr_etc = 0;
+
+       rsnd_ssi_hw_stop(ssi, rdai);
+
+       rsnd_dma_stop(dma);
+
+       return 0;
+}
+
+static struct rsnd_mod_ops rsnd_ssi_dma_ops = {
+       .name   = "ssi (dma)",
+       .init   = rsnd_ssi_init,
+       .quit   = rsnd_ssi_quit,
+       .start  = rsnd_ssi_dma_start,
+       .stop   = rsnd_ssi_dma_stop,
+};
+
+/*
+ *             Non SSI
+ */
+static int rsnd_ssi_non(struct rsnd_mod *mod,
+                       struct rsnd_dai *rdai,
+                       struct rsnd_dai_stream *io)
+{
+       struct rsnd_priv *priv = rsnd_mod_to_priv(mod);
+       struct device *dev = rsnd_priv_to_dev(priv);
+
+       dev_dbg(dev, "%s\n", __func__);
+
+       return 0;
+}
+
+static struct rsnd_mod_ops rsnd_ssi_non_ops = {
+       .name   = "ssi (non)",
+       .init   = rsnd_ssi_non,
+       .quit   = rsnd_ssi_non,
+       .start  = rsnd_ssi_non,
+       .stop   = rsnd_ssi_non,
+};
+
+/*
+ *             ssi mod function
+ */
+struct rsnd_mod *rsnd_ssi_mod_get_frm_dai(struct rsnd_priv *priv,
+                                         int dai_id, int is_play)
+{
+       struct rsnd_ssi *ssi;
+       int i, has_play;
+
+       is_play = !!is_play;
+
+       for_each_rsnd_ssi(ssi, priv, i) {
+               if (rsnd_ssi_dai_id(ssi) != dai_id)
+                       continue;
+
+               has_play = !!(rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY);
+
+               if (is_play == has_play)
+                       return &ssi->mod;
+       }
+
+       return NULL;
+}
+
+struct rsnd_mod *rsnd_ssi_mod_get(struct rsnd_priv *priv, int id)
+{
+       BUG_ON(id < 0 || id >= rsnd_ssi_nr(priv));
+
+       return &(((struct rsnd_ssiu *)(priv->ssiu))->ssi + id)->mod;
+}
+
+int rsnd_ssi_probe(struct platform_device *pdev,
+                  struct rcar_snd_info *info,
+                  struct rsnd_priv *priv)
+{
+       struct rsnd_ssi_platform_info *pinfo;
+       struct device *dev = rsnd_priv_to_dev(priv);
+       struct rsnd_mod_ops *ops;
+       struct clk *clk;
+       struct rsnd_ssiu *ssiu;
+       struct rsnd_ssi *ssi;
+       char name[RSND_SSI_NAME_SIZE];
+       int i, nr, ret;
+
+       /*
+        *      init SSI
+        */
+       nr      = info->ssi_info_nr;
+       ssiu    = devm_kzalloc(dev, sizeof(*ssiu) + (sizeof(*ssi) * nr),
+                              GFP_KERNEL);
+       if (!ssiu) {
+               dev_err(dev, "SSI allocate failed\n");
+               return -ENOMEM;
+       }
+
+       priv->ssiu      = ssiu;
+       ssiu->ssi       = (struct rsnd_ssi *)(ssiu + 1);
+       ssiu->ssi_nr    = nr;
+
+       for_each_rsnd_ssi(ssi, priv, i) {
+               pinfo = &info->ssi_info[i];
+
+               snprintf(name, RSND_SSI_NAME_SIZE, "ssi.%d", i);
+
+               clk = clk_get(dev, name);
+               if (IS_ERR(clk))
+                       return PTR_ERR(clk);
+
+               ssi->info       = pinfo;
+               ssi->clk        = clk;
+
+               ops = &rsnd_ssi_non_ops;
+
+               /*
+                * SSI DMA case
+                */
+               if (pinfo->dma_id > 0) {
+                       ret = rsnd_dma_init(
+                               priv, rsnd_mod_to_dma(&ssi->mod),
+                               (rsnd_ssi_mode_flags(ssi) & RSND_SSI_PLAY),
+                               pinfo->dma_id,
+                               rsnd_ssi_dma_inquiry,
+                               rsnd_ssi_dma_complete);
+                       if (ret < 0)
+                               dev_info(dev, "SSI DMA failed. try PIO transter\n");
+                       else
+                               ops     = &rsnd_ssi_dma_ops;
+
+                       dev_dbg(dev, "SSI%d use DMA transfer\n", i);
+               }
+
+               /*
+                * SSI PIO case
+                */
+               if (!rsnd_ssi_dma_available(ssi) &&
+                    rsnd_ssi_pio_available(ssi)) {
+                       ret = devm_request_irq(dev, pinfo->pio_irq,
+                                              &rsnd_ssi_pio_interrupt,
+                                              IRQF_SHARED,
+                                              dev_name(dev), ssi);
+                       if (ret) {
+                               dev_err(dev, "SSI request interrupt failed\n");
+                               return ret;
+                       }
+
+                       ops     = &rsnd_ssi_pio_ops;
+
+                       dev_dbg(dev, "SSI%d use PIO transfer\n", i);
+               }
+
+               rsnd_mod_init(priv, &ssi->mod, ops, i);
+       }
+
+       rsnd_ssi_mode_init(priv, ssiu);
+
+       dev_dbg(dev, "ssi probed\n");
+
+       return 0;
+}
+
+void rsnd_ssi_remove(struct platform_device *pdev,
+                  struct rsnd_priv *priv)
+{
+       struct rsnd_ssi *ssi;
+       int i;
+
+       for_each_rsnd_ssi(ssi, priv, i) {
+               clk_put(ssi->clk);
+               if (rsnd_ssi_dma_available(ssi))
+                       rsnd_dma_quit(priv, rsnd_mod_to_dma(&ssi->mod));
+       }
+
+}
index 06a8000aa07bedd1c47beb401d26e9052512fc54..53c9ecdd119f14fa7a33832d11a88746e257307a 100644 (file)
@@ -149,8 +149,9 @@ static int soc_compr_free(struct snd_compr_stream *cstream)
                                        SND_SOC_DAPM_STREAM_STOP);
                } else {
                        rtd->pop_wait = 1;
-                       schedule_delayed_work(&rtd->delayed_work,
-                               msecs_to_jiffies(rtd->pmdown_time));
+                       queue_delayed_work(system_power_efficient_wq,
+                                          &rtd->delayed_work,
+                                          msecs_to_jiffies(rtd->pmdown_time));
                }
        } else {
                /* capture streams can be powered down now */
@@ -334,7 +335,7 @@ static int soc_compr_copy(struct snd_compr_stream *cstream,
        return ret;
 }
 
-static int sst_compr_set_metadata(struct snd_compr_stream *cstream,
+static int soc_compr_set_metadata(struct snd_compr_stream *cstream,
                                struct snd_compr_metadata *metadata)
 {
        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
@@ -347,7 +348,7 @@ static int sst_compr_set_metadata(struct snd_compr_stream *cstream,
        return ret;
 }
 
-static int sst_compr_get_metadata(struct snd_compr_stream *cstream,
+static int soc_compr_get_metadata(struct snd_compr_stream *cstream,
                                struct snd_compr_metadata *metadata)
 {
        struct snd_soc_pcm_runtime *rtd = cstream->private_data;
@@ -364,8 +365,8 @@ static struct snd_compr_ops soc_compr_ops = {
        .open           = soc_compr_open,
        .free           = soc_compr_free,
        .set_params     = soc_compr_set_params,
-       .set_metadata   = sst_compr_set_metadata,
-       .get_metadata   = sst_compr_get_metadata,
+       .set_metadata   = soc_compr_set_metadata,
+       .get_metadata   = soc_compr_get_metadata,
        .get_params     = soc_compr_get_params,
        .trigger        = soc_compr_trigger,
        .pointer        = soc_compr_pointer,
index d82ee386eab564d352eccd4209dff767b222e6f3..4d0561312f3be0a9f383928b3d74f80a44cdd3b3 100644 (file)
 #include <linux/bitops.h>
 #include <linux/debugfs.h>
 #include <linux/platform_device.h>
+#include <linux/pinctrl/consumer.h>
 #include <linux/ctype.h>
 #include <linux/slab.h>
 #include <linux/of.h>
+#include <linux/gpio.h>
+#include <linux/of_gpio.h>
 #include <sound/ac97_codec.h>
 #include <sound/core.h>
 #include <sound/jack.h>
@@ -47,8 +50,6 @@
 
 #define NAME_SIZE      32
 
-static DECLARE_WAIT_QUEUE_HEAD(soc_pm_waitq);
-
 #ifdef CONFIG_DEBUG_FS
 struct dentry *snd_soc_debugfs_root;
 EXPORT_SYMBOL_GPL(snd_soc_debugfs_root);
@@ -69,6 +70,16 @@ static int pmdown_time = 5000;
 module_param(pmdown_time, int, 0);
 MODULE_PARM_DESC(pmdown_time, "DAPM stream powerdown time (msecs)");
 
+struct snd_ac97_reset_cfg {
+       struct pinctrl *pctl;
+       struct pinctrl_state *pstate_reset;
+       struct pinctrl_state *pstate_warm_reset;
+       struct pinctrl_state *pstate_run;
+       int gpio_sdata;
+       int gpio_sync;
+       int gpio_reset;
+};
+
 /* returns the minimum number of bytes needed to represent
  * a particular given value */
 static int min_bytes_needed(unsigned long val)
@@ -192,7 +203,7 @@ static ssize_t pmdown_time_set(struct device *dev,
        struct snd_soc_pcm_runtime *rtd = dev_get_drvdata(dev);
        int ret;
 
-       ret = strict_strtol(buf, 10, &rtd->pmdown_time);
+       ret = kstrtol(buf, 10, &rtd->pmdown_time);
        if (ret)
                return ret;
 
@@ -237,6 +248,7 @@ static ssize_t codec_reg_write_file(struct file *file,
        char *start = buf;
        unsigned long reg, value;
        struct snd_soc_codec *codec = file->private_data;
+       int ret;
 
        buf_size = min(count, (sizeof(buf)-1));
        if (copy_from_user(buf, user_buf, buf_size))
@@ -248,8 +260,9 @@ static ssize_t codec_reg_write_file(struct file *file,
        reg = simple_strtoul(start, &start, 16);
        while (*start == ' ')
                start++;
-       if (strict_strtoul(start, 16, &value))
-               return -EINVAL;
+       ret = kstrtoul(start, 16, &value);
+       if (ret)
+               return ret;
 
        /* Userspace has been fiddling around behind the kernel's back */
        add_taint(TAINT_USER, LOCKDEP_NOW_UNRELIABLE);
@@ -530,6 +543,15 @@ static int soc_ac97_dev_register(struct snd_soc_codec *codec)
 }
 #endif
 
+static void codec2codec_close_delayed_work(struct work_struct *work)
+{
+       /* Currently nothing to do for c2c links
+        * Since c2c links are internal nodes in the DAPM graph and
+        * don't interface with the outside world or application layer
+        * we don't have to do any special handling on close.
+        */
+}
+
 #ifdef CONFIG_PM_SLEEP
 /* powers down audio subsystem for suspend */
 int snd_soc_suspend(struct device *dev)
@@ -1223,9 +1245,6 @@ static int soc_post_component_init(struct snd_soc_card *card,
        }
        rtd->card = card;
 
-       /* Make sure all DAPM widgets are instantiated */
-       snd_soc_dapm_new_widgets(&codec->dapm);
-
        /* machine controls, routes and widgets are not prefixed */
        temp = codec->name_prefix;
        codec->name_prefix = NULL;
@@ -1428,6 +1447,9 @@ static int soc_probe_link_dais(struct snd_soc_card *card, int num, int order)
                                return ret;
                        }
                } else {
+                       INIT_DELAYED_WORK(&rtd->delayed_work,
+                                               codec2codec_close_delayed_work);
+
                        /* link the DAI widgets */
                        play_w = codec_dai->playback_widget;
                        capture_w = cpu_dai->capture_widget;
@@ -1718,8 +1740,6 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                snd_soc_dapm_add_routes(&card->dapm, card->dapm_routes,
                                        card->num_dapm_routes);
 
-       snd_soc_dapm_new_widgets(&card->dapm);
-
        for (i = 0; i < card->num_links; i++) {
                dai_link = &card->dai_link[i];
                dai_fmt = dai_link->dai_fmt;
@@ -1798,12 +1818,12 @@ static int snd_soc_instantiate_card(struct snd_soc_card *card)
                }
        }
 
-       snd_soc_dapm_new_widgets(&card->dapm);
-
        if (card->fully_routed)
                list_for_each_entry(codec, &card->codec_dev_list, card_list)
                        snd_soc_dapm_auto_nc_codec_pins(codec);
 
+       snd_soc_dapm_new_widgets(card);
+
        ret = snd_card_register(card->snd_card);
        if (ret < 0) {
                dev_err(card->dev, "ASoC: failed to register soundcard %d\n",
@@ -2080,6 +2100,117 @@ int snd_soc_new_ac97_codec(struct snd_soc_codec *codec,
 }
 EXPORT_SYMBOL_GPL(snd_soc_new_ac97_codec);
 
+static struct snd_ac97_reset_cfg snd_ac97_rst_cfg;
+
+static void snd_soc_ac97_warm_reset(struct snd_ac97 *ac97)
+{
+       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_warm_reset);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 1);
+
+       udelay(10);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+       msleep(2);
+}
+
+static void snd_soc_ac97_reset(struct snd_ac97 *ac97)
+{
+       struct pinctrl *pctl = snd_ac97_rst_cfg.pctl;
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_reset);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sync, 0);
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_sdata, 0);
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 0);
+
+       udelay(10);
+
+       gpio_direction_output(snd_ac97_rst_cfg.gpio_reset, 1);
+
+       pinctrl_select_state(pctl, snd_ac97_rst_cfg.pstate_run);
+       msleep(2);
+}
+
+static int snd_soc_ac97_parse_pinctl(struct device *dev,
+               struct snd_ac97_reset_cfg *cfg)
+{
+       struct pinctrl *p;
+       struct pinctrl_state *state;
+       int gpio;
+       int ret;
+
+       p = devm_pinctrl_get(dev);
+       if (IS_ERR(p)) {
+               dev_err(dev, "Failed to get pinctrl\n");
+               return PTR_RET(p);
+       }
+       cfg->pctl = p;
+
+       state = pinctrl_lookup_state(p, "ac97-reset");
+       if (IS_ERR(state)) {
+               dev_err(dev, "Can't find pinctrl state ac97-reset\n");
+               return PTR_RET(state);
+       }
+       cfg->pstate_reset = state;
+
+       state = pinctrl_lookup_state(p, "ac97-warm-reset");
+       if (IS_ERR(state)) {
+               dev_err(dev, "Can't find pinctrl state ac97-warm-reset\n");
+               return PTR_RET(state);
+       }
+       cfg->pstate_warm_reset = state;
+
+       state = pinctrl_lookup_state(p, "ac97-running");
+       if (IS_ERR(state)) {
+               dev_err(dev, "Can't find pinctrl state ac97-running\n");
+               return PTR_RET(state);
+       }
+       cfg->pstate_run = state;
+
+       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 0);
+       if (gpio < 0) {
+               dev_err(dev, "Can't find ac97-sync gpio\n");
+               return gpio;
+       }
+       ret = devm_gpio_request(dev, gpio, "AC97 link sync");
+       if (ret) {
+               dev_err(dev, "Failed requesting ac97-sync gpio\n");
+               return ret;
+       }
+       cfg->gpio_sync = gpio;
+
+       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 1);
+       if (gpio < 0) {
+               dev_err(dev, "Can't find ac97-sdata gpio %d\n", gpio);
+               return gpio;
+       }
+       ret = devm_gpio_request(dev, gpio, "AC97 link sdata");
+       if (ret) {
+               dev_err(dev, "Failed requesting ac97-sdata gpio\n");
+               return ret;
+       }
+       cfg->gpio_sdata = gpio;
+
+       gpio = of_get_named_gpio(dev->of_node, "ac97-gpios", 2);
+       if (gpio < 0) {
+               dev_err(dev, "Can't find ac97-reset gpio\n");
+               return gpio;
+       }
+       ret = devm_gpio_request(dev, gpio, "AC97 link reset");
+       if (ret) {
+               dev_err(dev, "Failed requesting ac97-reset gpio\n");
+               return ret;
+       }
+       cfg->gpio_reset = gpio;
+
+       return 0;
+}
+
 struct snd_ac97_bus_ops *soc_ac97_ops;
 EXPORT_SYMBOL_GPL(soc_ac97_ops);
 
@@ -2097,6 +2228,35 @@ int snd_soc_set_ac97_ops(struct snd_ac97_bus_ops *ops)
 }
 EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops);
 
+/**
+ * snd_soc_set_ac97_ops_of_reset - Set ac97 ops with generic ac97 reset functions
+ *
+ * This function sets the reset and warm_reset properties of ops and parses
+ * the device node of pdev to get pinctrl states and gpio numbers to use.
+ */
+int snd_soc_set_ac97_ops_of_reset(struct snd_ac97_bus_ops *ops,
+               struct platform_device *pdev)
+{
+       struct device *dev = &pdev->dev;
+       struct snd_ac97_reset_cfg cfg;
+       int ret;
+
+       ret = snd_soc_ac97_parse_pinctl(dev, &cfg);
+       if (ret)
+               return ret;
+
+       ret = snd_soc_set_ac97_ops(ops);
+       if (ret)
+               return ret;
+
+       ops->warm_reset = snd_soc_ac97_warm_reset;
+       ops->reset = snd_soc_ac97_reset;
+
+       snd_ac97_rst_cfg = cfg;
+       return 0;
+}
+EXPORT_SYMBOL_GPL(snd_soc_set_ac97_ops_of_reset);
+
 /**
  * snd_soc_free_ac97_codec - free AC97 codec device
  * @codec: audio codec
@@ -2299,6 +2459,22 @@ static int snd_soc_add_controls(struct snd_card *card, struct device *dev,
        return 0;
 }
 
+struct snd_kcontrol *snd_soc_card_get_kcontrol(struct snd_soc_card *soc_card,
+                                              const char *name)
+{
+       struct snd_card *card = soc_card->snd_card;
+       struct snd_kcontrol *kctl;
+
+       if (unlikely(!name))
+               return NULL;
+
+       list_for_each_entry(kctl, &card->controls, list)
+               if (!strncmp(kctl->id.name, name, sizeof(kctl->id.name)))
+                       return kctl;
+       return NULL;
+}
+EXPORT_SYMBOL_GPL(snd_soc_card_get_kcontrol);
+
 /**
  * snd_soc_add_codec_controls - add an array of controls to a codec.
  * Convenience function to add a list of controls. Many codecs were
@@ -2540,59 +2716,6 @@ int snd_soc_put_value_enum_double(struct snd_kcontrol *kcontrol,
 }
 EXPORT_SYMBOL_GPL(snd_soc_put_value_enum_double);
 
-/**
- * snd_soc_info_enum_ext - external enumerated single mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about an external enumerated
- * single mixer.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_enum_ext(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
-{
-       struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
-
-       uinfo->type = SNDRV_CTL_ELEM_TYPE_ENUMERATED;
-       uinfo->count = 1;
-       uinfo->value.enumerated.items = e->max;
-
-       if (uinfo->value.enumerated.item > e->max - 1)
-               uinfo->value.enumerated.item = e->max - 1;
-       strcpy(uinfo->value.enumerated.name,
-               e->texts[uinfo->value.enumerated.item]);
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_enum_ext);
-
-/**
- * snd_soc_info_volsw_ext - external single mixer info callback
- * @kcontrol: mixer control
- * @uinfo: control element information
- *
- * Callback to provide information about a single external mixer control.
- *
- * Returns 0 for success.
- */
-int snd_soc_info_volsw_ext(struct snd_kcontrol *kcontrol,
-       struct snd_ctl_elem_info *uinfo)
-{
-       int max = kcontrol->private_value;
-
-       if (max == 1 && !strstr(kcontrol->id.name, " Volume"))
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_BOOLEAN;
-       else
-               uinfo->type = SNDRV_CTL_ELEM_TYPE_INTEGER;
-
-       uinfo->count = 1;
-       uinfo->value.integer.min = 0;
-       uinfo->value.integer.max = max;
-       return 0;
-}
-EXPORT_SYMBOL_GPL(snd_soc_info_volsw_ext);
-
 /**
  * snd_soc_info_volsw - single mixer info callback
  * @kcontrol: mixer control
index 4375c9f2b791006717f807cdddcb343bb0f8074c..548b1c9e875e34be3664e3fa578f44e7f0d69102 100644 (file)
 
 #define DAPM_UPDATE_STAT(widget, val) widget->dapm->card->dapm_stats.val++;
 
+static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
+       struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
+       const char *control,
+       int (*connected)(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink));
+static struct snd_soc_dapm_widget *
+snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
+                        const struct snd_soc_dapm_widget *widget);
+
 /* dapm power sequences - make this per codec in the future */
 static int dapm_up_seq[] = {
        [snd_soc_dapm_pre] = 0,
@@ -73,16 +82,18 @@ static int dapm_up_seq[] = {
        [snd_soc_dapm_hp] = 10,
        [snd_soc_dapm_spk] = 10,
        [snd_soc_dapm_line] = 10,
-       [snd_soc_dapm_post] = 11,
+       [snd_soc_dapm_kcontrol] = 11,
+       [snd_soc_dapm_post] = 12,
 };
 
 static int dapm_down_seq[] = {
        [snd_soc_dapm_pre] = 0,
-       [snd_soc_dapm_adc] = 1,
-       [snd_soc_dapm_hp] = 2,
-       [snd_soc_dapm_spk] = 2,
-       [snd_soc_dapm_line] = 2,
-       [snd_soc_dapm_out_drv] = 2,
+       [snd_soc_dapm_kcontrol] = 1,
+       [snd_soc_dapm_adc] = 2,
+       [snd_soc_dapm_hp] = 3,
+       [snd_soc_dapm_spk] = 3,
+       [snd_soc_dapm_line] = 3,
+       [snd_soc_dapm_out_drv] = 3,
        [snd_soc_dapm_pga] = 4,
        [snd_soc_dapm_switch] = 5,
        [snd_soc_dapm_mixer_named_ctl] = 5,
@@ -174,36 +185,176 @@ static inline struct snd_soc_dapm_widget *dapm_cnew_widget(
        return kmemdup(_widget, sizeof(*_widget), GFP_KERNEL);
 }
 
-/* get snd_card from DAPM context */
-static inline struct snd_card *dapm_get_snd_card(
-       struct snd_soc_dapm_context *dapm)
+struct dapm_kcontrol_data {
+       unsigned int value;
+       struct snd_soc_dapm_widget *widget;
+       struct list_head paths;
+       struct snd_soc_dapm_widget_list *wlist;
+};
+
+static int dapm_kcontrol_data_alloc(struct snd_soc_dapm_widget *widget,
+       struct snd_kcontrol *kcontrol)
 {
-       if (dapm->codec)
-               return dapm->codec->card->snd_card;
-       else if (dapm->platform)
-               return dapm->platform->card->snd_card;
-       else
-               BUG();
+       struct dapm_kcontrol_data *data;
+       struct soc_mixer_control *mc;
 
-       /* unreachable */
-       return NULL;
+       data = kzalloc(sizeof(*data), GFP_KERNEL);
+       if (!data) {
+               dev_err(widget->dapm->dev,
+                               "ASoC: can't allocate kcontrol data for %s\n",
+                               widget->name);
+               return -ENOMEM;
+       }
+
+       INIT_LIST_HEAD(&data->paths);
+
+       switch (widget->id) {
+       case snd_soc_dapm_switch:
+       case snd_soc_dapm_mixer:
+       case snd_soc_dapm_mixer_named_ctl:
+               mc = (struct soc_mixer_control *)kcontrol->private_value;
+
+               if (mc->autodisable) {
+                       struct snd_soc_dapm_widget template;
+
+                       memset(&template, 0, sizeof(template));
+                       template.reg = mc->reg;
+                       template.mask = (1 << fls(mc->max)) - 1;
+                       template.shift = mc->shift;
+                       if (mc->invert)
+                               template.off_val = mc->max;
+                       else
+                               template.off_val = 0;
+                       template.on_val = template.off_val;
+                       template.id = snd_soc_dapm_kcontrol;
+                       template.name = kcontrol->id.name;
+
+                       data->widget = snd_soc_dapm_new_control(widget->dapm,
+                               &template);
+                       if (!data->widget) {
+                               kfree(data);
+                               return -ENOMEM;
+                       }
+               }
+               break;
+       default:
+               break;
+       }
+
+       kcontrol->private_data = data;
+
+       return 0;
 }
 
-/* get soc_card from DAPM context */
-static inline struct snd_soc_card *dapm_get_soc_card(
-               struct snd_soc_dapm_context *dapm)
+static void dapm_kcontrol_free(struct snd_kcontrol *kctl)
 {
-       if (dapm->codec)
-               return dapm->codec->card;
-       else if (dapm->platform)
-               return dapm->platform->card;
+       struct dapm_kcontrol_data *data = snd_kcontrol_chip(kctl);
+       kfree(data->widget);
+       kfree(data->wlist);
+       kfree(data);
+}
+
+static struct snd_soc_dapm_widget_list *dapm_kcontrol_get_wlist(
+       const struct snd_kcontrol *kcontrol)
+{
+       struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+
+       return data->wlist;
+}
+
+static int dapm_kcontrol_add_widget(struct snd_kcontrol *kcontrol,
+       struct snd_soc_dapm_widget *widget)
+{
+       struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+       struct snd_soc_dapm_widget_list *new_wlist;
+       unsigned int n;
+
+       if (data->wlist)
+               n = data->wlist->num_widgets + 1;
        else
-               BUG();
+               n = 1;
 
-       /* unreachable */
-       return NULL;
+       new_wlist = krealloc(data->wlist,
+                       sizeof(*new_wlist) + sizeof(widget) * n, GFP_KERNEL);
+       if (!new_wlist)
+               return -ENOMEM;
+
+       new_wlist->widgets[n - 1] = widget;
+       new_wlist->num_widgets = n;
+
+       data->wlist = new_wlist;
+
+       return 0;
+}
+
+static void dapm_kcontrol_add_path(const struct snd_kcontrol *kcontrol,
+       struct snd_soc_dapm_path *path)
+{
+       struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+
+       list_add_tail(&path->list_kcontrol, &data->paths);
+
+       if (data->widget) {
+               snd_soc_dapm_add_path(data->widget->dapm, data->widget,
+                   path->source, NULL, NULL);
+       }
+}
+
+static bool dapm_kcontrol_is_powered(const struct snd_kcontrol *kcontrol)
+{
+       struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+
+       if (!data->widget)
+               return true;
+
+       return data->widget->power;
+}
+
+static struct list_head *dapm_kcontrol_get_path_list(
+       const struct snd_kcontrol *kcontrol)
+{
+       struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+
+       return &data->paths;
 }
 
+#define dapm_kcontrol_for_each_path(path, kcontrol) \
+       list_for_each_entry(path, dapm_kcontrol_get_path_list(kcontrol), \
+               list_kcontrol)
+
+static unsigned int dapm_kcontrol_get_value(const struct snd_kcontrol *kcontrol)
+{
+       struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+
+       return data->value;
+}
+
+static bool dapm_kcontrol_set_value(const struct snd_kcontrol *kcontrol,
+       unsigned int value)
+{
+       struct dapm_kcontrol_data *data = snd_kcontrol_chip(kcontrol);
+
+       if (data->value == value)
+               return false;
+
+       if (data->widget)
+               data->widget->on_val = value;
+
+       data->value = value;
+
+       return true;
+}
+
+/**
+ * snd_soc_dapm_kcontrol_codec() - Returns the codec associated to a kcontrol
+ * @kcontrol: The kcontrol
+ */
+struct snd_soc_codec *snd_soc_dapm_kcontrol_codec(struct snd_kcontrol *kcontrol)
+{
+       return dapm_kcontrol_get_wlist(kcontrol)->widgets[0]->codec;
+}
+EXPORT_SYMBOL_GPL(snd_soc_dapm_kcontrol_codec);
+
 static void dapm_reset(struct snd_soc_card *card)
 {
        struct snd_soc_dapm_widget *w;
@@ -211,6 +362,7 @@ static void dapm_reset(struct snd_soc_card *card)
        memset(&card->dapm_stats, 0, sizeof(card->dapm_stats));
 
        list_for_each_entry(w, &card->widgets, list) {
+               w->new_power = w->power;
                w->power_checked = false;
                w->inputs = -1;
                w->outputs = -1;
@@ -428,6 +580,7 @@ static void dapm_set_path_status(struct snd_soc_dapm_widget *w,
        case snd_soc_dapm_spk:
        case snd_soc_dapm_line:
        case snd_soc_dapm_dai_link:
+       case snd_soc_dapm_kcontrol:
                p->connect = 1;
        break;
        /* does affect routing - dynamically connected */
@@ -507,17 +660,12 @@ static int dapm_is_shared_kcontrol(struct snd_soc_dapm_context *dapm,
        return 0;
 }
 
-static void dapm_kcontrol_free(struct snd_kcontrol *kctl)
-{
-       kfree(kctl->private_data);
-}
-
 /*
  * 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,
-       int kci, struct snd_soc_dapm_path *path)
+       int kci)
 {
        struct snd_soc_dapm_context *dapm = w->dapm;
        struct snd_card *card = dapm->card->snd_card;
@@ -525,9 +673,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
        size_t prefix_len;
        int shared;
        struct snd_kcontrol *kcontrol;
-       struct snd_soc_dapm_widget_list *wlist;
-       int wlistentries;
-       size_t wlistsize;
        bool wname_in_long_name, kcname_in_long_name;
        char *long_name;
        const char *name;
@@ -546,25 +691,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
        shared = dapm_is_shared_kcontrol(dapm, w, &w->kcontrol_news[kci],
                                         &kcontrol);
 
-       if (kcontrol) {
-               wlist = kcontrol->private_data;
-               wlistentries = wlist->num_widgets + 1;
-       } else {
-               wlist = NULL;
-               wlistentries = 1;
-       }
-
-       wlistsize = sizeof(struct snd_soc_dapm_widget_list) +
-                       wlistentries * sizeof(struct snd_soc_dapm_widget *);
-       wlist = krealloc(wlist, wlistsize, GFP_KERNEL);
-       if (wlist == NULL) {
-               dev_err(dapm->dev, "ASoC: can't allocate widget list for %s\n",
-                       w->name);
-               return -ENOMEM;
-       }
-       wlist->num_widgets = wlistentries;
-       wlist->widgets[wlistentries - 1] = w;
-
        if (!kcontrol) {
                if (shared) {
                        wname_in_long_name = false;
@@ -587,7 +713,6 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
                                kcname_in_long_name = false;
                                break;
                        default:
-                               kfree(wlist);
                                return -EINVAL;
                        }
                }
@@ -602,10 +727,8 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
                        long_name = kasprintf(GFP_KERNEL, "%s %s",
                                 w->name + prefix_len,
                                 w->kcontrol_news[kci].name);
-                       if (long_name == NULL) {
-                               kfree(wlist);
+                       if (long_name == NULL)
                                return -ENOMEM;
-                       }
 
                        name = long_name;
                } else if (wname_in_long_name) {
@@ -616,23 +739,33 @@ static int dapm_create_or_share_mixmux_kcontrol(struct snd_soc_dapm_widget *w,
                        name = w->kcontrol_news[kci].name;
                }
 
-               kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], wlist, name,
+               kcontrol = snd_soc_cnew(&w->kcontrol_news[kci], NULL, name,
                                        prefix);
-               kcontrol->private_free = dapm_kcontrol_free;
                kfree(long_name);
+               if (!kcontrol)
+                       return -ENOMEM;
+               kcontrol->private_free = dapm_kcontrol_free;
+
+               ret = dapm_kcontrol_data_alloc(w, kcontrol);
+               if (ret) {
+                       snd_ctl_free_one(kcontrol);
+                       return ret;
+               }
+
                ret = snd_ctl_add(card, kcontrol);
                if (ret < 0) {
                        dev_err(dapm->dev,
                                "ASoC: failed to add widget %s dapm kcontrol %s: %d\n",
                                w->name, name, ret);
-                       kfree(wlist);
                        return ret;
                }
        }
 
-       kcontrol->private_data = wlist;
+       ret = dapm_kcontrol_add_widget(kcontrol, w);
+       if (ret)
+               return ret;
+
        w->kcontrols[kci] = kcontrol;
-       path->kcontrol = kcontrol;
 
        return 0;
 }
@@ -652,13 +785,15 @@ static int dapm_new_mixer(struct snd_soc_dapm_widget *w)
                                continue;
 
                        if (w->kcontrols[i]) {
-                               path->kcontrol = w->kcontrols[i];
+                               dapm_kcontrol_add_path(w->kcontrols[i], path);
                                continue;
                        }
 
-                       ret = dapm_create_or_share_mixmux_kcontrol(w, i, path);
+                       ret = dapm_create_or_share_mixmux_kcontrol(w, i);
                        if (ret < 0)
                                return ret;
+
+                       dapm_kcontrol_add_path(w->kcontrols[i], path);
                }
        }
 
@@ -684,15 +819,12 @@ static int dapm_new_mux(struct snd_soc_dapm_widget *w)
                return -EINVAL;
        }
 
-       path = list_first_entry(&w->sources, struct snd_soc_dapm_path,
-                               list_sink);
-
-       ret = dapm_create_or_share_mixmux_kcontrol(w, 0, path);
+       ret = dapm_create_or_share_mixmux_kcontrol(w, 0);
        if (ret < 0)
                return ret;
 
        list_for_each_entry(path, &w->sources, list_sink)
-               path->kcontrol = w->kcontrols[0];
+               dapm_kcontrol_add_path(w->kcontrols[0], path);
 
        return 0;
 }
@@ -813,6 +945,7 @@ static int is_connected_output_ep(struct snd_soc_dapm_widget *widget,
        case snd_soc_dapm_supply:
        case snd_soc_dapm_regulator_supply:
        case snd_soc_dapm_clock_supply:
+       case snd_soc_dapm_kcontrol:
                return 0;
        default:
                break;
@@ -908,6 +1041,7 @@ static int is_connected_input_ep(struct snd_soc_dapm_widget *widget,
        case snd_soc_dapm_supply:
        case snd_soc_dapm_regulator_supply:
        case snd_soc_dapm_clock_supply:
+       case snd_soc_dapm_kcontrol:
                return 0;
        default:
                break;
@@ -1062,7 +1196,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
        int ret;
 
        if (SND_SOC_DAPM_EVENT_ON(event)) {
-               if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
+               if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {
                        ret = regulator_allow_bypass(w->regulator, false);
                        if (ret != 0)
                                dev_warn(w->dapm->dev,
@@ -1072,7 +1206,7 @@ int dapm_regulator_event(struct snd_soc_dapm_widget *w,
 
                return regulator_enable(w->regulator);
        } else {
-               if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
+               if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {
                        ret = regulator_allow_bypass(w->regulator, true);
                        if (ret != 0)
                                dev_warn(w->dapm->dev,
@@ -1244,10 +1378,9 @@ static void dapm_seq_insert(struct snd_soc_dapm_widget *new_widget,
        list_add_tail(&new_widget->power_list, list);
 }
 
-static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm,
+static void dapm_seq_check_event(struct snd_soc_card *card,
                                 struct snd_soc_dapm_widget *w, int event)
 {
-       struct snd_soc_card *card = dapm->card;
        const char *ev_name;
        int power, ret;
 
@@ -1281,55 +1414,50 @@ static void dapm_seq_check_event(struct snd_soc_dapm_context *dapm,
                return;
        }
 
-       if (w->power != power)
+       if (w->new_power != power)
                return;
 
        if (w->event && (w->event_flags & event)) {
-               pop_dbg(dapm->dev, card->pop_time, "pop test : %s %s\n",
+               pop_dbg(w->dapm->dev, card->pop_time, "pop test : %s %s\n",
                        w->name, ev_name);
                trace_snd_soc_dapm_widget_event_start(w, event);
                ret = w->event(w, NULL, event);
                trace_snd_soc_dapm_widget_event_done(w, event);
                if (ret < 0)
-                       dev_err(dapm->dev, "ASoC: %s: %s event failed: %d\n",
+                       dev_err(w->dapm->dev, "ASoC: %s: %s event failed: %d\n",
                               ev_name, w->name, ret);
        }
 }
 
 /* Apply the coalesced changes from a DAPM sequence */
-static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
+static void dapm_seq_run_coalesced(struct snd_soc_card *card,
                                   struct list_head *pending)
 {
-       struct snd_soc_card *card = dapm->card;
        struct snd_soc_dapm_widget *w;
-       int reg, power;
+       int reg;
        unsigned int value = 0;
        unsigned int mask = 0;
-       unsigned int cur_mask;
 
        reg = list_first_entry(pending, struct snd_soc_dapm_widget,
                               power_list)->reg;
 
        list_for_each_entry(w, pending, power_list) {
-               cur_mask = 1 << w->shift;
                BUG_ON(reg != w->reg);
+               w->power = w->new_power;
 
-               if (w->invert)
-                       power = !w->power;
+               mask |= w->mask << w->shift;
+               if (w->power)
+                       value |= w->on_val << w->shift;
                else
-                       power = w->power;
-
-               mask |= cur_mask;
-               if (power)
-                       value |= cur_mask;
+                       value |= w->off_val << w->shift;
 
-               pop_dbg(dapm->dev, card->pop_time,
+               pop_dbg(w->dapm->dev, card->pop_time,
                        "pop test : Queue %s: reg=0x%x, 0x%x/0x%x\n",
                        w->name, reg, value, mask);
 
                /* Check for events */
-               dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMU);
-               dapm_seq_check_event(dapm, w, SND_SOC_DAPM_PRE_PMD);
+               dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMU);
+               dapm_seq_check_event(card, w, SND_SOC_DAPM_PRE_PMD);
        }
 
        if (reg >= 0) {
@@ -1339,7 +1467,7 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
                w = list_first_entry(pending, struct snd_soc_dapm_widget,
                                     power_list);
 
-               pop_dbg(dapm->dev, card->pop_time,
+               pop_dbg(w->dapm->dev, card->pop_time,
                        "pop test : Applying 0x%x/0x%x to %x in %dms\n",
                        value, mask, reg, card->pop_time);
                pop_wait(card->pop_time);
@@ -1347,8 +1475,8 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
        }
 
        list_for_each_entry(w, pending, power_list) {
-               dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMU);
-               dapm_seq_check_event(dapm, w, SND_SOC_DAPM_POST_PMD);
+               dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMU);
+               dapm_seq_check_event(card, w, SND_SOC_DAPM_POST_PMD);
        }
 }
 
@@ -1360,8 +1488,8 @@ static void dapm_seq_run_coalesced(struct snd_soc_dapm_context *dapm,
  * Currently anything that requires more than a single write is not
  * handled.
  */
-static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
-                        struct list_head *list, int event, bool power_up)
+static void dapm_seq_run(struct snd_soc_card *card,
+       struct list_head *list, int event, bool power_up)
 {
        struct snd_soc_dapm_widget *w, *n;
        LIST_HEAD(pending);
@@ -1384,7 +1512,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
                if (sort[w->id] != cur_sort || w->reg != cur_reg ||
                    w->dapm != cur_dapm || w->subseq != cur_subseq) {
                        if (!list_empty(&pending))
-                               dapm_seq_run_coalesced(cur_dapm, &pending);
+                               dapm_seq_run_coalesced(card, &pending);
 
                        if (cur_dapm && cur_dapm->seq_notifier) {
                                for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
@@ -1444,7 +1572,7 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
        }
 
        if (!list_empty(&pending))
-               dapm_seq_run_coalesced(cur_dapm, &pending);
+               dapm_seq_run_coalesced(card, &pending);
 
        if (cur_dapm && cur_dapm->seq_notifier) {
                for (i = 0; i < ARRAY_SIZE(dapm_up_seq); i++)
@@ -1454,37 +1582,48 @@ static void dapm_seq_run(struct snd_soc_dapm_context *dapm,
        }
 }
 
-static void dapm_widget_update(struct snd_soc_dapm_context *dapm)
+static void dapm_widget_update(struct snd_soc_card *card)
 {
-       struct snd_soc_dapm_update *update = dapm->update;
-       struct snd_soc_dapm_widget *w;
+       struct snd_soc_dapm_update *update = card->update;
+       struct snd_soc_dapm_widget_list *wlist;
+       struct snd_soc_dapm_widget *w = NULL;
+       unsigned int wi;
        int ret;
 
-       if (!update)
+       if (!update || !dapm_kcontrol_is_powered(update->kcontrol))
                return;
 
-       w = update->widget;
+       wlist = dapm_kcontrol_get_wlist(update->kcontrol);
 
-       if (w->event &&
-           (w->event_flags & SND_SOC_DAPM_PRE_REG)) {
-               ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG);
-               if (ret != 0)
-                       dev_err(dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n",
-                              w->name, ret);
+       for (wi = 0; wi < wlist->num_widgets; wi++) {
+               w = wlist->widgets[wi];
+
+               if (w->event && (w->event_flags & SND_SOC_DAPM_PRE_REG)) {
+                       ret = w->event(w, update->kcontrol, SND_SOC_DAPM_PRE_REG);
+                       if (ret != 0)
+                               dev_err(w->dapm->dev, "ASoC: %s DAPM pre-event failed: %d\n",
+                                          w->name, ret);
+               }
        }
 
+       if (!w)
+               return;
+
        ret = soc_widget_update_bits_locked(w, update->reg, update->mask,
                                  update->val);
        if (ret < 0)
-               dev_err(dapm->dev, "ASoC: %s DAPM update failed: %d\n",
+               dev_err(w->dapm->dev, "ASoC: %s DAPM update failed: %d\n",
                        w->name, ret);
 
-       if (w->event &&
-           (w->event_flags & SND_SOC_DAPM_POST_REG)) {
-               ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG);
-               if (ret != 0)
-                       dev_err(dapm->dev, "ASoC: %s DAPM post-event failed: %d\n",
-                              w->name, ret);
+       for (wi = 0; wi < wlist->num_widgets; wi++) {
+               w = wlist->widgets[wi];
+
+               if (w->event && (w->event_flags & SND_SOC_DAPM_POST_REG)) {
+                       ret = w->event(w, update->kcontrol, SND_SOC_DAPM_POST_REG);
+                       if (ret != 0)
+                               dev_err(w->dapm->dev, "ASoC: %s DAPM post-event failed: %d\n",
+                                          w->name, ret);
+               }
        }
 }
 
@@ -1596,6 +1735,7 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
        case snd_soc_dapm_supply:
        case snd_soc_dapm_regulator_supply:
        case snd_soc_dapm_clock_supply:
+       case snd_soc_dapm_kcontrol:
                /* Supplies can't affect their outputs, only their inputs */
                break;
        default:
@@ -1612,8 +1752,6 @@ static void dapm_widget_set_power(struct snd_soc_dapm_widget *w, bool power,
                dapm_seq_insert(w, up_list, true);
        else
                dapm_seq_insert(w, down_list, false);
-
-       w->power = power;
 }
 
 static void dapm_power_one_widget(struct snd_soc_dapm_widget *w,
@@ -1647,9 +1785,8 @@ static void dapm_power_one_widget(struct snd_soc_dapm_widget *w,
  *  o Input pin to Output pin (bypass, sidetone)
  *  o DAC to ADC (loopback).
  */
-static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
+static int dapm_power_widgets(struct snd_soc_card *card, int event)
 {
-       struct snd_soc_card *card = dapm->card;
        struct snd_soc_dapm_widget *w;
        struct snd_soc_dapm_context *d;
        LIST_HEAD(up_list);
@@ -1689,7 +1826,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
                        break;
                }
 
-               if (w->power) {
+               if (w->new_power) {
                        d = w->dapm;
 
                        /* Supplies and micbiases only bring the
@@ -1731,29 +1868,29 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
        trace_snd_soc_dapm_walk_done(card);
 
        /* Run all the bias changes in parallel */
-       list_for_each_entry(d, &dapm->card->dapm_list, list)
+       list_for_each_entry(d, &card->dapm_list, list)
                async_schedule_domain(dapm_pre_sequence_async, d,
                                        &async_domain);
        async_synchronize_full_domain(&async_domain);
 
        list_for_each_entry(w, &down_list, power_list) {
-               dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMD);
+               dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMD);
        }
 
        list_for_each_entry(w, &up_list, power_list) {
-               dapm_seq_check_event(dapm, w, SND_SOC_DAPM_WILL_PMU);
+               dapm_seq_check_event(card, w, SND_SOC_DAPM_WILL_PMU);
        }
 
        /* Power down widgets first; try to avoid amplifying pops. */
-       dapm_seq_run(dapm, &down_list, event, false);
+       dapm_seq_run(card, &down_list, event, false);
 
-       dapm_widget_update(dapm);
+       dapm_widget_update(card);
 
        /* Now power up. */
-       dapm_seq_run(dapm, &up_list, event, true);
+       dapm_seq_run(card, &up_list, event, true);
 
        /* Run all the bias changes in parallel */
-       list_for_each_entry(d, &dapm->card->dapm_list, list)
+       list_for_each_entry(d, &card->dapm_list, list)
                async_schedule_domain(dapm_post_sequence_async, d,
                                        &async_domain);
        async_synchronize_full_domain(&async_domain);
@@ -1764,7 +1901,7 @@ static int dapm_power_widgets(struct snd_soc_dapm_context *dapm, int event)
                        d->stream_event(d, event);
        }
 
-       pop_dbg(dapm->dev, card->pop_time,
+       pop_dbg(card->dev, card->pop_time,
                "DAPM sequencing finished, waiting %dms\n", card->pop_time);
        pop_wait(card->pop_time);
 
@@ -1799,8 +1936,8 @@ static ssize_t dapm_widget_power_read_file(struct file *file,
 
        if (w->reg >= 0)
                ret += snprintf(buf + ret, PAGE_SIZE - ret,
-                               " - R%d(0x%x) bit %d",
-                               w->reg, w->reg, w->shift);
+                               " - R%d(0x%x) mask 0x%x",
+                               w->reg, w->reg, w->mask << w->shift);
 
        ret += snprintf(buf + ret, PAGE_SIZE - ret, "\n");
 
@@ -1937,22 +2074,14 @@ static inline void dapm_debugfs_cleanup(struct snd_soc_dapm_context *dapm)
 #endif
 
 /* test and update the power status of a mux widget */
-static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
+static int soc_dapm_mux_update_power(struct snd_soc_card *card,
                                 struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
 {
        struct snd_soc_dapm_path *path;
        int found = 0;
 
-       if (widget->id != snd_soc_dapm_mux &&
-           widget->id != snd_soc_dapm_virt_mux &&
-           widget->id != snd_soc_dapm_value_mux)
-               return -ENODEV;
-
        /* find dapm widget path assoc with kcontrol */
-       list_for_each_entry(path, &widget->dapm->card->paths, list) {
-               if (path->kcontrol != kcontrol)
-                       continue;
-
+       dapm_kcontrol_for_each_path(path, kcontrol) {
                if (!path->name || !e->texts[mux])
                        continue;
 
@@ -1967,73 +2096,68 @@ static int soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
                                                "mux disconnection");
                        path->connect = 0; /* old connection must be powered down */
                }
+               dapm_mark_dirty(path->sink, "mux change");
        }
 
-       if (found) {
-               dapm_mark_dirty(widget, "mux change");
-               dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
-       }
+       if (found)
+               dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
 
        return found;
 }
 
-int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_widget *widget,
-               struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e)
+int snd_soc_dapm_mux_update_power(struct snd_soc_dapm_context *dapm,
+       struct snd_kcontrol *kcontrol, int mux, struct soc_enum *e,
+       struct snd_soc_dapm_update *update)
 {
-       struct snd_soc_card *card = widget->dapm->card;
+       struct snd_soc_card *card = dapm->card;
        int ret;
 
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
-       ret = soc_dapm_mux_update_power(widget, kcontrol, mux, e);
+       card->update = update;
+       ret = soc_dapm_mux_update_power(card, kcontrol, mux, e);
+       card->update = NULL;
        mutex_unlock(&card->dapm_mutex);
        if (ret > 0)
-               soc_dpcm_runtime_update(widget);
+               soc_dpcm_runtime_update(card);
        return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_mux_update_power);
 
 /* test and update the power status of a mixer or switch widget */
-static int soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
+static int soc_dapm_mixer_update_power(struct snd_soc_card *card,
                                   struct snd_kcontrol *kcontrol, int connect)
 {
        struct snd_soc_dapm_path *path;
        int found = 0;
 
-       if (widget->id != snd_soc_dapm_mixer &&
-           widget->id != snd_soc_dapm_mixer_named_ctl &&
-           widget->id != snd_soc_dapm_switch)
-               return -ENODEV;
-
        /* find dapm widget path assoc with kcontrol */
-       list_for_each_entry(path, &widget->dapm->card->paths, list) {
-               if (path->kcontrol != kcontrol)
-                       continue;
-
-               /* found, now check type */
+       dapm_kcontrol_for_each_path(path, kcontrol) {
                found = 1;
                path->connect = connect;
                dapm_mark_dirty(path->source, "mixer connection");
+               dapm_mark_dirty(path->sink, "mixer update");
        }
 
-       if (found) {
-               dapm_mark_dirty(widget, "mixer update");
-               dapm_power_widgets(widget->dapm, SND_SOC_DAPM_STREAM_NOP);
-       }
+       if (found)
+               dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
 
        return found;
 }
 
-int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_widget *widget,
-                               struct snd_kcontrol *kcontrol, int connect)
+int snd_soc_dapm_mixer_update_power(struct snd_soc_dapm_context *dapm,
+       struct snd_kcontrol *kcontrol, int connect,
+       struct snd_soc_dapm_update *update)
 {
-       struct snd_soc_card *card = widget->dapm->card;
+       struct snd_soc_card *card = dapm->card;
        int ret;
 
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
-       ret = soc_dapm_mixer_update_power(widget, kcontrol, connect);
+       card->update = update;
+       ret = soc_dapm_mixer_update_power(card, kcontrol, connect);
+       card->update = NULL;
        mutex_unlock(&card->dapm_mutex);
        if (ret > 0)
-               soc_dpcm_runtime_update(widget);
+               soc_dpcm_runtime_update(card);
        return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_mixer_update_power);
@@ -2112,6 +2236,7 @@ static void dapm_free_path(struct snd_soc_dapm_path *path)
 {
        list_del(&path->list_sink);
        list_del(&path->list_source);
+       list_del(&path->list_kcontrol);
        list_del(&path->list);
        kfree(path);
 }
@@ -2206,70 +2331,20 @@ int snd_soc_dapm_sync(struct snd_soc_dapm_context *dapm)
                return 0;
 
        mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
-       ret = dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
+       ret = dapm_power_widgets(dapm->card, SND_SOC_DAPM_STREAM_NOP);
        mutex_unlock(&dapm->card->dapm_mutex);
        return ret;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_sync);
 
-static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
-                                 const struct snd_soc_dapm_route *route)
+static int snd_soc_dapm_add_path(struct snd_soc_dapm_context *dapm,
+       struct snd_soc_dapm_widget *wsource, struct snd_soc_dapm_widget *wsink,
+       const char *control,
+       int (*connected)(struct snd_soc_dapm_widget *source,
+                        struct snd_soc_dapm_widget *sink))
 {
        struct snd_soc_dapm_path *path;
-       struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
-       struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;
-       const char *sink;
-       const char *control = route->control;
-       const char *source;
-       char prefixed_sink[80];
-       char prefixed_source[80];
-       int ret = 0;
-
-       if (dapm->codec && dapm->codec->name_prefix) {
-               snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
-                        dapm->codec->name_prefix, route->sink);
-               sink = prefixed_sink;
-               snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
-                        dapm->codec->name_prefix, route->source);
-               source = prefixed_source;
-       } else {
-               sink = route->sink;
-               source = route->source;
-       }
-
-       /*
-        * find src and dest widgets over all widgets but favor a widget from
-        * current DAPM context
-        */
-       list_for_each_entry(w, &dapm->card->widgets, list) {
-               if (!wsink && !(strcmp(w->name, sink))) {
-                       wtsink = w;
-                       if (w->dapm == dapm)
-                               wsink = w;
-                       continue;
-               }
-               if (!wsource && !(strcmp(w->name, source))) {
-                       wtsource = w;
-                       if (w->dapm == dapm)
-                               wsource = w;
-               }
-       }
-       /* use widget from another DAPM context if not found from this */
-       if (!wsink)
-               wsink = wtsink;
-       if (!wsource)
-               wsource = wtsource;
-
-       if (wsource == NULL) {
-               dev_err(dapm->dev, "ASoC: no source widget found for %s\n",
-                       route->source);
-               return -ENODEV;
-       }
-       if (wsink == NULL) {
-               dev_err(dapm->dev, "ASoC: no sink widget found for %s\n",
-                       route->sink);
-               return -ENODEV;
-       }
+       int ret;
 
        path = kzalloc(sizeof(struct snd_soc_dapm_path), GFP_KERNEL);
        if (!path)
@@ -2277,8 +2352,9 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
 
        path->source = wsource;
        path->sink = wsink;
-       path->connected = route->connected;
+       path->connected = connected;
        INIT_LIST_HEAD(&path->list);
+       INIT_LIST_HEAD(&path->list_kcontrol);
        INIT_LIST_HEAD(&path->list_source);
        INIT_LIST_HEAD(&path->list_sink);
 
@@ -2298,6 +2374,9 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
                        wsource->ext = 1;
        }
 
+       dapm_mark_dirty(wsource, "Route added");
+       dapm_mark_dirty(wsink, "Route added");
+
        /* connect static paths */
        if (control == NULL) {
                list_add(&path->list, &dapm->card->paths);
@@ -2328,6 +2407,7 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
        case snd_soc_dapm_dai_in:
        case snd_soc_dapm_dai_out:
        case snd_soc_dapm_dai_link:
+       case snd_soc_dapm_kcontrol:
                list_add(&path->list, &dapm->card->paths);
                list_add(&path->list_sink, &wsink->sources);
                list_add(&path->list_source, &wsource->sinks);
@@ -2359,15 +2439,78 @@ static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
                return 0;
        }
 
-       dapm_mark_dirty(wsource, "Route added");
-       dapm_mark_dirty(wsink, "Route added");
-
        return 0;
+err:
+       kfree(path);
+       return ret;
+}
+
+static int snd_soc_dapm_add_route(struct snd_soc_dapm_context *dapm,
+                                 const struct snd_soc_dapm_route *route)
+{
+       struct snd_soc_dapm_widget *wsource = NULL, *wsink = NULL, *w;
+       struct snd_soc_dapm_widget *wtsource = NULL, *wtsink = NULL;
+       const char *sink;
+       const char *source;
+       char prefixed_sink[80];
+       char prefixed_source[80];
+       int ret;
 
+       if (dapm->codec && dapm->codec->name_prefix) {
+               snprintf(prefixed_sink, sizeof(prefixed_sink), "%s %s",
+                        dapm->codec->name_prefix, route->sink);
+               sink = prefixed_sink;
+               snprintf(prefixed_source, sizeof(prefixed_source), "%s %s",
+                        dapm->codec->name_prefix, route->source);
+               source = prefixed_source;
+       } else {
+               sink = route->sink;
+               source = route->source;
+       }
+
+       /*
+        * find src and dest widgets over all widgets but favor a widget from
+        * current DAPM context
+        */
+       list_for_each_entry(w, &dapm->card->widgets, list) {
+               if (!wsink && !(strcmp(w->name, sink))) {
+                       wtsink = w;
+                       if (w->dapm == dapm)
+                               wsink = w;
+                       continue;
+               }
+               if (!wsource && !(strcmp(w->name, source))) {
+                       wtsource = w;
+                       if (w->dapm == dapm)
+                               wsource = w;
+               }
+       }
+       /* use widget from another DAPM context if not found from this */
+       if (!wsink)
+               wsink = wtsink;
+       if (!wsource)
+               wsource = wtsource;
+
+       if (wsource == NULL) {
+               dev_err(dapm->dev, "ASoC: no source widget found for %s\n",
+                       route->source);
+               return -ENODEV;
+       }
+       if (wsink == NULL) {
+               dev_err(dapm->dev, "ASoC: no sink widget found for %s\n",
+                       route->sink);
+               return -ENODEV;
+       }
+
+       ret = snd_soc_dapm_add_path(dapm, wsource, wsink, route->control,
+               route->connected);
+       if (ret)
+               goto err;
+
+       return 0;
 err:
        dev_warn(dapm->dev, "ASoC: no dapm match for %s --> %s --> %s\n",
-                source, control, sink);
-       kfree(path);
+                source, route->control, sink);
        return ret;
 }
 
@@ -2569,14 +2712,14 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_weak_routes);
  *
  * Returns 0 for success.
  */
-int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
+int snd_soc_dapm_new_widgets(struct snd_soc_card *card)
 {
        struct snd_soc_dapm_widget *w;
        unsigned int val;
 
-       mutex_lock_nested(&dapm->card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_INIT);
 
-       list_for_each_entry(w, &dapm->card->widgets, list)
+       list_for_each_entry(w, &card->widgets, list)
        {
                if (w->new)
                        continue;
@@ -2586,7 +2729,7 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
                                                sizeof(struct snd_kcontrol *),
                                                GFP_KERNEL);
                        if (!w->kcontrols) {
-                               mutex_unlock(&dapm->card->dapm_mutex);
+                               mutex_unlock(&card->dapm_mutex);
                                return -ENOMEM;
                        }
                }
@@ -2612,12 +2755,9 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
 
                /* Read the initial power state from the device */
                if (w->reg >= 0) {
-                       val = soc_widget_read(w, w->reg);
-                       val &= 1 << w->shift;
-                       if (w->invert)
-                               val = !val;
-
-                       if (val)
+                       val = soc_widget_read(w, w->reg) >> w->shift;
+                       val &= w->mask;
+                       if (val == w->on_val)
                                w->power = 1;
                }
 
@@ -2627,8 +2767,8 @@ int snd_soc_dapm_new_widgets(struct snd_soc_dapm_context *dapm)
                dapm_debugfs_add_widget(w);
        }
 
-       dapm_power_widgets(dapm, SND_SOC_DAPM_STREAM_NOP);
-       mutex_unlock(&dapm->card->dapm_mutex);
+       dapm_power_widgets(card, SND_SOC_DAPM_STREAM_NOP);
+       mutex_unlock(&card->dapm_mutex);
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
@@ -2645,8 +2785,8 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_new_widgets);
 int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
+       struct snd_soc_card *card = codec->card;
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
        unsigned int reg = mc->reg;
@@ -2654,17 +2794,24 @@ int snd_soc_dapm_get_volsw(struct snd_kcontrol *kcontrol,
        int max = mc->max;
        unsigned int mask = (1 << fls(max)) - 1;
        unsigned int invert = mc->invert;
+       unsigned int val;
 
        if (snd_soc_volsw_is_stereo(mc))
-               dev_warn(widget->dapm->dev,
+               dev_warn(codec->dapm.dev,
                         "ASoC: Control '%s' is stereo, which is not supported\n",
                         kcontrol->id.name);
 
-       ucontrol->value.integer.value[0] =
-               (snd_soc_read(widget->codec, reg) >> shift) & mask;
+       mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
+       if (dapm_kcontrol_is_powered(kcontrol))
+               val = (snd_soc_read(codec, reg) >> shift) & mask;
+       else
+               val = dapm_kcontrol_get_value(kcontrol);
+       mutex_unlock(&card->dapm_mutex);
+
        if (invert)
-               ucontrol->value.integer.value[0] =
-                       max - ucontrol->value.integer.value[0];
+               ucontrol->value.integer.value[0] = max - val;
+       else
+               ucontrol->value.integer.value[0] = val;
 
        return 0;
 }
@@ -2682,9 +2829,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_volsw);
 int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-       struct snd_soc_codec *codec = widget->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
        struct snd_soc_card *card = codec->card;
        struct soc_mixer_control *mc =
                (struct soc_mixer_control *)kcontrol->private_value;
@@ -2696,10 +2841,9 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
        unsigned int val;
        int connect, change;
        struct snd_soc_dapm_update update;
-       int wi;
 
        if (snd_soc_volsw_is_stereo(mc))
-               dev_warn(widget->dapm->dev,
+               dev_warn(codec->dapm.dev,
                         "ASoC: Control '%s' is stereo, which is not supported\n",
                         kcontrol->id.name);
 
@@ -2708,29 +2852,26 @@ int snd_soc_dapm_put_volsw(struct snd_kcontrol *kcontrol,
 
        if (invert)
                val = max - val;
-       mask = mask << shift;
-       val = val << shift;
 
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
-       change = snd_soc_test_bits(widget->codec, reg, mask, val);
-       if (change) {
-               for (wi = 0; wi < wlist->num_widgets; wi++) {
-                       widget = wlist->widgets[wi];
+       dapm_kcontrol_set_value(kcontrol, val);
 
-                       widget->value = val;
+       mask = mask << shift;
+       val = val << shift;
 
-                       update.kcontrol = kcontrol;
-                       update.widget = widget;
-                       update.reg = reg;
-                       update.mask = mask;
-                       update.val = val;
-                       widget->dapm->update = &update;
+       change = snd_soc_test_bits(codec, reg, mask, val);
+       if (change) {
+               update.kcontrol = kcontrol;
+               update.reg = reg;
+               update.mask = mask;
+               update.val = val;
 
-                       soc_dapm_mixer_update_power(widget, kcontrol, connect);
+               card->update = &update;
 
-                       widget->dapm->update = NULL;
-               }
+               soc_dapm_mixer_update_power(card, kcontrol, connect);
+
+               card->update = NULL;
        }
 
        mutex_unlock(&card->dapm_mutex);
@@ -2750,12 +2891,11 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_volsw);
 int snd_soc_dapm_get_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val;
 
-       val = snd_soc_read(widget->codec, e->reg);
+       val = snd_soc_read(codec, e->reg);
        ucontrol->value.enumerated.item[0] = (val >> e->shift_l) & e->mask;
        if (e->shift_l != e->shift_r)
                ucontrol->value.enumerated.item[1] =
@@ -2777,15 +2917,12 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_double);
 int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-       struct snd_soc_codec *codec = widget->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
        struct snd_soc_card *card = codec->card;
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val, mux, change;
        unsigned int mask;
        struct snd_soc_dapm_update update;
-       int wi;
 
        if (ucontrol->value.enumerated.item[0] > e->max - 1)
                return -EINVAL;
@@ -2801,24 +2938,17 @@ int snd_soc_dapm_put_enum_double(struct snd_kcontrol *kcontrol,
 
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
-       change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+       change = snd_soc_test_bits(codec, e->reg, mask, val);
        if (change) {
-               for (wi = 0; wi < wlist->num_widgets; wi++) {
-                       widget = wlist->widgets[wi];
-
-                       widget->value = val;
+               update.kcontrol = kcontrol;
+               update.reg = e->reg;
+               update.mask = mask;
+               update.val = val;
+               card->update = &update;
 
-                       update.kcontrol = kcontrol;
-                       update.widget = widget;
-                       update.reg = e->reg;
-                       update.mask = mask;
-                       update.val = val;
-                       widget->dapm->update = &update;
+               soc_dapm_mux_update_power(card, kcontrol, mux, e);
 
-                       soc_dapm_mux_update_power(widget, kcontrol, mux, e);
-
-                       widget->dapm->update = NULL;
-               }
+               card->update = NULL;
        }
 
        mutex_unlock(&card->dapm_mutex);
@@ -2836,11 +2966,7 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_double);
 int snd_soc_dapm_get_enum_virt(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-
-       ucontrol->value.enumerated.item[0] = widget->value;
-
+       ucontrol->value.enumerated.item[0] = dapm_kcontrol_get_value(kcontrol);
        return 0;
 }
 EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
@@ -2855,30 +2981,22 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_enum_virt);
 int snd_soc_dapm_put_enum_virt(struct snd_kcontrol *kcontrol,
                               struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-       struct snd_soc_codec *codec = widget->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
        struct snd_soc_card *card = codec->card;
+       unsigned int value;
        struct soc_enum *e =
                (struct soc_enum *)kcontrol->private_value;
        int change;
-       int wi;
 
        if (ucontrol->value.enumerated.item[0] >= e->max)
                return -EINVAL;
 
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
-       change = widget->value != ucontrol->value.enumerated.item[0];
-       if (change) {
-               for (wi = 0; wi < wlist->num_widgets; wi++) {
-                       widget = wlist->widgets[wi];
-
-                       widget->value = ucontrol->value.enumerated.item[0];
-
-                       soc_dapm_mux_update_power(widget, kcontrol, widget->value, e);
-               }
-       }
+       value = ucontrol->value.enumerated.item[0];
+       change = dapm_kcontrol_set_value(kcontrol, value);
+       if (change)
+               soc_dapm_mux_update_power(card, kcontrol, value, e);
 
        mutex_unlock(&card->dapm_mutex);
        return change;
@@ -2901,12 +3019,11 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_put_enum_virt);
 int snd_soc_dapm_get_value_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int reg_val, val, mux;
 
-       reg_val = snd_soc_read(widget->codec, e->reg);
+       reg_val = snd_soc_read(codec, e->reg);
        val = (reg_val >> e->shift_l) & e->mask;
        for (mux = 0; mux < e->max; mux++) {
                if (val == e->values[mux])
@@ -2942,15 +3059,12 @@ EXPORT_SYMBOL_GPL(snd_soc_dapm_get_value_enum_double);
 int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
        struct snd_ctl_elem_value *ucontrol)
 {
-       struct snd_soc_dapm_widget_list *wlist = snd_kcontrol_chip(kcontrol);
-       struct snd_soc_dapm_widget *widget = wlist->widgets[0];
-       struct snd_soc_codec *codec = widget->codec;
+       struct snd_soc_codec *codec = snd_soc_dapm_kcontrol_codec(kcontrol);
        struct snd_soc_card *card = codec->card;
        struct soc_enum *e = (struct soc_enum *)kcontrol->private_value;
        unsigned int val, mux, change;
        unsigned int mask;
        struct snd_soc_dapm_update update;
-       int wi;
 
        if (ucontrol->value.enumerated.item[0] > e->max - 1)
                return -EINVAL;
@@ -2966,24 +3080,17 @@ int snd_soc_dapm_put_value_enum_double(struct snd_kcontrol *kcontrol,
 
        mutex_lock_nested(&card->dapm_mutex, SND_SOC_DAPM_CLASS_RUNTIME);
 
-       change = snd_soc_test_bits(widget->codec, e->reg, mask, val);
+       change = snd_soc_test_bits(codec, e->reg, mask, val);
        if (change) {
-               for (wi = 0; wi < wlist->num_widgets; wi++) {
-                       widget = wlist->widgets[wi];
-
-                       widget->value = val;
+               update.kcontrol = kcontrol;
+               update.reg = e->reg;
+               update.mask = mask;
+               update.val = val;
+               card->update = &update;
 
-                       update.kcontrol = kcontrol;
-                       update.widget = widget;
-                       update.reg = e->reg;
-                       update.mask = mask;
-                       update.val = val;
-                       widget->dapm->update = &update;
+               soc_dapm_mux_update_power(card, kcontrol, mux, e);
 
-                       soc_dapm_mux_update_power(widget, kcontrol, mux, e);
-
-                       widget->dapm->update = NULL;
-               }
+               card->update = NULL;
        }
 
        mutex_unlock(&card->dapm_mutex);
@@ -3080,7 +3187,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
                        return NULL;
                }
 
-               if (w->invert & SND_SOC_DAPM_REGULATOR_BYPASS) {
+               if (w->on_val & SND_SOC_DAPM_REGULATOR_BYPASS) {
                        ret = regulator_allow_bypass(w->regulator, true);
                        if (ret != 0)
                                dev_warn(w->dapm->dev,
@@ -3127,16 +3234,16 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
        case snd_soc_dapm_value_mux:
                w->power_check = dapm_generic_check_power;
                break;
-       case snd_soc_dapm_adc:
-       case snd_soc_dapm_aif_out:
        case snd_soc_dapm_dai_out:
                w->power_check = dapm_adc_check_power;
                break;
-       case snd_soc_dapm_dac:
-       case snd_soc_dapm_aif_in:
        case snd_soc_dapm_dai_in:
                w->power_check = dapm_dac_check_power;
                break;
+       case snd_soc_dapm_adc:
+       case snd_soc_dapm_aif_out:
+       case snd_soc_dapm_dac:
+       case snd_soc_dapm_aif_in:
        case snd_soc_dapm_pga:
        case snd_soc_dapm_out_drv:
        case snd_soc_dapm_input:
@@ -3152,6 +3259,7 @@ snd_soc_dapm_new_control(struct snd_soc_dapm_context *dapm,
        case snd_soc_dapm_supply:
        case snd_soc_dapm_regulator_supply:
        case snd_soc_dapm_clock_supply:
+       case snd_soc_dapm_kcontrol:
                w->power_check = dapm_supply_check_power;
                break;
        default:
@@ -3416,9 +3524,6 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
 {
        struct snd_soc_dapm_widget *dai_w, *w;
        struct snd_soc_dai *dai;
-       struct snd_soc_dapm_route r;
-
-       memset(&r, 0, sizeof(r));
 
        /* For each DAI widget... */
        list_for_each_entry(dai_w, &card->widgets, list) {
@@ -3445,29 +3550,27 @@ int snd_soc_dapm_link_dai_widgets(struct snd_soc_card *card)
                                break;
                        }
 
-                       if (!w->sname)
+                       if (!w->sname || !strstr(w->sname, dai_w->name))
                                continue;
 
                        if (dai->driver->playback.stream_name &&
                            strstr(w->sname,
                                   dai->driver->playback.stream_name)) {
-                               r.source = dai->playback_widget->name;
-                               r.sink = w->name;
                                dev_dbg(dai->dev, "%s -> %s\n",
-                                        r.source, r.sink);
+                                        dai->playback_widget->name, w->name);
 
-                               snd_soc_dapm_add_route(w->dapm, &r);
+                               snd_soc_dapm_add_path(w->dapm,
+                                       dai->playback_widget, w, NULL, NULL);
                        }
 
                        if (dai->driver->capture.stream_name &&
                            strstr(w->sname,
                                   dai->driver->capture.stream_name)) {
-                               r.source = w->name;
-                               r.sink = dai->capture_widget->name;
                                dev_dbg(dai->dev, "%s -> %s\n",
-                                       r.source, r.sink);
+                                       w->name, dai->capture_widget->name);
 
-                               snd_soc_dapm_add_route(w->dapm, &r);
+                               snd_soc_dapm_add_path(w->dapm, w,
+                                       dai->capture_widget, NULL, NULL);
                        }
                }
        }
@@ -3529,7 +3632,7 @@ static void soc_dapm_stream_event(struct snd_soc_pcm_runtime *rtd, int stream,
                }
        }
 
-       dapm_power_widgets(&rtd->card->dapm, event);
+       dapm_power_widgets(rtd->card, event);
 }
 
 /**
@@ -3798,7 +3901,7 @@ static void soc_dapm_shutdown_codec(struct snd_soc_dapm_context *dapm)
                if (dapm->bias_level == SND_SOC_BIAS_ON)
                        snd_soc_dapm_set_bias_level(dapm,
                                                    SND_SOC_BIAS_PREPARE);
-               dapm_seq_run(dapm, &down_list, 0, false);
+               dapm_seq_run(card, &down_list, 0, false);
                if (dapm->bias_level == SND_SOC_BIAS_PREPARE)
                        snd_soc_dapm_set_bias_level(dapm,
                                                    SND_SOC_BIAS_STANDBY);
index 0bb5cccd77663a4d819a67731245e51564af3224..71358e3b54d93ef10c3e402d98f66773b81d27fc 100644 (file)
@@ -183,8 +183,6 @@ int snd_soc_jack_add_pins(struct snd_soc_jack *jack, int count,
                list_add(&(pins[i].list), &jack->pins);
        }
 
-       snd_soc_dapm_new_widgets(&jack->codec->card->dapm);
-
        /* Update to reflect the last reported status; canned jack
         * implementations are likely to set their state before the
         * card has an opportunity to associate pins.
@@ -263,7 +261,7 @@ static irqreturn_t gpio_handler(int irq, void *data)
        if (device_may_wakeup(dev))
                pm_wakeup_event(dev, gpio->debounce_time + 50);
 
-       schedule_delayed_work(&gpio->work,
+       queue_delayed_work(system_power_efficient_wq, &gpio->work,
                              msecs_to_jiffies(gpio->debounce_time));
 
        return IRQ_HANDLED;
index b6c640332a1722c95c27fa8aa99b4d8b135d456f..fb70fbe26862f63ef879b5fde3c58e49e2fd49a6 100644 (file)
@@ -411,8 +411,9 @@ static int soc_pcm_close(struct snd_pcm_substream *substream)
                } else {
                        /* start delayed pop wq here for playback streams */
                        rtd->pop_wait = 1;
-                       schedule_delayed_work(&rtd->delayed_work,
-                               msecs_to_jiffies(rtd->pmdown_time));
+                       queue_delayed_work(system_power_efficient_wq,
+                                          &rtd->delayed_work,
+                                          msecs_to_jiffies(rtd->pmdown_time));
                }
        } else {
                /* capture streams can be powered down now */
@@ -1832,18 +1833,10 @@ static int dpcm_run_old_update(struct snd_soc_pcm_runtime *fe, int stream)
 /* Called by DAPM mixer/mux changes to update audio routing between PCMs and
  * any DAI links.
  */
-int soc_dpcm_runtime_update(struct snd_soc_dapm_widget *widget)
+int soc_dpcm_runtime_update(struct snd_soc_card *card)
 {
-       struct snd_soc_card *card;
        int i, old, new, paths;
 
-       if (widget->codec)
-               card = widget->codec->card;
-       else if (widget->platform)
-               card = widget->platform->card;
-       else
-               return -EINVAL;
-
        mutex_lock_nested(&card->mutex, SND_SOC_CARD_CLASS_RUNTIME);
        for (i = 0; i < card->num_rtd; i++) {
                struct snd_soc_dapm_widget_list *list;
index 3567d73b218ed9273a7b617ae3f91c5411f83cf3..0a53053495f3d39cf6363b2255bfa4ad629c6be0 100644 (file)
@@ -1,6 +1,6 @@
 config SND_SPEAR_SOC
        tristate
-       select SND_SOC_DMAENGINE_PCM
+       select SND_DMAENGINE_PCM
 
 config SND_SPEAR_SPDIF_OUT
        tristate
index 995b120c2cd0a49a90a191e1e06649cb52396839..8fc653ca3ab40b3ef04d1a58723a2466086ab8ff 100644 (file)
@@ -1,8 +1,8 @@
 config SND_SOC_TEGRA
        tristate "SoC Audio for the Tegra System-on-Chip"
-       depends on ARCH_TEGRA && TEGRA20_APB_DMA
+       depends on (ARCH_TEGRA && TEGRA20_APB_DMA) || COMPILE_TEST
        select REGMAP_MMIO
-       select SND_SOC_GENERIC_DMAENGINE_PCM if TEGRA20_APB_DMA
+       select SND_SOC_GENERIC_DMAENGINE_PCM
        help
          Say Y or M here if you want support for SoC audio on Tegra.
 
@@ -61,7 +61,7 @@ config SND_SOC_TEGRA30_I2S
 
 config SND_SOC_TEGRA_RT5640
        tristate "SoC Audio support for Tegra boards using an RT5640 codec"
-       depends on SND_SOC_TEGRA && I2C
+       depends on SND_SOC_TEGRA && I2C && GPIOLIB
        select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
        select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
        select SND_SOC_RT5640
@@ -71,7 +71,7 @@ config SND_SOC_TEGRA_RT5640
 
 config SND_SOC_TEGRA_WM8753
        tristate "SoC Audio support for Tegra boards using a WM8753 codec"
-       depends on SND_SOC_TEGRA && I2C
+       depends on SND_SOC_TEGRA && I2C && GPIOLIB
        select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
        select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
        select SND_SOC_WM8753
@@ -81,7 +81,7 @@ config SND_SOC_TEGRA_WM8753
 
 config SND_SOC_TEGRA_WM8903
        tristate "SoC Audio support for Tegra boards using a WM8903 codec"
-       depends on SND_SOC_TEGRA && I2C
+       depends on SND_SOC_TEGRA && I2C && GPIOLIB
        select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
        select SND_SOC_TEGRA30_I2S if ARCH_TEGRA_3x_SOC
        select SND_SOC_WM8903
@@ -92,7 +92,7 @@ config SND_SOC_TEGRA_WM8903
 
 config SND_SOC_TEGRA_WM9712
        tristate "SoC Audio support for Tegra boards using a WM9712 codec"
-       depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC
+       depends on SND_SOC_TEGRA && ARCH_TEGRA_2x_SOC && GPIOLIB
        select SND_SOC_TEGRA20_AC97
        select SND_SOC_WM9712
        help
@@ -110,7 +110,7 @@ config SND_SOC_TEGRA_TRIMSLICE
 
 config SND_SOC_TEGRA_ALC5632
        tristate "SoC Audio support for Tegra boards using an ALC5632 codec"
-       depends on SND_SOC_TEGRA && I2C
+       depends on SND_SOC_TEGRA && I2C && GPIOLIB
        select SND_SOC_TEGRA20_I2S if ARCH_TEGRA_2x_SOC
        select SND_SOC_ALC5632
        help
index 6c486625321bebb78b6f66370fda54a49a300fcb..ae27bcd586d25428458c305c16448395b9f74d88 100644 (file)
@@ -334,12 +334,6 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
        }
 
        mem = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!mem) {
-               dev_err(&pdev->dev, "No memory resource\n");
-               ret = -ENODEV;
-               goto err_clk_put;
-       }
-
        regs = devm_ioremap_resource(&pdev->dev, mem);
        if (IS_ERR(regs)) {
                ret = PTR_ERR(regs);
@@ -432,8 +426,6 @@ static int tegra20_ac97_platform_probe(struct platform_device *pdev)
 
        return 0;
 
-err_unregister_pcm:
-       tegra_pcm_platform_unregister(&pdev->dev);
 err_unregister_component:
        snd_soc_unregister_component(&pdev->dev);
 err_asoc_utils_fini:
index 48d05d9e1002b3f13529f386d543c0b59803874e..c61ea3a1030f7a683f55bd087049392fe4867bfb 100644 (file)
@@ -13,8 +13,6 @@
  * published by the Free Software Foundation.
  */
 
-#include <asm/mach-types.h>
-
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
index 08794f915a9433a1fdaa91842c74c16279e7f9f2..4511c5a875ecd8567e58c3f62496ab8e1076473f 100644 (file)
@@ -99,6 +99,7 @@ static struct snd_soc_jack_gpio tegra_rt5640_hp_jack_gpio = {
 static const struct snd_soc_dapm_widget tegra_rt5640_dapm_widgets[] = {
        SND_SOC_DAPM_HP("Headphones", NULL),
        SND_SOC_DAPM_SPK("Speakers", NULL),
+       SND_SOC_DAPM_MIC("Mic Jack", NULL),
 };
 
 static const struct snd_kcontrol_new tegra_rt5640_controls[] = {
index f87fc53e9b8cddab8d8f529c0ab730a8481ede8d..8e774d1a243c624dcca5b09c25065316629d886e 100644 (file)
@@ -28,8 +28,6 @@
  *
  */
 
-#include <asm/mach-types.h>
-
 #include <linux/module.h>
 #include <linux/platform_device.h>
 #include <linux/slab.h>
index 05c68aab5cf0a9a8e06a7fb93f27cd2b0d3cf3e0..734bfcd211481ccccf26c86a3db23399ecf9ffb3 100644 (file)
@@ -24,8 +24,6 @@
  *
  */
 
-#include <asm/mach-types.h>
-
 #include <linux/module.h>
 #include <linux/of.h>
 #include <linux/platform_device.h>
index 4bcce8a3cdedd618e18844a55373c273cbd6427c..e0305a1485680b18cbc85ba33a506bda799f47c1 100644 (file)
@@ -184,9 +184,6 @@ static int txx9aclc_ac97_dev_probe(struct platform_device *pdev)
        if (irq < 0)
                return irq;
        r = platform_get_resource(pdev, IORESOURCE_MEM, 0);
-       if (!r)
-               return -EBUSY;
-
        drvdata->base = devm_ioremap_resource(&pdev->dev, r);
        if (IS_ERR(drvdata->base))
                return PTR_ERR(drvdata->base);
index 8f5cd00a6e468622af895ed4faebeaf78d7b8f9e..178d1bad62591565589803f52c859a2062044df2 100644 (file)
@@ -52,6 +52,7 @@ static struct snd_soc_dai_link mop500_dai_links[] = {
 
 static struct snd_soc_card mop500_card = {
        .name = "MOP500-card",
+       .owner = THIS_MODULE,
        .probe = NULL,
        .dai_link = mop500_dai_links,
        .num_links = ARRAY_SIZE(mop500_dai_links),
index b9defcdeb7ef805af05a6453ce309a2eb64bdb18..780bf3f62d2800f3a8f9da7ffc8d011d0a6c3bd2 100644 (file)
@@ -346,10 +346,10 @@ static int usb6fire_fw_check(u8 *version)
                if (!memcmp(version, known_fw_versions + i, 2))
                        return 0;
 
-       snd_printk(KERN_ERR PREFIX "invalid fimware version in device: %*ph. "
+       snd_printk(KERN_ERR PREFIX "invalid fimware version in device: %4ph. "
                        "please reconnect to power. if this failure "
                        "still happens, check your firmware installation.",
-                       4, version);
+                       version);
        return -EINVAL;
 }
 
index 659950e5b94f6b411577a84821f1a1c4d93b4593..93e970f2b3c0ad4d58957faed6b76ec2a1fb2991 100644 (file)
@@ -418,6 +418,9 @@ struct snd_usb_endpoint *snd_usb_add_endpoint(struct snd_usb_audio *chip,
        struct snd_usb_endpoint *ep;
        int is_playback = direction == SNDRV_PCM_STREAM_PLAYBACK;
 
+       if (WARN_ON(!alts))
+               return NULL;
+
        mutex_lock(&chip->mutex);
 
        list_for_each_entry(ep, &chip->ep_list, list) {
index 15b151ed4899c6032704d361b9c641d2b53b849f..b375d58871e7ce9f7eb0e63a3f73644984c6c5e1 100644 (file)
@@ -327,6 +327,137 @@ static int search_roland_implicit_fb(struct usb_device *dev, int ifnum,
        return 0;
 }
 
+static int set_sync_ep_implicit_fb_quirk(struct snd_usb_substream *subs,
+                                        struct usb_device *dev,
+                                        struct usb_interface_descriptor *altsd,
+                                        unsigned int attr)
+{
+       struct usb_host_interface *alts;
+       struct usb_interface *iface;
+       unsigned int ep;
+
+       /* Implicit feedback sync EPs consumers are always playback EPs */
+       if (subs->direction != SNDRV_PCM_STREAM_PLAYBACK)
+               return 0;
+
+       switch (subs->stream->chip->usb_id) {
+       case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
+       case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
+               ep = 0x81;
+               iface = usb_ifnum_to_if(dev, 3);
+
+               if (!iface || iface->num_altsetting == 0)
+                       return -EINVAL;
+
+               alts = &iface->altsetting[1];
+               goto add_sync_ep;
+               break;
+       case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
+       case USB_ID(0x0763, 0x2081):
+               ep = 0x81;
+               iface = usb_ifnum_to_if(dev, 2);
+
+               if (!iface || iface->num_altsetting == 0)
+                       return -EINVAL;
+
+               alts = &iface->altsetting[1];
+               goto add_sync_ep;
+       }
+       if (attr == USB_ENDPOINT_SYNC_ASYNC &&
+           altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
+           altsd->bInterfaceProtocol == 2 &&
+           altsd->bNumEndpoints == 1 &&
+           USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ &&
+           search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1,
+                                     altsd->bAlternateSetting,
+                                     &alts, &ep) >= 0) {
+               goto add_sync_ep;
+       }
+
+       /* No quirk */
+       return 0;
+
+add_sync_ep:
+       subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+                                                  alts, ep, !subs->direction,
+                                                  SND_USB_ENDPOINT_TYPE_DATA);
+       if (!subs->sync_endpoint)
+               return -EINVAL;
+
+       subs->data_endpoint->sync_master = subs->sync_endpoint;
+
+       return 0;
+}
+
+static int set_sync_endpoint(struct snd_usb_substream *subs,
+                            struct audioformat *fmt,
+                            struct usb_device *dev,
+                            struct usb_host_interface *alts,
+                            struct usb_interface_descriptor *altsd)
+{
+       int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
+       unsigned int ep, attr;
+       bool implicit_fb;
+       int err;
+
+       /* we need a sync pipe in async OUT or adaptive IN mode */
+       /* check the number of EP, since some devices have broken
+        * descriptors which fool us.  if it has only one EP,
+        * assume it as adaptive-out or sync-in.
+        */
+       attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
+
+       err = set_sync_ep_implicit_fb_quirk(subs, dev, altsd, attr);
+       if (err < 0)
+               return err;
+
+       if (altsd->bNumEndpoints < 2)
+               return 0;
+
+       if ((is_playback && attr != USB_ENDPOINT_SYNC_ASYNC) ||
+           (!is_playback && attr != USB_ENDPOINT_SYNC_ADAPTIVE))
+               return 0;
+
+       /* check sync-pipe endpoint */
+       /* ... and check descriptor size before accessing bSynchAddress
+          because there is a version of the SB Audigy 2 NX firmware lacking
+          the audio fields in the endpoint descriptors */
+       if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC ||
+           (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+            get_endpoint(alts, 1)->bSynchAddress != 0)) {
+               snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n",
+                          dev->devnum, fmt->iface, fmt->altsetting,
+                          get_endpoint(alts, 1)->bmAttributes,
+                          get_endpoint(alts, 1)->bLength,
+                          get_endpoint(alts, 1)->bSynchAddress);
+               return -EINVAL;
+       }
+       ep = get_endpoint(alts, 1)->bEndpointAddress;
+       if (get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
+           ((is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
+            (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
+               snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n",
+                          dev->devnum, fmt->iface, fmt->altsetting,
+                          is_playback, ep, get_endpoint(alts, 0)->bSynchAddress);
+               return -EINVAL;
+       }
+
+       implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK)
+                       == USB_ENDPOINT_USAGE_IMPLICIT_FB;
+
+       subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
+                                                  alts, ep, !subs->direction,
+                                                  implicit_fb ?
+                                                       SND_USB_ENDPOINT_TYPE_DATA :
+                                                       SND_USB_ENDPOINT_TYPE_SYNC);
+       if (!subs->sync_endpoint)
+               return -EINVAL;
+
+       subs->data_endpoint->sync_master = subs->sync_endpoint;
+
+       return 0;
+}
+
 /*
  * find a matching format and set up the interface
  */
@@ -336,9 +467,7 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
        struct usb_host_interface *alts;
        struct usb_interface_descriptor *altsd;
        struct usb_interface *iface;
-       unsigned int ep, attr;
-       int is_playback = subs->direction == SNDRV_PCM_STREAM_PLAYBACK;
-       int err, implicit_fb = 0;
+       int err;
 
        iface = usb_ifnum_to_if(dev, fmt->iface);
        if (WARN_ON(!iface))
@@ -383,118 +512,22 @@ static int set_format(struct snd_usb_substream *subs, struct audioformat *fmt)
        subs->data_endpoint = snd_usb_add_endpoint(subs->stream->chip,
                                                   alts, fmt->endpoint, subs->direction,
                                                   SND_USB_ENDPOINT_TYPE_DATA);
+
        if (!subs->data_endpoint)
                return -EINVAL;
 
-       /* we need a sync pipe in async OUT or adaptive IN mode */
-       /* check the number of EP, since some devices have broken
-        * descriptors which fool us.  if it has only one EP,
-        * assume it as adaptive-out or sync-in.
-        */
-       attr = fmt->ep_attr & USB_ENDPOINT_SYNCTYPE;
-
-       switch (subs->stream->chip->usb_id) {
-       case USB_ID(0x0763, 0x2030): /* M-Audio Fast Track C400 */
-       case USB_ID(0x0763, 0x2031): /* M-Audio Fast Track C600 */
-               if (is_playback) {
-                       implicit_fb = 1;
-                       ep = 0x81;
-                       iface = usb_ifnum_to_if(dev, 3);
-
-                       if (!iface || iface->num_altsetting == 0)
-                               return -EINVAL;
-
-                       alts = &iface->altsetting[1];
-                       goto add_sync_ep;
-               }
-               break;
-       case USB_ID(0x0763, 0x2080): /* M-Audio FastTrack Ultra */
-       case USB_ID(0x0763, 0x2081):
-               if (is_playback) {
-                       implicit_fb = 1;
-                       ep = 0x81;
-                       iface = usb_ifnum_to_if(dev, 2);
-
-                       if (!iface || iface->num_altsetting == 0)
-                               return -EINVAL;
-
-                       alts = &iface->altsetting[1];
-                       goto add_sync_ep;
-               }
-       }
-       if (is_playback &&
-           attr == USB_ENDPOINT_SYNC_ASYNC &&
-           altsd->bInterfaceClass == USB_CLASS_VENDOR_SPEC &&
-           altsd->bInterfaceProtocol == 2 &&
-           altsd->bNumEndpoints == 1 &&
-           USB_ID_VENDOR(subs->stream->chip->usb_id) == 0x0582 /* Roland */ &&
-           search_roland_implicit_fb(dev, altsd->bInterfaceNumber + 1,
-                                     altsd->bAlternateSetting,
-                                     &alts, &ep) >= 0) {
-               implicit_fb = 1;
-               goto add_sync_ep;
-       }
-
-       if (((is_playback && attr == USB_ENDPOINT_SYNC_ASYNC) ||
-            (!is_playback && attr == USB_ENDPOINT_SYNC_ADAPTIVE)) &&
-           altsd->bNumEndpoints >= 2) {
-               /* check sync-pipe endpoint */
-               /* ... and check descriptor size before accessing bSynchAddress
-                  because there is a version of the SB Audigy 2 NX firmware lacking
-                  the audio fields in the endpoint descriptors */
-               if ((get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_XFERTYPE_MASK) != USB_ENDPOINT_XFER_ISOC ||
-                   (get_endpoint(alts, 1)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-                    get_endpoint(alts, 1)->bSynchAddress != 0 &&
-                    !implicit_fb)) {
-                       snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. bmAttributes %02x, bLength %d, bSynchAddress %02x\n",
-                                  dev->devnum, fmt->iface, fmt->altsetting,
-                                  get_endpoint(alts, 1)->bmAttributes,
-                                  get_endpoint(alts, 1)->bLength,
-                                  get_endpoint(alts, 1)->bSynchAddress);
-                       return -EINVAL;
-               }
-               ep = get_endpoint(alts, 1)->bEndpointAddress;
-               if (!implicit_fb &&
-                   get_endpoint(alts, 0)->bLength >= USB_DT_ENDPOINT_AUDIO_SIZE &&
-                   (( is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress | USB_DIR_IN)) ||
-                    (!is_playback && ep != (unsigned int)(get_endpoint(alts, 0)->bSynchAddress & ~USB_DIR_IN)))) {
-                       snd_printk(KERN_ERR "%d:%d:%d : invalid sync pipe. is_playback %d, ep %02x, bSynchAddress %02x\n",
-                                  dev->devnum, fmt->iface, fmt->altsetting,
-                                  is_playback, ep, get_endpoint(alts, 0)->bSynchAddress);
-                       return -EINVAL;
-               }
-
-               implicit_fb = (get_endpoint(alts, 1)->bmAttributes & USB_ENDPOINT_USAGE_MASK)
-                               == USB_ENDPOINT_USAGE_IMPLICIT_FB;
-
-add_sync_ep:
-               subs->sync_endpoint = snd_usb_add_endpoint(subs->stream->chip,
-                                                          alts, ep, !subs->direction,
-                                                          implicit_fb ?
-                                                               SND_USB_ENDPOINT_TYPE_DATA :
-                                                               SND_USB_ENDPOINT_TYPE_SYNC);
-               if (!subs->sync_endpoint)
-                       return -EINVAL;
-
-               subs->data_endpoint->sync_master = subs->sync_endpoint;
-       }
+       err = set_sync_endpoint(subs, fmt, dev, alts, altsd);
+       if (err < 0)
+               return err;
 
-       if ((err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt)) < 0)
+       err = snd_usb_init_pitch(subs->stream->chip, fmt->iface, alts, fmt);
+       if (err < 0)
                return err;
 
        subs->cur_audiofmt = fmt;
 
        snd_usb_set_format_quirk(subs, fmt);
 
-#if 0
-       printk(KERN_DEBUG
-              "setting done: format = %d, rate = %d..%d, channels = %d\n",
-              fmt->format, fmt->rate_min, fmt->rate_max, fmt->channels);
-       printk(KERN_DEBUG
-              "  datapipe = 0x%0x, syncpipe = 0x%0x\n",
-              subs->datapipe, subs->syncpipe);
-#endif
-
        return 0;
 }
 
index 1f9bbd55553fc79bd539b8ad17f369be5beed267..5a51b18c50fe9230faabb923219c281b45a47970 100644 (file)
@@ -305,11 +305,9 @@ static void usX2Y_unlinkSeq(struct snd_usX2Y_AsyncSeq *S)
 {
        int     i;
        for (i = 0; i < URBS_AsyncSeq; ++i) {
-               if (S[i].urb) {
-                       usb_kill_urb(S->urb[i]);
-                       usb_free_urb(S->urb[i]);
-                       S->urb[i] = NULL;
-               }
+               usb_kill_urb(S->urb[i]);
+               usb_free_urb(S->urb[i]);
+               S->urb[i] = NULL;
        }
        kfree(S->buffer);
 }